diff options
author | Brian Salomon <bsalomon@google.com> | 2019-01-10 12:09:52 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2019-01-10 17:51:21 +0000 |
commit | cdd8a0a0e84f6d4b4316e1dade43fb3b8e051993 (patch) | |
tree | 4f31873da034de367202bc5921706478c7a0a984 | |
parent | df0e1af263ef6456b49d01dba3cbd33764d9af6e (diff) | |
download | skqp-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.cpp | 10 | ||||
-rw-r--r-- | gn/core.gni | 2 | ||||
-rw-r--r-- | include/core/SkDeferredDisplayListRecorder.h | 69 | ||||
-rw-r--r-- | include/core/SkPromiseImageTexture.h | 50 | ||||
-rw-r--r-- | include/private/GrResourceKey.h | 19 | ||||
-rw-r--r-- | include/private/GrTypesPriv.h | 12 | ||||
-rw-r--r-- | infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json | 1 | ||||
-rw-r--r-- | infra/bots/recipes/test.py | 1 | ||||
-rw-r--r-- | src/core/SkDeferredDisplayListRecorder.cpp | 244 | ||||
-rw-r--r-- | src/core/SkPromiseImageTexture.cpp | 61 | ||||
-rw-r--r-- | src/gpu/GrResourceProvider.h | 3 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.cpp | 8 | ||||
-rw-r--r-- | src/image/SkImage_Gpu.h | 13 | ||||
-rw-r--r-- | src/image/SkImage_GpuBase.cpp | 232 | ||||
-rw-r--r-- | src/image/SkImage_GpuBase.h | 41 | ||||
-rw-r--r-- | src/image/SkImage_GpuYUVA.cpp | 28 | ||||
-rw-r--r-- | src/image/SkImage_GpuYUVA.h | 14 | ||||
-rw-r--r-- | tests/PromiseImageTest.cpp | 298 | ||||
-rw-r--r-- | tools/DDLPromiseImageHelper.cpp | 55 | ||||
-rw-r--r-- | tools/DDLPromiseImageHelper.h | 68 |
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; |