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:
parent
be834bfa2c
commit
cbb60bd0b0
2
BUILD.gn
2
BUILD.gn
@ -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",
|
||||
]
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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. */
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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; }
|
7
resources/sksl/runtime_errors/InvalidShaderMain.rts
Normal file
7
resources/sksl/runtime_errors/InvalidShaderMain.rts
Normal 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; }
|
@ -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) {
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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'");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
21
src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
Normal file
21
src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
Normal 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);
|
57
src/sksl/generated/sksl_rt_shader.dehydrated.sksl
Normal file
57
src/sksl/generated/sksl_rt_shader.dehydrated.sksl
Normal 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);
|
5
src/sksl/sksl_rt_colorfilter.sksl
Normal file
5
src/sksl/sksl_rt_colorfilter.sksl
Normal 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);
|
11
src/sksl/sksl_rt_shader.sksl
Normal file
11
src/sksl/sksl_rt_shader.sksl
Normal 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);
|
@ -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
|
||||
|
@ -0,0 +1,5 @@
|
||||
### Compilation failed:
|
||||
|
||||
error: 5: 'marker' is only permitted in runtime shaders
|
||||
error: 8: unknown identifier 'sk_FragCoord'
|
||||
2 errors
|
6
tests/sksl/runtime_errors/InvalidColorFilterMain.skvm
Normal file
6
tests/sksl/runtime_errors/InvalidColorFilterMain.skvm
Normal 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
|
5
tests/sksl/runtime_errors/InvalidShaderMain.skvm
Normal file
5
tests/sksl/runtime_errors/InvalidShaderMain.skvm
Normal 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
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user