aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gn/tests.gni1
-rw-r--r--include/core/SkDeferredDisplayListRecorder.h59
-rw-r--r--include/gpu/GrBackendSurface.h2
-rw-r--r--src/core/SkDeferredDisplayListRecorder.cpp31
-rw-r--r--src/gpu/GrProxyProvider.cpp19
-rw-r--r--src/gpu/GrProxyProvider.h5
-rw-r--r--src/gpu/GrSurfaceProxyPriv.h4
-rw-r--r--src/image/SkImage_Gpu.cpp142
-rw-r--r--src/image/SkImage_Gpu.h53
-rw-r--r--tests/DeferredDisplayListTest.cpp4
-rw-r--r--tests/LazyProxyTest.cpp15
-rw-r--r--tests/PromiseImageTest.cpp203
-rw-r--r--tools/gpu/GrTest.cpp24
-rw-r--r--tools/gpu/GrTest.h2
14 files changed, 543 insertions, 21 deletions
diff --git a/gn/tests.gni b/gn/tests.gni
index cde24b0ea3..0c34a6cfd3 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -173,6 +173,7 @@ tests_sources = [
"$_tests/PremulAlphaRoundTripTest.cpp",
"$_tests/PrimitiveProcessorTest.cpp",
"$_tests/ProcessorTest.cpp",
+ "$_tests/PromiseImageTest.cpp",
"$_tests/ProxyConversionTest.cpp",
"$_tests/ProxyRefTest.cpp",
"$_tests/ProxyTest.cpp",
diff --git a/include/core/SkDeferredDisplayListRecorder.h b/include/core/SkDeferredDisplayListRecorder.h
index c985e7377d..601cd533b5 100644
--- a/include/core/SkDeferredDisplayListRecorder.h
+++ b/include/core/SkDeferredDisplayListRecorder.h
@@ -8,11 +8,15 @@
#ifndef SkDeferredDisplayListMaker_DEFINED
#define SkDeferredDisplayListMaker_DEFINED
+#include "SkImageInfo.h"
#include "SkRefCnt.h"
#include "SkSurfaceCharacterization.h"
+#include "SkTypes.h"
#include "../private/SkDeferredDisplayList.h"
+class GrBackendFormat;
+class GrBackendTexture;
class GrContext;
class SkCanvas;
@@ -46,6 +50,61 @@ public:
std::unique_ptr<SkDeferredDisplayList> detach();
+ // Matches the defines in SkImage_Gpu.h
+ typedef void* TextureContext;
+ typedef void (*TextureReleaseProc)(TextureContext textureContext);
+ typedef void (*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
+
+ /**
+ Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main
+ difference is that the client doesn't have the backend texture on the gpu yet but they know
+ all the properties of the texture. So instead of passing in a GrBackendTexture the client
+ supplies a GrBackendFormat, width, height, and GrMipMapped state.
+
+ When we actually send the draw calls to the GPU, we will call the textureFulfillProc and
+ the client will return a GrBackendTexture to us. The properties of the GrBackendTexture must
+ match those set during the SkImage creation, and it must have a valid backend gpu texture.
+ The gpu texture supplied by the client must stay valid until we call the textureReleaseProc.
+
+ When we are done with the texture returned by the textureFulfillProc we will call the
+ textureReleaseProc passing in the textureContext. This is a signal to the client that they
+ are free to delete the underlying gpu texture. If future draws also use the same promise
+ image we will call the textureFulfillProc again if we've already called the
+ textureReleaseProc. We will always call textureFulfillProc and textureReleaseProc in pairs.
+ In other words we will never call textureFulfillProc or textureReleaseProc multiple times
+ for the same textureContext before calling the other.
+
+ This call is only valid if the SkDeferredDisplayListRecorder is backed by a gpu context.
+
+ @param backendFormat format of promised gpu texture
+ @param width width of promised gpu texture
+ @param height height of promised gpu texture
+ @param mipMapped mip mapped state of promised gpu texture
+ @param origin one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
+ @param colorType one of: kUnknown_SkColorType, kAlpha_8_SkColorType,
+ kRGB_565_SkColorType, kARGB_4444_SkColorType,
+ kRGBA_8888_SkColorType, kBGRA_8888_SkColorType,
+ kGray_8_SkColorType, kRGBA_F16_SkColorType
+ @param alphaType one of: kUnknown_SkAlphaType, kOpaque_SkAlphaType,
+ kPremul_SkAlphaType, kUnpremul_SkAlphaType
+ @param colorSpace range of colors; may be nullptr
+ @param textureFulfillProc function called to get actual gpu texture
+ @param textureReleaseProc function called when texture can be released
+ @param textureContext state passed to textureFulfillProc and textureReleaseProc
+ @return created SkImage, or nullptr
+ */
+ sk_sp<SkImage> makePromiseTexture(const GrBackendFormat& backendFormat,
+ int width,
+ int height,
+ GrMipMapped mipMapped,
+ GrSurfaceOrigin origin,
+ SkColorType colorType,
+ SkAlphaType alphaType,
+ sk_sp<SkColorSpace> colorSpace,
+ TextureFulfillProc textureFulfillProc,
+ TextureReleaseProc textureReleaseProc,
+ TextureContext textureContext);
+
private:
bool init();
diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h
index a2df90cd0d..94ab39aca2 100644
--- a/include/gpu/GrBackendSurface.h
+++ b/include/gpu/GrBackendSurface.h
@@ -26,7 +26,7 @@ public:
}
#ifdef SK_VULKAN
- static GrBackendFormat MakeVK(VkFormat format) {
+ static GrBackendFormat MakeVk(VkFormat format) {
return GrBackendFormat(format);
}
#endif
diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp
index 96bb575924..45992d8c6f 100644
--- a/src/core/SkDeferredDisplayListRecorder.cpp
+++ b/src/core/SkDeferredDisplayListRecorder.cpp
@@ -7,6 +7,7 @@
#include "SkDeferredDisplayListRecorder.h"
+
#if SK_SUPPORT_GPU
#include "GrContextPriv.h"
#include "GrProxyProvider.h"
@@ -14,6 +15,7 @@
#include "SkGpuDevice.h"
#include "SkGr.h"
+#include "SkImage_Gpu.h"
#include "SkSurface_Gpu.h"
#endif
@@ -150,3 +152,32 @@ std::unique_ptr<SkDeferredDisplayList> SkDeferredDisplayListRecorder::detach() {
}
+sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
+ const GrBackendFormat& backendFormat,
+ int width,
+ int height,
+ GrMipMapped mipMapped,
+ GrSurfaceOrigin origin,
+ SkColorType colorType,
+ SkAlphaType alphaType,
+ sk_sp<SkColorSpace> colorSpace,
+ TextureFulfillProc textureFulfillProc,
+ TextureReleaseProc textureReleaseProc,
+ TextureContext textureContext) {
+#if !defined(SK_RASTER_RECORDER_IMPLEMENTATION) && defined(SK_SUPPORT_GPU)
+ return SkImage_Gpu::MakePromiseTexture(fContext.get(),
+ backendFormat,
+ width,
+ height,
+ mipMapped,
+ origin,
+ colorType,
+ alphaType,
+ colorSpace,
+ textureFulfillProc,
+ textureReleaseProc,
+ textureContext);
+#else
+ return nullptr;
+#endif
+}
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index dbd49a7ac3..7c82544cb1 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -524,6 +524,20 @@ sk_sp<GrTextureProxy> GrProxyProvider::createLazyProxy(LazyInstantiateCallback&&
GrMipMapped mipMapped,
GrRenderTargetFlags renderTargetFlags,
SkBackingFit fit, SkBudgeted budgeted) {
+ // For non-ddl draws always make lazy proxy's single use.
+ LazyInstantiationType lazyType = fResourceProvider ? LazyInstantiationType::kSingleUse
+ : LazyInstantiationType::kMultipleUse;
+ return this->createLazyProxy(std::move(callback), desc, origin, mipMapped, renderTargetFlags,
+ fit, budgeted, lazyType);
+}
+
+sk_sp<GrTextureProxy> GrProxyProvider::createLazyProxy(LazyInstantiateCallback&& callback,
+ const GrSurfaceDesc& desc,
+ GrSurfaceOrigin origin,
+ GrMipMapped mipMapped,
+ GrRenderTargetFlags renderTargetFlags,
+ SkBackingFit fit, SkBudgeted budgeted,
+ LazyInstantiationType lazyType) {
SkASSERT((desc.fWidth <= 0 && desc.fHeight <= 0) ||
(desc.fWidth > 0 && desc.fHeight > 0));
uint32_t flags = GrResourceProvider::kNoPendingIO_Flag;
@@ -539,11 +553,6 @@ sk_sp<GrTextureProxy> GrProxyProvider::createLazyProxy(LazyInstantiateCallback&&
}
#endif
- using LazyInstantiationType = GrSurfaceProxy::LazyInstantiationType;
- // For non-ddl draws always make lazy proxy's single use.
- LazyInstantiationType lazyType = fResourceProvider ? LazyInstantiationType::kSingleUse
- : LazyInstantiationType::kMultipleUse;
-
return sk_sp<GrTextureProxy>(
SkToBool(kRenderTarget_GrSurfaceFlag & desc.fFlags)
? new GrTextureRenderTargetProxy(std::move(callback), lazyType, desc, origin,
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index 2f2eeb1113..45a3c1a1b7 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -155,6 +155,7 @@ public:
kYes = true
};
+ using LazyInstantiationType = GrSurfaceProxy::LazyInstantiationType;
/**
* Creates a texture proxy that will be instantiated by a user-supplied callback during flush.
* (Stencil is not supported by this method.) The width and height must either both be greater
@@ -167,6 +168,10 @@ public:
*/
sk_sp<GrTextureProxy> createLazyProxy(LazyInstantiateCallback&&, const GrSurfaceDesc&,
GrSurfaceOrigin, GrMipMapped, GrRenderTargetFlags,
+ SkBackingFit, SkBudgeted, LazyInstantiationType);
+
+ sk_sp<GrTextureProxy> createLazyProxy(LazyInstantiateCallback&&, const GrSurfaceDesc&,
+ GrSurfaceOrigin, GrMipMapped, GrRenderTargetFlags,
SkBackingFit, SkBudgeted);
sk_sp<GrTextureProxy> createLazyProxy(LazyInstantiateCallback&&, const GrSurfaceDesc&,
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 10203b71d0..00b38684d8 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -80,10 +80,6 @@ public:
GrSurfaceProxy::LazyInstantiationType::kUninstantiate == lazyInstantiationType();
}
- void testingOnly_setLazyInstantiationType(GrSurfaceProxy::LazyInstantiationType lazyType) {
- fProxy->fLazyInstantiationType = lazyType;
- }
-
static bool AttachStencilIfNeeded(GrResourceProvider*, GrSurface*, bool needsStencil);
private:
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 6ed3e15bb2..d49835ea90 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -576,6 +576,148 @@ sk_sp<SkImage> SkImage::makeTextureImage(GrContext* context, SkColorSpace* dstCo
return nullptr;
}
+class PromiseImageHelper {
+public:
+ PromiseImageHelper(SkImage_Gpu::TextureFulfillProc fulFillProc,
+ SkImage_Gpu::TextureReleaseProc releaseProc,
+ SkImage_Gpu::TextureContext context)
+ : fFulfillProc(fulFillProc)
+ , fReleaseProc(releaseProc)
+ , fContext(context) {}
+
+ void reset() {
+ this->resetReleaseHelper();
+ }
+
+ sk_sp<GrTexture> getTexture(GrResourceProvider* resourceProvider) {
+ // Releases the promise helper if there are no outstanding hard refs. This means that we
+ // don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
+ if (fReleaseHelper && fReleaseHelper->weak_expired()) {
+ this->resetReleaseHelper();
+ }
+
+ sk_sp<GrTexture> tex;
+ if (!fReleaseHelper) {
+ fFulfillProc(fContext, &fBackendTex);
+ if (!fBackendTex.isValid()) {
+ // Even though the GrBackendTexture is not valid, we must call the release
+ // proc to keep our contract of always calling Fulfill and Release in pairs.
+ fReleaseProc(fContext);
+ return sk_sp<GrTexture>();
+ }
+
+ tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
+ if (!tex) {
+ // Even though the GrBackendTexture is not valid, we must call the release
+ // proc to keep our contract of always calling Fulfill and Release in pairs.
+ fReleaseProc(fContext);
+ return sk_sp<GrTexture>();
+ }
+ fReleaseHelper = new GrReleaseProcHelper(fReleaseProc, fContext);
+ // Take a weak ref
+ fReleaseHelper->weak_ref();
+ } else {
+ SkASSERT(fBackendTex.isValid());
+ tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
+ if (!tex) {
+ // We weren't able to make a texture here, but since we are in this branch
+ // of the calls (promiseHelper.fReleaseHelper is valid) there is already a
+ // texture out there which will call the release proc so we don't need to
+ // call it here.
+ return sk_sp<GrTexture>();
+ }
+
+ SkAssertResult(fReleaseHelper->try_ref());
+ }
+ SkASSERT(tex);
+ // Pass the hard ref off to the texture
+ tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
+ return tex;
+ }
+
+private:
+ // Weak unrefs fReleaseHelper and sets it to null
+ void resetReleaseHelper() {
+ if (fReleaseHelper) {
+ fReleaseHelper->weak_unref();
+ fReleaseHelper = nullptr;
+ }
+ }
+
+ SkImage_Gpu::TextureFulfillProc fFulfillProc;
+ SkImage_Gpu::TextureReleaseProc fReleaseProc;
+ SkImage_Gpu::TextureContext fContext;
+
+ // We cache the GrBackendTexture so that if we deleted the GrTexture but the the release proc
+ // has yet not been called (this can happen on Vulkan), then we can create a new texture without
+ // needing to call the fulfill proc again.
+ GrBackendTexture fBackendTex;
+ // The fReleaseHelper is used to track a weak ref on the release proc. This helps us make sure
+ // we are always pairing fulfill and release proc calls correctly.
+ GrReleaseProcHelper* fReleaseHelper = nullptr;
+};
+
+sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
+ const GrBackendFormat& backendFormat,
+ int width,
+ int height,
+ GrMipMapped mipMapped,
+ GrSurfaceOrigin origin,
+ SkColorType colorType,
+ SkAlphaType alphaType,
+ sk_sp<SkColorSpace> colorSpace,
+ TextureFulfillProc textureFulfillProc,
+ TextureReleaseProc textureReleaseProc,
+ TextureContext textureContext) {
+ if (!context) {
+ return nullptr;
+ }
+
+ if (width <= 0 || height <= 0) {
+ return nullptr;
+ }
+
+ if (!textureFulfillProc || !textureReleaseProc) {
+ return nullptr;
+ }
+
+ SkImageInfo info = SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
+ if (!SkImageInfoIsValidAllowNumericalCS(info)) {
+ return nullptr;
+ }
+ GrPixelConfig config = kUnknown_GrPixelConfig;
+ if (!context->caps()->getConfigFromBackendFormat(backendFormat, colorType, &config)) {
+ return nullptr;
+ }
+
+ GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
+
+ GrSurfaceDesc desc;
+ desc.fWidth = width;
+ desc.fHeight = height;
+ desc.fConfig = config;
+
+ PromiseImageHelper promiseHelper(textureFulfillProc, textureReleaseProc, textureContext);
+
+ sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
+ [promiseHelper] (GrResourceProvider* resourceProvider) mutable {
+ if (!resourceProvider) {
+ promiseHelper.reset();
+ return sk_sp<GrTexture>();
+ }
+
+ return promiseHelper.getTexture(resourceProvider);
+ }, desc, origin, mipMapped, GrRenderTargetFlags::kNone, SkBackingFit::kExact,
+ SkBudgeted::kNo, GrSurfaceProxy::LazyInstantiationType::kUninstantiate);
+
+ if (!proxy) {
+ return nullptr;
+ }
+
+ return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType, std::move(proxy),
+ std::move(colorSpace), SkBudgeted::kNo);
+}
+
sk_sp<SkImage> SkImage::MakeCrossContextFromEncoded(GrContext* context, sk_sp<SkData> encoded,
bool buildMips, SkColorSpace* dstColorSpace) {
sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(std::move(encoded));
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 9cc28c317a..24a8c78869 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -60,6 +60,59 @@ public:
sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType,
SkTransferFunctionBehavior) const override;
+ typedef ReleaseContext TextureContext;
+ typedef void (*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
+
+ /**
+ Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main
+ difference is that the client doesn't have the backend texture on the gpu yet but they know
+ all the properties of the texture. So instead of passing in a GrBackendTexture the client
+ supplies a GrBackendFormat, width, height, and GrMipMapped state.
+
+ When we actually send the draw calls to the GPU, we will call the textureFulfillProc and
+ the client will return a GrBackendTexture to us. The properties of the GrBackendTexture must
+ match those set during the SkImage creation, and it must have a valid backend gpu texture.
+ The gpu texture supplied by the client must stay valid until we call the textureReleaseProc.
+
+ When we are done with the texture returned by the textureFulfillProc we will call the
+ textureReleaseProc passing in the textureContext. This is a signal to the client that they
+ are free to delete the underlying gpu texture. If future draws also use the same promise
+ image we will call the textureFulfillProc again if we've already called the
+ textureReleaseProc. We will always call textureFulfillProc and textureReleaseProc in pairs.
+ In other words we will never call textureFulfillProc or textureReleaseProc multiple times
+ for the same textureContext before calling the other.
+
+ @param context Gpu context
+ @param backendFormat format of promised gpu texture
+ @param width width of promised gpu texture
+ @param height height of promised gpu texture
+ @param mipMapped mip mapped state of promised gpu texture
+ @param origin one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
+ @param colorType one of: kUnknown_SkColorType, kAlpha_8_SkColorType,
+ kRGB_565_SkColorType, kARGB_4444_SkColorType,
+ kRGBA_8888_SkColorType, kBGRA_8888_SkColorType,
+ kGray_8_SkColorType, kRGBA_F16_SkColorType
+ @param alphaType one of: kUnknown_SkAlphaType, kOpaque_SkAlphaType,
+ kPremul_SkAlphaType, kUnpremul_SkAlphaType
+ @param colorSpace range of colors; may be nullptr
+ @param textureFulfillProc function called to get actual gpu texture
+ @param textureReleaseProc function called when texture can be released
+ @param textureContext state passed to textureFulfillProc and textureReleaseProc
+ @return created SkImage, or nullptr
+ */
+ static sk_sp<SkImage> MakePromiseTexture(GrContext* context,
+ const GrBackendFormat& backendFormat,
+ int width,
+ int height,
+ GrMipMapped mipMapped,
+ GrSurfaceOrigin origin,
+ SkColorType colorType,
+ SkAlphaType alphaType,
+ sk_sp<SkColorSpace> colorSpace,
+ TextureFulfillProc textureFulfillProc,
+ TextureReleaseProc textureReleaseProc,
+ TextureContext textureContext);
+
bool onIsValid(GrContext*) const override;
private:
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index b0c80c32ef..6700ba5570 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -42,9 +42,9 @@ static GrBackendFormat create_backend_format(GrContext* context, SkColorType col
if (kRGBA_8888_SkColorType == colorType) {
VkFormat format = caps->srgbSupport() ? VK_FORMAT_R8G8B8A8_SRGB
: VK_FORMAT_R8G8B8A8_UNORM;
- return GrBackendFormat::MakeVK(format);
+ return GrBackendFormat::MakeVk(format);
} else if (kRGBA_F16_SkColorType == colorType) {
- return GrBackendFormat::MakeVK(VK_FORMAT_R16G16B16A16_SFLOAT);
+ return GrBackendFormat::MakeVk(VK_FORMAT_R16G16B16A16_SFLOAT);
}
break;
#endif
diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp
index 88cbd01649..b7c4a9c43c 100644
--- a/tests/LazyProxyTest.cpp
+++ b/tests/LazyProxyTest.cpp
@@ -217,7 +217,8 @@ DEF_GPUTEST(LazyProxyReleaseTest, reporter, /* options */) {
using LazyInstantiationType = GrSurfaceProxy::LazyInstantiationType;
for (bool doInstantiate : {true, false}) {
for (auto lazyType : {LazyInstantiationType::kSingleUse,
- LazyInstantiationType::kMultipleUse}) {
+ LazyInstantiationType::kMultipleUse,
+ LazyInstantiationType::kUninstantiate}) {
int testCount = 0;
int* testCountPtr = &testCount;
sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
@@ -229,10 +230,8 @@ DEF_GPUTEST(LazyProxyReleaseTest, reporter, /* options */) {
*testCountPtr = 1;
return sk_sp<GrTexture>();
},
- desc, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, SkBackingFit::kExact,
- SkBudgeted::kNo);
-
- proxy->priv().testingOnly_setLazyInstantiationType(lazyType);
+ desc, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, GrRenderTargetFlags::kNone,
+ SkBackingFit::kExact, SkBudgeted::kNo, lazyType);
REPORTER_ASSERT(reporter, 0 == testCount);
@@ -423,10 +422,8 @@ DEF_GPUTEST(LazyProxyUninstantiateTest, reporter, /* options */) {
texture->setRelease(UninstantiateReleaseProc, releasePtr);
return texture;
},
- desc, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, SkBackingFit::kExact,
- SkBudgeted::kNo);
-
- lazyProxy->priv().testingOnly_setLazyInstantiationType(lazyType);
+ desc, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, GrRenderTargetFlags::kNone,
+ SkBackingFit::kExact, SkBudgeted::kNo, lazyType);
rtc->priv().testingOnly_addDrawOp(skstd::make_unique<LazyUninstantiateTestOp>(lazyProxy));
diff --git a/tests/PromiseImageTest.cpp b/tests/PromiseImageTest.cpp
new file mode 100644
index 0000000000..b475147c78
--- /dev/null
+++ b/tests/PromiseImageTest.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrBackendSurface.h"
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "GrTest.h"
+#include "SkDeferredDisplayListRecorder.h"
+#include "SkImage_Gpu.h"
+
+using namespace sk_gpu_test;
+
+struct PromiseTextureChecker {
+ explicit PromiseTextureChecker(const GrBackendTexture& tex)
+ : fTexture(tex)
+ , fFulfillCount(0)
+ , fReleaseCount(0) {}
+ GrBackendTexture fTexture;
+ int fFulfillCount;
+ int fReleaseCount;
+ static void Fulfill(void* self, GrBackendTexture* outTexture) {
+ static_cast<PromiseTextureChecker*>(self)->fFulfillCount++;
+ *outTexture = static_cast<PromiseTextureChecker*>(self)->fTexture;
+ }
+ static void Release(void* self) {
+ static_cast<PromiseTextureChecker*>(self)->fReleaseCount++;
+ }
+};
+
+// Because Vulkan may delay when it actually calls the ReleaseProcs depending on when command
+// buffers finish their work, we need some slight wiggle room in what values we expect for fulfill
+// and release counts.
+static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseChecker,
+ bool countsMustBeEqual,
+ int expectedFulfillCnt,
+ int expectedReleaseCnt,
+ bool expectedRequired,
+ skiatest::Reporter* reporter) {
+ bool result = true;
+ int countDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
+ // FulfillCount should always equal ReleaseCount or be at most one higher
+ if (countDiff != 0) {
+ if (countsMustBeEqual) {
+ result = false;
+ REPORTER_ASSERT(reporter, 0 == countDiff);
+ } else if (countDiff != 1) {
+ result = false;
+ REPORTER_ASSERT(reporter, 0 == countDiff || 1 == countDiff);
+ }
+ }
+
+ int fulfillDiff = expectedFulfillCnt - promiseChecker.fFulfillCount;
+ REPORTER_ASSERT(reporter, fulfillDiff >= 0);
+ if (fulfillDiff != 0) {
+ if (expectedRequired) {
+ result = false;
+ REPORTER_ASSERT(reporter, expectedFulfillCnt == promiseChecker.fFulfillCount);
+ } else if (fulfillDiff > 1) {
+ result = false;
+ REPORTER_ASSERT(reporter, fulfillDiff <= 1);
+ }
+ }
+
+ int releaseDiff = expectedReleaseCnt - promiseChecker.fReleaseCount;
+ REPORTER_ASSERT(reporter, releaseDiff >= 0);
+ if (releaseDiff != 0) {
+ if (expectedRequired) {
+ result = false;
+ REPORTER_ASSERT(reporter, expectedReleaseCnt == promiseChecker.fReleaseCount);
+ } else if (releaseDiff > 1) {
+ result = false;
+ REPORTER_ASSERT(reporter, releaseDiff <= 1);
+ }
+ }
+
+ return result;
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
+ const int kWidth = 10;
+ const int kHeight = 10;
+ std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
+
+ GrContext* ctx = ctxInfo.grContext();
+ GrGpu* gpu = ctx->contextPriv().getGpu();
+
+ GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
+ pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true, GrMipMapped::kNo);
+ REPORTER_ASSERT(reporter, backendTex.isValid());
+
+ GrBackendFormat backendFormat = GrTest::CreateBackendFormatFromTexture(backendTex);
+ REPORTER_ASSERT(reporter, backendFormat.isValid());
+
+ PromiseTextureChecker promiseChecker(backendTex);
+ GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
+ sk_sp<SkImage> refImg(
+ SkImage_Gpu::MakePromiseTexture(ctx, backendFormat, kWidth, kHeight, GrMipMapped::kNo,
+ texOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+ nullptr,
+ PromiseTextureChecker::Fulfill,
+ PromiseTextureChecker::Release,
+ &promiseChecker));
+
+ SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
+ SkCanvas* canvas = surface->getCanvas();
+
+ int expectedFulfillCnt = 0;
+ int expectedReleaseCnt = 0;
+
+ canvas->drawImage(refImg, 0, 0);
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ reporter));
+
+ bool isVulkan = kVulkan_GrBackend == ctx->contextPriv().getBackend();
+ canvas->flush();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ !isVulkan,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ !isVulkan,
+ reporter));
+
+ gpu->testingOnly_flushGpuAndSync();
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ reporter));
+
+ canvas->drawImage(refImg, 0, 0);
+ canvas->drawImage(refImg, 0, 0);
+
+ canvas->flush();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+
+ gpu->testingOnly_flushGpuAndSync();
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ reporter));
+
+ // Now test code path on Vulkan where we released the texture, but the GPU isn't done with
+ // resource yet and we do another draw. We should only call fulfill on the first draw and
+ // use the cached GrBackendTexture on the second. Release should only be called after the second
+ // draw is finished.
+ canvas->drawImage(refImg, 0, 0);
+ canvas->flush();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ !isVulkan,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ !isVulkan,
+ reporter));
+
+ canvas->drawImage(refImg, 0, 0);
+ canvas->flush();
+ expectedFulfillCnt++;
+
+ gpu->testingOnly_flushGpuAndSync();
+ expectedReleaseCnt++;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ !isVulkan,
+ reporter));
+ expectedFulfillCnt = promiseChecker.fFulfillCount;
+ expectedReleaseCnt = promiseChecker.fReleaseCount;
+
+ refImg.reset();
+
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ reporter));
+
+ gpu->deleteTestingOnlyBackendTexture(&backendTex);
+}
+
+#endif
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index 2160c7f47e..ae86aaed82 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -78,6 +78,30 @@ GrBackendTexture CreateBackendTexture(GrBackend backend, int width, int height,
}
}
+GrBackendFormat CreateBackendFormatFromTexture(const GrBackendTexture& tex) {
+ switch (tex.backend()) {
+#ifdef SK_VULKAN
+ case kVulkan_GrBackend: {
+ const GrVkImageInfo* vkInfo = tex.getVkImageInfo();
+ SkASSERT(vkInfo);
+ return GrBackendFormat::MakeVk(vkInfo->fFormat);
+ }
+#endif
+ case kOpenGL_GrBackend: {
+ const GrGLTextureInfo* glInfo = tex.getGLTextureInfo();
+ SkASSERT(glInfo);
+ return GrBackendFormat::MakeGL(glInfo->fFormat, glInfo->fTarget);
+ }
+ case kMock_GrBackend: {
+ const GrMockTextureInfo* mockInfo = tex.getMockTextureInfo();
+ SkASSERT(mockInfo);
+ return GrBackendFormat::MakeMock(mockInfo->fConfig);
+ }
+ default:
+ return GrBackendFormat();
+ }
+}
+
} // namespace GrTest
bool GrSurfaceProxy::isWrapped_ForTesting() const {
diff --git a/tools/gpu/GrTest.h b/tools/gpu/GrTest.h
index 6666ab1a20..76e5e93e03 100644
--- a/tools/gpu/GrTest.h
+++ b/tools/gpu/GrTest.h
@@ -22,6 +22,8 @@ namespace GrTest {
// TODO: remove this. It is only used in the SurfaceSemaphores Test.
GrBackendTexture CreateBackendTexture(GrBackend, int width, int height,
GrPixelConfig, GrMipMapped, GrBackendObject);
+
+ GrBackendFormat CreateBackendFormatFromTexture(const GrBackendTexture& tex);
};
#endif