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:
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user