Reland "Reland "SkSurface asynchronous read APIs allow client to extend pixel lifetime""
This is a reland of6fc04f88a8
Original change's description: > Reland "SkSurface asynchronous read APIs allow client to extend pixel lifetime" > > This is a reland ofce240cc6fd
> > 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:
parent
1244db5650
commit
9241a6d394
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
71
src/gpu/GrClientMappedBufferManager.cpp
Normal file
71
src/gpu/GrClientMappedBufferManager.cpp
Normal 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;
|
||||
}
|
72
src/gpu/GrClientMappedBufferManager.h
Normal file
72
src/gpu/GrClientMappedBufferManager.h
Normal 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
|
@ -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();
|
||||
|
@ -228,6 +228,10 @@ public:
|
||||
return fContext->fShaderErrorHandler;
|
||||
}
|
||||
|
||||
GrClientMappedBufferManager* clientMappedBufferManager() {
|
||||
return fContext->fMappedBufferManager.get();
|
||||
}
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
/** Reset GPU stats */
|
||||
void resetGpuStats() const ;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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()};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user