Reland "Reintroduce deinstantiate lazy proxy types and use for promise images."
This is a reland of 8b40ac35b2ec31a61c751e610b891681df272283
Original change's description:
> Reintroduce deinstantiate lazy proxy types and use for promise images.
>
> This reverts a fraction of b2c5dae65df952999f0a12b9df80bc1433ffa19a to
> restore the deinstantiate lazy proxy type, supporting implementation,
> and tests.
>
> Use them for promise images to avoid thread safety issues for promise
> image resources. Makes promise image instantiation callbacks do a thread
> safe unref of their fulfilled GrTexture in GrResourceCache. The
> GrResourceCache mechanism for receiving unref messages is extended to
> allow multiple pending unrefs. All this is new.
>
>
> Bug: skia:8800
> Change-Id: I7b1d4fea13c053b6fbbd39c0c6eaf567b8bf81f1
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/199002
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>
Bug: skia:8800
Change-Id: Ib939fc5c19edf0c6b965c9f6adf0afedd4267703
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/201220
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 8c98928..942c400 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -88,6 +88,8 @@
"$_src/gpu/GrDefaultGeoProcFactory.h",
"$_src/gpu/GrDeferredProxyUploader.h",
"$_src/gpu/GrDeferredUpload.h",
+ "$_src/gpu/GrDeinstantiateProxyTracker.cpp",
+ "$_src/gpu/GrDeinstantiateProxyTracker.h",
"$_src/gpu/GrDistanceFieldGenFromVector.cpp",
"$_src/gpu/GrDistanceFieldGenFromVector.h",
"$_src/gpu/GrDrawingManager.cpp",
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index 4a7935f..e20ebb0 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -79,7 +79,6 @@
}
private:
-
void computeScratchKey(GrScratchKey*) const override;
size_t onGpuMemorySize() const override;
void markMipMapsDirty();
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 7c572bb..c9eec6c 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -208,6 +208,8 @@
enum class LazyInstantiationType {
kSingleUse, // Instantiation callback is allowed to be called only once.
kMultipleUse, // Instantiation callback can be called multiple times.
+ kDeinstantiate, // Instantiation callback can be called multiple times,
+ // but we will deinstantiate the proxy after every flush.
};
enum class LazyState {
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index e184e8e..1bba019 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -42,7 +42,7 @@
// Attach our texture to this context's resource cache. This ensures that deletion will happen
// in the correct thread/context. This adds the only ref to the texture that will persist from
// this point. That ref will be released when the generator's RefHelper is freed.
- context->priv().getResourceCache()->insertCrossContextGpuResource(texture.get());
+ context->priv().getResourceCache()->insertDelayedResourceUnref(texture.get());
GrBackendTexture backendTexture = texture->getBackendTexture();
GrBackendFormat backendFormat = backendTexture.getBackendFormat();
diff --git a/src/gpu/GrDeinstantiateProxyTracker.cpp b/src/gpu/GrDeinstantiateProxyTracker.cpp
new file mode 100644
index 0000000..9870617
--- /dev/null
+++ b/src/gpu/GrDeinstantiateProxyTracker.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDeinstantiateProxyTracker.h"
+
+#include "GrSurfaceProxy.h"
+#include "GrSurfaceProxyPriv.h"
+
+void GrDeinstantiateProxyTracker::addProxy(GrSurfaceProxy* proxy) {
+#ifdef SK_DEBUG
+ using LazyType = GrSurfaceProxy::LazyInstantiationType;
+ SkASSERT(LazyType::kDeinstantiate == proxy->priv().lazyInstantiationType());
+ for (int i = 0; i < fProxies.count(); ++i) {
+ SkASSERT(proxy != fProxies[i].get());
+ }
+#endif
+ fProxies.push_back(sk_ref_sp(proxy));
+}
+
+void GrDeinstantiateProxyTracker::deinstantiateAllProxies() {
+ for (int i = 0; i < fProxies.count(); ++i) {
+ GrSurfaceProxy* proxy = fProxies[i].get();
+ SkASSERT(proxy->priv().isSafeToDeinstantiate());
+ proxy->deinstantiate();
+ }
+
+ fProxies.reset();
+}
diff --git a/src/gpu/GrDeinstantiateProxyTracker.h b/src/gpu/GrDeinstantiateProxyTracker.h
new file mode 100644
index 0000000..2555ab1
--- /dev/null
+++ b/src/gpu/GrDeinstantiateProxyTracker.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDeinstantiateProxyTracker_DEFINED
+#define GrDeinstantiateProxyTracker_DEFINED
+
+#include "GrSurfaceProxy.h"
+#include "SkTArray.h"
+
+class GrDeinstantiateProxyTracker {
+public:
+ GrDeinstantiateProxyTracker() {}
+
+ // Adds a proxy which will be deinstantiated at the end of flush. The same proxy may not be
+ // added multiple times.
+ void addProxy(GrSurfaceProxy* proxy);
+
+ // Loops through all tracked proxies and deinstantiates them.
+ void deinstantiateAllProxies();
+
+private:
+ SkTArray<sk_sp<GrSurfaceProxy>> fProxies;
+};
+
+#endif
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index b6d45ff..10da8f9 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -285,7 +285,7 @@
bool flushed = false;
{
- GrResourceAllocator alloc(resourceProvider);
+ GrResourceAllocator alloc(resourceProvider, flushState.deinstantiateProxyTracker());
for (int i = 0; i < fDAG.numOpLists(); ++i) {
if (fDAG.opList(i)) {
fDAG.opList(i)->gatherProxyIntervals(&alloc);
@@ -336,13 +336,20 @@
GrSemaphoresSubmitted result = gpu->finishFlush(proxy, access, flags, numSemaphores,
backendSemaphores);
+ flushState.deinstantiateProxyTracker()->deinstantiateAllProxies();
+
// Give the cache a chance to purge resources that become purgeable due to flushing.
if (flushed) {
resourceCache->purgeAsNeeded();
+ flushed = false;
}
for (GrOnFlushCallbackObject* onFlushCBObject : fOnFlushCBObjects) {
onFlushCBObject->postFlush(fTokenTracker.nextTokenToFlush(), fFlushingOpListIDs.begin(),
fFlushingOpListIDs.count());
+ flushed = true;
+ }
+ if (flushed) {
+ resourceCache->purgeAsNeeded();
}
fFlushingOpListIDs.reset();
fFlushing = false;
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index bc10920..07983d4 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -12,6 +12,7 @@
#include "GrAppliedClip.h"
#include "GrBufferAllocPool.h"
#include "GrDeferredUpload.h"
+#include "GrDeinstantiateProxyTracker.h"
#include "GrRenderTargetProxy.h"
#include "SkArenaAlloc.h"
#include "SkArenaAllocList.h"
@@ -108,6 +109,8 @@
// permissible).
GrAtlasManager* atlasManager() const final;
+ GrDeinstantiateProxyTracker* deinstantiateProxyTracker() { return &fDeinstantiateProxyTracker; }
+
private:
/** GrMeshDrawOp::Target override. */
SkArenaAlloc* allocator() override { return &fArena; }
@@ -161,6 +164,9 @@
// Variables that are used to track where we are in lists as ops are executed
SkArenaAllocList<Draw>::Iter fCurrDraw;
SkArenaAllocList<InlineUpload>::Iter fCurrUpload;
+
+ // Used to track the proxies that need to be deinstantiated after we finish a flush
+ GrDeinstantiateProxyTracker fDeinstantiateProxyTracker;
};
#endif
diff --git a/src/gpu/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp
index 81c2709..0d3c34e 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -7,6 +7,7 @@
#include "GrResourceAllocator.h"
+#include "GrDeinstantiateProxyTracker.h"
#include "GrGpuResourcePriv.h"
#include "GrOpList.h"
#include "GrRenderTargetProxy.h"
@@ -106,7 +107,12 @@
if (proxy->readOnly() || !fResourceProvider->explicitlyAllocateGPUResources()) {
// FIXME: remove this once we can do the lazy instantiation from assign instead.
if (GrSurfaceProxy::LazyState::kNot != proxy->lazyInstantiationState()) {
- proxy->priv().doLazyInstantiation(fResourceProvider);
+ if (proxy->priv().doLazyInstantiation(fResourceProvider)) {
+ if (proxy->priv().lazyInstantiationType() ==
+ GrSurfaceProxy::LazyInstantiationType::kDeinstantiate) {
+ fDeinstantiateTracker->addProxy(proxy);
+ }
+ }
}
}
}
@@ -369,6 +375,11 @@
if (GrSurfaceProxy::LazyState::kNot != cur->proxy()->lazyInstantiationState()) {
if (!cur->proxy()->priv().doLazyInstantiation(fResourceProvider)) {
*outError = AssignError::kFailedProxyInstantiation;
+ } else {
+ if (GrSurfaceProxy::LazyInstantiationType::kDeinstantiate ==
+ cur->proxy()->priv().lazyInstantiationType()) {
+ fDeinstantiateTracker->addProxy(cur->proxy());
+ }
}
} else if (sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy(), needsStencil)) {
// TODO: make getUniqueKey virtual on GrSurfaceProxy
diff --git a/src/gpu/GrResourceAllocator.h b/src/gpu/GrResourceAllocator.h
index cb366e6..ea1250f 100644
--- a/src/gpu/GrResourceAllocator.h
+++ b/src/gpu/GrResourceAllocator.h
@@ -16,6 +16,7 @@
#include "SkTDynamicHash.h"
#include "SkTMultiMap.h"
+class GrDeinstantiateProxyTracker;
class GrResourceProvider;
// Print out explicit allocation information
@@ -41,8 +42,8 @@
*/
class GrResourceAllocator {
public:
- GrResourceAllocator(GrResourceProvider* resourceProvider)
- : fResourceProvider(resourceProvider) {}
+ GrResourceAllocator(GrResourceProvider* resourceProvider, GrDeinstantiateProxyTracker* tracker)
+ : fResourceProvider(resourceProvider), fDeinstantiateTracker(tracker) {}
~GrResourceAllocator();
@@ -211,6 +212,7 @@
static const int kInitialArenaSize = 128 * sizeof(Interval);
GrResourceProvider* fResourceProvider;
+ GrDeinstantiateProxyTracker* fDeinstantiateTracker;
FreePoolMultiMap fFreePool; // Recently created/used GrSurfaces
IntvlHash fIntvlHash; // All the intervals, hashed by proxyID
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 9b17d92..c36634c 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -14,6 +14,7 @@
#include "GrTexture.h"
#include "GrTextureProxyCacheAccess.h"
#include "GrTracing.h"
+#include "SkExchange.h"
#include "SkGr.h"
#include "SkMessageBus.h"
#include "SkOpts.h"
@@ -67,7 +68,44 @@
GrResourceCache* fCache;
};
- //////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref() = default;
+
+inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref(GrGpuResource* resource)
+ : fResource(resource), fNumUnrefs(1) {}
+
+inline GrResourceCache::ResourceAwaitingUnref::ResourceAwaitingUnref(ResourceAwaitingUnref&& that) {
+ fResource = skstd::exchange(that.fResource, nullptr);
+ fNumUnrefs = skstd::exchange(that.fNumUnrefs, 0);
+}
+
+inline GrResourceCache::ResourceAwaitingUnref& GrResourceCache::ResourceAwaitingUnref::operator=(
+ ResourceAwaitingUnref&& that) {
+ fResource = skstd::exchange(that.fResource, nullptr);
+ fNumUnrefs = skstd::exchange(that.fNumUnrefs, 0);
+ return *this;
+}
+
+inline GrResourceCache::ResourceAwaitingUnref::~ResourceAwaitingUnref() {
+ if (fResource) {
+ for (int i = 0; i < fNumUnrefs; ++i) {
+ fResource->unref();
+ }
+ }
+}
+
+inline void GrResourceCache::ResourceAwaitingUnref::addRef() { ++fNumUnrefs; }
+
+inline void GrResourceCache::ResourceAwaitingUnref::unref() {
+ SkASSERT(fNumUnrefs > 0);
+ fResource->unref();
+ --fNumUnrefs;
+}
+
+inline bool GrResourceCache::ResourceAwaitingUnref::finished() { return !fNumUnrefs; }
+
+//////////////////////////////////////////////////////////////////////////////
GrResourceCache::GrResourceCache(const GrCaps* caps, GrSingleOwner* singleOwner,
uint32_t contextUniqueID)
@@ -179,10 +217,9 @@
void GrResourceCache::abandonAll() {
AutoValidate av(this);
- for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
- fResourcesWaitingForFreeMsg[i]->cacheAccess().abandon();
- }
- fResourcesWaitingForFreeMsg.reset();
+ // We need to make sure to free any resources that were waiting on a free message but never
+ // received one.
+ fResourcesAwaitingUnref.reset();
while (fNonpurgeableResources.count()) {
GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
@@ -204,7 +241,7 @@
SkASSERT(!fBudgetedCount);
SkASSERT(!fBudgetedBytes);
SkASSERT(!fPurgeableBytes);
- SkASSERT(!fResourcesWaitingForFreeMsg.count());
+ SkASSERT(!fResourcesAwaitingUnref.count());
}
void GrResourceCache::releaseAll() {
@@ -214,10 +251,7 @@
// We need to make sure to free any resources that were waiting on a free message but never
// received one.
- for (int i = 0; i < fResourcesWaitingForFreeMsg.count(); ++i) {
- fResourcesWaitingForFreeMsg[i]->unref();
- }
- fResourcesWaitingForFreeMsg.reset();
+ fResourcesAwaitingUnref.reset();
SkASSERT(fProxyProvider); // better have called setProxyProvider
// We must remove the uniqueKeys from the proxies here. While they possess a uniqueKey
@@ -244,7 +278,7 @@
SkASSERT(!fBudgetedCount);
SkASSERT(!fBudgetedBytes);
SkASSERT(!fPurgeableBytes);
- SkASSERT(!fResourcesWaitingForFreeMsg.count());
+ SkASSERT(!fResourcesAwaitingUnref.count());
}
class GrResourceCache::AvailableForScratchUse {
@@ -605,10 +639,14 @@
}
}
-void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
+void GrResourceCache::insertDelayedResourceUnref(GrGpuResource* resource) {
resource->ref();
- SkASSERT(!fResourcesWaitingForFreeMsg.contains(resource));
- fResourcesWaitingForFreeMsg.push_back(resource);
+ uint32_t id = resource->uniqueID().asUInt();
+ if (auto* data = fResourcesAwaitingUnref.find(id)) {
+ data->addRef();
+ } else {
+ fResourcesAwaitingUnref.set(id, {resource});
+ }
}
void GrResourceCache::processFreedGpuResources() {
@@ -616,14 +654,17 @@
fFreedGpuResourceInbox.poll(&msgs);
for (int i = 0; i < msgs.count(); ++i) {
SkASSERT(msgs[i].fOwningUniqueID == fContextUniqueID);
- int index = fResourcesWaitingForFreeMsg.find(msgs[i].fResource);
+ uint32_t id = msgs[i].fResource->uniqueID().asUInt();
+ ResourceAwaitingUnref* info = fResourcesAwaitingUnref.find(id);
// If we called release or abandon on the GrContext we will have already released our ref on
// the GrGpuResource. If then the message arrives before the actual GrContext gets destroyed
// we will try to process the message when we destroy the GrContext. This protects us from
// trying to unref the resource twice.
- if (index != -1) {
- fResourcesWaitingForFreeMsg.removeShuffle(index);
- msgs[i].fResource->unref();
+ if (info) {
+ info->unref();
+ if (info->finished()) {
+ fResourcesAwaitingUnref.remove(id);
+ }
}
}
}
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index c948864..84378e6 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -16,6 +16,7 @@
#include "SkRefCnt.h"
#include "SkTArray.h"
#include "SkTDPQueue.h"
+#include "SkTHash.h"
#include "SkTInternalLList.h"
#include "SkTMultiMap.h"
@@ -195,7 +196,7 @@
bool requestsFlush() const { return this->overBudget() && !fPurgeableQueue.count(); }
/** Maintain a ref to this resource until we receive a GrGpuResourceFreedMessage. */
- void insertCrossContextGpuResource(GrGpuResource* resource);
+ void insertDelayedResourceUnref(GrGpuResource* resource);
#if GR_CACHE_STATS
struct Stats {
@@ -305,6 +306,25 @@
};
typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
+ class ResourceAwaitingUnref {
+ public:
+ ResourceAwaitingUnref();
+ ResourceAwaitingUnref(GrGpuResource* resource);
+ ResourceAwaitingUnref(const ResourceAwaitingUnref&) = delete;
+ ResourceAwaitingUnref& operator=(const ResourceAwaitingUnref&) = delete;
+ ResourceAwaitingUnref(ResourceAwaitingUnref&&);
+ ResourceAwaitingUnref& operator=(ResourceAwaitingUnref&&);
+ ~ResourceAwaitingUnref();
+ void addRef();
+ void unref();
+ bool finished();
+
+ private:
+ GrGpuResource* fResource = nullptr;
+ int fNumUnrefs = 0;
+ };
+ using ReourcesAwaitingUnref = SkTHashMap<uint32_t, ResourceAwaitingUnref>;
+
static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
}
@@ -353,8 +373,7 @@
InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
FreedGpuResourceInbox fFreedGpuResourceInbox;
-
- SkTDArray<GrGpuResource*> fResourcesWaitingForFreeMsg;
+ ReourcesAwaitingUnref fResourcesAwaitingUnref;
uint32_t fContextUniqueID;
GrSingleOwner* fSingleOwner;
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 8fe7f83..3b383a5 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -60,6 +60,11 @@
return fProxy->fLazyInstantiationType;
}
+ bool isSafeToDeinstantiate() const {
+ return SkToBool(fProxy->fTarget) && SkToBool(fProxy->fLazyInstantiateCallback) &&
+ GrSurfaceProxy::LazyInstantiationType::kDeinstantiate == lazyInstantiationType();
+ }
+
static bool SK_WARN_UNUSED_RESULT AttachStencilIfNeeded(GrResourceProvider*, GrSurface*,
bool needsStencil);
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index ec2e651..908be47 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -445,12 +445,23 @@
~PromiseLazyInstantiateCallback() {
if (fIdleCallback) {
+ SkASSERT(!fTexture);
// We were never fulfilled. Pass false so done proc is still called.
fIdleCallback->abandon();
}
+ // Our destructor can run on any thread. We trigger the unref of fTexture by message.
+ if (fTexture) {
+ SkASSERT(!fIdleCallback);
+ SkMessageBus<GrGpuResourceFreedMessage>::Post({fTexture, fTextureContextID});
+ }
}
sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
+ // Our proxy is getting instantiated for the second+ time. We are only allowed to call
+ // Fulfill once. So return our cached result.
+ if (fTexture) {
+ return sk_ref_sp(fTexture);
+ }
SkASSERT(fIdleCallback);
PromiseImageTextureContext textureContext = fIdleCallback->context();
sk_sp<SkPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
@@ -495,10 +506,20 @@
}
tex->addIdleProc(std::move(fIdleCallback));
promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key);
+ fTexture = tex.get();
+ // We need to hold on to the GrTexture in case our proxy gets reinstantiated. However,
+ // we can't unref in our destructor because we may be on another thread then. So we
+ // let the cache know it is waiting on an unref message. We will send that message from
+ // our destructor.
+ GrContext* context = fTexture->getContext();
+ context->priv().getResourceCache()->insertDelayedResourceUnref(fTexture);
+ fTextureContextID = context->priv().contextID();
return std::move(tex);
}
private:
+ GrTexture* fTexture = nullptr;
+ uint32_t fTextureContextID = SK_InvalidUniqueID;
sk_sp<GrRefCntedCallback> fIdleCallback;
PromiseImageTextureFulfillProc fFulfillProc;
GrPixelConfig fConfig;
@@ -515,5 +536,5 @@
return proxyProvider->createLazyProxy(std::move(callback), backendFormat, desc, origin,
mipMapped, GrInternalSurfaceFlags::kReadOnly,
SkBackingFit::kExact, SkBudgeted::kNo,
- GrSurfaceProxy::LazyInstantiationType::kSingleUse);
+ GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
}
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index ce12da6..375d34c 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -488,6 +488,28 @@
// Now that the draw is fully consumed by the GPU, the texture should be idle.
REPORTER_ASSERT(reporter, idleIDs.find(2) != idleIDs.end());
+ // Make a proxy that should deinstantiate even if we keep a ref on it.
+ auto deinstantiateLazyCB = [&make, &context](GrResourceProvider* rp) {
+ return make(context, 3);
+ };
+ proxy = context->priv().proxyProvider()->createLazyProxy(
+ deinstantiateLazyCB, backendFormat, desc,
+ GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
+ GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
+ GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
+ rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
+ SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
+ SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
+ SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
+ // At this point the proxy shouldn't even be instantiated, there is no texture with
+ // id 3.
+ REPORTER_ASSERT(reporter, idleIDs.find(3) == idleIDs.end());
+ context->flush();
+ context->priv().getGpu()->testingOnly_flushGpuAndSync();
+ // Now that the draw is fully consumed, we should have deinstantiated the proxy and
+ // the texture it made should be idle.
+ REPORTER_ASSERT(reporter, idleIDs.find(3) != idleIDs.end());
+
// Make sure we make the call during various shutdown scenarios where the texture
// might persist after context is destroyed, abandoned, etc. We test three
// variations of each scenario. One where the texture is just created. Another,
@@ -501,7 +523,7 @@
if (api == GrBackendApi::kVulkan) {
continue;
}
- int id = 3;
+ int id = 4;
enum class DrawType {
kNoDraw,
kDraw,
diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp
index f3b90e9..91f7335 100644
--- a/tests/LazyProxyTest.cpp
+++ b/tests/LazyProxyTest.cpp
@@ -249,8 +249,9 @@
using LazyInstantiationType = GrSurfaceProxy::LazyInstantiationType;
for (bool doInstantiate : {true, false}) {
- for (auto lazyType :
- {LazyInstantiationType::kSingleUse, LazyInstantiationType::kMultipleUse}) {
+ for (auto lazyType : {LazyInstantiationType::kSingleUse,
+ LazyInstantiationType::kMultipleUse,
+ LazyInstantiationType::kDeinstantiate}) {
int testCount = 0;
// Sets an integer to 1 when the callback is called and -1 when it is deleted.
class TestCallback {
@@ -454,7 +455,7 @@
ctx->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
using LazyType = GrSurfaceProxy::LazyInstantiationType;
- for (auto lazyType : {LazyType::kSingleUse, LazyType::kMultipleUse}) {
+ for (auto lazyType : {LazyType::kSingleUse, LazyType::kMultipleUse, LazyType::kDeinstantiate}) {
sk_sp<GrRenderTargetContext> rtc = ctx->priv().makeDeferredRenderTargetContext(
format, SkBackingFit::kExact, 100, 100,
kRGBA_8888_GrPixelConfig, nullptr);
@@ -497,7 +498,11 @@
ctx->flush();
REPORTER_ASSERT(reporter, 1 == instantiateTestValue);
- REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+ if (LazyType::kDeinstantiate == lazyType) {
+ REPORTER_ASSERT(reporter, 1 == releaseTestValue);
+ } else {
+ REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+ }
// This should cause the uninstantiate proxies to be instantiated again but have no effect
// on the others
@@ -506,11 +511,20 @@
rtc->priv().testingOnly_addDrawOp(LazyDeinstantiateTestOp::Make(ctx.get(), lazyProxy));
ctx->flush();
- REPORTER_ASSERT(reporter, 1 == instantiateTestValue);
- REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+ if (LazyType::kDeinstantiate == lazyType) {
+ REPORTER_ASSERT(reporter, 2 == instantiateTestValue);
+ REPORTER_ASSERT(reporter, 2 == releaseTestValue);
+ } else {
+ REPORTER_ASSERT(reporter, 1 == instantiateTestValue);
+ REPORTER_ASSERT(reporter, 0 == releaseTestValue);
+ }
lazyProxy.reset();
- REPORTER_ASSERT(reporter, 1 == releaseTestValue);
+ if (LazyType::kDeinstantiate == lazyType) {
+ REPORTER_ASSERT(reporter, 2 == releaseTestValue);
+ } else {
+ REPORTER_ASSERT(reporter, 1 == releaseTestValue);
+ }
gpu->deleteTestingOnlyBackendTexture(backendTex);
}
diff --git a/tests/PromiseImageTest.cpp b/tests/PromiseImageTest.cpp
index 93a09ca..789cb24 100644
--- a/tests/PromiseImageTest.cpp
+++ b/tests/PromiseImageTest.cpp
@@ -331,6 +331,13 @@
auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key);
REPORTER_ASSERT(reporter, !surf);
}
+
+ // Must do this to ensure all callbacks occur before the PromiseImageChecker goes out of scope.
+ alphaImg.reset();
+ grayImg.reset();
+ ctx->flush();
+ ctx->priv().getGpu()->testingOnly_flushGpuAndSync();
+
gpu->deleteTestingOnlyBackendTexture(backendTex1);
}
@@ -461,7 +468,10 @@
surface->flush();
canvas->drawImage(image, 5, 0);
surface->flush();
- // Must call this to ensure that all callbacks are performed before the checker is destroyed.
+ // Must call these to ensure that all callbacks are performed before the checker is destroyed.
+ image.reset();
+ ctx->flush();
gpu->testingOnly_flushGpuAndSync();
+
gpu->deleteTestingOnlyBackendTexture(backendTex);
}
diff --git a/tests/ResourceAllocatorTest.cpp b/tests/ResourceAllocatorTest.cpp
index 69118fc..a71221d 100644
--- a/tests/ResourceAllocatorTest.cpp
+++ b/tests/ResourceAllocatorTest.cpp
@@ -10,6 +10,7 @@
#include "Test.h"
#include "GrContextPriv.h"
+#include "GrDeinstantiateProxyTracker.h"
#include "GrGpu.h"
#include "GrProxyProvider.h"
#include "GrResourceAllocator.h"
@@ -89,7 +90,8 @@
// assigned different GrSurfaces.
static void overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
GrSurfaceProxy* p1, GrSurfaceProxy* p2, bool expectedResult) {
- GrResourceAllocator alloc(resourceProvider);
+ GrDeinstantiateProxyTracker deinstantiateTracker;
+ GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
alloc.addInterval(p1, 0, 4);
alloc.addInterval(p2, 1, 2);
@@ -111,7 +113,8 @@
static void non_overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
GrSurfaceProxy* p1, GrSurfaceProxy* p2,
bool expectedResult) {
- GrResourceAllocator alloc(resourceProvider);
+ GrDeinstantiateProxyTracker deinstantiateTracker;
+ GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
alloc.addInterval(p1, 0, 2);
alloc.addInterval(p2, 3, 5);
@@ -285,7 +288,7 @@
}
sk_sp<GrSurfaceProxy> make_lazy(GrProxyProvider* proxyProvider, const GrCaps* caps,
- const ProxyParams& p) {
+ const ProxyParams& p, bool deinstantiate) {
GrColorType grCT = SkColorTypeToGrColorType(p.fColorType);
GrPixelConfig config = GrColorTypeToPixelConfig(grCT, GrSRGBEncoded::kNo);
@@ -308,7 +311,8 @@
}
};
const GrBackendFormat format = caps->getBackendFormatFromColorType(p.fColorType);
- auto lazyType = GrSurfaceProxy::LazyInstantiationType ::kSingleUse;
+ auto lazyType = deinstantiate ? GrSurfaceProxy::LazyInstantiationType ::kDeinstantiate
+ : GrSurfaceProxy::LazyInstantiationType ::kSingleUse;
GrInternalSurfaceFlags flags = GrInternalSurfaceFlags::kNone;
return proxyProvider->createLazyProxy(callback, format, desc, p.fOrigin, GrMipMapped::kNo,
flags, p.fFit, SkBudgeted::kNo, lazyType);
@@ -330,20 +334,28 @@
rtParams.fIsRT = true;
auto proxyProvider = context->priv().proxyProvider();
auto caps = context->priv().caps();
- auto p0 = make_lazy(proxyProvider, caps, texParams);
+ auto p0 = make_lazy(proxyProvider, caps, texParams, true);
+ auto p1 = make_lazy(proxyProvider, caps, texParams, false);
texParams.fFit = rtParams.fFit = SkBackingFit::kApprox;
- auto p1 = make_lazy(proxyProvider, caps, rtParams);
+ auto p2 = make_lazy(proxyProvider, caps, rtParams, true);
+ auto p3 = make_lazy(proxyProvider, caps, rtParams, false);
+ GrDeinstantiateProxyTracker deinstantiateTracker;
{
- GrResourceAllocator alloc(resourceProvider);
+ GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
alloc.addInterval(p0.get(), 0, 1);
alloc.addInterval(p1.get(), 0, 1);
+ alloc.addInterval(p2.get(), 0, 1);
+ alloc.addInterval(p3.get(), 0, 1);
alloc.markEndOfOpList(0);
int startIndex, stopIndex;
GrResourceAllocator::AssignError error;
alloc.assign(&startIndex, &stopIndex, &error);
}
- REPORTER_ASSERT(reporter, p0->isInstantiated());
+ deinstantiateTracker.deinstantiateAllProxies();
+ REPORTER_ASSERT(reporter, !p0->isInstantiated());
REPORTER_ASSERT(reporter, p1->isInstantiated());
+ REPORTER_ASSERT(reporter, !p2->isInstantiated());
+ REPORTER_ASSERT(reporter, p3->isInstantiated());
}
}
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 9bd5415..a1af2b3 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -1545,18 +1545,18 @@
GrGpu* gpu = context->priv().getGpu();
TestResource* wrapped1 = TestResource::CreateWrapped(gpu, GrWrapCacheable::kYes);
- cache->insertCrossContextGpuResource(wrapped1);
+ cache->insertDelayedResourceUnref(wrapped1);
REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
TestResource* wrapped2 = TestResource::CreateWrapped(gpu, GrWrapCacheable::kYes);
- cache->insertCrossContextGpuResource(wrapped2);
+ cache->insertDelayedResourceUnref(wrapped2);
// An uncacheable cross-context should not be purged as soon as we drop our ref. This
// is because inserting it as a cross-context resource actually holds a ref until the
// message is received.
TestResource* wrapped3 = TestResource::CreateWrapped(gpu, GrWrapCacheable::kNo);
- cache->insertCrossContextGpuResource(wrapped3);
+ cache->insertDelayedResourceUnref(wrapped3);
REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());