Revert "Revert "Reuse GrTexture instances when the same GrBackendTexture is used to""
This reverts commit 426ba463d54c7fdd6f3b94f88b66c10c0212dafd.
Bug: skia:8613
Change-Id: Iacaf40549369110aa95015e8d4579ec41db22d13
Reviewed-on: https://skia-review.googlesource.com/c/182963
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp
index da02727..716d241 100644
--- a/src/core/SkDeferredDisplayListRecorder.cpp
+++ b/src/core/SkDeferredDisplayListRecorder.cpp
@@ -6,7 +6,7 @@
*/
#include "SkDeferredDisplayListRecorder.h"
-
+#include "SkMessageBus.h"
#include "SkDeferredDisplayList.h"
#include "SkSurface.h"
#include "SkSurfaceCharacterization.h"
@@ -31,26 +31,58 @@
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContext) {
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContext) {
+ return nullptr;
+}
+
+sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
+ const GrBackendFormat& backendFormat,
+ int width,
+ int height,
+ GrMipMapped mipMapped,
+ GrSurfaceOrigin origin,
+ SkColorType colorType,
+ SkAlphaType alphaType,
+ sk_sp<SkColorSpace> colorSpace,
+ LegacyPromiseImageTextureFulfillProc textureFulfillProc,
+ LegacyPromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContext) {
return nullptr;
}
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
- SkYUVColorSpace yuvColorSpace,
- const GrBackendFormat yuvaFormats[],
- const SkISize yuvaSizes[],
- const SkYUVAIndex yuvaIndices[4],
- int imageWidth,
- int imageHeight,
- GrSurfaceOrigin imageOrigin,
- sk_sp<SkColorSpace> imageColorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContexts[]) {
+ SkYUVColorSpace yuvColorSpace,
+ const GrBackendFormat yuvaFormats[],
+ const SkISize yuvaSizes[],
+ const SkYUVAIndex yuvaIndices[4],
+ int imageWidth,
+ int imageHeight,
+ GrSurfaceOrigin imageOrigin,
+ sk_sp<SkColorSpace> imageColorSpace,
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContexts[]) {
+ return nullptr;
+}
+
+sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
+ SkYUVColorSpace yuvColorSpace,
+ const GrBackendFormat yuvaFormats[],
+ const SkISize yuvaSizes[],
+ const SkYUVAIndex yuvaIndices[4],
+ int imageWidth,
+ int imageHeight,
+ GrSurfaceOrigin imageOrigin,
+ sk_sp<SkColorSpace> imageColorSpace,
+ LegacyPromiseImageTextureFulfillProc textureFulfillProc,
+ LegacyPromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContexts[]) {
return nullptr;
}
@@ -60,10 +92,11 @@
#include "GrProxyProvider.h"
#include "GrRenderTargetContext.h"
#include "GrTexture.h"
-
#include "SkGr.h"
#include "SkImage_Gpu.h"
#include "SkImage_GpuYUVA.h"
+#include "SkMakeUnique.h"
+#include "SkPromiseImageTexture.h"
#include "SkSurface_Gpu.h"
#include "SkYUVASizeInfo.h"
@@ -216,10 +249,10 @@
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContext) {
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContext) {
if (!fContext) {
return nullptr;
}
@@ -235,10 +268,107 @@
std::move(colorSpace),
textureFulfillProc,
textureReleaseProc,
- promiseDoneProc,
+ textureDoneProc,
textureContext);
}
+// Converts from the old legacy APIs based on GrBackendTexture to the new implementation based on
+// PromiseImageTexture.
+static void wrap_legacy(
+ SkDeferredDisplayListRecorder::LegacyPromiseImageTextureFulfillProc textureFulfillProc,
+ SkDeferredDisplayListRecorder::LegacyPromiseImageTextureReleaseProc textureReleaseProc,
+ SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc textureDoneProc,
+ const SkDeferredDisplayListRecorder::PromiseImageTextureContext textureContexts[],
+ int numTextures,
+ SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc* wrappedFulfillProc,
+ SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc* wrappedReleaseProc,
+ SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc* wrappedDoneProc,
+ SkDeferredDisplayListRecorder::PromiseImageTextureContext wrappedTextureContext[]) {
+ struct WrapperContext {
+ SkDeferredDisplayListRecorder::LegacyPromiseImageTextureFulfillProc fLegacyFulfill;
+ SkDeferredDisplayListRecorder::LegacyPromiseImageTextureReleaseProc fLegacyRelease;
+ SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc fDone;
+ SkDeferredDisplayListRecorder::PromiseImageTextureContext fOriginalContext;
+ std::unique_ptr<SkPromiseImageTexture> fPromiseImageTexture;
+ };
+ *wrappedFulfillProc = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext context) {
+ auto* wc = static_cast<WrapperContext*>(context);
+ GrBackendTexture backendTexture;
+ wc->fLegacyFulfill(wc->fOriginalContext, &backendTexture);
+ wc->fPromiseImageTexture = skstd::make_unique<SkPromiseImageTexture>(backendTexture);
+ return wc->fPromiseImageTexture.get();
+ };
+ *wrappedReleaseProc = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext context,
+ const SkPromiseImageTexture*) {
+ auto* wc = static_cast<WrapperContext*>(context);
+ wc->fLegacyRelease(wc->fOriginalContext);
+ wc->fPromiseImageTexture.reset();
+ };
+ *wrappedDoneProc = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext context) {
+ const auto* wc = static_cast<WrapperContext*>(context);
+ wc->fDone(wc->fOriginalContext);
+ SkASSERT(!wc->fPromiseImageTexture);
+ delete wc;
+ };
+ for (int i = 0; i < numTextures; ++i) {
+ wrappedTextureContext[i] = new WrapperContext{textureFulfillProc, textureReleaseProc,
+ textureDoneProc, textureContexts[i], nullptr};
+ }
+}
+
+sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
+ const GrBackendFormat& backendFormat,
+ int width,
+ int height,
+ GrMipMapped mipMapped,
+ GrSurfaceOrigin origin,
+ SkColorType colorType,
+ SkAlphaType alphaType,
+ sk_sp<SkColorSpace> colorSpace,
+ LegacyPromiseImageTextureFulfillProc textureFulfillProc,
+ LegacyPromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContext) {
+ if (!fContext) {
+ return nullptr;
+ }
+
+ SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc wrappedFulfillProc;
+ SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc wrappedReleaseProc;
+ SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc wrappedDoneProc;
+ SkDeferredDisplayListRecorder::PromiseImageTextureContext wrappedTextureContext;
+ if (!textureDoneProc) {
+ return nullptr;
+ }
+ if (!textureFulfillProc || !textureReleaseProc) {
+ textureDoneProc(textureContext);
+ return nullptr;
+ }
+
+ wrap_legacy(textureFulfillProc,
+ textureReleaseProc,
+ textureDoneProc,
+ &textureContext,
+ 1,
+ &wrappedFulfillProc,
+ &wrappedReleaseProc,
+ &wrappedDoneProc,
+ &wrappedTextureContext);
+ return SkImage_Gpu::MakePromiseTexture(fContext.get(),
+ backendFormat,
+ width,
+ height,
+ mipMapped,
+ origin,
+ colorType,
+ alphaType,
+ std::move(colorSpace),
+ wrappedFulfillProc,
+ wrappedReleaseProc,
+ wrappedDoneProc,
+ wrappedTextureContext);
+}
+
sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
SkYUVColorSpace yuvColorSpace,
const GrBackendFormat yuvaFormats[],
@@ -248,10 +378,10 @@
int imageHeight,
GrSurfaceOrigin imageOrigin,
sk_sp<SkColorSpace> imageColorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContexts[]) {
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContexts[]) {
if (!fContext) {
return nullptr;
}
@@ -267,8 +397,66 @@
std::move(imageColorSpace),
textureFulfillProc,
textureReleaseProc,
- promiseDoneProc,
+ textureDoneProc,
textureContexts);
}
+sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
+ SkYUVColorSpace yuvColorSpace,
+ const GrBackendFormat yuvaFormats[],
+ const SkISize yuvaSizes[],
+ const SkYUVAIndex yuvaIndices[4],
+ int imageWidth,
+ int imageHeight,
+ GrSurfaceOrigin imageOrigin,
+ sk_sp<SkColorSpace> imageColorSpace,
+ LegacyPromiseImageTextureFulfillProc textureFulfillProc,
+ LegacyPromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContexts[]) {
+ if (!fContext) {
+ return nullptr;
+ }
+
+ int numTextures;
+ bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
+
+ SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc wrappedFulfillProc;
+ SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc wrappedReleaseProc;
+ SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc wrappedDoneProc;
+ SkDeferredDisplayListRecorder::PromiseImageTextureContext wrappedTextureContexts[4];
+ if (!textureDoneProc) {
+ return nullptr;
+ }
+ if (!valid || !textureFulfillProc || !textureReleaseProc) {
+ for (int i = 0; i < numTextures; ++i) {
+ textureDoneProc(textureContexts[i]);
+ }
+ return nullptr;
+ }
+
+ wrap_legacy(textureFulfillProc,
+ textureReleaseProc,
+ textureDoneProc,
+ textureContexts,
+ numTextures,
+ &wrappedFulfillProc,
+ &wrappedReleaseProc,
+ &wrappedDoneProc,
+ wrappedTextureContexts);
+
+ return SkImage_GpuYUVA::MakePromiseYUVATexture(fContext.get(),
+ yuvColorSpace,
+ yuvaFormats,
+ yuvaSizes,
+ yuvaIndices,
+ imageWidth,
+ imageHeight,
+ imageOrigin,
+ std::move(imageColorSpace),
+ wrappedFulfillProc,
+ wrappedReleaseProc,
+ wrappedDoneProc,
+ wrappedTextureContexts);
+}
#endif
diff --git a/src/core/SkPromiseImageTexture.cpp b/src/core/SkPromiseImageTexture.cpp
new file mode 100644
index 0000000..76eabc7
--- /dev/null
+++ b/src/core/SkPromiseImageTexture.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPromiseImageTexture.h"
+#include "SkMessageBus.h"
+
+#if SK_SUPPORT_GPU
+
+std::atomic<uint32_t> SkPromiseImageTexture::gUniqueID{1};
+
+SkPromiseImageTexture::SkPromiseImageTexture(const GrBackendTexture& backendTexture) {
+ if (backendTexture.isValid()) {
+ fBackendTexture = backendTexture;
+ fUniqueID = gUniqueID++;
+ }
+}
+
+SkPromiseImageTexture::SkPromiseImageTexture(SkPromiseImageTexture&& that) {
+ *this = std::move(that);
+}
+
+SkPromiseImageTexture& SkPromiseImageTexture::operator=(SkPromiseImageTexture&& that) {
+ for (const auto& msg : fMessages) {
+ SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(msg);
+ }
+ fMessages = that.fMessages;
+ that.fMessages.reset();
+ fBackendTexture = that.fBackendTexture;
+ that.fBackendTexture = {};
+ fUniqueID = that.fUniqueID;
+ that.fUniqueID = SK_InvalidUniqueID;
+ return *this;
+}
+
+SkPromiseImageTexture::~SkPromiseImageTexture() {
+ for (const auto& msg : fMessages) {
+ SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(msg);
+ }
+}
+
+void SkPromiseImageTexture::addKeyToInvalidate(uint32_t contextID, const GrUniqueKey& key) {
+ SkASSERT(contextID != SK_InvalidUniqueID);
+ SkASSERT(key.isValid());
+ fMessages.emplace_back(key, contextID);
+}
+
+#if GR_TEST_UTILS
+SkTArray<GrUniqueKey> SkPromiseImageTexture::testingOnly_uniqueKeysToInvalidate() const {
+ SkTArray<GrUniqueKey> results;
+ for (const auto& msg : fMessages) {
+ results.push_back(msg.key());
+ }
+ return results;
+}
+#endif
+
+#endif
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index fe1bdd3..84a97a1 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -66,8 +66,7 @@
* must be sure that if a resource of exists in the cache with the given unique key then it is
* of type T.
*/
- template <typename T>
- sk_sp<T> findByUniqueKey(const GrUniqueKey& key) {
+ template <typename T = GrGpuResource> sk_sp<T> findByUniqueKey(const GrUniqueKey& key) {
return sk_sp<T>(static_cast<T*>(this->findResourceByUniqueKey(key).release()));
}
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 8e00eb4..5de8aa9 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -322,10 +322,10 @@
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContext) {
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc promiseDoneProc,
+ PromiseImageTextureContext textureContext) {
// The contract here is that if 'promiseDoneProc' is passed in it should always be called,
// even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
// responsibility for calling the done proc.
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index de0407d..1f82539 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -76,8 +76,9 @@
@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 promiseDoneProc function called when we will no longer call textureFulfillProc
- @param textureContext state passed to textureFulfillProc and textureReleaseProc
+ @param textureDoneProc function called when we will no longer call textureFulfillProc
+ @param textureContext state passed to textureFulfillProc, textureReleaseProc, and
+ promiseDoneProc
@return created SkImage, or nullptr
*/
static sk_sp<SkImage> MakePromiseTexture(GrContext* context,
@@ -89,10 +90,10 @@
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContext);
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContext);
static sk_sp<SkImage> ConvertYUVATexturesToRGB(GrContext*, SkYUVColorSpace yuvColorSpace,
const GrBackendTexture yuvaTextures[],
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 1c013dd..73aecf4 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
+#include "SkImage_GpuBase.h"
#include "GrBackendSurface.h"
#include "GrClip.h"
#include "GrContext.h"
@@ -12,11 +13,11 @@
#include "GrRenderTargetContext.h"
#include "GrTexture.h"
#include "GrTextureAdjuster.h"
-#include "effects/GrYUVtoRGBEffect.h"
#include "SkBitmapCache.h"
#include "SkImage_Gpu.h"
-#include "SkImage_GpuBase.h"
+#include "SkPromiseImageTexture.h"
#include "SkReadPixelsRec.h"
+#include "effects/GrYUVtoRGBEffect.h"
SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
SkAlphaType at, sk_sp<SkColorSpace> cs)
@@ -391,9 +392,10 @@
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) {
+ PromiseImageTextureFulfillProc fulfillProc,
+ PromiseImageTextureReleaseProc releaseProc,
+ PromiseImageTextureDoneProc doneProc,
+ PromiseImageTextureContext textureContext) {
SkASSERT(context);
SkASSERT(width > 0 && height > 0);
SkASSERT(doneProc);
@@ -413,115 +415,169 @@
}
/**
- * 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).
+ * This class is the lazy instantiation callback for promise images. It manages calling the
+ * client's Fulfill, Release, and Done procs. It attempts to reuse a GrTexture instance in
+ * cases where the client provides the same SkPromiseImageTexture for successive Fulfill calls.
+ * The created GrTexture is given a key based on a unique ID associated with the
+ * SkPromiseImageTexture. When the texture enters "idle" state (meaning it is not being used by
+ * the GPU and is at rest in the resource cache) the client's Release proc is called
+ * using GrTexture's idle proc mechanism. If the same SkPromiseImageTexture is provided for
+ * another fulfill we find the cached GrTexture. If the proxy, and therefore this object,
+ * is destroyed, we invalidate the GrTexture's key. Also if the client overwrites or
+ * destroys their SkPromiseImageTexture we invalidate the key.
+ *
+ * Currently a GrTexture is only reused for a given SkPromiseImageTexture if the
+ * SkPromiseImageTexture is reused in Fulfill for the same promise SkImage. However, we'd
+ * like to relax that so that a SkPromiseImageTexture can be reused with different promise
+ * SkImages that will reuse a single GrTexture.
*/
class PromiseLazyInstantiateCallback {
public:
- PromiseLazyInstantiateCallback(SkImage_GpuBase::TextureFulfillProc fulfillProc,
- SkImage_GpuBase::TextureReleaseProc releaseProc,
- SkImage_GpuBase::PromiseDoneProc doneProc,
- SkImage_GpuBase::TextureContext context,
+ PromiseLazyInstantiateCallback(PromiseImageTextureFulfillProc fulfillProc,
+ PromiseImageTextureReleaseProc releaseProc,
+ PromiseImageTextureDoneProc doneProc,
+ PromiseImageTextureContext context,
GrPixelConfig config)
: fFulfillProc(fulfillProc)
, fReleaseProc(releaseProc)
, fContext(context)
, fConfig(config) {
- fDoneHelper.reset(new GrReleaseProcHelper(doneProc, context));
+ fDoneHelper = sk_make_sp<GrReleaseProcHelper>(doneProc, context);
+ static std::atomic<uint32_t> gUniqueID;
+ fUniqueID = gUniqueID.fetch_add(1) + 1;
+ }
+ ~PromiseLazyInstantiateCallback() {
+ // If we've already released the texture then it is safe to call done now. Here we may
+ // be on any thread.
+ if (fIdleContext && fIdleContext->fWasReleased.load()) {
+ // We still own a ref on fDoneHelper so no other thread can be calling the done
+ // proc.
+ fDoneHelper->callAndClear();
+ }
+ // Remove the key from the texture so that the texture will be removed from the cache.
+ // If we didn't just call the done proc above then it will get called when the texture
+ // is removed from the cache after this message is processed.
+ if (fLastFulfilledKey.isValid()) {
+ SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
+ GrUniqueKeyInvalidatedMessage(fLastFulfilledKey, fContextID));
+ }
}
sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
if (!resourceProvider) {
- this->reset();
+ return nullptr;
+ }
+
+ sk_sp<GrTexture> cachedTexture;
+ SkASSERT(fLastFulfilledKey.isValid() == (fLastFulfillID > 0));
+ if (fLastFulfilledKey.isValid()) {
+ auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey);
+ if (surf) {
+ cachedTexture = sk_ref_sp(surf->asTexture());
+ SkASSERT(cachedTexture);
+ }
+ }
+ // If the release callback hasn't been called already by releasing the GrTexture
+ // then we can be sure that won't happen so long as we have a ref to the texture.
+ // Moreoever, only this thread should be able to change the atomic to true, hence the
+ // relaxed memory order.
+ if (cachedTexture && !fIdleContext->fWasReleased.load(std::memory_order_relaxed)) {
+ return std::move(cachedTexture);
+ }
+ GrBackendTexture backendTexture;
+ SkPromiseImageTexture* promiseTexture = fFulfillProc(fContext);
+ if (!promiseTexture) {
+ fReleaseProc(fContext, nullptr);
+ return sk_sp<GrTexture>();
+ }
+ bool same = promiseTexture->uniqueID() == fLastFulfillID;
+ SkASSERT(!same || fLastFulfilledKey.isValid());
+ if (same && cachedTexture) {
+ SkASSERT(fIdleContext->unique());
+ // Reset the purgeable context so that we balance the new fulfill with a release.
+ fIdleContext->ref();
+ SkASSERT(fIdleContext->fReleaseProc == fReleaseProc);
+ SkASSERT(fIdleContext->fTextureContext == fContext);
+ // Memory order relaxed because only this thread can change fWasReleased to true.
+ fIdleContext->fWasReleased.store(false, std::memory_order_relaxed);
+ cachedTexture->setIdleProc(IdleProc, fIdleContext.get());
+ return std::move(cachedTexture);
+ } else if (cachedTexture) {
+ cachedTexture->resourcePriv().removeUniqueKey();
+ // We don't want calling the client's done proc to be tied to the old texture.
+ cachedTexture->setRelease(nullptr);
+ }
+ fLastFulfillID = promiseTexture->uniqueID();
+
+ backendTexture = promiseTexture->backendTexture();
+ backendTexture.fConfig = fConfig;
+ if (!backendTexture.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, promiseTexture);
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();
+ auto tex = resourceProvider->wrapBackendTexture(backendTexture, kBorrow_GrWrapOwnership,
+ kRead_GrIOType);
+ if (!tex) {
+ // Even though we failed to wrap the backend texture, we must call the release
+ // proc to keep our contract of always calling Fulfill and Release in pairs.
+ fReleaseProc(fContext, promiseTexture);
+ return sk_sp<GrTexture>();
}
-
- 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));
+ fIdleContext = sk_make_sp<IdleContext>(fReleaseProc, fContext, promiseTexture);
+ // The texture gets a ref, which is balanced when the idle callback is called.
+ fIdleContext->ref();
+ tex->setIdleProc(IdleProc, fIdleContext.get());
+ tex->setRelease(fDoneHelper);
+ static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+ GrUniqueKey::Builder builder(&fLastFulfilledKey, kDomain, 2, "promise");
+ builder[0] = promiseTexture->uniqueID();
+ builder[1] = fUniqueID;
+ builder.finish();
+ tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
+ SkASSERT(fContextID == SK_InvalidUniqueID ||
+ fContextID == tex->getContext()->uniqueID());
+ fContextID = tex->getContext()->uniqueID();
+ promiseTexture->addKeyToInvalidate(fContextID, fLastFulfilledKey);
return std::move(tex);
}
private:
- void reset() {
- this->resetReleaseHelper();
- fDoneHelper.reset();
+ struct IdleContext : public SkNVRefCnt<IdleContext> {
+ IdleContext(PromiseImageTextureReleaseProc proc, PromiseImageTextureContext context,
+ const SkPromiseImageTexture* texture)
+ : fReleaseProc(proc), fTextureContext(context), fPromiseImageTexture(texture) {}
+ PromiseImageTextureReleaseProc fReleaseProc;
+ PromiseImageTextureContext fTextureContext;
+ const SkPromiseImageTexture* fPromiseImageTexture;
+ std::atomic<bool> fWasReleased{false};
+ };
+ static void IdleProc(void* context) {
+ IdleContext* rc = static_cast<IdleContext*>(context);
+ SkASSERT(!rc->fWasReleased.load());
+ rc->fReleaseProc(rc->fTextureContext, rc->fPromiseImageTexture);
+ rc->fWasReleased.store(true);
+ // Drop the texture's implicit ref on the IdleContext.
+ rc->unref();
}
- // 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;
+ PromiseImageTextureFulfillProc fFulfillProc;
+ PromiseImageTextureReleaseProc fReleaseProc;
+ PromiseImageTextureContext 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<IdleContext> fIdleContext;
sk_sp<GrReleaseProcHelper> fDoneHelper;
+
+ // ID of the last SkPromiseImageTexture given to us by the client.
+ uint32_t fLastFulfillID = 0;
+ // ID of the GrContext that we are interacting with.
+ uint32_t fContextID = SK_InvalidUniqueID;
+ // Unique ID of this lazy instantiation callback.
+ uint32_t fUniqueID;
+ GrUniqueKey fLastFulfilledKey;
} callback(fulfillProc, releaseProc, doneProc, textureContext, config);
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
diff --git a/src/image/SkImage_GpuBase.h b/src/image/SkImage_GpuBase.h
index 7b183b7..5be97c8 100644
--- a/src/image/SkImage_GpuBase.h
+++ b/src/image/SkImage_GpuBase.h
@@ -10,6 +10,7 @@
#include "GrBackendSurface.h"
#include "GrTypesPriv.h"
+#include "SkDeferredDisplayListRecorder.h"
#include "SkImage_Base.h"
#include "SkYUVAIndex.h"
@@ -72,9 +73,12 @@
: kOpaque_SkAlphaType;
}
- typedef ReleaseContext TextureContext;
- typedef void(*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
- typedef void(*PromiseDoneProc)(TextureContext textureContext);
+ using PromiseImageTextureContext = SkDeferredDisplayListRecorder::PromiseImageTextureContext;
+ using PromiseImageTextureFulfillProc =
+ SkDeferredDisplayListRecorder::PromiseImageTextureFulfillProc;
+ using PromiseImageTextureReleaseProc =
+ SkDeferredDisplayListRecorder::PromiseImageTextureReleaseProc;
+ using PromiseImageTextureDoneProc = SkDeferredDisplayListRecorder::PromiseImageTextureDoneProc;
protected:
// Helper for making a lazy proxy for a promise image. The PromiseDoneProc we be called,
@@ -83,8 +87,8 @@
// be null.
static sk_sp<GrTextureProxy> MakePromiseImageLazyProxy(
GrContext*, int width, int height, GrSurfaceOrigin, GrPixelConfig, GrBackendFormat,
- GrMipMapped, SkImage_GpuBase::TextureFulfillProc, SkImage_GpuBase::TextureReleaseProc,
- SkImage_GpuBase::PromiseDoneProc, SkImage_GpuBase::TextureContext);
+ GrMipMapped, PromiseImageTextureFulfillProc, PromiseImageTextureReleaseProc,
+ PromiseImageTextureDoneProc, PromiseImageTextureContext);
static bool RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
const SkRect& rect, SkYUVColorSpace yuvColorSpace,
@@ -99,31 +103,4 @@
typedef SkImage_Base INHERITED;
};
-
-/**
- * This helper holds the normal hard ref for the Release proc as well as a hard ref on the DoneProc.
- * Thus when a GrTexture is being released, it will unref both the ReleaseProc and DoneProc.
- */
-class SkPromiseReleaseProcHelper : public GrReleaseProcHelper {
-public:
- SkPromiseReleaseProcHelper(SkImage_GpuBase::TextureReleaseProc releaseProc,
- SkImage_GpuBase::TextureContext context,
- sk_sp<GrReleaseProcHelper> doneHelper)
- : INHERITED(releaseProc, context)
- , fDoneProcHelper(std::move(doneHelper)) {
- }
-
- void weak_dispose() const override {
- // Call the inherited weak_dispose first so that we call the ReleaseProc before the DoneProc
- // if we hold the last ref to the DoneProc.
- INHERITED::weak_dispose();
- fDoneProcHelper.reset();
- }
-
-private:
- mutable sk_sp<GrReleaseProcHelper> fDoneProcHelper;
-
- typedef GrReleaseProcHelper INHERITED;
-};
-
#endif
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
index aa1d0b3..340f1ef 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -206,19 +206,20 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
-sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(GrContext* context,
- SkYUVColorSpace yuvColorSpace,
- const GrBackendFormat yuvaFormats[],
- const SkISize yuvaSizes[],
- const SkYUVAIndex yuvaIndices[4],
- int imageWidth,
- int imageHeight,
- GrSurfaceOrigin imageOrigin,
- sk_sp<SkColorSpace> imageColorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContexts[]) {
+sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
+ GrContext* context,
+ SkYUVColorSpace yuvColorSpace,
+ const GrBackendFormat yuvaFormats[],
+ const SkISize yuvaSizes[],
+ const SkYUVAIndex yuvaIndices[4],
+ int imageWidth,
+ int imageHeight,
+ GrSurfaceOrigin imageOrigin,
+ sk_sp<SkColorSpace> imageColorSpace,
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc promiseDoneProc,
+ PromiseImageTextureContext textureContexts[]) {
int numTextures;
bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
@@ -289,4 +290,3 @@
kNeedNewImageUniqueID, yuvColorSpace, proxies, numTextures,
yuvaIndices, imageOrigin, std::move(imageColorSpace));
}
-
diff --git a/src/image/SkImage_GpuYUVA.h b/src/image/SkImage_GpuYUVA.h
index c580284..1a6ab93 100644
--- a/src/image/SkImage_GpuYUVA.h
+++ b/src/image/SkImage_GpuYUVA.h
@@ -53,7 +53,6 @@
// Returns a ref-ed texture proxy with miplevels
sk_sp<GrTextureProxy> asMippedTextureProxyRef() const;
-
/**
Create a new SkImage_GpuYUVA that's very similar to SkImage created by MakeFromYUVATextures.
The main difference is that the client doesn't have the backend textures on the gpu yet but
@@ -90,8 +89,9 @@
@param imageColorSpace 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 promiseDoneProc function called when we will no longer call textureFulfillProc
- @param textureContext state passed to textureFulfillProc and textureReleaseProc
+ @param textureDoneProc function called when we will no longer call textureFulfillProc
+ @param textureContexts per-texture state passed to textureFulfillProc,
+ textureReleaseProc, and textureDoneProc
@return created SkImage, or nullptr
*/
static sk_sp<SkImage> MakePromiseYUVATexture(GrContext* context,
@@ -103,10 +103,10 @@
int height,
GrSurfaceOrigin imageOrigin,
sk_sp<SkColorSpace> imageColorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContexts[]);
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContexts[]);
private:
// This array will usually only be sparsely populated.