diff options
author | robertphillips <robertphillips@google.com> | 2014-07-22 10:18:06 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-07-22 10:18:06 -0700 |
commit | d771f6bc273457bc7aa95938ac326dfbbf876e1a (patch) | |
tree | 9a88b85ac2a0c044c9a160a0f960b5d7f761267f | |
parent | 17f0b6df7248b9bbdaddacc3a6c9c6efe4ae278e (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.h | 6 | ||||
-rw-r--r-- | include/core/SkDevice.h | 7 | ||||
-rw-r--r-- | include/core/SkPicture.h | 11 | ||||
-rw-r--r-- | include/gpu/SkGpuDevice.h | 2 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 7 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 4 | ||||
-rw-r--r-- | src/core/SkPicture.cpp | 22 | ||||
-rw-r--r-- | src/gpu/GrLayerCache.cpp | 36 | ||||
-rw-r--r-- | src/gpu/GrLayerCache.h | 22 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 9 | ||||
-rw-r--r-- | tests/GpuLayerCacheTest.cpp | 34 | ||||
-rw-r--r-- | tools/PictureBenchmark.cpp | 12 | ||||
-rw-r--r-- | tools/render_pictures_main.cpp | 6 |
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); |