Update Layer Hoisting to store its atlas texture in the resource cache

BUG=skia:4346

Committed: https://skia.googlesource.com/skia/+/42597bc99f00553825843b5ed41e81b121773368

Review URL: https://codereview.chromium.org/1406013006
This commit is contained in:
robertphillips 2015-11-09 13:51:06 -08:00 committed by Commit bot
parent e5911c9c5b
commit 60029a5397
13 changed files with 399 additions and 131 deletions

View File

@ -116,6 +116,8 @@ void SkMultiPictureDraw::draw(bool flush) {
// drawing the canvas that requires them.
SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled;
GrLayerHoister::Begin(context);
for (int i = 0; i < count; ++i) {
const DrawData& data = fGPUDrawData[i];
// we only expect 1 context for all the canvases
@ -199,9 +201,7 @@ void SkMultiPictureDraw::draw(bool flush) {
#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
GrLayerHoister::UnlockLayers(context, atlasedNeedRendering);
GrLayerHoister::UnlockLayers(context, atlasedRecycled);
#if !GR_CACHE_HOISTED_LAYERS
GrLayerHoister::PurgeCache(context);
#endif
GrLayerHoister::End(context);
#endif
}

View File

@ -6,6 +6,7 @@
* found in the LICENSE file.
*/
#include "GrGpuResourcePriv.h"
#include "GrLayerAtlas.h"
#include "GrRectanizer.h"
#include "GrTextureProvider.h"
@ -43,6 +44,32 @@ void GrLayerAtlas::Plot::reset() {
}
///////////////////////////////////////////////////////////////////////////////
GR_DECLARE_STATIC_UNIQUE_KEY(gLayerAtlasKey);
static const GrUniqueKey& get_layer_atlas_key() {
GR_DEFINE_STATIC_UNIQUE_KEY(gLayerAtlasKey);
return gLayerAtlasKey;
}
bool GrLayerAtlas::reattachBackingTexture() {
SkASSERT(!fTexture);
fTexture.reset(fTexProvider->findAndRefTextureByUniqueKey(get_layer_atlas_key()));
return SkToBool(fTexture);
}
void GrLayerAtlas::createBackingTexture() {
SkASSERT(!fTexture);
GrSurfaceDesc desc;
desc.fFlags = fFlags;
desc.fWidth = fBackingTextureSize.width();
desc.fHeight = fBackingTextureSize.height();
desc.fConfig = fPixelConfig;
fTexture.reset(fTexProvider->createTexture(desc, true, nullptr, 0));
fTexture->resourcePriv().setUniqueKey(get_layer_atlas_key());
}
GrLayerAtlas::GrLayerAtlas(GrTextureProvider* texProvider, GrPixelConfig config,
GrSurfaceFlags flags,
@ -52,7 +79,6 @@ GrLayerAtlas::GrLayerAtlas(GrTextureProvider* texProvider, GrPixelConfig config,
fPixelConfig = config;
fFlags = flags;
fBackingTextureSize = backingTextureSize;
fTexture = nullptr;
int textureWidth = fBackingTextureSize.width();
int textureHeight = fBackingTextureSize.height();
@ -81,8 +107,14 @@ GrLayerAtlas::GrLayerAtlas(GrTextureProvider* texProvider, GrPixelConfig config,
}
}
void GrLayerAtlas::resetPlots() {
PlotIter iter;
for (Plot* plot = iter.init(fPlotList, PlotIter::kHead_IterStart); plot; plot = iter.next()) {
plot->reset();
}
}
GrLayerAtlas::~GrLayerAtlas() {
SkSafeUnref(fTexture);
delete[] fPlotArray;
}
@ -111,14 +143,7 @@ GrLayerAtlas::Plot* GrLayerAtlas::addToAtlas(ClientPlotUsage* usage,
// before we get a new plot, make sure we have a backing texture
if (nullptr == fTexture) {
// TODO: Update this to use the cache rather than directly creating a texture.
GrSurfaceDesc desc;
desc.fFlags = fFlags;
desc.fWidth = fBackingTextureSize.width();
desc.fHeight = fBackingTextureSize.height();
desc.fConfig = fPixelConfig;
fTexture = fTexProvider->createTexture(desc, true, nullptr, 0);
this->createBackingTexture();
if (nullptr == fTexture) {
return nullptr;
}

View File

@ -9,15 +9,13 @@
#ifndef GrLayerAtlas_DEFINED
#define GrLayerAtlas_DEFINED
#include "GrTypes.h"
#include "GrTexture.h"
#include "SkPoint.h"
#include "SkSize.h"
#include "SkTDArray.h"
#include "SkTInternalLList.h"
class GrLayerAtlas;
class GrTexture;
class GrTextureProvider;
class GrRectanizer;
@ -109,10 +107,23 @@ public:
// nullptr is returned if there is no more space in the atlas.
Plot* addToAtlas(ClientPlotUsage*, int width, int height, SkIPoint16* loc);
GrTexture* getTexture() const {
GrTexture* getTextureOrNull() const {
return fTexture;
}
GrTexture* getTexture() const {
SkASSERT(fTexture);
return fTexture;
}
bool reattachBackingTexture();
void detachBackingTexture() {
fTexture.reset(nullptr);
}
void resetPlots();
enum IterOrder {
kLRUFirst_IterOrder,
kMRUFirst_IterOrder
@ -127,12 +138,14 @@ public:
}
private:
void createBackingTexture();
void makeMRU(Plot* plot);
GrTextureProvider* fTexProvider;
GrPixelConfig fPixelConfig;
GrSurfaceFlags fFlags;
GrTexture* fTexture;
SkAutoTUnref<GrTexture> fTexture;
SkISize fBackingTextureSize;

View File

@ -27,9 +27,11 @@ void GrCachedLayer::validate(const GrTexture* backingTexture) const {
SkASSERT(fRect.isEmpty());
SkASSERT(nullptr == fPlot);
SkASSERT(!fLocked); // layers without a texture cannot be locked
SkASSERT(!fAtlased); // can't be atlased if it doesn't have a texture
}
if (fPlot) {
SkASSERT(fAtlased);
// If a layer has a plot (i.e., is atlased) then it must point to
// the backing texture. Additionally, its rect should be non-empty.
SkASSERT(fTexture && backingTexture == fTexture);
@ -119,8 +121,10 @@ void GrLayerCache::freeAll() {
}
fLayerHash.rewind();
// The atlas only lets go of its texture when the atlas is deleted.
fAtlas.free();
if (fAtlas) {
fAtlas->resetPlots();
fAtlas->detachBackingTexture();
}
}
GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID,
@ -167,7 +171,7 @@ GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
bool GrLayerCache::tryToAtlas(GrCachedLayer* layer,
const GrSurfaceDesc& desc,
bool* needsRendering) {
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTexture() : nullptr, layer);)
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTextureOrNull() : nullptr, layer);)
SkASSERT(PlausiblyAtlasable(desc.fWidth, desc.fHeight));
SkASSERT(0 == desc.fSampleCnt);
@ -217,7 +221,7 @@ bool GrLayerCache::tryToAtlas(GrCachedLayer* layer,
// The layer was successfully added to the atlas
const SkIRect bounds = SkIRect::MakeXYWH(loc.fX, loc.fY,
desc.fWidth, desc.fHeight);
layer->setTexture(fAtlas->getTexture(), bounds);
layer->setTexture(fAtlas->getTexture(), bounds, true);
layer->setPlot(plot);
layer->setLocked(true);
this->incPlotLock(layer->plot()->id());
@ -227,7 +231,7 @@ bool GrLayerCache::tryToAtlas(GrCachedLayer* layer,
// 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()) {
if (!this->purgePlots(true)) {
break; // We weren't able to purge any plots
}
}
@ -260,14 +264,14 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrSurfaceDesc& desc, bool* n
return false;
}
layer->setTexture(tex, SkIRect::MakeWH(desc.fWidth, desc.fHeight));
layer->setTexture(tex, SkIRect::MakeWH(desc.fWidth, desc.fHeight), false);
layer->setLocked(true);
*needsRendering = true;
return true;
}
void GrLayerCache::unlock(GrCachedLayer* layer) {
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTexture() : nullptr, layer);)
SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTextureOrNull() : nullptr, layer);)
if (nullptr == layer || !layer->locked()) {
// invalid or not locked
@ -299,11 +303,11 @@ void GrLayerCache::unlock(GrCachedLayer* layer) {
}
layer->setPlot(nullptr);
layer->setTexture(nullptr, SkIRect::MakeEmpty());
layer->setTexture(nullptr, SkIRect::MakeEmpty(), false);
#endif
} else {
layer->setTexture(nullptr, SkIRect::MakeEmpty());
layer->setTexture(nullptr, SkIRect::MakeEmpty(), false);
}
layer->setLocked(false);
@ -318,7 +322,7 @@ void GrLayerCache::validate() const {
for (; !iter.done(); ++iter) {
const GrCachedLayer* layer = &(*iter);
layer->validate(fAtlas.get() ? fAtlas->getTexture() : nullptr);
layer->validate(fAtlas.get() ? fAtlas->getTextureOrNull() : nullptr);
const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
if (!pictInfo) {
@ -389,10 +393,11 @@ void GrLayerCache::purge(uint32_t pictureID) {
}
}
bool GrLayerCache::purgePlot() {
bool GrLayerCache::purgePlots(bool justOne) {
SkDEBUGCODE(GrAutoValidateCache avc(this);)
SkASSERT(fAtlas);
bool anyPurged = false;
GrLayerAtlas::PlotIter iter;
GrLayerAtlas::Plot* plot;
for (plot = fAtlas->iterInit(&iter, GrLayerAtlas::kLRUFirst_IterOrder);
@ -402,11 +407,14 @@ bool GrLayerCache::purgePlot() {
continue;
}
anyPurged = true;
this->purgePlot(plot);
return true;
if (justOne) {
break;
}
}
return false;
return anyPurged;
}
void GrLayerCache::purgePlot(GrLayerAtlas::Plot* plot) {
@ -455,27 +463,57 @@ void GrLayerCache::purgeAll() {
return;
}
GrLayerAtlas::PlotIter iter;
GrLayerAtlas::Plot* plot;
for (plot = fAtlas->iterInit(&iter, GrLayerAtlas::kLRUFirst_IterOrder);
plot;
plot = iter.prev()) {
SkASSERT(0 == fPlotLocks[plot->id()]);
this->purgePlot(plot);
}
this->purgePlots(false); // clear them all out
SkASSERT(0 == fPictureHash.count());
SkAutoTUnref<GrDrawContext> drawContext(
fContext->drawContext(fAtlas->getTexture()->asRenderTarget()));
if (fAtlas->getTextureOrNull()) {
SkAutoTUnref<GrDrawContext> drawContext(
fContext->drawContext(fAtlas->getTexture()->asRenderTarget()));
if (drawContext) {
drawContext->discard();
if (drawContext) {
drawContext->discard();
}
}
}
#endif
void GrLayerCache::begin() {
if (!fAtlas) {
return;
}
if (!fAtlas->reattachBackingTexture()) {
// We weren't able to re-attach. Clear out all the atlased layers.
this->purgePlots(false);
SkASSERT(0 == fPictureHash.count());
}
#ifdef SK_DEBUG
else {
// we've reattached - everything had better make sense
SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
for (; !iter.done(); ++iter) {
GrCachedLayer* layer = &(*iter);
if (layer->isAtlased()) {
SkASSERT(fAtlas->getTexture() == layer->texture());
}
}
}
#endif
}
void GrLayerCache::end() {
if (!fAtlas) {
return;
}
// Adding this call will clear out all the layers in the atlas
//this->purgePlots(false);
fAtlas->detachBackingTexture();
}
void GrLayerCache::processDeletedPictures() {
SkTArray<SkPicture::DeletionMessage> deletedPictures;
fPictDeletionInbox.poll(&deletedPictures);
@ -489,7 +527,7 @@ void GrLayerCache::processDeletedPictures() {
void GrLayerCache::writeLayersToDisk(const SkString& dirName) {
if (fAtlas) {
GrTexture* atlasTexture = fAtlas->getTexture();
GrTexture* atlasTexture = fAtlas->getTextureOrNull();
if (nullptr != atlasTexture) {
SkString fileName(dirName);
fileName.append("\\atlas.png");

View File

@ -165,6 +165,7 @@ public:
, fPaint(paint ? new SkPaint(*paint) : nullptr)
, fFilter(nullptr)
, fTexture(nullptr)
, fAtlased(false)
, fRect(SkIRect::MakeEmpty())
, fPlot(nullptr)
, fUses(0)
@ -180,7 +181,9 @@ public:
}
~GrCachedLayer() {
SkSafeUnref(fTexture);
if (!fAtlased) {
SkSafeUnref(fTexture);
}
SkSafeUnref(fFilter);
delete fPaint;
}
@ -195,8 +198,15 @@ public:
const SkIRect& srcIR() const { return fSrcIR; }
const SkIRect& dstIR() const { return fDstIR; }
int stop() const { return fStop; }
void setTexture(GrTexture* texture, const SkIRect& rect) {
SkRefCnt_SafeAssign(fTexture, texture);
void setTexture(GrTexture* texture, const SkIRect& rect, bool atlased) {
if (texture && !atlased) {
texture->ref(); // non-atlased textures carry a ref
}
if (fTexture && !fAtlased) {
fTexture->unref(); // non-atlased textures carry a ref
}
fTexture = texture;
fAtlased = atlased;
fRect = rect;
if (!fTexture) {
fLocked = false;
@ -216,7 +226,7 @@ public:
}
GrLayerAtlas::Plot* plot() { return fPlot; }
bool isAtlased() const { return SkToBool(fPlot); }
bool isAtlased() const { SkASSERT(fAtlased == SkToBool(fPlot)); return fAtlased; }
void setLocked(bool locked) { fLocked = locked; }
bool locked() const { return fLocked; }
@ -252,6 +262,10 @@ private:
// ref on a GrTexture for non-atlased textures.
GrTexture* fTexture;
// true if this layer is in the atlas (and 'fTexture' doesn't carry a ref)
// and false if the layer is a free floater (and carries a ref).
bool fAtlased;
// For both atlased and non-atlased layers 'fRect' contains the bound of
// the layer in whichever texture it resides. It is empty when 'fTexture'
// is nullptr.
@ -285,10 +299,9 @@ private:
// The GrLayerCache caches pre-computed saveLayers for later rendering.
// Non-atlased layers are stored in their own GrTexture while the atlased
// layers share a single GrTexture.
// Unlike the GrFontCache, the GrTexture atlas only has one GrAtlas (for 8888)
// and one GrPlot (for the entire atlas). As such, the GrLayerCache
// roughly combines the functionality of the GrFontCache and GrTextStrike
// classes.
// Unlike the GrFontCache, the GrLayerCache only has one atlas (for 8888).
// As such, the GrLayerCache roughly combines the functionality of the
// GrFontCache and GrTextStrike classes.
class GrLayerCache {
public:
GrLayerCache(GrContext*);
@ -346,6 +359,9 @@ public:
return width <= kPlotWidth && height <= kPlotHeight;
}
void begin();
void end();
#if !GR_CACHE_HOISTED_LAYERS
void purgeAll();
#endif
@ -361,7 +377,7 @@ private:
static const int kPlotHeight = kAtlasTextureHeight / kNumPlotsY;
GrContext* fContext; // pointer back to owning context
SkAutoTDelete<GrLayerAtlas> fAtlas; // TODO: could lazily allocate
SkAutoTDelete<GrLayerAtlas> fAtlas; // lazily allocated
// We cache this information here (rather then, say, on the owning picture)
// because we want to be able to clean it up as needed (e.g., if a picture
@ -397,9 +413,9 @@ private:
void purgePlot(GrLayerAtlas::Plot* plot);
// Try to find a purgeable plot and clear it out. Return true if a plot
// Either purge all un-locked plots or just one. Return true if >= 1 plot
// was purged; false otherwise.
bool purgePlot();
bool purgePlots(bool justOne);
void incPlotLock(int plotIdx) { ++fPlotLocks[plotIdx]; }
void decPlotLock(int plotIdx) {

View File

@ -322,7 +322,7 @@ void GrLayerHoister::FilterLayer(GrContext* context,
}
SkIRect newRect = SkIRect::MakeWH(filteredBitmap.width(), filteredBitmap.height());
layer->setTexture(filteredBitmap.getTexture(), newRect);
layer->setTexture(filteredBitmap.getTexture(), newRect, false);
layer->setOffset(offset);
}
@ -380,13 +380,22 @@ void GrLayerHoister::UnlockLayers(GrContext* context,
SkDEBUGCODE(layerCache->validate();)
}
void GrLayerHoister::PurgeCache(GrContext* context) {
#if !GR_CACHE_HOISTED_LAYERS
void GrLayerHoister::Begin(GrContext* context) {
GrLayerCache* layerCache = context->getLayerCache();
layerCache->begin();
}
void GrLayerHoister::End(GrContext* context) {
GrLayerCache* layerCache = context->getLayerCache();
#if !GR_CACHE_HOISTED_LAYERS
// This code completely clears out the atlas. It is required when
// caching is disabled so the atlas doesn't fill up and force more
// free floating layers
layerCache->purgeAll();
#endif
layerCache->end();
}

View File

@ -33,6 +33,13 @@ public:
// UnlockLayers should be called once to allow the texture resources to be recycled
class GrLayerHoister {
public:
/** Attempt to reattach layers that may have been atlased in the past
*/
static void Begin(GrContext* context);
/** Release cache resources
*/
static void End(GrContext* context);
/** Find the layers in 'topLevelPicture' that can be atlased. Note that the discovered
layers can be inside nested sub-pictures.

View File

@ -19,9 +19,9 @@ public:
this->reset();
}
virtual ~GrRectanizerSkyline() { }
~GrRectanizerSkyline() override { }
void reset() override{
void reset() override {
fAreaSoFar = 0;
fSkyline.reset();
SkylineSegment* seg = fSkyline.append(1);

View File

@ -182,7 +182,52 @@ public:
void notifyFlushOccurred();
#if GR_GPU_STATS
#if GR_CACHE_STATS
struct Stats {
int fTotal;
int fNumPurgeable;
int fNumNonPurgeable;
int fScratch;
int fExternal;
int fBorrowed;
int fAdopted;
size_t fUnbudgetedSize;
Stats() { this->reset(); }
void reset() {
fTotal = 0;
fNumPurgeable = 0;
fNumNonPurgeable = 0;
fScratch = 0;
fExternal = 0;
fBorrowed = 0;
fAdopted = 0;
fUnbudgetedSize = 0;
}
void update(GrGpuResource* resource) {
if (resource->cacheAccess().isScratch()) {
++fScratch;
}
if (resource->cacheAccess().isExternal()) {
++fExternal;
}
if (resource->cacheAccess().isBorrowed()) {
++fBorrowed;
}
if (resource->cacheAccess().isAdopted()) {
++fAdopted;
}
if (!resource->resourcePriv().isBudgeted()) {
fUnbudgetedSize += resource->gpuMemorySize();
}
}
};
void getStats(Stats*) const;
void dumpStats(SkString*) const;
#endif

View File

@ -140,47 +140,27 @@ void GrGpu::Stats::dump(SkString* out) {
#endif
#if GR_CACHE_STATS
void GrResourceCache::getStats(Stats* stats) const {
stats->reset();
stats->fTotal = this->getResourceCount();
stats->fNumNonPurgeable = fNonpurgeableResources.count();
stats->fNumPurgeable = fPurgeableQueue.count();
for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
stats->update(fNonpurgeableResources[i]);
}
for (int i = 0; i < fPurgeableQueue.count(); ++i) {
stats->update(fPurgeableQueue.at(i));
}
}
void GrResourceCache::dumpStats(SkString* out) const {
this->validate();
int locked = fNonpurgeableResources.count();
struct Stats {
int fScratch;
int fExternal;
int fBorrowed;
int fAdopted;
size_t fUnbudgetedSize;
Stats() : fScratch(0), fExternal(0), fBorrowed(0), fAdopted(0), fUnbudgetedSize(0) {}
void update(GrGpuResource* resource) {
if (resource->cacheAccess().isScratch()) {
++fScratch;
}
if (resource->cacheAccess().isExternal()) {
++fExternal;
}
if (resource->cacheAccess().isBorrowed()) {
++fBorrowed;
}
if (resource->cacheAccess().isAdopted()) {
++fAdopted;
}
if (!resource->resourcePriv().isBudgeted()) {
fUnbudgetedSize += resource->gpuMemorySize();
}
}
};
Stats stats;
for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
stats.update(fNonpurgeableResources[i]);
}
for (int i = 0; i < fPurgeableQueue.count(); ++i) {
stats.update(fPurgeableQueue.at(i));
}
this->getStats(&stats);
float countUtilization = (100.f * fBudgetedCount) / fMaxCount;
float byteUtilization = (100.f * fBudgetedBytes) / fMaxBytes;
@ -188,8 +168,9 @@ void GrResourceCache::dumpStats(SkString* out) const {
out->appendf("Budget: %d items %d bytes\n", fMaxCount, (int)fMaxBytes);
out->appendf("\t\tEntry Count: current %d"
" (%d budgeted, %d external(%d borrowed, %d adopted), %d locked, %d scratch %.2g%% full), high %d\n",
this->getResourceCount(), fBudgetedCount, stats.fExternal, stats.fBorrowed,
stats.fAdopted, locked, stats.fScratch, countUtilization, fHighWaterCount);
stats.fTotal, fBudgetedCount, stats.fExternal, stats.fBorrowed,
stats.fAdopted, stats.fNumNonPurgeable, stats.fScratch, countUtilization,
fHighWaterCount);
out->appendf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full, %d unbudgeted) high %d\n",
SkToInt(fBytes), SkToInt(fBudgetedBytes), byteUtilization,
SkToInt(stats.fUnbudgetedSize), SkToInt(fHighWaterBytes));

View File

@ -2028,6 +2028,8 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture
SkMatrix initialMatrix = mainCanvas->getTotalMatrix();
GrLayerHoister::Begin(fContext);
GrLayerHoister::FindLayersToAtlas(fContext, mainPicture,
initialMatrix,
clipBounds,
@ -2056,6 +2058,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture
GrLayerHoister::UnlockLayers(fContext, recycled);
GrLayerHoister::UnlockLayers(fContext, atlasedNeedRendering);
GrLayerHoister::UnlockLayers(fContext, atlasedRecycled);
GrLayerHoister::End(fContext);
return true;
#else

View File

@ -10,11 +10,24 @@
#include "GrContext.h"
#include "GrContextFactory.h"
#include "GrLayerCache.h"
#include "GrResourceCache.h"
#include "SkPictureRecorder.h"
#include "Test.h"
class TestingAccess {
public:
static int NumPlots() {
return GrLayerCache::kNumPlotsX * GrLayerCache::kNumPlotsY;
}
static SkISize PlotSize() {
return SkISize::Make(GrLayerCache::kAtlasTextureWidth / GrLayerCache::kNumPlotsX,
GrLayerCache::kAtlasTextureHeight / GrLayerCache::kNumPlotsY);
}
static GrTexture* GetBackingTexture(GrLayerCache* cache) {
return cache->fAtlas->getTextureOrNull();
}
static int NumLayers(GrLayerCache* cache) {
return cache->numLayers();
}
@ -39,17 +52,17 @@ static void create_layers(skiatest::Reporter* reporter,
int idOffset) {
for (int i = 0; i < numToAdd; ++i) {
int indices[1] = { idOffset+i+1 };
int key[1] = { idOffset+i+1 };
GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(),
idOffset+i+1, idOffset+i+2,
SkIRect::MakeEmpty(),
SkIRect::MakeEmpty(),
SkMatrix::I(),
indices, 1,
key, 1,
nullptr);
REPORTER_ASSERT(reporter, layer);
GrCachedLayer* temp = TestingAccess::Find(cache, picture.uniqueID(), SkMatrix::I(),
indices, 1);
key, 1);
REPORTER_ASSERT(reporter, temp == layer);
REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1);
@ -66,10 +79,11 @@ static void create_layers(skiatest::Reporter* reporter,
static void lock_layer(skiatest::Reporter* reporter,
GrLayerCache* cache,
GrCachedLayer* layer) {
// Make the layer 512x512 (so it can be atlased)
// Make each layer big enough to consume one whole plot in the atlas
GrSurfaceDesc desc;
desc.fWidth = 512;
desc.fHeight = 512;
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fWidth = TestingAccess::PlotSize().fWidth;
desc.fHeight = TestingAccess::PlotSize().fHeight;
desc.fConfig = kSkia8888_GrPixelConfig;
bool needsRerendering;
@ -95,9 +109,14 @@ static void lock_layer(skiatest::Reporter* reporter,
// locking & unlocking textures).
// TODO: need to add checks on VRAM usage!
DEF_GPUTEST(GpuLayerCache, reporter, factory) {
static const int kInitialNumLayers = 5;
// Add one more layer than can fit in the atlas
static const int kInitialNumLayers = TestingAccess::NumPlots() + 1;
for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
#if GR_CACHE_STATS
GrResourceCache::Stats stats;
#endif
for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
@ -110,50 +129,73 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
continue;
}
SkPictureRecorder recorder;
SkCanvas* c = recorder.beginRecording(1, 1);
// Draw something, anything, to prevent an empty-picture optimization,
// which is a singleton and never purged.
c->drawRect(SkRect::MakeWH(1,1), SkPaint());
SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
SkAutoTUnref<const SkPicture> picture;
{
SkPictureRecorder recorder;
SkCanvas* c = recorder.beginRecording(1, 1);
// Draw something, anything, to prevent an empty-picture optimization,
// which is a singleton and never purged.
c->drawRect(SkRect::MakeWH(1,1), SkPaint());
picture.reset(recorder.endRecording());
}
GrResourceCache* resourceCache = context->getResourceCache();
GrLayerCache cache(context);
create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
for (int i = 0; i < kInitialNumLayers; ++i) {
int indices[1] = { i + 1 };
int key[1] = { i + 1 };
GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
indices, 1);
key, 1);
REPORTER_ASSERT(reporter, layer);
lock_layer(reporter, &cache, layer);
// The first 4 layers should be in the atlas (and thus have non-empty
// rects)
if (i < 4) {
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
#endif
// The first 4 layers should be in the atlas (and thus have non-empty rects)
if (i < TestingAccess::NumPlots()) {
REPORTER_ASSERT(reporter, layer->isAtlased());
#if GR_CACHE_STATS
REPORTER_ASSERT(reporter, 1 == stats.fTotal);
#endif
} else {
// The 5th layer couldn't fit in the atlas
REPORTER_ASSERT(reporter, !layer->isAtlased());
#if GR_CACHE_STATS
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
#endif
}
}
// Unlock the textures
for (int i = 0; i < kInitialNumLayers; ++i) {
int indices[1] = { i+1 };
int key[1] = { i+1 };
GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
indices, 1);
key, 1);
REPORTER_ASSERT(reporter, layer);
cache.removeUse(layer);
}
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
// The floating layer is purgeable the cache is not
REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
#endif
for (int i = 0; i < kInitialNumLayers; ++i) {
int indices[1] = { i+1 };
int key[1] = { i+1 };
GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
indices, 1);
key, 1);
REPORTER_ASSERT(reporter, layer);
// All the layers should be unlocked
@ -176,14 +218,37 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
#endif
}
// Let go of the backing texture
cache.end();
REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
// Now both the floater and the atlas are purgeable
REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
#endif
// re-attach to the backing texture
cache.begin();
REPORTER_ASSERT(reporter, TestingAccess::GetBackingTexture(&cache));
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
// The atlas is restored to being non-purgeable
REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
#endif
{
int indices[1] = { kInitialNumLayers+1 };
int key[1] = { kInitialNumLayers+1 };
// 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 = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
indices, 1);
key, 1);
REPORTER_ASSERT(reporter, layer);
lock_layer(reporter, &cache, layer);
@ -191,10 +256,10 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
}
for (int i = 0; i < kInitialNumLayers+1; ++i) {
int indices[1] = { i+1 };
int key[1] = { i+1 };
GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
indices, 1);
key, 1);
#if GR_CACHE_HOISTED_LAYERS
// 3 old layers plus the new one should be in the atlas.
if (1 == i || 2 == i || 3 == i || 5 == i) {
@ -223,7 +288,14 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
TestingAccess::Purge(&cache, picture->uniqueID());
REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
// TODO: add VRAM/resource cache check here
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
// Atlas isn't purgeable
REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
#endif
//--------------------------------------------------------------------
// Test out the GrContext-style purge. This should remove all the layers
@ -235,18 +307,77 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
cache.freeAll();
REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
// TODO: add VRAM/resource cache check here
REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
#endif
// Purge the resource cache ...
resourceCache->purgeAllUnlocked();
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 0 == stats.fTotal);
#endif
// and try to re-attach to the backing texture. This should fail
cache.begin();
REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
//--------------------------------------------------------------------
// Test out the MessageBus-style purge. This will not free the atlas
// but should eliminate the free-floating layers.
create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
// Allocate/use the layers
for (int i = 0; i < kInitialNumLayers; ++i) {
int key[1] = { i + 1 };
GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
key, 1);
REPORTER_ASSERT(reporter, layer);
lock_layer(reporter, &cache, layer);
}
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
REPORTER_ASSERT(reporter, 2 == stats.fNumNonPurgeable);
#endif
// Unlock the textures
for (int i = 0; i < kInitialNumLayers; ++i) {
int key[1] = { i+1 };
GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
key, 1);
REPORTER_ASSERT(reporter, layer);
cache.removeUse(layer);
}
picture.reset(nullptr);
cache.processDeletedPictures();
REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
// TODO: add VRAM/resource cache check here
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
#endif
cache.end();
#if GR_CACHE_STATS
resourceCache->getStats(&stats);
REPORTER_ASSERT(reporter, 2 == stats.fTotal);
REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
#endif
}
}

View File

@ -125,7 +125,7 @@ void test_replacements(skiatest::Reporter* r, GrContext* context, bool doReplace
desc.fSampleCnt = 0;
texture.reset(context->textureProvider()->createTexture(desc, false, nullptr, 0));
layer->setTexture(texture, SkIRect::MakeWH(kWidth, kHeight));
layer->setTexture(texture, SkIRect::MakeWH(kWidth, kHeight), false);
}
SkRecord rerecord;