Add plot-based purging to GrLayerCache
This CL allows a GrPlot full of atlased layer to be purged from the atlas to make room for new layers. R=jvanverth@google.com Author: robertphillips@google.com Review URL: https://codereview.chromium.org/411703003
This commit is contained in:
parent
e7416bfc98
commit
320c92380f
@ -81,6 +81,12 @@ public:
|
|||||||
public:
|
public:
|
||||||
bool isEmpty() const { return 0 == fPlots.count(); }
|
bool isEmpty() const { return 0 == fPlots.count(); }
|
||||||
|
|
||||||
|
#ifdef SK_DEBUG
|
||||||
|
bool contains(const GrPlot* plot) const {
|
||||||
|
return fPlots.contains(const_cast<GrPlot*>(plot));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SkTDArray<GrPlot*> fPlots;
|
SkTDArray<GrPlot*> fPlots;
|
||||||
|
|
||||||
@ -112,6 +118,18 @@ public:
|
|||||||
|
|
||||||
void uploadPlotsToTexture();
|
void uploadPlotsToTexture();
|
||||||
|
|
||||||
|
enum IterOrder {
|
||||||
|
kLRUFirst_IterOrder,
|
||||||
|
kMRUFirst_IterOrder
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef GrPlotList::Iter PlotIter;
|
||||||
|
GrPlot* iterInit(PlotIter* iter, IterOrder order) {
|
||||||
|
return iter->init(fPlotList, kLRUFirst_IterOrder == order
|
||||||
|
? GrPlotList::Iter::kTail_IterStart
|
||||||
|
: GrPlotList::Iter::kHead_IterStart);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void makeMRU(GrPlot* plot);
|
void makeMRU(GrPlot* plot);
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ void GrCachedLayer::validate(const GrTexture* backingTexture) const {
|
|||||||
} else {
|
} else {
|
||||||
SkASSERT(fRect.isEmpty());
|
SkASSERT(fRect.isEmpty());
|
||||||
SkASSERT(NULL == fPlot);
|
SkASSERT(NULL == fPlot);
|
||||||
|
SkASSERT(!fLocked); // layers without a texture cannot be locked
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NULL != fPlot) {
|
if (NULL != fPlot) {
|
||||||
@ -35,6 +36,13 @@ void GrCachedLayer::validate(const GrTexture* backingTexture) const {
|
|||||||
SkASSERT(NULL != fTexture && backingTexture == fTexture);
|
SkASSERT(NULL != fTexture && backingTexture == fTexture);
|
||||||
SkASSERT(!fRect.isEmpty());
|
SkASSERT(!fRect.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fLocked) {
|
||||||
|
// If a layer is locked it must have a texture (though it need not be
|
||||||
|
// the atlas-backing texture) and occupy some space.
|
||||||
|
SkASSERT(NULL != fTexture);
|
||||||
|
SkASSERT(!fRect.isEmpty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GrAutoValidateLayer : ::SkNoncopyable {
|
class GrAutoValidateLayer : ::SkNoncopyable {
|
||||||
@ -65,6 +73,7 @@ private:
|
|||||||
GrLayerCache::GrLayerCache(GrContext* context)
|
GrLayerCache::GrLayerCache(GrContext* context)
|
||||||
: fContext(context) {
|
: fContext(context) {
|
||||||
this->initAtlas();
|
this->initAtlas();
|
||||||
|
memset(fPlotLocks, 0, sizeof(fPlotLocks));
|
||||||
}
|
}
|
||||||
|
|
||||||
GrLayerCache::~GrLayerCache() {
|
GrLayerCache::~GrLayerCache() {
|
||||||
@ -81,12 +90,8 @@ GrLayerCache::~GrLayerCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GrLayerCache::initAtlas() {
|
void GrLayerCache::initAtlas() {
|
||||||
static const int kAtlasTextureWidth = 1024;
|
|
||||||
static const int kAtlasTextureHeight = 1024;
|
|
||||||
|
|
||||||
SkASSERT(NULL == fAtlas.get());
|
SkASSERT(NULL == fAtlas.get());
|
||||||
|
|
||||||
// The layer cache only gets 1 plot
|
|
||||||
SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
|
SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
|
||||||
fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfig,
|
fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfig,
|
||||||
kRenderTarget_GrTextureFlagBit,
|
kRenderTarget_GrTextureFlagBit,
|
||||||
@ -138,7 +143,7 @@ GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, int lay
|
|||||||
bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
|
bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
|
||||||
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
|
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
|
||||||
|
|
||||||
if (NULL != layer->texture()) {
|
if (layer->locked()) {
|
||||||
// This layer is already locked
|
// This layer is already locked
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
if (layer->isAtlased()) {
|
if (layer->isAtlased()) {
|
||||||
@ -151,7 +156,13 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if USE_ATLAS
|
#if USE_ATLAS
|
||||||
{
|
if (layer->isAtlased()) {
|
||||||
|
// Hooray it is still in the atlas - make sure it stays there
|
||||||
|
layer->setLocked(true);
|
||||||
|
fPlotLocks[layer->plot()->id()]++;
|
||||||
|
return true;
|
||||||
|
} else if (PlausiblyAtlasable(desc.fWidth, desc.fHeight)) {
|
||||||
|
// Not in the atlas - will it fit?
|
||||||
GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
|
GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
|
||||||
if (NULL == pictInfo) {
|
if (NULL == pictInfo) {
|
||||||
pictInfo = SkNEW_ARGS(GrPictureInfo, (layer->pictureID()));
|
pictInfo = SkNEW_ARGS(GrPictureInfo, (layer->pictureID()));
|
||||||
@ -159,17 +170,29 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkIPoint16 loc;
|
SkIPoint16 loc;
|
||||||
GrPlot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage,
|
for (int i = 0; i < 2; ++i) { // extra pass in case we fail to add but are able to purge
|
||||||
desc.fWidth, desc.fHeight,
|
GrPlot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage,
|
||||||
NULL, &loc);
|
desc.fWidth, desc.fHeight,
|
||||||
// addToAtlas can allocate the backing texture
|
NULL, &loc);
|
||||||
SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture()));
|
// addToAtlas can allocate the backing texture
|
||||||
if (NULL != plot) {
|
SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture()));
|
||||||
GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
|
if (NULL != plot) {
|
||||||
SkToS16(desc.fWidth), SkToS16(desc.fHeight));
|
// The layer was successfully added to the atlas
|
||||||
layer->setTexture(fAtlas->getTexture(), bounds);
|
GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
|
||||||
layer->setPlot(plot);
|
SkToS16(desc.fWidth),
|
||||||
return false;
|
SkToS16(desc.fHeight));
|
||||||
|
layer->setTexture(fAtlas->getTexture(), bounds);
|
||||||
|
layer->setPlot(plot);
|
||||||
|
layer->setLocked(true);
|
||||||
|
fPlotLocks[layer->plot()->id()]++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The layer was rejected by the atlas (even though we know it is
|
||||||
|
// plausibly atlas-able). See if a plot can be purged and try again.
|
||||||
|
if (!this->purgePlot()) {
|
||||||
|
break; // We weren't able to purge any plots
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -179,35 +202,69 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
|
|||||||
// This can yield a lot of re-rendering
|
// This can yield a lot of re-rendering
|
||||||
layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kApprox_ScratchTexMatch),
|
layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kApprox_ScratchTexMatch),
|
||||||
GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight)));
|
GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight)));
|
||||||
|
layer->setLocked(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrLayerCache::unlock(GrCachedLayer* layer) {
|
void GrLayerCache::unlock(GrCachedLayer* layer) {
|
||||||
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
|
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
|
||||||
|
|
||||||
if (NULL == layer || NULL == layer->texture()) {
|
if (NULL == layer || !layer->locked()) {
|
||||||
|
// invalid or not locked
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layer->isAtlased()) {
|
if (layer->isAtlased()) {
|
||||||
SkASSERT(layer->texture() == fAtlas->getTexture());
|
const int plotID = layer->plot()->id();
|
||||||
|
|
||||||
GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
|
SkASSERT(fPlotLocks[plotID] > 0);
|
||||||
SkASSERT(NULL != pictInfo);
|
fPlotLocks[plotID]--;
|
||||||
pictInfo->fPlotUsage.isEmpty(); // just to silence compiler warnings for the time being
|
// At this point we could aggressively clear out un-locked plots but
|
||||||
|
// by delaying we may be able to reuse some of the atlased layers later.
|
||||||
// TODO: purging from atlas goes here
|
|
||||||
} else {
|
} else {
|
||||||
fContext->unlockScratchTexture(layer->texture());
|
fContext->unlockScratchTexture(layer->texture());
|
||||||
layer->setTexture(NULL, GrIRect16::MakeEmpty());
|
layer->setTexture(NULL, GrIRect16::MakeEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer->setLocked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
void GrLayerCache::validate() const {
|
void GrLayerCache::validate() const {
|
||||||
|
int plotLocks[kNumPlotsX * kNumPlotsY];
|
||||||
|
memset(plotLocks, 0, sizeof(plotLocks));
|
||||||
|
|
||||||
SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHash);
|
SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHash);
|
||||||
for (; !iter.done(); ++iter) {
|
for (; !iter.done(); ++iter) {
|
||||||
(*iter).validate(fAtlas->getTexture());
|
const GrCachedLayer* layer = &(*iter);
|
||||||
|
|
||||||
|
layer->validate(fAtlas->getTexture());
|
||||||
|
|
||||||
|
const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
|
||||||
|
if (NULL != pictInfo) {
|
||||||
|
// In aggressive cleanup mode a picture info should only exist if
|
||||||
|
// it has some atlased layers
|
||||||
|
SkASSERT(!pictInfo->fPlotUsage.isEmpty());
|
||||||
|
} else {
|
||||||
|
// If there is no picture info for this layer then all of its
|
||||||
|
// layers should be non-atlased.
|
||||||
|
SkASSERT(!layer->isAtlased());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL != layer->plot()) {
|
||||||
|
SkASSERT(NULL != pictInfo);
|
||||||
|
SkASSERT(pictInfo->fPictureID == layer->pictureID());
|
||||||
|
|
||||||
|
SkASSERT(pictInfo->fPlotUsage.contains(layer->plot()));
|
||||||
|
|
||||||
|
if (layer->locked()) {
|
||||||
|
plotLocks[layer->plot()->id()]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < kNumPlotsX*kNumPlotsY; ++i) {
|
||||||
|
SkASSERT(plotLocks[i] == fPlotLocks[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +309,53 @@ void GrLayerCache::purge(uint32_t pictureID) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GrLayerCache::purgePlot() {
|
||||||
|
SkDEBUGCODE(GrAutoValidateCache avc(this);)
|
||||||
|
|
||||||
|
GrAtlas::PlotIter iter;
|
||||||
|
GrPlot* plot;
|
||||||
|
for (plot = fAtlas->iterInit(&iter, GrAtlas::kLRUFirst_IterOrder);
|
||||||
|
NULL != plot;
|
||||||
|
plot = iter.prev()) {
|
||||||
|
if (fPlotLocks[plot->id()] > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to find all the layers in 'plot' and remove them.
|
||||||
|
SkTDArray<GrCachedLayer*> toBeRemoved;
|
||||||
|
|
||||||
|
SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
|
||||||
|
for (; !iter.done(); ++iter) {
|
||||||
|
if (plot == (*iter).plot()) {
|
||||||
|
*toBeRemoved.append() = &(*iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < toBeRemoved.count(); ++i) {
|
||||||
|
SkASSERT(!toBeRemoved[i]->locked());
|
||||||
|
|
||||||
|
GrPictureInfo* pictInfo = fPictureHash.find(toBeRemoved[i]->pictureID());
|
||||||
|
SkASSERT(NULL != pictInfo);
|
||||||
|
|
||||||
|
GrAtlas::RemovePlot(&pictInfo->fPlotUsage, plot);
|
||||||
|
|
||||||
|
// Aggressively remove layers and, if now totally uncached, picture info
|
||||||
|
fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
|
||||||
|
SkDELETE(toBeRemoved[i]);
|
||||||
|
|
||||||
|
if (pictInfo->fPlotUsage.isEmpty()) {
|
||||||
|
fPictureHash.remove(pictInfo->fPictureID);
|
||||||
|
SkDELETE(pictInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plot->resetRects();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
class GrPictureDeletionListener : public SkPicture::DeletionListener {
|
class GrPictureDeletionListener : public SkPicture::DeletionListener {
|
||||||
virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{
|
virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{
|
||||||
const GrPictureDeletedMessage message = { pictureID };
|
const GrPictureDeletedMessage message = { pictureID };
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
#define USE_ATLAS 0
|
#define USE_ATLAS 0
|
||||||
|
|
||||||
#include "GrAllocPool.h"
|
|
||||||
#include "GrAtlas.h"
|
#include "GrAtlas.h"
|
||||||
#include "GrPictureUtils.h"
|
#include "GrPictureUtils.h"
|
||||||
#include "GrRect.h"
|
#include "GrRect.h"
|
||||||
@ -47,6 +46,9 @@ public:
|
|||||||
// get a ref to the GrTexture in which they reside. In both cases 'fRect'
|
// get a ref to the GrTexture in which they reside. In both cases 'fRect'
|
||||||
// contains the layer's extent in its texture.
|
// contains the layer's extent in its texture.
|
||||||
// Atlased layers also get a pointer to the plot in which they reside.
|
// Atlased layers also get a pointer to the plot in which they reside.
|
||||||
|
// For non-atlased layers the lock field just corresponds to locking in
|
||||||
|
// the resource cache. For atlased layers it implements an additional level
|
||||||
|
// of locking to allow atlased layers to be reused multiple times.
|
||||||
struct GrCachedLayer {
|
struct GrCachedLayer {
|
||||||
public:
|
public:
|
||||||
// For SkTDynamicHash
|
// For SkTDynamicHash
|
||||||
@ -77,7 +79,8 @@ public:
|
|||||||
: fKey(pictureID, layerID)
|
: fKey(pictureID, layerID)
|
||||||
, fTexture(NULL)
|
, fTexture(NULL)
|
||||||
, fRect(GrIRect16::MakeEmpty())
|
, fRect(GrIRect16::MakeEmpty())
|
||||||
, fPlot(NULL) {
|
, fPlot(NULL)
|
||||||
|
, fLocked(false) {
|
||||||
SkASSERT(SK_InvalidGenID != pictureID && layerID >= 0);
|
SkASSERT(SK_InvalidGenID != pictureID && layerID >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,14 +107,17 @@ public:
|
|||||||
|
|
||||||
bool isAtlased() const { return NULL != fPlot; }
|
bool isAtlased() const { return NULL != fPlot; }
|
||||||
|
|
||||||
|
void setLocked(bool locked) { fLocked = locked; }
|
||||||
|
bool locked() const { return fLocked; }
|
||||||
|
|
||||||
|
SkDEBUGCODE(const GrPlot* plot() const { return fPlot; })
|
||||||
SkDEBUGCODE(void validate(const GrTexture* backingTexture) const;)
|
SkDEBUGCODE(void validate(const GrTexture* backingTexture) const;)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Key fKey;
|
const Key fKey;
|
||||||
|
|
||||||
// fTexture is a ref on the atlasing texture for atlased layers and a
|
// fTexture is a ref on the atlasing texture for atlased layers and a
|
||||||
// ref on a GrTexture for non-atlased textures. In both cases, if this is
|
// ref on a GrTexture for non-atlased textures.
|
||||||
// non-NULL, that means that the texture is locked in the texture cache.
|
|
||||||
GrTexture* fTexture;
|
GrTexture* fTexture;
|
||||||
|
|
||||||
// For both atlased and non-atlased layers 'fRect' contains the bound of
|
// For both atlased and non-atlased layers 'fRect' contains the bound of
|
||||||
@ -122,6 +128,14 @@ private:
|
|||||||
// For atlased layers, fPlot stores the atlas plot in which the layer rests.
|
// For atlased layers, fPlot stores the atlas plot in which the layer rests.
|
||||||
// It is always NULL for non-atlased layers.
|
// It is always NULL for non-atlased layers.
|
||||||
GrPlot* fPlot;
|
GrPlot* fPlot;
|
||||||
|
|
||||||
|
// For non-atlased layers 'fLocked' should always match "NULL != fTexture".
|
||||||
|
// (i.e., if there is a texture it is locked).
|
||||||
|
// For atlased layers, 'fLocked' is true if the layer is in a plot and
|
||||||
|
// actively required for rendering. If the layer is in a plot but not
|
||||||
|
// actively required for rendering, then 'fLocked' is false. If the
|
||||||
|
// layer isn't in a plot then is can never be locked.
|
||||||
|
bool fLocked;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The GrLayerCache caches pre-computed saveLayers for later rendering.
|
// The GrLayerCache caches pre-computed saveLayers for later rendering.
|
||||||
@ -161,9 +175,15 @@ public:
|
|||||||
SkDEBUGCODE(void validate() const;)
|
SkDEBUGCODE(void validate() const;)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const int kAtlasTextureWidth = 1024;
|
||||||
|
static const int kAtlasTextureHeight = 1024;
|
||||||
|
|
||||||
static const int kNumPlotsX = 2;
|
static const int kNumPlotsX = 2;
|
||||||
static const int kNumPlotsY = 2;
|
static const int kNumPlotsY = 2;
|
||||||
|
|
||||||
|
static const int kPlotWidth = kAtlasTextureWidth / kNumPlotsX;
|
||||||
|
static const int kPlotHeight = kAtlasTextureHeight / kNumPlotsY;
|
||||||
|
|
||||||
GrContext* fContext; // pointer back to owning context
|
GrContext* fContext; // pointer back to owning context
|
||||||
SkAutoTDelete<GrAtlas> fAtlas; // TODO: could lazily allocate
|
SkAutoTDelete<GrAtlas> fAtlas; // TODO: could lazily allocate
|
||||||
|
|
||||||
@ -180,12 +200,28 @@ private:
|
|||||||
|
|
||||||
SkAutoTUnref<SkPicture::DeletionListener> fDeletionListener;
|
SkAutoTUnref<SkPicture::DeletionListener> fDeletionListener;
|
||||||
|
|
||||||
|
// This implements a plot-centric locking mechanism (since the atlas
|
||||||
|
// backing texture is always locked). Each layer that is locked (i.e.,
|
||||||
|
// needed for the current rendering) in a plot increments the plot lock
|
||||||
|
// count for that plot. Similarly, once a rendering is complete all the
|
||||||
|
// layers used in it decrement the lock count for the used plots.
|
||||||
|
// Plots with a 0 lock count are open for recycling/purging.
|
||||||
|
int fPlotLocks[kNumPlotsX * kNumPlotsY];
|
||||||
|
|
||||||
void initAtlas();
|
void initAtlas();
|
||||||
GrCachedLayer* createLayer(const SkPicture* picture, int layerID);
|
GrCachedLayer* createLayer(const SkPicture* picture, int layerID);
|
||||||
|
|
||||||
// Remove all the layers (and unlock any resources) associated with 'pictureID'
|
// Remove all the layers (and unlock any resources) associated with 'pictureID'
|
||||||
void purge(uint32_t pictureID);
|
void purge(uint32_t pictureID);
|
||||||
|
|
||||||
|
static bool PlausiblyAtlasable(int width, int height) {
|
||||||
|
return width <= kPlotWidth && height <= kPlotHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find a purgeable plot and clear it out. Return true if a plot
|
||||||
|
// was purged; false otherwise.
|
||||||
|
bool purgePlot();
|
||||||
|
|
||||||
// for testing
|
// for testing
|
||||||
friend class TestingAccess;
|
friend class TestingAccess;
|
||||||
int numLayers() const { return fLayerHash.count(); }
|
int numLayers() const { return fLayerHash.count(); }
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "SkTLazy.h"
|
#include "SkTLazy.h"
|
||||||
#include "SkUtils.h"
|
#include "SkUtils.h"
|
||||||
#include "SkVertState.h"
|
#include "SkVertState.h"
|
||||||
|
#include "SkXfermode.h"
|
||||||
#include "SkErrorInternals.h"
|
#include "SkErrorInternals.h"
|
||||||
|
|
||||||
#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
|
#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
|
||||||
@ -2023,6 +2024,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi
|
|||||||
// TODO: ensure none of the atlased layers contain a clear call!
|
// TODO: ensure none of the atlased layers contain a clear call!
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
paint.setColor(SK_ColorTRANSPARENT);
|
paint.setColor(SK_ColorTRANSPARENT);
|
||||||
|
paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref();
|
||||||
canvas->drawRect(bound, paint);
|
canvas->drawRect(bound, paint);
|
||||||
} else {
|
} else {
|
||||||
canvas->clear(SK_ColorTRANSPARENT);
|
canvas->clear(SK_ColorTRANSPARENT);
|
||||||
|
@ -13,8 +13,6 @@
|
|||||||
#include "SkPictureRecorder.h"
|
#include "SkPictureRecorder.h"
|
||||||
#include "Test.h"
|
#include "Test.h"
|
||||||
|
|
||||||
static const int kNumLayers = 5;
|
|
||||||
|
|
||||||
class TestingAccess {
|
class TestingAccess {
|
||||||
public:
|
public:
|
||||||
static int NumLayers(GrLayerCache* cache) {
|
static int NumLayers(GrLayerCache* cache) {
|
||||||
@ -28,31 +26,53 @@ public:
|
|||||||
// Add several layers to the cache
|
// Add several layers to the cache
|
||||||
static void create_layers(skiatest::Reporter* reporter,
|
static void create_layers(skiatest::Reporter* reporter,
|
||||||
GrLayerCache* cache,
|
GrLayerCache* cache,
|
||||||
const SkPicture& picture) {
|
const SkPicture& picture,
|
||||||
GrCachedLayer* layers[kNumLayers];
|
int numToAdd,
|
||||||
|
int idOffset) {
|
||||||
|
|
||||||
for (int i = 0; i < kNumLayers; ++i) {
|
for (int i = 0; i < numToAdd; ++i) {
|
||||||
layers[i] = cache->findLayerOrCreate(&picture, i);
|
GrCachedLayer* layer = cache->findLayerOrCreate(&picture, idOffset+i);
|
||||||
REPORTER_ASSERT(reporter, NULL != layers[i]);
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
GrCachedLayer* layer = cache->findLayer(&picture, i);
|
GrCachedLayer* temp = cache->findLayer(&picture, idOffset+i);
|
||||||
REPORTER_ASSERT(reporter, layer == layers[i]);
|
REPORTER_ASSERT(reporter, temp == layer);
|
||||||
|
|
||||||
REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == i + 1);
|
REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1);
|
||||||
|
|
||||||
REPORTER_ASSERT(reporter, picture.uniqueID() == layers[i]->pictureID());
|
REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID());
|
||||||
REPORTER_ASSERT(reporter, layers[i]->layerID() == i);
|
REPORTER_ASSERT(reporter, layer->layerID() == idOffset + i);
|
||||||
REPORTER_ASSERT(reporter, NULL == layers[i]->texture());
|
REPORTER_ASSERT(reporter, NULL == layer->texture());
|
||||||
REPORTER_ASSERT(reporter, !layers[i]->isAtlased());
|
REPORTER_ASSERT(reporter, !layer->isAtlased());
|
||||||
}
|
}
|
||||||
|
|
||||||
cache->trackPicture(&picture);
|
cache->trackPicture(&picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lock_layer(skiatest::Reporter* reporter,
|
||||||
|
GrLayerCache* cache,
|
||||||
|
GrCachedLayer* layer) {
|
||||||
|
// Make the layer 512x512 (so it can be atlased)
|
||||||
|
GrTextureDesc desc;
|
||||||
|
desc.fWidth = 512;
|
||||||
|
desc.fHeight = 512;
|
||||||
|
desc.fConfig = kSkia8888_GrPixelConfig;
|
||||||
|
|
||||||
|
bool foundInCache = cache->lock(layer, desc);
|
||||||
|
REPORTER_ASSERT(reporter, !foundInCache);
|
||||||
|
|
||||||
|
foundInCache = cache->lock(layer, desc);
|
||||||
|
REPORTER_ASSERT(reporter, foundInCache);
|
||||||
|
|
||||||
|
REPORTER_ASSERT(reporter, NULL != layer->texture());
|
||||||
|
REPORTER_ASSERT(reporter, layer->locked());
|
||||||
|
}
|
||||||
|
|
||||||
// This test case exercises the public API of the GrLayerCache class.
|
// This test case exercises the public API of the GrLayerCache class.
|
||||||
// In particular it checks its interaction with the resource cache (w.r.t.
|
// In particular it checks its interaction with the resource cache (w.r.t.
|
||||||
// locking & unlocking textures).
|
// locking & unlocking textures).
|
||||||
// TODO: need to add checks on VRAM usage!
|
// TODO: need to add checks on VRAM usage!
|
||||||
DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
||||||
|
static const int kInitialNumLayers = 5;
|
||||||
|
|
||||||
for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
|
for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
|
||||||
GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
|
GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
|
||||||
|
|
||||||
@ -72,24 +92,14 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
|||||||
|
|
||||||
GrLayerCache cache(context);
|
GrLayerCache cache(context);
|
||||||
|
|
||||||
create_layers(reporter, &cache, *picture);
|
create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
|
||||||
|
|
||||||
// Lock the layers making them all 512x512
|
for (int i = 0; i < kInitialNumLayers; ++i) {
|
||||||
GrTextureDesc desc;
|
|
||||||
desc.fWidth = 512;
|
|
||||||
desc.fHeight = 512;
|
|
||||||
desc.fConfig = kSkia8888_GrPixelConfig;
|
|
||||||
|
|
||||||
for (int i = 0; i < kNumLayers; ++i) {
|
|
||||||
GrCachedLayer* layer = cache.findLayer(picture, i);
|
GrCachedLayer* layer = cache.findLayer(picture, i);
|
||||||
REPORTER_ASSERT(reporter, NULL != layer);
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
|
|
||||||
bool foundInCache = cache.lock(layer, desc);
|
lock_layer(reporter, &cache, layer);
|
||||||
REPORTER_ASSERT(reporter, !foundInCache);
|
|
||||||
foundInCache = cache.lock(layer, desc);
|
|
||||||
REPORTER_ASSERT(reporter, foundInCache);
|
|
||||||
|
|
||||||
REPORTER_ASSERT(reporter, NULL != layer->texture());
|
|
||||||
#if USE_ATLAS
|
#if USE_ATLAS
|
||||||
// The first 4 layers should be in the atlas (and thus have non-empty
|
// The first 4 layers should be in the atlas (and thus have non-empty
|
||||||
// rects)
|
// rects)
|
||||||
@ -97,32 +107,34 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
|||||||
REPORTER_ASSERT(reporter, layer->isAtlased());
|
REPORTER_ASSERT(reporter, layer->isAtlased());
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
REPORTER_ASSERT(reporter, !layer->isAtlased());
|
// The 5th layer couldn't fit in the atlas
|
||||||
|
REPORTER_ASSERT(reporter, !layer->isAtlased());
|
||||||
#if USE_ATLAS
|
#if USE_ATLAS
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock the textures
|
// Unlock the textures
|
||||||
for (int i = 0; i < kNumLayers; ++i) {
|
for (int i = 0; i < kInitialNumLayers; ++i) {
|
||||||
GrCachedLayer* layer = cache.findLayer(picture, i);
|
GrCachedLayer* layer = cache.findLayer(picture, i);
|
||||||
REPORTER_ASSERT(reporter, NULL != layer);
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
|
|
||||||
cache.unlock(layer);
|
cache.unlock(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < kNumLayers; ++i) {
|
for (int i = 0; i < kInitialNumLayers; ++i) {
|
||||||
GrCachedLayer* layer = cache.findLayer(picture, i);
|
GrCachedLayer* layer = cache.findLayer(picture, i);
|
||||||
REPORTER_ASSERT(reporter, NULL != layer);
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
|
|
||||||
|
REPORTER_ASSERT(reporter, !layer->locked());
|
||||||
#if USE_ATLAS
|
#if USE_ATLAS
|
||||||
// The first 4 layers should be in the atlas (and thus do not
|
// The first 4 layers should still be in the atlas.
|
||||||
// currently unlock). The final layer should be unlocked.
|
|
||||||
if (i < 4) {
|
if (i < 4) {
|
||||||
REPORTER_ASSERT(reporter, NULL != layer->texture());
|
REPORTER_ASSERT(reporter, NULL != layer->texture());
|
||||||
REPORTER_ASSERT(reporter, layer->isAtlased());
|
REPORTER_ASSERT(reporter, layer->isAtlased());
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
|
// The final layer should be unlocked.
|
||||||
REPORTER_ASSERT(reporter, NULL == layer->texture());
|
REPORTER_ASSERT(reporter, NULL == layer->texture());
|
||||||
REPORTER_ASSERT(reporter, !layer->isAtlased());
|
REPORTER_ASSERT(reporter, !layer->isAtlased());
|
||||||
#if USE_ATLAS
|
#if USE_ATLAS
|
||||||
@ -130,6 +142,41 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Add an additional layer. Since all the layers are unlocked this
|
||||||
|
// will force out the first atlased layer
|
||||||
|
create_layers(reporter, &cache, *picture, 1, kInitialNumLayers);
|
||||||
|
GrCachedLayer* layer = cache.findLayer(picture, kInitialNumLayers);
|
||||||
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
|
|
||||||
|
lock_layer(reporter, &cache, layer);
|
||||||
|
cache.unlock(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < kInitialNumLayers+1; ++i) {
|
||||||
|
GrCachedLayer* layer = cache.findLayer(picture, i);
|
||||||
|
#if USE_ATLAS
|
||||||
|
// 3 old layers plus the new one should be in the atlas.
|
||||||
|
if (1 == i || 2 == i || 3 == i || 5 == i) {
|
||||||
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
|
REPORTER_ASSERT(reporter, !layer->locked());
|
||||||
|
REPORTER_ASSERT(reporter, NULL != layer->texture());
|
||||||
|
REPORTER_ASSERT(reporter, layer->isAtlased());
|
||||||
|
} else if (4 == i) {
|
||||||
|
#endif
|
||||||
|
// The one that was never atlased should still be around
|
||||||
|
REPORTER_ASSERT(reporter, NULL != layer);
|
||||||
|
|
||||||
|
REPORTER_ASSERT(reporter, NULL == layer->texture());
|
||||||
|
REPORTER_ASSERT(reporter, !layer->isAtlased());
|
||||||
|
#if USE_ATLAS
|
||||||
|
} else {
|
||||||
|
// The one bumped out of the atlas (i.e., 0) should be gone
|
||||||
|
REPORTER_ASSERT(reporter, NULL == layer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// Free them all SkGpuDevice-style. This will not free up the
|
// Free them all SkGpuDevice-style. This will not free up the
|
||||||
// atlas' texture but will eliminate all the layers.
|
// atlas' texture but will eliminate all the layers.
|
||||||
@ -142,7 +189,7 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
|||||||
// Test out the GrContext-style purge. This should remove all the layers
|
// Test out the GrContext-style purge. This should remove all the layers
|
||||||
// and the atlas.
|
// and the atlas.
|
||||||
// Re-create the layers
|
// Re-create the layers
|
||||||
create_layers(reporter, &cache, *picture);
|
create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
|
||||||
|
|
||||||
// Free them again GrContext-style. This should free up everything.
|
// Free them again GrContext-style. This should free up everything.
|
||||||
cache.freeAll();
|
cache.freeAll();
|
||||||
@ -153,7 +200,7 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
|||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// Test out the MessageBus-style purge. This will not free the atlas
|
// Test out the MessageBus-style purge. This will not free the atlas
|
||||||
// but should eliminate the free-floating layers.
|
// but should eliminate the free-floating layers.
|
||||||
create_layers(reporter, &cache, *picture);
|
create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
|
||||||
|
|
||||||
picture.reset(NULL);
|
picture.reset(NULL);
|
||||||
cache.processDeletedPictures();
|
cache.processDeletedPictures();
|
||||||
|
Loading…
Reference in New Issue
Block a user