Disallow sk_FragCoord in SkRuntimeEffect
It's still accessible internally (and by default when using
SkMakeRuntimeEffect), but we don't want clients using this.
If we can come up with a simple and consistent coordinate
model that doesn't expose implementation details, we'll
revisit this.
Change-Id: I77726830480a286541ae887f9cd9822eb10ea913
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430422
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index 6b0fa6f..f3eb3f5 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -104,6 +104,11 @@
// still largely ES3-unaware and can still fail or crash if post-ES2 features are used.
// This is only intended for use by tests and certain internally created effects.
bool enforceES2Restrictions = true;
+
+ // Similarly: Public SkSL does not allow access to sk_FragCoord. The semantics of that
+ // variable are confusing, and expose clients to implementation details of saveLayer and
+ // image filters.
+ bool allowFragCoord = false;
};
// If the effect is compiled successfully, `effect` will be non-null.
@@ -118,9 +123,15 @@
// values are flexible. They are listed as being 'vec4', but they can also be 'half4' or
// 'float4'. ('vec4' is an alias for 'float4').
+ // We can't use a default argument for `options` due to a bug in Clang.
+ // https://bugs.llvm.org/show_bug.cgi?id=36684
+
// Color filter SkSL requires an entry point that looks like:
// vec4 main(vec4 inColor) { ... }
static Result MakeForColorFilter(SkString sksl, const Options&);
+ static Result MakeForColorFilter(SkString sksl) {
+ return MakeForColorFilter(std::move(sksl), Options{});
+ }
// Shader SkSL requires an entry point that looks like:
// vec4 main(vec2 inCoords) { ... }
@@ -129,27 +140,25 @@
//
// Most shaders don't use the input color, so that parameter is optional.
static Result MakeForShader(SkString sksl, const Options&);
+ static Result MakeForShader(SkString sksl) {
+ return MakeForShader(std::move(sksl), Options{});
+ }
// Blend SkSL requires an entry point that looks like:
// vec4 main(vec4 srcColor, vec4 dstColor) { ... }
static Result MakeForBlender(SkString sksl, const Options&);
-
- // We can't use a default argument for `options` due to a bug in Clang.
- // https://bugs.llvm.org/show_bug.cgi?id=36684
- static Result MakeForColorFilter(SkString sksl) {
- return MakeForColorFilter(std::move(sksl), Options{});
- }
- static Result MakeForShader(SkString sksl) {
- return MakeForShader(std::move(sksl), Options{});
- }
static Result MakeForBlender(SkString sksl) {
return MakeForBlender(std::move(sksl), Options{});
}
+ // DSL entry points
+ static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program, const Options&);
static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program);
+ static Result MakeForShader(std::unique_ptr<SkSL::Program> program, const Options&);
static Result MakeForShader(std::unique_ptr<SkSL::Program> program);
+ static Result MakeForBlender(std::unique_ptr<SkSL::Program> program, const Options&);
static Result MakeForBlender(std::unique_ptr<SkSL::Program> program);
// Object that allows passing either an SkShader or SkColorFilter as a child
@@ -237,13 +246,15 @@
std::vector<SkSL::SampleUsage>&& sampleUsages,
uint32_t flags);
- static Result Make(std::unique_ptr<SkSL::Program> program, SkSL::ProgramKind kind);
+ static Result MakeFromSource(SkString sksl, const Options& options, SkSL::ProgramKind kind);
- static Result Make(SkString sksl, const Options& options, SkSL::ProgramKind kind);
+ static Result MakeFromDSL(std::unique_ptr<SkSL::Program> program,
+ const Options& options,
+ SkSL::ProgramKind kind);
- static Result Make(std::unique_ptr<SkSL::Program> program,
- const Options& options,
- SkSL::ProgramKind kind);
+ static Result MakeInternal(std::unique_ptr<SkSL::Program> program,
+ const Options& options,
+ SkSL::ProgramKind kind);
uint32_t hash() const { return fHash; }
bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); }
diff --git a/include/sksl/DSLRuntimeEffects.h b/include/sksl/DSLRuntimeEffects.h
index 194850f..3d9b990 100644
--- a/include/sksl/DSLRuntimeEffects.h
+++ b/include/sksl/DSLRuntimeEffects.h
@@ -8,10 +8,9 @@
#ifndef SKSL_DSL_RUNTIME_EFFECTS
#define SKSL_DSL_RUNTIME_EFFECTS
+#include "include/effects/SkRuntimeEffect.h"
#include "include/sksl/DSL.h"
-class SkRuntimeEffect;
-
namespace SkSL {
class Compiler;
@@ -22,7 +21,7 @@
void StartRuntimeShader(SkSL::Compiler* compiler);
-sk_sp<SkRuntimeEffect> EndRuntimeShader();
+sk_sp<SkRuntimeEffect> EndRuntimeShader(SkRuntimeEffect::Options options = {});
#endif
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 63c3965..f62aea8 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -165,8 +165,9 @@
// in the IR generator would provide better errors messages (with locations).
#define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
-SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& options,
- SkSL::ProgramKind kind) {
+SkRuntimeEffect::Result SkRuntimeEffect::MakeFromSource(SkString sksl,
+ const Options& options,
+ SkSL::ProgramKind kind) {
std::unique_ptr<SkSL::Program> program;
{
// We keep this SharedCompiler in a separate scope to make sure it's destroyed before
@@ -184,20 +185,21 @@
RETURN_FAILURE("%s", compiler->errorText().c_str());
}
}
- return Make(std::move(program), options, kind);
+ return MakeInternal(std::move(program), options, kind);
}
-SkRuntimeEffect::Result SkRuntimeEffect::Make(std::unique_ptr<SkSL::Program> program,
- SkSL::ProgramKind kind) {
+SkRuntimeEffect::Result SkRuntimeEffect::MakeFromDSL(std::unique_ptr<SkSL::Program> program,
+ const Options& options,
+ SkSL::ProgramKind kind) {
// This factory is used for all DSL runtime effects, which don't have anything stored in the
// program's source. Populate it so that we can compute fHash, and serialize these effects.
program->fSource = std::make_unique<SkSL::String>(program->description());
- return Make(std::move(program), Options{}, kind);
+ return MakeInternal(std::move(program), options, kind);
}
-SkRuntimeEffect::Result SkRuntimeEffect::Make(std::unique_ptr<SkSL::Program> program,
- const Options& options,
- SkSL::ProgramKind kind) {
+SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Program> program,
+ const Options& options,
+ SkSL::ProgramKind kind) {
SkSL::SharedCompiler compiler;
// Find 'main', then locate the sample coords parameter. (It might not be present.)
@@ -225,6 +227,12 @@
flags |= kUsesSampleCoords_Flag;
}
+ // TODO(skia:12202): When we can layer modules, implement this restriction by moving the
+ // declaration of sk_FragCoord to a private module.
+ if (!options.allowFragCoord && SkSL::Analysis::ReferencesFragCoords(*program)) {
+ RETURN_FAILURE("unknown identifier 'sk_FragCoord'");
+ }
+
// Color filters and blends are not allowed to depend on position (local or device) in any way.
// The signature of main, and the declarations in sksl_rt_colorfilter/sksl_rt_blend should
// guarantee this.
@@ -322,41 +330,57 @@
}
SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(SkString sksl, const Options& options) {
- auto result = Make(std::move(sksl), options, SkSL::ProgramKind::kRuntimeColorFilter);
+ auto result = MakeFromSource(std::move(sksl), options, SkSL::ProgramKind::kRuntimeColorFilter);
SkASSERT(!result.effect || result.effect->allowColorFilter());
return result;
}
SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(SkString sksl, const Options& options) {
- auto result = Make(std::move(sksl), options, SkSL::ProgramKind::kRuntimeShader);
+ auto result = MakeFromSource(std::move(sksl), options, SkSL::ProgramKind::kRuntimeShader);
SkASSERT(!result.effect || result.effect->allowShader());
return result;
}
SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(SkString sksl, const Options& options) {
- auto result = Make(std::move(sksl), options, SkSL::ProgramKind::kRuntimeBlender);
+ auto result = MakeFromSource(std::move(sksl), options, SkSL::ProgramKind::kRuntimeBlender);
SkASSERT(!result.effect || result.effect->allowBlender());
return result;
}
-SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(std::unique_ptr<SkSL::Program> program) {
- auto result = Make(std::move(program), SkSL::ProgramKind::kRuntimeColorFilter);
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(std::unique_ptr<SkSL::Program> program,
+ const Options& options) {
+ auto result = MakeFromDSL(std::move(program), options, SkSL::ProgramKind::kRuntimeColorFilter);
SkASSERT(!result.effect || result.effect->allowColorFilter());
return result;
}
-SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(std::unique_ptr<SkSL::Program> program) {
- auto result = Make(std::move(program), SkSL::ProgramKind::kRuntimeShader);
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(std::unique_ptr<SkSL::Program> program,
+ const Options& options) {
+ auto result = MakeFromDSL(std::move(program), options, SkSL::ProgramKind::kRuntimeShader);
SkASSERT(!result.effect || result.effect->allowShader());
return result;
}
-SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(std::unique_ptr<SkSL::Program> program) {
- auto result = Make(std::move(program), SkSL::ProgramKind::kRuntimeBlender);
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(std::unique_ptr<SkSL::Program> program,
+ const Options& options) {
+ auto result = MakeFromDSL(std::move(program), options, SkSL::ProgramKind::kRuntimeBlender);
SkASSERT(!result.effect || result.effect->allowBlender());
return result;
}
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(
+ std::unique_ptr<SkSL::Program> program) {
+ return MakeForColorFilter(std::move(program), Options{});
+}
+
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(std::unique_ptr<SkSL::Program> program) {
+ return MakeForShader(std::move(program), Options{});
+}
+
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(std::unique_ptr<SkSL::Program> program) {
+ return MakeForBlender(std::move(program), Options{});
+}
+
sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (*make)(SkString sksl),
SkString sksl) {
SK_BEGIN_REQUIRE_DENSE
@@ -443,12 +467,14 @@
// be accounted for in `fHash`. If you've added a new field to Options and caused the static-
// assert below to trigger, please incorporate your field into `fHash` and update KnownOptions
// to match the layout of Options.
- struct KnownOptions { bool a, b; };
+ struct KnownOptions { bool forceNoInline, enforceES2Restrictions, allowFragCoord; };
static_assert(sizeof(Options) == sizeof(KnownOptions));
fHash = SkOpts::hash_fn(&options.forceNoInline,
sizeof(options.forceNoInline), fHash);
fHash = SkOpts::hash_fn(&options.enforceES2Restrictions,
sizeof(options.enforceES2Restrictions), fHash);
+ fHash = SkOpts::hash_fn(&options.allowFragCoord,
+ sizeof(options.allowFragCoord), fHash);
fFilterColorProgram = SkFilterColorProgram::Make(this);
}
diff --git a/src/core/SkRuntimeEffectPriv.h b/src/core/SkRuntimeEffectPriv.h
index 3b3cd96..9865c9f 100644
--- a/src/core/SkRuntimeEffectPriv.h
+++ b/src/core/SkRuntimeEffectPriv.h
@@ -16,6 +16,25 @@
#ifdef SK_ENABLE_SKSL
+class SkRuntimeEffectPriv {
+public:
+ // Helper function when creating an effect for a GrSkSLFP that verifies an effect will
+ // implement the constant output for constant input optimization flag.
+ static bool SupportsConstantOutputForConstantInput(sk_sp<SkRuntimeEffect> effect) {
+ return effect->getFilterColorProgram();
+ }
+
+ static SkRuntimeEffect::Options ES3Options() {
+ SkRuntimeEffect::Options options;
+ options.enforceES2Restrictions = false;
+ return options;
+ }
+
+ static void EnableFragCoord(SkRuntimeEffect::Options* options) {
+ options->allowFragCoord = true;
+ }
+};
+
// These internal APIs for creating runtime effects vary from the public API in two ways:
//
// 1) they're used in contexts where it's not useful to receive an error message;
@@ -38,6 +57,7 @@
SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&),
const char* sksl,
SkRuntimeEffect::Options options = SkRuntimeEffect::Options{}) {
+ SkRuntimeEffectPriv::EnableFragCoord(&options);
auto result = make(SkString{sksl}, options);
SkASSERTF(result.effect, "%s", result.errorText.c_str());
return result.effect;
@@ -123,21 +143,6 @@
bool fAlphaUnchanged;
};
-class SkRuntimeEffectPriv {
-public:
- // Helper function when creating an effect for a GrSkSLFP that verifies an effect will
- // implement the constant output for constant input optimization flag.
- static bool SupportsConstantOutputForConstantInput(sk_sp<SkRuntimeEffect> effect) {
- return effect->getFilterColorProgram();
- }
-
- static SkRuntimeEffect::Options ES3Options() {
- SkRuntimeEffect::Options options;
- options.enforceES2Restrictions = false;
- return options;
- }
-};
-
#endif // SK_ENABLE_SKSL
#endif // SkRuntimeEffectPriv_DEFINED
diff --git a/src/sksl/dsl/DSLRuntimeEffects.cpp b/src/sksl/dsl/DSLRuntimeEffects.cpp
index 0104e48..77df6a6 100644
--- a/src/sksl/dsl/DSLRuntimeEffects.cpp
+++ b/src/sksl/dsl/DSLRuntimeEffects.cpp
@@ -28,11 +28,11 @@
settings.fAllowNarrowingConversions = true;
}
-sk_sp<SkRuntimeEffect> EndRuntimeShader() {
+sk_sp<SkRuntimeEffect> EndRuntimeShader(SkRuntimeEffect::Options options) {
std::unique_ptr<SkSL::Program> program = ReleaseProgram();
SkRuntimeEffect::Result result;
if (program) {
- result = SkRuntimeEffect::MakeForShader(std::move(program));
+ result = SkRuntimeEffect::MakeForShader(std::move(program), options);
// TODO(skbug.com/11862): propagate errors properly
SkASSERTF(result.effect, "%s\n", result.errorText.c_str());
}
diff --git a/tests/SkDSLRuntimeEffectTest.cpp b/tests/SkDSLRuntimeEffectTest.cpp
index 9e61c56..3f283af 100644
--- a/tests/SkDSLRuntimeEffectTest.cpp
+++ b/tests/SkDSLRuntimeEffectTest.cpp
@@ -14,6 +14,7 @@
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/GrDirectContext.h"
#include "include/sksl/DSLRuntimeEffects.h"
+#include "src/core/SkRuntimeEffectPriv.h"
#include "src/core/SkTLazy.h"
#include "src/gpu/GrColor.h"
#include "src/sksl/SkSLCompiler.h"
@@ -37,7 +38,9 @@
}
void end(bool expectSuccess = true) {
- sk_sp<SkRuntimeEffect> effect = EndRuntimeShader();
+ SkRuntimeEffect::Options options;
+ SkRuntimeEffectPriv::EnableFragCoord(&options);
+ sk_sp<SkRuntimeEffect> effect = EndRuntimeShader(options);
REPORTER_ASSERT(fReporter, effect ? expectSuccess : !expectSuccess);
if (effect) {
fBuilder.init(std::move(effect));
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index 6b0d428..79b05e7 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -221,12 +221,14 @@
DEF_TEST(SkRuntimeEffectForShader, r) {
// Tests that the shader factory rejects or accepts certain SkSL constructs
- auto test_valid = [r](const char* sksl) {
- auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl));
+ auto test_valid = [r](const char* sksl, SkRuntimeEffect::Options options = {}) {
+ auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl), options);
REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
};
- auto test_invalid = [r](const char* sksl, const char* expected) {
+ auto test_invalid = [r](const char* sksl,
+ const char* expected,
+ SkRuntimeEffect::Options options = {}) {
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl));
REPORTER_ASSERT(r, !effect);
REPORTER_ASSERT(r,
@@ -258,8 +260,13 @@
test_invalid("half4 main() { return half4(1); }", "'main' parameter");
test_invalid("half4 main(half4 c) { return c; }", "'main' parameter");
- // sk_FragCoord should be available
- test_valid("half4 main(float2 p) { return sk_FragCoord.xy01; }");
+ // sk_FragCoord should be available, but only if we've enabled it via Options
+ test_invalid("half4 main(float2 p) { return sk_FragCoord.xy01; }",
+ "unknown identifier 'sk_FragCoord'");
+
+ SkRuntimeEffect::Options optionsWithFragCoord;
+ SkRuntimeEffectPriv::EnableFragCoord(&optionsWithFragCoord);
+ test_valid("half4 main(float2 p) { return sk_FragCoord.xy01; }", optionsWithFragCoord);
// Sampling a child shader requires that we pass explicit coords
test_valid("uniform shader child;"
@@ -344,7 +351,9 @@
: fReporter(r), fSurface(std::move(surface)) {}
void build(const char* src) {
- auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(src));
+ SkRuntimeEffect::Options options;
+ SkRuntimeEffectPriv::EnableFragCoord(&options);
+ auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(src), options);
if (!effect) {
REPORT_FAILURE(fReporter, "effect",
SkStringPrintf("Effect didn't compile: %s", errorText.c_str()));
@@ -684,7 +693,9 @@
std::thread threads[16];
for (auto& thread : threads) {
thread = std::thread([r]() {
- auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(kSource));
+ SkRuntimeEffect::Options options;
+ SkRuntimeEffectPriv::EnableFragCoord(&options);
+ auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(kSource), options);
REPORTER_ASSERT(r, effect);
});
}