Added an API for creating RuntimeEffects using the SkSL DSL.
Change-Id: I305016d305455e2b90fe904d8da93cf7735cc38e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/389316
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 5a3e5d8..ad9a00d 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -26,6 +26,7 @@
"$_include/sksl/DSLExpression.h",
"$_include/sksl/DSLFunction.h",
"$_include/sksl/DSLModifiers.h",
+ "$_include/sksl/DSLRuntimeEffects.h",
"$_include/sksl/DSLStatement.h",
"$_include/sksl/DSLType.h",
"$_include/sksl/DSLVar.h",
@@ -80,6 +81,7 @@
"$_src/sksl/dsl/DSLCore.cpp",
"$_src/sksl/dsl/DSLExpression.cpp",
"$_src/sksl/dsl/DSLFunction.cpp",
+ "$_src/sksl/dsl/DSLRuntimeEffects.cpp",
"$_src/sksl/dsl/DSLStatement.cpp",
"$_src/sksl/dsl/DSLType.cpp",
"$_src/sksl/dsl/DSLVar.cpp",
diff --git a/gn/tests.gni b/gn/tests.gni
index 5b56c58..e5bb1e6 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -245,6 +245,7 @@
"$_tests/SkColor4fTest.cpp",
"$_tests/SkColorSpaceXformStepsTest.cpp",
"$_tests/SkDOMTest.cpp",
+ "$_tests/SkDSLRuntimeEffectTest.cpp",
"$_tests/SkFixed15Test.cpp",
"$_tests/SkGaussFilterTest.cpp",
"$_tests/SkGlyphBufferTest.cpp",
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index f658bad..90b6303 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -126,6 +126,10 @@
return Make(std::move(sksl), Options{});
}
+ static Result MakeForColorFilter(std::unique_ptr<SkSL::Program> program);
+
+ static Result MakeForShader(std::unique_ptr<SkSL::Program> program);
+
sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms,
sk_sp<SkShader> children[],
size_t childCount,
@@ -203,8 +207,13 @@
std::vector<Varying>&& varyings,
uint32_t flags);
+ static Result Make(std::unique_ptr<SkSL::Program> program, SkSL::ProgramKind kind);
+
static Result Make(SkString sksl, const Options& options, SkSL::ProgramKind kind);
+ static Result Make(SkString sksl, 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); }
bool allowShader() const { return (fFlags & kAllowShader_Flag); }
diff --git a/include/sksl/DSLModifiers.h b/include/sksl/DSLModifiers.h
index 3ba5bd4..83515b7 100644
--- a/include/sksl/DSLModifiers.h
+++ b/include/sksl/DSLModifiers.h
@@ -41,6 +41,7 @@
SkSL::Modifiers fModifiers;
friend DSLType Struct(const char* name, SkTArray<DSLField> fields);
+ friend class DSLFunction;
friend class DSLVar;
friend class DSLWriter;
};
diff --git a/include/sksl/DSLRuntimeEffects.h b/include/sksl/DSLRuntimeEffects.h
new file mode 100644
index 0000000..194850f
--- /dev/null
+++ b/include/sksl/DSLRuntimeEffects.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_DSL_RUNTIME_EFFECTS
+#define SKSL_DSL_RUNTIME_EFFECTS
+
+#include "include/sksl/DSL.h"
+
+class SkRuntimeEffect;
+
+namespace SkSL {
+
+class Compiler;
+
+namespace dsl {
+
+#ifndef SKSL_STANDALONE
+
+void StartRuntimeShader(SkSL::Compiler* compiler);
+
+sk_sp<SkRuntimeEffect> EndRuntimeShader();
+
+#endif
+
+} // namespace dsl
+
+} // namespace SkSL
+
+#endif
diff --git a/include/sksl/DSLType.h b/include/sksl/DSLType.h
index 52ebe12..fba8ab9 100644
--- a/include/sksl/DSLType.h
+++ b/include/sksl/DSLType.h
@@ -58,6 +58,7 @@
kInt2_Type,
kInt3_Type,
kInt4_Type,
+ kShader_Type,
kShort_Type,
kShort2_Type,
kShort3_Type,
diff --git a/include/sksl/DSLVar.h b/include/sksl/DSLVar.h
index 4baf42a..3821b68 100644
--- a/include/sksl/DSLVar.h
+++ b/include/sksl/DSLVar.h
@@ -40,6 +40,10 @@
~DSLVar();
+ const char* name() const {
+ return fName;
+ }
+
DSLExpression x() {
return DSLExpression(*this).x();
}
@@ -115,10 +119,6 @@
*/
DSLVar(const char* name);
- const char* name() const {
- return fName;
- }
-
DSLModifiers fModifiers;
// We only need to keep track of the type here so that we can create the SkSL::Variable. For
// predefined variables this field is unnecessary, so we don't bother tracking it and just set
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 50dc7ae..755b3d9 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -109,31 +109,67 @@
const SkSL::Type* type,
SkRuntimeEffect::Uniform* v) {
using Type = SkRuntimeEffect::Uniform::Type;
+ if (*type == *ctx.fTypes.fFloat) { v->type = Type::kFloat; return true; }
+ if (*type == *ctx.fTypes.fHalf) { v->type = Type::kFloat; return true; }
+ if (*type == *ctx.fTypes.fFloat2) { v->type = Type::kFloat2; return true; }
+ if (*type == *ctx.fTypes.fHalf2) { v->type = Type::kFloat2; return true; }
+ if (*type == *ctx.fTypes.fFloat3) { v->type = Type::kFloat3; return true; }
+ if (*type == *ctx.fTypes.fHalf3) { v->type = Type::kFloat3; return true; }
+ if (*type == *ctx.fTypes.fFloat4) { v->type = Type::kFloat4; return true; }
+ if (*type == *ctx.fTypes.fHalf4) { v->type = Type::kFloat4; return true; }
+ if (*type == *ctx.fTypes.fFloat2x2) { v->type = Type::kFloat2x2; return true; }
+ if (*type == *ctx.fTypes.fHalf2x2) { v->type = Type::kFloat2x2; return true; }
+ if (*type == *ctx.fTypes.fFloat3x3) { v->type = Type::kFloat3x3; return true; }
+ if (*type == *ctx.fTypes.fHalf3x3) { v->type = Type::kFloat3x3; return true; }
+ if (*type == *ctx.fTypes.fFloat4x4) { v->type = Type::kFloat4x4; return true; }
+ if (*type == *ctx.fTypes.fHalf4x4) { v->type = Type::kFloat4x4; return true; }
- if (type == ctx.fTypes.fFloat.get()) { v->type = Type::kFloat; return true; }
- if (type == ctx.fTypes.fHalf.get()) { v->type = Type::kFloat; return true; }
- if (type == ctx.fTypes.fFloat2.get()) { v->type = Type::kFloat2; return true; }
- if (type == ctx.fTypes.fHalf2.get()) { v->type = Type::kFloat2; return true; }
- if (type == ctx.fTypes.fFloat3.get()) { v->type = Type::kFloat3; return true; }
- if (type == ctx.fTypes.fHalf3.get()) { v->type = Type::kFloat3; return true; }
- if (type == ctx.fTypes.fFloat4.get()) { v->type = Type::kFloat4; return true; }
- if (type == ctx.fTypes.fHalf4.get()) { v->type = Type::kFloat4; return true; }
- if (type == ctx.fTypes.fFloat2x2.get()) { v->type = Type::kFloat2x2; return true; }
- if (type == ctx.fTypes.fHalf2x2.get()) { v->type = Type::kFloat2x2; return true; }
- if (type == ctx.fTypes.fFloat3x3.get()) { v->type = Type::kFloat3x3; return true; }
- if (type == ctx.fTypes.fHalf3x3.get()) { v->type = Type::kFloat3x3; return true; }
- if (type == ctx.fTypes.fFloat4x4.get()) { v->type = Type::kFloat4x4; return true; }
- if (type == ctx.fTypes.fHalf4x4.get()) { v->type = Type::kFloat4x4; return true; }
-
- if (type == ctx.fTypes.fInt.get()) { v->type = Type::kInt; return true; }
- if (type == ctx.fTypes.fInt2.get()) { v->type = Type::kInt2; return true; }
- if (type == ctx.fTypes.fInt3.get()) { v->type = Type::kInt3; return true; }
- if (type == ctx.fTypes.fInt4.get()) { v->type = Type::kInt4; return true; }
+ if (*type == *ctx.fTypes.fInt) { v->type = Type::kInt; return true; }
+ if (*type == *ctx.fTypes.fInt2) { v->type = Type::kInt2; return true; }
+ if (*type == *ctx.fTypes.fInt3) { v->type = Type::kInt3; return true; }
+ if (*type == *ctx.fTypes.fInt4) { v->type = Type::kInt4; return true; }
return false;
}
+// 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__)}
+
+SkRuntimeEffect::Result SkRuntimeEffect::Make(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
+ // calling the Make overload at the end, which creates its own (non-reentrant)
+ // SharedCompiler instance
+ SkSL::SharedCompiler compiler;
+ SkSL::Program::Settings settings;
+ settings.fInlineThreshold = 0;
+ settings.fForceNoInline = options.forceNoInline;
+ settings.fAllowNarrowingConversions = true;
+ program = compiler->convertProgram(SkSL::ProgramKind::kRuntimeEffect,
+ 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__)}
+
+ if (!program) {
+ RETURN_FAILURE("%s", compiler->errorText().c_str());
+ }
+ }
+ return Make(std::move(sksl), std::move(program), options, kind);
+}
+
+SkRuntimeEffect::Result SkRuntimeEffect::Make(std::unique_ptr<SkSL::Program> program,
+ SkSL::ProgramKind kind) {
+ SkString source(program->description().c_str());
+ return Make(std::move(source), std::move(program), Options{}, kind);
+}
+
SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl,
+ std::unique_ptr<SkSL::Program> program,
const Options& options,
SkSL::ProgramKind kind) {
SkSL::SharedCompiler compiler;
@@ -141,15 +177,6 @@
settings.fInlineThreshold = 0;
settings.fForceNoInline = options.forceNoInline;
settings.fAllowNarrowingConversions = true;
- 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__)}
-
- if (!program) {
- RETURN_FAILURE("%s", compiler->errorText().c_str());
- }
const SkSL::FunctionDefinition* main = nullptr;
uint32_t flags = 0;
@@ -291,6 +318,18 @@
return result;
}
+SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(std::unique_ptr<SkSL::Program> program) {
+ auto result = Make(std::move(program), 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);
+ SkASSERT(!result.effect || result.effect->allowShader());
+ return result;
+}
+
sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkString sksl) {
SK_BEGIN_REQUIRE_DENSE
struct Key {
diff --git a/src/gpu/GrShaderUtils.h b/src/gpu/GrShaderUtils.h
index 83ef51e..f93dd12 100644
--- a/src/gpu/GrShaderUtils.h
+++ b/src/gpu/GrShaderUtils.h
@@ -10,8 +10,8 @@
#include "include/core/SkTypes.h"
#include "include/gpu/GrContextOptions.h"
+#include "include/private/SkSLProgramKind.h"
#include "include/private/SkSLString.h"
-#include "src/sksl/ir/SkSLProgram.h"
namespace GrShaderUtils {
diff --git a/src/sksl/SkSLContext.cpp b/src/sksl/SkSLContext.cpp
index d27a1ad..3ffc839 100644
--- a/src/sksl/SkSLContext.cpp
+++ b/src/sksl/SkSLContext.cpp
@@ -38,7 +38,8 @@
Context::Context(ErrorReporter& errors, const ShaderCapsClass& caps)
: fErrors(errors)
, fCaps(caps)
- , fDefined_Expression(std::make_unique<DefinedExpression>(fTypes.fInvalid.get())) {}
-
+ , fDefined_Expression(std::make_unique<DefinedExpression>(fTypes.fInvalid.get())) {
+ SkASSERT(!Pool::IsAttached());
+}
} // namespace SkSL
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 55e5348..a1f1f49 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -12,6 +12,7 @@
#include "src/sksl/SkSLBuiltinTypes.h"
#include "src/sksl/SkSLErrorReporter.h"
+#include "src/sksl/SkSLPool.h"
#include "src/sksl/SkSLUtil.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLType.h"
@@ -27,6 +28,10 @@
public:
Context(ErrorReporter& errors, const ShaderCapsClass& caps);
+ ~Context() {
+ SkASSERT(!Pool::IsAttached());
+ }
+
// The Context holds all of the built-in types.
BuiltinTypes fTypes;
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 645c594..d4f7845 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -1411,8 +1411,7 @@
std::make_unique<StructDefinition>(decl.fOffset, *type));
}
}
- fProgramElements->push_back(std::make_unique<GlobalVarDeclaration>(decl.fOffset,
- std::move(stmt)));
+ fProgramElements->push_back(std::make_unique<GlobalVarDeclaration>(std::move(stmt)));
}
}
@@ -2102,6 +2101,7 @@
};
BuiltinVariableScanner scanner(this);
+ SkASSERT(fProgramElements);
for (auto& e : *fProgramElements) {
scanner.visitProgramElement(*e);
}
@@ -2131,6 +2131,8 @@
const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions,
std::vector<std::unique_ptr<ProgramElement>>* elements,
std::vector<const ProgramElement*>* sharedElements) {
+ fProgramElements = elements;
+ fSharedElements = sharedElements;
fSymbolTable = base.fSymbols;
fIntrinsics = base.fIntrinsics.get();
if (fIntrinsics) {
@@ -2160,8 +2162,7 @@
auto decl = VarDeclaration::Make(fContext, var.get(), fContext.fTypes.fInt.get(),
/*arraySize=*/0, /*value=*/nullptr);
fSymbolTable->add(std::move(var));
- fProgramElements->push_back(
- std::make_unique<GlobalVarDeclaration>(/*offset=*/-1, std::move(decl)));
+ fProgramElements->push_back(std::make_unique<GlobalVarDeclaration>(std::move(decl)));
}
if (externalFunctions) {
@@ -2220,9 +2221,6 @@
std::vector<std::unique_ptr<ProgramElement>> elements;
std::vector<const ProgramElement*> sharedElements;
- fProgramElements = &elements;
- fSharedElements = &sharedElements;
-
this->start(base, isBuiltinCode, externalFunctions, &elements, &sharedElements);
Parser parser(text, length, *fSymbolTable, this->errorReporter());
diff --git a/src/sksl/SkSLPool.cpp b/src/sksl/SkSLPool.cpp
index f2b2a8b..60b4f7e 100644
--- a/src/sksl/SkSLPool.cpp
+++ b/src/sksl/SkSLPool.cpp
@@ -70,6 +70,10 @@
return pool;
}
+bool Pool::IsAttached() {
+ return get_thread_local_memory_pool();
+}
+
void Pool::attachToThread() {
VLOG("ATTACH Pool:0x%016llX\n", (uint64_t)fMemPool.get());
SkASSERT(get_thread_local_memory_pool() == nullptr);
diff --git a/src/sksl/SkSLPool.h b/src/sksl/SkSLPool.h
index 005310c..e464797 100644
--- a/src/sksl/SkSLPool.h
+++ b/src/sksl/SkSLPool.h
@@ -47,6 +47,8 @@
// the pool can be destroyed.
static void FreeMemory(void* ptr);
+ static bool IsAttached();
+
private:
void checkForLeaks();
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index 0021edc..cb6092c 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -332,7 +332,7 @@
}
case Rehydrator::kVarDeclarations_Command: {
std::unique_ptr<Statement> decl = this->statement();
- return std::make_unique<GlobalVarDeclaration>(/*offset=*/-1, std::move(decl));
+ return std::make_unique<GlobalVarDeclaration>(std::move(decl));
}
case Rehydrator::kStructDefinition_Command: {
const Symbol* type = this->symbol();
diff --git a/src/sksl/codegen/SkSLVMCodeGenerator.cpp b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
index 1989c9c..a122aca 100644
--- a/src/sksl/codegen/SkSLVMCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
@@ -1278,6 +1278,8 @@
// This merges currentFunction().fReturned into fConditionMask. Lanes that conditionally
// returned in the current function would otherwise resume execution within the child.
ScopedCondition m(this, ~currentFunction().fReturned);
+ SkASSERTF(f.function().definition(), "no definition for function '%s'",
+ f.function().description().c_str());
this->writeFunction(*f.function().definition(), argVals, result.asSpan());
}
diff --git a/src/sksl/dsl/DSLFunction.cpp b/src/sksl/dsl/DSLFunction.cpp
index 7e4f887..3aada20 100644
--- a/src/sksl/dsl/DSLFunction.cpp
+++ b/src/sksl/dsl/DSLFunction.cpp
@@ -22,6 +22,11 @@
std::vector<DSLVar*> params) {
std::vector<const Variable*> paramVars;
paramVars.reserve(params.size());
+ bool isMain = !strcmp(name, "main");
+ auto typeIsValidForColor = [&](const SkSL::Type& type) {
+ return type == *DSLWriter::Context().fTypes.fHalf4 ||
+ type == *DSLWriter::Context().fTypes.fFloat4;
+ };
for (DSLVar* param : params) {
// This counts as declaring the variable; make sure it hasn't been previously declared and
// then kill its pending declaration statement. Otherwise the statement will hang around
@@ -37,6 +42,22 @@
"initial values\n");
}
param->fDeclared = true;
+ param->fStorage = SkSL::VariableStorage::kParameter;
+ if (paramVars.empty()) {
+ SkSL::ProgramKind kind = DSLWriter::Context().fConfig->fKind;
+ if (isMain && (kind == ProgramKind::kRuntimeEffect ||
+ kind == ProgramKind::kFragmentProcessor)) {
+ const SkSL::Type& type = param->fType.skslType();
+ // 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
+ // a half4/float parameter is supposed to be the input color:
+ if (type == *DSLWriter::Context().fTypes.fFloat2) {
+ param->fModifiers.fModifiers.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
+ } else if (typeIsValidForColor(type)) {
+ param->fModifiers.fModifiers.fLayout.fBuiltin = SK_INPUT_COLOR_BUILTIN;
+ }
+ }
+ }
paramVars.push_back(&DSLWriter::Var(*param));
param->fDeclaration = nullptr;
}
@@ -44,7 +65,7 @@
fDecl = symbols.add(std::make_unique<SkSL::FunctionDeclaration>(
/*offset=*/-1,
DSLWriter::Modifiers(SkSL::Modifiers()),
- DSLWriter::Name(name),
+ isMain ? name : DSLWriter::Name(name),
std::move(paramVars), fReturnType,
/*builtin=*/false));
}
@@ -61,6 +82,7 @@
DSLWriter::Compiler().setErrorCount(0);
SkASSERT(!DSLWriter::Compiler().errorCount());
}
+ fDecl->fDefinition = function.get();
DSLWriter::ProgramElements().push_back(std::move(function));
}
diff --git a/src/sksl/dsl/DSLRuntimeEffects.cpp b/src/sksl/dsl/DSLRuntimeEffects.cpp
new file mode 100644
index 0000000..2408c03
--- /dev/null
+++ b/src/sksl/dsl/DSLRuntimeEffects.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "include/sksl/DSLRuntimeEffects.h"
+
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/sksl/DSLCore.h"
+#include "src/sksl/SkSLCompiler.h"
+#include "src/sksl/SkSLIRGenerator.h"
+#include "src/sksl/dsl/priv/DSLWriter.h"
+
+namespace SkSL {
+
+namespace dsl {
+
+#ifndef SKSL_STANDALONE
+
+void StartRuntimeShader(SkSL::Compiler* compiler) {
+ Start(compiler, SkSL::ProgramKind::kRuntimeEffect);
+ SkSL::ProgramSettings& settings = DSLWriter::IRGenerator().fContext.fConfig->fSettings;
+ SkASSERT(settings.fInlineThreshold == SkSL::kDefaultInlineThreshold);
+ settings.fInlineThreshold = 0;
+ SkASSERT(!settings.fAllowNarrowingConversions);
+ settings.fAllowNarrowingConversions = true;
+}
+
+sk_sp<SkRuntimeEffect> EndRuntimeShader() {
+ std::unique_ptr<SkSL::Program> program = DSLWriter::ReleaseProgram();
+ auto result = SkRuntimeEffect::MakeForShader(std::move(program));
+ // TODO(skbug.com/11862): propagate errors properly
+ SkASSERTF(result.effect, "%s\n", result.errorText.c_str());
+ SkSL::ProgramSettings& settings = DSLWriter::IRGenerator().fContext.fConfig->fSettings;
+ settings.fInlineThreshold = SkSL::kDefaultInlineThreshold;
+ settings.fAllowNarrowingConversions = false;
+ End();
+ return result.effect;
+}
+
+#endif // SKSL_STANDALONE
+
+} // namespace dsl
+
+} // namespace SkSL
diff --git a/src/sksl/dsl/DSLType.cpp b/src/sksl/dsl/DSLType.cpp
index c1db0f5..9c8fa81 100644
--- a/src/sksl/dsl/DSLType.cpp
+++ b/src/sksl/dsl/DSLType.cpp
@@ -91,6 +91,8 @@
return *context.fTypes.fInt3;
case kInt4_Type:
return *context.fTypes.fInt4;
+ case kShader_Type:
+ return *context.fTypes.fShader;
case kShort_Type:
return *context.fTypes.fShort;
case kShort2_Type:
diff --git a/src/sksl/dsl/DSLVar.cpp b/src/sksl/dsl/DSLVar.cpp
index 437f0b9..9756672 100644
--- a/src/sksl/dsl/DSLVar.cpp
+++ b/src/sksl/dsl/DSLVar.cpp
@@ -9,6 +9,7 @@
#include "include/sksl/DSLModifiers.h"
#include "include/sksl/DSLType.h"
+#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLUtil.h"
#include "src/sksl/dsl/priv/DSLWriter.h"
#include "src/sksl/ir/SkSLBinaryExpression.h"
@@ -34,13 +35,23 @@
// correctly-named variable with the right type, so we just create a placeholder for it.
// TODO(skia/11330): we'll need to fix this when switching over to nodes.
fVar = DSLWriter::SymbolTable()->takeOwnershipOfIRNode(
- std::make_unique<SkSL::Variable>(
- /*offset=*/-1,
- DSLWriter::IRGenerator().fModifiers->addToPool(SkSL::Modifiers()),
- fName,
- DSLWriter::Context().fTypes.fFloat2.get(),
- /*builtin=*/true,
- SkSL::VariableStorage::kGlobal));
+ std::make_unique<SkSL::Variable>(
+ /*offset=*/-1,
+ DSLWriter::IRGenerator().fModifiers->addToPool(
+ SkSL::Modifiers(
+ SkSL::Layout(/*flags=*/0, /*location=*/-1, /*offset=*/-1,
+ /*binding=*/-1, /*index=*/-1, /*set=*/-1,
+ SK_MAIN_COORDS_BUILTIN,
+ /*inputAttachmentIndex=*/-1,
+ Layout::kUnspecified_Primitive,
+ /*maxVertices=*/1, /*invocations=*/-1,
+ /*marker=*/"", /*when=*/"",
+ Layout::CType::kDefault),
+ SkSL::Modifiers::kNo_Flag)),
+ fName,
+ DSLWriter::Context().fTypes.fFloat2.get(),
+ /*builtin=*/true,
+ SkSL::VariableStorage::kGlobal));
return;
}
#endif
@@ -62,7 +73,7 @@
: fModifiers(std::move(modifiers))
, fType(std::move(type))
, fRawName(name)
- , fName(DSLWriter::Name(name))
+ , fName(fType.skslType().isOpaque() ? name : DSLWriter::Name(name))
, fInitialValue(std::move(initialValue))
, fStorage(Variable::Storage::kLocal)
, fDeclared(DSLWriter::Instance().fMarkVarsDeclared) {
diff --git a/src/sksl/dsl/priv/DSLWriter.cpp b/src/sksl/dsl/priv/DSLWriter.cpp
index f972c2b..1ed8c61 100644
--- a/src/sksl/dsl/priv/DSLWriter.cpp
+++ b/src/sksl/dsl/priv/DSLWriter.cpp
@@ -34,25 +34,28 @@
DSLWriter::DSLWriter(SkSL::Compiler* compiler, SkSL::ProgramKind kind)
: fCompiler(compiler) {
SkSL::ParsedModule module = fCompiler->moduleForProgramKind(kind);
- fConfig.fKind = kind;
+ fConfig = std::make_unique<ProgramConfig>();
+ fConfig->fKind = kind;
SkSL::IRGenerator& ir = *fCompiler->fIRGenerator;
- fOldSymbolTable = ir.fSymbolTable;
fOldConfig = fCompiler->fContext->fConfig;
- ir.fSymbolTable = module.fSymbols;
- fCompiler->fContext->fConfig = &fConfig;
- ir.pushSymbolTable();
+ fCompiler->fContext->fConfig = fConfig.get();
if (compiler->context().fCaps.useNodePools()) {
fPool = Pool::Create();
fPool->attachToThread();
}
+ ir.start(module, false, nullptr, &fProgramElements, &fSharedElements);
}
DSLWriter::~DSLWriter() {
- SkSL::IRGenerator& ir = *fCompiler->fIRGenerator;
- ir.fSymbolTable = fOldSymbolTable;
+ if (SymbolTable()) {
+ fCompiler->fIRGenerator->finish();
+ fProgramElements.clear();
+ } else {
+ // We should only be here with a null symbol table if ReleaseProgram was called
+ SkASSERT(fProgramElements.empty());
+ }
fCompiler->fContext->fConfig = fOldConfig;
- fProgramElements.clear();
if (fPool) {
fPool->detachFromThread();
}
@@ -198,8 +201,10 @@
const SkSL::Variable& DSLWriter::Var(DSLVar& var) {
if (!var.fVar) {
- DSLWriter::IRGenerator().checkVarDeclaration(/*offset=*/-1, var.fModifiers.fModifiers,
- &var.fType.skslType(), var.fStorage);
+ if (var.fStorage != SkSL::VariableStorage::kParameter) {
+ DSLWriter::IRGenerator().checkVarDeclaration(/*offset=*/-1, var.fModifiers.fModifiers,
+ &var.fType.skslType(), var.fStorage);
+ }
std::unique_ptr<SkSL::Variable> skslvar = DSLWriter::IRGenerator().convertVar(
/*offset=*/-1,
var.fModifiers.fModifiers,
@@ -214,6 +219,10 @@
var.fDeclaration = DSLWriter::IRGenerator().convertVarDeclaration(
std::move(skslvar),
var.fInitialValue.release());
+ if (var.fStorage == Variable::Storage::kGlobal) {
+ DSLWriter::ProgramElements().push_back(std::make_unique<SkSL::GlobalVarDeclaration>(
+ std::move(var.fDeclaration)));
+ }
}
return *var.fVar;
}
@@ -228,6 +237,27 @@
var.fDeclared = true;
}
+std::unique_ptr<SkSL::Program> DSLWriter::ReleaseProgram() {
+ SkSL::IRGenerator& ir = IRGenerator();
+ IRGenerator::IRBundle bundle = ir.finish();
+ Pool* pool = Instance().fPool.get();
+ auto result = std::make_unique<SkSL::Program>(/*source=*/nullptr,
+ std::move(DSLWriter::Instance().fConfig),
+ Compiler().fContext,
+ std::move(bundle.fElements),
+ std::move(bundle.fSharedElements),
+ std::move(bundle.fModifiers),
+ std::move(bundle.fSymbolTable),
+ std::move(Instance().fPool),
+ bundle.fInputs);
+ if (pool) {
+ pool->detachFromThread();
+ }
+ SkASSERT(ProgramElements().empty());
+ SkASSERT(!SymbolTable());
+ return result;
+}
+
#if !SK_SUPPORT_GPU || defined(SKSL_STANDALONE)
DSLWriter& DSLWriter::Instance() {
diff --git a/src/sksl/dsl/priv/DSLWriter.h b/src/sksl/dsl/priv/DSLWriter.h
index 8e47cbd..c2d936a 100644
--- a/src/sksl/dsl/priv/DSLWriter.h
+++ b/src/sksl/dsl/priv/DSLWriter.h
@@ -199,17 +199,19 @@
return Instance().fMangle;
}
+ static std::unique_ptr<SkSL::Program> ReleaseProgram();
+
static DSLWriter& Instance();
static void SetInstance(std::unique_ptr<DSLWriter> instance);
private:
- SkSL::ProgramConfig fConfig;
+ std::unique_ptr<SkSL::ProgramConfig> fConfig;
SkSL::Compiler* fCompiler;
std::unique_ptr<Pool> fPool;
- std::shared_ptr<SkSL::SymbolTable> fOldSymbolTable;
SkSL::ProgramConfig* fOldConfig;
std::vector<std::unique_ptr<SkSL::ProgramElement>> fProgramElements;
+ std::vector<const SkSL::ProgramElement*> fSharedElements;
ErrorHandler* fErrorHandler = nullptr;
bool fMangle = true;
bool fMarkVarsDeclared = false;
diff --git a/src/sksl/ir/SkSLFunctionDeclaration.h b/src/sksl/ir/SkSLFunctionDeclaration.h
index 5324cd5..8dda492 100644
--- a/src/sksl/ir/SkSLFunctionDeclaration.h
+++ b/src/sksl/ir/SkSLFunctionDeclaration.h
@@ -182,6 +182,8 @@
bool fBuiltin;
bool fIsMain;
+ friend class SkSL::dsl::DSLFunction;
+
using INHERITED = Symbol;
};
diff --git a/src/sksl/ir/SkSLProgram.h b/src/sksl/ir/SkSLProgram.h
index c762f4d..e44497e 100644
--- a/src/sksl/ir/SkSLProgram.h
+++ b/src/sksl/ir/SkSLProgram.h
@@ -191,6 +191,14 @@
std::vector<std::unique_ptr<ProgramElement>>& ownedElements() { return fElements; }
const std::vector<std::unique_ptr<ProgramElement>>& ownedElements() const { return fElements; }
+ String description() const {
+ String result;
+ for (const auto& e : this->elements()) {
+ result += e->description();
+ }
+ return result;
+ }
+
std::unique_ptr<String> fSource;
std::unique_ptr<ProgramConfig> fConfig;
std::shared_ptr<Context> fContext;
diff --git a/src/sksl/ir/SkSLVarDeclarations.h b/src/sksl/ir/SkSLVarDeclarations.h
index aba1869..8a2b7ca 100644
--- a/src/sksl/ir/SkSLVarDeclarations.h
+++ b/src/sksl/ir/SkSLVarDeclarations.h
@@ -105,8 +105,8 @@
public:
static constexpr Kind kProgramElementKind = Kind::kGlobalVar;
- GlobalVarDeclaration(int offset, std::unique_ptr<Statement> decl)
- : INHERITED(offset, kProgramElementKind)
+ GlobalVarDeclaration(std::unique_ptr<Statement> decl)
+ : INHERITED(decl->fOffset, kProgramElementKind)
, fDeclaration(std::move(decl)) {
SkASSERT(this->declaration()->is<VarDeclaration>());
}
@@ -120,7 +120,7 @@
}
std::unique_ptr<ProgramElement> clone() const override {
- return std::make_unique<GlobalVarDeclaration>(fOffset, this->declaration()->clone());
+ return std::make_unique<GlobalVarDeclaration>(this->declaration()->clone());
}
String description() const override {
diff --git a/src/sksl/ir/SkSLVariable.h b/src/sksl/ir/SkSLVariable.h
index e38ca9b..92796fb 100644
--- a/src/sksl/ir/SkSLVariable.h
+++ b/src/sksl/ir/SkSLVariable.h
@@ -22,6 +22,7 @@
namespace dsl {
class DSLCore;
+class DSLFunction;
} // namespace dsl
enum class VariableStorage : int8_t {
@@ -89,6 +90,7 @@
using INHERITED = Symbol;
friend class dsl::DSLCore;
+ friend class dsl::DSLFunction;
friend class VariableReference;
};
diff --git a/tests/SkDSLRuntimeEffectTest.cpp b/tests/SkDSLRuntimeEffectTest.cpp
new file mode 100644
index 0000000..7568b80
--- /dev/null
+++ b/tests/SkDSLRuntimeEffectTest.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkBitmap.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkData.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkSurface.h"
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/gpu/GrDirectContext.h"
+#include "include/sksl/DSLRuntimeEffects.h"
+#include "src/core/SkTLazy.h"
+#include "src/gpu/GrColor.h"
+#include "src/sksl/SkSLCompiler.h"
+#include "tests/Test.h"
+
+#include <algorithm>
+#include <thread>
+
+using namespace SkSL::dsl;
+
+class DSLTestEffect {
+public:
+ DSLTestEffect(skiatest::Reporter* r, sk_sp<SkSurface> surface)
+ : fReporter(r)
+ , fCaps(SkSL::ShaderCapsFactory::Standalone())
+ , fCompiler(std::make_unique<SkSL::Compiler>(fCaps.get()))
+ , fSurface(std::move(surface)) {}
+
+ void start() {
+ StartRuntimeShader(fCompiler.get());
+ }
+
+ void end() {
+ sk_sp<SkRuntimeEffect> effect = EndRuntimeShader();
+ SkASSERT(effect);
+ fBuilder.init(std::move(effect));
+ }
+
+ SkRuntimeShaderBuilder::BuilderUniform uniform(const char* name) {
+ return fBuilder->uniform(name);
+ }
+ SkRuntimeShaderBuilder::BuilderChild child(const char* name) {
+ return fBuilder->child(name);
+ }
+
+ using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>;
+
+ void test(GrColor TL, GrColor TR, GrColor BL, GrColor BR,
+ PreTestFn preTestCallback = nullptr) {
+ auto shader = fBuilder->makeShader(nullptr, false);
+ if (!shader) {
+ REPORT_FAILURE(fReporter, "shader", SkString("Effect didn't produce a shader"));
+ return;
+ }
+
+ SkCanvas* canvas = fSurface->getCanvas();
+ SkPaint paint;
+ paint.setShader(std::move(shader));
+ paint.setBlendMode(SkBlendMode::kSrc);
+
+ canvas->save();
+ if (preTestCallback) {
+ preTestCallback(canvas, &paint);
+ }
+ canvas->drawPaint(paint);
+ canvas->restore();
+
+ GrColor actual[4];
+ SkImageInfo info = fSurface->imageInfo();
+ if (!fSurface->readPixels(info, actual, info.minRowBytes(), 0, 0)) {
+ REPORT_FAILURE(fReporter, "readPixels", SkString("readPixels failed"));
+ return;
+ }
+
+ GrColor expected[4] = {TL, TR, BL, BR};
+ if (0 != memcmp(actual, expected, sizeof(actual))) {
+ REPORT_FAILURE(fReporter, "Runtime effect didn't match expectations",
+ SkStringPrintf("\n"
+ "Expected: [ %08x %08x %08x %08x ]\n"
+ "Got : [ %08x %08x %08x %08x ]\n"
+ "SkSL:\n%s\n",
+ TL, TR, BL, BR, actual[0], actual[1], actual[2],
+ actual[3], fBuilder->effect()->source().c_str()));
+ }
+ }
+
+ void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
+ this->test(expected, expected, expected, expected, preTestCallback);
+ }
+
+private:
+ skiatest::Reporter* fReporter;
+ SkSL::ShaderCapsPointer fCaps;
+ std::unique_ptr<SkSL::Compiler> fCompiler;
+ sk_sp<SkSurface> fSurface;
+ SkTLazy<SkRuntimeShaderBuilder> fBuilder;
+};
+
+static void test_RuntimeEffect_Shaders(skiatest::Reporter* r, GrRecordingContext* rContext) {
+ SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ sk_sp<SkSurface> surface = rContext
+ ? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
+ : SkSurface::MakeRaster(info);
+ REPORTER_ASSERT(r, surface);
+ using float4 = std::array<float, 4>;
+ using int4 = std::array<int, 4>;
+ DSLTestEffect effect(r, surface);
+
+ // Local coords
+ {
+ effect.start();
+ Var p(kFloat2_Type, "p");
+ Function(kHalf4_Type, "main", p).define(
+ Return(Half4(Half2(p - 0.5), 0, 1))
+ );
+ effect.end();
+ effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
+ }
+
+ // Use of a simple uniform. (Draw twice with two values to ensure it's updated).
+ {
+ effect.start();
+ Var gColor(kUniform_Modifier, kFloat4_Type);
+ Function(kHalf4_Type, "main").define(
+ Return(Half4(gColor))
+ );
+ effect.end();
+ effect.uniform(gColor.name()) = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
+ effect.test(0xFFBF4000);
+ effect.uniform(gColor.name()) = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
+ effect.test(0x7F00007F); // Tests that we clamp to valid premul
+ }
+
+ // Same, with integer uniforms
+ {
+ effect.start();
+ Var gColor(kUniform_Modifier, kInt4_Type);
+ Function(kHalf4_Type, "main").define(
+ Return(Half4(gColor) / 255)
+ );
+ effect.end();
+ effect.uniform(gColor.name()) = int4{ 0x00, 0x40, 0xBF, 0xFF };
+ effect.test(0xFFBF4000);
+ effect.uniform(gColor.name()) = int4{ 0xFF, 0x00, 0x00, 0x7F };
+ effect.test(0x7F00007F); // Tests that we clamp to valid premul
+ }
+
+ // Test sk_FragCoord (device coords). Rotate the canvas to be sure we're seeing device coords.
+ // Since the surface is 2x2, we should see (0,0), (1,0), (0,1), (1,1). Multiply by 0.498 to
+ // make sure we're not saturating unexpectedly.
+ {
+ effect.start();
+ Function(kHalf4_Type, "main").define(
+ Return(Half4(0.498 * (Half2(Swizzle(sk_FragCoord(), X, Y)) - 0.5), 0, 1))
+ );
+ effect.end();
+ effect.test(0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F,
+ [](SkCanvas* canvas, SkPaint*) { canvas->rotate(45.0f); });
+ }
+
+ // Runtime effects should use relaxed precision rules by default
+ {
+ effect.start();
+ Var p(kFloat2_Type, "p");
+ Function(kHalf4_Type, "main", p).define(
+ Return(Float4(p - 0.5, 0, 1))
+ );
+ effect.end();
+ effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
+ }
+
+ // ... and support *returning* float4, not just half4
+ {
+ effect.start();
+ Var p(kFloat2_Type, "p");
+ Function(kFloat4_Type, "main", p).define(
+ Return(Float4(p - 0.5, 0, 1))
+ );
+ effect.end();
+ effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
+ }
+
+ // Mutating coords should work. (skbug.com/10918)
+ {
+ effect.start();
+ Var p(kFloat2_Type, "p");
+ Function(kFloat4_Type, "main", p).define(
+ p -= 0.5,
+ Return(Float4(p, 0, 1))
+ );
+ effect.end();
+ effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
+ }
+ {
+ effect.start();
+ Var p1(kInOut_Modifier, kFloat2_Type, "p");
+ Function moveCoords(kVoid_Type, "moveCoords", p1);
+ moveCoords.define(
+ p1 -= 0.5
+ );
+ Var p2(kFloat2_Type, "p");
+ Function(kFloat4_Type, "main", p2).define(
+ moveCoords(p2),
+ Return(Float4(p2, 0, 1))
+ );
+ effect.end();
+ effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF);
+ }
+
+ //
+ // Sampling children
+ //
+
+ // Sampling a null child should return the paint color
+ {
+ effect.start();
+ Var child(kUniform_Modifier, kShader_Type, "child");
+ Function(kFloat4_Type, "main").define(
+ Return(Sample(child))
+ );
+ effect.end();
+ effect.child("child") = nullptr;
+ effect.test(0xFF00FFFF,
+ [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
+ }
+}
+
+DEF_TEST(DSLRuntimeEffectSimple, r) {
+ test_RuntimeEffect_Shaders(r, nullptr);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DSLRuntimeEffectSimple_GPU, r, ctxInfo) {
+ test_RuntimeEffect_Shaders(r, ctxInfo.directContext());
+}
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index 75f194f..34f4260 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -1492,7 +1492,7 @@
// Uniforms do not need to be explicitly declared
}
-DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLSample, r, ctxInfo) {
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLSampleFragmentProcessor, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), /*markVarsDeclared=*/true,
SkSL::ProgramKind::kFragmentProcessor);
DSLVar child(kUniform_Modifier, kFragmentProcessor_Type, "child");
@@ -1510,6 +1510,20 @@
}
}
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLSampleShader, r, ctxInfo) {
+ AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), /*markVarsDeclared=*/true,
+ SkSL::ProgramKind::kRuntimeEffect);
+ DSLVar shader(kUniform_Modifier, kShader_Type, "shader");
+ EXPECT_EQUAL(Sample(shader), "sample(shader)");
+ EXPECT_EQUAL(Sample(shader, Float2(0, 0)), "sample(shader, float2(0.0, 0.0))");
+ EXPECT_EQUAL(Sample(shader, Float3x3(1)), "sample(shader, float3x3(1.0))");
+
+ {
+ ExpectError error(r, "error: no match for sample(shader, half4)\n");
+ Sample(shader, Half4(1)).release();
+ }
+}
+
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLStruct, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), /*markVarsDeclared=*/false);