aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar robertphillips <robertphillips@google.com>2015-11-09 13:51:06 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-11-09 13:51:06 -0800
commit60029a5397f75aae4bdb994f26bd297edc3e433c (patch)
treeecfe56285b4e3810fb864bd3cb2c07d1855d9638
parente5911c9c5b859d60208f4b9ac4bf2a638f4bc35f (diff)
Update Layer Hoisting to store its atlas texture in the resource cache
-rw-r--r--src/core/SkMultiPictureDraw.cpp6
-rw-r--r--src/gpu/GrLayerAtlas.cpp45
-rw-r--r--src/gpu/GrLayerAtlas.h21
-rw-r--r--src/gpu/GrLayerCache.cpp92
-rw-r--r--src/gpu/GrLayerCache.h38
-rw-r--r--src/gpu/GrLayerHoister.cpp15
-rw-r--r--src/gpu/GrLayerHoister.h7
-rw-r--r--src/gpu/GrRectanizer_skyline.h4
-rw-r--r--src/gpu/GrResourceCache.h47
-rw-r--r--src/gpu/GrTest.cpp55
-rw-r--r--src/gpu/SkGpuDevice.cpp3
-rw-r--r--tests/GpuLayerCacheTest.cpp191
-rw-r--r--tests/RecordReplaceDrawTest.cpp2
13 files changed, 397 insertions, 129 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<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
}
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* 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;
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<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");
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<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) {
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 7a44942a16..2889e9709e 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -140,47 +140,27 @@ void GrGpu::Stats::dump(SkString* out) {
#endif
#if GR_CACHE_STATS
-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();
- }
- }
- };
+void GrResourceCache::getStats(Stats* stats) const {
+ stats->reset();
- Stats stats;
+ stats->fTotal = this->getResourceCount();
+ stats->fNumNonPurgeable = fNonpurgeableResources.count();
+ stats->fNumPurgeable = fPurgeableQueue.count();
for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
- stats.update(fNonpurgeableResources[i]);
+ stats->update(fNonpurgeableResources[i]);
}
for (int i = 0; i < fPurgeableQueue.count(); ++i) {
- stats.update(fPurgeableQueue.at(i));
+ stats->update(fPurgeableQueue.at(i));
}
+}
+
+void GrResourceCache::dumpStats(SkString* out) const {
+ this->validate();
+
+ Stats stats;
+
+ 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));
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index c97d24809a..36e0342f78 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -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
diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp
index 95ac7b641d..3d3a507381 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,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;
+
+#if GR_CACHE_STATS
+ GrResourceCache::Stats stats;
+#endif
- for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
+ 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
}
}
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;