Avoid unbounded listener growth on SkImage_Lazy when textures are purged
Generalizes the system used on SkPathRef where a GrTexture's key destructor signals that a listener on the image can be removed via the unique key custom data. Removes texturesAreCacheable() from SkImageGenerator. This was used to prevent unbounded growth in a narrow situation related to GrBackendTextureImageGenerator. Change-Id: I3c605da099acfac94751e793331e356a0979d359 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/274038 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
a1613c917e
commit
99a813ca29
@ -226,6 +226,7 @@ skia_core_sources = [
|
||||
"$_src/core/SkGraphics.cpp",
|
||||
"$_src/core/SkHalf.cpp",
|
||||
"$_src/core/SkICC.cpp",
|
||||
"$_src/core/SkIDChangeListener.h",
|
||||
"$_src/core/SkImageFilter.cpp",
|
||||
"$_src/core/SkImageFilter_Base.h",
|
||||
"$_src/core/SkImageFilterCache.cpp",
|
||||
|
@ -142,7 +142,6 @@ public:
|
||||
GrSurfaceProxyView generateTexture(GrRecordingContext*, const SkImageInfo& info,
|
||||
const SkIPoint& origin, GrMipMapped);
|
||||
|
||||
bool texturesAreCacheable() const { return this->onTexturesAreCacheable(); }
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -184,7 +183,6 @@ protected:
|
||||
|
||||
virtual GrSurfaceProxyView onGenerateTexture(GrRecordingContext*, const SkImageInfo&,
|
||||
const SkIPoint&, GrMipMapped); // returns nullptr
|
||||
virtual bool onTexturesAreCacheable() const { return true; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
@ -17,9 +17,11 @@
|
||||
#include "include/private/SkTDArray.h"
|
||||
#include "include/private/SkTemplates.h"
|
||||
#include "include/private/SkTo.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
|
||||
class SkIDChangeListener;
|
||||
class SkRBuffer;
|
||||
class SkWBuffer;
|
||||
|
||||
@ -307,27 +309,7 @@ public:
|
||||
*/
|
||||
uint32_t genID() const;
|
||||
|
||||
class GenIDChangeListener : public SkRefCnt {
|
||||
public:
|
||||
GenIDChangeListener() : fShouldUnregisterFromPath(false) {}
|
||||
virtual ~GenIDChangeListener() {}
|
||||
|
||||
virtual void onChange() = 0;
|
||||
|
||||
// The caller can use this method to notify the path that it no longer needs to listen. Once
|
||||
// called, the path will remove this listener from the list at some future point.
|
||||
void markShouldUnregisterFromPath() {
|
||||
fShouldUnregisterFromPath.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
bool shouldUnregisterFromPath() {
|
||||
return fShouldUnregisterFromPath.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> fShouldUnregisterFromPath;
|
||||
};
|
||||
|
||||
void addGenIDChangeListener(sk_sp<GenIDChangeListener>); // Threadsafe.
|
||||
void addGenIDChangeListener(sk_sp<SkIDChangeListener>); // Threadsafe.
|
||||
int genIDChangeListenerCount(); // Threadsafe
|
||||
|
||||
bool isValid() const;
|
||||
@ -490,7 +472,7 @@ private:
|
||||
SkDEBUGCODE(std::atomic<int> fEditorsAttached;) // assert only one editor in use at any time.
|
||||
|
||||
SkMutex fGenIDChangeListenersMutex;
|
||||
SkTDArray<GenIDChangeListener*> fGenIDChangeListeners; // pointers are reffed
|
||||
SkTDArray<SkIDChangeListener*> fGenIDChangeListeners; // pointers are reffed
|
||||
|
||||
mutable uint8_t fBoundsIsDirty;
|
||||
mutable bool fIsFinite; // only meaningful if bounds are valid
|
||||
|
43
src/core/SkIDChangeListener.h
Normal file
43
src/core/SkIDChangeListener.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkIDChangeListener_DEFINED
|
||||
#define SkIDChangeListener_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* Used to be notified when a gen/unique ID is invalidated, typically to preemptively purge
|
||||
* associated items from a cache that are no longer reachable. The listener can
|
||||
* be marked for deregistration if the cached item is remove before the listener is
|
||||
* triggered. This prevents unbounded listener growth when cache items are routinely
|
||||
* removed before the gen ID/unique ID is invalidated.
|
||||
*/
|
||||
class SkIDChangeListener : public SkRefCnt {
|
||||
public:
|
||||
SkIDChangeListener() = default;
|
||||
|
||||
virtual ~SkIDChangeListener() = default;
|
||||
|
||||
virtual void changed() = 0;
|
||||
|
||||
/**
|
||||
* Mark the listener is no longer needed. It should be removed and changed() should not be
|
||||
* called.
|
||||
*/
|
||||
void markShouldDeregister() { fShouldDeregister.store(true, std::memory_order_relaxed); }
|
||||
|
||||
/** Indicates whether markShouldDeregister was called. */
|
||||
bool shouldDeregister() { return fShouldDeregister.load(std::memory_order_acquire); }
|
||||
|
||||
private:
|
||||
std::atomic<bool> fShouldDeregister = false;
|
||||
};
|
||||
|
||||
#endif
|
@ -9,6 +9,7 @@
|
||||
#define SkPathPriv_DEFINED
|
||||
|
||||
#include "include/core/SkPath.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
|
||||
static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch");
|
||||
static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch");
|
||||
@ -93,8 +94,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void AddGenIDChangeListener(const SkPath& path,
|
||||
sk_sp<SkPathRef::GenIDChangeListener> listener) {
|
||||
static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) {
|
||||
path.fPathRef->addGenIDChangeListener(std::move(listener));
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "include/private/SkOnce.h"
|
||||
#include "include/private/SkTo.h"
|
||||
#include "src/core/SkBuffer.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/core/SkSafeMath.h"
|
||||
|
||||
@ -477,7 +478,7 @@ uint32_t SkPathRef::genID() const {
|
||||
return fGenerationID;
|
||||
}
|
||||
|
||||
void SkPathRef::addGenIDChangeListener(sk_sp<GenIDChangeListener> listener) {
|
||||
void SkPathRef::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) {
|
||||
if (nullptr == listener || this == gEmpty) {
|
||||
return;
|
||||
}
|
||||
@ -486,13 +487,13 @@ void SkPathRef::addGenIDChangeListener(sk_sp<GenIDChangeListener> listener) {
|
||||
|
||||
// Clean out any stale listeners before we append the new one.
|
||||
for (int i = 0; i < fGenIDChangeListeners.count(); ++i) {
|
||||
if (fGenIDChangeListeners[i]->shouldUnregisterFromPath()) {
|
||||
if (fGenIDChangeListeners[i]->shouldDeregister()) {
|
||||
fGenIDChangeListeners[i]->unref();
|
||||
fGenIDChangeListeners.removeShuffle(i--); // No need to preserve the order after i.
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(!listener->shouldUnregisterFromPath());
|
||||
SkASSERT(!listener->shouldDeregister());
|
||||
*fGenIDChangeListeners.append() = listener.release();
|
||||
}
|
||||
|
||||
@ -504,9 +505,9 @@ int SkPathRef::genIDChangeListenerCount() {
|
||||
// we need to be called *before* the genID gets changed or zerod
|
||||
void SkPathRef::callGenIDChangeListeners() {
|
||||
auto visit = [this]() {
|
||||
for (GenIDChangeListener* listener : fGenIDChangeListeners) {
|
||||
if (!listener->shouldUnregisterFromPath()) {
|
||||
listener->onChange();
|
||||
for (SkIDChangeListener* listener : fGenIDChangeListeners) {
|
||||
if (!listener->shouldDeregister()) {
|
||||
listener->changed();
|
||||
}
|
||||
// Listeners get at most one shot, so whether these triggered or not, blow them away.
|
||||
listener->unref();
|
||||
|
@ -41,7 +41,6 @@ protected:
|
||||
|
||||
GrSurfaceProxyView onGenerateTexture(GrRecordingContext*, const SkImageInfo&, const SkIPoint&,
|
||||
GrMipMapped mipMapped) override;
|
||||
bool onTexturesAreCacheable() const override { return false; }
|
||||
|
||||
private:
|
||||
GrBackendTextureImageGenerator(const SkImageInfo& info, GrTexture*, GrSurfaceOrigin,
|
||||
|
@ -5,6 +5,8 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/gpu/GrSoftwarePathRenderer.h"
|
||||
|
||||
#include "include/private/SkSemaphore.h"
|
||||
#include "src/core/SkTaskGroup.h"
|
||||
#include "src/core/SkTraceEvent.h"
|
||||
@ -19,8 +21,8 @@
|
||||
#include "src/gpu/GrRecordingContextPriv.h"
|
||||
#include "src/gpu/GrRenderTargetContextPriv.h"
|
||||
#include "src/gpu/GrSWMaskHelper.h"
|
||||
#include "src/gpu/GrSoftwarePathRenderer.h"
|
||||
#include "src/gpu/GrSurfaceContextPriv.h"
|
||||
#include "src/gpu/SkGr.h"
|
||||
#include "src/gpu/geometry/GrShape.h"
|
||||
#include "src/gpu/ops/GrDrawOp.h"
|
||||
|
||||
@ -215,20 +217,6 @@ private:
|
||||
GrAA fAA;
|
||||
};
|
||||
|
||||
// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
|
||||
class PathInvalidator : public SkPathRef::GenIDChangeListener {
|
||||
public:
|
||||
PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
|
||||
: fMsg(key, contextUniqueID) {}
|
||||
|
||||
private:
|
||||
GrUniqueKeyInvalidatedMessage fMsg;
|
||||
|
||||
void onChange() override {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -379,25 +367,11 @@ bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
|
||||
SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
|
||||
|
||||
// We will add an invalidator to the path so that if the path goes away we will
|
||||
// delete or recycle the mask texture. We also invalidate the other way: If the mask
|
||||
// goes away we signal that the invalidator on the path can be removed. This prevents
|
||||
// unbounded growth of invalidators on long lived paths.
|
||||
auto invalidator =
|
||||
sk_make_sp<PathInvalidator>(maskKey, args.fContext->priv().contextID());
|
||||
|
||||
auto invalidateInvalidator = [](const void* ptr, void* /*context*/) {
|
||||
auto invalidator = reinterpret_cast<const sk_sp<PathInvalidator>*>(ptr);
|
||||
(*invalidator)->markShouldUnregisterFromPath();
|
||||
delete invalidator;
|
||||
};
|
||||
auto data = SkData::MakeWithProc(new sk_sp<PathInvalidator>(invalidator),
|
||||
sizeof(sk_sp<PathInvalidator>),
|
||||
invalidateInvalidator,
|
||||
nullptr);
|
||||
maskKey.setCustomData(std::move(data));
|
||||
// delete or recycle the mask texture.
|
||||
auto listener = GrMakeUniqueKeyInvalidationListener(&maskKey,
|
||||
args.fContext->priv().contextID());
|
||||
fProxyProvider->assignUniqueKeyToProxy(maskKey, view.asTextureProxy());
|
||||
|
||||
args.fShape->addGenIDChangeListener(std::move(invalidator));
|
||||
args.fShape->addGenIDChangeListener(std::move(listener));
|
||||
}
|
||||
}
|
||||
SkASSERT(view);
|
||||
|
@ -9,9 +9,9 @@
|
||||
#define GrSoftwarePathRenderer_DEFINED
|
||||
|
||||
#include "src/gpu/GrPathRenderer.h"
|
||||
#include "src/gpu/GrSurfaceProxyView.h"
|
||||
|
||||
class GrProxyProvider;
|
||||
class GrTextureProxy;
|
||||
|
||||
/**
|
||||
* This class uses the software side to render a path to an SkBitmap and
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "src/core/SkAutoMalloc.h"
|
||||
#include "src/core/SkBlendModePriv.h"
|
||||
#include "src/core/SkColorSpacePriv.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/core/SkImagePriv.h"
|
||||
#include "src/core/SkMaskFilterBase.h"
|
||||
#include "src/core/SkMessageBus.h"
|
||||
@ -117,6 +118,35 @@ void GrInstallBitmapUniqueKeyInvalidator(const GrUniqueKey& key, uint32_t contex
|
||||
pixelRef->addGenIDChangeListener(new Invalidator(key, contextUniqueID));
|
||||
}
|
||||
|
||||
sk_sp<SkIDChangeListener> GrMakeUniqueKeyInvalidationListener(GrUniqueKey* key,
|
||||
uint32_t contextID) {
|
||||
class Listener : public SkIDChangeListener {
|
||||
public:
|
||||
Listener(const GrUniqueKey& key, uint32_t contextUniqueID) : fMsg(key, contextUniqueID) {}
|
||||
|
||||
void changed() override { SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg); }
|
||||
|
||||
private:
|
||||
GrUniqueKeyInvalidatedMessage fMsg;
|
||||
};
|
||||
|
||||
auto listener = sk_make_sp<Listener>(*key, contextID);
|
||||
|
||||
// We stick a SkData on the key that calls invalidateListener in its destructor.
|
||||
auto invalidateListener = [](const void* ptr, void* /*context*/) {
|
||||
auto listener = reinterpret_cast<const sk_sp<Listener>*>(ptr);
|
||||
(*listener)->markShouldDeregister();
|
||||
delete listener;
|
||||
};
|
||||
auto data = SkData::MakeWithProc(new sk_sp<Listener>(listener),
|
||||
sizeof(sk_sp<Listener>),
|
||||
invalidateListener,
|
||||
nullptr);
|
||||
SkASSERT(!key->getCustomData());
|
||||
key->setCustomData(std::move(data));
|
||||
return std::move(listener);
|
||||
}
|
||||
|
||||
GrSurfaceProxyView GrCopyBaseMipMapToTextureProxy(GrRecordingContext* ctx,
|
||||
GrSurfaceProxy* baseProxy,
|
||||
GrSurfaceOrigin origin,
|
||||
|
@ -202,4 +202,12 @@ void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& ima
|
||||
void GrInstallBitmapUniqueKeyInvalidator(const GrUniqueKey& key, uint32_t contextID,
|
||||
SkPixelRef* pixelRef);
|
||||
|
||||
/**
|
||||
* Makes a SkIDChangeListener from a GrUniqueKey. The key will be invalidated in the resource
|
||||
* cache if the ID becomes invalid. This also modifies the key so that it will cause the listener
|
||||
* to be deregistered if the key is destroyed (to prevent unbounded listener growth when resources
|
||||
* are purged before listeners trigger).
|
||||
*/
|
||||
sk_sp<SkIDChangeListener> GrMakeUniqueKeyInvalidationListener(GrUniqueKey*, uint32_t contextID);
|
||||
|
||||
#endif
|
||||
|
@ -87,7 +87,7 @@ uint32_t* GrCCPathCache::Key::data() {
|
||||
return reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(this) + sizeof(Key));
|
||||
}
|
||||
|
||||
void GrCCPathCache::Key::onChange() {
|
||||
void GrCCPathCache::Key::changed() {
|
||||
// Our key's corresponding path was invalidated. Post a thread-safe eviction message.
|
||||
SkMessageBus<sk_sp<Key>>::Post(sk_ref_sp(this));
|
||||
}
|
||||
@ -258,7 +258,7 @@ void GrCCPathCache::evict(const GrCCPathCache::Key& key, GrCCPathCacheEntry* ent
|
||||
}
|
||||
SkASSERT(*entry->fCacheKey == key);
|
||||
SkASSERT(!entry->hasBeenEvicted());
|
||||
entry->fCacheKey->markShouldUnregisterFromPath(); // Unregister the path listener.
|
||||
entry->fCacheKey->markShouldDeregister(); // Unregister the path listener.
|
||||
entry->releaseCachedAtlas(this);
|
||||
fLRU.remove(entry);
|
||||
fHashTable.remove(key);
|
||||
@ -325,7 +325,7 @@ void GrCCPathCache::evictInvalidatedCacheKeys() {
|
||||
SkTArray<sk_sp<Key>> invalidatedKeys;
|
||||
fInvalidatedKeysInbox.poll(&invalidatedKeys);
|
||||
for (const sk_sp<Key>& key : invalidatedKeys) {
|
||||
bool isInCache = !key->shouldUnregisterFromPath(); // Gets set upon exiting the cache.
|
||||
bool isInCache = !key->shouldDeregister(); // Gets set upon exiting the cache.
|
||||
if (isInCache) {
|
||||
this->evict(*key);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "include/private/SkTHash.h"
|
||||
#include "src/core/SkExchange.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/core/SkTInternalLList.h"
|
||||
#include "src/gpu/ccpr/GrCCAtlas.h"
|
||||
#include "src/gpu/ccpr/GrCCPathProcessor.h"
|
||||
@ -28,7 +29,7 @@ public:
|
||||
GrCCPathCache(uint32_t contextUniqueID);
|
||||
~GrCCPathCache();
|
||||
|
||||
class Key : public SkPathRef::GenIDChangeListener {
|
||||
class Key : public SkIDChangeListener {
|
||||
public:
|
||||
static sk_sp<Key> Make(uint32_t pathCacheUniqueID, int dataCountU32,
|
||||
const void* data = nullptr);
|
||||
@ -50,7 +51,7 @@ public:
|
||||
}
|
||||
|
||||
// Called when our corresponding path is modified or deleted. Not threadsafe.
|
||||
void onChange() override;
|
||||
void changed() override;
|
||||
|
||||
// TODO(b/30449950): use sized delete once P0722R3 is available
|
||||
static void operator delete(void* p);
|
||||
@ -248,7 +249,7 @@ private:
|
||||
: fCacheKey(std::move(cacheKey)), fMaskTransform(maskTransform) {
|
||||
}
|
||||
|
||||
bool hasBeenEvicted() const { return fCacheKey->shouldUnregisterFromPath(); }
|
||||
bool hasBeenEvicted() const { return fCacheKey->shouldDeregister(); }
|
||||
|
||||
// Resets this entry back to not having an atlas, and purges its previous atlas texture from the
|
||||
// resource cache if needed.
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include "src/gpu/geometry/GrShape.h"
|
||||
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
GrShape& GrShape::operator=(const GrShape& that) {
|
||||
@ -363,7 +365,7 @@ const SkPath* GrShape::originalPathForListeners() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GrShape::addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener> listener) const {
|
||||
void GrShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const {
|
||||
if (const auto* lp = this->originalPathForListeners()) {
|
||||
SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "src/gpu/GrStyle.h"
|
||||
#include <new>
|
||||
|
||||
class SkIDChangeListener;
|
||||
|
||||
/**
|
||||
* Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
|
||||
* It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
|
||||
@ -474,7 +476,7 @@ public:
|
||||
* a path is no longer in-use. If the shape started out as something other than a path, this
|
||||
* does nothing.
|
||||
*/
|
||||
void addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener>) const;
|
||||
void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const;
|
||||
|
||||
/**
|
||||
* Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "src/gpu/ops/GrTessellatingPathRenderer.h"
|
||||
#include <stdio.h>
|
||||
#include "src/core/SkGeometry.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/gpu/GrAuditTrail.h"
|
||||
#include "src/gpu/GrCaps.h"
|
||||
#include "src/gpu/GrClip.h"
|
||||
@ -42,17 +43,15 @@ struct TessInfo {
|
||||
};
|
||||
|
||||
// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
|
||||
class PathInvalidator : public SkPathRef::GenIDChangeListener {
|
||||
class UniqueKeyInvalidator : public SkIDChangeListener {
|
||||
public:
|
||||
PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
|
||||
UniqueKeyInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
|
||||
: fMsg(key, contextUniqueID) {}
|
||||
|
||||
private:
|
||||
GrUniqueKeyInvalidatedMessage fMsg;
|
||||
|
||||
void onChange() override {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
|
||||
}
|
||||
void changed() override { SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg); }
|
||||
};
|
||||
|
||||
bool cache_match(GrGpuBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
|
||||
@ -286,7 +285,8 @@ private:
|
||||
TessInfo info;
|
||||
info.fTolerance = isLinear ? 0 : tol;
|
||||
info.fCount = count;
|
||||
fShape.addGenIDChangeListener(sk_make_sp<PathInvalidator>(key, target->contextUniqueID()));
|
||||
fShape.addGenIDChangeListener(
|
||||
sk_make_sp<UniqueKeyInvalidator>(key, target->contextUniqueID()));
|
||||
key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
|
||||
rp->assignUniqueKeyToResource(key, vb.get());
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "include/private/GrRecordingContext.h"
|
||||
#include "include/private/GrResourceKey.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/gpu/GrBitmapTextureMaker.h"
|
||||
#include "src/gpu/GrCaps.h"
|
||||
#include "src/gpu/GrGpuResourcePriv.h"
|
||||
@ -133,10 +134,13 @@ SkImage_Lazy::SkImage_Lazy(Validator* validator)
|
||||
|
||||
SkImage_Lazy::~SkImage_Lazy() {
|
||||
#if SK_SUPPORT_GPU
|
||||
for (int i = 0; i < fUniqueKeyInvalidatedMessages.count(); ++i) {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(*fUniqueKeyInvalidatedMessages[i]);
|
||||
// We don't need the mutex. No other thread should have this image while it's being destroyed.
|
||||
for (int i = 0; i < fUniqueIDListeners.count(); ++i) {
|
||||
if (!fUniqueIDListeners[i]->shouldDeregister()) {
|
||||
fUniqueIDListeners[i]->changed();
|
||||
}
|
||||
fUniqueIDListeners[i]->unref();
|
||||
}
|
||||
fUniqueKeyInvalidatedMessages.deleteAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -368,23 +372,6 @@ private:
|
||||
typedef GrYUVProvider INHERITED;
|
||||
};
|
||||
|
||||
static void set_key_on_proxy(GrProxyProvider* proxyProvider,
|
||||
GrTextureProxy* proxy, GrTextureProxy* originalProxy,
|
||||
const GrUniqueKey& key) {
|
||||
if (key.isValid()) {
|
||||
if (originalProxy && originalProxy->getUniqueKey().isValid()) {
|
||||
SkASSERT(originalProxy->getUniqueKey() == key);
|
||||
SkASSERT(GrMipMapped::kYes == proxy->mipMapped() &&
|
||||
GrMipMapped::kNo == originalProxy->mipMapped());
|
||||
// If we had an originalProxy with a valid key, that means there already is a proxy in
|
||||
// the cache which matches the key, but it does not have mip levels and we require them.
|
||||
// Thus we must remove the unique key from that proxy.
|
||||
SkASSERT(originalProxy->getUniqueKey() == key);
|
||||
proxyProvider->removeUniqueKeyFromProxy(originalProxy);
|
||||
}
|
||||
proxyProvider->assignUniqueKeyToProxy(key, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkCachedData> SkImage_Lazy::getPlanes(SkYUVASizeInfo* yuvaSizeInfo,
|
||||
SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
|
||||
@ -439,6 +426,37 @@ GrSurfaceProxyView SkImage_Lazy::lockTextureProxyView(GrRecordingContext* ctx,
|
||||
GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
|
||||
GrSurfaceProxyView view;
|
||||
|
||||
auto installKey = [&](const GrSurfaceProxyView& view,
|
||||
const GrSurfaceProxyView& previouslyKeyedView = {}) {
|
||||
SkASSERT(view && view.asTextureProxy());
|
||||
if (key.isValid()) {
|
||||
if (!previouslyKeyedView) {
|
||||
// We will add an invalidator to the image so that if the path goes away we will
|
||||
// delete or recycle the mask texture.
|
||||
auto listener = GrMakeUniqueKeyInvalidationListener(&key, ctx->priv().contextID());
|
||||
this->addUniqueIDListener(std::move(listener));
|
||||
} else {
|
||||
auto previousProxy = previouslyKeyedView.asTextureProxy();
|
||||
SkASSERT(previousProxy->getUniqueKey() == key);
|
||||
SkASSERT(view.asTextureProxy()->mipMapped() == GrMipMapped::kYes &&
|
||||
previousProxy->mipMapped() == GrMipMapped::kNo);
|
||||
// If we had an previousProxy with a valid key, that means there already is a proxy
|
||||
// in the cache which matches the key, but it does not have mip levels and we
|
||||
// require them. Thus we must remove the unique key from that proxy.
|
||||
SkASSERT(previousProxy->getUniqueKey() == key);
|
||||
// We should have already put a listener invalidator on previousProxy's key. We
|
||||
// *may* have already put the listener on our local key. That depends on whether
|
||||
// previousProxy was created in this call or a previous call.
|
||||
SkASSERT(previousProxy->getUniqueKey().getCustomData());
|
||||
if (!key.getCustomData()) {
|
||||
key.setCustomData(sk_ref_sp(previousProxy->getUniqueKey().getCustomData()));
|
||||
}
|
||||
proxyProvider->removeUniqueKeyFromProxy(previousProxy);
|
||||
}
|
||||
proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
|
||||
}
|
||||
};
|
||||
|
||||
auto ct = this->colorTypeOfLockTextureProxy(caps);
|
||||
|
||||
// 1. Check the cache for a pre-existing one
|
||||
@ -456,20 +474,14 @@ GrSurfaceProxyView SkImage_Lazy::lockTextureProxyView(GrRecordingContext* ctx,
|
||||
}
|
||||
|
||||
// 2. Ask the generator to natively create one
|
||||
if (!view.proxy()) {
|
||||
if (!view) {
|
||||
ScopedGenerator generator(fSharedGenerator);
|
||||
view = generator->generateTexture(ctx, this->imageInfo(), fOrigin, mipMapped);
|
||||
if (view.proxy()) {
|
||||
if (view) {
|
||||
SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
|
||||
kLockTexturePathCount);
|
||||
GrTextureProxy* proxy = view.asTextureProxy();
|
||||
SkASSERT(proxy);
|
||||
set_key_on_proxy(proxyProvider, proxy, nullptr, key);
|
||||
installKey(view);
|
||||
if (satisfiesMipMapRequest(view)) {
|
||||
if (generator->texturesAreCacheable()) {
|
||||
*fUniqueKeyInvalidatedMessages.append() =
|
||||
new GrUniqueKeyInvalidatedMessage(key, ctx->priv().contextID());
|
||||
}
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@ -477,8 +489,7 @@ GrSurfaceProxyView SkImage_Lazy::lockTextureProxyView(GrRecordingContext* ctx,
|
||||
|
||||
// 3. Ask the generator to return YUV planes, which the GPU can convert. If we will be mipping
|
||||
// the texture we fall through here and have the CPU generate the mip maps for us.
|
||||
if (!view.proxy() && mipMapped == GrMipMapped::kNo &&
|
||||
!ctx->priv().options().fDisableGpuYUVConversion) {
|
||||
if (!view && mipMapped == GrMipMapped::kNo && !ctx->priv().options().fDisableGpuYUVConversion) {
|
||||
SkColorType colorType = this->colorType();
|
||||
|
||||
ScopedGenerator generator(fSharedGenerator);
|
||||
@ -496,32 +507,26 @@ GrSurfaceProxyView SkImage_Lazy::lockTextureProxyView(GrRecordingContext* ctx,
|
||||
view = provider.refAsTextureProxyView(ctx, this->imageInfo().dimensions(),
|
||||
SkColorTypeToGrColorType(colorType),
|
||||
generatorColorSpace, thisColorSpace);
|
||||
if (view.proxy()) {
|
||||
if (view) {
|
||||
SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
|
||||
kLockTexturePathCount);
|
||||
GrTextureProxy* proxy = view.asTextureProxy();
|
||||
SkASSERT(proxy);
|
||||
set_key_on_proxy(proxyProvider, proxy, nullptr, key);
|
||||
*fUniqueKeyInvalidatedMessages.append() =
|
||||
new GrUniqueKeyInvalidatedMessage(key, ctx->priv().contextID());
|
||||
installKey(view);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Ask the generator to return RGB(A) data, which the GPU can convert
|
||||
SkBitmap bitmap;
|
||||
if (!view.proxy() && this->getROPixels(&bitmap, chint)) {
|
||||
GrBitmapTextureMaker bitmapMaker(ctx, bitmap);
|
||||
if (!view && this->getROPixels(&bitmap, chint)) {
|
||||
GrBitmapTextureMaker bitmapMaker(ctx, bitmap, GrBitmapTextureMaker::Cached::kNo);
|
||||
view = bitmapMaker.view(mipMapped);
|
||||
GrTextureProxy* proxy = view.asTextureProxy();
|
||||
if (proxy && satisfiesMipMapRequest(view)) {
|
||||
SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
|
||||
kLockTexturePathCount);
|
||||
SkASSERT(proxy);
|
||||
set_key_on_proxy(proxyProvider, proxy, nullptr, key);
|
||||
*fUniqueKeyInvalidatedMessages.append() =
|
||||
new GrUniqueKeyInvalidatedMessage(key, ctx->priv().contextID());
|
||||
return view;
|
||||
if (view) {
|
||||
installKey(view);
|
||||
if (satisfiesMipMapRequest(view)) {
|
||||
SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
|
||||
kLockTexturePathCount);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,14 +542,11 @@ GrSurfaceProxyView SkImage_Lazy::lockTextureProxyView(GrRecordingContext* ctx,
|
||||
// generate the rest of the mips.
|
||||
SkASSERT(mipMapped == GrMipMapped::kYes);
|
||||
SkASSERT(view.asTextureProxy()->mipMapped() == GrMipMapped::kNo);
|
||||
*fUniqueKeyInvalidatedMessages.append() =
|
||||
new GrUniqueKeyInvalidatedMessage(key, ctx->priv().contextID());
|
||||
GrColorType srcColorType = SkColorTypeToGrColorType(this->colorType());
|
||||
GrSurfaceProxyView mippedView = GrCopyBaseMipMapToTextureProxy(
|
||||
ctx, view.proxy(), kTopLeft_GrSurfaceOrigin, srcColorType);
|
||||
auto mippedProxy = mippedView.asTextureProxy();
|
||||
if (mippedProxy) {
|
||||
set_key_on_proxy(proxyProvider, mippedProxy, view.asTextureProxy(), key);
|
||||
if (mippedView) {
|
||||
installKey(mippedView, view);
|
||||
return mippedView;
|
||||
}
|
||||
// We failed to make a mipped proxy with the base copied into it. This could have
|
||||
@ -562,6 +564,18 @@ GrColorType SkImage_Lazy::colorTypeOfLockTextureProxy(const GrCaps* caps) const
|
||||
return ct;
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
void SkImage_Lazy::addUniqueIDListener(sk_sp<SkIDChangeListener> listener) const {
|
||||
// Don't bother with the expense of a mutex lock if no other thread can have this image.
|
||||
if (this->unique()) {
|
||||
fUniqueIDListeners.push_back(listener.release());
|
||||
} else {
|
||||
SkAutoMutexExclusive lock(fUniqueIDListenersMutex);
|
||||
fUniqueIDListeners.push_back(listener.release());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkImage> SkImage::DecodeToTexture(GrContext* ctx, const void* encoded, size_t length,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#endif
|
||||
|
||||
class SharedGenerator;
|
||||
class SkIDChangeListener;
|
||||
|
||||
class SkImage_Lazy : public SkImage_Base {
|
||||
public:
|
||||
@ -71,6 +72,8 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
void addUniqueIDListener(sk_sp<SkIDChangeListener>) const;
|
||||
|
||||
class ScopedGenerator;
|
||||
|
||||
// Note that this->imageInfo() is not necessarily the info from the generator. It may be
|
||||
@ -87,10 +90,10 @@ private:
|
||||
mutable sk_sp<SkImage> fOnMakeColorTypeAndSpaceResult;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
// When the SkImage_Lazy goes away, we will iterate over all the unique keys we've used and
|
||||
// send messages to the GrContexts to say the unique keys are no longer valid. The GrContexts
|
||||
// can then release the resources, conntected with the those unique keys, from their caches.
|
||||
mutable SkTDArray<GrUniqueKeyInvalidatedMessage*> fUniqueKeyInvalidatedMessages;
|
||||
// When the SkImage_Lazy goes away, we will iterate over all the listeners to inform them
|
||||
// of the unique ID's demise. This is used to remove cached textures from GrContext.
|
||||
mutable SkTDArray<SkIDChangeListener*> fUniqueIDListeners;
|
||||
mutable SkMutex fUniqueIDListenersMutex;
|
||||
#endif
|
||||
|
||||
typedef SkImage_Base INHERITED;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "src/core/SkDevice.h"
|
||||
#include "src/core/SkDrawShadowInfo.h"
|
||||
#include "src/core/SkEffectPriv.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/core/SkRasterPipeline.h"
|
||||
#include "src/core/SkResourceCache.h"
|
||||
@ -381,7 +382,7 @@ private:
|
||||
static void* kNamespace;
|
||||
|
||||
// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
|
||||
class ShadowInvalidator : public SkPathRef::GenIDChangeListener {
|
||||
class ShadowInvalidator : public SkIDChangeListener {
|
||||
public:
|
||||
ShadowInvalidator(const SkResourceCache::Key& key) {
|
||||
fKey.reset(new uint8_t[key.size()]);
|
||||
@ -398,7 +399,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
void onChange() override {
|
||||
void changed() override {
|
||||
SkResourceCache::Find(this->getKey(), ShadowInvalidator::FindVisitor, nullptr);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkFont.h"
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkPathEffect.h"
|
||||
#include "include/core/SkRRect.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/core/SkStream.h"
|
||||
@ -21,6 +20,7 @@
|
||||
#include "include/utils/SkRandom.h"
|
||||
#include "src/core/SkAutoMalloc.h"
|
||||
#include "src/core/SkGeometry.h"
|
||||
#include "src/core/SkIDChangeListener.h"
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/core/SkReader32.h"
|
||||
#include "src/core/SkWriter32.h"
|
||||
@ -4347,13 +4347,12 @@ static void test_dump(skiatest::Reporter* reporter) {
|
||||
|
||||
namespace {
|
||||
|
||||
class ChangeListener : public SkPathRef::GenIDChangeListener {
|
||||
class ChangeListener : public SkIDChangeListener {
|
||||
public:
|
||||
ChangeListener(bool *changed) : fChanged(changed) { *fChanged = false; }
|
||||
~ChangeListener() override {}
|
||||
void onChange() override {
|
||||
*fChanged = true;
|
||||
}
|
||||
void changed() override { *fChanged = true; }
|
||||
|
||||
private:
|
||||
bool* fChanged;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user