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>
diff --git a/gn/tests.gni b/gn/tests.gni
index 418ce01..23e4f83 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -124,6 +124,7 @@
"$_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 e52588b..a5f876b 100644
--- a/include/private/GrRenderTargetProxy.h
+++ b/include/private/GrRenderTargetProxy.h
@@ -67,6 +67,9 @@
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 @@
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 23f6219..387aecd 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -212,15 +212,34 @@
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 @@
// 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 @@
/**
* 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 @@
* @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 @@
// 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 @@
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 cc1148a..a2f11d5 100644
--- a/include/private/GrTextureProxy.h
+++ b/include/private/GrTextureProxy.h
@@ -67,6 +67,10 @@
// 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 @@
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 4689e41..4c93518 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -24,6 +24,8 @@
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 d95a2c5..bf8d1ab 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 @@
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 0d73793..271badb 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -109,6 +109,7 @@
}
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 885723c..ff85a41 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -60,6 +60,12 @@
}
}
}
+
+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 7e600d4..93bab91 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -125,6 +125,7 @@
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 9453ce8..dea47e6 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -34,12 +34,20 @@
}
}
+// 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 @@
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 0afc0ed..bf46c61 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -73,6 +73,13 @@
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 @@
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 71ff637..a5c2be6 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 @@
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 @@
}
void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
+ SkASSERT(!this->isPendingLazyInstantiation());
SkASSERT(!fTarget && surface);
fTarget = surface.release();
this->INHERITED::transferRefs();
@@ -111,6 +130,7 @@
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 @@
}
void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
+ SkASSERT(!this->isPendingLazyInstantiation());
const GrRenderTargetProxy* rtp = this->asRenderTargetProxy();
int sampleCount = 0;
if (rtp) {
@@ -377,7 +398,15 @@
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::worstCaseHeight() const {
+ SkASSERT(!this->isPendingLazyInstantiation());
if (fTarget) {
return fTarget->height();
}
@@ -414,6 +444,7 @@
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, 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 @@
}
void GrSurfaceProxyPriv::exactify() {
+ SkASSERT(!fProxy->isPendingLazyInstantiation());
if (this->isExact()) {
return;
}
@@ -491,3 +524,30 @@
// 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 a93a20c..b4811b6 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -68,6 +68,8 @@
// 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 9afa63f..9b3dc9c 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 @@
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 @@
}
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 @@
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 dd79bfe..9435231 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 @@
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 @@
// 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 @@
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 @@
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 7005169..5719366 100644
--- a/src/gpu/GrTextureRenderTargetProxy.h
+++ b/src/gpu/GrTextureRenderTargetProxy.h
@@ -29,6 +29,9 @@
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 @@
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 0000000..8a620b6
--- /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 47f98a6..524cd63 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -274,7 +274,13 @@
#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 @@
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