Add lazy proxy's for wrapping backend textures
Bug: skia:
Change-Id: I3bb557cefc35312adc9515b5683d2ed747bb4eb3
Reviewed-on: https://skia-review.googlesource.com/96862
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 6b63076..deb0313 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -206,6 +206,10 @@
*
* Will return NULL if the specified backend texture is unsupported.
*
+ * This is not supported if the GrContext was obtained from a deferred display list canvas
+ * since if the SkImage never gets used and flushed to a real GrContext, we have no way to be
+ * able to delete the underlying backend texture.
+ *
* DEPRECATED: This factory is deprecated and clients should use the factory below which takes
* an SkColorType.
*/
@@ -224,6 +228,10 @@
* interpret the backend format supplied by the GrBackendTexture. If the format in the
* GrBackendTexture is not compitable with the SkColorType, SkAlphaType, and SkColorSpace we
* will return nullptr.
+ *
+ * This is not supported if the GrContext was obtained from a deferred display list canvas
+ * since if the SkImage never gets used and flushed to a real GrContext, we have no way to be
+ * able to delete the underlying backend texture.
*/
static sk_sp<SkImage> MakeFromAdoptedTexture(GrContext* context,
const GrBackendTexture& backendTexture,
diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h
index 4cc6da3..ffe422d 100644
--- a/include/gpu/GrBackendSurface.h
+++ b/include/gpu/GrBackendSurface.h
@@ -86,6 +86,7 @@
friend class SkImage;
friend class SkSurface;
friend class GrBackendTextureImageGenerator;
+ friend class GrProxyProvider;
friend class GrGpu;
friend class GrGLGpu;
friend class GrVkGpu;
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index b07115e..064727e 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -114,15 +114,14 @@
// Must make copies of member variables to capture in the lambda since this image generator may
// be deleted before we actuallly execute the lambda.
- GrSurfaceOrigin surfaceOrigin = fSurfaceOrigin;
sk_sp<GrSemaphore> semaphore = fSemaphore;
GrBackendTexture backendTexture = fBackendTexture;
RefHelper* refHelper = fRefHelper;
refHelper->ref();
sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
- [refHelper, semaphore, backendTexture, surfaceOrigin]
- (GrResourceProvider* resourceProvider, GrSurfaceOrigin* outOrigin) {
+ [refHelper, semaphore, backendTexture]
+ (GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) {
if (!resourceProvider) {
// If we get here then we never created a texture to pass the refHelper ref off
// to. Thus we must unref it ourselves.
@@ -168,7 +167,6 @@
tex->setRelease(ReleaseRefHelper_TextureReleaseProc, refHelper);
}
- *outOrigin = surfaceOrigin;
return tex;
}, desc, mipMapped, SkBackingFit::kExact, SkBudgeted::kNo);
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index c49f6b8..1267750 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -330,17 +330,46 @@
return nullptr;
}
- sk_sp<GrTexture> texture(fResourceProvider->wrapBackendTexture(backendTex, ownership));
- if (!texture) {
- return nullptr;
- }
- if (releaseProc) {
- texture->setRelease(releaseProc, releaseCtx);
- }
+ GrSurfaceDesc desc;
+ desc.fOrigin = origin;
+ desc.fWidth = backendTex.width();
+ desc.fHeight = backendTex.height();
+ desc.fConfig = backendTex.config();
+ GrMipMapped mipMapped = backendTex.hasMipMaps() ? GrMipMapped::kYes : GrMipMapped::kNo;
- SkASSERT(!texture->asRenderTarget()); // Strictly a GrTexture
+ sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
+ [backendTex, ownership, releaseProc, releaseCtx]
+ (GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) {
+ if (!resourceProvider) {
+ // This lazy proxy was never initialized. If it has a releaseProc we must call
+ // it now so that the client knows they can free the underlying backend object.
+ if (releaseProc) {
+ releaseProc(releaseCtx);
+ }
+ return sk_sp<GrTexture>();
+ }
- return this->createWrapped(std::move(texture), origin);
+ sk_sp<GrTexture> tex = resourceProvider->wrapBackendTexture(backendTex,
+ ownership);
+ if (!tex) {
+ return sk_sp<GrTexture>();
+ }
+ if (releaseProc) {
+ tex->setRelease(releaseProc, releaseCtx);
+ }
+ SkASSERT(!tex->asRenderTarget()); // Strictly a GrTexture
+ // Make sure we match how we created the proxy with SkBudgeted::kNo
+ SkASSERT(SkBudgeted::kNo == tex->resourcePriv().isBudgeted());
+
+ return tex;
+ }, desc, mipMapped, SkBackingFit::kExact, SkBudgeted::kNo);
+
+ if (fResourceProvider) {
+ // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however,
+ // we're better off instantiating the proxy immediately here.
+ proxy->priv().doLazyInstantiation(fResourceProvider);
+ }
+ return proxy;
}
sk_sp<GrTextureProxy> GrProxyProvider::createWrappedTextureProxy(const GrBackendTexture& tex,
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 9dadaca..08e4dd3 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -351,7 +351,19 @@
SkASSERT(fProxy->fLazyInstantiateCallback);
SkASSERT(!fProxy->fTarget);
- sk_sp<GrTexture> texture = fProxy->fLazyInstantiateCallback(resourceProvider, &fProxy->fOrigin);
+ GrSurfaceOrigin* outOrigin;
+ if (GrSurfaceProxy::LazyState::kPartially == fProxy->lazyInstantiationState()) {
+ // In the partially instantiated case, we set the origin on the SurfaceProxy at creation
+ // time (via a GrSurfaceDesc). In the lambda, the creation of the texture never needs to
+ // know the origin, and it also can't change or have any effect on it. Thus we just pass in
+ // nullptr in this case since it should never be set.
+ outOrigin = nullptr;
+ } else {
+ outOrigin = &fProxy->fOrigin;
+ }
+
+ sk_sp<GrTexture> texture = fProxy->fLazyInstantiateCallback(resourceProvider, outOrigin);
+
// Indicate we are no longer pending lazy instantiation.
fProxy->fLazyInstantiateCallback = nullptr;
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index fe4c2b9..c00c50d 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -156,6 +156,22 @@
GrSurfaceOrigin* origin) const {
SkASSERT(fProxy);
+ if (!fContext->contextPriv().resourceProvider() && !fProxy->priv().isInstantiated()) {
+ // This image was created with a DDL context and cannot be instantiated. Thus we return 0
+ // here which is considered invalid for all backends.
+ return 0;
+ }
+
+ if (GrSurfaceProxy::LazyState::kNot != fProxy->lazyInstantiationState()) {
+ SkASSERT(fContext->contextPriv().resourceProvider());
+ fProxy->priv().doLazyInstantiation(fContext->contextPriv().resourceProvider());
+ if (!fProxy->priv().isInstantiated()) {
+ // We failed to instantiate the lazy proxy. Thus we return 0 here which is considered
+ // invalid for all backends.
+ return 0;
+ }
+ }
+
if (!fProxy->instantiate(fContext->contextPriv().resourceProvider())) {
return 0;
}
@@ -335,6 +351,10 @@
sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
const GrBackendTexture& tex, GrSurfaceOrigin origin,
SkAlphaType at, sk_sp<SkColorSpace> cs) {
+ if (!ctx->contextPriv().resourceProvider()) {
+ // We have a DDL context and we don't support adopted textures for them.
+ return nullptr;
+ }
return new_wrapped_texture_common(ctx, tex, origin, at, std::move(cs), kAdopt_GrWrapOwnership,
nullptr, nullptr);
}
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index 1d99d00..8ee24be 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -9,6 +9,8 @@
#if SK_SUPPORT_GPU
+#include "GrBackendSurface.h"
+#include "GrGpu.h"
#include "SkCanvas.h"
#include "SkDeferredDisplayListRecorder.h"
#include "SkGpuDevice.h"
@@ -178,4 +180,160 @@
}
}
+static constexpr int kSize = 8;
+
+struct TextureReleaseChecker {
+ TextureReleaseChecker() : fReleaseCount(0) {}
+ int fReleaseCount;
+ static void Release(void* self) {
+ static_cast<TextureReleaseChecker*>(self)->fReleaseCount++;
+ }
+};
+
+enum class DDLStage { kMakeImage, kDrawImage, kDetach, kDrawDDL };
+
+// This tests the ability to create and use wrapped textures in a DDL world
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLWrapBackendTest, reporter, ctxInfo) {
+ GrContext* context = ctxInfo.grContext();
+ GrGpu* gpu = context->contextPriv().getGpu();
+ for (auto lastStage : { DDLStage::kMakeImage, DDLStage::kDrawImage,
+ DDLStage::kDetach, DDLStage::kDrawDDL } ) {
+ for (auto earlyImageReset : { false , true } ) {
+ GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
+ nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig, false, GrMipMapped::kNo);
+ if (!backendTex.isValid()) {
+ continue;
+ }
+
+ SurfaceParameters params;
+
+ sk_sp<SkSurface> s = params.make(context);
+ if (!s) {
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ SkSurfaceCharacterization c;
+ SkAssertResult(s->characterize(&c));
+
+ std::unique_ptr<SkDeferredDisplayListRecorder> recorder(
+ new SkDeferredDisplayListRecorder(c));
+
+ SkCanvas* canvas = recorder->getCanvas();
+ if (!canvas) {
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ GrContext* deferredContext = canvas->getGrContext();
+ if (!deferredContext) {
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ sk_sp<SkImage> image = SkImage::MakeFromAdoptedTexture(deferredContext, backendTex,
+ kTopLeft_GrSurfaceOrigin,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType, nullptr);
+ // Adopted Textures are not supported in DDL
+ REPORTER_ASSERT(reporter, !image);
+
+ TextureReleaseChecker releaseChecker;
+ image = SkImage::MakeFromTexture(deferredContext, backendTex,
+ kTopLeft_GrSurfaceOrigin,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType, nullptr,
+ TextureReleaseChecker::Release, &releaseChecker);
+
+ REPORTER_ASSERT(reporter, image);
+ if (!image) {
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ if (DDLStage::kMakeImage == lastStage) {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ image.reset();
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ recorder.reset();
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ canvas->drawImage(image.get(), 0, 0);
+
+ if (earlyImageReset) {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ image.reset();
+ // Ref should still be held by DDL recorder since we did the draw
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ }
+
+ if (DDLStage::kDrawImage == lastStage) {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ recorder.reset();
+ if (earlyImageReset) {
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ } else {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ image.reset();
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ }
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ std::unique_ptr<SkDeferredDisplayList> ddl = recorder->detach();
+ if (DDLStage::kDetach == lastStage) {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ recorder.reset();
+ // DDL TODO: Once copies of OpLists from the recorder to DDL are implemented we can
+ // uncomment this check. Currently the texture is getting reset when the recorder
+ // goes away (assuming we did an earlyImageReset).
+ // REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ ddl.reset();
+ if (earlyImageReset) {
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ } else {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ image.reset();
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ }
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ continue;
+ }
+
+ REPORTER_ASSERT(reporter, s->draw(ddl.get()));
+
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ recorder.reset();
+ // DDL TODO: Once copies of OpLists from the recorder to DDL are implemented we can
+ // uncomment these checks. Currently the texture is getting released when the recorder
+ // goes away (assuming we did an earlyImageReset).
+ // REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ ddl.reset();
+ // REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+
+ // Force all draws to flush and sync by calling a read pixels
+ SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType);
+ SkBitmap bitmap;
+ bitmap.allocPixels(imageInfo);
+ s->readPixels(imageInfo, bitmap.getPixels(), 0, 0, 0);
+
+ if (earlyImageReset) {
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ } else {
+ REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+ image.reset();
+ REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+ }
+
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+ }
+ }
+}
+
+
#endif