Reland "Reland "SkSurface asynchronous read APIs allow client to extend pixel lifetime""

This is a reland of 6fc04f88a8

Original change's description:
> Reland "SkSurface asynchronous read APIs allow client to extend pixel lifetime"
> 
> This is a reland of ce240cc6fd
> 
> Original change's description:
> > SkSurface asynchronous read APIs allow client to extend pixel lifetime
> > 
> > Previously the pixel data passed to the client was only valid during
> > the client's callback. This meant if the client wanted to defer
> > processing of the data a copy was necessary.
> > 
> > Now we pass an object to the callback and the pixel lifetime is tied
> > to the lifetime of that object.
> > 
> > The object may be holding a GPU transfer buffer mapped. We don't assume
> > that the object will be released on the direct GrContext thread. So
> > when the object is destroyed it posts a message to a new type,
> > GrClientMappedBufferManager, hanging off the direct context. The direct
> > context will periodically check for messages and unmap and then unref
> > buffers so that they can be reused. Currently this is done in
> > GrContext::performDeferredCleanup() and GrDrawingManager::flush().
> > 
> > The old API is kept around for backwards compatibility but it is
> > reimplemented as a bridge on top of the new mechanism.
> > 
> > Also a utility function to SkImageInfo is added to directly make a new
> > info with a specified dimensions rather than passing the width and
> > height separately to makeWH().
> > 
> > Bug: chromium:973403
> > Bug: skia:8962
> > 
> > Change-Id: Id5cf04235376170142a48e90d3ecd13fd021a2a6
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/245457
> > Reviewed-by: Brian Osman <brianosman@google.com>
> > Commit-Queue: Brian Salomon <bsalomon@google.com>
> 
> Bug: chromium:973403, skia:8962
> Change-Id: I5cecd36276c8b6dc942cf549c7095db2df88530c
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/245678
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

Bug: chromium:973403, skia:8962
Change-Id: Ie584c1c3ef8021c976f71b708e53871c693cc450
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/246057
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2019-10-03 13:26:54 -04:00 committed by Skia Commit-Bot
parent 1244db5650
commit 9241a6d394
40 changed files with 942 additions and 388 deletions

View File

@ -8,6 +8,10 @@ Milestone 79
[Insert new notes here.]
* New versions of SkSurface async-rescale-and read APIs that allow client to extend
the lifetime of the result data. Old versions are deprecated.
https://review.skia.org/245457
* Add SkColorInfo. It's dimensionless SkImageInfo.
https://review.skia.org/245261

View File

@ -32,8 +32,7 @@ void AndroidCodecBench::onDelayedSetup() {
std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(fData));
SkISize scaledSize = codec->getSampledDimensions(fSampleSize);
fInfo = codec->getInfo().makeWH(scaledSize.width(), scaledSize.height())
.makeColorType(kN32_SkColorType);
fInfo = codec->getInfo().makeDimensions(scaledSize).makeColorType(kN32_SkColorType);
if (kUnpremul_SkAlphaType == fInfo.alphaType()) {
fInfo = fInfo.makeAlphaType(kPremul_SkAlphaType);
}

View File

@ -74,7 +74,7 @@ protected:
const SkImageInfo info = SkImageInfo::MakeN32Premul(2048, 1024);
fSrc.allocPixels(info);
fSrc.eraseColor(SK_ColorBLACK);
fDst.allocPixels(info.makeWH(info.height(), info.width()));
fDst.allocPixels(info.makeDimensions(info.dimensions()));
}
const char* onGetName() override {

View File

@ -413,7 +413,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
if ((size.width() <= 10 || size.height() <= 10) && 1.0f != fScale) {
return Error::Nonfatal("Scaling very small images is uninteresting.");
}
decodeInfo = decodeInfo.makeWH(size.width(), size.height());
decodeInfo = decodeInfo.makeDimensions(size);
const int bpp = decodeInfo.bytesPerPixel();
const size_t rowBytes = size.width() * bpp;
@ -794,7 +794,7 @@ Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
if ((size.width() <= 10 || size.height() <= 10) && 1 != fSampleSize) {
return Error::Nonfatal("Scaling very small images is uninteresting.");
}
decodeInfo = decodeInfo.makeWH(size.width(), size.height());
decodeInfo = decodeInfo.makeDimensions(size);
int bpp = decodeInfo.bytesPerPixel();
size_t rowBytes = size.width() * bpp;
@ -976,8 +976,7 @@ Error ColorCodecSrc::draw(SkCanvas* canvas) const {
SkImageInfo info = codec->getInfo();
if (fDecodeToDst) {
info = canvas->imageInfo().makeWH(info.width(),
info.height());
info = canvas->imageInfo().makeDimensions(info.dimensions());
}
SkBitmap bitmap;
@ -1821,7 +1820,7 @@ Error ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkSt
SkBitmap uprighted;
SkISize size = auto_compute_translate(&upright, bitmap->width(), bitmap->height());
uprighted.allocPixels(bitmap->info().makeWH(size.width(), size.height()));
uprighted.allocPixels(bitmap->info().makeDimensions(size));
SkCanvas canvas(uprighted);
canvas.concat(upright);

View File

@ -428,7 +428,7 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
SkImageInfo decodeInfo = codec->getInfo();
SkISize size = codec->getScaledDimensions(fscale);
decodeInfo = decodeInfo.makeWH(size.width(), size.height());
decodeInfo = decodeInfo.makeDimensions(size);
SkBitmap bitmap;
SkCodec::Options options;

View File

@ -20,98 +20,74 @@
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
namespace {
struct AsyncContext {
bool fCalled = false;
std::unique_ptr<const SkSurface::AsyncReadResult> fResult;
};
} // anonymous namespace
// Making this a lambda in the test functions caused:
// "error: cannot compile this forwarded non-trivially copyable parameter yet"
// on x86/Win/Clang bot, referring to 'result'.
static void async_callback(void* c, std::unique_ptr<const SkSurface::AsyncReadResult> result) {
auto context = static_cast<AsyncContext*>(c);
context->fResult = std::move(result);
context->fCalled = true;
};
// Draws the image to a surface, does a asyncRescaleAndReadPixels of the image, and then sticks
// the result in a raster image.
static sk_sp<SkImage> do_read_and_scale(SkSurface* surface, const SkIRect& srcRect,
const SkImageInfo& ii, SkSurface::RescaleGamma rescaleGamma,
SkFilterQuality quality) {
SkBitmap bmp;
bmp.allocPixels(ii);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
struct Context {
SkPixmap fPixmap;
bool fCalled = false;
bool fSucceeded = false;
} context;
SkAssertResult(bmp.peekPixels(&context.fPixmap));
auto callback = [](void* c, const void* data, size_t rowBytes) {
auto context = reinterpret_cast<Context*>(c);
context->fCalled = true;
if (!data) {
context->fPixmap.reset();
return;
}
context->fSucceeded = true;
SkRectMemcpy(context->fPixmap.writable_addr(), context->fPixmap.rowBytes(), data, rowBytes,
context->fPixmap.info().minRowBytes(), context->fPixmap.height());
};
surface->asyncRescaleAndReadPixels(ii, srcRect, rescaleGamma, quality, callback, &context);
while (!context.fCalled) {
auto* context = new AsyncContext();
surface->asyncRescaleAndReadPixels(ii, srcRect, rescaleGamma, quality, async_callback, context);
while (!context->fCalled) {
// Only GPU should actually be asynchronous.
SkASSERT(surface->getCanvas()->getGrContext());
surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
}
return context.fSucceeded ? SkImage::MakeFromBitmap(bmp) : nullptr;
if (!context->fResult) {
return nullptr;
}
SkPixmap pixmap(ii, context->fResult->data(0), context->fResult->rowBytes(0));
auto releasePixels = [](const void*, void* c) { delete static_cast<AsyncContext*>(c); };
return SkImage::MakeFromRaster(pixmap, releasePixels, context);
}
static sk_sp<SkImage> do_read_and_scale_yuv(SkSurface* surface, SkYUVColorSpace yuvCS,
const SkIRect& srcRect, int dstW, int dstH,
const SkIRect& srcRect, SkISize size,
SkSurface::RescaleGamma rescaleGamma,
SkFilterQuality quality, SkScopeExit* cleanup) {
SkASSERT(!(dstW & 0b1) && !(dstH & 0b1));
SkASSERT(!(size.width() & 0b1) && !(size.height() & 0b1));
struct Context {
Context(int w, int h) {
SkImageInfo yII = SkImageInfo::Make(w, h, kGray_8_SkColorType, kPremul_SkAlphaType);
SkImageInfo uvII = SkImageInfo::Make(w / 2, h / 2, kGray_8_SkColorType,
kPremul_SkAlphaType);
SkISize uvSize = {size.width()/2, size.height()/2};
SkImageInfo yII = SkImageInfo::Make(size, kGray_8_SkColorType, kPremul_SkAlphaType);
SkImageInfo uvII = SkImageInfo::Make(uvSize, kGray_8_SkColorType, kPremul_SkAlphaType);
fYData.alloc(yII);
fUData.alloc(uvII);
fVData.alloc(uvII);
}
SkAutoPixmapStorage fYData;
SkAutoPixmapStorage fUData;
SkAutoPixmapStorage fVData;
bool fCalled = false;
bool fSucceeded = false;
} context(dstW, dstH);
auto callback = [](void* c, const void* data[3], size_t rowBytes[3]) {
auto context = reinterpret_cast<Context*>(c);
context->fCalled = true;
if (!data) {
return;
}
context->fSucceeded = true;
SkRectMemcpy(context->fYData.writable_addr(), context->fYData.rowBytes(), data[0],
rowBytes[0], context->fYData.width(), context->fYData.height());
SkRectMemcpy(context->fUData.writable_addr(), context->fUData.rowBytes(), data[1],
rowBytes[1], context->fUData.width(), context->fUData.height());
SkRectMemcpy(context->fVData.writable_addr(), context->fVData.rowBytes(), data[2],
rowBytes[2], context->fVData.width(), context->fVData.height());
};
surface->asyncRescaleAndReadPixelsYUV420(yuvCS, SkColorSpace::MakeSRGB(), srcRect, dstW, dstH,
rescaleGamma, quality, callback, &context);
AsyncContext context;
surface->asyncRescaleAndReadPixelsYUV420(yuvCS, SkColorSpace::MakeSRGB(), srcRect, size,
rescaleGamma, quality, async_callback, &context);
while (!context.fCalled) {
// Only GPU should actually be asynchronous.
SkASSERT(surface->getCanvas()->getGrContext());
surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
}
if (!context.fSucceeded) {
if (!context.fResult) {
return nullptr;
}
auto* gr = surface->getCanvas()->getGrContext();
GrBackendTexture backendTextures[3];
backendTextures[0] = gr->createBackendTexture(&context.fYData, 1,
GrRenderable::kNo, GrProtected::kNo);
backendTextures[1] = gr->createBackendTexture(&context.fUData, 1,
GrRenderable::kNo, GrProtected::kNo);
backendTextures[2] = gr->createBackendTexture(&context.fVData, 1,
GrRenderable::kNo, GrProtected::kNo);
SkPixmap yPM(yII, context.fResult->data(0), context.fResult->rowBytes(0));
SkPixmap uPM(uvII, context.fResult->data(1), context.fResult->rowBytes(1));
SkPixmap vPM(uvII, context.fResult->data(2), context.fResult->rowBytes(2));
backendTextures[0] = gr->createBackendTexture(yPM, GrRenderable::kNo, GrProtected::kNo);
backendTextures[1] = gr->createBackendTexture(uPM, GrRenderable::kNo, GrProtected::kNo);
backendTextures[2] = gr->createBackendTexture(vPM, GrRenderable::kNo, GrProtected::kNo);
SkYUVAIndex indices[4] = {
{ 0, SkColorChannel::kR},
{ 1, SkColorChannel::kR},
@ -128,14 +104,14 @@ static sk_sp<SkImage> do_read_and_scale_yuv(SkSurface* surface, SkYUVColorSpace
gr->deleteBackendTexture(backendTextures[2]);
}};
return SkImage::MakeFromYUVATextures(gr, yuvCS, backendTextures, indices, {dstW, dstH},
return SkImage::MakeFromYUVATextures(gr, yuvCS, backendTextures, indices, size,
kTopLeft_GrSurfaceOrigin, SkColorSpace::MakeSRGB());
}
// Draws a grid of rescales. The columns are none, low, and high filter quality. The rows are
// rescale in src gamma and rescale in linear gamma.
static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
const SkIRect& srcRect, int newW, int newH, bool doYUV420,
const SkIRect& srcRect, SkISize newSize, bool doYUV420,
SkString* errorMsg, int pad = 0) {
if (doYUV420) {
if (!canvas->getGrContext() || !canvas->getGrContext()->priv().asDirectContext()) {
@ -147,7 +123,7 @@ static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
*errorMsg = "Not supported on recording/vector backends.";
return skiagm::DrawResult::kSkip;
}
const auto ii = canvas->imageInfo().makeWH(newW, newH);
const auto ii = canvas->imageInfo().makeDimensions(newSize);
SkYUVColorSpace yuvColorSpace = kRec601_SkYUVColorSpace;
canvas->save();
@ -157,7 +133,7 @@ static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
SkScopeExit cleanup;
sk_sp<SkImage> result;
if (doYUV420) {
result = do_read_and_scale_yuv(surface, yuvColorSpace, srcRect, newW, newH, gamma,
result = do_read_and_scale_yuv(surface, yuvColorSpace, srcRect, newSize, gamma,
quality, &cleanup);
if (!result) {
errorMsg->printf("YUV420 async call failed. Allowed for now.");
@ -173,17 +149,17 @@ static skiagm::DrawResult do_rescale_grid(SkCanvas* canvas, SkSurface* surface,
}
}
canvas->drawImage(result, 0, 0);
canvas->translate(newW + pad, 0);
canvas->translate(newSize.width() + pad, 0);
}
canvas->restore();
canvas->translate(0, newH + pad);
canvas->translate(0, newSize.height() + pad);
}
canvas->restore();
return skiagm::DrawResult::kOk;
}
static skiagm::DrawResult do_rescale_image_grid(SkCanvas* canvas, const char* imageFile,
const SkIRect& srcRect, int newW, int newH,
const SkIRect& srcRect, SkISize newSize,
bool doYUV420, SkString* errorMsg) {
auto image = GetResourceAsImage(imageFile);
if (!image) {
@ -195,7 +171,7 @@ static skiagm::DrawResult do_rescale_image_grid(SkCanvas* canvas, const char* im
return skiagm::DrawResult::kSkip;
}
// Turn the image into a surface in order to call the read and rescale API
auto surfInfo = image->imageInfo().makeWH(image->width(), image->height());
auto surfInfo = image->imageInfo().makeDimensions(image->dimensions());
auto surface = canvas->makeSurface(surfInfo);
if (!surface && surfInfo.colorType() == kBGRA_8888_SkColorType) {
surfInfo = surfInfo.makeColorType(kRGBA_8888_SkColorType);
@ -212,19 +188,19 @@ static skiagm::DrawResult do_rescale_image_grid(SkCanvas* canvas, const char* im
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
surface->getCanvas()->drawImage(image, 0, 0, &paint);
return do_rescale_grid(canvas, surface.get(), srcRect, newW, newH, doYUV420, errorMsg);
return do_rescale_grid(canvas, surface.get(), srcRect, newSize, doYUV420, errorMsg);
}
#define DEF_RESCALE_AND_READ_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, W, H, false, errorMsg); \
#define DEF_RESCALE_AND_READ_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, false, errorMsg); \
}
#define DEF_RESCALE_AND_READ_YUV_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
#define DEF_RESCALE_AND_READ_YUV_GM(IMAGE_FILE, TAG, SRC_RECT, W, H) \
DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_yuv420_##TAG, canvas, errorMsg, 3 * W, 2 * H) { \
ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, W, H, true, errorMsg); \
ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25); \
return do_rescale_image_grid(canvas, #IMAGE_FILE, SRC_RECT, {W, H}, true, errorMsg); \
}
DEF_RESCALE_AND_READ_YUV_GM(images/yellow_rose.webp, rose, SkIRect::MakeXYWH(50, 5, 200, 150),
@ -272,16 +248,14 @@ DEF_SIMPLE_GM_CAN_FAIL(async_rescale_and_read_no_bleed, canvas, errorMsg, 60, 60
static constexpr int kPad = 2;
canvas->translate(kPad, kPad);
skiagm::DrawResult result;
auto downW = static_cast<int>(kInner / 2);
auto downH = static_cast<int>(kInner / 2);
result = do_rescale_grid(canvas, surface.get(), srcRect, downW, downH, false, errorMsg, kPad);
SkISize downSize = {static_cast<int>(kInner/2), static_cast<int>(kInner / 2)};
result = do_rescale_grid(canvas, surface.get(), srcRect, downSize, false, errorMsg, kPad);
if (result != skiagm::DrawResult::kOk) {
return result;
}
canvas->translate(0, 2 * downH);
auto upW = static_cast<int>(kInner * 3.5);
auto upH = static_cast<int>(kInner * 4.6);
result = do_rescale_grid(canvas, surface.get(), srcRect, upW, upH, false, errorMsg, kPad);
canvas->translate(0, 2 * downSize.height());
SkISize upSize = {static_cast<int>(kInner * 3.5), static_cast<int>(kInner * 4.6)};
result = do_rescale_grid(canvas, surface.get(), srcRect, upSize, false, errorMsg, kPad);
if (result != skiagm::DrawResult::kOk) {
return result;
}

View File

@ -79,7 +79,7 @@ struct DownsampleBitmapGM : public skiagm::GM {
static SkBitmap convert_bitmap_format(SkBitmap src, SkImageInfo info) {
SkBitmap dst;
dst.allocPixels(info.makeWH(src.width(), src.height()));
dst.allocPixels(info.makeDimensions(src.dimensions()));
SkPixmap pm;
SkAssertResult(dst.peekPixels(&pm));

View File

@ -60,6 +60,8 @@ skia_gpu_sources = [
"$_src/gpu/GrBufferAllocPool.h",
"$_src/gpu/GrCaps.h",
"$_src/gpu/GrCaps.cpp",
"$_src/gpu/GrClientMappedBufferManager.cpp",
"$_src/gpu/GrClientMappedBufferManager.h",
"$_src/gpu/GrClip.h",
"$_src/gpu/GrClipStackClip.h",
"$_src/gpu/GrClipStackClip.cpp",

View File

@ -560,6 +560,16 @@ public:
return Make({newWidth, newHeight}, fColorInfo);
}
/** Creates SkImageInfo with the same SkColorType, SkColorSpace, and SkAlphaType,
with dimensions set to newDimensions.
@param newSize pixel column and row count; must be zero or greater
@return created SkImageInfo
*/
SkImageInfo makeDimensions(SkISize newSize) const {
return Make(newSize, fColorInfo);
}
/** Creates SkImageInfo with same SkColorType, SkColorSpace, width, and height,
with SkAlphaType set to newAlphaType.

View File

@ -764,69 +764,132 @@ public:
*/
bool readPixels(const SkBitmap& dst, int srcX, int srcY);
/** Makes pixel data available to caller, possibly asynchronously. Can perform rescaling.
/** The result from asyncRescaleAndReadPixels() or asyncRescaleAndReadPixelsYUV420(). */
class AsyncReadResult {
public:
AsyncReadResult(const AsyncReadResult&) = delete;
AsyncReadResult(AsyncReadResult&&) = delete;
AsyncReadResult& operator=(const AsyncReadResult&) = delete;
AsyncReadResult& operator=(AsyncReadResult&&) = delete;
virtual ~AsyncReadResult() = default;
virtual int count() const = 0;
virtual const void* data(int i) const = 0;
virtual size_t rowBytes(int i) const = 0;
protected:
AsyncReadResult() = default;
};
/** Client-provided context that is passed to client-provided ReadPixelsContext. */
using ReadPixelsContext = void*;
/** Client-provided callback to asyncRescaleAndReadPixels() or
asyncRescaleAndReadPixelsYUV420() that is called when read result is ready or on failure.
*/
using ReadPixelsCallback = void(ReadPixelsContext, std::unique_ptr<const AsyncReadResult>);
/** Controls the gamma that rescaling occurs in for asyncRescaleAndReadPixels() and
asyncRescaleAndReadPixelsYUV420().
*/
enum RescaleGamma : bool { kSrc, kLinear };
/** Makes surface pixel data available to caller, possibly asynchronously. It can also rescale
the surface pixels.
Currently asynchronous reads are only supported on the GPU backend and only when the
underlying 3D API supports transfer buffers and CPU/GPU synchronization primitives. In all
other cases this operates synchronously.
Data is read from the source rectangle, is optionally converted to a linear gamma, is
Data is read from the source sub-rectangle, is optionally converted to a linear gamma, is
rescaled to the size indicated by 'info', is then converted to the color space, color type,
and alpha type of 'info'.
and alpha type of 'info'. A 'srcRect' that is not contained by the bounds of the surface
causes failure.
When the pixel data is ready the caller's ReadPixelsCallback is called with a pointer to
the data in the requested color type, alpha type, and color space. The data pointer is
only valid for the duration of the callback.
When the pixel data is ready the caller's ReadPixelsCallback is called with a
AsyncReadResult containing pixel data in the requested color type, alpha type, and color
space. The AsyncReadResult will have count() == 1. Upon failure the callback is called
with nullptr for AsyncReadResult.
Upon failure the the callback is called with nullptr as the data pointer.
The data is valid for the lifetime of AsyncReadResult with the exception that if the
SkSurface is GPU-backed the data is immediately invalidated if the GrContext is abandoned
or destroyed.
If the src rectangle is not contained by the bounds of the surface then failure occurs.
Failure is indicated by calling callback with a nullptr for 'data'.
@param info info of the requested pixels
@param srcRect subrectangle of surface to read
@param rescaleGamma controls whether rescaling is done in the surface's gamma or whether
the source data is transformed to a linear gamma before rescaling.
@param rescaleQuality controls the quality (and cost) of the rescaling
@param callback function to call with result of the read
@param context passed to callback
@param info info of the requested pixels
@param srcRect subrectangle of surface to read
@param rescaleGamma controls whether rescaling is done in the surface's gamma or whether
the source data is transformed to a linear gamma before rescaling.
@param rescaleQuality controls the quality (and cost) of the rescaling
@param callback function to call with result of the read
@param context passed to callback
*/
using ReadPixelsContext = void*;
using ReadPixelsCallback = void(ReadPixelsContext, const void* data, size_t rowBytes);
enum RescaleGamma : bool { kSrc, kLinear };
void asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallback callback, ReadPixelsContext context);
/** Legacy version of asyncRescaleAndReadPixels() that passes data directly to the callback
rather than using AsyncReadResult. The data is only valid during the lifetime of the
callback.
Deprecated.
*/
using LegacyReadPixelsCallback = void(ReadPixelsContext, const void* data, size_t rowBytes);
void asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
LegacyReadPixelsCallback callback, ReadPixelsContext context);
/**
Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The
RGB->YUV conversion is controlled by 'yuvColorSpace'. The YUV data is returned as three
planes ordered y, u, v. The u and v planes are half the width and height of the resized
rectangle. Currently this fails if dstW or dstH are not even.
rectangle. The y, u, and v values are single bytes. Currently this fails if 'dstSize'
width and height are not even. A 'srcRect' that is not contained by the bounds of the
surface causes failure.
On failure the callback is called with a null data pointer array. Fails if srcRect is not
contained in the surface bounds.
When the pixel data is ready the caller's ReadPixelsCallback is called with a
AsyncReadResult containing the planar data. The AsyncReadResult will have count() == 3.
Upon failure the callback is called with nullptr for AsyncReadResult.
The data is valid for the lifetime of AsyncReadResult with the exception that if the
SkSurface is GPU-backed the data is immediately invalidated if the GrContext is abandoned
or destroyed.
@param yuvColorSpace The transformation from RGB to YUV. Applied to the resized image
after it is converted to dstColorSpace.
@param dstColorSpace The color space to convert the resized image to, after rescaling.
@param srcRect The portion of the surface to rescale and convert to YUV planes.
@param dstW The width to rescale srcRect to
@param dstH The height to rescale srcRect to
@param rescaleGamma controls whether rescaling is done in the surface's gamma or whether
the source data is transformed to a linear gamma before rescaling.
@param rescaleQuality controls the quality (and cost) of the rescaling
@param callback function to call with the planar read result
@param context passed to callback
@param dstSize The size to rescale srcRect to
@param rescaleGamma controls whether rescaling is done in the surface's gamma or whether
the source data is transformed to a linear gamma before rescaling.
@param rescaleQuality controls the quality (and cost) of the rescaling
@param callback function to call with the planar read result
@param context passed to callback
*/
using ReadPixelsCallbackYUV420 = void(ReadPixelsContext, const void* data[3],
size_t rowBytes[3]);
void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
int dstW, int dstH, RescaleGamma rescaleGamma,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback, ReadPixelsContext);
ReadPixelsCallback callback,
ReadPixelsContext);
/** Legacy version of asyncRescaleAndReadPixelsYUV420() that passes data directly to the
callback rather than using AsyncReadResult. The data is only valid during the lifetime of
the callback.
Deprecated.
*/
using LegacyReadPixelsCallbackYUV420 = void(ReadPixelsContext, const void* data[3],
size_t rowBytes[3]);
void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
int dstW, int dstH,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
LegacyReadPixelsCallbackYUV420 callback,
ReadPixelsContext);
/** Copies SkRect of pixels from the src SkPixmap to the SkSurface.

View File

@ -21,6 +21,7 @@
class GrAtlasManager;
class GrBackendSemaphore;
class GrCaps;
class GrClientMappedBufferManager;
class GrContextPriv;
class GrContextThreadSafeProxy;
class GrFragmentProcessor;
@ -471,6 +472,13 @@ public:
GrBackendTexture createBackendTexture(const SkPixmap srcData[], int numLevels,
GrRenderable, GrProtected);
// Helper version of above for a single level.
GrBackendTexture createBackendTexture(const SkPixmap& srcData,
GrRenderable renderable,
GrProtected isProtected) {
return this->createBackendTexture(&srcData, 1, renderable, isProtected);
}
void deleteBackendTexture(GrBackendTexture);
// This interface allows clients to pre-compile shaders and populate the runtime program cache.
@ -525,16 +533,11 @@ private:
GrContextOptions::PersistentCache* fPersistentCache;
GrContextOptions::ShaderErrorHandler* fShaderErrorHandler;
std::unique_ptr<GrClientMappedBufferManager> fMappedBufferManager;
// TODO: have the GrClipStackClip use renderTargetContexts and rm this friending
friend class GrContextPriv;
/**
* These functions create premul <-> unpremul effects, using the specialized round-trip effects
* from GrConfigConversionEffect.
*/
std::unique_ptr<GrFragmentProcessor> createPMToUPMEffect(std::unique_ptr<GrFragmentProcessor>);
std::unique_ptr<GrFragmentProcessor> createUPMToPMEffect(std::unique_ptr<GrFragmentProcessor>);
typedef GrRecordingContext INHERITED;
};

View File

@ -465,8 +465,8 @@ static void draw_dag(SkCanvas* canvas, sk_sp<SkImageFilter> filter,
// provided CTM during draw_node calls.
FilterNode dag = build_dag(ctm, rect, filter.get());
sk_sp<SkSurface> nodeSurface = canvas->makeSurface(
canvas->imageInfo().makeWH(surfaceSize.width(), surfaceSize.height()));
sk_sp<SkSurface> nodeSurface =
canvas->makeSurface(canvas->imageInfo().makeDimensions(surfaceSize));
draw_dag(canvas, nodeSurface.get(), dag);
canvas->restore();

View File

@ -23,7 +23,7 @@ sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> cod
if (!codec) {
return nullptr;
}
auto info = codec->getInfo().makeWH(scaledSize.width(), scaledSize.height());
auto info = codec->getInfo().makeDimensions(scaledSize);
return Make(std::move(codec), info, cropRect, std::move(postProcess));
}
@ -40,7 +40,7 @@ sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> cod
|| scaledSize.height() >= decodeInfo.height()) {
// Only libwebp can decode to arbitrary smaller sizes.
auto dims = codec->getInfo().dimensions();
decodeInfo = decodeInfo.makeWH(dims.width(), dims.height());
decodeInfo = decodeInfo.makeDimensions(dims);
}
auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,

View File

@ -246,7 +246,7 @@ bool zero_rect(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
}
}
const SkImageInfo info = dstInfo.makeWH(prevRect.width(), prevRect.height());
const SkImageInfo info = dstInfo.makeDimensions(prevRect.size());
const size_t bpp = dstInfo.bytesPerPixel();
const size_t offset = prevRect.x() * bpp + prevRect.y() * rowBytes;
void* eraseDst = SkTAddOffset<void>(pixels, offset);

View File

@ -101,7 +101,7 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
int scaledSubsetWidth = info.width();
int scaledSubsetHeight = info.height();
const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.height());
const SkImageInfo scaledInfo = info.makeDimensions(scaledSize);
{
// Although startScanlineDecode expects the bottom and top to match the
@ -211,7 +211,7 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
const int startY = samplingOffsetY + subsetY;
const int dstHeight = info.height();
const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height());
const SkImageInfo nativeInfo = info.makeDimensions(nativeSize);
{
// Although startScanlineDecode expects the bottom and top to match the

View File

@ -451,7 +451,7 @@ bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
SkBitmap dst;
dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes());
dst.setInfo(this->info().makeDimensions(r.size()), this->rowBytes());
dst.setIsVolatile(this->isVolatile());
if (fPixelRef) {
@ -492,7 +492,7 @@ bool SkBitmap::writePixels(const SkPixmap& src, int dstX, int dstY) {
}
void* dstPixels = this->getAddr(rec.fX, rec.fY);
const SkImageInfo dstInfo = this->info().makeWH(rec.fInfo.width(), rec.fInfo.height());
const SkImageInfo dstInfo = this->info().makeDimensions(rec.fInfo.dimensions());
SkConvertPixels(dstInfo, dstPixels, this->rowBytes(), rec.fInfo, rec.fPixels, rec.fRowBytes);
this->notifyPixelsChanged();
return true;

View File

@ -149,7 +149,7 @@ bool SkReadPixelsRec::trim(int srcWidth, int srcHeight) {
// we negate and add them so UBSAN (pointer-overflow) doesn't get confused.
fPixels = ((char*)fPixels + -y*fRowBytes + -x*fInfo.bytesPerPixel());
// the intersect may have shrunk info's logical size
fInfo = fInfo.makeWH(srcR.width(), srcR.height());
fInfo = fInfo.makeDimensions(srcR.size());
fX = srcR.x();
fY = srcR.y();
@ -186,7 +186,7 @@ bool SkWritePixelsRec::trim(int dstWidth, int dstHeight) {
// we negate and add them so UBSAN (pointer-overflow) doesn't get confused.
fPixels = ((const char*)fPixels + -y*fRowBytes + -x*fInfo.bytesPerPixel());
// the intersect may have shrunk info's logical size
fInfo = fInfo.makeWH(dstR.width(), dstR.height());
fInfo = fInfo.makeDimensions(dstR.size());
fX = dstR.x();
fY = dstR.y();

View File

@ -76,7 +76,7 @@ bool SkPixmap::extractSubset(SkPixmap* result, const SkIRect& subset) const {
const size_t bpp = fInfo.bytesPerPixel();
pixels = (const uint8_t*)fPixels + r.fTop * fRowBytes + r.fLeft * bpp;
}
result->reset(fInfo.makeWH(r.width(), r.height()), pixels, fRowBytes);
result->reset(fInfo.makeDimensions(r.size()), pixels, fRowBytes);
return true;
}
@ -163,7 +163,7 @@ bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t ds
}
const void* srcPixels = this->addr(rec.fX, rec.fY);
const SkImageInfo srcInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
const SkImageInfo srcInfo = fInfo.makeDimensions(rec.fInfo.dimensions());
SkConvertPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, srcInfo, srcPixels, this->rowBytes());
return true;
}

View File

@ -346,7 +346,7 @@ sk_sp<SkSpecialImage> SkSpecialImage::CopyFromRaster(const SkIRect& subset,
}
SkBitmap tmp;
SkImageInfo info = bm.info().makeWH(subset.width(), subset.height());
SkImageInfo info = bm.info().makeDimensions(subset.size());
// As in MakeFromRaster, must force src to N32 for ImageFilters
if (!valid_for_imagefilters(bm.info())) {
info = info.makeColorType(kN32_SkColorType);

View File

@ -193,10 +193,10 @@ static bool intersect(SkPixmap* dst, SkPixmap* src, int srcDx, int srcDy) {
if (!sect.intersect(dstR, srcR)) {
return false;
}
*dst = SkPixmap(dst->info().makeWH(sect.width(), sect.height()),
*dst = SkPixmap(dst->info().makeDimensions(sect.size()),
dst->addr(sect.fLeft, sect.fTop),
dst->rowBytes());
*src = SkPixmap(src->info().makeWH(sect.width(), sect.height()),
*src = SkPixmap(src->info().makeDimensions(sect.size()),
src->addr(SkTMax(0, -srcDx), SkTMax(0, -srcDy)),
src->rowBytes());
return true;

View File

@ -0,0 +1,71 @@
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/GrClientMappedBufferManager.h"
#include <algorithm>
GrClientMappedBufferManager::GrClientMappedBufferManager(uint32_t contextID)
: fFinishedBufferInbox(contextID) {}
GrClientMappedBufferManager::~GrClientMappedBufferManager() {
this->process();
if (!fAbandoned) {
// If we're going down before we got the messages we go ahead and unmap all the buffers.
// It's up to the client to ensure that they aren't being accessed on another thread while
// this is happening (or afterwards on any thread).
for (auto& b : fClientHeldBuffers) {
b->unmap();
}
}
}
void GrClientMappedBufferManager::insert(sk_sp<GrGpuBuffer> b) {
SkDEBUGCODE(auto end = fClientHeldBuffers.end());
SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) == end);
fClientHeldBuffers.emplace_front(std::move(b));
}
void GrClientMappedBufferManager::process() {
SkSTArray<4, BufferFinishedMessage> messages;
fFinishedBufferInbox.poll(&messages);
if (!fAbandoned) {
for (auto& m : messages) {
this->remove(m.fBuffer);
m.fBuffer->unmap();
}
}
}
void GrClientMappedBufferManager::abandon() {
fAbandoned = true;
fClientHeldBuffers.clear();
}
void GrClientMappedBufferManager::remove(const sk_sp<GrGpuBuffer>& b) {
// There is no convenient remove only the first element that equals a value functionality in
// std::forward_list.
auto prev = fClientHeldBuffers.before_begin();
auto end = fClientHeldBuffers.end();
SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) != end);
for (auto cur = fClientHeldBuffers.begin(); cur != end; prev = cur++) {
if (*cur == b) {
fClientHeldBuffers.erase_after(prev);
break;
}
}
SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) == end);
}
//////////////////////////////////////////////////////////////////////////////
DECLARE_SKMESSAGEBUS_MESSAGE(GrClientMappedBufferManager::BufferFinishedMessage)
bool SkShouldPostMessageToBus(const GrClientMappedBufferManager::BufferFinishedMessage& m,
uint32_t msgBusUniqueID) {
return m.fInboxID == msgBusUniqueID;
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrClientMappedBufferManager_DEFINED
#define GrClientMappedBufferManager_DEFINED
#include "include/private/SkTArray.h"
#include "src/core/SkMessageBus.h"
#include "src/gpu/GrGpuBuffer.h"
#include <forward_list>
/**
* We sometimes hand clients objects that contain mapped GrGpuBuffers. The client may consume
* the mapped buffer on another thread. This object manages receiving messages that buffers are
* ready to be unmapped (on the direct GrContext's thread). It also handles cleaning up mapped
* buffers if the GrContext is destroyed before the client has finished with the buffer.
*
* Buffers are first registered using insert() before being passed the client. process() should be
* called periodically on the direct GrContext thread to poll for messages and process them.
*/
class GrClientMappedBufferManager final {
public:
/**
* The message type that internal users of this should post to unmap the buffer.
* Set fInboxID to inboxID(). fBuffer must have been previously passed to insert().
*/
struct BufferFinishedMessage {
sk_sp<GrGpuBuffer> fBuffer;
uint32_t fInboxID;
};
using BufferFinishedMessageBus = SkMessageBus<BufferFinishedMessage>;
GrClientMappedBufferManager(uint32_t contextID);
GrClientMappedBufferManager(const GrClientMappedBufferManager&) = delete;
GrClientMappedBufferManager(GrClientMappedBufferManager&&) = delete;
~GrClientMappedBufferManager();
GrClientMappedBufferManager& operator=(const GrClientMappedBufferManager&) = delete;
GrClientMappedBufferManager& operator=(GrClientMappedBufferManager&&) = delete;
/** Initialize BufferFinishedMessage::fInboxID to this value. */
uint32_t inboxID() const { return fFinishedBufferInbox.uniqueID(); }
/**
* Let the manager know to expect a message with buffer 'b'. It's illegal for a buffer to be
* inserted again before it is unmapped by process().
*/
void insert(sk_sp<GrGpuBuffer> b);
/** Poll for messages and unmap any incoming buffers. */
void process();
/** Notifies the manager that the context has been abandoned. No more unmaps() will occur.*/
void abandon();
private:
BufferFinishedMessageBus::Inbox fFinishedBufferInbox;
std::forward_list<sk_sp<GrGpuBuffer>> fClientHeldBuffers;
bool fAbandoned = false;
void remove(const sk_sp<GrGpuBuffer>& b);
};
bool SkShouldPostMessageToBus(const GrClientMappedBufferManager::BufferFinishedMessage&,
uint32_t msgBusUniqueID);
#endif

View File

@ -5,13 +5,15 @@
* found in the LICENSE file.
*/
#include "include/gpu/GrContext.h"
#include "include/core/SkTraceMemoryDump.h"
#include "include/gpu/GrBackendSemaphore.h"
#include "include/gpu/GrContext.h"
#include "include/private/SkDeferredDisplayList.h"
#include "include/private/SkImageInfoPriv.h"
#include "src/core/SkMakeUnique.h"
#include "src/core/SkTaskGroup.h"
#include "src/gpu/GrClientMappedBufferManager.h"
#include "src/gpu/GrDrawingManager.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrMemoryPool.h"
@ -31,7 +33,6 @@
#include "src/gpu/text/GrTextContext.h"
#include "src/image/SkSurface_Gpu.h"
#include <atomic>
#include <unordered_map>
#define ASSERT_OWNED_PROXY(P) \
SkASSERT(!(P) || !((P)->peekTexture()) || (P)->peekTexture()->getContext() == this)
@ -77,6 +78,7 @@ bool GrContext::init(sk_sp<const GrCaps> caps, sk_sp<GrSkSLFPFactoryCache> FPFac
if (fGpu) {
fResourceCache = new GrResourceCache(this->caps(), this->singleOwner(), this->contextID());
fResourceProvider = new GrResourceProvider(fGpu.get(), fResourceCache, this->singleOwner());
fMappedBufferManager = skstd::make_unique<GrClientMappedBufferManager>(this->contextID());
}
if (fResourceCache) {
@ -113,6 +115,8 @@ void GrContext::abandonContext() {
INHERITED::abandonContext();
fMappedBufferManager->abandon();
fResourceProvider->abandon();
// Need to cleanup the drawing manager first so all the render targets
@ -124,6 +128,8 @@ void GrContext::abandonContext() {
fResourceCache->abandonAll();
fGpu->disconnect(GrGpu::DisconnectType::kAbandon);
fMappedBufferManager.reset();
}
void GrContext::releaseResourcesAndAbandonContext() {
@ -133,6 +139,8 @@ void GrContext::releaseResourcesAndAbandonContext() {
INHERITED::abandonContext();
fMappedBufferManager.reset();
fResourceProvider->abandon();
// Need to cleanup the drawing manager first so all the render targets
@ -184,6 +192,11 @@ void GrContext::performDeferredCleanup(std::chrono::milliseconds msNotUsed) {
ASSERT_SINGLE_OWNER
if (this->abandoned()) {
return;
}
fMappedBufferManager->process();
auto purgeTime = GrStdSteadyClock::now() - msNotUsed;
fResourceCache->purgeAsNeeded();

View File

@ -228,6 +228,10 @@ public:
return fContext->fShaderErrorHandler;
}
GrClientMappedBufferManager* clientMappedBufferManager() {
return fContext->fMappedBufferManager.get();
}
#if GR_TEST_UTILS
/** Reset GPU stats */
void resetGpuStats() const ;

View File

@ -13,6 +13,7 @@
#include "include/private/SkDeferredDisplayList.h"
#include "src/core/SkTTopoSort.h"
#include "src/gpu/GrAuditTrail.h"
#include "src/gpu/GrClientMappedBufferManager.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrCopyRenderTask.h"
#include "src/gpu/GrGpu.h"
@ -248,6 +249,7 @@ GrSemaphoresSubmitted GrDrawingManager::flush(GrSurfaceProxy* proxies[], int num
}
return GrSemaphoresSubmitted::kNo; // Can't flush while DDL recording
}
direct->priv().clientMappedBufferManager()->process();
GrGpu* gpu = direct->priv().getGpu();
if (!gpu) {

View File

@ -17,6 +17,7 @@
#include "src/core/SkDrawShadowInfo.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkLatticeIter.h"
#include "src/core/SkMakeUnique.h"
#include "src/core/SkMatrixPriv.h"
#include "src/core/SkRRectPriv.h"
#include "src/core/SkSurfacePriv.h"
@ -24,6 +25,7 @@
#include "src/gpu/GrAuditTrail.h"
#include "src/gpu/GrBlurUtils.h"
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrClientMappedBufferManager.h"
#include "src/gpu/GrColor.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrDataUtils.h"
@ -1509,18 +1511,18 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
SkFilterQuality rescaleQuality, ReadPixelsCallback callback, ReadPixelsContext context) {
auto direct = fContext->priv().asDirectContext();
if (!direct) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
auto dstCT = SkColorTypeToGrColorType(info.colorType());
// TODO: Support reading to gray.
if (dstCT == GrColorType::kUnknown ||
GrColorTypeComponentFlags(dstCT) & kGray_SkColorTypeComponentFlag) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height();
@ -1535,7 +1537,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
backendFormatOfFinalContext, dstCT);
// Fail if we can't read from the source surface's color type.
if (readInfo.fColorType == GrColorType::kUnknown) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
// Fail if read color type does not have all of dstCT's color channels and those missing color
@ -1544,7 +1546,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
uint32_t legalReadComponents = GrColorTypeComponentFlags(readInfo.fColorType);
uint32_t srcComponents = GrColorTypeComponentFlags(this->colorInfo().colorType());
if ((~legalReadComponents & dstComponents) & srcComponents) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
@ -1554,7 +1556,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
if (needsRescale) {
tempRTC = this->rescale(info, srcRect, rescaleGamma, rescaleQuality);
if (!tempRTC) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
@ -1570,7 +1572,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
// We flip or color convert by drawing and we don't currently support drawing to
// kPremul.
if (info.alphaType() == kUnpremul_SkAlphaType) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
sk_sp<GrTextureProxy> texProxy = sk_ref_sp(fRenderTargetProxy->asTextureProxy());
@ -1581,7 +1583,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
GrMipMapped::kNo, srcRect, SkBackingFit::kApprox,
SkBudgeted::kNo);
if (!texProxy) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height());
@ -1591,7 +1593,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
this->colorInfo().colorType(), info.refColorSpace(), 1, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin);
if (!tempRTC) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
tempRTC->drawTexture(GrNoClip(), std::move(texProxy), GrSamplerState::Filter::kNearest,
@ -1607,55 +1609,126 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
info.colorType(), callback, context);
}
class GrRenderTargetContext::AsyncReadResult : public SkSurface::AsyncReadResult {
public:
AsyncReadResult(uint32_t inboxID) : fInboxID(inboxID) {}
~AsyncReadResult() override {
for (int i = 0; i < fPlanes.count(); ++i) {
if (!fPlanes[i].fMappedBuffer) {
delete[] static_cast<const char*>(fPlanes[i].fData);
} else {
GrClientMappedBufferManager::BufferFinishedMessageBus::Post(
{std::move(fPlanes[i].fMappedBuffer), fInboxID});
}
}
}
int count() const override { return fPlanes.count(); }
const void* data(int i) const override { return fPlanes[i].fData; }
size_t rowBytes(int i) const override { return fPlanes[i].fRowBytes; }
bool addTransferResult(const PixelTransferResult& result,
SkISize size,
size_t rowBytes,
GrClientMappedBufferManager* manager) {
SkASSERT(!result.fTransferBuffer->isMapped());
const void* mappedData = result.fTransferBuffer->map();
if (!mappedData) {
return false;
}
if (result.fPixelConverter) {
std::unique_ptr<char[]> convertedData(new char[rowBytes * size.height()]);
result.fPixelConverter(convertedData.get(), mappedData);
this->addCpuPlane(std::move(convertedData), rowBytes);
result.fTransferBuffer->unmap();
} else {
manager->insert(result.fTransferBuffer);
this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
}
return true;
}
void addCpuPlane(std::unique_ptr<const char[]> data, size_t rowBytes) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
fPlanes.emplace_back(data.release(), rowBytes, nullptr);
}
private:
void addMappedPlane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> mappedBuffer) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
SkASSERT(mappedBuffer);
SkASSERT(mappedBuffer->isMapped());
fPlanes.emplace_back(data, rowBytes, std::move(mappedBuffer));
}
struct Plane {
Plane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> buffer)
: fData(data), fRowBytes(rowBytes), fMappedBuffer(std::move(buffer)) {}
const void* fData;
size_t fRowBytes;
// If this is null then fData is heap alloc and must be delete[]ed as const char[].
sk_sp<GrGpuBuffer> fMappedBuffer;
};
SkSTArray<3, Plane> fPlanes;
uint32_t fInboxID;
};
void GrRenderTargetContext::asyncReadPixels(const SkIRect& rect, SkColorType colorType,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
auto directContext = fContext->priv().asDirectContext();
SkASSERT(directContext);
auto mappedBufferManager = directContext->priv().clientMappedBufferManager();
auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
if (!transferResult.fTransferBuffer) {
SkAutoPixmapStorage pm;
auto ii = SkImageInfo::Make(rect.size(), colorType,
this->colorInfo().alphaType(),
this->colorInfo().refColorSpace());
pm.alloc(ii);
auto result = skstd::make_unique<AsyncReadResult>(0);
std::unique_ptr<char[]> data(new char[ii.computeMinByteSize()]);
SkPixmap pm(ii, data.get(), ii.minRowBytes());
result->addCpuPlane(std::move(data), pm.rowBytes());
if (!this->readPixels(ii, pm.writable_addr(), pm.rowBytes(), {rect.fLeft, rect.fTop})) {
callback(context, nullptr, 0);
callback(context, nullptr);
}
callback(context, pm.addr(), pm.rowBytes());
callback(context, std::move(result));
return;
}
struct FinishContext {
ReadPixelsCallback* fClientCallback;
ReadPixelsContext fClientContext;
int fW, fH;
SkISize fSize;
SkColorType fColorType;
GrClientMappedBufferManager* fMappedBufferManager;
PixelTransferResult fTransferResult;
};
// Assumption is that the caller would like to flush. We could take a parameter or require an
// explicit flush from the caller. We'd have to have a way to defer attaching the finish
// callback to GrGpu until after the next flush that flushes our op list, though.
auto* finishContext = new FinishContext{callback, context, rect.width(),
rect.height(), colorType, std::move(transferResult)};
auto* finishContext = new FinishContext{callback,
context,
rect.size(),
colorType,
mappedBufferManager,
std::move(transferResult)};
auto finishCallback = [](GrGpuFinishedContext c) {
const auto* context = reinterpret_cast<const FinishContext*>(c);
const void* data = context->fTransferResult.fTransferBuffer->map();
if (!data) {
(*context->fClientCallback)(context->fClientContext, nullptr, 0);
delete context;
return;
auto result = skstd::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
size_t rowBytes = context->fSize.width() * SkColorTypeBytesPerPixel(context->fColorType);
if (!result->addTransferResult(context->fTransferResult, context->fSize, rowBytes,
context->fMappedBufferManager)) {
result.reset();
}
std::unique_ptr<char[]> tmp;
size_t rowBytes = context->fW * SkColorTypeBytesPerPixel(context->fColorType);
if (context->fTransferResult.fPixelConverter) {
tmp.reset(new char[rowBytes * context->fH]);
context->fTransferResult.fPixelConverter(tmp.get(), data);
data = tmp.get();
}
(*context->fClientCallback)(context->fClientContext, data, rowBytes);
(*context->fClientCallback)(context->fClientContext, std::move(result));
delete context;
};
GrFlushInfo flushInfo;
@ -1664,37 +1737,40 @@ void GrRenderTargetContext::asyncReadPixels(const SkIRect& rect, SkColorType col
this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
}
void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
SkASSERT((dstW % 2 == 0) && (dstH % 2 == 0));
SkASSERT(!dstSize.isZero());
SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
auto direct = fContext->priv().asDirectContext();
if (!direct) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
callback(context, nullptr, nullptr);
return;
}
if (dstW & 0x1) {
callback(context, nullptr);
return;
}
int x = srcRect.fLeft;
int y = srcRect.fTop;
std::unique_ptr<GrRenderTargetContext> tempRTC;
bool needsRescale = srcRect.width() != dstW || srcRect.height() != dstH;
bool needsRescale = srcRect.size() != dstSize;
if (needsRescale) {
// We assume the caller wants kPremul. There is no way to indicate a preference.
auto info = SkImageInfo::Make(dstW, dstH, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
auto info = SkImageInfo::Make(dstSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
dstColorSpace);
// TODO: Incorporate the YUV conversion into last pass of rescaling.
tempRTC = this->rescale(info, srcRect, rescaleGamma, rescaleQuality);
if (!tempRTC) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace()));
@ -1709,15 +1785,16 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
sk_sp<GrTextureProxy> texProxy = this->asTextureProxyRef();
// TODO: Do something if the input is not a texture already.
if (!texProxy) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
SkRect srcRectToDraw = SkRect::Make(srcRect);
tempRTC = direct->priv().makeDeferredRenderTargetContext(
SkBackingFit::kApprox, dstW, dstH, this->colorInfo().colorType(), dstColorSpace,
1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
SkBackingFit::kApprox, dstSize.width(), dstSize.height(),
this->colorInfo().colorType(), dstColorSpace, 1, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin);
if (!tempRTC) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
tempRTC->drawTexture(GrNoClip(), std::move(texProxy), GrSamplerState::Filter::kNearest,
@ -1731,20 +1808,22 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
auto srcProxy = tempRTC ? tempRTC->asTextureProxyRef() : this->asTextureProxyRef();
// TODO: Do something if the input is not a texture already.
if (!srcProxy) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
auto yRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
SkBackingFit::kApprox, dstW, dstH, GrColorType::kAlpha_8, dstColorSpace, 1,
GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
SkBackingFit::kApprox, dstSize.width(), dstSize.height(), GrColorType::kAlpha_8,
dstColorSpace, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
int halfW = dstSize.width()/2;
int halfH = dstSize.height()/2;
auto uRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
SkBackingFit::kApprox, dstW / 2, dstH / 2, GrColorType::kAlpha_8, dstColorSpace, 1,
SkBackingFit::kApprox, halfW, halfH, GrColorType::kAlpha_8, dstColorSpace, 1,
GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
auto vRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
SkBackingFit::kApprox, dstW / 2, dstH / 2, GrColorType::kAlpha_8, dstColorSpace, 1,
SkBackingFit::kApprox, halfW, halfH, GrColorType::kAlpha_8, dstColorSpace, 1,
GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
if (!yRTC || !uRTC || !vRTC) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
@ -1787,8 +1866,8 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
auto texMatrix = SkMatrix::MakeTrans(x, y);
SkRect dstRectY = SkRect::MakeWH(dstW, dstH);
SkRect dstRectUV = SkRect::MakeWH(dstW / 2, dstH / 2);
SkRect dstRectY = SkRect::Make(dstSize);
SkRect dstRectUV = SkRect::MakeWH(halfW, halfH);
// This matrix generates (r,g,b,a) = (0, 0, 0, y)
float yM[20];
@ -1804,7 +1883,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
auto yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(yRTC->width(), yRTC->height()));
if (!yTransfer.fTransferBuffer) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
@ -1823,7 +1902,7 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
auto uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(uRTC->width(), uRTC->height()));
if (!uTransfer.fTransferBuffer) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
@ -1841,14 +1920,15 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
auto vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8,
SkIRect::MakeWH(vRTC->width(), vRTC->height()));
if (!vTransfer.fTransferBuffer) {
callback(context, nullptr, nullptr);
callback(context, nullptr);
return;
}
struct FinishContext {
ReadPixelsCallbackYUV420* fClientCallback;
ReadPixelsCallback* fClientCallback;
ReadPixelsContext fClientContext;
int fW, fH;
GrClientMappedBufferManager* fMappedBufferManager;
SkISize fSize;
PixelTransferResult fYTransfer;
PixelTransferResult fUTransfer;
PixelTransferResult fVTransfer;
@ -1858,56 +1938,34 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
// callback to GrGpu until after the next flush that flushes our op list, though.
auto* finishContext = new FinishContext{callback,
context,
dstW,
dstH,
direct->priv().clientMappedBufferManager(),
dstSize,
std::move(yTransfer),
std::move(uTransfer),
std::move(vTransfer)};
auto finishCallback = [](GrGpuFinishedContext c) {
const auto* context = reinterpret_cast<const FinishContext*>(c);
const void* y = context->fYTransfer.fTransferBuffer->map();
const void* u = context->fUTransfer.fTransferBuffer->map();
const void* v = context->fVTransfer.fTransferBuffer->map();
if (!y || !u || !v) {
if (y) {
context->fYTransfer.fTransferBuffer->unmap();
}
if (u) {
context->fUTransfer.fTransferBuffer->unmap();
}
if (v) {
context->fVTransfer.fTransferBuffer->unmap();
}
(*context->fClientCallback)(context->fClientContext, nullptr, 0);
auto result = skstd::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID());
auto manager = context->fMappedBufferManager;
size_t rowBytes = SkToSizeT(context->fSize.width());
if (!result->addTransferResult(context->fYTransfer, context->fSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
size_t w = SkToSizeT(context->fW);
size_t h = SkToSizeT(context->fH);
std::unique_ptr<uint8_t[]> yTemp;
if (context->fYTransfer.fPixelConverter) {
yTemp.reset(new uint8_t[w * h]);
context->fYTransfer.fPixelConverter(yTemp.get(), y);
y = yTemp.get();
rowBytes /= 2;
SkISize uvSize = {context->fSize.width()/2, context->fSize.height()/2};
if (!result->addTransferResult(context->fUTransfer, uvSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
std::unique_ptr<uint8_t[]> uTemp;
if (context->fUTransfer.fPixelConverter) {
uTemp.reset(new uint8_t[w / 2 * h / 2]);
context->fUTransfer.fPixelConverter(uTemp.get(), u);
u = uTemp.get();
if (!result->addTransferResult(context->fVTransfer, uvSize, rowBytes, manager)) {
(*context->fClientCallback)(context->fClientContext, nullptr);
delete context;
return;
}
std::unique_ptr<uint8_t[]> vTemp;
if (context->fVTransfer.fPixelConverter) {
vTemp.reset(new uint8_t[w / 2 * h / 2]);
context->fVTransfer.fPixelConverter(vTemp.get(), v);
v = vTemp.get();
}
const void* data[] = {y, u, v};
size_t rowBytes[] = {w, w / 2, w / 2};
(*context->fClientCallback)(context->fClientContext, data, rowBytes);
context->fYTransfer.fTransferBuffer->unmap();
context->fUTransfer.fTransferBuffer->unmap();
context->fVTransfer.fTransferBuffer->unmap();
(*context->fClientCallback)(context->fClientContext, std::move(result));
delete context;
};
GrFlushInfo flushInfo;

View File

@ -441,7 +441,6 @@ public:
void drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>, const SkRect& bounds);
using ReadPixelsCallback = SkSurface::ReadPixelsCallback;
using ReadPixelsCallbackYUV420 = SkSurface::ReadPixelsCallbackYUV420;
using ReadPixelsContext = SkSurface::ReadPixelsContext;
using RescaleGamma = SkSurface::RescaleGamma;
@ -451,10 +450,12 @@ public:
ReadPixelsCallback callback, ReadPixelsContext context);
// GPU implementation for SkSurface::asyncRescaleAndReadPixelsYUV420.
void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
int dstW, int dstH, RescaleGamma rescaleGamma,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback,
ReadPixelsCallback callback,
ReadPixelsContext context);
/**
@ -622,6 +623,8 @@ private:
bool SK_WARN_UNUSED_RESULT setupDstProxy(const GrClip&, const GrOp& op,
GrXferProcessor::DstProxy* result);
class AsyncReadResult;
// The async read step of asyncRescaleAndReadPixels()
void asyncReadPixels(const SkIRect& rect, SkColorType colorType, ReadPixelsCallback callback,
ReadPixelsContext context);

View File

@ -83,7 +83,7 @@ SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* su
subset = &bounds;
}
fInfo = info.makeWH(subset->width(), subset->height());
fInfo = info.makeDimensions(subset->size());
fOrigin = SkIPoint::Make(subset->x(), subset->y());
if (colorType || colorSpace) {
if (colorType) {
@ -321,7 +321,7 @@ sk_sp<SkImage> SkImage::DecodeToRaster(const void* encoded, size_t length, const
if (!SkIRect::MakeWH(info.width(), info.height()).contains(*subset)) {
return nullptr;
}
info = info.makeWH(subset->width(), subset->height());
info = info.makeDimensions(subset->size());
origin = {subset->x(), subset->y()};
}

View File

@ -231,7 +231,7 @@ void SkImage_Raster::onUnpinAsTexture(GrContext* ctx) const {
#endif
sk_sp<SkImage> SkImage_Raster::onMakeSubset(GrRecordingContext*, const SkIRect& subset) const {
SkImageInfo info = fBitmap.info().makeWH(subset.width(), subset.height());
SkImageInfo info = fBitmap.info().makeDimensions(subset.size());
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(info)) {
return nullptr;

View File

@ -12,6 +12,7 @@
#include "include/gpu/GrBackendSurface.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkImagePriv.h"
#include "src/core/SkMakeUnique.h"
#include "src/image/SkSurface_Base.h"
static SkPixelGeometry compute_default_geometry() {
@ -144,7 +145,7 @@ void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const
ii = ii.makeColorType(this->getCanvas()->imageInfo().colorType());
linearSurf = this->makeSurface(ii);
if (!linearSurf) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
}
@ -182,7 +183,7 @@ void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const
}
auto next = this->makeSurface(ii);
if (!next) {
callback(context, nullptr, 0);
callback(context, nullptr);
return;
}
next->getCanvas()->drawImageRect(
@ -195,22 +196,35 @@ void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const
constraint = SkCanvas::kFast_SrcRectConstraint;
}
SkAutoPixmapStorage pm;
pm.alloc(info);
size_t rowBytes = info.minRowBytes();
std::unique_ptr<char[]> data(new char[info.height() * rowBytes]);
SkPixmap pm(info, data.get(), rowBytes);
if (src->readPixels(pm, srcX, srcY)) {
callback(context, pm.addr(), pm.rowBytes());
class Result : public AsyncReadResult {
public:
Result(std::unique_ptr<const char[]> data, size_t rowBytes)
: fData(std::move(data)), fRowBytes(rowBytes) {}
int count() const override { return 1; }
const void* data(int i) const override { return fData.get(); }
size_t rowBytes(int i) const override { return fRowBytes; }
private:
std::unique_ptr<const char[]> fData;
size_t fRowBytes;
};
callback(context, skstd::make_unique<Result>(std::move(data), rowBytes));
} else {
callback(context, nullptr, 0);
callback(context, nullptr);
}
}
void SkSurface_Base::onAsyncRescaleAndReadPixelsYUV420(
SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
const SkISize& dstSize, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallback callback, ReadPixelsContext context) {
// TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
// call client's callback.
callback(context, nullptr, nullptr);
callback(context, nullptr);
}
bool SkSurface_Base::outstandingImageSnapshot() const {
@ -347,30 +361,119 @@ bool SkSurface::readPixels(const SkBitmap& bitmap, int srcX, int srcY) {
return bitmap.peekPixels(&pm) && this->readPixels(pm, srcX, srcY);
}
void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallback callback, ReadPixelsContext context) {
// Stuff to keep the legacy async readback APIs working on top of the new implementation.
namespace {
struct BridgeContext {
SkSurface::ReadPixelsContext fClientContext;
SkSurface::LegacyReadPixelsCallback* fClientCallback;
};
struct BridgeContextYUV420 {
SkSurface::ReadPixelsContext fClientContext;
SkSurface::LegacyReadPixelsCallbackYUV420* fClientCallback;
};
} // anonymous namespace
static void bridge_callback(SkSurface::ReadPixelsContext context,
std::unique_ptr<const SkSurface::AsyncReadResult> result) {
auto bridgeContext = static_cast<const BridgeContext*>(context);
if (!result || result->count() != 1) {
bridgeContext->fClientCallback(bridgeContext->fClientContext, nullptr, 0);
} else {
bridgeContext->fClientCallback(bridgeContext->fClientContext, result->data(0),
result->rowBytes(0));
}
delete bridgeContext;
}
static void bridge_callback_yuv420(SkSurface::ReadPixelsContext context,
std::unique_ptr<const SkSurface::AsyncReadResult> result) {
auto bridgeContext = static_cast<const BridgeContextYUV420*>(context);
if (!result || result->count() != 3) {
bridgeContext->fClientCallback(bridgeContext->fClientContext, nullptr, 0);
} else {
const void* data[] = {result->data(0), result->data(1), result->data(2)};
size_t rowBytes[] = {result->rowBytes(0), result->rowBytes(1), result->rowBytes(2)};
bridgeContext->fClientCallback(bridgeContext->fClientContext, data, rowBytes);
}
delete bridgeContext;
}
void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
LegacyReadPixelsCallback callback,
ReadPixelsContext context) {
if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
!SkImageInfoIsValid(info)) {
callback(context, nullptr, 0);
return;
}
asSB(this)->onAsyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality, callback,
context);
auto bridgeContext = new BridgeContext{context, callback};
asSB(this)->onAsyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality,
bridge_callback, bridgeContext);
}
void SkSurface::asyncRescaleAndReadPixelsYUV420(
SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
!SkImageInfoIsValid(info)) {
callback(context, nullptr);
return;
}
asSB(this)->onAsyncRescaleAndReadPixels(
info, srcRect, rescaleGamma, rescaleQuality, callback, context);
}
void SkSurface::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
int dstW, int dstH,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
LegacyReadPixelsCallbackYUV420 callback,
ReadPixelsContext context) {
if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || (dstW & 0b1) ||
(dstH & 0b1)) {
callback(context, nullptr, nullptr);
return;
}
asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace, std::move(dstColorSpace), srcRect,
dstW, dstH, rescaleGamma, rescaleQuality,
callback, context);
auto bridgeContext = new BridgeContextYUV420{context, callback};
asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
std::move(dstColorSpace), srcRect,
{dstW, dstH},
rescaleGamma,
rescaleQuality,
bridge_callback_yuv420,
bridgeContext);
}
void SkSurface::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
(dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
callback(context, nullptr);
return;
}
asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
std::move(dstColorSpace),
srcRect,
dstSize,
rescaleGamma,
rescaleQuality,
callback,
context);
}
void SkSurface::writePixels(const SkPixmap& pmap, int x, int y) {

View File

@ -51,21 +51,23 @@ public:
/**
* Default implementation does a rescale/read and then calls the callback.
*/
virtual void onAsyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context);
virtual void onAsyncRescaleAndReadPixels(const SkImageInfo&,
const SkIRect& srcRect,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback,
ReadPixelsContext);
/**
* Default implementation does a rescale/read/yuv conversion and then calls the callback.
*/
virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
virtual void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect, int dstW, int dstH,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback,
ReadPixelsContext context);
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma,
SkFilterQuality,
ReadPixelsCallback,
ReadPixelsContext);
/**
* Default implementation:

View File

@ -132,7 +132,8 @@ void SkSurface_Gpu::onWritePixels(const SkPixmap& src, int x, int y) {
fDevice->writePixels(src, x, y);
}
void SkSurface_Gpu::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
void SkSurface_Gpu::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
const SkIRect& srcRect,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
@ -141,13 +142,23 @@ void SkSurface_Gpu::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const S
rtc->asyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality, callback, context);
}
void SkSurface_Gpu::onAsyncRescaleAndReadPixelsYUV420(
SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
void SkSurface_Gpu::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallback callback,
ReadPixelsContext context) {
auto* rtc = this->fDevice->accessRenderTargetContext();
rtc->asyncRescaleAndReadPixelsYUV420(yuvColorSpace, std::move(dstColorSpace), srcRect, dstW,
dstH, rescaleGamma, rescaleQuality, callback, context);
rtc->asyncRescaleAndReadPixelsYUV420(yuvColorSpace,
std::move(dstColorSpace),
srcRect,
dstSize,
rescaleGamma,
rescaleQuality,
callback,
context);
}
// Create a new render target and, if necessary, copy the contents of the old

View File

@ -40,10 +40,11 @@ public:
ReadPixelsContext context) override;
void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
sk_sp<SkColorSpace> dstColorSpace,
const SkIRect& srcRect, int dstW, int dstH,
const SkIRect& srcRect,
const SkISize& dstSize,
RescaleGamma rescaleGamma,
SkFilterQuality rescaleQuality,
ReadPixelsCallbackYUV420 callback,
ReadPixelsCallback callback,
ReadPixelsContext context) override;
void onCopyOnWrite(ContentChangeMode) override;

View File

@ -111,7 +111,7 @@ sk_sp<SkImage> SkSurface_Raster::onNewImageSnapshot(const SkIRect* subset) {
if (subset) {
SkASSERT(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()).contains(*subset));
SkBitmap dst;
dst.allocPixels(fBitmap.info().makeWH(subset->width(), subset->height()));
dst.allocPixels(fBitmap.info().makeDimensions(subset->size()));
SkAssertResult(fBitmap.readPixels(dst.pixmap(), subset->left(), subset->top()));
dst.setImmutable(); // key, so MakeFromBitmap doesn't make a copy of the buffer
return SkImage::MakeFromBitmap(dst);

View File

@ -291,7 +291,7 @@ DEF_TEST(AndroidCodec_sampledOrientation, r) {
options.fSampleSize = sampleSize;
SkBitmap bm;
auto info = androidCodec->getInfo().makeWH(sampledDims.width(), sampledDims.height());
auto info = androidCodec->getInfo().makeDimensions(sampledDims);
bm.allocPixels(info);
auto result = androidCodec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &options);

View File

@ -192,8 +192,7 @@ DEF_TEST(BitmapReadPixels, reporter) {
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
clear_4x4_pixels(dstPixels);
dstInfo = dstInfo.makeWH(gRec[i].fRequestedDstSize.width(),
gRec[i].fRequestedDstSize.height());
dstInfo = dstInfo.makeDimensions(gRec[i].fRequestedDstSize);
bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes,
gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y());

View File

@ -415,7 +415,7 @@ DEF_TEST(AndroidCodec_animated, r) {
for (int sampleSize : { 8, 32, 100 }) {
auto dimensions = codec->codec()->getScaledDimensions(1.0f / sampleSize);
info = info.makeWH(dimensions.width(), dimensions.height());
info = info.makeDimensions(dimensions);
SkBitmap bm;
bm.allocPixels(info);

View File

@ -407,7 +407,7 @@ static void check(skiatest::Reporter* r,
const bool supported = codec->getValidSubset(&subset);
REPORTER_ASSERT(r, supported == supportsSubsetDecoding);
SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
SkImageInfo subsetInfo = info.makeDimensions(subset.size());
SkBitmap bm;
bm.allocPixels(subsetInfo);
const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts);
@ -594,7 +594,7 @@ static void test_dimensions(skiatest::Reporter* r, const char path[]) {
// Scale the output dimensions
SkISize scaledDims = codec->getSampledDimensions(sampleSize);
SkImageInfo scaledInfo = codec->getInfo()
.makeWH(scaledDims.width(), scaledDims.height())
.makeDimensions(scaledDims)
.makeColorType(kN32_SkColorType);
// Set up for the decode
@ -1335,7 +1335,7 @@ DEF_TEST(Codec_reusePng, r) {
SkAndroidCodec::AndroidOptions opts;
opts.fSampleSize = 5;
auto size = codec->getSampledDimensions(opts.fSampleSize);
auto info = codec->getInfo().makeWH(size.fWidth, size.fHeight).makeColorType(kN32_SkColorType);
auto info = codec->getInfo().makeDimensions(size).makeColorType(kN32_SkColorType);
SkBitmap bm;
bm.allocPixels(info);
auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
@ -1709,7 +1709,7 @@ DEF_TEST(Codec_78329453, r) {
// but the ones tested by DM happen to not.
constexpr int kSampleSize = 19;
const auto size = codec->getSampledDimensions(kSampleSize);
auto info = codec->getInfo().makeWH(size.width(), size.height());
auto info = codec->getInfo().makeDimensions(size);
SkBitmap bm;
bm.allocPixels(info);
bm.eraseColor(SK_ColorTRANSPARENT);

View File

@ -645,16 +645,33 @@ static int min_rgb_channel_bits(SkColorType ct) {
SK_ABORT("Unexpected color type.");
}
namespace {
struct AsyncContext {
bool fCalled = false;
std::unique_ptr<const SkSurface::AsyncReadResult> fResult;
};
} // anonymous namespace
// Making this a lambda in the test functions caused:
// "error: cannot compile this forwarded non-trivially copyable parameter yet"
// on x86/Win/Clang bot, referring to 'result'.
static void async_callback(void* c, std::unique_ptr<const SkSurface::AsyncReadResult> result) {
auto context = static_cast<AsyncContext*>(c);
context->fResult = std::move(result);
context->fCalled = true;
};
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) {
struct Context {
struct LegacyContext {
SkPixmap* fPixmap = nullptr;
bool fSuceeded = false;
bool fCalled = false;
};
auto callback = [](SkSurface::ReleaseContext context, const void* data, size_t rowBytes) {
auto* pm = static_cast<Context*>(context)->fPixmap;
static_cast<Context*>(context)->fCalled = true;
if ((static_cast<Context*>(context)->fSuceeded = SkToBool(data))) {
auto legacy_callback = [](SkSurface::ReleaseContext context, const void* data,
size_t rowBytes) {
auto* pm = static_cast<LegacyContext*>(context)->fPixmap;
static_cast<LegacyContext*>(context)->fCalled = true;
if ((static_cast<LegacyContext*>(context)->fSuceeded = SkToBool(data))) {
auto dst = static_cast<char*>(pm->writable_addr());
const auto* src = static_cast<const char*>(data);
for (int y = 0; y < pm->height(); ++y, src += rowBytes, dst += pm->rowBytes()) {
@ -703,76 +720,222 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) {
SkIRect::MakeLTRB(1, 2, kW - 3, kH - 4),
SkIRect::MakeXYWH(1, 1, 0, 0),
SkIRect::MakeWH(kW + 1, kH / 2)}) {
SkAutoPixmapStorage result;
Context context;
context.fPixmap = &result;
info = SkImageInfo::Make(rect.size(), readCT, kPremul_SkAlphaType, readCS);
result.alloc(info);
memset(result.writable_addr(), 0xAB, result.computeByteSize());
// Rescale quality and linearity don't matter since we're doing a non-
// scaling readback.
surf->asyncRescaleAndReadPixels(info, rect, SkSurface::RescaleGamma::kSrc,
kNone_SkFilterQuality, callback, &context);
while (!context.fCalled) {
ctxInfo.grContext()->checkAsyncWorkCompletion();
}
if (rect.isEmpty() || !SkIRect::MakeWH(kW, kH).contains(rect)) {
REPORTER_ASSERT(reporter, !context.fSuceeded);
}
bool didCSConversion =
!SkColorSpace::Equals(readCS.get(), surf->imageInfo().colorSpace());
if (context.fSuceeded) {
REPORTER_ASSERT(reporter, readCT != kUnknown_SkColorType &&
!rect.isEmpty());
} else {
// TODO: Support reading to kGray, support kRGB_101010x at all in GPU.
auto surfBounds = SkIRect::MakeWH(surf->width(), surf->height());
if (readCT != kUnknown_SkColorType && readCT != kGray_8_SkColorType &&
readCT != kRGB_101010x_SkColorType && !rect.isEmpty() &&
surfBounds.contains(rect)) {
ERRORF(reporter,
"Async read failed. Surf Color Type: %s, Read CT: %s,"
"Rect [%d, %d, %d, %d], origin: %d, CS conversion: %d\n",
ToolUtils::colortype_name(surfCT),
ToolUtils::colortype_name(readCT),
rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, origin,
didCSConversion);
for (bool legacy : {false, true}) {
SkPixmap result;
std::unique_ptr<char[]> tempPixels;
info = SkImageInfo::Make(rect.size(), readCT, kPremul_SkAlphaType,
readCS);
// Rescale quality and linearity don't matter since we're doing a non-
// scaling readback.
static constexpr auto kQuality = kNone_SkFilterQuality;
static constexpr auto kGamma = SkSurface::RescaleGamma::kSrc;
bool succeeded = false;
// This holds the pixel results and so must live until comparisons are
// finished.
AsyncContext asyncContext;
if (legacy) {
LegacyContext context;
tempPixels.reset(new char[info.computeMinByteSize()]);
result.reset(info, tempPixels.get(), info.minRowBytes());
memset(result.writable_addr(), 0xAB, info.computeMinByteSize());
context.fPixmap = &result;
surf->asyncRescaleAndReadPixels(info, rect, kGamma, kQuality,
legacy_callback, &context);
while (!context.fCalled) {
ctxInfo.grContext()->checkAsyncWorkCompletion();
}
succeeded = context.fSuceeded;
} else {
surf->asyncRescaleAndReadPixels(info, rect, kGamma, kQuality,
async_callback, &asyncContext);
while (!asyncContext.fCalled) {
ctxInfo.grContext()->checkAsyncWorkCompletion();
}
if (asyncContext.fResult) {
int count = asyncContext.fResult->count();
if (count == 1) {
succeeded = true;
result.reset(info,
asyncContext.fResult->data(0),
asyncContext.fResult->rowBytes(0));
} else {
ERRORF(reporter, "Unexpected AsyncResult::count(): %d",
count);
continue;
}
}
}
continue;
}
SkPixmap ref;
refImg->peekPixels(&ref);
SkAssertResult(ref.extractSubset(&ref, rect));
// A CS conversion allows a 3 value difference and otherwise a 2 value
// difference. Note that sometimes read back on GPU can be lossy even when
// there no conversion at all because GPU->CPU read may go to a a lower bit
// depth format and then be promoted back to the original type. For example,
// GL ES cannot read to 1010102, so we go through 8888.
float numer = didCSConversion ? 3.f : 2.f;
int rgbBits = std::min({min_rgb_channel_bits(readCT),
min_rgb_channel_bits(surfCT), 8});
float tol = numer / (1 << rgbBits);
const float tols[4] = {tol, tol, tol, 0};
auto error = std::function<ComparePixmapsErrorReporter>(
[&](int x, int y, const float diffs[4]) {
SkASSERT(x >= 0 && y >= 0);
if (rect.isEmpty() || !SkIRect::MakeWH(kW, kH).contains(rect)) {
REPORTER_ASSERT(reporter, !succeeded);
}
bool didCSConversion = !SkColorSpace::Equals(
readCS.get(), surf->imageInfo().colorSpace());
if (succeeded) {
REPORTER_ASSERT(reporter,
readCT != kUnknown_SkColorType && !rect.isEmpty());
} else {
// TODO: Support reading to kGray, support kRGB_101010x at all in
// GPU.
auto surfBounds = SkIRect::MakeWH(surf->width(), surf->height());
if (readCT != kUnknown_SkColorType &&
readCT != kGray_8_SkColorType &&
readCT != kRGB_101010x_SkColorType && !rect.isEmpty() &&
surfBounds.contains(rect)) {
ERRORF(reporter,
"Surf Color Type: %s, Read CT: %s, Rect [%d, %d, %d, %d]"
", origin: %d, CS conversion: %d\n"
"Error at %d, %d. Diff in floats: (%f, %f, %f %f)",
"Async read failed. Surf Color Type: %s, Read CT: %s,"
"Rect [%d, %d, %d, %d], origin: %d, CS conversion: %d\n",
ToolUtils::colortype_name(surfCT),
ToolUtils::colortype_name(readCT),
rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, origin,
didCSConversion, x, y, diffs[0], diffs[1], diffs[2],
diffs[3]);
});
compare_pixels(ref, result, tols, error);
ToolUtils::colortype_name(readCT), rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom, origin, didCSConversion);
}
continue;
}
SkPixmap ref;
refImg->peekPixels(&ref);
SkAssertResult(ref.extractSubset(&ref, rect));
// A CS conversion allows a 3 value difference and otherwise a 2 value
// difference. Note that sometimes read back on GPU can be lossy even
// when there no conversion at all because GPU->CPU read may go to a
// lower bit depth format and then be promoted back to the original
// type. For example, GL ES cannot read to 1010102, so we go through
// 8888.
float numer = didCSConversion ? 3.f : 2.f;
int rgbBits = std::min({min_rgb_channel_bits(readCT),
min_rgb_channel_bits(surfCT), 8});
float tol = numer / (1 << rgbBits);
const float tols[4] = {tol, tol, tol, 0};
auto error = std::function<
ComparePixmapsErrorReporter>([&](int x, int y,
const float diffs[4]) {
SkASSERT(x >= 0 && y >= 0);
ERRORF(reporter,
"Surf Color Type: %s, Read CT: %s, Rect [%d, %d, %d, %d]"
", origin: %d, CS conversion: %d\n"
"Error at %d, %d. Diff in floats: (%f, %f, %f %f)",
ToolUtils::colortype_name(surfCT),
ToolUtils::colortype_name(readCT), rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom, origin, didCSConversion, x, y,
diffs[0], diffs[1], diffs[2], diffs[3]);
});
compare_pixels(ref, result, tols, error);
}
}
}
}
}
}
}
DEF_GPUTEST(AsyncReadPixelsContextShutdown, reporter, options) {
const auto ii = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
SkColorSpace::MakeSRGB());
enum class ShutdownSequence {
kFreeResult_DestroyContext,
kDestroyContext_FreeResult,
kFreeResult_ReleaseAndAbandon_DestroyContext,
kFreeResult_Abandon_DestroyContext,
kReleaseAndAbandon_FreeResult_DestroyContext,
kAbandon_FreeResult_DestroyContext,
kReleaseAndAbandon_DestroyContext_FreeResult,
kAbandon_DestroyContext_FreeResult,
};
for (int t = 0; t < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++t) {
auto type = static_cast<sk_gpu_test::GrContextFactory::ContextType>(t);
for (auto sequence : {ShutdownSequence::kFreeResult_DestroyContext,
ShutdownSequence::kDestroyContext_FreeResult,
ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext,
ShutdownSequence::kFreeResult_Abandon_DestroyContext,
ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext,
ShutdownSequence::kAbandon_FreeResult_DestroyContext,
ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult,
ShutdownSequence::kAbandon_DestroyContext_FreeResult}) {
// Vulkan context abandoning without resource release has issues outside of the scope of
// this test.
if (type == sk_gpu_test::GrContextFactory::kVulkan_ContextType &&
(sequence == ShutdownSequence::kAbandon_FreeResult_DestroyContext ||
sequence == ShutdownSequence::kAbandon_DestroyContext_FreeResult ||
sequence == ShutdownSequence::kFreeResult_Abandon_DestroyContext)) {
continue;
}
for (bool yuv : {false, true}) {
sk_gpu_test::GrContextFactory factory(options);
auto context = factory.get(type);
if (!context) {
continue;
}
// This test is only meaningful for contexts that support transfer buffers.
if (!context->priv().caps()->transferBufferSupport()) {
continue;
}
auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, ii, 1, nullptr);
if (!surf) {
continue;
}
AsyncContext cbContext;
if (yuv) {
surf->asyncRescaleAndReadPixelsYUV420(
kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(), ii.bounds(),
ii.dimensions(), SkSurface::RescaleGamma::kSrc, kNone_SkFilterQuality,
&async_callback, &cbContext);
} else {
surf->asyncRescaleAndReadPixels(ii, ii.bounds(), SkSurface::RescaleGamma::kSrc,
kNone_SkFilterQuality, &async_callback,
&cbContext);
}
while (!cbContext.fCalled) {
context->checkAsyncWorkCompletion();
}
if (!cbContext.fResult) {
ERRORF(reporter, "Callback failed on %s. is YUV: %d",
sk_gpu_test::GrContextFactory::ContextTypeName(type), yuv);
continue;
}
// The real test is that we don't crash, get Vulkan validation errors, etc, during
// this shutdown sequence.
switch (sequence) {
case ShutdownSequence::kFreeResult_DestroyContext:
case ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext:
case ShutdownSequence::kFreeResult_Abandon_DestroyContext:
break;
case ShutdownSequence::kDestroyContext_FreeResult:
factory.destroyContexts();
break;
case ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext:
factory.releaseResourcesAndAbandonContexts();
break;
case ShutdownSequence::kAbandon_FreeResult_DestroyContext:
factory.abandonContexts();
break;
case ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult:
factory.releaseResourcesAndAbandonContexts();
factory.destroyContexts();
break;
case ShutdownSequence::kAbandon_DestroyContext_FreeResult:
factory.abandonContexts();
factory.destroyContexts();
break;
}
cbContext.fResult.reset();
switch (sequence) {
case ShutdownSequence::kFreeResult_ReleaseAndAbandon_DestroyContext:
factory.releaseResourcesAndAbandonContexts();
break;
case ShutdownSequence::kFreeResult_Abandon_DestroyContext:
factory.abandonContexts();
break;
case ShutdownSequence::kFreeResult_DestroyContext:
case ShutdownSequence::kDestroyContext_FreeResult:
case ShutdownSequence::kReleaseAndAbandon_FreeResult_DestroyContext:
case ShutdownSequence::kAbandon_FreeResult_DestroyContext:
case ShutdownSequence::kReleaseAndAbandon_DestroyContext_FreeResult:
case ShutdownSequence::kAbandon_DestroyContext_FreeResult:
break;
}
}
}
}
}

View File

@ -143,8 +143,7 @@ static void init(Source* source, std::shared_ptr<SkCodec> codec) {
source->draw = [codec](SkCanvas* canvas) {
SkImageInfo info = codec->getInfo();
if (FLAGS_decodeToDst) {
info = canvas->imageInfo().makeWH(info.width(),
info.height());
info = canvas->imageInfo().makeDimensions(info.dimensions());
}
SkBitmap bm;
@ -500,8 +499,7 @@ int main(int argc, char** argv) {
fprintf(stdout, "%50s", source.name.c_str());
fflush(stdout);
const SkImageInfo info = unsized_info.makeWH(source.size.width(),
source.size.height());
const SkImageInfo info = unsized_info.makeDimensions(source.size);
auto draw = [&source](SkCanvas* canvas) {
Result result = source.draw(canvas);