Add an arg to SkImage::makeSubset to take a direct context

This is part of a larger effort to force users to provide a context
when manipulating GPU images which may be shared, instead of having
images themselves retain powerful context pointers.

Chrome flag landed in Chrome CL 2292800

Bug: skia:10466
Change-Id: Ic530a2c5eb1f4399db899d243ea944760fdf2055
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300707
Commit-Queue: Adlai Holler <adlai@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Adlai Holler 2020-07-10 14:33:22 -04:00 committed by Skia Commit-Bot
parent 31bc350754
commit 872a32c58d
18 changed files with 125 additions and 38 deletions

View File

@ -199,7 +199,7 @@ DEF_BENCH( return new ColorFilterBench("gaussian", []() {
return SkColorFilterPriv::MakeGaussian();
}); )
#ifdef SK_SUPPORT_GPU
#if SK_SUPPORT_GPU
DEF_BENCH( return new ColorFilterBench("src_runtime", []() {
static sk_sp<SkRuntimeEffect> gEffect = std::get<0>(
SkRuntimeEffect::Make(SkString(RuntimeNone_GPU_SRC)));

View File

@ -57,7 +57,7 @@ DEF_SIMPLE_GPU_GM_CAN_FAIL(cross_context_image, context, rtc, canvas, errorMsg,
canvas->drawImage(images[i], 0, 0);
canvas->translate(0, 256 + 10);
canvas->drawImage(images[i]->makeSubset(SkIRect::MakeXYWH(64, 64, 128, 128)), 0, 0);
canvas->drawImage(images[i]->makeSubset(SkIRect::MakeXYWH(64, 64, 128, 128), direct), 0, 0);
canvas->translate(128, 0);
SkPaint paint;

View File

@ -29,6 +29,7 @@
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/GrDirectContext.h"
#include "src/core/SkBlurMask.h"
#include "src/core/SkMathPriv.h"
#include "tools/ToolUtils.h"
@ -133,7 +134,8 @@ static void imagesubsetproc(SkCanvas* canvas, SkImage* image, const SkBitmap& bm
return;
}
if (sk_sp<SkImage> subset = image->makeSubset(srcR)) {
auto direct = GrAsDirectContext(canvas->recordingContext());
if (sk_sp<SkImage> subset = image->makeSubset(srcR, direct)) {
canvas->drawImageRect(subset, dstR, paint);
}
}

View File

@ -17,6 +17,7 @@
#include "include/core/SkSize.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GrDirectContext.h"
#include "tools/ToolUtils.h"
namespace {
@ -82,7 +83,8 @@ DEF_SIMPLE_GM(imagemasksubset, canvas, 480, 480) {
sk_sp<SkImage> image = makers[i](canvas, info);
if (image) {
canvas->drawImageRect(image, SkRect::Make(kSubset), kDest, &paint);
sk_sp<SkImage> subset = image->makeSubset(kSubset);
auto direct = GrAsDirectContext(canvas->recordingContext());
sk_sp<SkImage> subset = image->makeSubset(kSubset, direct);
canvas->drawImageRect(subset, kDest.makeOffset(kSize.width() * 1.5f, 0), &paint);
}
canvas->translate(0, kSize.height() * 1.5f);

View File

@ -1618,6 +1618,7 @@ protected:
void onDraw(GrRecordingContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
SkASSERT(fImages[0][0] && fImages[0][1] && fImages[1][0] && fImages[1][1]);
auto direct = context->asDirectContext();
int x = kPad;
for (int tagged : { 0, 1 }) {
for (int opaque : { 0, 1 }) {
@ -1634,8 +1635,8 @@ protected:
canvas->drawImage(yuv, x, y);
y += kTileWidthHeight + kPad;
auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
kTileWidthHeight / 2));
SkIRect bounds = SkIRect::MakeWH(kTileWidthHeight / 2, kTileWidthHeight / 2);
auto subset = yuv->makeSubset(bounds, direct);
canvas->drawImage(subset, x, y);
y += kTileWidthHeight + kPad;

View File

@ -104,6 +104,7 @@ tests_sources = [
"$_tests/GrContextAbandonTest.cpp",
"$_tests/GrContextFactoryTest.cpp",
"$_tests/GrContextOOM.cpp",
"$_tests/GrDDLImageTest.cpp",
"$_tests/GrFinishedFlushTest.cpp",
"$_tests/GrMemoryPoolTest.cpp",
"$_tests/GrMeshTest.cpp",

View File

@ -32,6 +32,8 @@ class SkPicture;
class SkSurface;
class GrBackendTexture;
class GrContext;
class GrDirectContext;
class GrRecordingContext;
class GrContextThreadSafeProxy;
class GrRecordingContext;
@ -1166,15 +1168,21 @@ public:
/** Returns subset of SkImage. subset must be fully contained by SkImage dimensions().
The implementation may share pixels, or may copy them.
Returns nullptr if subset is empty, or subset is not contained by bounds, or
pixels in SkImage could not be read or copied.
Returns nullptr if any of the following are true:
- Subset is empty
- Subset is not contained by bounds
- Pixels in SkImage could not be read or copied
If this image is texture-backed, the context parameter is required and must match the
context of the source image.
@param subset bounds of returned SkImage
@param context the GrDirectContext in play, if it exists
@return partial or full SkImage, or nullptr
example: https://fiddle.skia.org/c/@Image_makeSubset
*/
sk_sp<SkImage> makeSubset(const SkIRect& subset) const;
sk_sp<SkImage> makeSubset(const SkIRect& subset, GrDirectContext* direct = nullptr) const;
/** Returns SkImage backed by GPU texture associated with context. Returned SkImage is
compatible with SkSurface created with dstColorSpace. The returned SkImage respects

View File

@ -69,7 +69,7 @@ protected:
uint32_t contextID() const;
bool matches(GrContext_Base* candidate) const {
return candidate->contextID() == this->contextID();
return candidate && candidate->contextID() == this->contextID();
}
/*

View File

@ -24,6 +24,7 @@ class GrCaps;
class GrColorInfo;
class GrColorSpaceXform;
class GrContext;
class GrDirectContext;
class GrFragmentProcessor;
class GrPaint;
class GrRecordingContext;

View File

@ -29,6 +29,7 @@
#if SK_SUPPORT_GPU
#include "include/gpu/GrDirectContext.h"
#include "src/gpu/GrContextPriv.h"
#include "src/image/SkImage_Gpu.h"
#endif
#include "include/gpu/GrBackendSurface.h"
@ -170,7 +171,7 @@ sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* su
///////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset, GrDirectContext* direct) const {
if (subset.isEmpty()) {
return nullptr;
}
@ -180,17 +181,24 @@ sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
return nullptr;
}
#if SK_SUPPORT_GPU
auto myContext = as_IB(this)->context();
#ifdef SK_IMAGE_SUBSET_USE_SOURCE_CONTEXT
if (!direct) {
direct = GrAsDirectContext(myContext);
}
#endif
if (myContext && !myContext->priv().matches(direct)) {
return nullptr;
}
#endif
// optimization : return self if the subset == our bounds
if (bounds == subset) {
return sk_ref_sp(const_cast<SkImage*>(this));
}
// CONTEXT TODO: propagate the context parameter to the top-level API
#if SK_SUPPORT_GPU
return as_IB(this)->onMakeSubset(as_IB(this)->context(), subset);
#else
return as_IB(this)->onMakeSubset(nullptr, subset);
#endif
return as_IB(this)->onMakeSubset(subset, direct);
}
#if SK_SUPPORT_GPU

View File

@ -17,12 +17,12 @@
#include "src/gpu/GrSurfaceProxyView.h"
#include "src/gpu/GrTextureProxy.h"
class GrRecordingContext;
class GrTexture;
#endif
#include <new>
class GrDirectContext;
class GrSamplerState;
class SkCachedData;
struct SkYUVASizeInfo;
@ -98,7 +98,7 @@ public:
// but only inspect them (or encode them).
virtual bool getROPixels(SkBitmap*, CachingHint = kAllow_CachingHint) const = 0;
virtual sk_sp<SkImage> onMakeSubset(GrRecordingContext*, const SkIRect&) const = 0;
virtual sk_sp<SkImage> onMakeSubset(const SkIRect&, GrDirectContext*) const = 0;
virtual sk_sp<SkCachedData> getPlanes(SkYUVASizeInfo*, SkYUVAIndex[4],
SkYUVColorSpace*, const void* planes[4]);

View File

@ -755,13 +755,15 @@ bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx,
sk_sp<SkImage> image,
GrBackendTexture* backendTexture,
BackendTextureReleaseProc* releaseProc) {
if (!image || !ctx || !backendTexture || !releaseProc) {
// TODO: Elevate direct context requirement to public API.
auto direct = GrAsDirectContext(ctx);
if (!image || !direct || !backendTexture || !releaseProc) {
return false;
}
// Ensure we have a texture backed image.
if (!image->isTextureBacked()) {
image = image->makeTextureImage(ctx);
image = image->makeTextureImage(direct);
if (!image) {
return false;
}
@ -774,19 +776,19 @@ bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx,
}
// If the image's context doesn't match the provided context, fail.
if (texture->getContext() != ctx) {
if (texture->getContext() != direct) {
return false;
}
// Flush any pending IO on the texture.
ctx->priv().flushSurface(as_IB(image)->peekProxy());
direct->priv().flushSurface(as_IB(image)->peekProxy());
// We must make a copy of the image if the image is not unique, if the GrTexture owned by the
// image is not unique, or if the texture wraps an external object.
if (!image->unique() || !texture->unique() ||
texture->resourcePriv().refsWrappedObjects()) {
// onMakeSubset will always copy the image.
image = as_IB(image)->onMakeSubset(ctx, image->bounds());
image = as_IB(image)->onMakeSubset(image->bounds(), direct);
if (!image) {
return false;
}
@ -797,7 +799,7 @@ bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx,
}
// Flush to ensure that the copy is completed before we return the texture.
ctx->priv().flushSurface(as_IB(image)->peekProxy());
direct->priv().flushSurface(as_IB(image)->peekProxy());
}
SkASSERT(!texture->resourcePriv().refsWrappedObjects());

View File

@ -134,16 +134,16 @@ bool SkImage_GpuBase::getROPixels(SkBitmap* dst, CachingHint chint) const {
return true;
}
sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(GrRecordingContext* context,
const SkIRect& subset) const {
if (!context || !fContext->priv().matches(context)) {
sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset,
GrDirectContext* direct) const {
if (!direct || !fContext->priv().matches(direct)) {
return nullptr;
}
const GrSurfaceProxyView* view = this->view(context);
const GrSurfaceProxyView* view = this->view(direct);
SkASSERT(view && view->proxy());
auto copyView = GrSurfaceProxyView::Copy(context, *view, GrMipMapped::kNo, subset,
auto copyView = GrSurfaceProxyView::Copy(direct, *view, GrMipMapped::kNo, subset,
SkBackingFit::kExact, view->proxy()->isBudgeted());
if (!copyView) {
@ -151,7 +151,7 @@ sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(GrRecordingContext* context,
}
// MDB: this call is okay bc we know 'sContext' was kExact
return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, std::move(copyView),
return sk_make_sp<SkImage_Gpu>(sk_ref_sp(direct), kNeedNewImageUniqueID, std::move(copyView),
this->colorType(), this->alphaType(), this->refColorSpace());
}

View File

@ -11,7 +11,7 @@
#include "include/core/SkDeferredDisplayListRecorder.h"
#include "include/core/SkYUVAIndex.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContext.h"
#include "include/gpu/GrDirectContext.h"
#include "include/private/GrTypesPriv.h"
#include "src/image/SkImage_Base.h"
@ -23,7 +23,7 @@ public:
GrContext* context() const final { return fContext.get(); }
bool getROPixels(SkBitmap*, CachingHint) const final;
sk_sp<SkImage> onMakeSubset(GrRecordingContext*, const SkIRect& subset) const final;
sk_sp<SkImage> onMakeSubset(const SkIRect& subset, GrDirectContext*) const final;
bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
int srcX, int srcY, CachingHint) const override;
@ -90,6 +90,7 @@ protected:
GrSurfaceProxyView views[4],
const SkYUVAIndex yuvaIndices[4]);
// TODO: Migrate this to something much weaker, such as GrContextThreadSafeProxy.
sk_sp<GrContext> fContext;
private:

View File

@ -239,8 +239,7 @@ GrSurfaceProxyView SkImage_Lazy::refView(GrRecordingContext* context, GrMipMappe
}
#endif
sk_sp<SkImage> SkImage_Lazy::onMakeSubset(GrRecordingContext* context,
const SkIRect& subset) const {
sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset, GrDirectContext*) const {
SkASSERT(this->bounds().contains(subset));
SkASSERT(this->bounds() != subset);

View File

@ -48,7 +48,7 @@ public:
SkYUVColorSpace*, const void* planes[4]) override;
#endif
sk_sp<SkData> onRefEncoded() const override;
sk_sp<SkImage> onMakeSubset(GrRecordingContext*, const SkIRect&) const override;
sk_sp<SkImage> onMakeSubset(const SkIRect&, GrDirectContext*) const override;
bool getROPixels(SkBitmap*, CachingHint) const override;
bool onIsLazyGenerated() const override { return true; }
sk_sp<SkImage> onMakeColorTypeAndColorSpace(GrRecordingContext*,

View File

@ -89,7 +89,7 @@ public:
#endif
bool getROPixels(SkBitmap*, CachingHint) const override;
sk_sp<SkImage> onMakeSubset(GrRecordingContext*, const SkIRect&) const override;
sk_sp<SkImage> onMakeSubset(const SkIRect&, GrDirectContext*) const override;
SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
@ -233,7 +233,7 @@ void SkImage_Raster::onUnpinAsTexture(GrContext* ctx) const {
}
#endif
sk_sp<SkImage> SkImage_Raster::onMakeSubset(GrRecordingContext*, const SkIRect& subset) const {
sk_sp<SkImage> SkImage_Raster::onMakeSubset(const SkIRect& subset, GrDirectContext*) const {
SkImageInfo info = fBitmap.info().makeDimensions(subset.size());
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(info)) {

62
tests/GrDDLImageTest.cpp Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkImage.h"
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceCharacterization.h"
#include "tests/Test.h"
DEF_GPUTEST(GrDDLImage_MakeSubset, reporter, options) {
sk_gpu_test::GrContextFactory factory(options);
for (int ct = 0; ct < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++ct) {
auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(ct);
auto direct = factory.get(contextType);
if (!direct) {
continue;
}
SkIRect subsetBounds = SkIRect::MakeLTRB(4,4,8,8);
SkImageInfo ii = SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
// Raster image:
SkBitmap bm;
bm.setInfo(ii);
bm.allocPixels();
bm.eraseColor(SK_ColorBLACK);
bm.setImmutable();
auto rasterImg = SkImage::MakeFromBitmap(bm);
REPORTER_ASSERT(reporter, rasterImg->isValid(static_cast<GrRecordingContext*>(nullptr)));
// raster + context:
auto subImg1 = rasterImg->makeSubset(subsetBounds, direct);
REPORTER_ASSERT(reporter, subImg1->isValid(direct));
// raster + no context:
auto subImg2 = rasterImg->makeSubset(subsetBounds, nullptr);
REPORTER_ASSERT(reporter, subImg2->isValid(static_cast<GrRecordingContext*>(nullptr)));
// Texture image:
auto surf = SkSurface::MakeRenderTarget(direct, SkBudgeted::kNo, ii);
SkSurfaceCharacterization sc;
REPORTER_ASSERT(reporter, surf->characterize(&sc));
GrBackendTexture tex = direct->createBackendTexture(sc);
auto gpuImage = SkImage::MakeFromTexture(direct, tex, kTopLeft_GrSurfaceOrigin,
ii.colorType(), ii.alphaType(),
ii.refColorSpace());
REPORTER_ASSERT(reporter, gpuImage->isValid(direct));
// gpu image + context:
auto subImg5 = gpuImage->makeSubset(subsetBounds, direct);
REPORTER_ASSERT(reporter, subImg5->isValid(direct));
// gpu image + nullptr:
REPORTER_ASSERT(reporter, !gpuImage->makeSubset(subsetBounds, nullptr));
direct->flush();
direct->submit(true);
direct->deleteBackendTexture(tex);
}
}