Consolidate details of lazy proxy creation for promise image textures.
The SkSpecialImage helper is now function-local and *is* the lazy
instantiation callback rather than coordinating with one in the
promise SkImage factory functions.
Bug: skia:
Change-Id: I4883618ef440d09a9ac7959cef254e6f5a49aa55
Reviewed-on: https://skia-review.googlesource.com/c/175822
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index b12fee1..3bd42cf 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -381,53 +381,151 @@
return true;
}
-/////////////////////////////////////////////////////////////////////////////////////////////////
-sk_sp<GrTexture> SkPromiseImageHelper::getTexture(GrResourceProvider* resourceProvider,
- GrPixelConfig config) {
- // 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<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
+ GrContext* context, int width, int height, GrSurfaceOrigin origin, GrPixelConfig config,
+ GrBackendFormat backendFormat, GrMipMapped mipMapped,
+ SkImage_GpuBase::TextureFulfillProc fulfillProc,
+ SkImage_GpuBase::TextureReleaseProc releaseProc, SkImage_GpuBase::PromiseDoneProc doneProc,
+ SkImage_GpuBase::TextureContext textureContext) {
+ SkASSERT(context);
+ SkASSERT(width > 0 && height > 0);
+ SkASSERT(doneProc);
+
+ if (!fulfillProc || !releaseProc) {
+ doneProc(textureContext);
+ return nullptr;
}
- sk_sp<GrTexture> tex;
- if (!fReleaseHelper) {
- fFulfillProc(fContext, &fBackendTex);
- fBackendTex.fConfig = config;
- 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,
- kRead_GrIOType);
- 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 SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
- // Take a weak ref
- fReleaseHelper->weak_ref();
- } else {
- SkASSERT(fBackendTex.isValid());
- tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
- kRead_GrIOType);
- 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());
+ if (mipMapped == GrMipMapped::kYes &&
+ GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
+ // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
+ // well.
+ doneProc(textureContext);
+ return nullptr;
}
- SkASSERT(tex);
- // Pass the hard ref off to the texture
- tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
- return tex;
+
+ /**
+ * This helper class manages the ref counting for the the ReleaseProc and DoneProc for promise
+ * images. It holds a weak ref on the ReleaseProc (hard refs are owned by GrTextures). The weak
+ * ref allows us to reuse an outstanding ReleaseProc (because we dropped our GrTexture but the
+ * GrTexture isn't done on the GPU) without needing to call FulfillProc again. It also holds a
+ * hard ref on the DoneProc. The idea is that after every flush we may call the ReleaseProc so
+ * that the client can free up their GPU memory if they want to. The life time of the DoneProc
+ * matches that of any outstanding ReleaseProc as well as the PromiseLazyInstantiateCallback.
+ * Thus we won't call the DoneProc until all ReleaseProcs are finished and we are finished with
+ * the PromiseImageHelper (i.e. won't call FulfillProc again).
+ */
+ class PromiseLazyInstantiateCallback {
+ public:
+ PromiseLazyInstantiateCallback(SkImage_GpuBase::TextureFulfillProc fulfillProc,
+ SkImage_GpuBase::TextureReleaseProc releaseProc,
+ SkImage_GpuBase::PromiseDoneProc doneProc,
+ SkImage_GpuBase::TextureContext context,
+ GrPixelConfig config)
+ : fFulfillProc(fulfillProc)
+ , fReleaseProc(releaseProc)
+ , fContext(context)
+ , fConfig(config) {
+ fDoneHelper.reset(new GrReleaseProcHelper(doneProc, context));
+ }
+
+ sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
+ if (!resourceProvider) {
+ this->reset();
+ return sk_sp<GrTexture>();
+ }
+
+ // 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);
+ fBackendTex.fConfig = fConfig;
+ 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,
+ kRead_GrIOType);
+ 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 SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
+ // Take a weak ref
+ fReleaseHelper->weak_ref();
+ } else {
+ SkASSERT(fBackendTex.isValid());
+ tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
+ kRead_GrIOType);
+ 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 std::move(tex);
+ }
+
+ private:
+ void reset() {
+ this->resetReleaseHelper();
+ fDoneHelper.reset();
+ }
+
+ // Weak unrefs fReleaseHelper and sets it to null
+ void resetReleaseHelper() {
+ if (fReleaseHelper) {
+ fReleaseHelper->weak_unref();
+ fReleaseHelper = nullptr;
+ }
+ }
+
+ SkImage_GpuBase::TextureFulfillProc fFulfillProc;
+ SkImage_GpuBase::TextureReleaseProc fReleaseProc;
+ SkImage_GpuBase::TextureContext fContext;
+
+ GrPixelConfig fConfig;
+ // 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.
+ SkPromiseReleaseProcHelper* fReleaseHelper = nullptr;
+ // We don't want to call the fDoneHelper until we are done with the PromiseImageHelper and
+ // all ReleaseHelpers are finished. Thus we hold a hard ref here and we will pass a hard ref
+ // to each fReleaseHelper we make.
+ sk_sp<GrReleaseProcHelper> fDoneHelper;
+ } callback(fulfillProc, releaseProc, doneProc, textureContext, config);
+
+ GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
+
+ GrSurfaceDesc desc;
+ desc.fWidth = width;
+ desc.fHeight = height;
+ desc.fConfig = config;
+
+ // We pass kReadOnly here since we should treat content of the client's texture as immutable.
+ return proxyProvider->createLazyProxy(std::move(callback), backendFormat, desc, origin,
+ mipMapped, GrInternalSurfaceFlags::kReadOnly,
+ SkBackingFit::kExact, SkBudgeted::kNo,
+ GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
}