From b54b3ae511adea2783e1668ad5674c7584b753b4 Mon Sep 17 00:00:00 2001 From: Julian Harnath Date: Fri, 13 Nov 2015 11:52:41 +0100 Subject: [PATCH] app_server: add a cache for AlphaMasks * If the same shape alpha mask is set again and again, we now keep the rendered masks in a cache. On certain websites, WebKit sets the same shape for clipping hundreds of times, which uses a lot of time to render the masks. * When a shape mask was generated, we put it into AlphaMaskCache. The constructor for ShapeAlphaMask is made private and a factory method is used for instantiation instead, which transparently looks up in the cache whether a suitable mask was already generated before (so the entire caching is encapsulated inside the AlphaMask class). * When taking a mask out of the cache, we still create a new AlphaMask instance. However, the new instance will share the mask bitmap with the previously generated instance (aside from the rendering of their bitmap, AlphaMask instances are pretty lightweight). Shape masks are only seen as identical when their shape is the same, the inverse flag, and they have the same parent mask. * Cache is limited to a fixed size of currently 8 MiB, using a simple random replacement scheme. An LRU scheme can be added in the future if necessary. Counting of bytes for the cache size includes parent masks of masks in the cache, even if the parent itself is not cached. A reference counter for "indirect" cache references keeps track of which masks are not part of the cache, but still need to be added to the cache byte size. * For now, only for ShapeAlphaMasks, other mask types can be added as necessary. --- headers/private/interface/ShapePrivate.h | 40 ++++- src/servers/app/DrawState.cpp | 3 +- src/servers/app/ServerWindow.cpp | 15 +- src/servers/app/drawing/AlphaMask.cpp | 152 +++++++++++++++-- src/servers/app/drawing/AlphaMask.h | 44 ++++- src/servers/app/drawing/AlphaMaskCache.cpp | 190 +++++++++++++++++++++ src/servers/app/drawing/AlphaMaskCache.h | 106 ++++++++++++ src/servers/app/drawing/Jamfile | 1 + 8 files changed, 525 insertions(+), 26 deletions(-) create mode 100644 src/servers/app/drawing/AlphaMaskCache.cpp create mode 100644 src/servers/app/drawing/AlphaMaskCache.h diff --git a/headers/private/interface/ShapePrivate.h b/headers/private/interface/ShapePrivate.h index 9fa63448ae..31e201e2d5 100644 --- a/headers/private/interface/ShapePrivate.h +++ b/headers/private/interface/ShapePrivate.h @@ -9,6 +9,13 @@ #ifndef SHAPE_PRIVATE_H #define SHAPE_PRIVATE_H +#include +#include +#include + +#include +#include + #define OP_LINETO 0x10000000 #define OP_BEZIERTO 0x20000000 @@ -20,14 +27,43 @@ #define OP_SMALL_ARC_TO_CCW 0x08000000 -struct shape_data { +struct shape_data : public BReferenceable { uint32* opList; + BPoint* ptList; int32 opCount; int32 opSize; - BPoint* ptList; int32 ptCount; int32 ptSize; + bool fOwnsMemory; + + shape_data() + : + fOwnsMemory(false) + { + } + + ~shape_data() + { + if (fOwnsMemory) { + delete[] opList; + delete[] ptList; + } + } + + shape_data(const shape_data& other) + { + opList = new(std::nothrow) uint32[other.opCount]; + ptList = new(std::nothrow) BPoint[other.ptCount]; + fOwnsMemory = true; + opCount = other.opCount; + opSize = other.opSize; + ptCount = other.ptCount; + ptSize = other.ptSize; + memcpy(opList, other.opList, opSize); + memcpy(ptList, other.ptList, ptSize); + } + BRect DetermineBoundingBox() const { BRect bounds; diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index 0e81463b1f..a0c61e0c53 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -515,8 +515,9 @@ DrawState::ClipToShape(shape_data* shape, bool inverse) if (!fCombinedTransform.IsIdentity()) fCombinedTransform.Apply(shape->ptList, shape->ptCount); - AlphaMask* const mask = new ShapeAlphaMask(GetAlphaMask(), *shape, + AlphaMask* const mask = ShapeAlphaMask::Create(GetAlphaMask(), *shape, BPoint(0, 0), inverse); + SetAlphaMask(mask); if (mask != NULL) mask->ReleaseReference(); diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index d91225e443..5e0531f583 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -2086,11 +2086,12 @@ fDesktop->LockSingleWindow(); shape_data shape; link.Read(&shape.opCount); link.Read(&shape.ptCount); + shape.opSize = shape.opCount * sizeof(uint32); + shape.ptSize = shape.ptCount * sizeof(BPoint); shape.opList = new(nothrow) uint32[shape.opCount]; shape.ptList = new(nothrow) BPoint[shape.ptCount]; - if (link.Read(shape.opList, shape.opCount * sizeof(uint32)) >= B_OK - && link.Read(shape.ptList, - shape.ptCount * sizeof(BPoint)) >= B_OK) { + if (link.Read(shape.opList, shape.opSize) >= B_OK + && link.Read(shape.ptList, shape.ptSize) >= B_OK) { fCurrentView->ClipToShape(&shape, inverse); _UpdateDrawState(fCurrentView); } @@ -3044,6 +3045,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, { DTRACE(("ServerWindow %s: Message AS_VIEW_END_LAYER\n", Title())); + fCurrentView->BlendAllLayers(); fCurrentView->SetPicture(NULL); fCurrentView->CurrentState()->SetDrawingModeLocked(false); @@ -3573,11 +3575,12 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) shape_data shape; link.Read(&shape.opCount); link.Read(&shape.ptCount); + shape.opSize = shape.opCount * sizeof(uint32); + shape.ptSize = shape.ptCount * sizeof(BPoint); shape.opList = new(nothrow) uint32[shape.opCount]; shape.ptList = new(nothrow) BPoint[shape.ptCount]; - if (link.Read(shape.opList, shape.opCount * sizeof(uint32)) >= B_OK - && link.Read(shape.ptList, - shape.ptCount * sizeof(BPoint)) >= B_OK) { + if (link.Read(shape.opList, shape.opSize) >= B_OK + && link.Read(shape.ptList, shape.ptSize) >= B_OK) { picture->WriteClipToShape(shape.opCount, shape.opList, shape.ptCount, shape.ptList, inverse); } diff --git a/src/servers/app/drawing/AlphaMask.cpp b/src/servers/app/drawing/AlphaMask.cpp index 35da811034..e7601dc96d 100644 --- a/src/servers/app/drawing/AlphaMask.cpp +++ b/src/servers/app/drawing/AlphaMask.cpp @@ -11,6 +11,7 @@ #include "AlphaMask.h" +#include "AlphaMaskCache.h" #include "BitmapHWInterface.h" #include "BitmapManager.h" #include "Canvas.h" @@ -34,11 +35,39 @@ AlphaMask::AlphaMask(AlphaMask* previousMask, bool inverse) fCanvasBounds(), fInverse(inverse), fBackgroundOpacity(0), + fNextMaskCount(0), + fInCache(false), + fIndirectCacheReferences(0), fBits(NULL), fBuffer(), fMask(), fScanline(fMask) { + if (previousMask != NULL) + atomic_add(&previousMask->fNextMaskCount, 1); +} + + +AlphaMask::AlphaMask(AlphaMask* previousMask, AlphaMask* other) + : + fPreviousMask(previousMask), + fBounds(other->fBounds), + fClippedToCanvas(other->fClippedToCanvas), + fCanvasOrigin(other->fCanvasOrigin), + fCanvasBounds(other->fCanvasBounds), + fInverse(other->fInverse), + fBackgroundOpacity(other->fBackgroundOpacity), + fNextMaskCount(0), + fInCache(false), + fIndirectCacheReferences(0), + fBits(other->fBits), + fBuffer(other->fBuffer), + fMask(other->fMask), + fScanline(fMask) +{ + if (previousMask != NULL) + atomic_add(&previousMask->fNextMaskCount, 1); + fBits->AcquireReference(); } @@ -51,6 +80,9 @@ AlphaMask::AlphaMask(uint8 backgroundOpacity) fCanvasBounds(), fInverse(false), fBackgroundOpacity(backgroundOpacity), + fNextMaskCount(0), + fInCache(false), + fIndirectCacheReferences(0), fBits(NULL), fBuffer(), fMask(), @@ -61,7 +93,10 @@ AlphaMask::AlphaMask(uint8 backgroundOpacity) AlphaMask::~AlphaMask() { - delete[] fBits; + if (fBits != NULL) + fBits->ReleaseReference(); + if (fPreviousMask.Get() != NULL) + atomic_add(&fPreviousMask->fNextMaskCount, -1); } @@ -95,6 +130,13 @@ AlphaMask::SetCanvasGeometry(IntPoint origin, IntRect bounds) } +size_t +AlphaMask::BitmapSize() const +{ + return fBits->BitsLength(); +} + + ServerBitmap* AlphaMask::_CreateTemporaryBitmap(BRect bounds) const { @@ -124,14 +166,16 @@ AlphaMask::_Generate() return; } - const int32 width = fBounds.IntegerWidth() + 1; - const int32 height = fBounds.IntegerHeight() + 1; - - delete[] fBits; - fBits = new(std::nothrow) uint8[width * height]; + if (fBits != NULL) + fBits->ReleaseReference(); + fBits = new(std::nothrow) UtilityBitmap(fBounds, B_GRAY8, 0); + if (fBits == NULL) + return; + const int32 width = fBits->Width(); + const int32 height = fBits->Height(); uint8* source = bitmap->Bits(); - uint8* destination = fBits; + uint8* destination = fBits->Bits(); uint32 numPixels = width * height; if (fPreviousMask != NULL) { @@ -159,8 +203,10 @@ AlphaMask::_Generate() } } - fBuffer.attach(fBits, width, height, width); + fBuffer.attach(fBits->Bits(), width, height, width); _AttachMaskToBuffer(); + + _AddToCache(); } @@ -228,6 +274,12 @@ UniformAlphaMask::_Offset() } +void +UniformAlphaMask::_AddToCache() +{ +} + + // #pragma mark - VectorAlphaMask @@ -241,6 +293,16 @@ VectorAlphaMask::VectorAlphaMask(AlphaMask* previousMask, } +template +VectorAlphaMask::VectorAlphaMask(AlphaMask* previousMask, + VectorAlphaMask* other) + : + AlphaMask(previousMask, other), + fWhere(other->fWhere) +{ +} + + template ServerBitmap* VectorAlphaMask::_RenderSource(const IntRect& canvasBounds) @@ -291,6 +353,7 @@ VectorAlphaMask::_RenderSource(const IntRect& canvasBounds) engine->UnlockParallelAccess(); } + canvas.PopState(); delete engine; return bitmap; @@ -360,17 +423,73 @@ PictureAlphaMask::GetDrawState() const } +void +PictureAlphaMask::_AddToCache() +{ + // currently not implemented +} + + // #pragma mark - ShapeAlphaMask +DrawState* ShapeAlphaMask::fDrawState = NULL; + + ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, const shape_data& shape, BPoint where, bool inverse) : VectorAlphaMask(previousMask, where, inverse), - fShape(shape), + fShape(new shape_data(shape)), fDrawState() { - fShapeBounds = fShape.DetermineBoundingBox(); + if (fDrawState == NULL) + fDrawState = new(std::nothrow) DrawState(); + + fShapeBounds = fShape->DetermineBoundingBox(); +} + + +ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, + ShapeAlphaMask* other) + : + VectorAlphaMask(previousMask, other), + fShape(other->fShape), + fShapeBounds(other->fShapeBounds) +{ + fShape->AcquireReference(); +} + + +ShapeAlphaMask::~ShapeAlphaMask() +{ + fShape->ReleaseReference(); +} + + +/* static */ ShapeAlphaMask* +ShapeAlphaMask::Create(AlphaMask* previousMask, const shape_data& shape, + BPoint where, bool inverse) +{ + // Look if we have a suitable cached mask + ShapeAlphaMask* mask = AlphaMaskCache::Default()->Get(shape, previousMask, + inverse); + + if (mask == NULL) { + // No cached mask, create new one + mask = new(std::nothrow) ShapeAlphaMask(previousMask, shape, + BPoint(0, 0), inverse); + } else { + // Create new mask which reuses the parameters and the mask bitmap + // of the cache entry + // TODO: don't make a new mask if the cache entry has no drawstate + // using it anymore, because then we ca just immediately reuse it + AlphaMask* cachedMask = mask; + mask = new(std::nothrow) ShapeAlphaMask(previousMask, mask); + cachedMask->ReleaseReference(); + } + + return mask; } @@ -378,8 +497,8 @@ void ShapeAlphaMask::DrawVectors(Canvas* canvas) { canvas->GetDrawingEngine()->DrawShape(fBounds, - fShape.opCount, fShape.opList, - fShape.ptCount, fShape.ptList, + fShape->opCount, fShape->opList, + fShape->ptCount, fShape->ptList, true, BPoint(0, 0), 1.0); } @@ -394,5 +513,12 @@ ShapeAlphaMask::DetermineBoundingBox() const const DrawState& ShapeAlphaMask::GetDrawState() const { - return fDrawState; + return *fDrawState; +} + + +void +ShapeAlphaMask::_AddToCache() +{ + AlphaMaskCache::Default()->Put(this); } diff --git a/src/servers/app/drawing/AlphaMask.h b/src/servers/app/drawing/AlphaMask.h index 592f486cb4..7d53d67c53 100644 --- a/src/servers/app/drawing/AlphaMask.h +++ b/src/servers/app/drawing/AlphaMask.h @@ -20,6 +20,7 @@ class BShape; class ServerBitmap; class ServerPicture; class shape_data; +class UtilityBitmap; // #pragma mark - AlphaMask @@ -29,6 +30,8 @@ class AlphaMask : public BReferenceable { public: AlphaMask(AlphaMask* previousMask, bool inverse); + AlphaMask(AlphaMask* previousMask, + AlphaMask* other); AlphaMask(uint8 backgroundOpacity); virtual ~AlphaMask(); @@ -41,11 +44,14 @@ public: agg::clipped_alpha_mask* Mask() { return &fMask; } + size_t BitmapSize() const; + protected: ServerBitmap* _CreateTemporaryBitmap(BRect bounds) const; void _Generate(); void _SetNoClipping(); const IntRect& _PreviousMaskBounds() const; + virtual void _AddToCache() = 0; private: virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds) = 0; @@ -59,12 +65,22 @@ protected: bool fClippedToCanvas; private: + friend class AlphaMaskCache; + IntPoint fCanvasOrigin; IntRect fCanvasBounds; const bool fInverse; uint8 fBackgroundOpacity; - uint8* fBits; + int32 fNextMaskCount; + bool fInCache; + uint32 fIndirectCacheReferences; + // number of times this mask has been + // seen as "previous mask" of another + // one in the cache, without being + // in the cache itself + + UtilityBitmap* fBits; agg::rendering_buffer fBuffer; agg::clipped_alpha_mask fMask; scanline_unpacked_masked_type fScanline; @@ -78,6 +94,7 @@ public: private: virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds); virtual IntPoint _Offset(); + virtual void _AddToCache(); }; @@ -89,6 +106,8 @@ class VectorAlphaMask : public AlphaMask { public: VectorAlphaMask(AlphaMask* previousMask, BPoint where, bool inverse); + VectorAlphaMask(AlphaMask* previousMask, + VectorAlphaMask* other); private: virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds); @@ -114,6 +133,9 @@ public: BRect DetermineBoundingBox() const; const DrawState& GetDrawState() const; +private: + virtual void _AddToCache(); + private: BReference fPicture; DrawState* fDrawState; @@ -124,19 +146,33 @@ private: class ShapeAlphaMask : public VectorAlphaMask { -public: +private: ShapeAlphaMask(AlphaMask* previousMask, const shape_data& shape, BPoint where, bool inverse); + ShapeAlphaMask(AlphaMask* previousMask, + ShapeAlphaMask* other); + +public: + virtual ~ShapeAlphaMask(); + + static ShapeAlphaMask* Create(AlphaMask* previousMask, + const shape_data& shape, + BPoint where, bool inverse); void DrawVectors(Canvas* canvas); BRect DetermineBoundingBox() const; const DrawState& GetDrawState() const; private: - const shape_data& fShape; + virtual void _AddToCache(); + +private: + friend class AlphaMaskCache; + + shape_data* fShape; BRect fShapeBounds; - DrawState fDrawState; + static DrawState* fDrawState; }; diff --git a/src/servers/app/drawing/AlphaMaskCache.cpp b/src/servers/app/drawing/AlphaMaskCache.cpp new file mode 100644 index 0000000000..dcaa212a1f --- /dev/null +++ b/src/servers/app/drawing/AlphaMaskCache.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "AlphaMaskCache.h" + +#include "AlphaMask.h" +#include "ShapePrivate.h" + +#include + + +//#define PRINT_ALPHA_MASK_CACHE_STATISTICS +#ifdef PRINT_ALPHA_MASK_CACHE_STATISTICS +static uint32 sAlphaMaskGetCount = 0; +#endif + + +AlphaMaskCache AlphaMaskCache::sDefaultInstance; + + +AlphaMaskCache::AlphaMaskCache() + : + fLock("AlphaMask cache"), + fCurrentCacheBytes(0), + fTooLargeMaskCount(0), + fMasksReplacedCount(0), + fHitCount(0), + fMissCount(0), + fLowerMaskReferencedCount(0) +{ +} + + +AlphaMaskCache::~AlphaMaskCache() +{ + Clear(); +} + + +/* static */ AlphaMaskCache* +AlphaMaskCache::Default() +{ + return &sDefaultInstance; +} + + +status_t +AlphaMaskCache::Put(ShapeAlphaMask* mask) +{ + AutoLocker locker(fLock); + + size_t maskStackSize = mask->BitmapSize(); + maskStackSize += _FindUncachedPreviousMasks(mask, true); + + if (maskStackSize > kMaxCacheBytes) { + _FindUncachedPreviousMasks(mask, false); + fTooLargeMaskCount++; + return B_NO_MEMORY; + } + + if (fCurrentCacheBytes + maskStackSize > kMaxCacheBytes) { + for (ShapeMaskSet::iterator it = fShapeMasks.begin(); + it != fShapeMasks.end();) { + + if (atomic_get(&it->fMask->fNextMaskCount) > 0) { + it++; + continue; + } + + size_t removedMaskStackSize = it->fMask->BitmapSize(); + removedMaskStackSize += _FindUncachedPreviousMasks(it->fMask, + false); + fCurrentCacheBytes -= removedMaskStackSize; + + it->fMask->fInCache = false; + it->fMask->ReleaseReference(); + fMasksReplacedCount++; + fShapeMasks.erase(it++); + + if (fCurrentCacheBytes + maskStackSize <= kMaxCacheBytes) + break; + } + } + + if (fCurrentCacheBytes + maskStackSize > kMaxCacheBytes) { + _FindUncachedPreviousMasks(mask, false); + fTooLargeMaskCount++; + return B_NO_MEMORY; + } + + fCurrentCacheBytes += maskStackSize; + + ShapeMaskElement element(mask->fShape, mask, mask->fPreviousMask.Get(), + mask->fInverse); + fShapeMasks.insert(element); + mask->AcquireReference(); + mask->fInCache = true; + return B_OK; +} + + +ShapeAlphaMask* +AlphaMaskCache::Get(const shape_data& shape, AlphaMask* previousMask, + bool inverse) +{ + AutoLocker locker(fLock); + +#ifdef PRINT_ALPHA_MASK_CACHE_STATISTICS + if (sAlphaMaskGetCount++ > 200) { + _PrintAndResetStatistics(); + sAlphaMaskGetCount = 0; + } +#endif + + ShapeMaskElement element(&shape, NULL, previousMask, inverse); + ShapeMaskSet::iterator it = fShapeMasks.find(element); + if (it == fShapeMasks.end()) { + fMissCount++; + return NULL; + } + fHitCount++; + it->fMask->AcquireReference(); + return it->fMask; +} + + +void +AlphaMaskCache::Clear() +{ + AutoLocker locker(fLock); + + for (ShapeMaskSet::iterator it = fShapeMasks.begin(); + it != fShapeMasks.end(); it++) { + it->fMask->fInCache = false; + it->fMask->fIndirectCacheReferences = 0; + it->fMask->ReleaseReference(); + } + fShapeMasks.clear(); + fTooLargeMaskCount = 0; + fMasksReplacedCount = 0; + fHitCount = 0; + fMissCount = 0; + fLowerMaskReferencedCount = 0; +} + + +size_t +AlphaMaskCache::_FindUncachedPreviousMasks(AlphaMask* mask, bool reference) +{ + const int32 referenceModifier = reference ? 1 : -1; + size_t addedOrRemovedSize = 0; + + for (AlphaMask* lowerMask = mask->fPreviousMask.Get(); lowerMask != NULL; + lowerMask = lowerMask->fPreviousMask.Get()) { + if (lowerMask->fInCache) + continue; + uint32 oldReferences = lowerMask->fIndirectCacheReferences; + lowerMask->fIndirectCacheReferences += referenceModifier; + if (lowerMask->fIndirectCacheReferences == 0 || oldReferences == 0) { + // We either newly referenced the mask for the first time, or + // released the last reference + addedOrRemovedSize += lowerMask->BitmapSize(); + fLowerMaskReferencedCount += referenceModifier; + } + } + + return addedOrRemovedSize; +} + + +void +AlphaMaskCache::_PrintAndResetStatistics() +{ + debug_printf("AlphaMaskCache statistics: size=%4ld bytes=%4ld lower=%4ld " + "total=%4ld too_large=%4ld replaced=%4ld hit=%4ld miss=%4ld\n", + fShapeMasks.size(), + fCurrentCacheBytes, + fLowerMaskReferencedCount, + fShapeMasks.size() + fLowerMaskReferencedCount, + fTooLargeMaskCount, + fMasksReplacedCount, + fHitCount, + fMissCount); + fTooLargeMaskCount = 0; + fMasksReplacedCount = 0; + fHitCount = 0; + fMissCount = 0; +} diff --git a/src/servers/app/drawing/AlphaMaskCache.h b/src/servers/app/drawing/AlphaMaskCache.h new file mode 100644 index 0000000000..495d469b85 --- /dev/null +++ b/src/servers/app/drawing/AlphaMaskCache.h @@ -0,0 +1,106 @@ +/* + * Copyright 2015 Julian Harnath + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef ALPHA_MASK_CACHE_H +#define ALPHA_MASK_CACHE_H + +#include + +#include "ShapePrivate.h" +#include +#include + + +class AlphaMask; +class ShapeAlphaMask; + + +class AlphaMaskCache { +private: + enum { + kMaxCacheBytes = 8 * 1024 * 1024 // 8 MiB + }; + +public: + AlphaMaskCache(); + ~AlphaMaskCache(); + + static AlphaMaskCache* Default(); + + status_t Put(ShapeAlphaMask* mask); + ShapeAlphaMask* Get(const shape_data& shape, + AlphaMask* previousMask, + bool inverse); + + void Clear(); + +private: + size_t _FindUncachedPreviousMasks(AlphaMask* mask, + bool reference); + void _PrintAndResetStatistics(); + +private: + struct ShapeMaskElement { + ShapeMaskElement(const shape_data* shape, + ShapeAlphaMask* mask, AlphaMask* previousMask, + bool inverse) + : + fShape(shape), + fInverse(inverse), + fMask(mask), + fPreviousMask(previousMask) + { + } + + bool operator<(const ShapeMaskElement& other) const + { + if (fInverse != other.fInverse) + return fInverse < other.fInverse; + if (fPreviousMask != other.fPreviousMask) + return fPreviousMask < other.fPreviousMask; + + // compare shapes + if (fShape->ptCount != other.fShape->ptCount) + return fShape->ptCount < other.fShape->ptCount; + if (fShape->opCount != other.fShape->opCount) + return fShape->opCount < other.fShape->opCount; + int diff = memcmp(fShape->ptList, other.fShape->ptList, + fShape->ptSize); + if (diff != 0) + return diff < 0; + diff = memcmp(fShape->opList, other.fShape->opList, + fShape->opSize); + if (diff != 0) + return diff < 0; + + // equal + return false; + } + + const shape_data* fShape; + bool fInverse; + ShapeAlphaMask* fMask; + AlphaMask* fPreviousMask; + }; + +private: + typedef std::set ShapeMaskSet; + + static AlphaMaskCache sDefaultInstance; + + BLocker fLock; + + size_t fCurrentCacheBytes; + ShapeMaskSet fShapeMasks; + + // Statistics counters + uint32 fTooLargeMaskCount; + uint32 fMasksReplacedCount; + uint32 fHitCount; + uint32 fMissCount; + uint32 fLowerMaskReferencedCount; +}; + + +#endif // ALPHA_MASK_CACHE_H diff --git a/src/servers/app/drawing/Jamfile b/src/servers/app/drawing/Jamfile index 0893dfb118..0a3eb39109 100644 --- a/src/servers/app/drawing/Jamfile +++ b/src/servers/app/drawing/Jamfile @@ -17,6 +17,7 @@ Includes [ FGristFiles AlphaMask.cpp DrawingEngine.cpp ] StaticLibrary libasdrawing.a : AlphaMask.cpp + AlphaMaskCache.cpp BitmapBuffer.cpp BitmapDrawingEngine.cpp drawing_support.cpp