paint color -> shader

This means we can share programs with color shaders
whether they come through as a paint color or shader.

The GM now only ever JITs one program.

Change-Id: If61f9e7b79aa6b8fd1de7c16ff52f78bfd7dc4ec
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/252079
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2019-10-31 15:41:58 -05:00 committed by Skia Commit-Bot
parent 476061e4ce
commit 9d0c67ab1b
4 changed files with 82 additions and 54 deletions

View File

@ -17,27 +17,29 @@
DEF_SIMPLE_GM(SkVMBlitter, canvas, 100, 100) {
SkPaint p;
// These two draws are supported by SkVMBlitter,
// and the green draw will reuse the program cached by the blue draw.
// These three draws are supported by SkVMBlitter,
// and the later draws will reuse the program cached by earlier draws.
//
// We don't have any API to detect this, but you can flip on the #if guard
// around "calls to done" in SkVMBlitter.cpp and run this GM in isolation.
// You should see 2 calls to done, one for the blitter that clears to white,
// and one for the program used by both shader draws.
// You should see 1 call to done.
//
// $ ninja -C out fm && out/fm -b cpu -s SkVMBlitter
// ...
// 2 calls to done
// 1 calls to done
//
// Clever readers might realize this could actually be one single blitter:
// only the fact that some colors come via shader and the white clear via
// the paint makes the two programs cache differently, despite basically
// doing the same thing. (TODO: unify these two paths also so they'd hit
// the cache.)
// The program is actually first created when the GM framework first
// clears the buffer white by setting a paint color to SK_ColorWHITE like
// we do with SK_ColorRED. SkVMBlitter is clever enough now to build the
// same program for paint colors or color shaders.
p.setShader(SkShaders::Color(SK_ColorBLUE));
canvas->drawRect({0,0,50,50}, p);
canvas->drawRect({0,0, 50,50}, p);
p.setShader(SkShaders::Color(SK_ColorGREEN));
canvas->drawRect({50,50,100,100}, p);
canvas->drawRect({50,50, 100,100}, p);
p.setShader(nullptr);
p.setColor(SK_ColorRED);
canvas->drawRect({0,50, 50,100}, p);
}

View File

@ -116,8 +116,8 @@ namespace {
struct Uniforms {
uint32_t paint_color;
uint8_t coverage; // Used when Coverage::UniformA8.
uint8_t padding[3]; // Keep 32-bit aligned.
};
struct Builder : public skvm::Builder {
@ -185,14 +185,16 @@ namespace {
static bool CanBuild(const Params& params) {
// These checks parallel the TODOs in Builder::Builder().
if (params.shader) {
// TODO: probably want to pass all of the device's SkColorInfo?
if (!as_SB(params.shader)->program(nullptr,
params.colorSpace.get(),
skvm::Arg{0}, 0,
nullptr,nullptr,nullptr,nullptr)) {
return false;
SkASSERT(params.shader);
// TODO: probably want to pass all of the device's SkColorInfo?
if (!as_SB(params.shader)->program(nullptr,
params.colorSpace.get(),
skvm::Arg{0}, 0,
nullptr,nullptr,nullptr,nullptr)) {
if (debug_dump(key(params))) {
SkDebugf("%s not yet supported\n", params.shader->getTypeName());
}
return false;
}
switch (params.colorType) {
@ -221,13 +223,12 @@ namespace {
// If coverage is Mask3D there'll next come two varyings for mul and add planes,
// and then finally if coverage is any Mask?? format, a varying for the mask.
Color src = unpack_8888(uniform32(uniforms, offsetof(Uniforms, paint_color)));
if (params.shader) {
SkAssertResult(as_SB(params.shader)->program(this,
params.colorSpace.get(),
uniforms, sizeof(Uniforms),
&src.r, &src.g, &src.b, &src.a));
}
Color src;
SkASSERT(params.shader);
SkAssertResult(as_SB(params.shader)->program(this,
params.colorSpace.get(),
uniforms, sizeof(Uniforms),
&src.r, &src.g, &src.b, &src.a));
if (params.coverage == Coverage::Mask3D) {
skvm::I32 M = load8(varying<uint8_t>()),
@ -392,9 +393,16 @@ namespace {
const char* getTypeName() const override { return "AlphaShader"; }
};
static sk_sp<SkShader> alpha_shader(sk_sp<SkShader> shader, uint8_t alpha) {
return (shader && alpha < 0xff) ? sk_make_sp<AlphaShader>(std::move(shader), alpha)
: std::move(shader);
static sk_sp<SkShader> effective_shader(sk_sp<SkShader> shader, SkColor4f paintColor) {
// When there's no shader, the paint color becomes the shader.
if (!shader) {
return SkShaders::Color(paintColor, nullptr);
}
// When there is a shader, we modulate it by the paint's alpha.
uint8_t alpha = paintColor.toBytes_RGBA() >> 24;
return alpha < 0xff ? sk_make_sp<AlphaShader>(std::move(shader), alpha)
: std::move(shader);
}
class Blitter final : public SkBlitter {
@ -405,7 +413,7 @@ namespace {
: fDevice(device)
, fParams {
device.refColorSpace(),
alpha_shader(paint.refShader(), paint.getAlpha()),
effective_shader(paint.refShader(), paint.getColor4f()),
device.colorType(),
device.alphaType(),
paint.getBlendMode(),
@ -416,16 +424,7 @@ namespace {
{
// Color filters have been folded back into shader and/or paint color by now.
SkASSERT(!paint.getColorFilter());
SkColor4f color = paint.getColor4f();
SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
device.colorSpace(), kUnpremul_SkAlphaType}.apply(color.vec());
if (color.fitsInBytes() && Builder::CanBuild(fParams)) {
uint32_t rgba = color.premul().toBytes_RGBA();
memcpy(fUniforms.data() + offsetof(Uniforms, paint_color), &rgba, sizeof(rgba));
ok = true;
}
ok = Builder::CanBuild(fParams);
}
~Blitter() override {

View File

@ -90,14 +90,13 @@ bool SkColor4Shader::onAppendStages(const SkStageRec& rec) const {
return true;
}
bool SkColorShader::program(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, int offset,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
SkColor4f color = SkColor4f::FromColor(fColor);
SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
dstCS, kUnpremul_SkAlphaType).apply(color.vec());
static bool common_program(SkColor4f color, SkColorSpace* cs,
skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, int offset,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) {
SkColorSpaceXformSteps( cs, kUnpremul_SkAlphaType,
dstCS, kUnpremul_SkAlphaType).apply(color.vec());
if (color.fitsInBytes()) {
if (p) {
@ -111,11 +110,10 @@ bool SkColorShader::program(skvm::Builder* p,
}
return false;
}
size_t SkColorShader::uniforms(SkColorSpace* dstCS, uint8_t* uniform_buffer) const {
SkColor4f color = SkColor4f::FromColor(fColor);
SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
dstCS, kUnpremul_SkAlphaType).apply(color.vec());
static size_t common_uniforms(SkColor4f color, SkColorSpace* cs,
SkColorSpace* dstCS, uint8_t* uniform_buffer) {
SkColorSpaceXformSteps( cs, kUnpremul_SkAlphaType,
dstCS, kUnpremul_SkAlphaType).apply(color.vec());
SkASSERT(color.fitsInBytes());
@ -126,6 +124,29 @@ size_t SkColorShader::uniforms(SkColorSpace* dstCS, uint8_t* uniform_buffer) con
return sizeof(rgba);
}
bool SkColorShader::program(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, int offset,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
return common_program(SkColor4f::FromColor(fColor), sk_srgb_singleton(),
p, dstCS, uniforms, offset, r,g,b,a);
}
bool SkColor4Shader::program(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, int offset,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
return common_program(fColor, fColorSpace.get(),
p, dstCS, uniforms, offset, r,g,b,a);
}
size_t SkColorShader::uniforms(SkColorSpace* dstCS, uint8_t* uniform_buffer) const {
return common_uniforms(SkColor4f::FromColor(fColor), sk_srgb_singleton(),
dstCS, uniform_buffer);
}
size_t SkColor4Shader::uniforms(SkColorSpace* dstCS, uint8_t* uniform_buffer) const {
return common_uniforms(fColor, fColorSpace.get(),
dstCS, uniform_buffer);
}
#if SK_SUPPORT_GPU

View File

@ -70,6 +70,12 @@ private:
void flatten(SkWriteBuffer&) const override;
bool onAppendStages(const SkStageRec&) const override;
bool program(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, int offset,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override;
size_t uniforms(SkColorSpace* dstCS, uint8_t* uniform_buffer) const override;
sk_sp<SkColorSpace> fColorSpace;
const SkColor4f fColor;
};