/* * 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 "GrProxyProvider.h" #include "GrCaps.h" #include "GrRenderTarget.h" #include "GrResourceKey.h" #include "GrResourceProvider.h" #include "GrSurfaceProxy.h" #include "GrSurfaceProxyPriv.h" #include "GrTexture.h" #include "GrTextureProxyCacheAccess.h" #include "GrTextureRenderTargetProxy.h" #include "../private/GrSingleOwner.h" #include "SkGr.h" #include "SkImage.h" #include "SkImage_Base.h" #include "SkMipMap.h" #define ASSERT_SINGLE_OWNER \ SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);) GrProxyProvider::GrProxyProvider(GrResourceProvider* resourceProvider, GrResourceCache* resourceCache, sk_sp caps, GrSingleOwner* owner) : fResourceProvider(resourceProvider) , fResourceCache(resourceCache) , fAbandoned(false) , fCaps(caps) #ifdef SK_DEBUG , fSingleOwner(owner) #endif { } GrProxyProvider::~GrProxyProvider() { SkASSERT(!fUniquelyKeyedProxies.count()); } bool GrProxyProvider::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy* proxy) { ASSERT_SINGLE_OWNER SkASSERT(key.isValid()); if (this->isAbandoned() || !proxy) { return false; } // 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(!fResourceCache || !fResourceCache->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 false; } SkASSERT(!fUniquelyKeyedProxies.find(key)); // multiple proxies can't get the same key proxy->cacheAccess().setUniqueKey(this, key); SkASSERT(proxy->getUniqueKey() == key); fUniquelyKeyedProxies.add(proxy); return true; } void GrProxyProvider::adoptUniqueKeyFromSurface(GrTextureProxy* proxy, const GrSurface* surf) { SkASSERT(surf->getUniqueKey().isValid()); proxy->cacheAccess().setUniqueKey(this, surf->getUniqueKey()); SkASSERT(proxy->getUniqueKey() == surf->getUniqueKey()); // multiple proxies can't get the same key SkASSERT(!fUniquelyKeyedProxies.find(surf->getUniqueKey())); fUniquelyKeyedProxies.add(proxy); } void GrProxyProvider::removeUniqueKeyFromProxy(const GrUniqueKey& key, GrTextureProxy* proxy) { ASSERT_SINGLE_OWNER if (this->isAbandoned() || !proxy) { return; } this->processInvalidProxyUniqueKey(key, proxy, true); } sk_sp GrProxyProvider::findProxyByUniqueKey(const GrUniqueKey& key, GrSurfaceOrigin origin) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } sk_sp result = sk_ref_sp(fUniquelyKeyedProxies.find(key)); if (result) { SkASSERT(result->origin() == origin); } return result; } sk_sp GrProxyProvider::createWrapped(sk_sp tex, GrSurfaceOrigin origin) { #ifdef SK_DEBUG if (tex->getUniqueKey().isValid()) { SkASSERT(!this->findProxyByUniqueKey(tex->getUniqueKey(), origin)); } #endif if (tex->asRenderTarget()) { return sk_sp(new GrTextureRenderTargetProxy(std::move(tex), origin)); } else { return sk_sp(new GrTextureProxy(std::move(tex), origin)); } } sk_sp GrProxyProvider::findOrCreateProxyByUniqueKey(const GrUniqueKey& key, GrSurfaceOrigin origin) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } sk_sp result = this->findProxyByUniqueKey(key, origin); if (result) { return result; } if (!fResourceCache) { return nullptr; } GrGpuResource* resource = fResourceCache->findAndRefUniqueResource(key); if (!resource) { return nullptr; } sk_sp texture(static_cast(resource)->asTexture()); SkASSERT(texture); result = this->createWrapped(std::move(texture), origin); SkASSERT(result->getUniqueKey() == key); // createWrapped should've added this for us SkASSERT(fUniquelyKeyedProxies.find(key)); return result; } sk_sp GrProxyProvider::createInstantiatedProxy(const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted, uint32_t flags) { sk_sp tex; if (SkBackingFit::kApprox == fit) { tex = fResourceProvider->createApproxTexture(desc, flags); } else { tex = fResourceProvider->createTexture(desc, budgeted, flags); } if (!tex) { return nullptr; } return this->createWrapped(std::move(tex), desc.fOrigin); } sk_sp GrProxyProvider::createTextureProxy(const GrSurfaceDesc& desc, SkBudgeted budgeted, const void* srcData, size_t rowBytes) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } if (srcData) { GrMipLevel mipLevel = { srcData, rowBytes }; sk_sp tex = fResourceProvider->createTexture(desc, budgeted, SkBackingFit::kExact, mipLevel); if (!tex) { return nullptr; } return this->createWrapped(std::move(tex), desc.fOrigin); } return this->createProxy(desc, SkBackingFit::kExact, budgeted); } sk_sp GrProxyProvider::createTextureProxy(sk_sp srcImage, GrSurfaceFlags flags, GrSurfaceOrigin origin, int sampleCnt, SkBudgeted budgeted, SkBackingFit fit) { ASSERT_SINGLE_OWNER SkASSERT(srcImage); if (this->isAbandoned()) { return nullptr; } GrSurfaceDesc desc; desc.fWidth = srcImage->width(); desc.fHeight = srcImage->height(); desc.fFlags = flags; desc.fOrigin = origin; desc.fSampleCnt = sampleCnt; desc.fConfig = SkImageInfo2GrPixelConfig(as_IB(srcImage)->onImageInfo(), *this->caps()); sk_sp proxy = this->createLazyProxy( [desc, budgeted, srcImage, fit] (GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) { if (!resourceProvider) { // Nothing to clean up here. Once the proxy (and thus lambda) is deleted the ref // on srcImage will be released. return sk_sp(); } SkPixmap pixMap; SkAssertResult(srcImage->peekPixels(&pixMap)); GrMipLevel mipLevel = { pixMap.addr(), pixMap.rowBytes() }; return resourceProvider->createTexture(desc, budgeted, fit, mipLevel); }, desc, GrMipMapped::kNo, fit, budgeted); if (fResourceProvider) { // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however // we're better off instantiating the proxy immediately here. if (!proxy->priv().doLazyInstantiation(fResourceProvider)) { return nullptr; } } return proxy; } sk_sp GrProxyProvider::createMipMapProxy( const GrSurfaceDesc& desc, SkBudgeted budgeted, const GrMipLevel texels[], int mipLevelCount, SkDestinationSurfaceColorMode mipColorMode) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } if (!mipLevelCount) { if (texels) { return nullptr; } return this->createProxy(desc, SkBackingFit::kExact, budgeted); } if (!texels) { return nullptr; } if (1 == mipLevelCount) { return this->createTextureProxy(desc, budgeted, texels[0].fPixels, texels[0].fRowBytes); } #ifdef SK_DEBUG // There are only three states we want to be in when uploading data to a mipped surface. // 1) We have data to upload to all layers // 2) We are not uploading data to any layers // 3) We are only uploading data to the base layer // We check here to make sure we do not have any other state. bool firstLevelHasData = SkToBool(texels[0].fPixels); bool allOtherLevelsHaveData = true, allOtherLevelsLackData = true; for (int i = 1; i < mipLevelCount; ++i) { if (texels[i].fPixels) { allOtherLevelsLackData = false; } else { allOtherLevelsHaveData = false; } } SkASSERT((firstLevelHasData && allOtherLevelsHaveData) || allOtherLevelsLackData); #endif sk_sp tex(fResourceProvider->createTexture(desc, budgeted, texels, mipLevelCount, mipColorMode)); if (!tex) { return nullptr; } return this->createWrapped(std::move(tex), desc.fOrigin); } sk_sp GrProxyProvider::createMipMapProxy(const GrSurfaceDesc& desc, SkBudgeted budgeted) { // SkMipMap doesn't include the base level in the level count so we have to add 1 int mipCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1; std::unique_ptr texels(new GrMipLevel[mipCount]); // We don't want to upload any texel data for (int i = 0; i < mipCount; i++) { texels[i].fPixels = nullptr; texels[i].fRowBytes = 0; } return this->createMipMapProxy(desc, budgeted, texels.get(), mipCount, SkDestinationSurfaceColorMode::kLegacy); } sk_sp GrProxyProvider::createProxy(const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted, uint32_t flags) { SkASSERT(0 == flags || GrResourceProvider::kNoPendingIO_Flag == flags); if (!this->caps()->validateSurfaceDesc(desc, GrMipMapped::kNo)) { return nullptr; } GrSurfaceDesc copyDesc = desc; if (desc.fFlags & kRenderTarget_GrSurfaceFlag) { copyDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(desc.fSampleCnt, desc.fConfig); } if (copyDesc.fFlags & kRenderTarget_GrSurfaceFlag) { // We know anything we instantiate later from this deferred path will be // both texturable and renderable return sk_sp( new GrTextureRenderTargetProxy(*this->caps(), copyDesc, fit, budgeted, flags)); } return sk_sp(new GrTextureProxy(copyDesc, fit, budgeted, nullptr, 0, flags)); } sk_sp GrProxyProvider::createWrappedTextureProxy( const GrBackendTexture& backendTex, GrSurfaceOrigin origin, GrWrapOwnership ownership, ReleaseProc releaseProc, ReleaseContext releaseCtx) { if (this->isAbandoned()) { return nullptr; } GrSurfaceDesc desc; desc.fOrigin = origin; desc.fWidth = backendTex.width(); desc.fHeight = backendTex.height(); desc.fConfig = backendTex.config(); GrMipMapped mipMapped = backendTex.hasMipMaps() ? GrMipMapped::kYes : GrMipMapped::kNo; sk_sp releaseHelper; if (releaseProc) { releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx)); } sk_sp proxy = this->createLazyProxy( [backendTex, ownership, releaseHelper] (GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) { if (!resourceProvider) { // If this had a releaseHelper it will get unrefed when we delete this lambda // and will call the release proc so that the client knows they can free the // underlying backend object. return sk_sp(); } sk_sp tex = resourceProvider->wrapBackendTexture(backendTex, ownership); if (!tex) { return sk_sp(); } if (releaseHelper) { // This gives the texture a ref on the releaseHelper tex->setRelease(releaseHelper); } SkASSERT(!tex->asRenderTarget()); // Strictly a GrTexture // Make sure we match how we created the proxy with SkBudgeted::kNo SkASSERT(SkBudgeted::kNo == tex->resourcePriv().isBudgeted()); return tex; }, desc, mipMapped, SkBackingFit::kExact, SkBudgeted::kNo); if (fResourceProvider) { // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however, // we're better off instantiating the proxy immediately here. if (!proxy->priv().doLazyInstantiation(fResourceProvider)) { return nullptr; } } return proxy; } sk_sp GrProxyProvider::createWrappedTextureProxy(const GrBackendTexture& tex, GrSurfaceOrigin origin, int sampleCnt) { if (this->isAbandoned()) { return nullptr; } sk_sp texture(fResourceProvider->wrapRenderableBackendTexture(tex, sampleCnt)); if (!texture) { return nullptr; } SkASSERT(texture->asRenderTarget()); // A GrTextureRenderTarget return this->createWrapped(std::move(texture), origin); } sk_sp GrProxyProvider::createWrappedRenderTargetProxy( const GrBackendRenderTarget& backendRT, GrSurfaceOrigin origin) { if (this->isAbandoned()) { return nullptr; } sk_sp rt(fResourceProvider->wrapBackendRenderTarget(backendRT)); if (!rt) { return nullptr; } SkASSERT(!rt->asTexture()); // Strictly a GrRenderTarget SkASSERT(!rt->getUniqueKey().isValid()); return sk_sp(new GrRenderTargetProxy(std::move(rt), origin)); } sk_sp GrProxyProvider::createWrappedRenderTargetProxy(const GrBackendTexture& tex, GrSurfaceOrigin origin, int sampleCnt) { if (this->isAbandoned()) { return nullptr; } sk_sp rt(fResourceProvider->wrapBackendTextureAsRenderTarget(tex, sampleCnt)); if (!rt) { return nullptr; } SkASSERT(!rt->asTexture()); // Strictly a GrRenderTarget SkASSERT(!rt->getUniqueKey().isValid()); return sk_sp(new GrRenderTargetProxy(std::move(rt), origin)); } sk_sp GrProxyProvider::createLazyProxy(LazyInstantiateCallback&& callback, const GrSurfaceDesc& desc, GrMipMapped mipMapped, SkBackingFit fit, SkBudgeted budgeted) { SkASSERT((desc.fWidth <= 0 && desc.fHeight <= 0) || (desc.fWidth > 0 && desc.fHeight > 0)); uint32_t flags = GrResourceProvider::kNoPendingIO_Flag; return sk_sp(SkToBool(kRenderTarget_GrSurfaceFlag & desc.fFlags) ? new GrTextureRenderTargetProxy(std::move(callback), desc, mipMapped, fit, budgeted, flags) : new GrTextureProxy(std::move(callback), desc, mipMapped, fit, budgeted, flags)); } sk_sp GrProxyProvider::createLazyRenderTargetProxy( LazyInstantiateCallback&& callback, const GrSurfaceDesc& desc, Textureable textureable, GrMipMapped mipMapped, SkBackingFit fit, SkBudgeted budgeted) { SkASSERT((desc.fWidth <= 0 && desc.fHeight <= 0) || (desc.fWidth > 0 && desc.fHeight > 0)); uint32_t flags = GrResourceProvider::kNoPendingIO_Flag; if (Textureable::kYes == textureable) { return sk_sp(new GrTextureRenderTargetProxy(std::move(callback), desc, mipMapped, fit, budgeted, flags)); } return sk_sp(new GrRenderTargetProxy(std::move(callback), desc, fit, budgeted, flags)); } sk_sp GrProxyProvider::createFullyLazyProxy(LazyInstantiateCallback&& callback, Renderable renderable, GrPixelConfig config) { GrSurfaceDesc desc; if (Renderable::kYes == renderable) { desc.fFlags = kRenderTarget_GrSurfaceFlag; } desc.fOrigin = kTopLeft_GrSurfaceOrigin; desc.fWidth = -1; desc.fHeight = -1; desc.fConfig = config; desc.fSampleCnt = 1; return this->createLazyProxy(std::move(callback), desc, GrMipMapped::kNo, SkBackingFit::kApprox, SkBudgeted::kYes); } bool GrProxyProvider::IsFunctionallyExact(GrSurfaceProxy* proxy) { return proxy->priv().isExact() || (SkIsPow2(proxy->width()) && SkIsPow2(proxy->height())); } void GrProxyProvider::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) { this->processInvalidProxyUniqueKey(key, proxy, false); } } void GrProxyProvider::processInvalidProxyUniqueKey(const GrUniqueKey& key, GrTextureProxy* proxy, bool invalidateSurface) { SkASSERT(proxy); SkASSERT(proxy->getUniqueKey().isValid()); SkASSERT(proxy->getUniqueKey() == key); fUniquelyKeyedProxies.remove(key); proxy->cacheAccess().clearUniqueKey(); if (invalidateSurface && proxy->priv().isInstantiated()) { GrSurface* surface = proxy->priv().peekSurface(); if (surface) { surface->resourcePriv().removeUniqueKey(); } } } void GrProxyProvider::removeAllUniqueKeys() { UniquelyKeyedProxyHash::Iter iter(&fUniquelyKeyedProxies); for (UniquelyKeyedProxyHash::Iter iter(&fUniquelyKeyedProxies); !iter.done(); ++iter) { GrTextureProxy& tmp = *iter; this->processInvalidProxyUniqueKey(tmp.getUniqueKey(), &tmp, false); } SkASSERT(!fUniquelyKeyedProxies.count()); }