Add runtime color filter and shader modes to the SkSL compiler

These enforce stricter rules about the signature of main, and each one
uses a separate pre-include module. That prevents color filters from
being able to reference sk_FragCoord (or coords passed to main) at all.
It also limits the versions of sample() that are exposed.

In the new world, an effect created for a specific stage of the Skia
pipeline can only be used to create instances of that stage (SkShader or
SkColorFilter). For now, SkRuntimeEffect::Make uses kRuntimeEffect,
which continues to be more lenient and allow creation of either shaders
or color filters from a single effect. After we migrate all clients, we
can deprecate and then delete that mode.

Bug: skia:11813
Change-Id: I0afd79a72beeec84da42c86146e8fcd8d0e4c09f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/395716
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2021-04-12 09:49:20 -04:00 committed by Skia Commit-Bot
parent be834bfa2c
commit cbb60bd0b0
24 changed files with 360 additions and 79 deletions

View File

@ -673,6 +673,8 @@ if (skia_compile_processors || skia_compile_sksl_tests) {
"src/sksl/sksl_geom.sksl",
"src/sksl/sksl_gpu.sksl",
"src/sksl/sksl_public.sksl",
"src/sksl/sksl_rt_colorfilter.sksl",
"src/sksl/sksl_rt_shader.sksl",
"src/sksl/sksl_runtime.sksl",
"src/sksl/sksl_vert.sksl",
]

View File

@ -580,12 +580,13 @@ void RunSkSLMemoryBenchmarks(NanoJSONResultsWriter* log) {
bench("sksl_compiler_gpu", after - before);
}
// Heap used by a compiler with the runtime effect module loaded
// Heap used by a compiler with the runtime shader & color filter modules loaded
{
int before = heap_bytes_used();
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeEffect);
compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeColorFilter);
compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeShader);
int after = heap_bytes_used();
bench("sksl_compiler_runtimeeffect", after - before);
}

View File

@ -579,6 +579,9 @@ sksl_rte_error_tests = [
"/sksl/runtime_errors/IllegalOperators.rte",
"/sksl/runtime_errors/IllegalShaderUse.rte",
"/sksl/runtime_errors/IllegalStatements.rte",
"/sksl/runtime_errors/InvalidColorFilterFeatures.rtcf",
"/sksl/runtime_errors/InvalidColorFilterMain.rtcf",
"/sksl/runtime_errors/InvalidShaderMain.rts",
"/sksl/runtime_errors/LoopConditionErrors.rte",
"/sksl/runtime_errors/LoopExpressionErrors.rte",
"/sksl/runtime_errors/LoopInitializerErrors.rte",

View File

@ -27,6 +27,7 @@ class SkShader;
namespace SkSL {
class FunctionDefinition;
struct Program;
enum class ProgramKind : int8_t;
} // namespace SkSL
namespace skvm {
@ -92,11 +93,38 @@ public:
SkString errorText;
};
static Result Make(SkString sksl, const Options& options);
// MakeForColorFilter and MakeForShader verify that the SkSL code is valid for those stages of
// the Skia pipeline. In all of the signatures described below, color parameters and return
// values are flexible. They are listed as being 'vec4', but they can also be 'half4' or
// 'float4'. ('vec4' is an alias for 'float4').
// Color filter SkSL requires an entry point that looks like:
// vec4 main(vec4 inColor) { ... }
static Result MakeForColorFilter(SkString sksl, const Options&);
// Shader SkSL requires an entry point that looks like:
// vec4 main(vec2 inCoords) { ... }
// -or-
// vec4 main(vec2 inCoords, vec4 inColor) { ... }
//
// Most shaders don't use the input color, so that parameter is optional.
static Result MakeForShader(SkString sksl, const Options&);
// [DEPRECATED] Make supports SkSL that is legal as either an SkShader or SkColorFilter.
// makeColorFilter might return nullptr, if the effect is dependent on position in any way.
static Result Make(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 Make(SkString sksl) { return Make(std::move(sksl), Options{}); }
static Result MakeForColorFilter(SkString sksl) {
return MakeForColorFilter(std::move(sksl), Options{});
}
static Result MakeForShader(SkString sksl) {
return MakeForShader(std::move(sksl), Options{});
}
static Result Make(SkString sksl) {
return Make(std::move(sksl), Options{});
}
sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms,
sk_sp<SkShader> children[],
@ -159,6 +187,12 @@ public:
#endif
private:
enum Flags {
kUsesSampleCoords_Flag = 0x1,
kAllowColorFilter_Flag = 0x2,
kAllowShader_Flag = 0x4,
};
SkRuntimeEffect(SkString sksl,
std::unique_ptr<SkSL::Program> baseProgram,
const Options& options,
@ -167,11 +201,14 @@ private:
std::vector<SkString>&& children,
std::vector<SkSL::SampleUsage>&& sampleUsages,
std::vector<Varying>&& varyings,
bool usesSampleCoords,
bool allowColorFilter);
uint32_t flags);
static Result Make(SkString sksl, const Options& options, SkSL::ProgramKind kind);
uint32_t hash() const { return fHash; }
bool usesSampleCoords() const { return fUsesSampleCoords; }
bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); }
bool allowShader() const { return (fFlags & kAllowShader_Flag); }
bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); }
struct FilterColorInfo {
const skvm::Program& program;
@ -201,8 +238,7 @@ private:
std::unique_ptr<skvm::Program> fColorFilterProgram;
bool fColorFilterProgramLeavesAlphaUnchanged;
bool fUsesSampleCoords;
bool fAllowColorFilter;
uint32_t fFlags; // Flags
};
/** Base class for SkRuntimeShaderBuilder, defined below. */

View File

@ -20,7 +20,9 @@ enum class ProgramKind : int8_t {
kVertex,
kGeometry,
kFragmentProcessor,
kRuntimeEffect,
kRuntimeEffect, // Legacy: Generic runtime effect that can be kColorFilter or kShader
kRuntimeColorFilter, // Runtime effect only suitable as SkColorFilter
kRuntimeShader, // " " " " " SkShader
kGeneric,
};

View File

@ -0,0 +1,9 @@
// Runtime color filters may not use layout(marker), or sk_FragCoord
// Expect 2 errors
layout(marker=ctm) uniform float4x4 ctm;
half4 main(half4 color) {
return sk_FragCoord.xy01;
}

View File

@ -0,0 +1,8 @@
// Runtime color filters require specific main signatures. Test that older signatures, or those
// intended for shaders don't work.
// Expect 3 errors
half4 main() { return half(1); }
half4 main(float2 coord) { return half4(1); }
half4 main(float2 coord, half4 color) { return color; }

View File

@ -0,0 +1,7 @@
// Runtime shaders require specific main signatures. Test that older signatures, or those intended
// for color filters don't work.
// Expect 2 errors
half4 main() { return half4(1); }
half4 main(half4 color) { return color; }

View File

@ -133,15 +133,16 @@ static bool init_uniform_type(const SkSL::Context& ctx,
return false;
}
SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& options) {
SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl,
const Options& options,
SkSL::ProgramKind kind) {
SkSL::SharedCompiler compiler;
SkSL::Program::Settings settings;
settings.fInlineThreshold = 0;
settings.fForceNoInline = options.forceNoInline;
settings.fAllowNarrowingConversions = true;
auto program = compiler->convertProgram(SkSL::ProgramKind::kRuntimeEffect,
SkSL::String(sksl.c_str(), sksl.size()),
settings);
auto program =
compiler->convertProgram(kind, SkSL::String(sksl.c_str(), sksl.size()), settings);
// 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__)}
@ -151,8 +152,17 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& opti
}
const SkSL::FunctionDefinition* main = nullptr;
const bool usesSampleCoords = SkSL::Analysis::ReferencesSampleCoords(*program);
const bool usesFragCoords = SkSL::Analysis::ReferencesFragCoords(*program);
uint32_t flags = 0;
switch (kind) {
case SkSL::ProgramKind::kRuntimeColorFilter: flags |= kAllowColorFilter_Flag; break;
case SkSL::ProgramKind::kRuntimeShader: flags |= kAllowShader_Flag; break;
case SkSL::ProgramKind::kRuntimeEffect: flags |= (kAllowColorFilter_Flag |
kAllowShader_Flag); break;
default: SkUNREACHABLE;
}
if (SkSL::Analysis::ReferencesSampleCoords(*program)) {
flags |= kUsesSampleCoords_Flag;
}
// Color filters are not allowed to depend on position (local or device) in any way, but they
// can sample children with matrices or explicit coords. Because the children are color filters,
@ -160,7 +170,13 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& opti
//
// Further down, we also ensure that color filters can't use varyings or layout(marker), which
// would allow them to change behavior based on the CTM.
bool allowColorFilter = !usesSampleCoords && !usesFragCoords;
// TODO(skbug.com/11813): When ProgramKind is always kRuntimeColorFilter or kRuntimeShader,
// this can be simpler. There is no way for color filters to refer to sk_FragCoord or sample
// coords in that mode.
if ((flags & kAllowColorFilter_Flag) &&
(SkSL::Analysis::ReferencesFragCoords(*program) || (flags & kUsesSampleCoords_Flag))) {
flags &= ~kAllowColorFilter_Flag;
}
size_t offset = 0;
std::vector<Uniform> uniforms;
@ -181,7 +197,7 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& opti
// Varyings (only used in conjunction with drawVertices)
if (var.modifiers().fFlags & SkSL::Modifiers::kVarying_Flag) {
allowColorFilter = false;
flags &= ~kAllowColorFilter_Flag;
varyings.push_back({var.name(),
varType.typeKind() == SkSL::Type::TypeKind::kVector
? varType.columns()
@ -213,7 +229,7 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& opti
const SkSL::StringFragment& marker(var.modifiers().fLayout.fMarker);
if (marker.fLength) {
uni.flags |= Uniform::kMarker_Flag;
allowColorFilter = false;
flags &= ~kAllowColorFilter_Flag;
if (!parse_marker(marker, &uni.marker, &uni.flags)) {
RETURN_FAILURE("Invalid 'marker' string: '%.*s'", (int)marker.fLength,
marker.fChars);
@ -255,11 +271,26 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& opti
std::move(children),
std::move(sampleUsages),
std::move(varyings),
usesSampleCoords,
allowColorFilter));
flags));
return Result{std::move(effect), SkString()};
}
SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& options) {
return Make(std::move(sksl), options, SkSL::ProgramKind::kRuntimeEffect);
}
SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(SkString sksl, const Options& options) {
auto result = Make(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);
SkASSERT(!result.effect || result.effect->allowShader());
return result;
}
sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkString sksl) {
SK_BEGIN_REQUIRE_DENSE
struct Key {
@ -332,8 +363,7 @@ SkRuntimeEffect::SkRuntimeEffect(SkString sksl,
std::vector<SkString>&& children,
std::vector<SkSL::SampleUsage>&& sampleUsages,
std::vector<Varying>&& varyings,
bool usesSampleCoords,
bool allowColorFilter)
uint32_t flags)
: fHash(SkGoodHash()(sksl))
, fSkSL(std::move(sksl))
, fBaseProgram(std::move(baseProgram))
@ -342,8 +372,7 @@ SkRuntimeEffect::SkRuntimeEffect(SkString sksl,
, fChildren(std::move(children))
, fSampleUsages(std::move(sampleUsages))
, fVaryings(std::move(varyings))
, fUsesSampleCoords(usesSampleCoords)
, fAllowColorFilter(allowColorFilter) {
, fFlags(flags) {
SkASSERT(fBaseProgram);
SkASSERT(fChildren.size() == fSampleUsages.size());
@ -376,7 +405,7 @@ int SkRuntimeEffect::findChild(const char* name) const {
}
SkRuntimeEffect::FilterColorInfo SkRuntimeEffect::getFilterColorInfo() {
SkASSERT(fAllowColorFilter);
SkASSERT(this->allowColorFilter());
fColorFilterProgramOnce([&] {
// Runtime effects are often long lived & cached. So: build and save a program that can
@ -853,6 +882,9 @@ sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<SkData> uniforms,
size_t childCount,
const SkMatrix* localMatrix,
bool isOpaque) const {
if (!this->allowShader()) {
return nullptr;
}
if (!uniforms) {
uniforms = SkData::MakeEmpty();
}
@ -948,7 +980,7 @@ sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* recordingContext,
sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms,
sk_sp<SkColorFilter> children[],
size_t childCount) const {
if (!fAllowColorFilter) {
if (!this->allowColorFilter()) {
return nullptr;
}
if (!uniforms) {

View File

@ -174,8 +174,8 @@ GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect,
const char* name,
sk_sp<SkData> uniforms)
: INHERITED(kGrSkSLFP_ClassID,
effect->fAllowColorFilter ? kConstantOutputForConstantInput_OptimizationFlag
: kNone_OptimizationFlags)
effect->allowColorFilter() ? kConstantOutputForConstantInput_OptimizationFlag
: kNone_OptimizationFlags)
, fEffect(std::move(effect))
, fName(name)
, fUniforms(std::move(uniforms)) {

View File

@ -63,6 +63,8 @@
#include "src/sksl/generated/sksl_geom.dehydrated.sksl"
#include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
#include "src/sksl/generated/sksl_public.dehydrated.sksl"
#include "src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl"
#include "src/sksl/generated/sksl_rt_shader.dehydrated.sksl"
#include "src/sksl/generated/sksl_runtime.dehydrated.sksl"
#include "src/sksl/generated/sksl_vert.dehydrated.sksl"
@ -242,39 +244,63 @@ const ParsedModule& Compiler::loadPublicModule() {
return fPublicModule;
}
static void add_glsl_type_aliases(SkSL::SymbolTable* symbols, const SkSL::BuiltinTypes& types) {
// Add some aliases to the runtime effect modules so that it's friendlier, and more like GLSL
symbols->addAlias("vec2", types.fFloat2.get());
symbols->addAlias("vec3", types.fFloat3.get());
symbols->addAlias("vec4", types.fFloat4.get());
symbols->addAlias("ivec2", types.fInt2.get());
symbols->addAlias("ivec3", types.fInt3.get());
symbols->addAlias("ivec4", types.fInt4.get());
symbols->addAlias("bvec2", types.fBool2.get());
symbols->addAlias("bvec3", types.fBool3.get());
symbols->addAlias("bvec4", types.fBool4.get());
symbols->addAlias("mat2", types.fFloat2x2.get());
symbols->addAlias("mat3", types.fFloat3x3.get());
symbols->addAlias("mat4", types.fFloat4x4.get());
}
const ParsedModule& Compiler::loadRuntimeEffectModule() {
if (!fRuntimeEffectModule.fSymbols) {
fRuntimeEffectModule = this->parseModule(ProgramKind::kRuntimeEffect, MODULE_DATA(runtime),
this->loadPublicModule());
// Add some aliases to the runtime effect module so that it's friendlier, and more like GLSL
fRuntimeEffectModule.fSymbols->addAlias("vec2", fContext->fTypes.fFloat2.get());
fRuntimeEffectModule.fSymbols->addAlias("vec3", fContext->fTypes.fFloat3.get());
fRuntimeEffectModule.fSymbols->addAlias("vec4", fContext->fTypes.fFloat4.get());
fRuntimeEffectModule.fSymbols->addAlias("ivec2", fContext->fTypes.fInt2.get());
fRuntimeEffectModule.fSymbols->addAlias("ivec3", fContext->fTypes.fInt3.get());
fRuntimeEffectModule.fSymbols->addAlias("ivec4", fContext->fTypes.fInt4.get());
fRuntimeEffectModule.fSymbols->addAlias("bvec2", fContext->fTypes.fBool2.get());
fRuntimeEffectModule.fSymbols->addAlias("bvec3", fContext->fTypes.fBool3.get());
fRuntimeEffectModule.fSymbols->addAlias("bvec4", fContext->fTypes.fBool4.get());
fRuntimeEffectModule.fSymbols->addAlias("mat2", fContext->fTypes.fFloat2x2.get());
fRuntimeEffectModule.fSymbols->addAlias("mat3", fContext->fTypes.fFloat3x3.get());
fRuntimeEffectModule.fSymbols->addAlias("mat4", fContext->fTypes.fFloat4x4.get());
fRuntimeEffectModule = this->parseModule(
ProgramKind::kRuntimeEffect, MODULE_DATA(runtime), this->loadPublicModule());
add_glsl_type_aliases(fRuntimeEffectModule.fSymbols.get(), fContext->fTypes);
}
return fRuntimeEffectModule;
}
const ParsedModule& Compiler::loadRuntimeColorFilterModule() {
if (!fRuntimeColorFilterModule.fSymbols) {
fRuntimeColorFilterModule = this->parseModule(ProgramKind::kRuntimeColorFilter,
MODULE_DATA(rt_colorfilter),
this->loadPublicModule());
add_glsl_type_aliases(fRuntimeColorFilterModule.fSymbols.get(), fContext->fTypes);
}
return fRuntimeColorFilterModule;
}
const ParsedModule& Compiler::loadRuntimeShaderModule() {
if (!fRuntimeShaderModule.fSymbols) {
fRuntimeShaderModule = this->parseModule(
ProgramKind::kRuntimeShader, MODULE_DATA(rt_shader), this->loadPublicModule());
add_glsl_type_aliases(fRuntimeShaderModule.fSymbols.get(), fContext->fTypes);
}
return fRuntimeShaderModule;
}
const ParsedModule& Compiler::moduleForProgramKind(ProgramKind kind) {
switch (kind) {
case ProgramKind::kVertex: return this->loadVertexModule(); break;
case ProgramKind::kFragment: return this->loadFragmentModule(); break;
case ProgramKind::kGeometry: return this->loadGeometryModule(); break;
case ProgramKind::kFragmentProcessor: return this->loadFPModule(); break;
case ProgramKind::kRuntimeEffect: return this->loadRuntimeEffectModule(); break;
case ProgramKind::kGeneric: return this->loadPublicModule(); break;
case ProgramKind::kVertex: return this->loadVertexModule(); break;
case ProgramKind::kFragment: return this->loadFragmentModule(); break;
case ProgramKind::kGeometry: return this->loadGeometryModule(); break;
case ProgramKind::kFragmentProcessor: return this->loadFPModule(); break;
case ProgramKind::kRuntimeEffect: return this->loadRuntimeEffectModule(); break;
case ProgramKind::kRuntimeColorFilter: return this->loadRuntimeColorFilterModule(); break;
case ProgramKind::kRuntimeShader: return this->loadRuntimeShaderModule(); break;
case ProgramKind::kGeneric: return this->loadPublicModule(); break;
}
SkUNREACHABLE;
}

View File

@ -190,6 +190,8 @@ private:
const ParsedModule& loadGeometryModule();
const ParsedModule& loadPublicModule();
const ParsedModule& loadRuntimeEffectModule();
const ParsedModule& loadRuntimeColorFilterModule();
const ParsedModule& loadRuntimeShaderModule();
/** Verifies that @if and @switch statements were actually optimized away. */
void verifyStaticTests(const Program& program);
@ -214,17 +216,19 @@ private:
std::shared_ptr<SymbolTable> fRootSymbolTable;
std::shared_ptr<SymbolTable> fPrivateSymbolTable;
ParsedModule fRootModule; // Core types
ParsedModule fRootModule; // Core types
ParsedModule fPrivateModule; // [Root] + Internal types
ParsedModule fGPUModule; // [Private] + GPU intrinsics, helper functions
ParsedModule fVertexModule; // [GPU] + Vertex stage decls
ParsedModule fFragmentModule; // [GPU] + Fragment stage decls
ParsedModule fGeometryModule; // [GPU] + Geometry stage decls
ParsedModule fFPModule; // [GPU] + FP features
ParsedModule fPrivateModule; // [Root] + Internal types
ParsedModule fGPUModule; // [Private] + GPU intrinsics, helper functions
ParsedModule fVertexModule; // [GPU] + Vertex stage decls
ParsedModule fFragmentModule; // [GPU] + Fragment stage decls
ParsedModule fGeometryModule; // [GPU] + Geometry stage decls
ParsedModule fFPModule; // [GPU] + FP features
ParsedModule fPublicModule; // [Root] + Public features
ParsedModule fRuntimeEffectModule; // [Public] + Runtime effect decls
ParsedModule fPublicModule; // [Root] + Public features
ParsedModule fRuntimeEffectModule; // [Public] + Runtime effect decls
ParsedModule fRuntimeColorFilterModule; // [Public] + Runtime shader decls
ParsedModule fRuntimeShaderModule; // [Public] + Runtime color filter decls
// holds ModifiersPools belonging to the core includes for lifetime purposes
std::vector<std::unique_ptr<ModifiersPool>> fModifiers;

View File

@ -293,7 +293,9 @@ void IRGenerator::checkVarDeclaration(int offset, const Modifiers& modifiers, co
"'key' is only permitted within fragment processors");
}
}
if (this->programKind() == ProgramKind::kRuntimeEffect) {
if (this->programKind() == ProgramKind::kRuntimeEffect ||
this->programKind() == ProgramKind::kRuntimeColorFilter ||
this->programKind() == ProgramKind::kRuntimeShader) {
if (modifiers.fFlags & Modifiers::kIn_Flag) {
this->errorReporter().error(offset, "'in' variables not permitted in runtime effects");
}
@ -307,9 +309,9 @@ void IRGenerator::checkVarDeclaration(int offset, const Modifiers& modifiers, co
this->errorReporter().error(offset, "'key' is not permitted on 'uniform' variables");
}
if (modifiers.fLayout.fMarker.fLength) {
if (this->programKind() != ProgramKind::kRuntimeEffect) {
this->errorReporter().error(offset,
"'marker' is only permitted in runtime effects");
if (this->programKind() != ProgramKind::kRuntimeEffect &&
this->programKind() != ProgramKind::kRuntimeShader) {
this->errorReporter().error(offset, "'marker' is only permitted in runtime shaders");
}
if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
this->errorReporter().error(offset,
@ -321,7 +323,9 @@ void IRGenerator::checkVarDeclaration(int offset, const Modifiers& modifiers, co
}
}
if (modifiers.fLayout.fFlags & Layout::kSRGBUnpremul_Flag) {
if (this->programKind() != ProgramKind::kRuntimeEffect) {
if (this->programKind() != ProgramKind::kRuntimeEffect &&
this->programKind() != ProgramKind::kRuntimeColorFilter &&
this->programKind() != ProgramKind::kRuntimeShader) {
this->errorReporter().error(offset,
"'srgb_unpremul' is only permitted in runtime effects");
}
@ -341,8 +345,9 @@ void IRGenerator::checkVarDeclaration(int offset, const Modifiers& modifiers, co
}
}
if (modifiers.fFlags & Modifiers::kVarying_Flag) {
if (this->programKind() != ProgramKind::kRuntimeEffect) {
this->errorReporter().error(offset, "'varying' is only permitted in runtime effects");
if (this->programKind() != ProgramKind::kRuntimeEffect &&
this->programKind() != ProgramKind::kRuntimeShader) {
this->errorReporter().error(offset, "'varying' is only permitted in runtime shaders");
}
if (!baseType->isFloat() &&
!(baseType->isVector() && baseType->componentType().isFloat())) {
@ -1061,6 +1066,8 @@ void IRGenerator::convertFunction(const ASTNode& f) {
Modifiers m = pd.fModifiers;
if (isMain && (this->programKind() == ProgramKind::kRuntimeEffect ||
this->programKind() == ProgramKind::kRuntimeColorFilter ||
this->programKind() == ProgramKind::kRuntimeShader ||
this->programKind() == ProgramKind::kFragmentProcessor)) {
// We verify that the signature is fully correct later. For now, if this is an .fp or
// runtime effect of any flavor, a float2 param is supposed to be the coords, and
@ -1095,11 +1102,6 @@ void IRGenerator::convertFunction(const ASTNode& f) {
switch (this->programKind()) {
case ProgramKind::kRuntimeEffect: {
// Legacy/generic runtime effects take a wide variety of main() signatures.
// TODO(skbug.com/11813): When we have dedicated program kinds for runtime shader
// vs color filter, those will only accept the suitable versions. Also, be even
// more restrictive: shaders must take coords, and color filters must take input
// color, even if unused.
// (half4|float4) main(float2?, (half4|float4)?)
if (!typeIsValidForColor(*returnType)) {
this->errorReporter().error(f.fOffset,
@ -1119,6 +1121,37 @@ void IRGenerator::convertFunction(const ASTNode& f) {
}
break;
}
case ProgramKind::kRuntimeColorFilter: {
// (half4|float4) main(half4|float4)
if (!typeIsValidForColor(*returnType)) {
this->errorReporter().error(f.fOffset,
"'main' must return: 'vec4', 'float4', or 'half4'");
return;
}
bool validParams = (parameters.size() == 1 && paramIsInputColor(0));
if (!validParams) {
this->errorReporter().error(
f.fOffset, "'main' parameter must be 'vec4', 'float4', or 'half4'");
return;
}
break;
}
case ProgramKind::kRuntimeShader: {
// (half4|float4) main(float2) -or- (half4|float4) main(float2, half4|float4)
if (!typeIsValidForColor(*returnType)) {
this->errorReporter().error(f.fOffset,
"'main' must return: 'vec4', 'float4', or 'half4'");
return;
}
bool validParams =
(parameters.size() == 1 && paramIsCoords(0)) ||
(parameters.size() == 2 && paramIsCoords(0) && paramIsInputColor(1));
if (!validParams) {
this->errorReporter().error(
f.fOffset, "'main' parameters must be (float2, (vec4|float4|half4)?)");
}
break;
}
case ProgramKind::kFragmentProcessor: {
if (*returnType != *fContext.fTypes.fHalf4) {
this->errorReporter().error(f.fOffset, ".fp 'main' must return 'half4'");

View File

@ -284,8 +284,13 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
kind = SkSL::ProgramKind::kFragmentProcessor;
} else if (inputPath.endsWith(".rte")) {
kind = SkSL::ProgramKind::kRuntimeEffect;
} else if (inputPath.endsWith(".rtcf")) {
kind = SkSL::ProgramKind::kRuntimeColorFilter;
} else if (inputPath.endsWith(".rts")) {
kind = SkSL::ProgramKind::kRuntimeShader;
} else {
printf("input filename must end in '.vert', '.frag', '.geom', '.fp', '.rte', or '.sksl'\n");
printf("input filename must end in '.vert', '.frag', '.geom', '.fp', '.rte', '.rtcf', "
"'.rts', or '.sksl'\n");
return ResultCode::kInputError;
}

View File

@ -74,7 +74,10 @@ struct ProgramConfig {
ProgramSettings fSettings;
bool strictES2Mode() const {
return fKind == ProgramKind::kRuntimeEffect || fKind == ProgramKind::kGeneric;
return fKind == ProgramKind::kRuntimeEffect ||
fKind == ProgramKind::kRuntimeColorFilter ||
fKind == ProgramKind::kRuntimeShader ||
fKind == ProgramKind::kGeneric;
}
};

View File

@ -0,0 +1,21 @@
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,
49,3,0,
53,1,0,
16,2,0,
50,2,0,4,0,3,
53,3,0,
16,11,0,
50,4,0,18,0,3,
30,5,0,
16,25,0,2,1,0,3,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);

View File

@ -0,0 +1,57 @@
static uint8_t SKSL_INCLUDE_sksl_rt_shader[] = {76,0,
0,
12,115,107,95,70,114,97,103,67,111,111,114,100,
6,102,108,111,97,116,52,
1,115,
6,115,104,97,100,101,114,
6,115,97,109,112,108,101,
5,104,97,108,102,52,
9,116,114,97,110,115,102,111,114,109,
8,102,108,111,97,116,51,120,51,
6,99,111,111,114,100,115,
6,102,108,111,97,116,50,
49,11,0,
53,1,0,
37,
36,0,32,0,0,255,255,255,255,255,15,0,255,255,255,255,2,0,2,0,0,0,3,0,
50,2,0,16,0,0,
53,3,0,
16,23,0,
50,4,0,25,0,3,
30,5,0,
16,32,0,1,3,0,
50,6,0,39,0,
53,7,0,
16,23,0,
47,4,0,3,
53,8,0,
16,45,0,
50,9,0,55,0,3,
52,10,0,2,
47,5,0,
30,11,0,
16,32,0,2,7,0,8,0,
47,6,0,
47,11,0,
53,12,0,
16,23,0,
47,4,0,3,
53,13,0,
16,64,0,
50,14,0,71,0,3,
52,15,0,3,
47,5,0,
47,11,0,
30,16,0,
16,32,0,2,12,0,13,0,
47,6,0,
47,16,0,2,0,
9,0,
0,0,
19,
55,
54,1,0,
47,2,0,0,
57,
20,};
static constexpr size_t SKSL_INCLUDE_sksl_rt_shader_LENGTH = sizeof(SKSL_INCLUDE_sksl_rt_shader);

View File

@ -0,0 +1,5 @@
half4 sample(shader s, float2 coords);
// TODO(skbug.com/11813): Implement sample() that takes a color
// half4 sample(colorFilter f, half4 color);
// half4 sample(shader s, half4 color, float2 coords);

View File

@ -0,0 +1,11 @@
layout(builtin=15) float4 sk_FragCoord;
half4 sample(shader s);
half4 sample(shader s, float3x3 transform);
half4 sample(shader s, float2 coords);
// TODO(skbug.com/11813): Implement sample() that takes a color
// half4 sample(colorFilter f, half4 color);
// half4 sample(shader s, half4 color);
// half4 sample(shader s, half4 color, float3x3 transform);
// half4 sample(shader s, half4 color, float2 coords);

View File

@ -16,7 +16,7 @@ error: 4: 'varying' is not permitted here
error: 4: 'inline' is not permitted here
error: 4: 'noinline' is not permitted here
error: 6: 'in uniform' variables only permitted within fragment processors
error: 6: 'varying' is only permitted in runtime effects
error: 6: 'varying' is only permitted in runtime shaders
error: 6: 'sk_has_side_effects' is not permitted here
error: 6: 'inline' is not permitted here
error: 6: 'noinline' is not permitted here

View File

@ -0,0 +1,5 @@
### Compilation failed:
error: 5: 'marker' is only permitted in runtime shaders
error: 8: unknown identifier 'sk_FragCoord'
2 errors

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 6: 'main' parameter must be 'vec4', 'float4', or 'half4'
error: 7: 'main' parameter must be 'vec4', 'float4', or 'half4'
error: 8: 'main' parameter must be 'vec4', 'float4', or 'half4'
3 errors

View File

@ -0,0 +1,5 @@
### Compilation failed:
error: 6: 'main' parameters must be (float2, (vec4|float4|half4)?)
error: 7: 'main' parameters must be (float2, (vec4|float4|half4)?)
2 errors

View File

@ -96,7 +96,7 @@ bool SkSLSlide::rebuild() {
fwrite(fSkSL.c_str(), 1, fSkSL.size(), backup);
fclose(backup);
}
auto [effect, errorText] = SkRuntimeEffect::Make(sksl);
auto [effect, errorText] = SkRuntimeEffect::MakeForShader(sksl);
if (backup) {
std::remove(kBackupFile);
}