From 42597bc99f00553825843b5ed41e81b121773368 Mon Sep 17 00:00:00 2001 From: robertphillips Date: Fri, 6 Nov 2015 04:14:55 -0800 Subject: [PATCH] Update Layer Hoisting to store its atlas texture in the resource cache BUG=skia:4346 Review URL: https://codereview.chromium.org/1406013006 --- src/core/SkMultiPictureDraw.cpp | 6 +- src/gpu/GrLayerAtlas.cpp | 45 ++++++-- src/gpu/GrLayerAtlas.h | 23 +++- src/gpu/GrLayerCache.cpp | 92 +++++++++++----- src/gpu/GrLayerCache.h | 38 +++++-- src/gpu/GrLayerHoister.cpp | 15 ++- src/gpu/GrLayerHoister.h | 7 ++ src/gpu/GrRectanizer_skyline.h | 4 +- src/gpu/GrResourceCache.h | 47 +++++++- src/gpu/GrTest.cpp | 57 ++++------ src/gpu/SkGpuDevice.cpp | 3 + tests/GpuLayerCacheTest.cpp | 190 +++++++++++++++++++++++++++----- tests/RecordReplaceDrawTest.cpp | 2 +- 13 files changed, 398 insertions(+), 131 deletions(-) diff --git a/src/core/SkMultiPictureDraw.cpp b/src/core/SkMultiPictureDraw.cpp index 4003808310..672bd628b7 100644 --- a/src/core/SkMultiPictureDraw.cpp +++ b/src/core/SkMultiPictureDraw.cpp @@ -116,6 +116,8 @@ void SkMultiPictureDraw::draw(bool flush) { // drawing the canvas that requires them. SkTDArray 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 } diff --git a/src/gpu/GrLayerAtlas.cpp b/src/gpu/GrLayerAtlas.cpp index 3b30607c8c..9beb509f86 100644 --- a/src/gpu/GrLayerAtlas.cpp +++ b/src/gpu/GrLayerAtlas.cpp @@ -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; } diff --git a/src/gpu/GrLayerAtlas.h b/src/gpu/GrLayerAtlas.h index ae08e4527b..e84667b5ea 100644 --- a/src/gpu/GrLayerAtlas.h +++ b/src/gpu/GrLayerAtlas.h @@ -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 fTexture; SkISize fBackingTextureSize; diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp index 9af89a3466..105ee04e53 100644 --- a/src/gpu/GrLayerCache.cpp +++ b/src/gpu/GrLayerCache.cpp @@ -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 drawContext( - fContext->drawContext(fAtlas->getTexture()->asRenderTarget())); + if (fAtlas->getTextureOrNull()) { + SkAutoTUnref 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::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 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"); diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h index 914d0d5a58..a606681896 100644 --- a/src/gpu/GrLayerCache.h +++ b/src/gpu/GrLayerCache.h @@ -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 fAtlas; // TODO: could lazily allocate + SkAutoTDelete 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) { diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp index 2bf1abb5fd..004e4d0e6e 100644 --- a/src/gpu/GrLayerHoister.cpp +++ b/src/gpu/GrLayerHoister.cpp @@ -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(); } diff --git a/src/gpu/GrLayerHoister.h b/src/gpu/GrLayerHoister.h index f30c53c038..13cac5173a 100644 --- a/src/gpu/GrLayerHoister.h +++ b/src/gpu/GrLayerHoister.h @@ -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. diff --git a/src/gpu/GrRectanizer_skyline.h b/src/gpu/GrRectanizer_skyline.h index a06bba00fb..576b1fc62a 100644 --- a/src/gpu/GrRectanizer_skyline.h +++ b/src/gpu/GrRectanizer_skyline.h @@ -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); diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h index 2412174121..ed2affeae3 100644 --- a/src/gpu/GrResourceCache.h +++ b/src/gpu/GrResourceCache.h @@ -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 diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp index 4adc3f7b7b..eafe902a47 100644 --- a/src/gpu/GrTest.cpp +++ b/src/gpu/GrTest.cpp @@ -109,47 +109,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; @@ -157,8 +137,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)); diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 6a317d5edc..3a8e751f84 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1986,6 +1986,8 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture SkMatrix initialMatrix = mainCanvas->getTotalMatrix(); + GrLayerHoister::Begin(fContext); + GrLayerHoister::FindLayersToAtlas(fContext, mainPicture, initialMatrix, clipBounds, @@ -2014,6 +2016,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 diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp index 95ac7b641d..b91c5b01b0 100644 --- a/tests/GpuLayerCacheTest.cpp +++ b/tests/GpuLayerCacheTest.cpp @@ -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,10 @@ 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.fWidth = TestingAccess::PlotSize().fWidth; + desc.fHeight = TestingAccess::PlotSize().fHeight; desc.fConfig = kSkia8888_GrPixelConfig; bool needsRerendering; @@ -95,9 +108,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 +128,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 picture(recorder.endRecording()); + SkAutoTUnref 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 +217,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 +255,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 +287,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 +306,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 } } diff --git a/tests/RecordReplaceDrawTest.cpp b/tests/RecordReplaceDrawTest.cpp index 225185ddd3..46f6af3c13 100644 --- a/tests/RecordReplaceDrawTest.cpp +++ b/tests/RecordReplaceDrawTest.cpp @@ -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;