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.
This commit is contained in:
Julian Harnath
2015-11-14 16:09:52 +01:00
parent 10df15449b
commit b54b3ae511
8 changed files with 525 additions and 26 deletions
+38 -2
View File
@@ -9,6 +9,13 @@
#ifndef SHAPE_PRIVATE_H #ifndef SHAPE_PRIVATE_H
#define SHAPE_PRIVATE_H #define SHAPE_PRIVATE_H
#include <Point.h>
#include <Rect.h>
#include <Referenceable.h>
#include <string.h>
#include <stdio.h>
#define OP_LINETO 0x10000000 #define OP_LINETO 0x10000000
#define OP_BEZIERTO 0x20000000 #define OP_BEZIERTO 0x20000000
@@ -20,14 +27,43 @@
#define OP_SMALL_ARC_TO_CCW 0x08000000 #define OP_SMALL_ARC_TO_CCW 0x08000000
struct shape_data { struct shape_data : public BReferenceable {
uint32* opList; uint32* opList;
BPoint* ptList;
int32 opCount; int32 opCount;
int32 opSize; int32 opSize;
BPoint* ptList;
int32 ptCount; int32 ptCount;
int32 ptSize; 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 DetermineBoundingBox() const
{ {
BRect bounds; BRect bounds;
+2 -1
View File
@@ -515,8 +515,9 @@ DrawState::ClipToShape(shape_data* shape, bool inverse)
if (!fCombinedTransform.IsIdentity()) if (!fCombinedTransform.IsIdentity())
fCombinedTransform.Apply(shape->ptList, shape->ptCount); fCombinedTransform.Apply(shape->ptList, shape->ptCount);
AlphaMask* const mask = new ShapeAlphaMask(GetAlphaMask(), *shape, AlphaMask* const mask = ShapeAlphaMask::Create(GetAlphaMask(), *shape,
BPoint(0, 0), inverse); BPoint(0, 0), inverse);
SetAlphaMask(mask); SetAlphaMask(mask);
if (mask != NULL) if (mask != NULL)
mask->ReleaseReference(); mask->ReleaseReference();
+9 -6
View File
@@ -2086,11 +2086,12 @@ fDesktop->LockSingleWindow();
shape_data shape; shape_data shape;
link.Read<int32>(&shape.opCount); link.Read<int32>(&shape.opCount);
link.Read<int32>(&shape.ptCount); link.Read<int32>(&shape.ptCount);
shape.opSize = shape.opCount * sizeof(uint32);
shape.ptSize = shape.ptCount * sizeof(BPoint);
shape.opList = new(nothrow) uint32[shape.opCount]; shape.opList = new(nothrow) uint32[shape.opCount];
shape.ptList = new(nothrow) BPoint[shape.ptCount]; shape.ptList = new(nothrow) BPoint[shape.ptCount];
if (link.Read(shape.opList, shape.opCount * sizeof(uint32)) >= B_OK if (link.Read(shape.opList, shape.opSize) >= B_OK
&& link.Read(shape.ptList, && link.Read(shape.ptList, shape.ptSize) >= B_OK) {
shape.ptCount * sizeof(BPoint)) >= B_OK) {
fCurrentView->ClipToShape(&shape, inverse); fCurrentView->ClipToShape(&shape, inverse);
_UpdateDrawState(fCurrentView); _UpdateDrawState(fCurrentView);
} }
@@ -3044,6 +3045,7 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code,
{ {
DTRACE(("ServerWindow %s: Message AS_VIEW_END_LAYER\n", DTRACE(("ServerWindow %s: Message AS_VIEW_END_LAYER\n",
Title())); Title()));
fCurrentView->BlendAllLayers(); fCurrentView->BlendAllLayers();
fCurrentView->SetPicture(NULL); fCurrentView->SetPicture(NULL);
fCurrentView->CurrentState()->SetDrawingModeLocked(false); fCurrentView->CurrentState()->SetDrawingModeLocked(false);
@@ -3573,11 +3575,12 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link)
shape_data shape; shape_data shape;
link.Read<int32>(&shape.opCount); link.Read<int32>(&shape.opCount);
link.Read<int32>(&shape.ptCount); link.Read<int32>(&shape.ptCount);
shape.opSize = shape.opCount * sizeof(uint32);
shape.ptSize = shape.ptCount * sizeof(BPoint);
shape.opList = new(nothrow) uint32[shape.opCount]; shape.opList = new(nothrow) uint32[shape.opCount];
shape.ptList = new(nothrow) BPoint[shape.ptCount]; shape.ptList = new(nothrow) BPoint[shape.ptCount];
if (link.Read(shape.opList, shape.opCount * sizeof(uint32)) >= B_OK if (link.Read(shape.opList, shape.opSize) >= B_OK
&& link.Read(shape.ptList, && link.Read(shape.ptList, shape.ptSize) >= B_OK) {
shape.ptCount * sizeof(BPoint)) >= B_OK) {
picture->WriteClipToShape(shape.opCount, shape.opList, picture->WriteClipToShape(shape.opCount, shape.opList,
shape.ptCount, shape.ptList, inverse); shape.ptCount, shape.ptList, inverse);
} }
+139 -13
View File
@@ -11,6 +11,7 @@
#include "AlphaMask.h" #include "AlphaMask.h"
#include "AlphaMaskCache.h"
#include "BitmapHWInterface.h" #include "BitmapHWInterface.h"
#include "BitmapManager.h" #include "BitmapManager.h"
#include "Canvas.h" #include "Canvas.h"
@@ -34,11 +35,39 @@ AlphaMask::AlphaMask(AlphaMask* previousMask, bool inverse)
fCanvasBounds(), fCanvasBounds(),
fInverse(inverse), fInverse(inverse),
fBackgroundOpacity(0), fBackgroundOpacity(0),
fNextMaskCount(0),
fInCache(false),
fIndirectCacheReferences(0),
fBits(NULL), fBits(NULL),
fBuffer(), fBuffer(),
fMask(), fMask(),
fScanline(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(), fCanvasBounds(),
fInverse(false), fInverse(false),
fBackgroundOpacity(backgroundOpacity), fBackgroundOpacity(backgroundOpacity),
fNextMaskCount(0),
fInCache(false),
fIndirectCacheReferences(0),
fBits(NULL), fBits(NULL),
fBuffer(), fBuffer(),
fMask(), fMask(),
@@ -61,7 +93,10 @@ AlphaMask::AlphaMask(uint8 backgroundOpacity)
AlphaMask::~AlphaMask() 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* ServerBitmap*
AlphaMask::_CreateTemporaryBitmap(BRect bounds) const AlphaMask::_CreateTemporaryBitmap(BRect bounds) const
{ {
@@ -124,14 +166,16 @@ AlphaMask::_Generate()
return; return;
} }
const int32 width = fBounds.IntegerWidth() + 1; if (fBits != NULL)
const int32 height = fBounds.IntegerHeight() + 1; fBits->ReleaseReference();
fBits = new(std::nothrow) UtilityBitmap(fBounds, B_GRAY8, 0);
delete[] fBits; if (fBits == NULL)
fBits = new(std::nothrow) uint8[width * height]; return;
const int32 width = fBits->Width();
const int32 height = fBits->Height();
uint8* source = bitmap->Bits(); uint8* source = bitmap->Bits();
uint8* destination = fBits; uint8* destination = fBits->Bits();
uint32 numPixels = width * height; uint32 numPixels = width * height;
if (fPreviousMask != NULL) { if (fPreviousMask != NULL) {
@@ -159,8 +203,10 @@ AlphaMask::_Generate()
} }
} }
fBuffer.attach(fBits, width, height, width); fBuffer.attach(fBits->Bits(), width, height, width);
_AttachMaskToBuffer(); _AttachMaskToBuffer();
_AddToCache();
} }
@@ -228,6 +274,12 @@ UniformAlphaMask::_Offset()
} }
void
UniformAlphaMask::_AddToCache()
{
}
// #pragma mark - VectorAlphaMask // #pragma mark - VectorAlphaMask
@@ -241,6 +293,16 @@ VectorAlphaMask<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
} }
template<class VectorMaskType>
VectorAlphaMask<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
VectorAlphaMask* other)
:
AlphaMask(previousMask, other),
fWhere(other->fWhere)
{
}
template<class VectorMaskType> template<class VectorMaskType>
ServerBitmap* ServerBitmap*
VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds) VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds)
@@ -291,6 +353,7 @@ VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds)
engine->UnlockParallelAccess(); engine->UnlockParallelAccess();
} }
canvas.PopState();
delete engine; delete engine;
return bitmap; return bitmap;
@@ -360,17 +423,73 @@ PictureAlphaMask::GetDrawState() const
} }
void
PictureAlphaMask::_AddToCache()
{
// currently not implemented
}
// #pragma mark - ShapeAlphaMask // #pragma mark - ShapeAlphaMask
DrawState* ShapeAlphaMask::fDrawState = NULL;
ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask,
const shape_data& shape, BPoint where, bool inverse) const shape_data& shape, BPoint where, bool inverse)
: :
VectorAlphaMask<ShapeAlphaMask>(previousMask, where, inverse), VectorAlphaMask<ShapeAlphaMask>(previousMask, where, inverse),
fShape(shape), fShape(new shape_data(shape)),
fDrawState() fDrawState()
{ {
fShapeBounds = fShape.DetermineBoundingBox(); if (fDrawState == NULL)
fDrawState = new(std::nothrow) DrawState();
fShapeBounds = fShape->DetermineBoundingBox();
}
ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask,
ShapeAlphaMask* other)
:
VectorAlphaMask<ShapeAlphaMask>(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) ShapeAlphaMask::DrawVectors(Canvas* canvas)
{ {
canvas->GetDrawingEngine()->DrawShape(fBounds, canvas->GetDrawingEngine()->DrawShape(fBounds,
fShape.opCount, fShape.opList, fShape->opCount, fShape->opList,
fShape.ptCount, fShape.ptList, fShape->ptCount, fShape->ptList,
true, BPoint(0, 0), 1.0); true, BPoint(0, 0), 1.0);
} }
@@ -394,5 +513,12 @@ ShapeAlphaMask::DetermineBoundingBox() const
const DrawState& const DrawState&
ShapeAlphaMask::GetDrawState() const ShapeAlphaMask::GetDrawState() const
{ {
return fDrawState; return *fDrawState;
}
void
ShapeAlphaMask::_AddToCache()
{
AlphaMaskCache::Default()->Put(this);
} }
+40 -4
View File
@@ -20,6 +20,7 @@ class BShape;
class ServerBitmap; class ServerBitmap;
class ServerPicture; class ServerPicture;
class shape_data; class shape_data;
class UtilityBitmap;
// #pragma mark - AlphaMask // #pragma mark - AlphaMask
@@ -29,6 +30,8 @@ class AlphaMask : public BReferenceable {
public: public:
AlphaMask(AlphaMask* previousMask, AlphaMask(AlphaMask* previousMask,
bool inverse); bool inverse);
AlphaMask(AlphaMask* previousMask,
AlphaMask* other);
AlphaMask(uint8 backgroundOpacity); AlphaMask(uint8 backgroundOpacity);
virtual ~AlphaMask(); virtual ~AlphaMask();
@@ -41,11 +44,14 @@ public:
agg::clipped_alpha_mask* Mask() agg::clipped_alpha_mask* Mask()
{ return &fMask; } { return &fMask; }
size_t BitmapSize() const;
protected: protected:
ServerBitmap* _CreateTemporaryBitmap(BRect bounds) const; ServerBitmap* _CreateTemporaryBitmap(BRect bounds) const;
void _Generate(); void _Generate();
void _SetNoClipping(); void _SetNoClipping();
const IntRect& _PreviousMaskBounds() const; const IntRect& _PreviousMaskBounds() const;
virtual void _AddToCache() = 0;
private: private:
virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds) = 0; virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds) = 0;
@@ -59,12 +65,22 @@ protected:
bool fClippedToCanvas; bool fClippedToCanvas;
private: private:
friend class AlphaMaskCache;
IntPoint fCanvasOrigin; IntPoint fCanvasOrigin;
IntRect fCanvasBounds; IntRect fCanvasBounds;
const bool fInverse; const bool fInverse;
uint8 fBackgroundOpacity; 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::rendering_buffer fBuffer;
agg::clipped_alpha_mask fMask; agg::clipped_alpha_mask fMask;
scanline_unpacked_masked_type fScanline; scanline_unpacked_masked_type fScanline;
@@ -78,6 +94,7 @@ public:
private: private:
virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds); virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds);
virtual IntPoint _Offset(); virtual IntPoint _Offset();
virtual void _AddToCache();
}; };
@@ -89,6 +106,8 @@ class VectorAlphaMask : public AlphaMask {
public: public:
VectorAlphaMask(AlphaMask* previousMask, VectorAlphaMask(AlphaMask* previousMask,
BPoint where, bool inverse); BPoint where, bool inverse);
VectorAlphaMask(AlphaMask* previousMask,
VectorAlphaMask* other);
private: private:
virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds); virtual ServerBitmap* _RenderSource(const IntRect& canvasBounds);
@@ -114,6 +133,9 @@ public:
BRect DetermineBoundingBox() const; BRect DetermineBoundingBox() const;
const DrawState& GetDrawState() const; const DrawState& GetDrawState() const;
private:
virtual void _AddToCache();
private: private:
BReference<ServerPicture> fPicture; BReference<ServerPicture> fPicture;
DrawState* fDrawState; DrawState* fDrawState;
@@ -124,19 +146,33 @@ private:
class ShapeAlphaMask : public VectorAlphaMask<ShapeAlphaMask> { class ShapeAlphaMask : public VectorAlphaMask<ShapeAlphaMask> {
public: private:
ShapeAlphaMask(AlphaMask* previousMask, ShapeAlphaMask(AlphaMask* previousMask,
const shape_data& shape, const shape_data& shape,
BPoint where, bool inverse); 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); void DrawVectors(Canvas* canvas);
BRect DetermineBoundingBox() const; BRect DetermineBoundingBox() const;
const DrawState& GetDrawState() const; const DrawState& GetDrawState() const;
private: private:
const shape_data& fShape; virtual void _AddToCache();
private:
friend class AlphaMaskCache;
shape_data* fShape;
BRect fShapeBounds; BRect fShapeBounds;
DrawState fDrawState; static DrawState* fDrawState;
}; };
+190
View File
@@ -0,0 +1,190 @@
/*
* Copyright 2015 Julian Harnath <julian.harnath@rwth-aachen.de>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "AlphaMaskCache.h"
#include "AlphaMask.h"
#include "ShapePrivate.h"
#include <AutoLocker.h>
//#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<BLocker> 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<BLocker> 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<BLocker> 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;
}
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright 2015 Julian Harnath <julian.harnath@rwth-aachen.de>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef ALPHA_MASK_CACHE_H
#define ALPHA_MASK_CACHE_H
#include <set>
#include "ShapePrivate.h"
#include <Locker.h>
#include <kernel/OS.h>
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<ShapeMaskElement> 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
+1
View File
@@ -17,6 +17,7 @@ Includes [ FGristFiles AlphaMask.cpp DrawingEngine.cpp ]
StaticLibrary libasdrawing.a : StaticLibrary libasdrawing.a :
AlphaMask.cpp AlphaMask.cpp
AlphaMaskCache.cpp
BitmapBuffer.cpp BitmapBuffer.cpp
BitmapDrawingEngine.cpp BitmapDrawingEngine.cpp
drawing_support.cpp drawing_support.cpp