diff options
author | reed <reed@google.com> | 2015-08-04 08:10:13 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-04 08:10:13 -0700 |
commit | 6f1216ac158e36a3a1cc805e7f899c755c5b98a2 (patch) | |
tree | 0e644bea3787fd4c5fcbf20e1e6d121c1692449d | |
parent | fae010266f32b715f334c8680aeada2c72d44668 (diff) |
cache private readback for gpu-images
Does not try to cache calls to readPixels at the moment:
- not triggered by drawing
- not clear if we want to perform any pixel transformations (that readPixels allows) on the GPU or CPU
Can consider that another time.
BUG=513695
Review URL: https://codereview.chromium.org/1262923003
-rw-r--r-- | include/core/SkPixelRef.h | 3 | ||||
-rw-r--r-- | src/core/SkBitmapCache.cpp | 14 | ||||
-rw-r--r-- | src/core/SkBitmapCache.h | 4 | ||||
-rw-r--r-- | src/core/SkPixelRef.cpp | 12 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 20 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.h | 14 | ||||
-rw-r--r-- | tests/ImageTest.cpp | 62 |
7 files changed, 122 insertions, 7 deletions
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h index 1dc01f79ab..0ed3099126 100644 --- a/include/core/SkPixelRef.h +++ b/include/core/SkPixelRef.h @@ -379,6 +379,9 @@ private: friend class SkBitmap; // only for cloneGenID void cloneGenID(const SkPixelRef&); + void setImmutableWithID(uint32_t genID); + friend class SkImage_Gpu; + typedef SkRefCnt INHERITED; }; diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp index aabf87ad5b..3f1feac7bb 100644 --- a/src/core/SkBitmapCache.cpp +++ b/src/core/SkBitmapCache.cpp @@ -142,6 +142,20 @@ bool SkBitmapCache::Add(SkPixelRef* pr, const SkIRect& subset, const SkBitmap& r } } +bool SkBitmapCache::Find(uint32_t genID, SkBitmap* result, SkResourceCache* localCache) { + BitmapKey key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeEmpty()); + + return CHECK_LOCAL(localCache, find, Find, key, BitmapRec::Finder, result); +} + +void SkBitmapCache::Add(uint32_t genID, const SkBitmap& result, SkResourceCache* localCache) { + SkASSERT(result.isImmutable()); + + BitmapRec* rec = SkNEW_ARGS(BitmapRec, (genID, 1, 1, SkIRect::MakeEmpty(), result)); + + CHECK_LOCAL(localCache, add, Add, rec); +} + ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkBitmapCache.h b/src/core/SkBitmapCache.h index de50aabe1e..c758942080 100644 --- a/src/core/SkBitmapCache.h +++ b/src/core/SkBitmapCache.h @@ -52,6 +52,10 @@ public: */ static bool Add(SkPixelRef*, const SkIRect& subset, const SkBitmap& result, SkResourceCache* localCache = NULL); + + static bool Find(uint32_t genID, SkBitmap* result, SkResourceCache* localCache = NULL); + // todo: eliminate the need to specify ID, since it should == the bitmap's + static void Add(uint32_t genID, const SkBitmap&, SkResourceCache* localCache = NULL); }; class SkMipMapCache { diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp index e79dfba27c..2a46385c24 100644 --- a/src/core/SkPixelRef.cpp +++ b/src/core/SkPixelRef.cpp @@ -356,6 +356,18 @@ void SkPixelRef::changeAlphaType(SkAlphaType at) { void SkPixelRef::setImmutable() { fMutability = kImmutable; } + +void SkPixelRef::setImmutableWithID(uint32_t genID) { + /* + * We are forcing the genID to match an external value. The caller must ensure that this + * value does not conflict with other content. + * + * One use is to force this pixelref's id to match an SkImage's id + */ + fMutability = kImmutable; + fTaggedGenID.store(genID); +} + void SkPixelRef::setTemporarilyImmutable() { SkASSERT(fMutability != kImmutable); fMutability = kTemporarilyImmutable; diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index c89c08de6e..d410de81b8 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -5,13 +5,14 @@ * found in the LICENSE file. */ +#include "SkBitmapCache.h" #include "SkImage_Gpu.h" #include "GrContext.h" #include "GrDrawContext.h" #include "effects/GrYUVtoRGBEffect.h" #include "SkCanvas.h" #include "SkGpuDevice.h" - +#include "SkPixelRef.h" SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, GrTexture* tex, int sampleCountForNewSurfaces, SkSurface::Budgeted budgeted) @@ -22,6 +23,12 @@ SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, GrText , fBudgeted(budgeted) {} +SkImage_Gpu::~SkImage_Gpu() { + if (fAddedRasterVersionToCache.load()) { + SkNotifyBitmapGenIDIsStale(this->uniqueID()); + } +} + SkSurface* SkImage_Gpu::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const { GrTexture* tex = this->getTexture(); SkASSERT(tex); @@ -49,6 +56,13 @@ SkShader* SkImage_Gpu::onNewShader(SkShader::TileMode tileX, SkShader::TileMode } bool SkImage_Gpu::getROPixels(SkBitmap* dst) const { + if (SkBitmapCache::Find(this->uniqueID(), dst)) { + SkASSERT(dst->getGenerationID() == this->uniqueID()); + SkASSERT(dst->isImmutable()); + SkASSERT(dst->getPixels()); + return true; + } + SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType; if (!dst->tryAllocPixels(SkImageInfo::MakeN32(this->width(), this->height(), at))) { return false; @@ -57,6 +71,10 @@ bool SkImage_Gpu::getROPixels(SkBitmap* dst) const { dst->getPixels(), dst->rowBytes())) { return false; } + + dst->pixelRef()->setImmutableWithID(this->uniqueID()); + SkBitmapCache::Add(this->uniqueID(), *dst); + fAddedRasterVersionToCache.store(true); return true; } diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h index 4c7ebd6f71..9481690714 100644 --- a/src/image/SkImage_Gpu.h +++ b/src/image/SkImage_Gpu.h @@ -8,6 +8,7 @@ #ifndef SkImage_Gpu_DEFINED #define SkImage_Gpu_DEFINED +#include "SkAtomics.h" #include "GrTexture.h" #include "GrGpuResourcePriv.h" #include "SkBitmap.h" @@ -17,14 +18,13 @@ class SkImage_Gpu : public SkImage_Base { public: - - /** * An "image" can be a subset/window into a larger texture, so we explicit take the * width and height. */ SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType, GrTexture*, int sampleCountForNewSurfaces, SkSurface::Budgeted); + ~SkImage_Gpu() override; void applyBudgetDecision() const { GrTexture* tex = this->getTexture(); @@ -47,10 +47,12 @@ public: int srcX, int srcY) const override; private: - SkAutoTUnref<GrTexture> fTexture; - const int fSampleCountForNewSurfaces; // 0 if we don't know - const SkAlphaType fAlphaType; - SkSurface::Budgeted fBudgeted; + SkAutoTUnref<GrTexture> fTexture; + const int fSampleCountForNewSurfaces; // 0 if we don't know + const SkAlphaType fAlphaType; + const SkSurface::Budgeted fBudgeted; + mutable SkAtomic<bool> fAddedRasterVersionToCache; + typedef SkImage_Base INHERITED; }; diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index ba071bb1f7..f5e1abc0a4 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -249,3 +249,65 @@ DEF_TEST(image_newfrombitmap, reporter) { REPORTER_ASSERT(reporter, peekSuccess == rec[i].fExpectPeekSuccess); } } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#if SK_SUPPORT_GPU + +static SkImage* make_gpu_image(GrContext* ctx, const SkImageInfo& info, SkColor color) { + const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted; + SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, budgeted, info, 0)); + surface->getCanvas()->drawColor(color); + return surface->newImageSnapshot(); +} + +#include "SkBitmapCache.h" + +/* + * This tests the caching (and preemptive purge) of the raster equivalent of a gpu-image. + * We cache it for performance when drawing into a raster surface. + * + * A cleaner test would know if each drawImage call triggered a read-back from the gpu, + * but we don't have that facility (at the moment) so we use a little internal knowledge + * of *how* the raster version is cached, and look for that. + */ +DEF_GPUTEST(SkImage_Gpu2Cpu, reporter, factory) { + GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType); + if (!ctx) { + REPORTER_ASSERT(reporter, false); + return; + } + + const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); + SkAutoTUnref<SkImage> image(make_gpu_image(ctx, info, SK_ColorRED)); + const uint32_t uniqueID = image->uniqueID(); + + SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info)); + + // now we can test drawing a gpu-backed image into a cpu-backed surface + + { + SkBitmap cachedBitmap; + REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap)); + } + + surface->getCanvas()->drawImage(image, 0, 0); + { + SkBitmap cachedBitmap; + if (SkBitmapCache::Find(uniqueID, &cachedBitmap)) { + REPORTER_ASSERT(reporter, cachedBitmap.getGenerationID() == uniqueID); + REPORTER_ASSERT(reporter, cachedBitmap.isImmutable()); + REPORTER_ASSERT(reporter, cachedBitmap.getPixels()); + } else { + // unexpected, but not really a bug, since the cache is global and this test may be + // run w/ other threads competing for its budget. + SkDebugf("SkImage_Gpu2Cpu : cachedBitmap was already purged\n"); + } + } + + image.reset(nullptr); + { + SkBitmap cachedBitmap; + REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap)); + } +} +#endif |