Update wacky_yuv_formats GM to demonstrate YUV resizing on the GPU

Change-Id: Idd2b75ca84c1d7984aa983820b4325fbbda2b753
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266203
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Robert Phillips 2020-01-29 08:37:01 -05:00 committed by Skia Commit-Bot
parent 45c9487914
commit 99044e1a6a
6 changed files with 229 additions and 69 deletions

View File

@ -1013,6 +1013,55 @@ static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
return SkColorFilters::Matrix(kJPEGConversionMatrix); return SkColorFilters::Matrix(kJPEGConversionMatrix);
} }
// Get the SkColorType to use when creating an SkSurface wrapping 'format'.
static SkColorType get_color_type(const GrBackendFormat& format) {
GrGLFormat glFormat = format.asGLFormat();
if (GrGLFormat::kUnknown != glFormat) {
switch (glFormat) {
case GrGLFormat::kLUMINANCE8: // fall through
case GrGLFormat::kR8: // fall through
case GrGLFormat::kALPHA8: return kAlpha_8_SkColorType;
case GrGLFormat::kRG8: return kR8G8_unorm_SkColorType;
case GrGLFormat::kRGB8: return kRGB_888x_SkColorType;
case GrGLFormat::kRGBA8: return kRGBA_8888_SkColorType;
case GrGLFormat::kBGRA8: return kBGRA_8888_SkColorType;
case GrGLFormat::kRGB10_A2: return kRGBA_1010102_SkColorType;
case GrGLFormat::kLUMINANCE16F: // fall through
case GrGLFormat::kR16F: return kA16_float_SkColorType;
case GrGLFormat::kRG16F: return kR16G16_float_SkColorType;
case GrGLFormat::kR16: return kA16_unorm_SkColorType;
case GrGLFormat::kRG16: return kR16G16_unorm_SkColorType;
case GrGLFormat::kRGBA16: return kR16G16B16A16_unorm_SkColorType;
default: return kUnknown_SkColorType;
}
SkUNREACHABLE;
}
VkFormat vkFormat;
if (format.asVkFormat(&vkFormat)) {
switch (vkFormat) {
case VK_FORMAT_R8_UNORM: return kAlpha_8_SkColorType;
case VK_FORMAT_R8G8_UNORM: return kR8G8_unorm_SkColorType;
case VK_FORMAT_R8G8B8_UNORM: return kRGB_888x_SkColorType;
case VK_FORMAT_R8G8B8A8_UNORM: return kRGBA_8888_SkColorType;
case VK_FORMAT_B8G8R8A8_UNORM: return kBGRA_8888_SkColorType;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return kRGBA_1010102_SkColorType;
case VK_FORMAT_R16_SFLOAT: return kA16_float_SkColorType;
case VK_FORMAT_R16G16_SFLOAT: return kR16G16_float_SkColorType;
case VK_FORMAT_R16_UNORM: return kA16_unorm_SkColorType;
case VK_FORMAT_R16G16_UNORM: return kR16G16_unorm_SkColorType;
case VK_FORMAT_R16G16B16A16_UNORM: return kR16G16B16A16_unorm_SkColorType;
default: return kUnknown_SkColorType;
}
SkUNREACHABLE;
}
return kUnknown_SkColorType;
}
namespace skiagm { namespace skiagm {
// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines // This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
@ -1033,9 +1082,10 @@ namespace skiagm {
// YV12 // YV12
class WackyYUVFormatsGM : public GM { class WackyYUVFormatsGM : public GM {
public: public:
WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain) WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain, bool quarterSize)
: fUseTargetColorSpace(useTargetColorSpace) : fUseTargetColorSpace(useTargetColorSpace)
, fUseDomain(useDomain) { , fUseDomain(useDomain)
, fQuarterSize(quarterSize) {
this->setBGColor(0xFFCCCCCC); this->setBGColor(0xFFCCCCCC);
} }
@ -1049,6 +1099,9 @@ protected:
if (fUseDomain) { if (fUseDomain) {
name += "_domain"; name += "_domain";
} }
if (fQuarterSize) {
name += "_qtr";
}
return name; return name;
} }
@ -1085,6 +1138,71 @@ protected:
} }
} }
// Resize all the backend textures in 'yuvaTextures' to a quarter their size.
sk_sp<SkImage> resizeOnGpu(GrContext* context,
YUVFormat yuvFormat,
SkYUVColorSpace yuvColorSpace,
bool opaque,
const GrBackendTexture yuvaTextures[],
const SkYUVAIndex yuvaIndices[4],
int numTextures,
SkISize imageSize) {
GrBackendTexture shrunkTextures[4];
for (int i = 0; i < numTextures; ++i) {
SkColorType ct = get_color_type(yuvaTextures[i].getBackendFormat());
if (ct == kUnknown_SkColorType || !context->colorTypeSupportedAsSurface(ct)) {
return nullptr;
}
SkISize shrunkPlaneSize = { yuvaTextures[i].width() / 2, yuvaTextures[i].height() / 2 };
sk_sp<SkImage> wrappedOrig = SkImage::MakeFromTexture(context, yuvaTextures[i],
kTopLeft_GrSurfaceOrigin,
ct,
kPremul_SkAlphaType,
nullptr);
shrunkTextures[i] = context->createBackendTexture(shrunkPlaneSize.width(),
shrunkPlaneSize.height(),
yuvaTextures[i].getBackendFormat(),
GrMipMapped::kNo,
GrRenderable::kYes);
if (!shrunkTextures[i].isValid()) {
return nullptr;
}
// Store this away so it will be cleaned up at the end.
fBackendTextures.push_back(shrunkTextures[i]);
sk_sp<SkSurface> s = SkSurface::MakeFromBackendTexture(context, shrunkTextures[i],
kTopLeft_GrSurfaceOrigin, 0,
ct, nullptr, nullptr);
if (!s) {
return nullptr;
}
SkCanvas* c = s->getCanvas();
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
c->drawImageRect(wrappedOrig,
SkRect::MakeWH(shrunkPlaneSize.width(), shrunkPlaneSize.height()),
&paint);
s->flush();
}
SkISize shrunkImageSize = { imageSize.width() / 2, imageSize.height() / 2 };
return SkImage::MakeFromYUVATextures(context,
yuvColorSpace,
shrunkTextures,
yuvaIndices,
shrunkImageSize,
kTopLeft_GrSurfaceOrigin);
}
void createImages(GrContext* context) { void createImages(GrContext* context) {
int counter = 0; int counter = 0;
for (bool opaque : { false, true }) { for (bool opaque : { false, true }) {
@ -1125,12 +1243,24 @@ protected:
yuvaPixmaps[i] = resultBMs[i].pixmap(); yuvaPixmaps[i] = resultBMs[i].pixmap();
} }
if (fQuarterSize) {
fImages[opaque][cs][format] = this->resizeOnGpu(
context,
(YUVFormat) format,
(SkYUVColorSpace) cs,
opaque,
yuvaTextures,
yuvaIndices,
numTextures,
fOriginalBMs[opaque].dimensions());
} else {
int counterMod = counter % 3; int counterMod = counter % 3;
if (fUseDomain && counterMod == 0) { if (fUseDomain && counterMod == 0) {
// Copies flatten to RGB when they copy the YUVA data, which doesn't // Copies flatten to RGB when they copy the YUVA data, which doesn't
// know about the intended domain and the domain padding bleeds in // know about the intended domain and the domain padding bleeds in
counterMod = 1; counterMod = 1;
} }
switch (counterMod) { switch (counterMod) {
case 0: case 0:
fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy( fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
@ -1162,6 +1292,7 @@ protected:
break; break;
} }
++counter; ++counter;
}
} else { } else {
fImages[opaque][cs][format] = make_yuv_gen_image( fImages[opaque][cs][format] = make_yuv_gen_image(
fOriginalBMs[opaque].info(), fOriginalBMs[opaque].info(),
@ -1177,12 +1308,32 @@ protected:
void onDraw(SkCanvas* canvas) override { void onDraw(SkCanvas* canvas) override {
this->createImages(canvas->getGrContext()); this->createImages(canvas->getGrContext());
float cellWidth = kTileWidthHeight, cellHeight = kTileWidthHeight;
if (fUseDomain) {
cellWidth *= 1.5f;
cellHeight *= 1.5f;
}
SkRect origSrcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height()); SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height()); SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
if (fQuarterSize) {
if (canvas->getGrContext()) {
// The src is only shrunk on the GPU
srcRect = SkRect::MakeWH(fOriginalBMs[0].width()/2.0f,
fOriginalBMs[0].height()/2.0f);
}
// but the dest is always drawn smaller
dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f,
fOriginalBMs[0].width()/2.0f,
fOriginalBMs[0].height()/2.0f);
}
SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint; SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
if (fUseDomain) { if (fUseDomain) {
srcRect.inset(kDomainPadding, kDomainPadding); srcRect.inset(kDomainPadding, kDomainPadding);
origSrcRect.inset(kDomainPadding, kDomainPadding);
// Draw a larger rectangle to ensure bilerp filtering would normally read outside the // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
// srcRect and hit the red pixels, if strict constraint weren't used. // srcRect and hit the red pixels, if strict constraint weren't used.
dstRect.fRight = kLabelWidth + 1.5f * srcRect.width(); dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
@ -1201,10 +1352,11 @@ protected:
for (int opaque : { 0, 1 }) { for (int opaque : { 0, 1 }) {
dstRect.offsetTo(dstRect.fLeft, kLabelHeight); dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
draw_col_label(canvas, dstRect.fLeft + dstRect.height() / 2, cs, opaque); draw_col_label(canvas, dstRect.fLeft + cellWidth / 2, cs, opaque);
canvas->drawBitmapRect(fOriginalBMs[opaque], srcRect, dstRect, nullptr, constraint); canvas->drawBitmapRect(fOriginalBMs[opaque], origSrcRect, dstRect,
dstRect.offset(0.f, dstRect.height() + kPad); nullptr, constraint);
dstRect.offset(0.f, cellHeight + kPad);
for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) { for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
draw_row_label(canvas, dstRect.fTop, format); draw_row_label(canvas, dstRect.fTop, format);
@ -1216,13 +1368,13 @@ protected:
fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace); fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint); canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
} else { } else {
canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect, &paint, canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect,
constraint); &paint, constraint);
} }
dstRect.offset(0.f, dstRect.height() + kPad); dstRect.offset(0.f, cellHeight + kPad);
} }
dstRect.offset(dstRect.width() + kPad, 0.f); dstRect.offset(cellWidth + kPad, 0.f);
} }
} }
if (auto context = canvas->getGrContext()) { if (auto context = canvas->getGrContext()) {
@ -1246,6 +1398,7 @@ private:
SkTArray<GrBackendTexture> fBackendTextures; SkTArray<GrBackendTexture> fBackendTextures;
bool fUseTargetColorSpace; bool fUseTargetColorSpace;
bool fUseDomain; bool fUseDomain;
bool fQuarterSize;
sk_sp<SkColorSpace> fTargetColorSpace; sk_sp<SkColorSpace> fTargetColorSpace;
typedef GM INHERITED; typedef GM INHERITED;
@ -1253,9 +1406,10 @@ private:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false);) DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false, /* quarterSize */ false);)
DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false);) DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false, /* quarterSize */ true);)
DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true);) DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false, /* quarterSize */ false);)
DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true, /* quarterSize */ false);)
class YUVMakeColorSpaceGM : public GpuGM { class YUVMakeColorSpaceGM : public GpuGM {
public: public:

View File

@ -256,8 +256,7 @@ public:
* use maxSurfaceSampleCountForColorType(). * use maxSurfaceSampleCountForColorType().
*/ */
bool colorTypeSupportedAsSurface(SkColorType colorType) const { bool colorTypeSupportedAsSurface(SkColorType colorType) const {
if (kR8G8_unorm_SkColorType == colorType || if (kR16G16_unorm_SkColorType == colorType ||
kR16G16_unorm_SkColorType == colorType ||
kA16_unorm_SkColorType == colorType || kA16_unorm_SkColorType == colorType ||
kA16_float_SkColorType == colorType || kA16_float_SkColorType == colorType ||
kR16G16_float_SkColorType == colorType || kR16G16_float_SkColorType == colorType ||

View File

@ -18,7 +18,7 @@ class GrImageContext : public GrContext_Base {
public: public:
~GrImageContext() override; ~GrImageContext() override;
GrBackendFormat defaultBackendFormat(SkColorType ct, GrRenderable renderable) const { SK_API GrBackendFormat defaultBackendFormat(SkColorType ct, GrRenderable renderable) const {
return INHERITED::defaultBackendFormat(ct, renderable); return INHERITED::defaultBackendFormat(ct, renderable);
} }

View File

@ -487,6 +487,10 @@
"serialize-8888", "serialize-8888",
"gm", "gm",
"_", "_",
"wacky_yuv_formats_qtr",
"serialize-8888",
"gm",
"_",
"analytic_antialias_convex", "analytic_antialias_convex",
"serialize-8888", "serialize-8888",
"gm", "gm",

View File

@ -555,6 +555,7 @@ def dm_flags(api, bot):
bad_serialize_gms.append('readpixels') bad_serialize_gms.append('readpixels')
bad_serialize_gms.append('draw_image_set_rect_to_rect') bad_serialize_gms.append('draw_image_set_rect_to_rect')
bad_serialize_gms.append('compositor_quads_shader') bad_serialize_gms.append('compositor_quads_shader')
bad_serialize_gms.append('wacky_yuv_formats_qtr')
# This GM forces a path to be convex. That property doesn't survive # This GM forces a path to be convex. That property doesn't survive
# serialization. # serialization.

View File

@ -48,28 +48,30 @@ struct TestCase {
SkColorType fColorType; SkColorType fColorType;
SkAlphaType fAlphaType; SkAlphaType fAlphaType;
SkColorTypeComponentFlag fComponents; SkColorTypeComponentFlag fComponents;
bool fCanMakeSurfaces; bool fGpuCanMakeSurfaces;
bool fCpuCanMakeSurfaces;
}; };
static const TestCase gTests[] = { static const TestCase gTests[] = {
{ kAlpha_8_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorTypeComponentFlag, true }, { kAlpha_8_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorTypeComponentFlag, true, true },
{ kA16_unorm_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorTypeComponentFlag, false }, { kA16_unorm_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorTypeComponentFlag, false, false},
{ kA16_float_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorTypeComponentFlag, false }, { kA16_float_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorTypeComponentFlag, false, false},
{ kRGB_565_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorTypeComponentFlags, true }, { kRGB_565_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorTypeComponentFlags, true, true },
{ kARGB_4444_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kARGB_4444_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kRGBA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kRGBA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kRGB_888x_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorTypeComponentFlags, true }, { kRGB_888x_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorTypeComponentFlags, true, true },
{ kBGRA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kBGRA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kRGBA_1010102_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kRGBA_1010102_SkColorType,kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kRGB_101010x_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorTypeComponentFlags, true }, { kRGB_101010x_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorTypeComponentFlags, true, true },
{ kGray_8_SkColorType, kOpaque_SkAlphaType, kGray_SkColorTypeComponentFlag, true }, { kGray_8_SkColorType, kOpaque_SkAlphaType, kGray_SkColorTypeComponentFlag, true, true },
{ kRGBA_F16Norm_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kRGBA_F16Norm_SkColorType,kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kRGBA_F16_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kRGBA_F16_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kRGBA_F32_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true }, { kRGBA_F32_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, true, true },
{ kR8G8_unorm_SkColorType, kOpaque_SkAlphaType, kRG_SkColorTypeComponentFlags, false }, { kR8G8_unorm_SkColorType, kOpaque_SkAlphaType, kRG_SkColorTypeComponentFlags, true, false},
{ kR16G16_unorm_SkColorType, kOpaque_SkAlphaType, kRG_SkColorTypeComponentFlags, false }, { kR16G16_unorm_SkColorType,kOpaque_SkAlphaType, kRG_SkColorTypeComponentFlags, false, false},
{ kR16G16_float_SkColorType, kOpaque_SkAlphaType, kRG_SkColorTypeComponentFlags, false }, { kR16G16_float_SkColorType,kOpaque_SkAlphaType, kRG_SkColorTypeComponentFlags, false, false},
{ kR16G16B16A16_unorm_SkColorType,kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, false }, { kR16G16B16A16_unorm_SkColorType,
kPremul_SkAlphaType, kRGBA_SkColorTypeComponentFlags, false, false},
}; };
static void raster_tests(skiatest::Reporter* reporter, const TestCase& test) { static void raster_tests(skiatest::Reporter* reporter, const TestCase& test) {
@ -84,7 +86,7 @@ static void raster_tests(skiatest::Reporter* reporter, const TestCase& test) {
// Not all colorTypes can be drawn to // Not all colorTypes can be drawn to
{ {
auto s = SkSurface::MakeRaster(nativeII); auto s = SkSurface::MakeRaster(nativeII);
REPORTER_ASSERT(reporter, SkToBool(s) == test.fCanMakeSurfaces); REPORTER_ASSERT(reporter, SkToBool(s) == test.fCpuCanMakeSurfaces);
} }
// opaque formats should make transparent black become opaque // opaque formats should make transparent black become opaque
@ -177,7 +179,7 @@ static void gpu_tests(GrContext* context, skiatest::Reporter* reporter, const Te
kUnpremul_SkAlphaType); kUnpremul_SkAlphaType);
// We had better not be able to render to prohibited colorTypes // We had better not be able to render to prohibited colorTypes
if (!test.fCanMakeSurfaces) { if (!test.fGpuCanMakeSurfaces) {
auto s = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, nativeII); auto s = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, nativeII);
REPORTER_ASSERT(reporter, !SkToBool(s)); REPORTER_ASSERT(reporter, !SkToBool(s));
} }