Make GrTexture caching for SkPromiseImageTexture work when same texture
fulfills a different SkImage.
Bug: skia:8613
Change-Id: I7ee14112c69f8aaef223a37dda455259b501a2bf
Reviewed-on: https://skia-review.googlesource.com/c/184440
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkPromiseImageTexture.cpp b/src/core/SkPromiseImageTexture.cpp
index f4801bb..5c55a9a 100644
--- a/src/core/SkPromiseImageTexture.cpp
+++ b/src/core/SkPromiseImageTexture.cpp
@@ -27,6 +27,11 @@
void SkPromiseImageTexture::addKeyToInvalidate(uint32_t contextID, const GrUniqueKey& key) {
SkASSERT(contextID != SK_InvalidUniqueID);
SkASSERT(key.isValid());
+ for (const auto& msg : fMessages) {
+ if (msg.contextID() == contextID && msg.key() == key) {
+ return;
+ }
+ }
fMessages.emplace_back(key, contextID);
}
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index db83eea..f9d4acd 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -86,6 +86,7 @@
fIdleProc = proc;
fIdleProcContext = context;
}
+ void* idleContext() const override { return fIdleProcContext; }
// These functions are used to track the texture parameters associated with the texture.
GrGpu::ResetTimestamp getCachedParamsTimestamp() const { return fParamsTimestamp; }
diff --git a/src/gpu/mock/GrMockTexture.h b/src/gpu/mock/GrMockTexture.h
index ac5eab0..3674de6 100644
--- a/src/gpu/mock/GrMockTexture.h
+++ b/src/gpu/mock/GrMockTexture.h
@@ -52,6 +52,7 @@
fIdleProc = proc;
fIdleProcContext = context;
}
+ void* idleContext() const override { return fIdleProcContext; }
protected:
// constructor for subclasses
diff --git a/src/gpu/mtl/GrMtlTexture.h b/src/gpu/mtl/GrMtlTexture.h
index 8037c68..2cdb92b 100644
--- a/src/gpu/mtl/GrMtlTexture.h
+++ b/src/gpu/mtl/GrMtlTexture.h
@@ -47,6 +47,7 @@
fIdleProc = proc;
fIdleProcContext = context;
}
+ void* idleContext() const override { return fIdleProcContext; }
protected:
GrMtlTexture(GrMtlGpu*, const GrSurfaceDesc&, id<MTLTexture>, GrMipMapsStatus);
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index d913aed..0592fc8 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -46,6 +46,7 @@
}
void setIdleProc(IdleProc, void* context) override;
+ void* idleContext() const override { return fIdleProcContext; }
protected:
GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp<GrVkImageLayout>,
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 8d90be5..0e74923 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -17,6 +17,7 @@
#include "SkImage_Gpu.h"
#include "SkPromiseImageTexture.h"
#include "SkReadPixelsRec.h"
+#include "SkTLList.h"
#include "effects/GrYUVtoRGBEffect.h"
SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
@@ -405,18 +406,11 @@
: fFulfillProc(fulfillProc)
, fConfig(config) {
auto doneHelper = sk_make_sp<GrReleaseProcHelper>(doneProc, context);
- fIdleContext = sk_make_sp<IdleContext>(releaseProc, context, std::move(doneHelper));
- static std::atomic<uint32_t> gUniqueID;
- fUniqueID = gUniqueID.fetch_add(1) + 1;
+ fReleaseContext = sk_make_sp<IdleContext::PromiseImageReleaseContext>(
+ releaseProc, context, std::move(doneHelper));
}
- ~PromiseLazyInstantiateCallback() {
- // Remove the key from the texture so that the texture will be removed from the cache.
- if (fLastFulfilledKey.isValid()) {
- SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
- GrUniqueKeyInvalidatedMessage(fLastFulfilledKey, fContextID));
- }
- }
+ ~PromiseLazyInstantiateCallback() = default;
sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
if (!resourceProvider) {
@@ -434,24 +428,22 @@
}
// 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.
- if (cachedTexture && !fIdleContext->wasReleased()) {
+ if (cachedTexture && !fReleaseContext->isReleased()) {
return std::move(cachedTexture);
}
GrBackendTexture backendTexture;
sk_sp<SkPromiseImageTexture> promiseTexture =
- fFulfillProc(fIdleContext->textureContext());
- fIdleContext->reset();
+ fFulfillProc(fReleaseContext->textureContext());
+ fReleaseContext->notifyWasFulfilled();
if (!promiseTexture) {
- IdleContext::IdleProc(fIdleContext.get());
+ fReleaseContext->release();
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();
- cachedTexture->setIdleProc(IdleContext::IdleProc, fIdleContext.get());
+ SkASSERT(fReleaseContext->unique());
+ this->addToIdleContext(cachedTexture.get());
return std::move(cachedTexture);
} else if (cachedTexture) {
cachedTexture->resourcePriv().removeUniqueKey();
@@ -463,7 +455,7 @@
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.
- IdleContext::IdleProc(fIdleContext.get());
+ fReleaseContext->release();
return sk_sp<GrTexture>();
}
@@ -472,16 +464,15 @@
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.
- IdleContext::IdleProc(fIdleContext.get());
+ fReleaseContext->release();
return sk_sp<GrTexture>();
}
// The texture gets a ref, which is balanced when the idle callback is called.
- fIdleContext->ref();
- tex->setIdleProc(IdleContext::IdleProc, fIdleContext.get());
+ this->addToIdleContext(tex.get());
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(&fLastFulfilledKey, kDomain, 2, "promise");
builder[0] = promiseTexture->uniqueID();
- builder[1] = fUniqueID;
+ builder[1] = fConfig;
builder.finish();
tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
SkASSERT(fContextID == SK_InvalidUniqueID ||
@@ -498,39 +489,74 @@
// ownership of the IdleContext. Thus, the IdleContext is destroyed and calls the Done proc
// after the last fulfilled texture goes idle and calls the Release proc or the proxy's
// destructor destroys the lazy callback, whichever comes last.
- class IdleContext : public SkNVRefCnt<IdleContext> {
+ class IdleContext {
public:
- IdleContext() = delete;
- IdleContext(PromiseImageTextureReleaseProc releaseProc,
- PromiseImageTextureContext textureContext,
- sk_sp<GrReleaseProcHelper> doneHelper)
- : fReleaseProc(releaseProc)
- , fTextureContext(textureContext)
- , fDoneHelper(std::move(doneHelper)) {}
+ class PromiseImageReleaseContext;
- ~IdleContext() { SkASSERT(fWasReleased); }
+ IdleContext() = default;
- void reset() { fWasReleased = false; }
- bool wasReleased() const { return fWasReleased; }
+ ~IdleContext() = default;
- PromiseImageTextureContext textureContext() const { return fTextureContext; }
-
- static void IdleProc(void* context) {
- IdleContext* rc = static_cast<IdleContext*>(context);
- rc->fReleaseProc(rc->fTextureContext);
- rc->fWasReleased = true;
- // Drop the texture's implicit ref on the IdleContext.
- rc->unref();
+ void addImageReleaseContext(sk_sp<PromiseImageReleaseContext> context) {
+ fReleaseContexts.addToHead(std::move(context));
}
+ static void IdleProc(void* context) {
+ IdleContext* idleContext = static_cast<IdleContext*>(context);
+ for (ReleaseContextList::Iter iter = idleContext->fReleaseContexts.headIter();
+ iter.get();
+ iter.next()) {
+ (*iter.get())->release();
+ }
+ idleContext->fReleaseContexts.reset();
+ delete idleContext;
+ }
+
+ class PromiseImageReleaseContext : public SkNVRefCnt<PromiseImageReleaseContext> {
+ public:
+ PromiseImageReleaseContext(PromiseImageTextureReleaseProc releaseProc,
+ PromiseImageTextureContext textureContext,
+ sk_sp<GrReleaseProcHelper> doneHelper)
+ : fReleaseProc(releaseProc)
+ , fTextureContext(textureContext)
+ , fDoneHelper(std::move(doneHelper)) {}
+
+ ~PromiseImageReleaseContext() { SkASSERT(fIsReleased); }
+
+ void release() {
+ SkASSERT(!fIsReleased);
+ fReleaseProc(fTextureContext);
+ fIsReleased = true;
+ }
+
+ void notifyWasFulfilled() { fIsReleased = false; }
+ bool isReleased() const { return fIsReleased; }
+
+ PromiseImageTextureContext textureContext() const { return fTextureContext; }
+
+ private:
+ PromiseImageTextureReleaseProc fReleaseProc;
+ PromiseImageTextureContext fTextureContext;
+ sk_sp<GrReleaseProcHelper> fDoneHelper;
+ bool fIsReleased = true;
+ };
+
private:
- PromiseImageTextureReleaseProc fReleaseProc;
- PromiseImageTextureContext fTextureContext;
- sk_sp<GrReleaseProcHelper> fDoneHelper;
- bool fWasReleased = true;
+ using ReleaseContextList = SkTLList<sk_sp<PromiseImageReleaseContext>, 4>;
+ ReleaseContextList fReleaseContexts;
};
- sk_sp<IdleContext> fIdleContext;
+ void addToIdleContext(GrTexture* texture) {
+ SkASSERT(!fReleaseContext->isReleased());
+ IdleContext* idleContext = static_cast<IdleContext*>(texture->idleContext());
+ if (!idleContext) {
+ idleContext = new IdleContext();
+ texture->setIdleProc(IdleContext::IdleProc, idleContext);
+ }
+ idleContext->addImageReleaseContext(fReleaseContext);
+ }
+
+ sk_sp<IdleContext::PromiseImageReleaseContext> fReleaseContext;
PromiseImageTextureFulfillProc fFulfillProc;
GrPixelConfig fConfig;
@@ -538,8 +564,6 @@
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);