From d4f100dad90ed5beb1b614464d8c4fcb22c0a993 Mon Sep 17 00:00:00 2001 From: Robert Phillips Date: Wed, 20 Sep 2017 15:58:44 -0400 Subject: [PATCH] Add native caching of uniquely keyed GrTextureProxies Change-Id: I303fe025b7856b8d681a2d35b416c015bd468e1d Reviewed-on: https://skia-review.googlesource.com/48300 Commit-Queue: Robert Phillips Reviewed-by: Brian Salomon --- gm/image_pict.cpp | 4 +- gn/gpu.gni | 1 + gn/tests.gni | 1 + include/private/GrSurfaceProxy.h | 2 +- include/private/GrTextureProxy.h | 35 ++++ src/gpu/GrBitmapTextureMaker.cpp | 1 - src/gpu/GrClipStackClip.cpp | 2 - src/gpu/GrRenderTargetProxy.cpp | 2 +- src/gpu/GrResourceCache.cpp | 64 +++++++ src/gpu/GrResourceCache.h | 55 ++++++ src/gpu/GrResourceProvider.cpp | 17 +- src/gpu/GrResourceProvider.h | 20 ++- src/gpu/GrSurfaceProxy.cpp | 10 +- src/gpu/GrTextureProxy.cpp | 44 ++++- src/gpu/GrTextureProxyCacheAccess.h | 46 +++++ src/gpu/GrTextureRenderTargetProxy.cpp | 9 +- src/gpu/SkGr.cpp | 1 - tests/TextureProxyTest.cpp | 238 +++++++++++++++++++++++++ 18 files changed, 515 insertions(+), 37 deletions(-) create mode 100644 src/gpu/GrTextureProxyCacheAccess.h create mode 100644 tests/TextureProxyTest.cpp diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp index 73fedec3a0..25c3b1c085 100644 --- a/gm/image_pict.cpp +++ b/gm/image_pict.cpp @@ -152,7 +152,7 @@ public: : SkImageGenerator(info) , fCtx(SkRef(ctx)) { - sk_sp surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, + sk_sp surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin, nullptr)); if (surface) { surface->getCanvas()->clear(0); @@ -188,7 +188,7 @@ protected: sk_sp dstContext(fCtx->contextPriv().makeDeferredSurfaceContext( desc, SkBackingFit::kExact, - SkBudgeted::kNo)); + SkBudgeted::kYes)); if (!dstContext) { return nullptr; } diff --git a/gn/gpu.gni b/gn/gpu.gni index 1a5687e9e8..fbc976b70d 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -185,6 +185,7 @@ skia_gpu_sources = [ "$_src/gpu/GrTessellator.h", "$_src/gpu/GrTextureOpList.cpp", "$_src/gpu/GrTextureOpList.h", + "$_src/gpu/GrTextureProxyCacheAccess.h", "$_src/gpu/GrTracing.h", "$_src/gpu/GrTestUtils.cpp", "$_src/gpu/GrTestUtils.h", diff --git a/gn/tests.gni b/gn/tests.gni index c2f3759cd2..9cea3ea355 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -248,6 +248,7 @@ tests_sources = [ "$_tests/TestUtils.cpp", "$_tests/TextBlobCacheTest.cpp", "$_tests/TextBlobTest.cpp", + "$_tests/TextureProxyTest.cpp", "$_tests/Time.cpp", "$_tests/TLSTest.cpp", "$_tests/TopoSortTest.cpp", diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h index 12ba91ba91..fcb839c1fe 100644 --- a/include/private/GrSurfaceProxy.h +++ b/include/private/GrSurfaceProxy.h @@ -378,7 +378,7 @@ protected: bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, bool needsStencil, GrSurfaceFlags flags, bool isMipMapped, - SkDestinationSurfaceColorMode mipColorMode); + SkDestinationSurfaceColorMode mipColorMode, const GrUniqueKey*); // For wrapped resources, 'fConfig', 'fWidth', 'fHeight', and 'fOrigin; will always be filled in // from the wrapped resource. diff --git a/include/private/GrTextureProxy.h b/include/private/GrTextureProxy.h index 9db2bb2e50..3634ce4d0a 100644 --- a/include/private/GrTextureProxy.h +++ b/include/private/GrTextureProxy.h @@ -12,6 +12,7 @@ #include "GrSurfaceProxy.h" class GrCaps; +class GrResourceCache; class GrResourceProvider; class GrTextureOpList; @@ -38,6 +39,31 @@ public: bool isMipMapped() const { return fIsMipMapped; } + /** + * Return the texture proxy's unique key. It will be invalid if the proxy doesn't have one. + */ + const GrUniqueKey& getUniqueKey() const { +#ifdef SK_DEBUG + if (fTarget && fUniqueKey.isValid()) { + SkASSERT(fTarget->getUniqueKey().isValid()); + // It is possible for a non-keyed proxy to have a uniquely keyed resource assigned to + // it. This just means that a future user of the resource will be filling it with unique + // data. However, if the proxy has a unique key its attached resource should also + // have that key. + SkASSERT(fUniqueKey == fTarget->getUniqueKey()); + } +#endif + + return fUniqueKey; + } + + /** + * Internal-only helper class used for manipulations of the resource by the cache. + */ + class CacheAccess; + inline CacheAccess cacheAccess(); + inline const CacheAccess cacheAccess() const; + protected: friend class GrSurfaceProxy; // for ctors @@ -47,6 +73,8 @@ protected: // Wrapped version GrTextureProxy(sk_sp, GrSurfaceOrigin); + ~GrTextureProxy() override; + SkDestinationSurfaceColorMode mipColorMode() const { return fMipColorMode; } sk_sp createSurface(GrResourceProvider*) const override; @@ -55,8 +83,15 @@ private: bool fIsMipMapped; SkDestinationSurfaceColorMode fMipColorMode; + GrUniqueKey fUniqueKey; + GrResourceCache* fCache; // only set when fUniqueKey is valid + size_t onUninstantiatedGpuMemorySize() const override; + // Methods made available via GrTextureProxy::CacheAccess + void setUniqueKey(GrResourceCache*, const GrUniqueKey&); + void clearUniqueKey(); + // For wrapped proxies the GrTexture pointer is stored in GrIORefProxy. // For deferred proxies that pointer will be filled in when we need to instantiate // the deferred resource diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp index 85e791ef90..2bf3afe4e1 100644 --- a/src/gpu/GrBitmapTextureMaker.cpp +++ b/src/gpu/GrBitmapTextureMaker.cpp @@ -53,7 +53,6 @@ sk_sp GrBitmapTextureMaker::refOriginalTextureProxy(bool willBeM if (proxy && fOriginalKey.isValid()) { SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); this->context()->resourceProvider()->assignUniqueKeyToProxy(fOriginalKey, proxy.get()); - // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching GrInstallBitmapUniqueKeyInvalidator(fOriginalKey, fBitmap.pixelRef()); } return proxy; diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp index 165b442d90..69d81a08a2 100644 --- a/src/gpu/GrClipStackClip.cpp +++ b/src/gpu/GrClipStackClip.cpp @@ -420,7 +420,6 @@ sk_sp GrClipStackClip::createAlphaClipMask(GrContext* context, SkASSERT(result->origin() == kBottomLeft_GrSurfaceOrigin); resourceProvider->assignUniqueKeyToProxy(key, result.get()); - // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching add_invalidate_on_pop_message(*fStack, reducedClip.elementsGenID(), key); return result; @@ -559,7 +558,6 @@ sk_sp GrClipStackClip::createSoftwareClipMask( SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); context->resourceProvider()->assignUniqueKeyToProxy(key, proxy.get()); - // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching add_invalidate_on_pop_message(*fStack, reducedClip.elementsGenID(), key); return proxy; } diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp index fff3d0bcea..111ecf6625 100644 --- a/src/gpu/GrRenderTargetProxy.cpp +++ b/src/gpu/GrRenderTargetProxy.cpp @@ -53,7 +53,7 @@ bool GrRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) { if (!this->instantiateImpl(resourceProvider, fSampleCnt, fNeedsStencil, kFlags, /* isMipped = */ false, - SkDestinationSurfaceColorMode::kLegacy)) { + SkDestinationSurfaceColorMode::kLegacy, nullptr)) { return false; } SkASSERT(fTarget->asRenderTarget()); diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp index f6170c01b6..c207b44a38 100644 --- a/src/gpu/GrResourceCache.cpp +++ b/src/gpu/GrResourceCache.cpp @@ -10,6 +10,8 @@ #include "GrCaps.h" #include "GrGpuResourceCacheAccess.h" +#include "GrTexture.h" +#include "GrTextureProxyCacheAccess.h" #include "GrTracing.h" #include "SkGr.h" #include "SkMessageBus.h" @@ -578,6 +580,8 @@ void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScr void GrResourceCache::processInvalidUniqueKeys( const SkTArray& msgs) { for (int i = 0; i < msgs.count(); ++i) { + this->processInvalidProxyUniqueKey(msgs[i].key()); + GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key()); if (resource) { resource->resourcePriv().removeUniqueKey(); @@ -847,3 +851,63 @@ bool GrResourceCache::isInCache(const GrGpuResource* resource) const { } #endif + +void GrResourceCache::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy* proxy) { + SkASSERT(key.isValid()); + SkASSERT(proxy); + + // If there is already a GrResource with this key then the caller has violated the normal + // usage pattern of uniquely keyed resources (e.g., they have created one w/o first seeing + // if it already existed in the cache). + SkASSERT(!this->findAndRefUniqueResource(key)); + + // Uncached resources can never have a unique key, unless they're wrapped resources. Wrapped + // resources are a special case: the unique keys give us a weak ref so that we can reuse the + // same resource (rather than re-wrapping). When a wrapped resource is no longer referenced, + // it will always be released - it is never converted to a scratch resource. + if (SkBudgeted::kNo == proxy->isBudgeted() && + (!proxy->priv().isInstantiated() || + !proxy->priv().peekSurface()->resourcePriv().refsWrappedObjects())) { + return; + } + + SkASSERT(!fUniquelyKeyedProxies.find(key)); // multiple proxies can't get the same key + + proxy->cacheAccess().setUniqueKey(this, key); + SkASSERT(proxy->getUniqueKey() == key); + fUniquelyKeyedProxies.add(proxy); +} + +sk_sp GrResourceCache::findProxyByUniqueKey(const GrUniqueKey& key, + GrSurfaceOrigin origin) { + + sk_sp result = sk_ref_sp(fUniquelyKeyedProxies.find(key)); + if (result) { + SkASSERT(result->origin() == origin); + return result; + } + + GrGpuResource* resource = findAndRefUniqueResource(key); + if (!resource) { + return nullptr; + } + + sk_sp texture(static_cast(resource)->asTexture()); + SkASSERT(texture); + + result = GrSurfaceProxy::MakeWrapped(std::move(texture), origin); + SkASSERT(result->getUniqueKey() == key); + fUniquelyKeyedProxies.add(result.get()); + return result; +} + +void GrResourceCache::processInvalidProxyUniqueKey(const GrUniqueKey& key) { + // Note: this method is called for the whole variety of GrGpuResources so often 'key' + // will not be in 'fUniquelyKeyedProxies'. + GrTextureProxy* proxy = fUniquelyKeyedProxies.find(key); + if (proxy) { + fUniquelyKeyedProxies.remove(key); + proxy->cacheAccess().clearUniqueKey(); + } +} + diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h index 5cdc5636ea..96323be73c 100644 --- a/src/gpu/GrResourceCache.h +++ b/src/gpu/GrResourceCache.h @@ -13,6 +13,7 @@ #include "GrGpuResourcePriv.h" #include "GrResourceCache.h" #include "GrResourceKey.h" +#include "GrTextureProxy.h" #include "SkMessageBus.h" #include "SkRefCnt.h" #include "SkTArray.h" @@ -156,6 +157,48 @@ public: return resource; } + /////////////////////////////////////////////////////////////////////////// + // TextureProxies & GrUniqueKeys + // + // The two GrResourceCache methods assignUniqueKeyToProxy and findProxyByUniqueKey drive + // the behavior of uniqueKeys on proxies. + // + // assignUniqueKeyToProxy does the following: + // if the proxy is wrapped, it sets the texture & proxy keys & adds the proxy to the hash + // if the proxy is deferred, it just set the unique key on the proxy & adds it to the hash + // + // Note that when a deferred proxy with a unique key is instantiated, its unique key will be + // pushed to the backing resource. + // + // Futher note, a proxy can only receive a unique key once. It can be removed if Ganesh + // determines that the key will never be used again but, in that case, the proxy should + // never receive another key. + // + // findProxyByUniqueKey does the following: + // first looks in the UniqueKeyProxy hash table to see if there is already a proxy w/ the key + // failing that it looks in the ResourceCache to see there is a texture with that key + // if so, it will wrap the texture in a proxy, add the proxy to the hash and return it + // failing that it will return null + + /* + * Associate the provided proxy with the provided unique key. + */ + void assignUniqueKeyToProxy(const GrUniqueKey&, GrTextureProxy*); + + /** + * Find a texture proxy that is associated with the provided unique key. + */ + sk_sp findProxyByUniqueKey(const GrUniqueKey&, GrSurfaceOrigin); + + /** + * Either the proxy attached to the unique key is being deleted (in which case we + * don't want it cluttering up the hash table) or the client has indicated that + * it will never refer to the unique key again. In either case, remove the key + * from the hash table. + * Note: this does not, by itself, alter unique key attached to the underlying GrTexture. + */ + void processInvalidProxyUniqueKey(const GrUniqueKey&); + /** * Query whether a unique key exists in the cache. */ @@ -249,6 +292,8 @@ public: // Enumerates all cached resources and dumps their details to traceMemoryDump. void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const; + int numUniqueKeyProxies_TestOnly() const; + private: /////////////////////////////////////////////////////////////////////////// /// @name Methods accessible via ResourceAccess @@ -303,6 +348,13 @@ private: }; typedef SkTDynamicHash UniqueHash; + struct UniquelyKeyedProxyHashTraits { + static const GrUniqueKey& GetKey(const GrTextureProxy& p) { return p.getUniqueKey(); } + + static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); } + }; + typedef SkTDynamicHash UniquelyKeyedProxyHash; + static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) { return a->cacheAccess().timestamp() < b->cacheAccess().timestamp(); } @@ -327,6 +379,9 @@ private: ScratchMap fScratchMap; // This holds all resources that have unique keys. UniqueHash fUniqueHash; + // This holds the texture proxies that have unique keys. The resourceCache does not get a ref + // on these proxies but they must send a message to the resourceCache when they are deleted. + UniquelyKeyedProxyHash fUniquelyKeyedProxies; // our budget, used in purgeAsNeeded() int fMaxCount; diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp index 8dce5497d8..0182baa977 100644 --- a/src/gpu/GrResourceProvider.cpp +++ b/src/gpu/GrResourceProvider.cpp @@ -292,7 +292,6 @@ void GrResourceProvider::assignUniqueKeyToTexture(const GrUniqueKey& key, GrText this->assignUniqueKeyToResource(key, texture); } -// MDB TODO (caching): this side-steps the issue of texture proxies with unique IDs void GrResourceProvider::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy* proxy) { ASSERT_SINGLE_OWNER SkASSERT(key.isValid()); @@ -300,25 +299,13 @@ void GrResourceProvider::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextur return; } - if (!proxy->instantiate(this)) { - return; - } - GrTexture* texture = proxy->priv().peekTexture(); - - this->assignUniqueKeyToResource(key, texture); + fCache->assignUniqueKeyToProxy(key, proxy); } -// MDB TODO (caching): this side-steps the issue of texture proxies with unique IDs sk_sp GrResourceProvider::findProxyByUniqueKey(const GrUniqueKey& key, GrSurfaceOrigin origin) { ASSERT_SINGLE_OWNER - - sk_sp texture(this->findAndRefTextureByUniqueKey(key)); - if (!texture) { - return nullptr; - } - - return GrSurfaceProxy::MakeWrapped(std::move(texture), origin); + return this->isAbandoned() ? nullptr : fCache->findProxyByUniqueKey(key, origin); } const GrBuffer* GrResourceProvider::createPatternedIndexBuffer(const uint16_t* pattern, diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h index 1ab1ec42fc..7be8ec1793 100644 --- a/src/gpu/GrResourceProvider.h +++ b/src/gpu/GrResourceProvider.h @@ -45,18 +45,20 @@ public: return static_cast(this->findAndRefResourceByUniqueKey(key)); } + /* + * Assigns a unique key to a proxy. The proxy will be findable via this key using + * findProxyByUniqueKey(). It is an error if an existing proxy already has a key. + */ + void assignUniqueKeyToProxy(const GrUniqueKey&, GrTextureProxy*); + + /* + * Finds a proxy by unique key. + */ + sk_sp findProxyByUniqueKey(const GrUniqueKey&, GrSurfaceOrigin); + /////////////////////////////////////////////////////////////////////////// // Textures - /** Assigns a unique key to the texture. The texture will be findable via this key using - findTextureByUniqueKey(). If an existing texture has this key, it's key will be removed. */ - void assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy*); - - /** Finds a texture by unique key. If the texture is found it is ref'ed and returned. */ - // MDB TODO (caching): If this were actually caching proxies (rather than shallowly - // wrapping GrSurface caching) we would not need the origin parameter. - sk_sp findProxyByUniqueKey(const GrUniqueKey& key, GrSurfaceOrigin); - /** * Finds a texture that approximately matches the descriptor. Will be at least as large in width * and height as desc specifies. If desc specifies that the texture should be a render target diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp index 5918830bf8..433c230881 100644 --- a/src/gpu/GrSurfaceProxy.cpp +++ b/src/gpu/GrSurfaceProxy.cpp @@ -109,8 +109,12 @@ void GrSurfaceProxy::assign(sk_sp surface) { bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, bool needsStencil, GrSurfaceFlags flags, bool isMipMapped, - SkDestinationSurfaceColorMode mipColorMode) { + SkDestinationSurfaceColorMode mipColorMode, + const GrUniqueKey* uniqueKey) { if (fTarget) { + if (uniqueKey) { + SkASSERT(fTarget->getUniqueKey() == *uniqueKey); + } return attach_stencil_if_needed(resourceProvider, fTarget, needsStencil); } @@ -120,6 +124,10 @@ bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int s return false; } + if (uniqueKey) { + resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get()); + } + this->assign(std::move(surface)); return true; } diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp index 3cb87b3faf..8a68e03bac 100644 --- a/src/gpu/GrTextureProxy.cpp +++ b/src/gpu/GrTextureProxy.cpp @@ -7,25 +7,43 @@ #include "GrTextureProxy.h" +#include "GrContext.h" +#include "GrResourceCache.h" + #include "GrTexturePriv.h" GrTextureProxy::GrTextureProxy(const GrSurfaceDesc& srcDesc, SkBackingFit fit, SkBudgeted budgeted, const void* srcData, size_t /*rowBytes*/, uint32_t flags) : INHERITED(srcDesc, fit, budgeted, flags) , fIsMipMapped(srcDesc.fIsMipMapped) - , fMipColorMode(SkDestinationSurfaceColorMode::kLegacy) { + , fMipColorMode(SkDestinationSurfaceColorMode::kLegacy) + , fCache(nullptr) { SkASSERT(!srcData); // currently handled in Make() } GrTextureProxy::GrTextureProxy(sk_sp surf, GrSurfaceOrigin origin) : INHERITED(std::move(surf), origin, SkBackingFit::kExact) , fIsMipMapped(fTarget->asTexture()->texturePriv().hasMipMaps()) - , fMipColorMode(fTarget->asTexture()->texturePriv().mipColorMode()) { + , fMipColorMode(fTarget->asTexture()->texturePriv().mipColorMode()) + , fCache(nullptr) { + if (fTarget->getUniqueKey().isValid()) { + fUniqueKey = fTarget->getUniqueKey(); + fCache = fTarget->asTexture()->getContext()->getResourceCache(); + } +} + +GrTextureProxy::~GrTextureProxy() { + if (fUniqueKey.isValid()) { + fCache->processInvalidProxyUniqueKey(fUniqueKey); + } else { + SkASSERT(!fCache); + } } bool GrTextureProxy::instantiate(GrResourceProvider* resourceProvider) { if (!this->instantiateImpl(resourceProvider, 0, /* needsStencil = */ false, - kNone_GrSurfaceFlags, fIsMipMapped, fMipColorMode)) { + kNone_GrSurfaceFlags, fIsMipMapped, fMipColorMode, + fUniqueKey.isValid() ? &fUniqueKey : nullptr)) { return false; } @@ -80,3 +98,23 @@ size_t GrTextureProxy::onUninstantiatedGpuMemorySize() const { return GrSurface::ComputeSize(fConfig, fWidth, fHeight, 1, kHasMipMaps, SkBackingFit::kApprox == fFit); } + +void GrTextureProxy::setUniqueKey(GrResourceCache* cache, const GrUniqueKey& key) { + SkASSERT(key.isValid()); + SkASSERT(!fUniqueKey.isValid()); // proxies can only ever get one uniqueKey + + if (fTarget) { + SkASSERT(!fTarget->getUniqueKey().isValid()); + fTarget->resourcePriv().setUniqueKey(key); + SkASSERT(fTarget->getUniqueKey() == key); + } + + fUniqueKey = key; + fCache = cache; +} + +void GrTextureProxy::clearUniqueKey() { + fUniqueKey.reset(); + fCache = nullptr; +} + diff --git a/src/gpu/GrTextureProxyCacheAccess.h b/src/gpu/GrTextureProxyCacheAccess.h new file mode 100644 index 0000000000..79cf845987 --- /dev/null +++ b/src/gpu/GrTextureProxyCacheAccess.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureProxyCacheAccess_DEFINED +#define GrTextureProxyCacheAccess_DEFINED + +#include "GrTextureProxy.h" + +/** + * This class allows GrResourceCache increased privileged access to GrTextureProxy objects. + */ +class GrTextureProxy::CacheAccess { +private: + void setUniqueKey(GrResourceCache* cache, const GrUniqueKey& key) { + fTextureProxy->setUniqueKey(cache, key); + } + + void clearUniqueKey() { + fTextureProxy->clearUniqueKey(); + } + + explicit CacheAccess(GrTextureProxy* textureProxy) : fTextureProxy(textureProxy) {} + CacheAccess(const CacheAccess&) {} // unimpl + CacheAccess& operator=(const CacheAccess&); // unimpl + + // No taking addresses of this type. + const CacheAccess* operator&() const; + CacheAccess* operator&(); + + GrTextureProxy* fTextureProxy; + + friend class GrTextureProxy; // to construct/copy this type. + friend class GrResourceCache; // to use this type +}; + +inline GrTextureProxy::CacheAccess GrTextureProxy::cacheAccess() { return CacheAccess(this); } + +inline const GrTextureProxy::CacheAccess GrTextureProxy::cacheAccess() const { + return CacheAccess(const_cast(this)); +} + +#endif diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp index 4a4d79c531..7515c3df08 100644 --- a/src/gpu/GrTextureRenderTargetProxy.cpp +++ b/src/gpu/GrTextureRenderTargetProxy.cpp @@ -48,10 +48,17 @@ size_t GrTextureRenderTargetProxy::onUninstantiatedGpuMemorySize() const { bool GrTextureRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) { static constexpr GrSurfaceFlags kFlags = kRenderTarget_GrSurfaceFlag; + const GrUniqueKey& key = this->getUniqueKey(); + if (!this->instantiateImpl(resourceProvider, this->numStencilSamples(), this->needsStencil(), - kFlags, this->isMipMapped(), this->mipColorMode())) { + kFlags, this->isMipMapped(), this->mipColorMode(), + key.isValid() ? &key : nullptr)) { return false; } + if (key.isValid()) { + SkASSERT(key == this->getUniqueKey()); + } + SkASSERT(fTarget->asRenderTarget()); SkASSERT(fTarget->asTexture()); diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 4943804553..b9f75e0821 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -266,7 +266,6 @@ sk_sp GrMakeCachedBitmapProxy(GrResourceProvider* resourceProvid if (proxy && originalKey.isValid()) { SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); resourceProvider->assignUniqueKeyToProxy(originalKey, proxy.get()); - // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching GrInstallBitmapUniqueKeyInvalidator(originalKey, bitmap.pixelRef()); } } diff --git a/tests/TextureProxyTest.cpp b/tests/TextureProxyTest.cpp new file mode 100644 index 0000000000..58c29fcb85 --- /dev/null +++ b/tests/TextureProxyTest.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrBackendSurface.h" +#include "GrContextPriv.h" +#include "GrResourceCache.h" +#include "GrResourceProvider.h" +#include "GrTest.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" + +#include "SkGr.h" +#include "SkImage.h" + +int GrResourceCache::numUniqueKeyProxies_TestOnly() const { + return fUniquelyKeyedProxies.count(); +} + +static GrSurfaceDesc make_desc(GrSurfaceFlags flags) { + GrSurfaceDesc desc; + desc.fFlags = flags; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + desc.fWidth = 64; + desc.fHeight = 64; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fSampleCnt = 0; + + return desc; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Basic test + +static sk_sp deferred_tex(GrResourceProvider* provider, SkBackingFit fit) { + GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags); + + // Only budgeted & wrapped external proxies get to carry uniqueKeys + return GrSurfaceProxy::MakeDeferred(provider, desc, fit, SkBudgeted::kYes); +} + +static sk_sp deferred_texRT(GrResourceProvider* provider, SkBackingFit fit) { + GrSurfaceDesc desc = make_desc(kRenderTarget_GrSurfaceFlag); + + // Only budgeted & wrapped external proxies get to carry uniqueKeys + return GrSurfaceProxy::MakeDeferred(provider, desc, fit, SkBudgeted::kYes); +} + +static sk_sp wrapped(GrResourceProvider* provider, SkBackingFit fit) { + GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags); + + sk_sp tex; + if (SkBackingFit::kApprox == fit) { + tex = sk_sp(provider->createApproxTexture(desc, 0)); + } else { + // Only budgeted & wrapped external proxies get to carry uniqueKeys + tex = provider->createTexture(desc, SkBudgeted::kYes); + } + + return GrSurfaceProxy::MakeWrapped(std::move(tex), kBottomLeft_GrSurfaceOrigin); +} + +static sk_sp create_wrapped_backend(GrContext* context, SkBackingFit fit, + sk_sp* backingSurface) { + GrResourceProvider* provider = context->resourceProvider(); + + GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags); + + *backingSurface = provider->createTexture(desc, SkBudgeted::kNo); + if (!(*backingSurface)) { + return nullptr; + } + + GrBackendTexture backendTex = + GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + 64, 64, + kRGBA_8888_GrPixelConfig, + (*backingSurface)->getTextureHandle()); + + return GrSurfaceProxy::MakeWrappedBackend(context, backendTex, kBottomLeft_GrSurfaceOrigin); +} + + +// This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning +// and looking them up work, etc. +static void basic_test(GrContext* context, + skiatest::Reporter* reporter, + sk_sp proxy, bool proxyIsCached) { + static int id = 1; + + GrResourceProvider* provider = context->resourceProvider(); + GrResourceCache* cache = context->getResourceCache(); + + int startCacheCount = cache->getResourceCount(); + + REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid()); + + GrUniqueKey key; + GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64)); + ++id; + + // Assigning the uniqueKey adds the proxy to the hash but doesn't force instantiation + REPORTER_ASSERT(reporter, !cache->numUniqueKeyProxies_TestOnly()); + provider->assignUniqueKeyToProxy(key, proxy.get()); + REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount()); + + // setUniqueKey had better stick + REPORTER_ASSERT(reporter, key == proxy->getUniqueKey()); + + // We just added it, surely we can find it + REPORTER_ASSERT(reporter, provider->findProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin)); + REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly()); + + // Once instantiated, the backing resource should have the same key + SkAssertResult(proxy->instantiate(provider)); + const GrUniqueKey& texKey = proxy->priv().peekSurface()->getUniqueKey(); + REPORTER_ASSERT(reporter, texKey.isValid()); + REPORTER_ASSERT(reporter, key == texKey); + if (proxyIsCached) { + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + } + + // deleting the proxy should delete it from the hash but not the cache + proxy = nullptr; + REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + // If the proxy was cached refinding it should bring it back to life + proxy = provider->findProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin); + if (proxyIsCached) { + REPORTER_ASSERT(reporter, proxy); + REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly()); + } else { + REPORTER_ASSERT(reporter, !proxy); + REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly()); + } + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + // Mega-purging it should remove it from both the hash and the cache + proxy = nullptr; + cache->purgeAllUnlocked(); + if (proxyIsCached) { + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + } else { + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + } + + // We can bring neither the texture nor proxy back from perma-death + proxy = provider->findProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin); + REPORTER_ASSERT(reporter, !proxy); + if (proxyIsCached) { + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + } else { + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Invalidation test + +// Test if invalidating unique ids operates as expected for texture proxies. +static void invalidation_test(GrContext* context, skiatest::Reporter* reporter) { + + GrResourceCache* cache = context->getResourceCache(); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + + sk_sp rasterImg; + + { + SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType); + + SkBitmap bm; + bm.allocPixels(ii); + + rasterImg = SkImage::MakeFromBitmap(bm); + REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + } + + sk_sp textureImg = rasterImg->makeTextureImage(context, nullptr); + REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + rasterImg = nullptr; // this invalidates the uniqueKey + + // this forces the cache to respond to the inval msg + int maxNum; + size_t maxBytes; + context->getResourceCacheLimits(&maxNum, &maxBytes); + context->setResourceCacheLimits(maxNum-1, maxBytes); + + REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + textureImg = nullptr; + context->purgeAllUnlockedResources(); + + REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrResourceProvider* provider = context->resourceProvider(); + GrResourceCache* cache = context->getResourceCache(); + + REPORTER_ASSERT(reporter, !cache->numUniqueKeyProxies_TestOnly()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + + for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) { + for (auto create : { deferred_tex, deferred_texRT, wrapped }) { + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + basic_test(context, reporter, create(provider, fit), true); + } + + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + sk_sp backingTex; + sk_sp proxy = create_wrapped_backend(context, fit, &backingTex); + basic_test(context, reporter, std::move(proxy), false); + + backingTex = nullptr; + cache->purgeAllUnlocked(); + } + + invalidation_test(context, reporter); +} + +#endif