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/image/SkImage_Gpu.cpp | 142 ++++++++++++++++++++++++++++++++++++++++++++++ src/image/SkImage_Gpu.h | 53 +++++++++++++++++ 2 files changed, 195 insertions(+) (limited to 'src/image') 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