Revert "Better first-class shader & color filter support in runtime effects"
This reverts commit adadb95a9f1ef21ccc5264c7d0bdc83b56cf91e9.
Reason for revert: breaking android
Original change's description:
> Better first-class shader & color filter support in runtime effects
>
> This does a few things, because they're all intertwined:
>
> 1) SkRuntimeEffect's API now includes details about children (which Skia
> stage was declared, not just the name). The factories verify that the
> declared types in the SkSL match up with the C++ types being passed.
> Today, we still only support adding children of the same type, so the
> checks are simple. Once we allow mixing types, we'll be testing the
> declared type against the actual C++ type supplied for each slot.
> 2) Adds sample variants that supply the input color to the child. This
> is now the only way to invoke a colorFilter child. Internally, we
> support passing a color when invoking a child shader, but I'm not
> exposing that. It's not clearly part of the semantics of the Skia
> pipeline, and is almost never useful. It also exposes users to
> several inconsistencies (skbug.com/11942).
> 3) Because of #2, it's possible that we can't compute a reusable program
> to filter individual colors. In that case, we don't set the constant
> output for constant input optimization, and filterColor4f falls back
> to the slower base-class implementation.
>
> Bug: skia:11813 skia:11942
> Change-Id: I06c41e1b35056e486f3163a72acf6b9535d7fed4
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/401917
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Klein <mtklein@google.com>
TBR=mtklein@google.com,bsalomon@google.com,brianosman@google.com
Change-Id: I94ba57e73305b2302f86fd0c1d76f667d4e45b92
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:11813 skia:11942
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404117
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp b/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp
index e96743d..bf79ca6 100644
--- a/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp
+++ b/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp
@@ -37,16 +37,9 @@
void defineStruct(const char* /*definition*/) override {}
void declareGlobal(const char* /*declaration*/) override {}
- String sampleChild(int index, String coords, String color) override {
- String result = "sample(" + SkSL::to_string(index);
- if (!coords.empty()) {
- result += ", " + coords;
- }
- if (!color.empty()) {
- result += ", " + color;
- }
- result += ")";
- return result;
+ String sampleChild(int index, String coords) override {
+ return SkSL::String::printf("sample(%d%s%s)", index, coords.empty() ? "" : ", ",
+ coords.c_str());
}
};
diff --git a/gm/composecolorfilter.cpp b/gm/composecolorfilter.cpp
deleted file mode 100644
index 50460a8..0000000
--- a/gm/composecolorfilter.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "gm/gm.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColor.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkImage.h"
-#include "include/core/SkMatrix.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPoint.h"
-#include "include/core/SkRect.h"
-#include "include/core/SkRefCnt.h"
-#include "include/core/SkScalar.h"
-#include "include/core/SkShader.h"
-#include "include/core/SkSize.h"
-#include "include/core/SkString.h"
-#include "include/core/SkTileMode.h"
-#include "include/core/SkTypes.h"
-#include "include/effects/SkGradientShader.h"
-#include "include/effects/SkLumaColorFilter.h"
-#include "include/effects/SkRuntimeEffect.h"
-#include "tools/Resources.h"
-
-#include <math.h>
-
-// A tint filter maps colors to a given range (gradient), based on the input luminance:
-//
-// c' = lerp(lo, hi, luma(c))
-static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi, bool useSkSL) {
- const auto r_lo = SkColorGetR(lo),
- g_lo = SkColorGetG(lo),
- b_lo = SkColorGetB(lo),
- a_lo = SkColorGetA(lo),
- r_hi = SkColorGetR(hi),
- g_hi = SkColorGetG(hi),
- b_hi = SkColorGetB(hi),
- a_hi = SkColorGetA(hi);
-
- // We map component-wise:
- //
- // r' = lo.r + (hi.r - lo.r) * luma
- // g' = lo.g + (hi.g - lo.g) * luma
- // b' = lo.b + (hi.b - lo.b) * luma
- // a' = lo.a + (hi.a - lo.a) * luma
- //
- // The input luminance is stored in the alpha channel
- // (and RGB are cleared -- see SkLumaColorFilter). Thus:
- const float tint_matrix[] = {
- 0, 0, 0, (r_hi - r_lo) / 255.0f, SkIntToScalar(r_lo) / 255.0f,
- 0, 0, 0, (g_hi - g_lo) / 255.0f, SkIntToScalar(g_lo) / 255.0f,
- 0, 0, 0, (b_hi - b_lo) / 255.0f, SkIntToScalar(b_lo) / 255.0f,
- 0, 0, 0, (a_hi - a_lo) / 255.0f, SkIntToScalar(a_lo) / 255.0f,
- };
-
- sk_sp<SkColorFilter> inner = SkLumaColorFilter::Make(),
- outer = SkColorFilters::Matrix(tint_matrix);
-
- // Prove that we can implement compose-color-filter using runtime effects
- if (useSkSL) {
- auto [effect, error] = SkRuntimeEffect::MakeForColorFilter(SkString(R"(
- uniform colorFilter inner;
- uniform colorFilter outer;
- half4 main(half4 c) { return sample(outer, sample(inner, c)); }
- )"));
- SkASSERT(effect);
- sk_sp<SkColorFilter> children[] = { inner, outer };
- return effect->makeColorFilter(nullptr, children, SK_ARRAY_COUNT(children));
- } else {
- return outer->makeComposed(inner);
- }
-}
-
-DEF_SIMPLE_GM(composeCF, canvas, 200, 200) {
- // This GM draws a simple color-filter network, using the existing "makeComposed" API, and also
- // using a runtime color filter that does the same thing.
- SkPaint paint;
- const SkColor gradient_colors[] = {SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED};
- paint.setShader(SkGradientShader::MakeSweep(
- 50, 50, gradient_colors, nullptr, SK_ARRAY_COUNT(gradient_colors)));
-
- canvas->save();
- for (bool useSkSL : {false, true}) {
- auto cf0 = MakeTintColorFilter(0xff300000, 0xffa00000, useSkSL); // red tint
- auto cf1 = MakeTintColorFilter(0xff003000, 0xff00a000, useSkSL); // green tint
-
- paint.setColorFilter(cf0);
- canvas->drawRect({0, 0, 100, 100}, paint);
- canvas->translate(100, 0);
-
- paint.setColorFilter(cf1);
- canvas->drawRect({0, 0, 100, 100}, paint);
-
- canvas->restore();
- canvas->translate(0, 100);
- }
-}
diff --git a/gn/gm.gni b/gn/gm.gni
index 9004832..b325b51 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -98,7 +98,6 @@
"$_gm/complexclip3.cpp",
"$_gm/complexclip4.cpp",
"$_gm/complexclip_blur_tiled.cpp",
- "$_gm/composecolorfilter.cpp",
"$_gm/composeshader.cpp",
"$_gm/compositor_quads.cpp",
"$_gm/compressed_textures.cpp",
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index 1be9ad2..9c235da 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -72,17 +72,6 @@
size_t sizeInBytes() const;
};
- struct Child {
- enum class Type {
- kShader,
- kColorFilter,
- };
-
- SkString name;
- Type type;
- int index;
- };
-
struct Options {
// For testing purposes, completely disable the inliner. (Normally, Runtime Effects don't
// run the inliner directly, but they still get an inlining pass once they are painted.)
@@ -172,13 +161,13 @@
size_t uniformSize() const;
ConstIterable<Uniform> uniforms() const { return ConstIterable<Uniform>(fUniforms); }
- ConstIterable<Child> children() const { return ConstIterable<Child>(fChildren); }
+ ConstIterable<SkString> children() const { return ConstIterable<SkString>(fChildren); }
// Returns pointer to the named uniform variable's description, or nullptr if not found
const Uniform* findUniform(const char* name) const;
- // Returns pointer to the named child's description, or nullptr if not found
- const Child* findChild(const char* name) const;
+ // Returns index of the named child, or -1 if not found
+ int findChild(const char* name) const;
static void RegisterFlattenables();
~SkRuntimeEffect() override;
@@ -202,7 +191,7 @@
const Options& options,
const SkSL::FunctionDefinition& main,
std::vector<Uniform>&& uniforms,
- std::vector<Child>&& children,
+ std::vector<SkString>&& children,
std::vector<SkSL::SampleUsage>&& sampleUsages,
uint32_t flags);
@@ -219,10 +208,9 @@
bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); }
struct FilterColorInfo {
- const skvm::Program* program; // May be nullptr if it's not possible to compute
+ const skvm::Program& program;
bool alphaUnchanged;
};
- void initFilterColorInfo();
FilterColorInfo getFilterColorInfo();
#if SK_SUPPORT_GPU
@@ -239,11 +227,12 @@
std::unique_ptr<SkSL::Program> fBaseProgram;
const SkSL::FunctionDefinition& fMain;
std::vector<Uniform> fUniforms;
- std::vector<Child> fChildren;
+ std::vector<SkString> fChildren;
std::vector<SkSL::SampleUsage> fSampleUsages;
+ SkOnce fColorFilterProgramOnce;
std::unique_ptr<skvm::Program> fColorFilterProgram;
- bool fColorFilterProgramLeavesAlphaUnchanged = false;
+ bool fColorFilterProgramLeavesAlphaUnchanged;
uint32_t fFlags; // Flags
};
@@ -307,18 +296,16 @@
struct BuilderChild {
template <typename C> BuilderChild& operator=(C&& val) {
- // TODO(skbug:11813): Validate that the type of val lines up with the type of the child
- // (SkShader vs. SkColorFilter).
- if (!fChild) {
+ if (fIndex < 0) {
SkDEBUGFAIL("Assigning to missing child");
} else {
- fOwner->fChildren[fChild->index] = std::forward<C>(val);
+ fOwner->fChildren[fIndex] = std::forward<C>(val);
}
return *this;
}
- SkRuntimeEffectBuilder* fOwner;
- const SkRuntimeEffect::Child* fChild; // nullptr if the child was not found
+ SkRuntimeEffectBuilder* fOwner;
+ int fIndex; // -1 if the child was not found
};
const SkRuntimeEffect* effect() const { return fEffect.get(); }
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 87f4f27..3633e09 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -464,11 +464,12 @@
sk_sp<SkRuntimeEffect> effect = SkMakeCachedRuntimeEffect(
SkRuntimeEffect::MakeForColorFilter,
- "uniform colorFilter cf0;"
- "uniform colorFilter cf1;"
+ "uniform shader cf0;"
+ "uniform shader cf1;"
"uniform half weight;"
"half4 main(half4 color) {"
- "return mix(sample(cf0, color), sample(cf1, color), weight);"
+ "float2 moot_xy;"
+ "return mix(sample(cf0, moot_xy), sample(cf1, moot_xy), weight);"
"}"
);
SkASSERT(effect);
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 4a72713..5fd4c88 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -118,14 +118,6 @@
return false;
}
-static SkRuntimeEffect::Child::Type child_type(const SkSL::Type& type) {
- switch (type.typeKind()) {
- case SkSL::Type::TypeKind::kColorFilter: return SkRuntimeEffect::Child::Type::kColorFilter;
- case SkSL::Type::TypeKind::kShader: return SkRuntimeEffect::Child::Type::kShader;
- default: SkUNREACHABLE;
- }
-}
-
// TODO: Many errors aren't caught until we process the generated Program here. Catching those
// in the IR generator would provide better errors messages (with locations).
#define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
@@ -204,7 +196,7 @@
size_t offset = 0;
std::vector<Uniform> uniforms;
- std::vector<Child> children;
+ std::vector<SkString> children;
std::vector<SkSL::SampleUsage> sampleUsages;
const SkSL::Context& ctx(compiler->context());
@@ -220,11 +212,7 @@
// Child effects that can be sampled ('shader' or 'colorFilter')
if (varType.isEffectChild()) {
- Child c;
- c.name = var.name();
- c.type = child_type(varType);
- c.index = children.size();
- children.push_back(c);
+ children.push_back(var.name());
sampleUsages.push_back(SkSL::Analysis::GetSampleUsage(
*program, var, sampleCoordsUsage.fWrite != 0));
}
@@ -366,7 +354,7 @@
const Options& options,
const SkSL::FunctionDefinition& main,
std::vector<Uniform>&& uniforms,
- std::vector<Child>&& children,
+ std::vector<SkString>&& children,
std::vector<SkSL::SampleUsage>&& sampleUsages,
uint32_t flags)
: fHash(SkGoodHash()(sksl))
@@ -390,8 +378,6 @@
sizeof(options.forceNoInline), fHash);
fHash = SkOpts::hash_fn(&options.enforceES2Restrictions,
sizeof(options.enforceES2Restrictions), fHash);
-
- this->initFilterColorInfo();
}
SkRuntimeEffect::~SkRuntimeEffect() = default;
@@ -407,85 +393,70 @@
return iter == fUniforms.end() ? nullptr : &(*iter);
}
-const SkRuntimeEffect::Child* SkRuntimeEffect::findChild(const char* name) const {
+int SkRuntimeEffect::findChild(const char* name) const {
auto iter = std::find_if(fChildren.begin(), fChildren.end(),
- [name](const Child& c) { return c.name.equals(name); });
- return iter == fChildren.end() ? nullptr : &(*iter);
-}
-
-void SkRuntimeEffect::initFilterColorInfo() {
- // Runtime effects are often long lived & cached. So: build and save a program that can
- // filter a single color, without baking in anything tied to a particular instance
- // (uniforms or children). This isn't possible (or needed) for shaders.
- if (!this->allowColorFilter()) {
- return;
- }
-
- // We allocate a uniform color for the input color, and for each child in the SkSL. When we run
- // this program later, these uniform values are replaced with either the results of the child,
- // or the input color (if the child is nullptr). These Uniform ids are loads from the *first*
- // arg ptr.
- //
- // This scheme only works if every child is sampled using the original input color. If we detect
- // a sampleChild call where a different color is being supplied, we bail out, and the returned
- // info will have a null program. (Callers will need to fall back to another implementation.)
- skvm::Builder p;
- skvm::Uniforms childColorUniforms{p.uniform(), 0};
- skvm::Color inputColor = p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms);
- std::vector<skvm::Color> childColors;
- for (size_t i = 0; i < fChildren.size(); ++i) {
- childColors.push_back(
- p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms));
- }
- bool allSampleCallsPassInputColor = true;
- auto sampleChild = [&](int ix, skvm::Coord, skvm::Color color) {
- if (color.r.id != inputColor.r.id ||
- color.g.id != inputColor.g.id ||
- color.b.id != inputColor.b.id ||
- color.a.id != inputColor.a.id) {
- allSampleCallsPassInputColor = false;
- }
- return childColors[ix];
- };
-
- // For SkSL uniforms, we reserve space and allocate skvm Uniform ids for each one. When we run
- // the program, these ids will be loads from the *second* arg ptr, the uniform data of the
- // specific color filter instance.
- skvm::Uniforms skslUniforms{p.uniform(), 0};
- const size_t uniformCount = this->uniformSize() / 4;
- std::vector<skvm::Val> uniform;
- uniform.reserve(uniformCount);
- for (size_t i = 0; i < uniformCount; i++) {
- uniform.push_back(p.uniform32(skslUniforms.push(/*placeholder*/ 0)).id);
- }
-
- // Emit the skvm instructions for the SkSL
- skvm::Coord zeroCoord = {p.splat(0.0f), p.splat(0.0f)};
- skvm::Color result = SkSL::ProgramToSkVM(*fBaseProgram,
- fMain,
- &p,
- uniform,
- /*device=*/zeroCoord,
- /*local=*/zeroCoord,
- inputColor,
- sampleChild);
-
- // Then store the result to the *third* arg ptr
- p.store({skvm::PixelFormat::FLOAT, 32, 32, 32, 32, 0, 32, 64, 96}, p.arg(16), result);
-
- // This is conservative. If a filter gets the input color by sampling a null child, we'll
- // return an (acceptable) false negative. All internal runtime color filters should work.
- fColorFilterProgramLeavesAlphaUnchanged = (inputColor.a.id == result.a.id);
-
- // We'll use this program to filter one color at a time, don't bother with jit
- fColorFilterProgram = allSampleCallsPassInputColor
- ? std::make_unique<skvm::Program>(
- p.done(/*debug_name=*/nullptr, /*allow_jit=*/false))
- : nullptr;
+ [name](const SkString& s) { return s.equals(name); });
+ return iter == fChildren.end() ? -1 : static_cast<int>(iter - fChildren.begin());
}
SkRuntimeEffect::FilterColorInfo SkRuntimeEffect::getFilterColorInfo() {
- return {fColorFilterProgram.get(), fColorFilterProgramLeavesAlphaUnchanged};
+ SkASSERT(this->allowColorFilter());
+
+ fColorFilterProgramOnce([&] {
+ // Runtime effects are often long lived & cached. So: build and save a program that can
+ // filter a single color, without baking in anything tied to a particular instance
+ // (uniforms or children).
+ skvm::Builder p;
+
+ // We allocate a uniform color for the input color, and for each child in the SkSL.
+ // When we run this program later, these uniform values are replaced with either the
+ // results of the child, or the input color (if the child is nullptr). These Uniform ids
+ // are loads from the *first* arg ptr.
+ skvm::Uniforms childColorUniforms{p.uniform(), 0};
+ skvm::Color inputColor =
+ p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms);
+ std::vector<skvm::Color> childColors;
+ for (size_t i = 0; i < fChildren.size(); ++i) {
+ childColors.push_back(
+ p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms));
+ }
+ auto sampleChild = [&](int ix, skvm::Coord) { return childColors[ix]; };
+
+ // For SkSL uniforms, we reserve space and allocate skvm Uniform ids for each one.
+ // When we run the program, these ids will be loads from the *second* arg ptr, the
+ // uniform data of the specific color filter instance.
+ skvm::Uniforms skslUniforms{p.uniform(), 0};
+ const size_t uniformCount = this->uniformSize() / 4;
+ std::vector<skvm::Val> uniform;
+ uniform.reserve(uniformCount);
+ for (size_t i = 0; i < uniformCount; i++) {
+ uniform.push_back(p.uniform32(skslUniforms.push(/*placeholder*/ 0)).id);
+ }
+
+ // Emit the skvm instructions for the SkSL
+ skvm::Coord zeroCoord = { p.splat(0.0f), p.splat(0.0f) };
+ skvm::Color result = SkSL::ProgramToSkVM(*fBaseProgram,
+ fMain,
+ &p,
+ uniform,
+ /*device=*/zeroCoord,
+ /*local=*/zeroCoord,
+ inputColor,
+ sampleChild);
+
+ // Then store the result to the *third* arg ptr
+ p.store({skvm::PixelFormat::FLOAT, 32,32,32,32, 0,32,64,96}, p.arg(16), result);
+
+ // This is conservative. If a filter gets the input color by sampling a null child, we'll
+ // return an (acceptable) false negative. All internal runtime color filters should work.
+ fColorFilterProgramLeavesAlphaUnchanged = (inputColor.a.id == result.a.id);
+
+ // We'll use this program to filter one color at a time, don't bother with jit
+ fColorFilterProgram = std::make_unique<skvm::Program>(
+ p.done(/*debug_name=*/nullptr, /*allow_jit=*/false));
+ });
+
+ return {*fColorFilterProgram, fColorFilterProgramLeavesAlphaUnchanged};
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -590,11 +561,11 @@
// something. (Uninitialized values can trigger asserts in skvm::Builder).
skvm::Coord zeroCoord = { p->splat(0.0f), p->splat(0.0f) };
- auto sampleChild = [&](int ix, skvm::Coord /*coord*/, skvm::Color color) {
+ auto sampleChild = [&](int ix, skvm::Coord /*coord*/) {
if (fChildren[ix]) {
- return as_CFB(fChildren[ix])->program(p, color, dstCS, uniforms, alloc);
+ return as_CFB(fChildren[ix])->program(p, c, dstCS, uniforms, alloc);
} else {
- return color;
+ return c;
}
};
@@ -613,12 +584,7 @@
SkPMColor4f onFilterColor4f(const SkPMColor4f& color, SkColorSpace* dstCS) const override {
// Get the generic program for filtering a single color
- const skvm::Program* program = fEffect->getFilterColorInfo().program;
- if (!program) {
- // We were unable to build a cached (per-effect) program. Use the base-class fallback,
- // which builds a program for the specific filter instance.
- return SkColorFilterBase::onFilterColor4f(color, dstCS);
- }
+ const skvm::Program& program = fEffect->getFilterColorInfo().program;
// Get our specific uniform values
sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dstCS);
@@ -635,7 +601,7 @@
}
SkPMColor4f result;
- program->eval(1, inputColors.begin(), inputs->data(), result.vec());
+ program.eval(1, inputColors.begin(), inputs->data(), result.vec());
return result;
}
@@ -712,14 +678,9 @@
get_xformed_uniforms(fEffect.get(), fUniforms, args.fDstColorInfo->colorSpace());
SkASSERT(uniforms);
- // If we sample children with explicit colors, this may not be true.
- // TODO: Determine this via analysis?
- GrFPArgs childArgs = args;
- childArgs.fInputColorIsOpaque = false;
-
auto fp = GrSkSLFP::Make(fEffect, "runtime_shader", std::move(uniforms));
for (const auto& child : fChildren) {
- auto childFP = child ? as_SB(child)->asFragmentProcessor(childArgs) : nullptr;
+ auto childFP = child ? as_SB(child)->asFragmentProcessor(args) : nullptr;
fp->addChild(std::move(childFP));
}
std::unique_ptr<GrFragmentProcessor> result = std::move(fp);
@@ -762,14 +723,14 @@
}
local = SkShaderBase::ApplyMatrix(p,inv,local,uniforms);
- auto sampleChild = [&](int ix, skvm::Coord coord, skvm::Color color) {
+ auto sampleChild = [&](int ix, skvm::Coord coord) {
if (fChildren[ix]) {
SkOverrideDeviceMatrixProvider mats{matrices, SkMatrix::I()};
- return as_SB(fChildren[ix])->program(p, device, coord, color,
+ return as_SB(fChildren[ix])->program(p, device, coord, paint,
mats, nullptr, dst,
uniforms, alloc);
} else {
- return color;
+ return paint;
}
};
@@ -889,22 +850,10 @@
if (!uniforms) {
uniforms = SkData::MakeEmpty();
}
- // Verify that all child objects are shaders (to match the C++ types here).
- // TODO(skia:11813) When we support shader and colorFilter children (with different samplng
- // semantics), the 'children' parameter will contain both types, so this will be more complex.
- if (!std::all_of(fChildren.begin(), fChildren.end(), [](const Child& c) {
- return c.type == Child::Type::kShader;
- })) {
- return nullptr;
- }
return uniforms->size() == this->uniformSize() && childCount == fChildren.size()
- ? sk_sp<SkShader>(new SkRTShader(sk_ref_sp(this),
- std::move(uniforms),
- localMatrix,
- children,
- childCount,
- isOpaque))
- : nullptr;
+ ? sk_sp<SkShader>(new SkRTShader(sk_ref_sp(this), std::move(uniforms), localMatrix,
+ children, childCount, isOpaque))
+ : nullptr;
}
sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* recordingContext,
@@ -994,18 +943,10 @@
if (!uniforms) {
uniforms = SkData::MakeEmpty();
}
- // Verify that all child objects are color filters (to match the C++ types here).
- // TODO(skia:11813) When we support shader and colorFilter children (with different samplng
- // semantics), the 'children' parameter will contain both types, so this will be more complex.
- if (!std::all_of(fChildren.begin(), fChildren.end(), [](const Child& c) {
- return c.type == Child::Type::kColorFilter;
- })) {
- return nullptr;
- }
return uniforms->size() == this->uniformSize() && childCount == fChildren.size()
- ? sk_sp<SkColorFilter>(new SkRuntimeColorFilter(
- sk_ref_sp(this), std::move(uniforms), children, childCount))
- : nullptr;
+ ? sk_sp<SkColorFilter>(new SkRuntimeColorFilter(sk_ref_sp(this), std::move(uniforms),
+ children, childCount))
+ : nullptr;
}
sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms) const {
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index 8a353aa..d78ef7e 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -95,7 +95,7 @@
fArgs.fFragBuilder->definitionAppend(declaration);
}
- String sampleChild(int index, String coords, String color) override {
+ String sampleChild(int index, String coords) override {
// If the child was sampled using the coords passed to main (and they are never
// modified), then we will have marked the child as PassThrough. The code generator
// doesn't know that, and still supplies coords. Inside invokeChild, we assert that
@@ -112,11 +112,7 @@
if (child && !child->isSampledWithExplicitCoords()) {
coords.clear();
}
- return String(fSelf->invokeChild(index,
- color.empty() ? fInputColor : color.c_str(),
- fArgs,
- coords)
- .c_str());
+ return String(fSelf->invokeChild(index, fInputColor, fArgs, coords).c_str());
}
GrGLSLSkSLFP* fSelf;
@@ -190,11 +186,12 @@
return std::unique_ptr<GrSkSLFP>(new GrSkSLFP(std::move(effect), name, std::move(uniforms)));
}
-GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name, sk_sp<SkData> uniforms)
+GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect,
+ const char* name,
+ sk_sp<SkData> uniforms)
: INHERITED(kGrSkSLFP_ClassID,
- effect->getFilterColorInfo().program
- ? kConstantOutputForConstantInput_OptimizationFlag
- : kNone_OptimizationFlags)
+ effect->allowColorFilter() ? kConstantOutputForConstantInput_OptimizationFlag
+ : kNone_OptimizationFlags)
, fEffect(std::move(effect))
, fName(name)
, fUniforms(std::move(uniforms)) {
@@ -248,8 +245,7 @@
}
SkPMColor4f GrSkSLFP::constantOutputForConstantInput(const SkPMColor4f& inputColor) const {
- const skvm::Program* program = fEffect->getFilterColorInfo().program;
- SkASSERT(program);
+ const skvm::Program& program = fEffect->getFilterColorInfo().program;
SkSTArray<3, SkPMColor4f, true> childColors;
childColors.push_back(inputColor);
@@ -258,7 +254,7 @@
}
SkPMColor4f result;
- program->eval(1, childColors.begin(), fUniforms->data(), result.vec());
+ program.eval(1, childColors.begin(), fUniforms->data(), result.vec());
return result;
}
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
index 5c71d7b..e8b3620 100644
--- a/src/sksl/SkSLMain.cpp
+++ b/src/sksl/SkSLMain.cpp
@@ -439,19 +439,14 @@
fOutput += declaration;
}
- String sampleChild(int index, String coords, String color) override {
- String result = "sample(child_" + SkSL::to_string(index);
- if (!coords.empty()) {
- result += ", " + coords;
- }
- if (!color.empty()) {
- result += ", " + color;
- }
- result += ")";
- return result;
+ String sampleChild(int index, String coords) override {
+ return String::printf("sample(child_%d%s%s)",
+ index,
+ coords.empty() ? "" : ", ",
+ coords.c_str());
}
- String fOutput;
+ String fOutput;
};
Callbacks callbacks;
SkSL::PipelineStage::ConvertProgram(program, "_coords", "_inColor", &callbacks);
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
index e555c4f..979d75d 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
@@ -144,17 +144,16 @@
const FunctionDeclaration& function = c.function();
const ExpressionArray& arguments = c.arguments();
if (function.isBuiltin() && function.name() == "sample") {
- SkASSERT(arguments.size() == 2);
- const Expression* child = arguments[0].get();
- SkASSERT(child->type().isEffectChild());
- SkASSERT(child->is<VariableReference>());
+ SkASSERT(arguments.size() <= 2);
+ SkASSERT(arguments[0]->type().isEffectChild());
+ SkASSERT(arguments[0]->is<VariableReference>());
int index = 0;
bool found = false;
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
- if (&decl.var() == child->as<VariableReference>().variable()) {
+ if (&decl.var() == arguments[0]->as<VariableReference>().variable()) {
found = true;
} else if (decl.var().type().isEffectChild()) {
++index;
@@ -166,25 +165,14 @@
}
SkASSERT(found);
- // Shaders require a coordinate argument. Color filters require a color argument.
- // When we call sampleChild, the other value remains empty.
- String color;
String coords;
- {
+ if (arguments.size() > 1) {
AutoOutputBuffer outputToBuffer(this);
- this->writeExpression(*arguments.back(), Precedence::kSequence);
- if (child->type().typeKind() == Type::TypeKind::kShader) {
- SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fFloat2);
- coords = outputToBuffer.fBuffer.str();
- } else {
- SkASSERT(child->type().typeKind() == Type::TypeKind::kColorFilter);
- SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
- arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
- color = outputToBuffer.fBuffer.str();
- }
+ this->writeExpression(*arguments[1], Precedence::kSequence);
+ coords = outputToBuffer.fBuffer.str();
}
- this->write(fCallbacks->sampleChild(index, std::move(coords), std::move(color)));
+ this->write(fCallbacks->sampleChild(index, std::move(coords)));
return;
}
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h
index bf30482..f00cfed 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h
@@ -28,7 +28,7 @@
virtual void declareGlobal(const char* declaration) = 0;
virtual String declareUniform(const VarDeclaration*) = 0;
- virtual String sampleChild(int index, String coords, String color) = 0;
+ virtual String sampleChild(int index, String coords) = 0;
};
/*
diff --git a/src/sksl/codegen/SkSLVMCodeGenerator.cpp b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
index d0dc4dc..7a772d9 100644
--- a/src/sksl/codegen/SkSLVMCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
@@ -116,7 +116,6 @@
SkSpan<skvm::Val> uniforms,
skvm::Coord device,
skvm::Coord local,
- skvm::Color inputColor,
SampleChildFn sampleChild);
void writeFunction(const FunctionDefinition& function,
@@ -288,7 +287,6 @@
skvm::Builder* fBuilder;
const skvm::Coord fLocalCoord;
- const skvm::Color fInputColor;
const SampleChildFn fSampleChild;
// [Variable, first slot in fSlots]
@@ -346,12 +344,10 @@
SkSpan<skvm::Val> uniforms,
skvm::Coord device,
skvm::Coord local,
- skvm::Color inputColor,
SampleChildFn sampleChild)
: fProgram(program)
, fBuilder(builder)
, fLocalCoord(local)
- , fInputColor(inputColor)
, fSampleChild(std::move(sampleChild)) {
fConditionMask = fLoopMask = fBuilder->splat(0xffff'ffff);
@@ -1000,32 +996,25 @@
if (found->second == Intrinsic::kSample) {
// Sample is very special, the first argument is a child (shader/colorFilter), which can't
// be evaluated
- SkASSERT(nargs == 2);
- const Expression* child = c.arguments()[0].get();
- SkASSERT(child->type().isEffectChild());
- SkASSERT(child->is<VariableReference>());
-
- auto fp_it = fVariableMap.find(child->as<VariableReference>().variable());
- SkASSERT(fp_it != fVariableMap.end());
-
- // Shaders require a coordinate argument. Color filters require a color argument.
- // When we call sampleChild, the other value remains the incoming default.
- skvm::Color inColor = fInputColor;
- skvm::Coord coord = fLocalCoord;
- const Expression* arg = c.arguments()[1].get();
- Value argVal = this->writeExpression(*arg);
-
- if (child->type().typeKind() == Type::TypeKind::kShader) {
- SkASSERT(arg->type() == *fProgram.fContext->fTypes.fFloat2);
- coord = {f32(argVal[0]), f32(argVal[1])};
- } else {
- SkASSERT(child->type().typeKind() == Type::TypeKind::kColorFilter);
- SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
- arg->type() == *fProgram.fContext->fTypes.fFloat4);
- inColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
+ const Context& ctx = *fProgram.fContext;
+ if (nargs > 2 || !c.arguments()[0]->type().isEffectChild() ||
+ (nargs == 2 && (c.arguments()[1]->type() != *ctx.fTypes.fFloat2 &&
+ c.arguments()[1]->type() != *ctx.fTypes.fFloat3x3))) {
+ SkDEBUGFAIL("Invalid call to sample");
+ return {};
}
- skvm::Color color = fSampleChild(fp_it->second, coord, inColor);
+ auto fp_it = fVariableMap.find(c.arguments()[0]->as<VariableReference>().variable());
+ SkASSERT(fp_it != fVariableMap.end());
+
+ skvm::Coord coord = fLocalCoord;
+ if (nargs == 2) {
+ Value arg = this->writeExpression(*c.arguments()[1]);
+ SkASSERT(arg.slots() == 2);
+ coord = {f32(arg[0]), f32(arg[1])};
+ }
+
+ skvm::Color color = fSampleChild(fp_it->second, coord);
Value result(4);
result[0] = color.r;
result[1] = color.g;
@@ -1705,8 +1694,7 @@
}
SkASSERT(argSlots <= SK_ARRAY_COUNT(args));
- SkVMGenerator generator(
- program, builder, uniforms, device, local, inputColor, std::move(sampleChild));
+ SkVMGenerator generator(program, builder, uniforms, device, local, std::move(sampleChild));
generator.writeFunction(function, {args, argSlots}, result);
return skvm::Color{{builder, result[0]},
@@ -1744,11 +1732,9 @@
returnVals.push_back(b->splat(0.0f).id);
}
- skvm::F32 zero = b->splat(0.0f);
- skvm::Coord zeroCoord = {zero, zero};
- skvm::Color zeroColor = {zero, zero, zero, zero};
+ skvm::Coord zeroCoord = {b->splat(0.0f), b->splat(0.0f)};
SkVMGenerator generator(program, b, uniforms, /*device=*/zeroCoord, /*local=*/zeroCoord,
- /*inputColor=*/zeroColor, /*sampleChild=*/{});
+ /*sampleChild=*/{});
generator.writeFunction(function, argVals, returnVals);
// generateCode has updated the contents of 'argVals' for any 'out' or 'inout' parameters.
@@ -1865,7 +1851,7 @@
children.push_back({uniforms.pushPtr(nullptr), builder->uniform32(uniforms.push(0))});
}
- auto sampleChild = [&](int i, skvm::Coord coord, skvm::Color) {
+ auto sampleChild = [&](int i, skvm::Coord coord) {
skvm::PixelFormat pixelFormat = skvm::SkColorType_to_PixelFormat(kRGBA_F32_SkColorType);
skvm::I32 index = trunc(coord.x);
index += trunc(coord.y) * children[i].rowBytesAsPixels;
diff --git a/src/sksl/codegen/SkSLVMCodeGenerator.h b/src/sksl/codegen/SkSLVMCodeGenerator.h
index 2ef8f9b..3a13d7a 100644
--- a/src/sksl/codegen/SkSLVMCodeGenerator.h
+++ b/src/sksl/codegen/SkSLVMCodeGenerator.h
@@ -20,7 +20,10 @@
class FunctionDefinition;
struct Program;
-using SampleChildFn = std::function<skvm::Color(int, skvm::Coord, skvm::Color)>;
+using SampleChildFn = std::function<skvm::Color(int, skvm::Coord)>;
+
+// TODO: Have a generic entry point, supporting SkSpan<skvm::Val> for parameters and return values.
+// That would be useful for interpreter use cases like SkParticleEffect.
// Convert 'function' to skvm instructions in 'builder', for use by shaders and color filters
skvm::Color ProgramToSkVM(const Program& program,
diff --git a/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl b/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
index 7c9be5c..5459f25 100644
--- a/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
+++ b/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
@@ -1,14 +1,11 @@
-static uint8_t SKSL_INCLUDE_sksl_rt_colorfilter[] = {56,0,
+static uint8_t SKSL_INCLUDE_sksl_rt_colorfilter[] = {36,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,
-49,7,0,
+49,3,0,
53,1,0,
16,2,0,
50,2,0,4,0,3,
@@ -17,20 +14,8 @@
50,4,0,18,0,3,
30,5,0,
16,25,0,2,1,0,3,0,
-50,6,0,32,0,
-53,7,0,
-16,38,0,
-50,8,0,40,0,3,
-53,9,0,
-16,52,0,
-47,6,0,3,
-52,10,0,2,
-47,5,0,
-30,11,0,
-16,25,0,2,7,0,9,0,
-47,6,0,
-47,11,0,1,0,
-5,0,
+50,6,0,32,0,1,0,
+2,0,
19,
20,};
static constexpr size_t SKSL_INCLUDE_sksl_rt_colorfilter_LENGTH = sizeof(SKSL_INCLUDE_sksl_rt_colorfilter);
diff --git a/src/sksl/generated/sksl_rt_shader.dehydrated.sksl b/src/sksl/generated/sksl_rt_shader.dehydrated.sksl
index f0e0516..ced2f98 100644
--- a/src/sksl/generated/sksl_rt_shader.dehydrated.sksl
+++ b/src/sksl/generated/sksl_rt_shader.dehydrated.sksl
@@ -1,4 +1,4 @@
-static uint8_t SKSL_INCLUDE_sksl_rt_shader[] = {77,0,
+static uint8_t SKSL_INCLUDE_sksl_rt_shader[] = {57,0,
0,
12,115,107,95,70,114,97,103,67,111,111,114,100,
6,102,108,111,97,116,52,
@@ -8,10 +8,7 @@
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,
-49,8,0,
+49,4,0,
53,1,0,
37,
36,0,32,0,0,255,255,255,255,255,15,0,255,255,255,255,2,0,0,0,3,0,
@@ -24,20 +21,8 @@
50,6,0,39,0,3,
30,7,0,
16,46,0,2,3,0,5,0,
-50,8,0,53,0,
-53,9,0,
-16,59,0,
-50,10,0,61,0,3,
-53,11,0,
-16,73,0,
-47,8,0,3,
-52,12,0,2,
-47,7,0,
-30,13,0,
-16,46,0,2,9,0,11,0,
-47,8,0,
-47,13,0,2,0,
-6,0,
+50,8,0,53,0,2,0,
+3,0,
0,0,
19,
55,
diff --git a/src/sksl/sksl_rt_colorfilter.sksl b/src/sksl/sksl_rt_colorfilter.sksl
index 06b4a42..1f6183a 100644
--- a/src/sksl/sksl_rt_colorfilter.sksl
+++ b/src/sksl/sksl_rt_colorfilter.sksl
@@ -1,2 +1,5 @@
half4 sample(shader s, float2 coords);
-half4 sample(colorFilter f, half4 color);
+
+// TODO(skbug.com/11813): Implement sample() that takes a color
+// half4 sample(colorFilter f, half4 color);
+// half4 sample(shader s, half4 color, float2 coords);
diff --git a/src/sksl/sksl_rt_shader.sksl b/src/sksl/sksl_rt_shader.sksl
index e259cc3..e098bad 100644
--- a/src/sksl/sksl_rt_shader.sksl
+++ b/src/sksl/sksl_rt_shader.sksl
@@ -1,4 +1,7 @@
layout(builtin=15) float4 sk_FragCoord;
half4 sample(shader s, float2 coords);
-half4 sample(colorFilter f, half4 color);
+
+// TODO(skbug.com/11813): Implement sample() that takes a color
+// half4 sample(colorFilter f, half4 color);
+// half4 sample(shader s, half4 color, float2 coords);
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index 88fce0b..a6ba883 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -128,43 +128,15 @@
// Sampling a child shader requires that we pass explicit coords
test_valid("uniform shader child;"
"half4 main(half4 c) { return sample(child, c.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 c) { return sample(child, c.rg, c); }",
- "no match for sample(shader, half2, half4)");
- // Shader with just a color
- test_invalid("uniform shader child;"
- "half4 main(half4 c) { return sample(child, c); }",
- "no match for sample(shader, half4)");
- // Coords and color in a differet order
- test_invalid("uniform shader child;"
- "half4 main(half4 c) { return sample(child, c, c.rg); }",
- "no match for sample(shader, half4, half2)");
-
- // Older variants that are no longer allowed
test_invalid(
"uniform shader child;"
"half4 main(half4 c) { return sample(child); }",
- "no match for sample(shader)");
+ "expected 2 arguments");
test_invalid(
"uniform shader child;"
"half4 main(half4 c) { 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 c) { return sample(child, c); }");
-
- test_invalid("uniform colorFilter child;"
- "half4 main(half4 c) { return sample(child); }",
- "sample(colorFilter)");
- test_invalid("uniform colorFilter child;"
- "half4 main(half4 c) { return sample(child, c.rg); }",
- "sample(colorFilter, half2)");
- test_invalid("uniform colorFilter child;"
- "half4 main(half4 c) { return sample(child, c.rg, c); }",
- "sample(colorFilter, half2, half4)");
+ "expected 'float2'");
}
DEF_TEST(SkRuntimeEffectForShader, r) {
@@ -213,43 +185,14 @@
test_valid("uniform shader child;"
"half4 main(float2 p) { return sample(child, p); }");
- // Trying to pass a color as well. (Works internally with FPs, but not in runtime effects).
- test_invalid("uniform shader child;"
- "half4 main(float2 p, half4 c) { return sample(child, p, c); }",
- "no match for sample(shader, float2, half4)");
-
- // Shader with just a color
- test_invalid("uniform shader child;"
- "half4 main(float2 p, half4 c) { return sample(child, c); }",
- "no match for sample(shader, half4)");
- // Coords and color in a different order
- test_invalid("uniform shader child;"
- "half4 main(float2 p, half4 c) { return sample(child, c, p); }",
- "no match for sample(shader, half4, float2)");
-
- // Older variants that are no longer allowed
test_invalid(
"uniform shader child;"
"half4 main(float2 p) { return sample(child); }",
- "no match for sample(shader)");
+ "expected 2 arguments");
test_invalid(
"uniform shader child;"
"half4 main(float2 p) { 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(float2 p, half4 c) { return sample(child, c); }");
-
- test_invalid("uniform colorFilter child;"
- "half4 main(float2 p) { return sample(child); }",
- "sample(colorFilter)");
- test_invalid("uniform colorFilter child;"
- "half4 main(float2 p) { return sample(child, p); }",
- "sample(colorFilter, float2)");
- test_invalid("uniform colorFilter child;"
- "half4 main(float2 p, half4 c) { return sample(child, p, c); }",
- "sample(colorFilter, float2, half4)");
+ "expected 'float2'");
}
class TestEffect {
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index e4b352d..53c1d0b 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -1754,7 +1754,7 @@
EXPECT_EQUAL(Sample(shader, Float2(0, 0)), "sample(shader, float2(0.0, 0.0))");
{
- ExpectError error(r, "error: no match for sample(shader, half4)\n");
+ ExpectError error(r, "error: expected 'float2', but found 'half4'\n");
Sample(shader, Half4(1)).release();
}
}
diff --git a/tools/viewer/SkSLSlide.cpp b/tools/viewer/SkSLSlide.cpp
index 7df9cc8..9d06fce 100644
--- a/tools/viewer/SkSLSlide.cpp
+++ b/tools/viewer/SkSLSlide.cpp
@@ -217,17 +217,15 @@
}
}
- for (const auto& c : fEffect->children()) {
- auto curShader =
- std::find_if(fShaders.begin(), fShaders.end(), [tgt = fChildren[c.index]](auto p) {
- return p.second == tgt;
- });
- SkASSERT(curShader != fShaders.end());
+ for (const auto& [i, name] : SkMakeEnumerate(fEffect->children())) {
+ auto curShader = std::find_if(fShaders.begin(), fShaders.end(),
+ [tgt = fChildren[i]](auto p) { return p.second == tgt; });
+ SkASSERT(curShader!= fShaders.end());
- if (ImGui::BeginCombo(c.name.c_str(), curShader->first)) {
+ if (ImGui::BeginCombo(name.c_str(), curShader->first)) {
for (const auto& namedShader : fShaders) {
if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
- fChildren[c.index] = namedShader.second;
+ fChildren[i] = namedShader.second;
}
}
ImGui::EndCombo();