aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Salomon <bsalomon@google.com>2019-01-10 12:09:52 -0500
committerSkia Commit-Bot <skia-commit-bot@chromium.org>2019-01-10 17:51:21 +0000
commitcdd8a0a0e84f6d4b4316e1dade43fb3b8e051993 (patch)
tree4f31873da034de367202bc5921706478c7a0a984
parentdf0e1af263ef6456b49d01dba3cbd33764d9af6e (diff)
downloadskqp-cdd8a0a0e84f6d4b4316e1dade43fb3b8e051993.tar.gz
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>
-rw-r--r--dm/DMSrcSink.cpp10
-rw-r--r--gn/core.gni2
-rw-r--r--include/core/SkDeferredDisplayListRecorder.h69
-rw-r--r--include/core/SkPromiseImageTexture.h50
-rw-r--r--include/private/GrResourceKey.h19
-rw-r--r--include/private/GrTypesPriv.h12
-rw-r--r--infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json1
-rw-r--r--infra/bots/recipes/test.py1
-rw-r--r--src/core/SkDeferredDisplayListRecorder.cpp244
-rw-r--r--src/core/SkPromiseImageTexture.cpp61
-rw-r--r--src/gpu/GrResourceProvider.h3
-rw-r--r--src/image/SkImage_Gpu.cpp8
-rw-r--r--src/image/SkImage_Gpu.h13
-rw-r--r--src/image/SkImage_GpuBase.cpp232
-rw-r--r--src/image/SkImage_GpuBase.h41
-rw-r--r--src/image/SkImage_GpuYUVA.cpp28
-rw-r--r--src/image/SkImage_GpuYUVA.h14
-rw-r--r--tests/PromiseImageTest.cpp298
-rw-r--r--tools/DDLPromiseImageHelper.cpp55
-rw-r--r--tools/DDLPromiseImageHelper.h68
20 files changed, 990 insertions, 239 deletions
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 46c16756a9..37fed6efee 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1975,7 +1975,10 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
// This is here bc this is the first point where we have access to the context
promiseImageHelper.uploadAllToGPU(context);
- // We draw N times, with a clear between.
+ // We draw N times, with a clear between. Between each run we invalidate and delete half of
+ // the textures backing promise images. So half the images exercise reusing a cached
+ // GrTexture and the other half exercise the case whem the client provides a different
+ // backing texture in fulfill.
for (int replay = 0; replay < fNumReplays; ++replay) {
if (replay > 0) {
// Clear the drawing of the previous replay
@@ -1994,6 +1997,11 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
// This drops the promiseImageHelper's refs on all the promise images if we're in
// the last run.
promiseImageHelper.reset();
+ } else {
+ // This ought to ensure that all promise image textures from the last pass are
+ // released.
+ context->contextPriv().getGpu()->testingOnly_flushGpuAndSync();
+ promiseImageHelper.replaceEveryOtherPromiseTexture(context);
}
// Fourth, synchronously render the display lists into the dest tiles
diff --git a/gn/core.gni b/gn/core.gni
index c0249ecdff..8c674e5fab 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -207,6 +207,7 @@ skia_core_sources = [
"$_src/core/SkPixmap.cpp",
"$_src/core/SkPoint.cpp",
"$_src/core/SkPoint3.cpp",
+ "$_src/core/SkPromiseImageTexture.cpp",
"$_src/core/SkPtrRecorder.cpp",
"$_src/core/SkQuadClipper.cpp",
"$_src/core/SkQuadClipper.h",
@@ -388,6 +389,7 @@ skia_core_sources = [
"$_include/core/SkPoint.h",
"$_include/core/SkPoint3.h",
"$_include/core/SkPreConfig.h",
+ "$_include/core/SkPromiseImageTexture.h",
"$_include/core/SkRect.h",
"$_include/core/SkRefCnt.h",
"$_include/core/SkRegion.h",
diff --git a/include/core/SkDeferredDisplayListRecorder.h b/include/core/SkDeferredDisplayListRecorder.h
index 300687a528..7eeff69baf 100644
--- a/include/core/SkDeferredDisplayListRecorder.h
+++ b/include/core/SkDeferredDisplayListRecorder.h
@@ -5,22 +5,21 @@
* found in the LICENSE file.
*/
-#ifndef SkDeferredDisplayListMaker_DEFINED
-#define SkDeferredDisplayListMaker_DEFINED
+#ifndef SkDeferredDisplayListRecorder_DEFINED
+#define SkDeferredDisplayListRecorder_DEFINED
+#include "../private/SkDeferredDisplayList.h"
#include "SkImageInfo.h"
#include "SkRefCnt.h"
#include "SkSurfaceCharacterization.h"
#include "SkTypes.h"
-#include "../private/SkDeferredDisplayList.h"
-
class GrBackendFormat;
class GrBackendTexture;
class GrContext;
-
class SkCanvas;
class SkImage;
+class SkPromiseImageTexture;
class SkSurface;
struct SkYUVAIndex;
struct SkYUVASizeInfo;
@@ -53,11 +52,17 @@ public:
std::unique_ptr<SkDeferredDisplayList> detach();
- // Matches the defines in SkImage_GpuBase.h
- typedef void* TextureContext;
- typedef void (*TextureReleaseProc)(TextureContext textureContext);
- typedef void (*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
- typedef void (*PromiseDoneProc)(TextureContext textureContext);
+ using PromiseImageTextureContext = void*;
+ using PromiseImageTextureFulfillProc = SkPromiseImageTexture* (*)(PromiseImageTextureContext);
+ using PromiseImageTextureReleaseProc = void (*)(PromiseImageTextureContext,
+ const SkPromiseImageTexture*);
+ using PromiseImageTextureDoneProc = void (*)(PromiseImageTextureContext);
+
+ // Deprecated types. To be removed.
+ using LegacyPromiseImageTextureFulfillProc = void (*)(PromiseImageTextureContext,
+ GrBackendTexture*);
+ using LegacyPromiseImageTextureReleaseProc = void (*)(PromiseImageTextureContext);
+ using TextureContext = PromiseImageTextureContext;
/**
Create a new SkImage that is very similar to an SkImage created by MakeFromTexture. The main
@@ -112,10 +117,23 @@ public:
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContext);
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContext);
+ /** Deprecated variant of above. */
+ sk_sp<SkImage> 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);
/**
This entry point operates the same as 'makePromiseTexture' except that its
@@ -132,12 +150,25 @@ public:
int imageHeight,
GrSurfaceOrigin imageOrigin,
sk_sp<SkColorSpace> imageColorSpace,
- TextureFulfillProc textureFulfillProc,
- TextureReleaseProc textureReleaseProc,
- PromiseDoneProc promiseDoneProc,
- TextureContext textureContexts[]);
+ PromiseImageTextureFulfillProc textureFulfillProc,
+ PromiseImageTextureReleaseProc textureReleaseProc,
+ PromiseImageTextureDoneProc textureDoneProc,
+ PromiseImageTextureContext textureContexts[]);
+ /** Deprecated variant of above. */
+ sk_sp<SkImage> 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[]);
- private:
+private:
bool init();
const SkSurfaceCharacterization fCharacterization;
diff --git a/include/core/SkPromiseImageTexture.h b/include/core/SkPromiseImageTexture.h
new file mode 100644
index 0000000000..abc0467221
--- /dev/null
+++ b/include/core/SkPromiseImageTexture.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPromiseImageTexture_DEFINED
+#define SkPromiseImageTexture_DEFINED
+
+#include "../private/GrResourceKey.h"
+#include "GrBackendSurface.h"
+
+#if SK_SUPPORT_GPU
+/**
+ * This type is used to fulfill textures for PromiseImages. Once an instance is returned from a
+ * PromiseImageTextureFulfillProc it must remain valid until the corresponding
+ * PromiseImageTextureReleaseProc is called. For performance reasons it is recommended that the
+ * the client reuse a single PromiseImageTexture every time a given texture is returned by
+ * the PromiseImageTextureFulfillProc rather than recreating PromiseImageTextures representing
+ * the same underlying backend API texture.
+ */
+class SK_API SkPromiseImageTexture {
+public:
+ SkPromiseImageTexture() = default;
+ SkPromiseImageTexture(const SkPromiseImageTexture&) = delete;
+ explicit SkPromiseImageTexture(const GrBackendTexture& backendTexture);
+ SkPromiseImageTexture(SkPromiseImageTexture&&);
+ ~SkPromiseImageTexture();
+ SkPromiseImageTexture& operator=(const SkPromiseImageTexture&) = delete;
+ SkPromiseImageTexture& operator=(SkPromiseImageTexture&&);
+ const GrBackendTexture& backendTexture() const { return fBackendTexture; }
+ bool isValid() const { return SkToBool(fUniqueID); }
+
+ void addKeyToInvalidate(uint32_t contextID, const GrUniqueKey& key);
+ uint32_t uniqueID() const { return fUniqueID; }
+
+#if GR_TEST_UTILS
+ SkTArray<GrUniqueKey> testingOnly_uniqueKeysToInvalidate() const;
+#endif
+
+private:
+ SkSTArray<1, GrUniqueKeyInvalidatedMessage> fMessages;
+ GrBackendTexture fBackendTexture;
+ uint32_t fUniqueID = SK_InvalidUniqueID;
+ static std::atomic<uint32_t> gUniqueID;
+};
+#endif
+
+#endif
diff --git a/include/private/GrResourceKey.h b/include/private/GrResourceKey.h
index 8179b19dc1..0a4b5a4abc 100644
--- a/include/private/GrResourceKey.h
+++ b/include/private/GrResourceKey.h
@@ -56,13 +56,16 @@ protected:
}
GrResourceKey& operator=(const GrResourceKey& that) {
- SkASSERT(that.isValid());
if (this != &that) {
- size_t bytes = that.size();
- SkASSERT(SkIsAlign4(bytes));
- fKey.reset(SkToInt(bytes / sizeof(uint32_t)));
- memcpy(fKey.get(), that.fKey.get(), bytes);
- this->validate();
+ if (!that.isValid()) {
+ this->reset();
+ } else {
+ size_t bytes = that.size();
+ SkASSERT(SkIsAlign4(bytes));
+ fKey.reset(SkToInt(bytes / sizeof(uint32_t)));
+ memcpy(fKey.get(), that.fKey.get(), bytes);
+ this->validate();
+ }
}
return *this;
}
@@ -146,6 +149,7 @@ private:
size_t internalSize() const { return fKey[kDomainAndSize_MetaDataIdx] >> 16; }
void validate() const {
+ SkASSERT(this->isValid());
SkASSERT(fKey[kHash_MetaDataIdx] ==
GrResourceKeyHash(&fKey[kHash_MetaDataIdx] + 1,
this->internalSize() - sizeof(uint32_t)));
@@ -327,6 +331,7 @@ static inline void gr_init_static_unique_key_once(SkAlignedSTStorage<1, GrUnique
// The cache listens for these messages to purge junk resources proactively.
class GrUniqueKeyInvalidatedMessage {
public:
+ GrUniqueKeyInvalidatedMessage() = default;
GrUniqueKeyInvalidatedMessage(const GrUniqueKey& key, uint32_t contextUniqueID)
: fKey(key), fContextID(contextUniqueID) {
SkASSERT(SK_InvalidUniqueID != contextUniqueID);
@@ -341,7 +346,7 @@ public:
private:
GrUniqueKey fKey;
- uint32_t fContextID;
+ uint32_t fContextID = SK_InvalidUniqueID;
};
static inline bool SkShouldPostMessageToBus(const GrUniqueKeyInvalidatedMessage& msg,
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 29ab81470f..effa560c9f 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -1389,17 +1389,23 @@ static inline GrPixelConfig GrColorTypeToPixelConfig(GrColorType config,
return kUnknown_GrPixelConfig;
}
-class GrReleaseProcHelper : public SkWeakRefCnt {
+class GrReleaseProcHelper : public SkRefCnt {
public:
// These match the definitions in SkImage, from whence they came
typedef void* ReleaseCtx;
typedef void (*ReleaseProc)(ReleaseCtx);
GrReleaseProcHelper(ReleaseProc proc, ReleaseCtx ctx) : fReleaseProc(proc), fReleaseCtx(ctx) {}
- ~GrReleaseProcHelper() override {}
+ ~GrReleaseProcHelper() override {
+ if (fReleaseProc) {
+ fReleaseProc(fReleaseCtx);
+ }
+ }
- void weak_dispose() const override {
+ void callAndClear() {
fReleaseProc(fReleaseCtx);
+ fReleaseProc = nullptr;
+ fReleaseCtx = nullptr;
}
private:
diff --git a/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json b/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json
index a02c9e0f52..9726e685e9 100644
--- a/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json
+++ b/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json
@@ -498,6 +498,7 @@
"~^GrPipelineDynamicStateTest$",
"~^InitialTextureClear$",
"~^PromiseImageTest$",
+ "~^PromiseImageTextureReuse$",
"~^ResourceAllocatorTest$",
"~^RGB565TextureTest$",
"~^RGBA4444TextureTest$",
diff --git a/infra/bots/recipes/test.py b/infra/bots/recipes/test.py
index 655372d681..28b9e4a7b8 100644
--- a/infra/bots/recipes/test.py
+++ b/infra/bots/recipes/test.py
@@ -765,6 +765,7 @@ def dm_flags(api, bot):
match.append('~^GrPipelineDynamicStateTest$')
match.append('~^InitialTextureClear$')
match.append('~^PromiseImageTest$')
+ match.append('~^PromiseImageTextureReuse$')
match.append('~^ResourceAllocatorTest$')
match.append('~^RGB565TextureTest$')
match.append('~^RGBA4444TextureTest$')
diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp
index da02727b00..716d241ada 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 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
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 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
#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 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
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 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makePromiseTexture(
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 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
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 @@ sk_sp<SkImage> SkDeferredDisplayListRecorder::makeYUVAPromiseTexture(
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 0000000000..76eabc708e
--- /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 fe1bdd32f7..84a97a1808 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -66,8 +66,7 @@ public:
* 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 8e00eb4894..5de8aa95b6 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -322,10 +322,10 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
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 de0407d5cf..1f825391d3 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -76,8 +76,9 @@ public:
@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 @@ public:
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 1c013dd44e..73aecf4414 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 @@ bool SkImage_GpuBase::RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* re
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 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
}
/**
- * 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 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();
+ return nullptr;
}
- 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>();
+ 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>();
+ }
- SkAssertResult(fReleaseHelper->try_ref());
+ 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>();
}
- 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();
- }
-
- // Weak unrefs fReleaseHelper and sets it to null
- void resetReleaseHelper() {
- if (fReleaseHelper) {
- fReleaseHelper->weak_unref();
- fReleaseHelper = nullptr;
- }
+ 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();
}
- 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 7b183b79b6..5be97c8d91 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 @@ public:
: 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 @@ protected:
// 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 @@ private:
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 aa1d0b38bf..340f1ef695 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -206,19 +206,20 @@ sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(
/////////////////////////////////////////////////////////////////////////////////////////////////
-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 @@ sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(GrContext* context,
kNeedNewImageUniqueID, yuvColorSpace, proxies, numTextures,
yuvaIndices, imageOrigin, std::move(imageColorSpace));
}
-
diff --git a/src/image/SkImage_GpuYUVA.h b/src/image/SkImage_GpuYUVA.h
index c5802847ae..1a6ab93e1a 100644
--- a/src/image/SkImage_GpuYUVA.h
+++ b/src/image/SkImage_GpuYUVA.h
@@ -53,7 +53,6 @@ public:
// 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 @@ public:
@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 @@ public:
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.
diff --git a/tests/PromiseImageTest.cpp b/tests/PromiseImageTest.cpp
index 216d656036..54bb3c8ce1 100644
--- a/tests/PromiseImageTest.cpp
+++ b/tests/PromiseImageTest.cpp
@@ -11,6 +11,7 @@
#include "GrContextPriv.h"
#include "GrGpu.h"
#include "SkImage_Gpu.h"
+#include "SkPromiseImageTexture.h"
using namespace sk_gpu_test;
@@ -20,15 +21,39 @@ struct PromiseTextureChecker {
, fFulfillCount(0)
, fReleaseCount(0)
, fDoneCount(0) {}
- GrBackendTexture fTexture;
+ SkPromiseImageTexture fTexture;
int fFulfillCount;
int fReleaseCount;
int fDoneCount;
- static void Fulfill(void* self, GrBackendTexture* outTexture) {
- static_cast<PromiseTextureChecker*>(self)->fFulfillCount++;
- *outTexture = static_cast<PromiseTextureChecker*>(self)->fTexture;
+ GrBackendTexture fLastFulfilledTexture;
+
+ /**
+ * Replaces the backend texture that this checker will return from fulfill. Also, transfers
+ * ownership of the previous PromiseImageTexture to the caller, if they want to control when
+ * it is deleted. The default argument will remove the existing texture without installing a
+ * valid replacement.
+ */
+ SkPromiseImageTexture replaceTexture(const GrBackendTexture& tex = GrBackendTexture()) {
+ // Can't change this while in active fulfillment.
+ SkASSERT(fFulfillCount == fReleaseCount);
+ auto temp = std::move(fTexture);
+ fTexture = SkPromiseImageTexture(tex);
+ return temp;
+ }
+
+ SkTArray<GrUniqueKey> uniqueKeys() const {
+ return fTexture.testingOnly_uniqueKeysToInvalidate();
}
- static void Release(void* self) {
+
+ static SkPromiseImageTexture* Fulfill(void* self) {
+ auto checker = static_cast<PromiseTextureChecker*>(self);
+ SkASSERT(checker->fTexture.isValid());
+ checker->fFulfillCount++;
+ checker->fLastFulfilledTexture = checker->fTexture.backendTexture();
+ return &checker->fTexture;
+ }
+ static void Release(void* self, const SkPromiseImageTexture* texture) {
+ SkASSERT(&static_cast<PromiseTextureChecker*>(self)->fTexture == texture);
static_cast<PromiseTextureChecker*>(self)->fReleaseCount++;
}
static void Done(void* self) {
@@ -236,3 +261,266 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
gpu->deleteTestingOnlyBackendTexture(backendTex);
}
}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo) {
+ const int kWidth = 10;
+ const int kHeight = 10;
+
+ GrContext* ctx = ctxInfo.grContext();
+ GrGpu* gpu = ctx->contextPriv().getGpu();
+
+ GrBackendTexture backendTex1 = gpu->createTestingOnlyBackendTexture(
+ nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
+ GrBackendTexture backendTex2 = gpu->createTestingOnlyBackendTexture(
+ nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
+ GrBackendTexture backendTex3 = gpu->createTestingOnlyBackendTexture(
+ nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
+ REPORTER_ASSERT(reporter, backendTex1.isValid());
+ REPORTER_ASSERT(reporter, backendTex2.isValid());
+ REPORTER_ASSERT(reporter, backendTex3.isValid());
+
+ GrBackendFormat backendFormat = backendTex1.getBackendFormat();
+ REPORTER_ASSERT(reporter, backendFormat.isValid());
+ REPORTER_ASSERT(reporter, backendFormat == backendTex2.getBackendFormat());
+ REPORTER_ASSERT(reporter, backendFormat == backendTex3.getBackendFormat());
+
+ PromiseTextureChecker promiseChecker(backendTex1);
+ 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,
+ PromiseTextureChecker::Done,
+ &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;
+ int expectedDoneCnt = 0;
+
+ canvas->drawImage(refImg, 0, 0);
+ canvas->drawImage(refImg, 5, 5);
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+
+ bool isVulkan = GrBackendApi::kVulkan == ctx->contextPriv().getBackend();
+ canvas->flush();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ !isVulkan,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ !isVulkan,
+ expectedDoneCnt,
+ reporter));
+ REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
+ promiseChecker.fLastFulfilledTexture, backendTex1));
+ // We should have put a GrTexture for this fulfillment into the cache.
+ auto keys = promiseChecker.uniqueKeys();
+ REPORTER_ASSERT(reporter, keys.count() == 1);
+ GrUniqueKey texKey1;
+ if (keys.count()) {
+ texKey1 = keys[0];
+ }
+ REPORTER_ASSERT(reporter, texKey1.isValid());
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
+
+ gpu->testingOnly_flushGpuAndSync();
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+ REPORTER_ASSERT(reporter,
+ GrBackendTexture::TestingOnly_Equals(
+ promiseChecker.replaceTexture().backendTexture(), backendTex1));
+ gpu->deleteTestingOnlyBackendTexture(backendTex1);
+
+ ctx->contextPriv().getResourceCache()->purgeAsNeeded();
+ // We should have invalidated the key on the previously cached texture (after ensuring
+ // invalidation messages have been processed by calling purgeAsNeeded.)
+ REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
+
+ promiseChecker.replaceTexture(backendTex2);
+
+ canvas->drawImage(refImg, 0, 0);
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+
+ canvas->flush();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+ // Second texture should be in the cache.
+ keys = promiseChecker.uniqueKeys();
+ REPORTER_ASSERT(reporter, keys.count() == 1);
+ GrUniqueKey texKey2;
+ if (keys.count()) {
+ texKey2 = keys[0];
+ }
+ REPORTER_ASSERT(reporter, texKey2.isValid() && texKey2 != texKey1);
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
+
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ !isVulkan,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ !isVulkan,
+ expectedDoneCnt,
+ reporter));
+ REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
+ promiseChecker.fLastFulfilledTexture, backendTex2));
+
+ gpu->testingOnly_flushGpuAndSync();
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+
+ // Because we have kept the SkPromiseImageTexture alive, we should be able to use it again and
+ // hit the cache.
+ ctx->contextPriv().getResourceCache()->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
+
+ canvas->drawImage(refImg, 0, 0);
+
+ canvas->flush();
+ gpu->testingOnly_flushGpuAndSync();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+
+ // Make sure we didn't add another key and that the second texture is still alive in the cache.
+ keys = promiseChecker.uniqueKeys();
+ REPORTER_ASSERT(reporter, keys.count() == 1);
+ if (keys.count()) {
+ REPORTER_ASSERT(reporter, texKey2 == keys[0]);
+ }
+ ctx->contextPriv().getResourceCache()->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
+
+ // Now we test keeping tex2 alive but fulfilling with a new texture.
+ SkPromiseImageTexture promiseImageTexture2 = promiseChecker.replaceTexture(backendTex3);
+ REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
+ promiseImageTexture2.backendTexture(), backendTex2));
+
+ canvas->drawImage(refImg, 0, 0);
+
+ canvas->flush();
+ gpu->testingOnly_flushGpuAndSync();
+ expectedFulfillCnt++;
+ expectedReleaseCnt++;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+
+ keys = promiseChecker.uniqueKeys();
+ REPORTER_ASSERT(reporter, keys.count() == 1);
+ GrUniqueKey texKey3;
+ if (keys.count()) {
+ texKey3 = keys[0];
+ }
+ ctx->contextPriv().getResourceCache()->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
+ gpu->deleteTestingOnlyBackendTexture(promiseImageTexture2.backendTexture());
+
+ // Make a new promise image also backed by texture 3.
+ sk_sp<SkImage> refImg2(
+ SkImage_Gpu::MakePromiseTexture(ctx, backendFormat, kWidth, kHeight,
+ GrMipMapped::kNo, texOrigin,
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+ nullptr,
+ PromiseTextureChecker::Fulfill,
+ PromiseTextureChecker::Release,
+ PromiseTextureChecker::Done,
+ &promiseChecker));
+ canvas->drawImage(refImg, 0, 0);
+ canvas->drawImage(refImg2, 1, 1);
+
+ canvas->flush();
+ gpu->testingOnly_flushGpuAndSync();
+ expectedFulfillCnt += 2;
+ expectedReleaseCnt += 2;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+
+ // We currently expect each promise image to make and cache its own GrTexture. We will likely
+ // try to make these share in the future.
+ keys = promiseChecker.uniqueKeys();
+ REPORTER_ASSERT(reporter, keys.count() == 2);
+ GrUniqueKey texKey4;
+ if (keys.count() == 2) {
+ REPORTER_ASSERT(reporter, texKey3 == keys[0]);
+ texKey4 = keys[1];
+ }
+ ctx->contextPriv().getResourceCache()->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
+ REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey4));
+
+ // If we delete the SkPromiseImageTexture we should trigger both key removals.
+ REPORTER_ASSERT(reporter,
+ GrBackendTexture::TestingOnly_Equals(
+ promiseChecker.replaceTexture().backendTexture(), backendTex3));
+
+ ctx->contextPriv().getResourceCache()->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
+ REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey4));
+ gpu->deleteTestingOnlyBackendTexture(backendTex3);
+
+ // After deleting each image we should get a done call.
+ refImg.reset();
+ ++expectedDoneCnt;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+ refImg2.reset();
+ ++expectedDoneCnt;
+ REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
+ true,
+ expectedFulfillCnt,
+ expectedReleaseCnt,
+ true,
+ expectedDoneCnt,
+ reporter));
+}
diff --git a/tools/DDLPromiseImageHelper.cpp b/tools/DDLPromiseImageHelper.cpp
index 2586835868..fa722651bc 100644
--- a/tools/DDLPromiseImageHelper.cpp
+++ b/tools/DDLPromiseImageHelper.cpp
@@ -17,13 +17,30 @@
#include "SkYUVASizeInfo.h"
DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
+ SkASSERT(fDoneCnt == fNumImages);
+ SkASSERT(!fUnreleasedFulfills);
+ SkASSERT(fTotalReleases == fTotalFulfills);
+ SkASSERT(!fTotalFulfills || fDoneCnt);
GrGpu* gpu = fContext->contextPriv().getGpu();
- if (fBackendTexture.isValid()) {
- gpu->deleteTestingOnlyBackendTexture(fBackendTexture);
+ if (fPromiseImageTexture.isValid()) {
+ gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture.backendTexture());
}
}
+void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
+ const GrBackendTexture& backendTexture) {
+ SkASSERT(!fUnreleasedFulfills);
+ if (fPromiseImageTexture.isValid()) {
+ GrGpu* gpu = fContext->contextPriv().getGpu();
+
+ if (fPromiseImageTexture.isValid()) {
+ gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture.backendTexture());
+ }
+ }
+ fPromiseImageTexture = SkPromiseImageTexture{backendTexture};
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
@@ -111,7 +128,7 @@ void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
info.yuvaIndices(), j));
- SkAssertResult(callbackContext->backendTexture().isValid());
+ SkASSERT(callbackContext->promiseImageTexture()->isValid());
fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
}
@@ -133,7 +150,34 @@ void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
}
+ }
+}
+void DDLPromiseImageHelper::replaceEveryOtherPromiseTexture(GrContext* context) {
+ GrGpu* gpu = context->contextPriv().getGpu();
+ SkASSERT(gpu);
+
+ for (int i = 0; i < fImageInfo.count(); i += 2) {
+ PromiseImageInfo& info = fImageInfo[i];
+
+ // DDL TODO: how can we tell if we need mipmapping!
+ if (info.isYUV()) {
+ int numPixmaps;
+ SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
+ for (int j = 0; j < numPixmaps; ++j) {
+ const SkPixmap& yuvPixmap = info.yuvPixmap(j);
+ info.callbackContext(j)->setBackendTexture(
+ create_yuva_texture(gpu, yuvPixmap, info.yuvaIndices(), j));
+ SkASSERT(info.callbackContext(j)->promiseImageTexture()->isValid());
+ }
+ } else {
+ const SkBitmap& bm = info.normalBitmap();
+ info.callbackContext(0)->setBackendTexture(gpu->createTestingOnlyBackendTexture(
+ bm.getPixels(), bm.width(), bm.height(), bm.colorType(), false,
+ GrMipMapped::kNo, bm.rowBytes()));
+ // The GMs sometimes request too large an image
+ // SkAssertResult(callbackContext->backendTexture().isValid());
+ }
}
}
@@ -206,7 +250,9 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
contexts);
-
+ for (int i = 0; i < textureCount; ++i) {
+ curImage.callbackContext(i)->wasAddedToImage();
+ }
} else {
const GrBackendTexture& backendTex = curImage.backendTexture(0);
GrBackendFormat backendFormat = backendTex.getBackendFormat();
@@ -227,6 +273,7 @@ sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
(void*) curImage.refCallbackContext(0).release());
+ curImage.callbackContext(0)->wasAddedToImage();
}
perRecorderContext->fPromiseImages->push_back(image);
SkASSERT(image);
diff --git a/tools/DDLPromiseImageHelper.h b/tools/DDLPromiseImageHelper.h
index 7a636ce15b..f8627aab36 100644
--- a/tools/DDLPromiseImageHelper.h
+++ b/tools/DDLPromiseImageHelper.h
@@ -8,16 +8,17 @@
#ifndef PromiseImageHelper_DEFINED
#define PromiseImageHelper_DEFINED
-#include "SkBitmap.h"
-#include "SkTArray.h"
-
#include "GrBackendSurface.h"
+#include "SkBitmap.h"
#include "SkCachedData.h"
+#include "SkDeferredDisplayListRecorder.h"
+#include "SkPromiseImageTexture.h"
+#include "SkTArray.h"
+#include "SkTLazy.h"
#include "SkYUVAIndex.h"
#include "SkYUVASizeInfo.h"
class GrContext;
-class SkDeferredDisplayListRecorder;
class SkImage;
class SkPicture;
struct SkYUVAIndex;
@@ -54,6 +55,10 @@ public:
void uploadAllToGPU(GrContext* context);
+ // Change the backing store texture for half the images. (Must ensure all fulfilled images are
+ // released before calling this.).
+ void replaceEveryOtherPromiseTexture(GrContext*);
+
// reinflate a deflated SKP, replacing all the indices with promise images.
sk_sp<SkPicture> reinflateSKP(SkDeferredDisplayListRecorder*,
SkData* compressedPicture,
@@ -76,16 +81,37 @@ private:
~PromiseImageCallbackContext();
- void setBackendTexture(const GrBackendTexture& backendTexture) {
- SkASSERT(!fBackendTexture.isValid());
- fBackendTexture = backendTexture;
+ void setBackendTexture(const GrBackendTexture& backendTexture);
+
+ void fulfill() {
+ SkASSERT(fUnreleasedFulfills >= 0);
+ ++fUnreleasedFulfills;
+ ++fTotalFulfills;
}
- const GrBackendTexture& backendTexture() const { return fBackendTexture; }
+ void release() {
+ SkASSERT(fUnreleasedFulfills > 0);
+ --fUnreleasedFulfills;
+ ++fTotalReleases;
+ }
+
+ void done() {
+ ++fDoneCnt;
+ SkASSERT(fDoneCnt <= fNumImages);
+ }
+
+ void wasAddedToImage() { fNumImages++; }
+
+ SkPromiseImageTexture* promiseImageTexture() { return &fPromiseImageTexture; }
private:
- GrContext* fContext;
- GrBackendTexture fBackendTexture;
+ GrContext* fContext;
+ SkPromiseImageTexture fPromiseImageTexture;
+ int fNumImages = 0;
+ int fTotalFulfills = 0;
+ int fTotalReleases = 0;
+ int fUnreleasedFulfills = 0;
+ int fDoneCnt = 0;
typedef SkRefCnt INHERITED;
};
@@ -134,7 +160,7 @@ private:
SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
fCallbackContexts[index] = callbackContext;
}
- PromiseImageCallbackContext* callbackContext(int index) {
+ PromiseImageCallbackContext* callbackContext(int index) const {
SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
return fCallbackContexts[index].get();
}
@@ -145,7 +171,7 @@ private:
const GrBackendTexture& backendTexture(int index) const {
SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
- return fCallbackContexts[index]->backendTexture();
+ return fCallbackContexts[index]->promiseImageTexture()->backendTexture();
}
void setNormalBitmap(const SkBitmap& bm) { fBitmap = bm; }
@@ -190,21 +216,24 @@ private:
SkTArray<sk_sp<SkImage>>* fPromiseImages;
};
- static void PromiseImageFulfillProc(void* textureContext, GrBackendTexture* outTexture) {
+ static SkPromiseImageTexture* PromiseImageFulfillProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
- SkASSERT(callbackContext->backendTexture().isValid());
- *outTexture = callbackContext->backendTexture();
+ SkASSERT(callbackContext->promiseImageTexture()->isValid());
+ callbackContext->fulfill();
+ return callbackContext->promiseImageTexture();
}
- static void PromiseImageReleaseProc(void* textureContext) {
-#ifdef SK_DEBUG
+ static void PromiseImageReleaseProc(void* textureContext,
+ const SkPromiseImageTexture* texture) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
- SkASSERT(callbackContext->backendTexture().isValid());
-#endif
+ callbackContext->release();
+ SkASSERT(texture == callbackContext->promiseImageTexture());
+ SkASSERT(callbackContext->promiseImageTexture()->isValid());
}
static void PromiseImageDoneProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
+ callbackContext->done();
callbackContext->unref();
}
@@ -212,6 +241,7 @@ private:
bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); }
const PromiseImageInfo& getInfo(int id) const { return fImageInfo[id]; }
+ void uploadImage(GrContext*, PromiseImageInfo*);
// returns -1 if not found
int findImage(SkImage* image) const;