From d8a57af725e8fa8905207df3cf7465be50598752 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Wed, 19 Mar 2014 21:19:16 +0000 Subject: [PATCH] Adding a new SkSurface factory for generating surfaces from the scratch texture pool. TEST=Surface unit test BUG=crbug.com/351798 R=bsalomon@google.com, robertphillips@google.com, reed@google.com Author: junov@chromium.org Review URL: https://codereview.chromium.org/201153023 git-svn-id: http://skia.googlecode.com/svn/trunk@13864 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkSurface.h | 14 ++++++++++++++ include/gpu/GrContext.h | 5 +++++ include/gpu/SkGpuDevice.h | 19 ++++++++++-------- src/gpu/GrContext.cpp | 4 ++++ src/gpu/GrResourceCache.h | 5 +++++ src/gpu/SkGpuDevice.cpp | 37 +++++++++++++---------------------- src/image/SkSurface_Gpu.cpp | 33 ++++++++++++++++++++++++++----- tests/SurfaceTest.cpp | 47 ++++++++++++++++++++++++++++++++++++++++----- 8 files changed, 122 insertions(+), 42 deletions(-) diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h index 8e430d8..f5052ca 100644 --- a/include/core/SkSurface.h +++ b/include/core/SkSurface.h @@ -73,6 +73,20 @@ public: */ static SkSurface* NewRenderTarget(GrContext*, const SkImageInfo&, int sampleCount = 0); + /** + * Return a new surface whose contents will be drawn to an offscreen + * render target, allocated by the surface from the scratch texture pool + * managed by the GrContext. The scratch texture pool serves the purpose + * of retaining textures after they are no longer in use in order to + * re-use them later without having to re-allocate. Scratch textures + * should be used in cases where high turnover is expected. This allows, + * for example, the copy on write to recycle a texture from a recently + * released SkImage snapshot of the surface. + * Note: Scratch textures count against the GrContext's cached resource + * budget. + */ + static SkSurface* NewScratchRenderTarget(GrContext*, const SkImageInfo&, int sampleCount = 0); + int width() const { return fWidth; } int height() const { return fHeight; } diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 1834586..52a25b4 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -111,6 +111,11 @@ public: */ size_t getGpuTextureCacheBytes() const; + /** + * Returns the number of resources hosted by the texture cache. + */ + int getGpuTextureCacheResourceCount() const; + /////////////////////////////////////////////////////////////////////////// // Textures diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index ad4ebd7..3e20e16 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -29,12 +29,18 @@ class GrTextContext; */ class SK_API SkGpuDevice : public SkBitmapDevice { public: + enum Flags { + kNeedClear_Flag = 1 << 0, //!< Surface requires an initial clear + kCached_Flag = 1 << 1, //!< Surface is cached and needs to be unlocked when released + }; /** * Creates an SkGpuDevice from a GrSurface. This will fail if the surface is not a render - * target. The caller owns a ref on the returned device. + * target. The caller owns a ref on the returned device. If the surface is cached, + * the kCached_Flag should be specified to make the device responsible for unlocking + * the surface when it is released. */ - static SkGpuDevice* Create(GrSurface* surface); + static SkGpuDevice* Create(GrSurface* surface, unsigned flags = 0); /** * New device that will create an offscreen renderTarget based on the @@ -58,7 +64,7 @@ public: * DEPRECATED -- need to make this private, call Create(surface) * New device that will render to the specified renderTarget. */ - SkGpuDevice(GrContext*, GrRenderTarget*); + SkGpuDevice(GrContext*, GrRenderTarget*, unsigned flags = 0); /** * DEPRECATED -- need to make this private, call Create(surface) @@ -66,7 +72,7 @@ public: * The GrTexture's asRenderTarget() must be non-NULL or device will not * function. */ - SkGpuDevice(GrContext*, GrTexture*); + SkGpuDevice(GrContext*, GrTexture*, unsigned flags = 0); virtual ~SkGpuDevice(); @@ -173,10 +179,7 @@ private: bool fNeedClear; // called from rt and tex cons - void initFromRenderTarget(GrContext*, GrRenderTarget*, bool cached); - - // used by createCompatibleDevice - SkGpuDevice(GrContext*, GrTexture* texture, bool needClear); + void initFromRenderTarget(GrContext*, GrRenderTarget*, unsigned flags); virtual SkBaseDevice* onCreateDevice(const SkImageInfo&, Usage) SK_OVERRIDE; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index c7e3433..c480a1f 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -220,6 +220,10 @@ size_t GrContext::getGpuTextureCacheBytes() const { return fTextureCache->getCachedResourceBytes(); } +int GrContext::getGpuTextureCacheResourceCount() const { + return fTextureCache->getCachedResourceCount(); +} + //////////////////////////////////////////////////////////////////////////////// GrTexture* GrContext::findAndRefTexture(const GrTextureDesc& desc, diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h index 41c6b51..b595303 100644 --- a/src/gpu/GrResourceCache.h +++ b/src/gpu/GrResourceCache.h @@ -213,6 +213,11 @@ public: */ size_t getCachedResourceBytes() const { return fEntryBytes; } + /** + * Returns the number of cached resources. + */ + int getCachedResourceCount() const { return fEntryCount; } + // For a found or added resource to be completely exclusive to the caller // both the kNoOtherOwners and kHide flags need to be specified enum OwnershipFlags { diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index de01953..e9682d2 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -158,31 +158,31 @@ static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { return bitmap; } -SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) { +SkGpuDevice* SkGpuDevice::Create(GrSurface* surface, unsigned flags) { SkASSERT(NULL != surface); if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { return NULL; } if (surface->asTexture()) { - return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture())); + return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture(), flags)); } else { - return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget())); + return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget(), flags)); } } -SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture) +SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture, unsigned flags) : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { - this->initFromRenderTarget(context, texture->asRenderTarget(), false); + this->initFromRenderTarget(context, texture->asRenderTarget(), flags); } -SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget) +SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget, unsigned flags) : SkBitmapDevice(make_bitmap(context, renderTarget)) { - this->initFromRenderTarget(context, renderTarget, false); + this->initFromRenderTarget(context, renderTarget, flags); } void SkGpuDevice::initFromRenderTarget(GrContext* context, GrRenderTarget* renderTarget, - bool cached) { + unsigned flags) { fDrawProcs = NULL; fContext = context; @@ -192,7 +192,7 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context, fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); fRenderTarget = NULL; - fNeedClear = false; + fNeedClear = flags & kNeedClear_Flag; SkASSERT(NULL != renderTarget); fRenderTarget = renderTarget; @@ -209,7 +209,7 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context, SkImageInfo info; surface->asImageInfo(&info); - SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, cached)); + SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, flags & kCached_Flag)); this->setPixelRef(pr)->unref(); } @@ -1971,11 +1971,12 @@ SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage) SkAutoTUnref texture; // Skia's convention is to only clear a device if it is non-opaque. - bool needClear = !info.isOpaque(); + unsigned flags = info.isOpaque() ? 0 : kNeedClear_Flag; #if CACHE_COMPATIBLE_DEVICE_TEXTURES // layers are never draw in repeat modes, so we can request an approx // match and ignore any padding. + flags |= kCached_Flag; const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ? GrContext::kApprox_ScratchTexMatch : GrContext::kExact_ScratchTexMatch; @@ -1984,7 +1985,7 @@ SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage) texture.reset(fContext->createUncachedTexture(desc, NULL, 0)); #endif if (NULL != texture.get()) { - return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear)); + return SkGpuDevice::Create(texture, flags); } else { GrPrintf("---- failed to create compatible device texture [%d %d]\n", info.width(), info.height()); @@ -1996,18 +1997,6 @@ SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) { return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples()); } -SkGpuDevice::SkGpuDevice(GrContext* context, - GrTexture* texture, - bool needClear) - : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { - - SkASSERT(texture && texture->asRenderTarget()); - // This constructor is called from onCreateDevice. It has locked the RT in the texture - // cache. We pass true for the third argument so that it will get unlocked. - this->initFromRenderTarget(context, texture->asRenderTarget(), true); - fNeedClear = needClear; -} - class GPUAccelData : public SkPicture::AccelData { public: GPUAccelData(Key key) : INHERITED(key) { } diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp index ae05ea8..50d6606 100644 --- a/src/image/SkSurface_Gpu.cpp +++ b/src/image/SkSurface_Gpu.cpp @@ -14,7 +14,7 @@ class SkSurface_Gpu : public SkSurface_Base { public: SK_DECLARE_INST_COUNT(SkSurface_Gpu) - SkSurface_Gpu(GrRenderTarget*); + SkSurface_Gpu(GrRenderTarget*, bool cached); virtual ~SkSurface_Gpu(); virtual SkCanvas* onNewCanvas() SK_OVERRIDE; @@ -32,9 +32,9 @@ private: /////////////////////////////////////////////////////////////////////////////// -SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget) +SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget, bool cached) : INHERITED(renderTarget->width(), renderTarget->height()) { - fDevice = SkNEW_ARGS(SkGpuDevice, (renderTarget->getContext(), renderTarget)); + fDevice = SkGpuDevice::Create(renderTarget, cached ? SkGpuDevice::kCached_Flag : 0); if (kRGB_565_GrPixelConfig != renderTarget->config()) { fDevice->clear(0x0); @@ -95,7 +95,7 @@ SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target) { if (NULL == target) { return NULL; } - return SkNEW_ARGS(SkSurface_Gpu, (target)); + return SkNEW_ARGS(SkSurface_Gpu, (target, false)); } SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, int sampleCount) { @@ -117,5 +117,28 @@ SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, i return NULL; } - return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget())); + return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), false)); +} + +SkSurface* SkSurface::NewScratchRenderTarget(GrContext* ctx, const SkImageInfo& info, int sampleCount) { + if (NULL == ctx) { + return NULL; + } + + SkBitmap::Config config = SkImageInfoToBitmapConfig(info); + + GrTextureDesc desc; + desc.fFlags = kRenderTarget_GrTextureFlagBit | kCheckAllocation_GrTextureFlagBit; + desc.fWidth = info.fWidth; + desc.fHeight = info.fHeight; + desc.fConfig = SkBitmapConfig2GrPixelConfig(config); + desc.fSampleCnt = sampleCount; + + SkAutoTUnref tex(ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch)); + + if (NULL == tex) { + return NULL; + } + + return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), true)); } diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index 7029ef2..c0839a5 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -24,6 +24,7 @@ enum SurfaceType { kRaster_SurfaceType, kRasterDirect_SurfaceType, kGpu_SurfaceType, + kGpuScratch_SurfaceType, kPicture_SurfaceType }; @@ -50,6 +51,11 @@ static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context, return context ? SkSurface::NewRenderTarget(context, info) : NULL; #endif break; + case kGpuScratch_SurfaceType: +#if SK_SUPPORT_GPU + return context ? SkSurface::NewScratchRenderTarget(context, info) : NULL; +#endif + break; case kPicture_SurfaceType: return SkSurface::NewPicture(info.fWidth, info.fHeight); } @@ -123,7 +129,7 @@ static void test_imagepeek(skiatest::Reporter* reporter) { } gRec[] = { { kRasterCopy_ImageType, true }, { kRasterData_ImageType, true }, - { kGpu_ImageType, false }, + { kGpu_ImageType, false }, { kPicture_ImageType, false }, { kCodec_ImageType, false }, }; @@ -164,6 +170,7 @@ static void test_canvaspeek(skiatest::Reporter* reporter, { kRasterDirect_SurfaceType, true }, #if SK_SUPPORT_GPU { kGpu_SurfaceType, false }, + { kGpuScratch_SurfaceType, false }, #endif { kPicture_SurfaceType, false }, }; @@ -305,14 +312,36 @@ static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter } #if SK_SUPPORT_GPU +static void TestSurfaceInCache(skiatest::Reporter* reporter, + SurfaceType surfaceType, + GrContext* context) { + context->freeGpuResources(); + REPORTER_ASSERT(reporter, 0 == context->getGpuTextureCacheResourceCount()); + SkAutoTUnref surface(createSurface(surfaceType, context)); + // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses + // one cached resource, and kGpuScratch_SurfaceType uses two. + int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1; + REPORTER_ASSERT(reporter, expectedCachedResources == context->getGpuTextureCacheResourceCount()); + + // Verify that all the cached resources are locked in cache. + context->freeGpuResources(); + REPORTER_ASSERT(reporter, expectedCachedResources == context->getGpuTextureCacheResourceCount()); + + // Verify that all the cached resources are unlocked upon surface release + surface.reset(0); + context->freeGpuResources(); + REPORTER_ASSERT(reporter, 0 == context->getGpuTextureCacheResourceCount()); +} + static void Test_crbug263329(skiatest::Reporter* reporter, + SurfaceType surfaceType, GrContext* context) { // This is a regression test for crbug.com/263329 // Bug was caused by onCopyOnWrite releasing the old surface texture // back to the scratch texture pool even though the texture is used // by and active SkImage_Gpu. - SkAutoTUnref surface1(createSurface(kGpu_SurfaceType, context)); - SkAutoTUnref surface2(createSurface(kGpu_SurfaceType, context)); + SkAutoTUnref surface1(createSurface(surfaceType, context)); + SkAutoTUnref surface2(createSurface(surfaceType, context)); SkCanvas* canvas1 = surface1->getCanvas(); SkCanvas* canvas2 = surface2->getCanvas(); canvas1->clear(1); @@ -345,7 +374,7 @@ static void TestGetTexture(skiatest::Reporter* reporter, SkAutoTUnref surface(createSurface(surfaceType, context)); SkAutoTUnref image(surface->newImageSnapshot()); GrTexture* texture = image->getTexture(); - if (surfaceType == kGpu_SurfaceType) { + if (surfaceType == kGpu_SurfaceType || surfaceType == kGpuScratch_SurfaceType) { REPORTER_ASSERT(reporter, NULL != texture); REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle()); } else { @@ -407,12 +436,20 @@ DEF_GPUTEST(Surface, reporter, factory) { if (NULL != factory) { GrContext* context = factory->get(GrContextFactory::kNative_GLContextType); if (NULL != context) { - Test_crbug263329(reporter, context); + TestSurfaceInCache(reporter, kGpu_SurfaceType, context); + TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context); + Test_crbug263329(reporter, kGpu_SurfaceType, context); + Test_crbug263329(reporter, kGpuScratch_SurfaceType, context); TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context); + TestSurfaceCopyOnWrite(reporter, kGpuScratch_SurfaceType, context); TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context); + TestSurfaceWritableAfterSnapshotRelease(reporter, kGpuScratch_SurfaceType, context); TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode); + TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode); TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode); + TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kRetain_ContentChangeMode); TestGetTexture(reporter, kGpu_SurfaceType, context); + TestGetTexture(reporter, kGpuScratch_SurfaceType, context); } } #endif -- 2.7.4