diff options
author | Robert Phillips <robertphillips@google.com> | 2016-11-01 17:28:40 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-11-02 15:32:04 +0000 |
commit | 8bc06d07f57ede17ccabfa38f1d7e31bbf311ab5 (patch) | |
tree | 5b983d773fd391bfc07e6c9429f28ad941fa4bfc | |
parent | 9caeb0bc83ab45859a996efecbcd1215be0f1b1e (diff) |
Back SkSpecialImage_Gpu with a GrTextureProxy
This is split out of https://codereview.chromium.org/2215323003/ (Start using RenderTargetProxy (omnibus))
The addition of the gpuMemorySize methods is for the SkSpecialImage cache.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4227
Change-Id: Ia9b9d42fb2a0caf61bbfa3ebcc84308c56f541fc
Reviewed-on: https://skia-review.googlesource.com/4227
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
-rw-r--r-- | include/private/GrRenderTargetProxy.h | 2 | ||||
-rw-r--r-- | include/private/GrSurfaceProxy.h | 29 | ||||
-rw-r--r-- | include/private/GrTextureProxy.h | 7 | ||||
-rw-r--r-- | src/core/SkSpecialImage.cpp | 132 | ||||
-rw-r--r-- | src/core/SkSpecialImage.h | 14 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetProxy.cpp | 20 | ||||
-rw-r--r-- | src/gpu/GrSurfaceProxy.cpp | 1 | ||||
-rw-r--r-- | src/gpu/GrTextureProxy.cpp | 42 | ||||
-rw-r--r-- | tests/ProxyTest.cpp | 3 | ||||
-rw-r--r-- | tests/SpecialImageTest.cpp | 41 |
10 files changed, 240 insertions, 51 deletions
diff --git a/include/private/GrRenderTargetProxy.h b/include/private/GrRenderTargetProxy.h index 7d36bf642d..27e0492d9e 100644 --- a/include/private/GrRenderTargetProxy.h +++ b/include/private/GrRenderTargetProxy.h @@ -72,6 +72,8 @@ private: // Wrapped version GrRenderTargetProxy(const GrCaps&, sk_sp<GrRenderTarget> rt); + size_t onGpuMemorySize() const override; + // For wrapped render targets the actual GrRenderTarget is stored in the GrIORefProxy class. // For deferred proxies that pointer is filled in when we need to instantiate the // deferred resource. diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h index d57c2f5993..37669fee19 100644 --- a/include/private/GrSurfaceProxy.h +++ b/include/private/GrSurfaceProxy.h @@ -112,6 +112,25 @@ public: void setLastOpList(GrOpList* opList); GrOpList* getLastOpList() { return fLastOpList; } + /** + * Retrieves the amount of GPU memory that will be or currently is used by this resource + * in bytes. It is approximate since we aren't aware of additional padding or copies made + * by the driver. + * + * @return the amount of GPU memory used in bytes + */ + size_t gpuMemorySize() const { + if (fTarget) { + return fTarget->gpuMemorySize(); + } + + if (kInvalidGpuMemorySize == fGpuMemorySize) { + fGpuMemorySize = this->onGpuMemorySize(); + SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize); + } + return fGpuMemorySize; + } + protected: // Deferred version GrSurfaceProxy(const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted) @@ -119,6 +138,7 @@ protected: , fFit(fit) , fBudgeted(budgeted) , fUniqueID(GrGpuResource::CreateUniqueID()) + , fGpuMemorySize(kInvalidGpuMemorySize) , fLastOpList(nullptr) { } @@ -133,7 +153,16 @@ protected: const SkBudgeted fBudgeted; // set from the backing resource for wrapped resources const uint32_t fUniqueID; // set from the backing resource for wrapped resources + static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0); + // This entry is lazily evaluated so, when the proxy wraps a resource, the resource + // will be called but, when the proxy is deferred, it will compute the answer itself. + // If the proxy computes its own answer that answer is checked (in debug mode) in + // the instantiation method. + mutable size_t fGpuMemorySize; + private: + virtual size_t onGpuMemorySize() const = 0; + // The last opList that wrote to or is currently going to write to this surface // The opList can be closed (e.g., no render target context is currently bound // to this renderTarget). diff --git a/include/private/GrTextureProxy.h b/include/private/GrTextureProxy.h index 5b4eeef7bc..b85302feea 100644 --- a/include/private/GrTextureProxy.h +++ b/include/private/GrTextureProxy.h @@ -18,7 +18,8 @@ class GrTextureProxy : public GrSurfaceProxy { public: // TODO: need to refine ownership semantics of 'srcData' if we're in completely // deferred mode - static sk_sp<GrTextureProxy> Make(const GrSurfaceDesc&, SkBackingFit, SkBudgeted, + static sk_sp<GrTextureProxy> Make(GrTextureProvider*, const GrSurfaceDesc&, + SkBackingFit, SkBudgeted, const void* srcData = nullptr, size_t rowBytes = 0); static sk_sp<GrTextureProxy> Make(sk_sp<GrTexture>); @@ -27,7 +28,7 @@ public: const GrTextureProxy* asTextureProxy() const override { return this; } // Actually instantiate the backing texture, if necessary - GrTexture* instantiate(GrTextureProvider* texProvider); + GrTexture* instantiate(GrTextureProvider*); private: // Deferred version @@ -36,6 +37,8 @@ private: // Wrapped version GrTextureProxy(sk_sp<GrTexture> tex); + size_t onGpuMemorySize() const override; + // For wrapped proxies the GrTexture pointer is stored in GrIORefProxy. // For deferred proxies that pointer will be filled n when we need to instantiate // the deferred resource diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp index eeb00c995e..e750571ff3 100644 --- a/src/core/SkSpecialImage.cpp +++ b/src/core/SkSpecialImage.cpp @@ -19,6 +19,7 @@ #include "GrContext.h" #include "GrTexture.h" #include "GrTextureParams.h" +#include "GrTextureProxy.h" #include "SkGr.h" #include "SkGrPriv.h" #endif @@ -42,12 +43,13 @@ public: virtual bool onGetROPixels(SkBitmap*) const = 0; - virtual GrTexture* onPeekTexture() const { return nullptr; } + virtual GrContext* onGetContext() const { return nullptr; } virtual SkColorSpace* onGetColorSpace() const = 0; #if SK_SUPPORT_GPU virtual sk_sp<GrTexture> onAsTextureRef(GrContext* context) const = 0; + virtual sk_sp<GrTextureProxy> onAsTextureProxy(GrContext* context) const = 0; #endif virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0; @@ -82,8 +84,8 @@ sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrContext* context) { if (!context) { return nullptr; } - if (GrTexture* peek = as_SIB(this)->onPeekTexture()) { - return peek->getContext() == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr; + if (GrContext* curContext = as_SIB(this)->onGetContext()) { + return curContext == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr; } SkBitmap bmp; @@ -125,22 +127,11 @@ bool SkSpecialImage::getROPixels(SkBitmap* bm) const { } bool SkSpecialImage::isTextureBacked() const { -#if SK_SUPPORT_GPU - return as_SIB(this)->onPeekTexture() && as_SIB(this)->onPeekTexture()->getContext(); -#else - return false; -#endif + return SkToBool(as_SIB(this)->onGetContext()); } GrContext* SkSpecialImage::getContext() const { -#if SK_SUPPORT_GPU - GrTexture* texture = as_SIB(this)->onPeekTexture(); - - if (texture) { - return texture->getContext(); - } -#endif - return nullptr; + return as_SIB(this)->onGetContext(); } SkColorSpace* SkSpecialImage::getColorSpace() const { @@ -151,6 +142,10 @@ SkColorSpace* SkSpecialImage::getColorSpace() const { sk_sp<GrTexture> SkSpecialImage::asTextureRef(GrContext* context) const { return as_SIB(this)->onAsTextureRef(context); } + +sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxy(GrContext* context) const { + return as_SIB(this)->onAsTextureProxy(context); +} #endif sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps, @@ -254,6 +249,19 @@ public: return nullptr; } + + sk_sp<GrTextureProxy> onAsTextureProxy(GrContext* context) const override { + if (context) { + sk_sp<GrTexture> tex(sk_ref_sp(GrRefCachedBitmapTexture( + context, + fBitmap, + GrTextureParams::ClampNoFilter(), + SkSourceGammaTreatment::kRespect))); + return GrTextureProxy::Make(tex); + } + + return nullptr; + } #endif // TODO: The raster implementations of image filters all currently assume that the pixels are @@ -349,7 +357,19 @@ public: uint32_t uniqueID, sk_sp<GrTexture> tex, SkAlphaType at, sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props) : INHERITED(subset, uniqueID, props) - , fTexture(std::move(tex)) + , fContext(tex->getContext()) + , fAlphaType(at) + , fColorSpace(std::move(colorSpace)) + , fAddedRasterVersionToCache(false) { + fTextureProxy = GrTextureProxy::Make(std::move(tex)); + } + + SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset, + uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at, + sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props) + : INHERITED(subset, uniqueID, props) + , fContext(context) + , fTextureProxy(std::move(proxy)) , fAlphaType(at) , fColorSpace(std::move(colorSpace)) , fAddedRasterVersionToCache(false) { @@ -363,23 +383,33 @@ public: SkAlphaType alphaType() const override { return fAlphaType; } - size_t getSize() const override { return fTexture->gpuMemorySize(); } + size_t getSize() const override { return fTextureProxy->gpuMemorySize(); } void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override { SkRect dst = SkRect::MakeXYWH(x, y, this->subset().width(), this->subset().height()); - auto img = sk_sp<SkImage>(new SkImage_Gpu(fTexture->width(), fTexture->height(), - this->uniqueID(), fAlphaType, fTexture, + // TODO: add GrTextureProxy-backed SkImage_Gpus + sk_sp<GrTexture> tex = sk_ref_sp(fTextureProxy->instantiate(fContext->textureProvider())); + + auto img = sk_sp<SkImage>(new SkImage_Gpu(fTextureProxy->width(), fTextureProxy->height(), + this->uniqueID(), fAlphaType, std::move(tex), fColorSpace, SkBudgeted::kNo)); canvas->drawImageRect(img, this->subset(), dst, paint, SkCanvas::kStrict_SrcRectConstraint); } - GrTexture* onPeekTexture() const override { return fTexture.get(); } + GrContext* onGetContext() const override { return fContext; } - sk_sp<GrTexture> onAsTextureRef(GrContext*) const override { return fTexture; } + // This entry point should go away in favor of asTextureProxy + sk_sp<GrTexture> onAsTextureRef(GrContext* context) const override { + return sk_ref_sp(fTextureProxy->instantiate(context->textureProvider())); + } + + sk_sp<GrTextureProxy> onAsTextureProxy(GrContext*) const override { + return fTextureProxy; + } bool onGetROPixels(SkBitmap* dst) const override { if (SkBitmapCache::Find(this->uniqueID(), dst)) { @@ -396,8 +426,11 @@ public: return false; } - if (!fTexture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig, - dst->getPixels(), dst->rowBytes())) { + // Reading back to an SkBitmap ends deferral + GrTexture* texture = fTextureProxy->instantiate(fContext->textureProvider()); + + if (!texture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig, + dst->getPixels(), dst->rowBytes())) { return false; } @@ -413,46 +446,49 @@ public: sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps, const SkISize& size, SkAlphaType at) const override { - if (!fTexture->getContext()) { + if (!fContext) { return nullptr; } SkColorSpace* colorSpace = outProps.colorSpace(); return SkSpecialSurface::MakeRenderTarget( - fTexture->getContext(), size.width(), size.height(), + fContext, size.width(), size.height(), GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace)); } sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override { - return SkSpecialImage::MakeFromGpu(subset, - this->uniqueID(), - fTexture, - fColorSpace, - &this->props(), - fAlphaType); + return SkSpecialImage::MakeDeferredFromGpu(fContext, + subset, + this->uniqueID(), + fTextureProxy, + fColorSpace, + &this->props(), + fAlphaType); } sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override { + // TODO: add GrTextureProxy-backed SkImage_Gpus + sk_sp<GrTexture> tex = sk_ref_sp(fTextureProxy->instantiate(fContext->textureProvider())); + if (0 == subset.fLeft && 0 == subset.fTop && - fTexture->width() == subset.width() && - fTexture->height() == subset.height()) { + fTextureProxy->width() == subset.width() && + fTextureProxy->height() == subset.height()) { // The existing GrTexture is already tight so reuse it in the SkImage - return sk_make_sp<SkImage_Gpu>(fTexture->width(), fTexture->height(), + return sk_make_sp<SkImage_Gpu>(tex->width(), tex->height(), kNeedNewImageUniqueID, - fAlphaType, fTexture, fColorSpace, + fAlphaType, std::move(tex), fColorSpace, SkBudgeted::kYes); } - GrContext* ctx = fTexture->getContext(); - GrSurfaceDesc desc = fTexture->desc(); + GrSurfaceDesc desc = fTextureProxy->desc(); desc.fWidth = subset.width(); desc.fHeight = subset.height(); - sk_sp<GrTexture> subTx(ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes)); + sk_sp<GrTexture> subTx(fContext->textureProvider()->createTexture(desc, SkBudgeted::kYes)); if (!subTx) { return nullptr; } - ctx->copySurface(subTx.get(), fTexture.get(), subset, SkIPoint::Make(0, 0)); + fContext->copySurface(subTx.get(), tex.get(), subset, SkIPoint::Make(0, 0)); return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID, fAlphaType, std::move(subTx), fColorSpace, SkBudgeted::kYes); } @@ -464,11 +500,12 @@ public: ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType; SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at, sk_ref_sp(colorSpace)); - return SkSurface::MakeRenderTarget(fTexture->getContext(), SkBudgeted::kYes, info); + return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info); } private: - sk_sp<GrTexture> fTexture; + GrContext* fContext; + sk_sp<GrTextureProxy> fTextureProxy; const SkAlphaType fAlphaType; sk_sp<SkColorSpace> fColorSpace; mutable SkAtomic<bool> fAddedRasterVersionToCache; @@ -487,4 +524,15 @@ sk_sp<SkSpecialImage> SkSpecialImage::MakeFromGpu(const SkIRect& subset, std::move(colorSpace), props); } +sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context, + const SkIRect& subset, + uint32_t uniqueID, + sk_sp<GrTextureProxy> proxy, + sk_sp<SkColorSpace> colorSpace, + const SkSurfaceProps* props, + SkAlphaType at) { + SkASSERT(rect_fits(subset, proxy->width(), proxy->height())); + return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at, + std::move(colorSpace), props); +} #endif diff --git a/src/core/SkSpecialImage.h b/src/core/SkSpecialImage.h index c1f3791edf..99e348920d 100644 --- a/src/core/SkSpecialImage.h +++ b/src/core/SkSpecialImage.h @@ -17,6 +17,7 @@ class GrContext; class GrTexture; +class GrTextureProxy; class SkBitmap; class SkCanvas; class SkImage; @@ -82,6 +83,14 @@ public: sk_sp<SkColorSpace>, const SkSurfaceProps* = nullptr, SkAlphaType at = kPremul_SkAlphaType); + + static sk_sp<SkSpecialImage> MakeDeferredFromGpu(GrContext*, + const SkIRect& subset, + uint32_t uniqueID, + sk_sp<GrTextureProxy>, + sk_sp<SkColorSpace>, + const SkSurfaceProps* = nullptr, + SkAlphaType at = kPremul_SkAlphaType); #endif /** @@ -129,6 +138,11 @@ public: * The active portion of the texture can be retrieved via 'subset'. */ sk_sp<GrTexture> asTextureRef(GrContext*) const; + + /** + * The same as above but return the contents as a GrTextureProxy. + */ + sk_sp<GrTextureProxy> asTextureProxy(GrContext*) const; #endif // TODO: hide this whe the imagefilter all have a consistent draw path (see skbug.com/5063) diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp index 50d79f44b8..380984871e 100644 --- a/src/gpu/GrRenderTargetProxy.cpp +++ b/src/gpu/GrRenderTargetProxy.cpp @@ -53,6 +53,12 @@ GrRenderTarget* GrRenderTargetProxy::instantiate(GrTextureProvider* texProvider) return nullptr; } +#ifdef SK_DEBUG + if (kInvalidGpuMemorySize != fGpuMemorySize) { + SkASSERT(fTarget->gpuMemorySize() <= fGpuMemorySize); + } +#endif + // Check that our a priori computation matched the ultimate reality SkASSERT(fFlags == fTarget->asRenderTarget()->renderTargetPriv().flags()); @@ -70,6 +76,20 @@ void GrRenderTargetProxy::validate(GrContext* context) const { } #endif +size_t GrRenderTargetProxy::onGpuMemorySize() const { + if (fTarget) { + return fTarget->gpuMemorySize(); + } + + SkASSERT(kUnknown_GrPixelConfig != fDesc.fConfig); + SkASSERT(!GrPixelConfigIsCompressed(fDesc.fConfig)); + size_t colorBytes = GrBytesPerPixel(fDesc.fConfig); + SkASSERT(colorBytes > 0); + + // TODO: do we have enough information to improve this worst case estimate? + return (fDesc.fSampleCnt + 1) * fDesc.fWidth * fDesc.fHeight * colorBytes; +} + sk_sp<GrRenderTargetProxy> GrRenderTargetProxy::Make(const GrCaps& caps, const GrSurfaceDesc& desc, SkBackingFit fit, diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp index 094eaa366a..c5afd0f262 100644 --- a/src/gpu/GrSurfaceProxy.cpp +++ b/src/gpu/GrSurfaceProxy.cpp @@ -16,6 +16,7 @@ GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, SkBackingFit fit) , fFit(fit) , fBudgeted(fTarget->resourcePriv().isBudgeted()) , fUniqueID(fTarget->uniqueID()) + , fGpuMemorySize(kInvalidGpuMemorySize) , fLastOpList(nullptr) { } diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp index 41775b1557..be09bd4024 100644 --- a/src/gpu/GrTextureProxy.cpp +++ b/src/gpu/GrTextureProxy.cpp @@ -10,9 +10,9 @@ #include "GrTextureProvider.h" GrTextureProxy::GrTextureProxy(const GrSurfaceDesc& srcDesc, SkBackingFit fit, SkBudgeted budgeted, - const void* /*srcData*/, size_t /*rowBytes*/) + const void* srcData, size_t /*rowBytes*/) : INHERITED(srcDesc, fit, budgeted) { - // TODO: Handle 'srcData' here + SkASSERT(!srcData); // currently handled in Make() } GrTextureProxy::GrTextureProxy(sk_sp<GrTexture> tex) @@ -30,17 +30,47 @@ GrTexture* GrTextureProxy::instantiate(GrTextureProvider* texProvider) { fTarget = texProvider->createTexture(fDesc, fBudgeted); } +#ifdef SK_DEBUG + if (kInvalidGpuMemorySize != fGpuMemorySize) { + SkASSERT(fTarget->gpuMemorySize() <= fGpuMemorySize); + } +#endif + return fTarget->asTexture(); } -sk_sp<GrTextureProxy> GrTextureProxy::Make(const GrSurfaceDesc& desc, +size_t GrTextureProxy::onGpuMemorySize() const { + size_t textureSize; + + if (GrPixelConfigIsCompressed(fDesc.fConfig)) { + textureSize = GrCompressedFormatDataSize(fDesc.fConfig, fDesc.fWidth, fDesc.fHeight); + } else { + textureSize = (size_t) fDesc.fWidth * fDesc.fHeight * GrBytesPerPixel(fDesc.fConfig); + } + + // TODO: add tracking of mipmap state to improve the estimate + textureSize += textureSize/3; + + SkASSERT(!SkToBool(fDesc.fFlags & kRenderTarget_GrSurfaceFlag)); + SkASSERT(textureSize <= GrSurface::WorstCaseSize(fDesc)); + + return textureSize; +} + +sk_sp<GrTextureProxy> GrTextureProxy::Make(GrTextureProvider* texProvider, + const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted, const void* srcData, size_t rowBytes) { - // TODO: handle 'srcData' (we could use the wrapped version if there is data) - SkASSERT(!srcData && !rowBytes); - return sk_sp<GrTextureProxy>(new GrTextureProxy(desc, fit, budgeted, srcData, rowBytes)); + if (srcData) { + // If we have srcData, for now, we create a wrapped GrTextureProxy + sk_sp<GrTexture> tex = sk_ref_sp(texProvider->createTexture(desc, budgeted, + srcData, rowBytes)); + return GrTextureProxy::Make(std::move(tex)); + } + + return sk_sp<GrTextureProxy>(new GrTextureProxy(desc, fit, budgeted, nullptr, 0)); } sk_sp<GrTextureProxy> GrTextureProxy::Make(sk_sp<GrTexture> tex) { diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp index 9a7697c8ae..b69e410103 100644 --- a/tests/ProxyTest.cpp +++ b/tests/ProxyTest.cpp @@ -116,7 +116,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) { desc.fSampleCnt = 0; - sk_sp<GrTextureProxy> texProxy(GrTextureProxy::Make(desc, + sk_sp<GrTextureProxy> texProxy(GrTextureProxy::Make(provider, + desc, fit, budgeted)); check_surface(reporter, texProxy.get(), origin, diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp index e992177430..23e8d5b7e3 100644 --- a/tests/SpecialImageTest.cpp +++ b/tests/SpecialImageTest.cpp @@ -17,6 +17,7 @@ #if SK_SUPPORT_GPU #include "GrContext.h" +#include "GrTextureProxy.h" #endif @@ -290,4 +291,44 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) { } } +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_DeferredGpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + SkBitmap bm = create_bm(); + + GrSurfaceDesc desc; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fWidth = kFullSize; + desc.fHeight = kFullSize; + + sk_sp<GrTextureProxy> proxy(GrTextureProxy::Make(context->textureProvider(), desc, + SkBackingFit::kExact, SkBudgeted::kNo, + bm.getPixels(), 0)); + if (!proxy) { + return; + } + + sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu( + context, + SkIRect::MakeWH(kFullSize, kFullSize), + kNeedNewImageUniqueID_SpecialImage, + proxy, nullptr)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + { + sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu( + context, + subset, + kNeedNewImageUniqueID_SpecialImage, + proxy, nullptr)); + test_image(subSImg1, reporter, context, true, kPad, kFullSize); + } + + { + sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset)); + test_image(subSImg2, reporter, context, true, kPad, kFullSize); + } +} + #endif |