diff options
author | 2017-11-29 22:01:06 -0700 | |
---|---|---|
committer | 2017-11-30 15:37:12 +0000 | |
commit | 706a6ff60c55bee85cff06fc9f8f3764f6e5154b (patch) | |
tree | ce416111dc4101cf58ee12bd7c35007317f33819 | |
parent | de2f1dfebd9d28787cdfd77496f365ed3eb6894d (diff) |
Add "lazy" texture proxies
Adds ultra-deferred proxies that are instantiated by a user-supplied
callback during flush.
Bug: skia:7190
Change-Id: I75a7ac6dba953c3b0a99febc203a7f4d2f3789fc
Reviewed-on: https://skia-review.googlesource.com/76461
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
-rw-r--r-- | gn/tests.gni | 1 | ||||
-rw-r--r-- | include/private/GrRenderTargetProxy.h | 4 | ||||
-rw-r--r-- | include/private/GrSurfaceProxy.h | 51 | ||||
-rw-r--r-- | include/private/GrTextureProxy.h | 6 | ||||
-rw-r--r-- | include/private/GrTypesPriv.h | 2 | ||||
-rw-r--r-- | src/gpu/GrDrawingManager.cpp | 12 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContextPriv.h | 1 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetOpList.cpp | 6 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetOpList.h | 1 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetProxy.cpp | 20 | ||||
-rw-r--r-- | src/gpu/GrResourceAllocator.cpp | 12 | ||||
-rw-r--r-- | src/gpu/GrSurfaceProxy.cpp | 62 | ||||
-rw-r--r-- | src/gpu/GrSurfaceProxyPriv.h | 2 | ||||
-rw-r--r-- | src/gpu/GrTextureProxy.cpp | 22 | ||||
-rw-r--r-- | src/gpu/GrTextureRenderTargetProxy.cpp | 40 | ||||
-rw-r--r-- | src/gpu/GrTextureRenderTargetProxy.h | 5 | ||||
-rw-r--r-- | tests/LazyProxyTest.cpp | 189 | ||||
-rw-r--r-- | tools/gpu/GrTest.cpp | 8 |
18 files changed, 412 insertions, 32 deletions
diff --git a/gn/tests.gni b/gn/tests.gni index 418ce01d95..23e4f831b9 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -124,6 +124,7 @@ tests_sources = [ "$_tests/IsClosedSingleContourTest.cpp", "$_tests/LayerDrawLooperTest.cpp", "$_tests/LayerRasterizerTest.cpp", + "$_tests/LazyProxyTest.cpp", "$_tests/LListTest.cpp", "$_tests/LRUCacheTest.cpp", "$_tests/MallocPixelRefTest.cpp", diff --git a/include/private/GrRenderTargetProxy.h b/include/private/GrRenderTargetProxy.h index e52588b1f1..a5f876bdf1 100644 --- a/include/private/GrRenderTargetProxy.h +++ b/include/private/GrRenderTargetProxy.h @@ -67,6 +67,9 @@ protected: GrRenderTargetProxy(const GrCaps&, const GrSurfaceDesc&, SkBackingFit, SkBudgeted, uint32_t flags); + // Lazy-callback version + GrRenderTargetProxy(LazyInstantiateCallback&&, GrPixelConfig); + // Wrapped version GrRenderTargetProxy(sk_sp<GrSurface>, GrSurfaceOrigin); @@ -74,6 +77,7 @@ protected: private: size_t onUninstantiatedGpuMemorySize() const override; + SkDEBUGCODE(void validateLazyTexture(const GrTexture*) override { SkASSERT(0); }) int fSampleCnt; bool fNeedsStencil; diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h index 23f62195c2..387aecd428 100644 --- a/include/private/GrSurfaceProxy.h +++ b/include/private/GrSurfaceProxy.h @@ -212,15 +212,34 @@ public: static sk_sp<GrTextureProxy> MakeWrappedBackend(GrContext*, GrBackendTexture&, GrSurfaceOrigin); + using LazyInstantiateCallback = std::function<sk_sp<GrTexture>(GrResourceProvider*, + GrSurfaceOrigin* outOrigin)>; + + enum class Renderable : bool { + kNo = false, + kYes = true + }; + + /** + * Creates a texture proxy that will be instantiated by a user-supplied callback during flush. + * (Mipmapping, MSAA, and stencil are not supported by this method.) + */ + static sk_sp<GrTextureProxy> MakeLazy(LazyInstantiateCallback&&, Renderable, GrPixelConfig); + + GrPixelConfig config() const { return fConfig; } + int width() const { SkASSERT(!this->isPendingLazyInstantiation()); return fWidth; } + int height() const { SkASSERT(!this->isPendingLazyInstantiation()); return fHeight; } + int worstCaseWidth() const; + int worstCaseHeight() const; GrSurfaceOrigin origin() const { + SkASSERT(!this->isPendingLazyInstantiation()); SkASSERT(kTopLeft_GrSurfaceOrigin == fOrigin || kBottomLeft_GrSurfaceOrigin == fOrigin); return fOrigin; } - int width() const { return fWidth; } - int height() const { return fHeight; } - int worstCaseWidth() const; - int worstCaseHeight() const; - GrPixelConfig config() const { return fConfig; } + + // If the client gave us a LazyInstantiateCallback (via MakeLazy), then we will invoke that + // callback during flush. fWidth, fHeight, and fOrigin will be undefined until that time. + bool isPendingLazyInstantiation() const { return SkToBool(fLazyInstantiateCallback); } class UniqueID { public: @@ -230,7 +249,7 @@ public: // wrapped explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { } - // deferred + // deferred and lazy-callback UniqueID() : fID(GrGpuResource::CreateUniqueID()) { } uint32_t asUInt() const { return fID; } @@ -281,7 +300,10 @@ public: /** * Helper that gets the width and height of the surface as a bounding rectangle. */ - SkRect getBoundsRect() const { return SkRect::MakeIWH(this->width(), this->height()); } + SkRect getBoundsRect() const { + SkASSERT(!this->isPendingLazyInstantiation()); + return SkRect::MakeIWH(this->width(), this->height()); + } /** * @return the texture proxy associated with the surface proxy, may be NULL. @@ -314,6 +336,7 @@ public: * @return the amount of GPU memory used in bytes */ size_t gpuMemorySize() const { + SkASSERT(!this->isPendingLazyInstantiation()); if (fTarget) { return fTarget->gpuMemorySize(); } @@ -363,6 +386,9 @@ protected: // Note: this ctor pulls a new uniqueID from the same pool at the GrGpuResources } + // Lazy-callback version + GrSurfaceProxy(LazyInstantiateCallback&& callback, GrPixelConfig config); + // Wrapped version GrSurfaceProxy(sk_sp<GrSurface> surface, GrSurfaceOrigin origin, SkBackingFit fit); @@ -392,23 +418,28 @@ protected: GrSurfaceFlags flags, GrMipMapped mipMapped, SkDestinationSurfaceColorMode mipColorMode, const GrUniqueKey*); +private: // For wrapped resources, 'fConfig', 'fWidth', 'fHeight', and 'fOrigin; will always be filled in // from the wrapped resource. GrPixelConfig fConfig; int fWidth; int fHeight; GrSurfaceOrigin fOrigin; - SkBackingFit fFit; // always exact for wrapped resources - mutable SkBudgeted fBudgeted; // set from the backing resource for wrapped resources + SkBackingFit fFit; // always kApprox for lazy-callback resources + // always kExact for wrapped resources + mutable SkBudgeted fBudgeted; // always kYes for lazy-callback resources + // set from the backing resource for wrapped resources // mutable bc of SkSurface/SkImage wishy-washiness const uint32_t fFlags; const UniqueID fUniqueID; // set from the backing resource for wrapped resources + LazyInstantiateCallback fLazyInstantiateCallback; + SkDEBUGCODE(virtual void validateLazyTexture(const GrTexture*) = 0;) + static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0); SkDEBUGCODE(size_t getRawGpuMemorySize_debugOnly() const { return fGpuMemorySize; }) -private: virtual size_t onUninstantiatedGpuMemorySize() const = 0; bool fNeedsClear; diff --git a/include/private/GrTextureProxy.h b/include/private/GrTextureProxy.h index cc1148a3f4..a2f11d5b87 100644 --- a/include/private/GrTextureProxy.h +++ b/include/private/GrTextureProxy.h @@ -67,6 +67,10 @@ protected: // Deferred version GrTextureProxy(const GrSurfaceDesc& srcDesc, SkBackingFit, SkBudgeted, const void* srcData, size_t srcRowBytes, uint32_t flags); + + // Lazy-callback version + GrTextureProxy(LazyInstantiateCallback&&, GrPixelConfig); + // Wrapped version GrTextureProxy(sk_sp<GrSurface>, GrSurfaceOrigin); @@ -94,6 +98,8 @@ private: void setUniqueKey(GrResourceCache*, const GrUniqueKey&); void clearUniqueKey(); + SkDEBUGCODE(void validateLazyTexture(const GrTexture*) override;) + // 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/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h index 4689e41140..4c93518c73 100644 --- a/include/private/GrTypesPriv.h +++ b/include/private/GrTypesPriv.h @@ -24,6 +24,8 @@ using GrStdSteadyClock = std::chrono::monotonic_clock; using GrStdSteadyClock = std::chrono::steady_clock; #endif +static constexpr GrSurfaceOrigin kGrUnknownSurfaceOrigin = static_cast<GrSurfaceOrigin>(-1); + /** This enum is used to specify the load operation to be used when an * opList/GrGpuCommandBuffer begins execution. */ diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp index d95a2c5c70..bf8d1abbcf 100644 --- a/src/gpu/GrDrawingManager.cpp +++ b/src/gpu/GrDrawingManager.cpp @@ -21,6 +21,8 @@ #include "GrSurfaceProxyPriv.h" #include "GrTextureContext.h" #include "GrTextureOpList.h" +#include "GrTextureProxy.h" +#include "GrTextureProxyPriv.h" #include "SkSurface_Gpu.h" #include "SkTTopoSort.h" @@ -150,10 +152,18 @@ GrSemaphoresSubmitted GrDrawingManager::internalFlush(GrSurfaceProxy*, fFlushingOpListIDs.begin(), fFlushingOpListIDs.count(), &renderTargetContexts); for (const sk_sp<GrRenderTargetContext>& rtc : renderTargetContexts) { - sk_sp<GrOpList> onFlushOpList = sk_ref_sp(rtc->getOpList()); + sk_sp<GrRenderTargetOpList> onFlushOpList = sk_ref_sp(rtc->getRTOpList()); if (!onFlushOpList) { continue; // Odd - but not a big deal } +#ifdef SK_DEBUG + // OnFlush callbacks are already invoked during flush, and are therefore expected to + // handle resource allocation & usage on their own. (No deferred or lazy proxies!) + onFlushOpList->visitProxies_debugOnly([](GrSurfaceProxy* p) { + SkASSERT(!p->asTextureProxy() || !p->asTextureProxy()->texPriv().isDeferred()); + SkASSERT(!p->isPendingLazyInstantiation()); + }); +#endif onFlushOpList->makeClosed(*fContext->caps()); onFlushOpList->prepare(&fFlushState); fOnFlushCBOpLists.push_back(std::move(onFlushOpList)); diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h index 0d73793c9d..271badb64c 100644 --- a/src/gpu/GrRenderTargetContextPriv.h +++ b/src/gpu/GrRenderTargetContextPriv.h @@ -109,6 +109,7 @@ public: } uint32_t testingOnly_addDrawOp(std::unique_ptr<GrDrawOp>); + uint32_t testingOnly_addDrawOp(const GrClip&, std::unique_ptr<GrDrawOp>); bool refsWrappedObjects() const { return fRenderTargetContext->fRenderTargetProxy->refsWrappedObjects(); diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp index 885723c0d6..ff85a41afe 100644 --- a/src/gpu/GrRenderTargetOpList.cpp +++ b/src/gpu/GrRenderTargetOpList.cpp @@ -60,6 +60,12 @@ void GrRenderTargetOpList::dump() const { } } } + +void GrRenderTargetOpList::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const { + for (const RecordedOp& recordedOp : fRecordedOps) { + recordedOp.visitProxies(func); + } +} #endif void GrRenderTargetOpList::onPrepare(GrOpFlushState* flushState) { diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h index 7e600d41d1..93bab91051 100644 --- a/src/gpu/GrRenderTargetOpList.h +++ b/src/gpu/GrRenderTargetOpList.h @@ -125,6 +125,7 @@ public: SkDEBUGCODE(int numOps() const override { return fRecordedOps.count(); }) SkDEBUGCODE(int numClips() const override { return fNumClips; }) + SkDEBUGCODE(void visitProxies_debugOnly(const GrOp::VisitProxyFunc&) const;) private: friend class GrRenderTargetContextPriv; // for stencil clip state. TODO: this is invasive diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp index 9453ce8204..dea47e6b5c 100644 --- a/src/gpu/GrRenderTargetProxy.cpp +++ b/src/gpu/GrRenderTargetProxy.cpp @@ -34,12 +34,20 @@ GrRenderTargetProxy::GrRenderTargetProxy(const GrCaps& caps, const GrSurfaceDesc } } +// Lazy-callback version +GrRenderTargetProxy::GrRenderTargetProxy(LazyInstantiateCallback&& callback, GrPixelConfig config) + : INHERITED(std::move(callback), config) + , fSampleCnt(0) + , fNeedsStencil(false) + , fRenderTargetFlags(GrRenderTargetFlags::kNone) { +} + // Wrapped version GrRenderTargetProxy::GrRenderTargetProxy(sk_sp<GrSurface> surf, GrSurfaceOrigin origin) - : INHERITED(std::move(surf), origin, SkBackingFit::kExact) - , fSampleCnt(fTarget->asRenderTarget()->numStencilSamples()) - , fNeedsStencil(false) - , fRenderTargetFlags(fTarget->asRenderTarget()->renderTargetPriv().flags()) { + : INHERITED(std::move(surf), origin, SkBackingFit::kExact) + , fSampleCnt(fTarget->asRenderTarget()->numStencilSamples()) + , fNeedsStencil(false) + , fRenderTargetFlags(fTarget->asRenderTarget()->renderTargetPriv().flags()) { } int GrRenderTargetProxy::maxWindowRectangles(const GrCaps& caps) const { @@ -85,8 +93,8 @@ size_t GrRenderTargetProxy::onUninstantiatedGpuMemorySize() const { int colorSamplesPerPixel = this->numColorSamples() + 1; // TODO: do we have enough information to improve this worst case estimate? - return GrSurface::ComputeSize(fConfig, fWidth, fHeight, colorSamplesPerPixel, GrMipMapped::kNo, - SkBackingFit::kApprox == fFit); + return GrSurface::ComputeSize(this->config(), this->width(), this->height(), + colorSamplesPerPixel, GrMipMapped::kNo, !this->priv().isExact()); } bool GrRenderTargetProxy::refsWrappedObjects() const { diff --git a/src/gpu/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp index 0afc0ed8c9..bf46c61e8e 100644 --- a/src/gpu/GrResourceAllocator.cpp +++ b/src/gpu/GrResourceAllocator.cpp @@ -73,6 +73,13 @@ void GrResourceAllocator::addInterval(GrSurfaceProxy* proxy, unsigned int start, fIntvlList.insertByIncreasingStart(newIntvl); fIntvlHash.add(newIntvl); + +#ifdef SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION + // FIXME: remove this once we can do the lazy instantiation from assign instead. + if (proxy->isPendingLazyInstantiation()) { + proxy->priv().doLazyInstantiation(fResourceProvider); + } +#endif } GrResourceAllocator::Interval* GrResourceAllocator::IntervalList::popHead() { @@ -224,8 +231,9 @@ bool GrResourceAllocator::assign(int* startIndex, int* stopIndex) { continue; } - sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy(), needsStencil); - if (surface) { + if (cur->proxy()->isPendingLazyInstantiation()) { + cur->proxy()->priv().doLazyInstantiation(fResourceProvider); + } else if (sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy(), needsStencil)) { // TODO: make getUniqueKey virtual on GrSurfaceProxy GrTextureProxy* tex = cur->proxy()->asTextureProxy(); if (tex && tex->getUniqueKey().isValid()) { diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp index 71ff6374ff..a5c2be622b 100644 --- a/src/gpu/GrSurfaceProxy.cpp +++ b/src/gpu/GrSurfaceProxy.cpp @@ -21,6 +21,23 @@ #include "SkMathPriv.h" #include "SkMipMap.h" +// Lazy-callback version +GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback, GrPixelConfig config) + : fConfig(config) + , fWidth(-1) // Width, height, and origin will be initialized upon lazy instantiation. + , fHeight(-1) + , fOrigin(kGrUnknownSurfaceOrigin) + , fFit(SkBackingFit::kApprox) + , fBudgeted(SkBudgeted::kYes) + , fFlags(GrResourceProvider::kNoPendingIO_Flag) + , fLazyInstantiateCallback(std::move(callback)) + , fNeedsClear(false) + , fGpuMemorySize(kInvalidGpuMemorySize) + , fLastOpList(nullptr) { + // NOTE: the default fUniqueID ctor pulls a value from the same pool as the GrGpuResources. +} + +// Wrapped version GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, GrSurfaceOrigin origin, SkBackingFit fit) : INHERITED(std::move(surface)) , fConfig(fTarget->config()) @@ -64,6 +81,7 @@ sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl( int sampleCnt, bool needsStencil, GrSurfaceFlags flags, GrMipMapped mipMapped, SkDestinationSurfaceColorMode mipColorMode) const { + SkASSERT(!this->isPendingLazyInstantiation()); SkASSERT(GrMipMapped::kNo == mipMapped); GrSurfaceDesc desc; desc.fFlags = flags; @@ -96,6 +114,7 @@ sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl( } void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) { + SkASSERT(!this->isPendingLazyInstantiation()); SkASSERT(!fTarget && surface); fTarget = surface.release(); this->INHERITED::transferRefs(); @@ -111,6 +130,7 @@ bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int s bool needsStencil, GrSurfaceFlags flags, GrMipMapped mipMapped, SkDestinationSurfaceColorMode mipColorMode, const GrUniqueKey* uniqueKey) { + SkASSERT(!this->isPendingLazyInstantiation()); if (fTarget) { if (uniqueKey) { SkASSERT(fTarget->getUniqueKey() == *uniqueKey); @@ -135,6 +155,7 @@ bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int s } void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const { + SkASSERT(!this->isPendingLazyInstantiation()); const GrRenderTargetProxy* rtp = this->asRenderTargetProxy(); int sampleCount = 0; if (rtp) { @@ -377,7 +398,15 @@ sk_sp<GrTextureProxy> GrSurfaceProxy::MakeWrappedBackend(GrContext* context, return GrSurfaceProxy::MakeWrapped(std::move(tex), origin); } +sk_sp<GrTextureProxy> GrSurfaceProxy::MakeLazy(LazyInstantiateCallback&& callback, + Renderable renderable, GrPixelConfig config) { + return sk_sp<GrTextureProxy>(Renderable::kYes == renderable ? + new GrTextureRenderTargetProxy(std::move(callback), config) : + new GrTextureProxy(std::move(callback), config)); +} + int GrSurfaceProxy::worstCaseWidth() const { + SkASSERT(!this->isPendingLazyInstantiation()); if (fTarget) { return fTarget->width(); } @@ -389,6 +418,7 @@ int GrSurfaceProxy::worstCaseWidth() const { } int GrSurfaceProxy::worstCaseHeight() const { + SkASSERT(!this->isPendingLazyInstantiation()); if (fTarget) { return fTarget->height(); } @@ -414,6 +444,7 @@ sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context, GrMipMapped mipMapped, SkIRect srcRect, SkBudgeted budgeted) { + SkASSERT(!src->isPendingLazyInstantiation()); if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) { return nullptr; } @@ -442,12 +473,13 @@ sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context, sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context, GrSurfaceProxy* src, GrMipMapped mipMapped, SkBudgeted budgeted) { + SkASSERT(!src->isPendingLazyInstantiation()); return Copy(context, src, mipMapped, SkIRect::MakeWH(src->width(), src->height()), budgeted); } sk_sp<GrSurfaceContext> GrSurfaceProxy::TestCopy(GrContext* context, const GrSurfaceDesc& dstDesc, GrSurfaceProxy* srcProxy) { - + SkASSERT(!srcProxy->isPendingLazyInstantiation()); sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext( dstDesc, GrMipMapped::kNo, @@ -465,6 +497,7 @@ sk_sp<GrSurfaceContext> GrSurfaceProxy::TestCopy(GrContext* context, const GrSur } void GrSurfaceProxyPriv::exactify() { + SkASSERT(!fProxy->isPendingLazyInstantiation()); if (this->isExact()) { return; } @@ -491,3 +524,30 @@ void GrSurfaceProxyPriv::exactify() { // exact amount. } +void GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) { + SkASSERT(fProxy->fLazyInstantiateCallback); + SkASSERT(!fProxy->fTarget); + + sk_sp<GrTexture> texture = fProxy->fLazyInstantiateCallback(resourceProvider, &fProxy->fOrigin); + + // Indicate we are no longer pending lazy instantiation. + fProxy->fLazyInstantiateCallback = nullptr; + + if (!texture) { + fProxy->fWidth = 0; + fProxy->fHeight = 0; + fProxy->fOrigin = kTopLeft_GrSurfaceOrigin; + return; + } + + fProxy->fWidth = texture->width(); + fProxy->fHeight = texture->height(); + + SkASSERT(texture->config() == fProxy->fConfig); + SkASSERT(kGrUnknownSurfaceOrigin != fProxy->origin()); + SkASSERT(kTopLeft_GrSurfaceOrigin == fProxy->fOrigin || + kBottomLeft_GrSurfaceOrigin == fProxy->fOrigin); + SkDEBUGCODE(fProxy->validateLazyTexture(texture.get());) + this->assign(std::move(texture)); +} + diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h index a93a20c051..b4811b68c8 100644 --- a/src/gpu/GrSurfaceProxyPriv.h +++ b/src/gpu/GrSurfaceProxyPriv.h @@ -68,6 +68,8 @@ public: // Don't. Just don't. void exactify(); + void doLazyInstantiation(GrResourceProvider*); + static bool AttachStencilIfNeeded(GrResourceProvider*, GrSurface*, bool needsStencil); private: diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp index 9afa63f374..9b3dc9cb53 100644 --- a/src/gpu/GrTextureProxy.cpp +++ b/src/gpu/GrTextureProxy.cpp @@ -13,6 +13,7 @@ #include "GrResourceCache.h" #include "GrTexturePriv.h" +// Deferred version GrTextureProxy::GrTextureProxy(const GrSurfaceDesc& srcDesc, SkBackingFit fit, SkBudgeted budgeted, const void* srcData, size_t /*rowBytes*/, uint32_t flags) : INHERITED(srcDesc, fit, budgeted, flags) @@ -23,6 +24,16 @@ GrTextureProxy::GrTextureProxy(const GrSurfaceDesc& srcDesc, SkBackingFit fit, S SkASSERT(!srcData); // currently handled in Make() } +// Lazy-callback version +GrTextureProxy::GrTextureProxy(LazyInstantiateCallback&& callback, GrPixelConfig config) + : INHERITED(std::move(callback), config) + , fMipMapped(GrMipMapped::kNo) + , fMipColorMode(SkDestinationSurfaceColorMode::kLegacy) + , fCache(nullptr) + , fDeferredUploader(nullptr) { +} + +// Wrapped version GrTextureProxy::GrTextureProxy(sk_sp<GrSurface> surf, GrSurfaceOrigin origin) : INHERITED(std::move(surf), origin, SkBackingFit::kExact) , fMipMapped(fTarget->asTexture()->texturePriv().mipMapped()) @@ -109,8 +120,8 @@ GrSamplerState::Filter GrTextureProxy::highestFilterMode() const { } size_t GrTextureProxy::onUninstantiatedGpuMemorySize() const { - return GrSurface::ComputeSize(fConfig, fWidth, fHeight, 1, this->mipMapped(), - SkBackingFit::kApprox == fFit); + return GrSurface::ComputeSize(this->config(), this->width(), this->height(), 1, + this->mipMapped(), !this->priv().isExact()); } void GrTextureProxy::setUniqueKey(GrResourceCache* cache, const GrUniqueKey& key) { @@ -131,3 +142,10 @@ void GrTextureProxy::clearUniqueKey() { fCache = nullptr; } +#ifdef SK_DEBUG +void GrTextureProxy::validateLazyTexture(const GrTexture* texture) { + SkASSERT(!texture->asRenderTarget()); + SkASSERT(GrMipMapped::kNo == this->mipMapped()); +} +#endif + diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp index dd79bfefff..943523116d 100644 --- a/src/gpu/GrTextureRenderTargetProxy.cpp +++ b/src/gpu/GrTextureRenderTargetProxy.cpp @@ -7,6 +7,10 @@ #include "GrTextureRenderTargetProxy.h" +#include "GrTexture.h" +#include "GrRenderTarget.h" +#include "GrSurfaceProxyPriv.h" + // Deferred version // This class is virtually derived from GrSurfaceProxy (via both GrTextureProxy and // GrRenderTargetProxy) so its constructor must be explicitly called. @@ -15,10 +19,20 @@ GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(const GrCaps& caps, SkBackingFit fit, SkBudgeted budgeted, uint32_t flags) - : GrSurfaceProxy(desc, fit, budgeted, flags) - // for now textures w/ data are always wrapped - , GrTextureProxy(desc, fit, budgeted, nullptr, 0, flags) - , GrRenderTargetProxy(caps, desc, fit, budgeted, flags) { + : GrSurfaceProxy(desc, fit, budgeted, flags) + // for now textures w/ data are always wrapped + , GrTextureProxy(desc, fit, budgeted, nullptr, 0, flags) + , GrRenderTargetProxy(caps, desc, fit, budgeted, flags) { +} + +// Lazy-callback version +GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(LazyInstantiateCallback&& callback, + GrPixelConfig config) + : GrSurfaceProxy(std::move(callback), config) + // Since we have virtual inheritance, we initialize GrSurfaceProxy directly. Send null + // callbacks to the texture and RT proxies simply to route to the appropriate constructors. + , GrTextureProxy(LazyInstantiateCallback(), config) + , GrRenderTargetProxy(LazyInstantiateCallback(), config) { } // Wrapped version @@ -26,9 +40,9 @@ GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(const GrCaps& caps, // GrRenderTargetProxy) so its constructor must be explicitly called. GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(sk_sp<GrSurface> surf, GrSurfaceOrigin origin) - : GrSurfaceProxy(surf, origin, SkBackingFit::kExact) - , GrTextureProxy(surf, origin) - , GrRenderTargetProxy(surf, origin) { + : GrSurfaceProxy(surf, origin, SkBackingFit::kExact) + , GrTextureProxy(surf, origin) + , GrRenderTargetProxy(surf, origin) { SkASSERT(surf->asTexture()); SkASSERT(surf->asRenderTarget()); } @@ -37,8 +51,8 @@ size_t GrTextureRenderTargetProxy::onUninstantiatedGpuMemorySize() const { int colorSamplesPerPixel = this->numColorSamples() + 1; // TODO: do we have enough information to improve this worst case estimate? - return GrSurface::ComputeSize(fConfig, fWidth, fHeight, colorSamplesPerPixel, this->mipMapped(), - SkBackingFit::kApprox == fFit); + return GrSurface::ComputeSize(this->config(), this->width(), this->height(), + colorSamplesPerPixel, this->mipMapped(), !this->priv().isExact()); } bool GrTextureRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) { @@ -77,3 +91,11 @@ sk_sp<GrSurface> GrTextureRenderTargetProxy::createSurface( return surface; } +#ifdef SK_DEBUG +void GrTextureRenderTargetProxy::validateLazyTexture(const GrTexture* texture) { + SkASSERT(texture->asRenderTarget()); + SkASSERT(texture->asRenderTarget()->numStencilSamples() == this->numStencilSamples()); + SkASSERT(GrMipMapped::kNo == this->mipMapped()); +} +#endif + diff --git a/src/gpu/GrTextureRenderTargetProxy.h b/src/gpu/GrTextureRenderTargetProxy.h index 7005169b22..5719366954 100644 --- a/src/gpu/GrTextureRenderTargetProxy.h +++ b/src/gpu/GrTextureRenderTargetProxy.h @@ -29,6 +29,9 @@ private: GrTextureRenderTargetProxy(const GrCaps&, const GrSurfaceDesc&, SkBackingFit, SkBudgeted, uint32_t flags); + // Lazy-callback version + GrTextureRenderTargetProxy(LazyInstantiateCallback&&, GrPixelConfig); + // Wrapped version GrTextureRenderTargetProxy(sk_sp<GrSurface>, GrSurfaceOrigin); @@ -36,6 +39,8 @@ private: sk_sp<GrSurface> createSurface(GrResourceProvider*) const override; size_t onUninstantiatedGpuMemorySize() const override; + + SkDEBUGCODE(void validateLazyTexture(const GrTexture*) override;) }; #ifdef SK_BUILD_FOR_WIN diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp new file mode 100644 index 0000000000..8a620b6a38 --- /dev/null +++ b/tests/LazyProxyTest.cpp @@ -0,0 +1,189 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrClip.h" +#include "GrContextPriv.h" +#include "GrOnFlushResourceProvider.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetContextPriv.h" +#include "GrSurfaceProxy.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" +#include "GrTextureProxyPriv.h" +#include "SkMakeUnique.h" +#include "mock/GrMockTypes.h" + +// This test verifies that lazy proxy callbacks get invoked during flush, after onFlush callbacks, +// but before Ops are executed. It also ensures that lazy proxy callbacks are invoked both for +// regular Ops and for clips. +class LazyProxyTest final : public GrOnFlushCallbackObject { +public: + LazyProxyTest(skiatest::Reporter* reporter) + : fReporter(reporter) + , fHasOpTexture(false) + , fHasClipTexture(false) { + } + + ~LazyProxyTest() override { + REPORTER_ASSERT(fReporter, fHasOpTexture); + REPORTER_ASSERT(fReporter, fHasClipTexture); + } + + void preFlush(GrOnFlushResourceProvider*, const uint32_t*, int, + SkTArray<sk_sp<GrRenderTargetContext>>*) override { + REPORTER_ASSERT(fReporter, !fHasOpTexture); + REPORTER_ASSERT(fReporter, !fHasClipTexture); + } + + void postFlush(GrDeferredUploadToken, const uint32_t* opListIDs, int numOpListIDs) override { + REPORTER_ASSERT(fReporter, fHasOpTexture); + REPORTER_ASSERT(fReporter, fHasClipTexture); + } + + class Op final : public GrDrawOp { + public: + DEFINE_OP_CLASS_ID + + Op(LazyProxyTest* test, bool nullTexture) : GrDrawOp(ClassID()), fTest(test) { + fProxy = GrSurfaceProxy::MakeLazy([this, nullTexture](GrResourceProvider* rp, + GrSurfaceOrigin* origin) { + REPORTER_ASSERT(fTest->fReporter, !fTest->fHasOpTexture); + fTest->fHasOpTexture = true; + *origin = kTopLeft_GrSurfaceOrigin; + if (nullTexture) { + return sk_sp<GrTexture>(); + } else { + GrSurfaceDesc desc; + desc.fWidth = 1234; + desc.fHeight = 567; + desc.fOrigin = kTopLeft_GrSurfaceOrigin; + desc.fConfig = kRGB_565_GrPixelConfig; + sk_sp<GrTexture> texture = rp->createTexture(desc, SkBudgeted::kYes); + REPORTER_ASSERT(fTest->fReporter, texture); + return texture; + } + }, GrSurfaceProxy::Renderable::kNo, kRGB_565_GrPixelConfig); + this->setBounds(SkRect::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo); + } + + void visitProxies(const VisitProxyFunc& func) const override { + func(fProxy.get()); + } + + void onExecute(GrOpFlushState*) override { + REPORTER_ASSERT(fTest->fReporter, fTest->fHasOpTexture); + REPORTER_ASSERT(fTest->fReporter, fTest->fHasClipTexture); + } + + private: + const char* name() const override { return "LazyProxyTest::Op"; } + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*, + GrPixelConfigIsClamped) override { + return RequiresDstTexture::kNo; + } + void wasRecorded(GrRenderTargetOpList*) override {} + bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } + void onPrepare(GrOpFlushState*) override {} + + LazyProxyTest* const fTest; + sk_sp<GrTextureProxy> fProxy; + }; + + class ClipFP : public GrFragmentProcessor { + public: + ClipFP(LazyProxyTest* test, GrTextureProxy* atlas) + : GrFragmentProcessor(kTestFP_ClassID, kNone_OptimizationFlags) + , fTest(test) + , fAtlas(atlas) { + fLazyProxy = GrSurfaceProxy::MakeLazy([this](GrResourceProvider* rp, + GrSurfaceOrigin* origin) { + REPORTER_ASSERT(fTest->fReporter, !fTest->fHasClipTexture); + fTest->fHasClipTexture = true; + *origin = kBottomLeft_GrSurfaceOrigin; + fAtlas->instantiate(rp); + return sk_ref_sp(fAtlas->priv().peekTexture()); + }, GrSurfaceProxy::Renderable::kYes, kAlpha_half_GrPixelConfig); + fAccess.reset(fLazyProxy, GrSamplerState::Filter::kNearest, + GrSamplerState::WrapMode::kClamp, kFragment_GrShaderFlag); + this->addTextureSampler(&fAccess); + } + + private: + const char* name() const override { return "LazyProxyTest::ClipFP"; } + std::unique_ptr<GrFragmentProcessor> clone() const override { + return skstd::make_unique<ClipFP>(fTest, fAtlas); + } + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return nullptr; } + void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} + bool onIsEqual(const GrFragmentProcessor&) const override { return false; } + + LazyProxyTest* const fTest; + GrTextureProxy* const fAtlas; + sk_sp<GrTextureProxy> fLazyProxy; + TextureSampler fAccess; + }; + + + class Clip : public GrClip { + public: + Clip(LazyProxyTest* test, GrTextureProxy* atlas) + : fTest(test) + , fAtlas(atlas) {} + + private: + bool apply(GrContext*, GrRenderTargetContext*, bool, bool, GrAppliedClip* out, + SkRect* bounds) const override { + out->addCoverageFP(skstd::make_unique<ClipFP>(fTest, fAtlas)); + return true; + } + bool quickContains(const SkRect&) const final { return false; } + bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const final { return false; } + void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final { + rect->set(0, 0, width, height); + if (iior) { + *iior = false; + } + } + + LazyProxyTest* const fTest; + GrTextureProxy* fAtlas; + }; + +private: + skiatest::Reporter* fReporter; + bool fHasOpTexture; + bool fHasClipTexture; +}; + +DEF_GPUTEST(LazyProxyTest, reporter, /* options */) { + GrMockOptions mockOptions; + mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderable[0] = true; + mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true; + sk_sp<GrContext> ctx = GrContext::MakeMock(&mockOptions, GrContextOptions()); + for (bool nullTexture : {false, true}) { + LazyProxyTest test(reporter); + ctx->contextPriv().addOnFlushCallbackObject(&test); + sk_sp<GrRenderTargetContext> rtc = + ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 100, 100, + kRGBA_8888_GrPixelConfig, nullptr); + REPORTER_ASSERT(reporter, rtc); + sk_sp<GrRenderTargetContext> mockAtlas = + ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 10, 10, + kAlpha_half_GrPixelConfig, nullptr); + REPORTER_ASSERT(reporter, mockAtlas); + rtc->priv().testingOnly_addDrawOp(LazyProxyTest::Clip(&test, mockAtlas->asTextureProxy()), + skstd::make_unique<LazyProxyTest::Op>(&test, nullTexture)); + ctx->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&test); + } +} + +#endif diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp index 47f98a6c20..524cd634fe 100644 --- a/tools/gpu/GrTest.cpp +++ b/tools/gpu/GrTest.cpp @@ -274,7 +274,13 @@ int GrResourceCache::countUniqueKeysWithTag(const char* tag) const { #define ASSERT_SINGLE_OWNER \ SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fRenderTargetContext->singleOwner());) + uint32_t GrRenderTargetContextPriv::testingOnly_addDrawOp(std::unique_ptr<GrDrawOp> op) { + return this->testingOnly_addDrawOp(GrNoClip(), std::move(op)); +} + +uint32_t GrRenderTargetContextPriv::testingOnly_addDrawOp(const GrClip& clip, + std::unique_ptr<GrDrawOp> op) { ASSERT_SINGLE_OWNER if (fRenderTargetContext->drawingManager()->wasAbandoned()) { return SK_InvalidUniqueID; @@ -282,7 +288,7 @@ uint32_t GrRenderTargetContextPriv::testingOnly_addDrawOp(std::unique_ptr<GrDraw SkDEBUGCODE(fRenderTargetContext->validate()); GR_AUDIT_TRAIL_AUTO_FRAME(fRenderTargetContext->fAuditTrail, "GrRenderTargetContext::testingOnly_addDrawOp"); - return fRenderTargetContext->addDrawOp(GrNoClip(), std::move(op)); + return fRenderTargetContext->addDrawOp(clip, std::move(op)); } #undef ASSERT_SINGLE_OWNER |