aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkSurface.h14
-rw-r--r--include/gpu/GrContext.h5
-rw-r--r--include/gpu/SkGpuDevice.h19
-rw-r--r--src/gpu/GrContext.cpp4
-rw-r--r--src/gpu/GrResourceCache.h5
-rw-r--r--src/gpu/SkGpuDevice.cpp37
-rw-r--r--src/image/SkSurface_Gpu.cpp33
-rw-r--r--tests/SurfaceTest.cpp47
8 files changed, 122 insertions, 42 deletions
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 8e430d8f47..f5052ca10c 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 1834586578..52a25b4527 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 ad4ebd798e..3e20e1610a 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 c7e3433938..c480a1f718 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 41c6b516b9..b5953032f6 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 de01953b67..e9682d2d51 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<GrTexture> 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 ae05ea8c04..50d6606145 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<GrTexture> 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 7029ef26f6..c0839a5038 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<SkSurface> 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<SkSurface> surface1(createSurface(kGpu_SurfaceType, context));
- SkAutoTUnref<SkSurface> surface2(createSurface(kGpu_SurfaceType, context));
+ SkAutoTUnref<SkSurface> surface1(createSurface(surfaceType, context));
+ SkAutoTUnref<SkSurface> 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<SkSurface> surface(createSurface(surfaceType, context));
SkAutoTUnref<SkImage> 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