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>
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);