From a8d9211bc3f34fcb3cf331d8fd76e4ea5803fe20 Mon Sep 17 00:00:00 2001 From: Greg Daniel Date: Fri, 9 Mar 2018 12:05:04 -0500 Subject: Add promise images for deferred instantiation of wrapped gpu textures This will allow a client to make an SkImage that "wraps" a gpu texture, however the client does need to supply the actual gpu texture at Image creation time. Instead it is retrieve at flush time via a callback. Bug: skia: Change-Id: I6267a55ab7102101a7bd80a6f547b6a870d2df08 Reviewed-on: https://skia-review.googlesource.com/109021 Commit-Queue: Greg Daniel Reviewed-by: Robert Phillips Reviewed-by: Cary Clark --- src/core/SkDeferredDisplayListRecorder.cpp | 31 +++++++ src/gpu/GrProxyProvider.cpp | 19 +++- src/gpu/GrProxyProvider.h | 5 + src/gpu/GrSurfaceProxyPriv.h | 4 - src/image/SkImage_Gpu.cpp | 142 +++++++++++++++++++++++++++++ src/image/SkImage_Gpu.h | 53 +++++++++++ 6 files changed, 245 insertions(+), 9 deletions(-) (limited to 'src') 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 SkDeferredDisplayListRecorder::detach() { } +sk_sp SkDeferredDisplayListRecorder::makePromiseTexture( + const GrBackendFormat& backendFormat, + int width, + int height, + GrMipMapped mipMapped, + GrSurfaceOrigin origin, + SkColorType colorType, + SkAlphaType alphaType, + sk_sp 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 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 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 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( 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 @@ -165,6 +166,10 @@ public: * It also must support being passed in a null GrResourceProvider. When this happens, the * callback should cleanup any resources it captured and return an empty sk_sp. */ + sk_sp createLazyProxy(LazyInstantiateCallback&&, const GrSurfaceDesc&, + GrSurfaceOrigin, GrMipMapped, GrRenderTargetFlags, + SkBackingFit, SkBudgeted, LazyInstantiationType); + sk_sp createLazyProxy(LazyInstantiateCallback&&, const GrSurfaceDesc&, GrSurfaceOrigin, GrMipMapped, GrRenderTargetFlags, SkBackingFit, SkBudgeted); 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::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 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 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(); + } + + 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(); + } + 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(); + } + + SkAssertResult(fReleaseHelper->try_ref()); + } + SkASSERT(tex); + // Pass the hard ref off to the texture + tex->setRelease(sk_sp(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_Gpu::MakePromiseTexture(GrContext* context, + const GrBackendFormat& backendFormat, + int width, + int height, + GrMipMapped mipMapped, + GrSurfaceOrigin origin, + SkColorType colorType, + SkAlphaType alphaType, + sk_sp 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 proxy = proxyProvider->createLazyProxy( + [promiseHelper] (GrResourceProvider* resourceProvider) mutable { + if (!resourceProvider) { + promiseHelper.reset(); + return sk_sp(); + } + + return promiseHelper.getTexture(resourceProvider); + }, desc, origin, mipMapped, GrRenderTargetFlags::kNone, SkBackingFit::kExact, + SkBudgeted::kNo, GrSurfaceProxy::LazyInstantiationType::kUninstantiate); + + if (!proxy) { + return nullptr; + } + + return sk_make_sp(context, kNeedNewImageUniqueID, alphaType, std::move(proxy), + std::move(colorSpace), SkBudgeted::kNo); +} + sk_sp SkImage::MakeCrossContextFromEncoded(GrContext* context, sk_sp encoded, bool buildMips, SkColorSpace* dstColorSpace) { sk_sp 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 onMakeColorSpace(sk_sp, 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 MakePromiseTexture(GrContext* context, + const GrBackendFormat& backendFormat, + int width, + int height, + GrMipMapped mipMapped, + GrSurfaceOrigin origin, + SkColorType colorType, + SkAlphaType alphaType, + sk_sp colorSpace, + TextureFulfillProc textureFulfillProc, + TextureReleaseProc textureReleaseProc, + TextureContext textureContext); + bool onIsValid(GrContext*) const override; private: -- cgit v1.2.3