Skip to content

Commit

Permalink
app_server: add a cache for AlphaMasks
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
juafromspace committed Nov 14, 2015
1 parent 10df154 commit b54b3ae
Show file tree
Hide file tree
Showing 8 changed files with 525 additions and 26 deletions.
40 changes: 38 additions & 2 deletions headers/private/interface/ShapePrivate.h
Expand Up @@ -9,6 +9,13 @@
#ifndef 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_BEZIERTO 0x20000000
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/servers/app/DrawState.cpp
Expand Up @@ -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();
Expand Down
15 changes: 9 additions & 6 deletions src/servers/app/ServerWindow.cpp
Expand Up @@ -2086,11 +2086,12 @@ fDesktop->LockSingleWindow();
shape_data shape;
link.Read<int32>(&shape.opCount);
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.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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -3573,11 +3575,12 @@ ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link)
shape_data shape;
link.Read<int32>(&shape.opCount);
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.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);
}
Expand Down
152 changes: 139 additions & 13 deletions src/servers/app/drawing/AlphaMask.cpp
Expand Up @@ -11,6 +11,7 @@

#include "AlphaMask.h"

#include "AlphaMaskCache.h"
#include "BitmapHWInterface.h"
#include "BitmapManager.h"
#include "Canvas.h"
Expand All @@ -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();
}


Expand All @@ -51,6 +80,9 @@ AlphaMask::AlphaMask(uint8 backgroundOpacity)
fCanvasBounds(),
fInverse(false),
fBackgroundOpacity(backgroundOpacity),
fNextMaskCount(0),
fInCache(false),
fIndirectCacheReferences(0),
fBits(NULL),
fBuffer(),
fMask(),
Expand All @@ -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);
}


Expand Down Expand Up @@ -95,6 +130,13 @@ AlphaMask::SetCanvasGeometry(IntPoint origin, IntRect bounds)
}


size_t
AlphaMask::BitmapSize() const
{
return fBits->BitsLength();
}


ServerBitmap*
AlphaMask::_CreateTemporaryBitmap(BRect bounds) const
{
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -159,8 +203,10 @@ AlphaMask::_Generate()
}
}

fBuffer.attach(fBits, width, height, width);
fBuffer.attach(fBits->Bits(), width, height, width);
_AttachMaskToBuffer();

_AddToCache();
}


Expand Down Expand Up @@ -228,6 +274,12 @@ UniformAlphaMask::_Offset()
}


void
UniformAlphaMask::_AddToCache()
{
}


// #pragma mark - VectorAlphaMask


Expand All @@ -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>
ServerBitmap*
VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds)
Expand Down Expand Up @@ -291,6 +353,7 @@ VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds)
engine->UnlockParallelAccess();
}

canvas.PopState();
delete engine;

return bitmap;
Expand Down Expand Up @@ -360,26 +423,82 @@ 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<ShapeAlphaMask>(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<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;
}


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);
}

Expand All @@ -394,5 +513,12 @@ ShapeAlphaMask::DetermineBoundingBox() const
const DrawState&
ShapeAlphaMask::GetDrawState() const
{
return fDrawState;
return *fDrawState;
}


void
ShapeAlphaMask::_AddToCache()
{
AlphaMaskCache::Default()->Put(this);
}

0 comments on commit b54b3ae

Please sign in to comment.