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:
Brian Salomon 2020-03-02 12:50:47 -05:00 committed by Skia Commit-Bot
parent a1613c917e
commit 99a813ca29
20 changed files with 203 additions and 145 deletions

View File

@ -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",

View File

@ -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:

View File

@ -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

View 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

View File

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

View File

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

View File

@ -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,

View File

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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

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

View File

@ -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.

View File

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

View File

@ -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

View File

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

View File

@ -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,

View File

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

View File

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

View File

@ -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;
};