aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar robertphillips <robertphillips@google.com>2014-07-22 10:18:06 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-07-22 10:18:06 -0700
commitd771f6bc273457bc7aa95938ac326dfbbf876e1a (patch)
tree9a88b85ac2a0c044c9a160a0f960b5d7f761267f
parent17f0b6df7248b9bbdaddacc3a6c9c6efe4ae278e (diff)
Add auto purging for SkPicture-related Ganesh resources (esp. layers)
This is intended to lower the bookkeeping burden for the Layer Caching feature. Cached layers are now automatically purged when a picture is deleted. R=bsalomon@google.com Author: robertphillips@google.com Review URL: https://codereview.chromium.org/408923002
-rw-r--r--include/core/SkCanvas.h6
-rw-r--r--include/core/SkDevice.h7
-rw-r--r--include/core/SkPicture.h11
-rw-r--r--include/gpu/SkGpuDevice.h2
-rw-r--r--src/core/SkCanvas.cpp7
-rw-r--r--src/core/SkDevice.cpp4
-rw-r--r--src/core/SkPicture.cpp22
-rw-r--r--src/gpu/GrLayerCache.cpp36
-rw-r--r--src/gpu/GrLayerCache.h22
-rw-r--r--src/gpu/SkGpuDevice.cpp9
-rw-r--r--tests/GpuLayerCacheTest.cpp34
-rw-r--r--tools/PictureBenchmark.cpp12
-rw-r--r--tools/render_pictures_main.cpp6
13 files changed, 114 insertions, 64 deletions
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 6bd3cc5363..d68a9c50ab 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -964,12 +964,6 @@ public:
*/
void EXPERIMENTAL_optimize(const SkPicture* picture);
- /** PRIVATE / EXPERIMENTAL -- do not call
- Purge all the discardable optimization information associated with
- 'picture'. If NULL is passed in, purge all discardable information.
- */
- void EXPERIMENTAL_purge(const SkPicture* picture);
-
/** Draw the picture into this canvas. This method effective brackets the
playback of the picture's draw calls with save/restore, so the state
of this canvas will be unchanged after this call.
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 2e06aab74f..4a9edee0ec 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -338,13 +338,6 @@ protected:
/**
* PRIVATE / EXPERIMENTAL -- do not call
- * Purge all discardable optimization information for 'picture'. If
- * picture is NULL then purge discardable information for all pictures.
- */
- virtual void EXPERIMENTAL_purge(const SkPicture* picture);
-
- /**
- * PRIVATE / EXPERIMENTAL -- do not call
* This entry point gives the backend an opportunity to take over the rendering
* of 'picture'. If optimization data is available (due to an earlier
* 'optimize' call) this entry point should make use of it and return true
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index db996e02e8..21ebef32cd 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -190,6 +190,14 @@ public:
bool suitableForGpuRasterization(GrContext*, const char ** = NULL) const;
#endif
+ class DeletionListener : public SkRefCnt {
+ public:
+ virtual void onDeletion(uint32_t pictureID) = 0;
+ };
+
+ // Takes ref on listener.
+ void addDeletionListener(DeletionListener* listener) const;
+
private:
// V2 : adds SkPixelRef's generation ID.
// V3 : PictInfo tag at beginning, and EOF tag at the end
@@ -237,7 +245,10 @@ private:
int fWidth, fHeight;
mutable SkAutoTUnref<const AccelData> fAccelData;
+ mutable SkTDArray<DeletionListener*> fDeletionListeners; // pointers are refed
+
void needsNewGenID() { fUniqueID = SK_InvalidGenID; }
+ void callDeletionListeners();
// Create a new SkPicture from an existing SkPictureData. The new picture
// takes ownership of 'data'.
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 6451e15c9b..9a5a92e2df 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -143,8 +143,6 @@ protected:
/** PRIVATE / EXPERIMENTAL -- do not call */
virtual void EXPERIMENTAL_optimize(const SkPicture* picture) SK_OVERRIDE;
/** PRIVATE / EXPERIMENTAL -- do not call */
- virtual void EXPERIMENTAL_purge(const SkPicture* picture) SK_OVERRIDE;
- /** PRIVATE / EXPERIMENTAL -- do not call */
virtual bool EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* picture) SK_OVERRIDE;
private:
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 4b1749a08b..504c9908ab 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2386,13 +2386,6 @@ void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
}
}
-void SkCanvas::EXPERIMENTAL_purge(const SkPicture* picture) {
- SkBaseDevice* device = this->getTopDevice();
- if (NULL != device) {
- device->EXPERIMENTAL_purge(picture);
- }
-}
-
void SkCanvas::drawPicture(const SkPicture* picture) {
if (NULL != picture) {
this->onDrawPicture(picture);
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 6a09c0b9db..e71c889451 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -134,10 +134,6 @@ void SkBaseDevice::EXPERIMENTAL_optimize(const SkPicture* picture) {
// The base class doesn't perform any analysis but derived classes may
}
-void SkBaseDevice::EXPERIMENTAL_purge(const SkPicture* picture) {
- // Derived-classes may have data to purge but not the base class
-}
-
bool SkBaseDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* picture) {
// The base class doesn't perform any accelerated picture rendering
return false;
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index d57de97d78..0b7a1dbcc5 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -144,7 +144,9 @@ static SkPicture* backport(const SkRecord& src, int width, int height) {
}
// fRecord OK
-SkPicture::~SkPicture() {}
+SkPicture::~SkPicture() {
+ this->callDeletionListeners();
+}
#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
// fRecord TODO, fix by deleting this method
@@ -492,3 +494,21 @@ SkPicture::SkPicture(int width, int height, SkRecord* record)
, fRecordWillPlayBackBitmaps(SkRecordWillPlaybackBitmaps(*record)) {
this->needsNewGenID();
}
+
+// Note that we are assuming that this entry point will only be called from
+// one thread. Currently the only client of this method is
+// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
+// thread.
+void SkPicture::addDeletionListener(DeletionListener* listener) const {
+ SkASSERT(NULL != listener);
+
+ *fDeletionListeners.append() = SkRef(listener);
+}
+
+void SkPicture::callDeletionListeners() {
+ for (int i = 0; i < fDeletionListeners.count(); ++i) {
+ fDeletionListeners[i]->onDeletion(this->uniqueID());
+ }
+
+ fDeletionListeners.unrefAll();
+}
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index 1a0923004b..32f5a5b6c4 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -9,6 +9,8 @@
#include "GrGpu.h"
#include "GrLayerCache.h"
+DECLARE_SKMESSAGEBUS_MESSAGE(GrPictureDeletedMessage);
+
#ifdef SK_DEBUG
void GrCachedLayer::validate(const GrTexture* backingTexture) const {
SkASSERT(SK_InvalidGenID != fKey.getPictureID());
@@ -223,7 +225,8 @@ private:
};
#endif
-void GrLayerCache::purge(const SkPicture* picture) {
+void GrLayerCache::purge(uint32_t pictureID) {
+
SkDEBUGCODE(GrAutoValidateCache avc(this);)
// We need to find all the layers associated with 'picture' and remove them.
@@ -231,7 +234,7 @@ void GrLayerCache::purge(const SkPicture* picture) {
SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
for (; !iter.done(); ++iter) {
- if (picture->uniqueID() == (*iter).pictureID()) {
+ if (pictureID == (*iter).pictureID()) {
*toBeRemoved.append() = &(*iter);
}
}
@@ -242,9 +245,34 @@ void GrLayerCache::purge(const SkPicture* picture) {
SkDELETE(toBeRemoved[i]);
}
- GrPictureInfo* pictInfo = fPictureHash.find(picture->uniqueID());
+ GrPictureInfo* pictInfo = fPictureHash.find(pictureID);
if (NULL != pictInfo) {
- fPictureHash.remove(picture->uniqueID());
+ fPictureHash.remove(pictureID);
SkDELETE(pictInfo);
}
}
+
+class GrPictureDeletionListener : public SkPicture::DeletionListener {
+ virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{
+ const GrPictureDeletedMessage message = { pictureID };
+ SkMessageBus<GrPictureDeletedMessage>::Post(message);
+ }
+};
+
+void GrLayerCache::trackPicture(const SkPicture* picture) {
+ if (NULL == fDeletionListener) {
+ fDeletionListener.reset(SkNEW(GrPictureDeletionListener));
+ }
+
+ picture->addDeletionListener(fDeletionListener);
+}
+
+void GrLayerCache::processDeletedPictures() {
+ SkTDArray<GrPictureDeletedMessage> deletedPictures;
+ fPictDeletionInbox.poll(&deletedPictures);
+
+ for (int i = 0; i < deletedPictures.count(); i++) {
+ this->purge(deletedPictures[i].pictureID);
+ }
+}
+
diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h
index 3d4c98a0d7..0fdd6c97b6 100644
--- a/src/gpu/GrLayerCache.h
+++ b/src/gpu/GrLayerCache.h
@@ -16,9 +16,15 @@
#include "GrRect.h"
#include "SkChecksum.h"
#include "SkTDynamicHash.h"
+#include "SkMessageBus.h"
class SkPicture;
+// The layer cache listens for these messages to purge picture-related resources.
+struct GrPictureDeletedMessage {
+ uint32_t pictureID;
+};
+
// GrPictureInfo stores the atlas plots used by a single picture. A single
// plot may be used to store layers from multiple pictures.
struct GrPictureInfo {
@@ -146,8 +152,11 @@ public:
// Inform the cache that layer's cached image is not currently required
void unlock(GrCachedLayer* layer);
- // Remove all the layers (and unlock any resources) associated with 'picture'
- void purge(const SkPicture* picture);
+ // Setup to be notified when 'picture' is deleted
+ void trackPicture(const SkPicture* picture);
+
+ // Cleanup after any SkPicture deletions
+ void processDeletedPictures();
SkDEBUGCODE(void validate() const;)
@@ -167,11 +176,18 @@ private:
SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key> fLayerHash;
+ SkMessageBus<GrPictureDeletedMessage>::Inbox fPictDeletionInbox;
+
+ SkAutoTUnref<SkPicture::DeletionListener> fDeletionListener;
+
void initAtlas();
GrCachedLayer* createLayer(const SkPicture* picture, int layerID);
+ // Remove all the layers (and unlock any resources) associated with 'pictureID'
+ void purge(uint32_t pictureID);
+
// for testing
- friend class GetNumLayers;
+ friend class TestingAccess;
int numLayers() const { return fLayerHash.count(); }
};
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index b06046a557..c35ff41219 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1842,6 +1842,8 @@ SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) {
}
void SkGpuDevice::EXPERIMENTAL_optimize(const SkPicture* picture) {
+ fContext->getLayerCache()->processDeletedPictures();
+
SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
const SkPicture::AccelData* existing = picture->EXPERIMENTAL_getAccelData(key);
@@ -1854,6 +1856,8 @@ void SkGpuDevice::EXPERIMENTAL_optimize(const SkPicture* picture) {
picture->EXPERIMENTAL_addAccelData(data);
GatherGPUInfo(picture, data);
+
+ fContext->getLayerCache()->trackPicture(picture);
}
static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) {
@@ -1862,11 +1866,8 @@ static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* re
result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
}
-void SkGpuDevice::EXPERIMENTAL_purge(const SkPicture* picture) {
- fContext->getLayerCache()->purge(picture);
-}
-
bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* picture) {
+ fContext->getLayerCache()->processDeletedPictures();
SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp
index 8da2b9ea8d..eb7d92c8e2 100644
--- a/tests/GpuLayerCacheTest.cpp
+++ b/tests/GpuLayerCacheTest.cpp
@@ -15,11 +15,14 @@
static const int kNumLayers = 5;
-class GetNumLayers {
+class TestingAccess {
public:
static int NumLayers(GrLayerCache* cache) {
return cache->numLayers();
}
+ static void Purge(GrLayerCache* cache, uint32_t pictureID) {
+ cache->purge(pictureID);
+ }
};
// Add several layers to the cache
@@ -34,7 +37,7 @@ static void create_layers(skiatest::Reporter* reporter,
GrCachedLayer* layer = cache->findLayer(&picture, i);
REPORTER_ASSERT(reporter, layer == layers[i]);
- REPORTER_ASSERT(reporter, GetNumLayers::NumLayers(cache) == i+1);
+ REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == i + 1);
REPORTER_ASSERT(reporter, picture.uniqueID() == layers[i]->pictureID());
REPORTER_ASSERT(reporter, layers[i]->layerID() == i);
@@ -42,6 +45,7 @@ static void create_layers(skiatest::Reporter* reporter,
REPORTER_ASSERT(reporter, !layers[i]->isAtlased());
}
+ cache->trackPicture(&picture);
}
// This test case exercises the public API of the GrLayerCache class.
@@ -126,22 +130,36 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
#endif
}
+ //--------------------------------------------------------------------
// Free them all SkGpuDevice-style. This will not free up the
// atlas' texture but will eliminate all the layers.
- cache.purge(picture);
+ TestingAccess::Purge(&cache, picture->uniqueID());
- REPORTER_ASSERT(reporter, GetNumLayers::NumLayers(&cache) == 0);
+ REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
// TODO: add VRAM/resource cache check here
-#if 0
+
+ //--------------------------------------------------------------------
+ // Test out the GrContext-style purge. This should remove all the layers
+ // and the atlas.
// Re-create the layers
- create_layers(reporter, &cache, picture);
+ create_layers(reporter, &cache, *picture);
// Free them again GrContext-style. This should free up everything.
cache.freeAll();
- REPORTER_ASSERT(reporter, GetNumLayers::NumLayers(&cache) == 0);
+ REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
+ // TODO: add VRAM/resource cache check here
+
+ //--------------------------------------------------------------------
+ // Test out the MessageBus-style purge. This will not free the atlas
+ // but should eliminate the free-floating layers.
+ create_layers(reporter, &cache, *picture);
+
+ picture.reset(NULL);
+ cache.processDeletedPictures();
+
+ REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
// TODO: add VRAM/resource cache check here
-#endif
}
}
diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp
index 70ce180dac..e2a726f1a5 100644
--- a/tools/PictureBenchmark.cpp
+++ b/tools/PictureBenchmark.cpp
@@ -81,12 +81,6 @@ void PictureBenchmark::run(SkPicture* pict) {
fRenderer->render(NULL);
fRenderer->resetState(true); // flush, swapBuffers and Finish
- if (fPreprocess) {
- if (NULL != fRenderer->getCanvas()) {
- fRenderer->getCanvas()->EXPERIMENTAL_purge(pict);
- }
- }
-
if (fPurgeDecodedTex) {
fRenderer->purgeTextures();
}
@@ -220,12 +214,6 @@ void PictureBenchmark::run(SkPicture* pict) {
SkAssertResult(perRunTimerData.appendTimes(perRunTimer.get()));
- if (fPreprocess) {
- if (NULL != fRenderer->getCanvas()) {
- fRenderer->getCanvas()->EXPERIMENTAL_purge(pict);
- }
- }
-
if (fPurgeDecodedTex) {
fRenderer->purgeTextures();
}
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
index 13ae6ead3f..c2c7875b28 100644
--- a/tools/render_pictures_main.cpp
+++ b/tools/render_pictures_main.cpp
@@ -205,12 +205,6 @@ static bool render_picture_internal(const SkString& inputPath, const SkString* w
SkDebugf("Failed to render %s\n", inputFilename.c_str());
}
- if (FLAGS_preprocess) {
- if (NULL != renderer.getCanvas()) {
- renderer.getCanvas()->EXPERIMENTAL_purge(renderer.getPicture());
- }
- }
-
renderer.end();
SkDELETE(picture);