Add support for child effects on SkRuntimeBlender.
This will allow runtime blenders to leverage existing color filters or shaders when computing a blend. Change-Id: I743d5fc6d83a92cd190cb6bfd1c79789d8be2089 Bug: skia:12249 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430658 Commit-Queue: John Stiles <johnstiles@google.com> Commit-Queue: Brian Osman <brianosman@google.com> Auto-Submit: John Stiles <johnstiles@google.com> Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
ff7a4a576f
commit
8e51bad14f
@ -194,7 +194,7 @@ public:
|
||||
sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms,
|
||||
SkSpan<ChildPtr> children) const;
|
||||
|
||||
sk_sp<SkBlender> makeBlender(sk_sp<SkData> uniforms) const;
|
||||
sk_sp<SkBlender> makeBlender(sk_sp<SkData> uniforms, SkSpan<ChildPtr> children = {}) const;
|
||||
|
||||
const std::string& source() const;
|
||||
|
||||
|
@ -264,12 +264,6 @@ SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Prog
|
||||
|
||||
// Child effects that can be sampled ('shader' or 'colorFilter')
|
||||
if (varType.isEffectChild()) {
|
||||
// Runtime blends currently don't support child effects.
|
||||
if (kind == SkSL::ProgramKind::kRuntimeBlender) {
|
||||
RETURN_FAILURE("'%s' is not allowed in runtime blend",
|
||||
varType.displayName().c_str());
|
||||
}
|
||||
|
||||
Child c;
|
||||
c.name = SkString(var.name());
|
||||
c.type = child_type(varType);
|
||||
@ -722,6 +716,7 @@ static GrFPResult make_effect_fp(sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
sk_sp<SkData> uniforms,
|
||||
std::unique_ptr<GrFragmentProcessor> inputFP,
|
||||
std::unique_ptr<GrFragmentProcessor> destColorFP,
|
||||
SkSpan<const SkRuntimeEffect::ChildPtr> children,
|
||||
const GrFPArgs& childArgs) {
|
||||
SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
|
||||
@ -748,7 +743,7 @@ static GrFPResult make_effect_fp(sk_sp<SkRuntimeEffect> effect,
|
||||
auto fp = GrSkSLFP::MakeWithData(std::move(effect),
|
||||
name,
|
||||
std::move(inputFP),
|
||||
/*destColorFP=*/nullptr,
|
||||
std::move(destColorFP),
|
||||
std::move(uniforms),
|
||||
SkMakeSpan(childFPs));
|
||||
SkASSERT(fp);
|
||||
@ -778,6 +773,7 @@ public:
|
||||
"runtime_color_filter",
|
||||
std::move(uniforms),
|
||||
std::move(inputFP),
|
||||
/*destColorFP=*/nullptr,
|
||||
SkMakeSpan(fChildren),
|
||||
childArgs);
|
||||
}
|
||||
@ -938,6 +934,7 @@ public:
|
||||
"runtime_shader",
|
||||
std::move(uniforms),
|
||||
/*inputFP=*/nullptr,
|
||||
/*destColorFP=*/nullptr,
|
||||
SkMakeSpan(fChildren),
|
||||
childArgs);
|
||||
if (!success) {
|
||||
@ -1090,9 +1087,12 @@ sk_sp<SkFlattenable> SkRTShader::CreateProc(SkReadBuffer& buffer) {
|
||||
|
||||
class SkRuntimeBlender : public SkBlenderBase {
|
||||
public:
|
||||
SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms)
|
||||
SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect,
|
||||
sk_sp<SkData> uniforms,
|
||||
SkSpan<SkRuntimeEffect::ChildPtr> children)
|
||||
: fEffect(std::move(effect))
|
||||
, fUniforms(std::move(uniforms)) {}
|
||||
, fUniforms(std::move(uniforms))
|
||||
, fChildren(children.begin(), children.end()) {}
|
||||
|
||||
SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
|
||||
|
||||
@ -1103,6 +1103,19 @@ public:
|
||||
colorInfo.colorSpace());
|
||||
SkASSERT(inputs);
|
||||
|
||||
auto sampleChild = [&](int index, skvm::Coord coord, skvm::Color color) {
|
||||
const SkRuntimeEffect::ChildPtr& effect = fChildren[index];
|
||||
if (effect.shader) {
|
||||
SkSimpleMatrixProvider mats{SkMatrix::I()};
|
||||
return as_SB(effect.shader)->program(p, coord, coord, color, mats,
|
||||
/*localM=*/nullptr, colorInfo, uniforms, alloc);
|
||||
}
|
||||
if (effect.colorFilter) {
|
||||
return as_CFB(effect.colorFilter)->program(p, color, colorInfo, uniforms, alloc);
|
||||
}
|
||||
return color;
|
||||
};
|
||||
|
||||
const size_t uniformCount = fEffect->uniformSize() / 4;
|
||||
std::vector<skvm::Val> uniform;
|
||||
uniform.reserve(uniformCount);
|
||||
@ -1115,8 +1128,7 @@ public:
|
||||
// Emit the blend function as an SkVM program.
|
||||
skvm::Coord zeroCoord = {p->splat(0.0f), p->splat(0.0f)};
|
||||
return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, SkMakeSpan(uniform),
|
||||
/*device=*/zeroCoord, /*local=*/zeroCoord,
|
||||
src, dst, /*sampleChild=*/nullptr);
|
||||
/*device=*/zeroCoord, /*local=*/zeroCoord, src, dst, sampleChild);
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
@ -1127,13 +1139,15 @@ public:
|
||||
sk_sp<SkData> uniforms = get_xformed_uniforms(fEffect.get(), fUniforms,
|
||||
args.fDstColorInfo->colorSpace());
|
||||
SkASSERT(uniforms);
|
||||
auto [success, fp] = make_effect_fp(fEffect,
|
||||
"runtime_blender",
|
||||
std::move(uniforms),
|
||||
std::move(srcFP),
|
||||
std::move(dstFP),
|
||||
SkMakeSpan(fChildren),
|
||||
args);
|
||||
|
||||
return GrSkSLFP::MakeWithData(fEffect,
|
||||
"runtime_blender",
|
||||
std::move(srcFP),
|
||||
std::move(dstFP),
|
||||
std::move(uniforms),
|
||||
/*childFPs=*/{});
|
||||
return success ? std::move(fp) : nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1149,6 +1163,7 @@ private:
|
||||
|
||||
sk_sp<SkRuntimeEffect> fEffect;
|
||||
sk_sp<SkData> fUniforms;
|
||||
std::vector<SkRuntimeEffect::ChildPtr> fChildren;
|
||||
};
|
||||
|
||||
sk_sp<SkFlattenable> SkRuntimeBlender::CreateProc(SkReadBuffer& buffer) {
|
||||
@ -1312,17 +1327,21 @@ sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms) co
|
||||
return this->makeColorFilter(std::move(uniforms), /*children=*/{});
|
||||
}
|
||||
|
||||
sk_sp<SkBlender> SkRuntimeEffect::makeBlender(sk_sp<SkData> uniforms) const {
|
||||
sk_sp<SkBlender> SkRuntimeEffect::makeBlender(sk_sp<SkData> uniforms,
|
||||
SkSpan<ChildPtr> children) const {
|
||||
if (!this->allowBlender()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!verify_child_effects(fChildren, children)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!uniforms) {
|
||||
uniforms = SkData::MakeEmpty();
|
||||
}
|
||||
if (uniforms->size() != this->uniformSize() || !fChildren.empty()) {
|
||||
if (uniforms->size() != this->uniformSize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return sk_sp<SkBlender>(new SkRuntimeBlender(sk_ref_sp(this), std::move(uniforms)));
|
||||
return sk_sp<SkBlender>(new SkRuntimeBlender(sk_ref_sp(this), std::move(uniforms), children));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1363,7 +1382,8 @@ SkRuntimeBlendBuilder::SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect> effect)
|
||||
SkRuntimeBlendBuilder::~SkRuntimeBlendBuilder() = default;
|
||||
|
||||
sk_sp<SkBlender> SkRuntimeBlendBuilder::makeBlender() {
|
||||
return this->effect()->makeBlender(this->uniforms());
|
||||
return this->effect()->makeBlender(this->uniforms(),
|
||||
SkMakeSpan(this->children(), this->numChildren()));
|
||||
}
|
||||
|
||||
#endif // SK_ENABLE_SKSL
|
||||
|
@ -1,5 +1,36 @@
|
||||
static uint8_t SKSL_INCLUDE_sksl_rt_blend[] = {0,0,
|
||||
47,0,0,0,0,
|
||||
static uint8_t SKSL_INCLUDE_sksl_rt_blend[] = {56,0,
|
||||
1,115,
|
||||
6,115,104,97,100,101,114,
|
||||
6,99,111,111,114,100,115,
|
||||
6,102,108,111,97,116,50,
|
||||
6,115,97,109,112,108,101,
|
||||
5,104,97,108,102,52,
|
||||
1,102,
|
||||
11,99,111,108,111,114,70,105,108,116,101,114,
|
||||
5,99,111,108,111,114,
|
||||
47,7,0,
|
||||
51,1,0,
|
||||
16,2,0,
|
||||
48,2,0,4,0,3,
|
||||
51,3,0,
|
||||
16,11,0,
|
||||
48,4,0,18,0,3,
|
||||
28,5,0,
|
||||
16,25,0,2,1,0,3,0,
|
||||
48,6,0,32,0,
|
||||
51,7,0,
|
||||
16,38,0,
|
||||
48,8,0,40,0,3,
|
||||
51,9,0,
|
||||
16,52,0,
|
||||
45,6,0,3,
|
||||
50,10,0,2,
|
||||
45,5,0,
|
||||
28,11,0,
|
||||
16,25,0,2,7,0,9,0,
|
||||
45,6,0,
|
||||
45,11,0,1,0,
|
||||
5,0,
|
||||
19,
|
||||
20,};
|
||||
static constexpr size_t SKSL_INCLUDE_sksl_rt_blend_LENGTH = sizeof(SKSL_INCLUDE_sksl_rt_blend);
|
||||
|
@ -1 +1,2 @@
|
||||
// TODO(skia:12080): if any blend-specific program elements are needed, add them here.
|
||||
half4 sample(shader s, float2 coords);
|
||||
half4 sample(colorFilter f, half4 color);
|
||||
|
@ -176,8 +176,8 @@ DEF_TEST(SkRuntimeEffectForBlender, r) {
|
||||
errorText.c_str());
|
||||
};
|
||||
|
||||
// Color filters must use the 'half4 main(half4, half4)' signature. Any mixture of
|
||||
// float4/vec4/half4 is allowed.
|
||||
// Blenders must use the 'half4 main(half4, half4)' signature. Any mixture of float4/vec4/half4
|
||||
// is allowed.
|
||||
test_valid("half4 main(half4 s, half4 d) { return s; }");
|
||||
test_valid("float4 main(float4 s, float4 d) { return d; }");
|
||||
test_valid("float4 main(half4 s, float4 d) { return s; }");
|
||||
@ -202,11 +202,44 @@ DEF_TEST(SkRuntimeEffectForBlender, r) {
|
||||
test_invalid("half4 main(half4 s, half4 d) { return sk_FragCoord.xy01; }",
|
||||
"unknown identifier");
|
||||
|
||||
// Child shaders are currently unsupported in blends
|
||||
test_invalid("uniform shader sh; half4 main(half4 s, half4 d) { return s; }",
|
||||
"'shader' is not allowed in runtime blend");
|
||||
test_invalid("uniform shader sh; half4 main(half4 s, half4 d) { return sample(sh, s.rg); }",
|
||||
"unknown identifier 'sample'");
|
||||
// Sampling a child shader requires that we pass explicit coords
|
||||
test_valid("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, s.rg); }");
|
||||
// Trying to pass a color as well. (Works internally with FPs, but not in runtime effects).
|
||||
test_invalid("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, s.rg, d); }",
|
||||
"no match for sample(shader, half2, half4)");
|
||||
|
||||
// Shader with just a color
|
||||
test_invalid("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, s); }",
|
||||
"no match for sample(shader, half4)");
|
||||
// Coords and color in a different order
|
||||
test_invalid("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, s, d.rg); }",
|
||||
"no match for sample(shader, half4, half2)");
|
||||
|
||||
// Older variants that are no longer allowed
|
||||
test_invalid("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child); }",
|
||||
"no match for sample(shader)");
|
||||
test_invalid("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, float3x3(1)); }",
|
||||
"no match for sample(shader, float3x3)");
|
||||
|
||||
// Sampling a colorFilter requires a color. No other signatures are valid.
|
||||
test_valid("uniform colorFilter child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, d); }");
|
||||
|
||||
test_invalid("uniform colorFilter child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child); }",
|
||||
"sample(colorFilter)");
|
||||
test_invalid("uniform colorFilter child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, d.rg); }",
|
||||
"sample(colorFilter, half2)");
|
||||
test_invalid("uniform colorFilter child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, d.rg, s); }",
|
||||
"sample(colorFilter, half2, half4)");
|
||||
}
|
||||
|
||||
DEF_TEST(SkRuntimeEffectForShader, r) {
|
||||
@ -406,6 +439,10 @@ public:
|
||||
return fBuilder->uniform(name);
|
||||
}
|
||||
|
||||
SkRuntimeBlendBuilder::BuilderChild child(const char* name) {
|
||||
return fBuilder->child(name);
|
||||
}
|
||||
|
||||
void test(std::array<GrColor, 4> expected, PreTestFn preTestCallback = nullptr) {
|
||||
auto blender = fBuilder->makeBlender();
|
||||
if (!blender) {
|
||||
@ -552,6 +589,7 @@ static void test_RuntimeEffect_Blenders(skiatest::Reporter* r, GrRecordingContex
|
||||
REPORTER_ASSERT(r, surface);
|
||||
TestBlend effect(r, surface);
|
||||
|
||||
using float2 = std::array<float, 2>;
|
||||
using float4 = std::array<float, 4>;
|
||||
using int4 = std::array<int, 4>;
|
||||
|
||||
@ -600,6 +638,40 @@ static void test_RuntimeEffect_Blenders(skiatest::Reporter* r, GrRecordingContex
|
||||
effect.test(0x00000000);
|
||||
effect.build("half4 main(half4 s, half4 d) { return half4(2); }");
|
||||
effect.test(0xFFFFFFFF);
|
||||
|
||||
//
|
||||
// Sampling children
|
||||
//
|
||||
|
||||
// Sampling a null child should return the paint color
|
||||
effect.build("uniform shader child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, s.rg); }");
|
||||
effect.child("child") = nullptr;
|
||||
effect.test(0xFF00FFFF,
|
||||
[](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
|
||||
|
||||
// Sampling a shader at various coordinates
|
||||
effect.build("uniform shader child;"
|
||||
"uniform half2 pos;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, pos); }");
|
||||
effect.child("child") = make_RGBW_shader();
|
||||
effect.uniform("pos") = float2{0, 0};
|
||||
effect.test(0xFF0000FF);
|
||||
|
||||
effect.uniform("pos") = float2{1, 0};
|
||||
effect.test(0xFF00FF00);
|
||||
|
||||
effect.uniform("pos") = float2{0, 1};
|
||||
effect.test(0xFFFF0000);
|
||||
|
||||
effect.uniform("pos") = float2{1, 1};
|
||||
effect.test(0xFFFFFFFF);
|
||||
|
||||
// Sampling a color filter
|
||||
effect.build("uniform colorFilter child;"
|
||||
"half4 main(half4 s, half4 d) { return sample(child, half4(1)); }");
|
||||
effect.child("child") = SkColorFilters::Blend(0xFF012345, SkBlendMode::kSrc);
|
||||
effect.test(0xFF452301);
|
||||
}
|
||||
|
||||
DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {
|
||||
|
Loading…
Reference in New Issue
Block a user