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>
This commit is contained in:
parent
224e5e4d7e
commit
624a529fbd
@ -26,6 +26,7 @@ skia_sksl_sources = [
|
||||
"$_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 @@ skia_sksl_sources = [
|
||||
"$_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",
|
||||
|
@ -245,6 +245,7 @@ tests_sources = [
|
||||
"$_tests/SkColor4fTest.cpp",
|
||||
"$_tests/SkColorSpaceXformStepsTest.cpp",
|
||||
"$_tests/SkDOMTest.cpp",
|
||||
"$_tests/SkDSLRuntimeEffectTest.cpp",
|
||||
"$_tests/SkFixed15Test.cpp",
|
||||
"$_tests/SkGaussFilterTest.cpp",
|
||||
"$_tests/SkGlyphBufferTest.cpp",
|
||||
|
@ -126,6 +126,10 @@ public:
|
||||
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 @@ private:
|
||||
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); }
|
||||
|
@ -41,6 +41,7 @@ private:
|
||||
SkSL::Modifiers fModifiers;
|
||||
|
||||
friend DSLType Struct(const char* name, SkTArray<DSLField> fields);
|
||||
friend class DSLFunction;
|
||||
friend class DSLVar;
|
||||
friend class DSLWriter;
|
||||
};
|
||||
|
33
include/sksl/DSLRuntimeEffects.h
Normal file
33
include/sksl/DSLRuntimeEffects.h
Normal file
@ -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
|
@ -58,6 +58,7 @@ enum TypeConstant : uint8_t {
|
||||
kInt2_Type,
|
||||
kInt3_Type,
|
||||
kInt4_Type,
|
||||
kShader_Type,
|
||||
kShort_Type,
|
||||
kShort2_Type,
|
||||
kShort3_Type,
|
||||
|
@ -40,6 +40,10 @@ public:
|
||||
|
||||
~DSLVar();
|
||||
|
||||
const char* name() const {
|
||||
return fName;
|
||||
}
|
||||
|
||||
DSLExpression x() {
|
||||
return DSLExpression(*this).x();
|
||||
}
|
||||
@ -115,10 +119,6 @@ private:
|
||||
*/
|
||||
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
|
||||
|
@ -109,31 +109,67 @@ static bool init_uniform_type(const SkSL::Context& ctx,
|
||||
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 @@ SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl,
|
||||
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 @@ SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(SkString sksl, const Opti
|
||||
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 {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -38,7 +38,8 @@ public:
|
||||
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
|
||||
|
||||
|
@ -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 @@ class Context {
|
||||
public:
|
||||
Context(ErrorReporter& errors, const ShaderCapsClass& caps);
|
||||
|
||||
~Context() {
|
||||
SkASSERT(!Pool::IsAttached());
|
||||
}
|
||||
|
||||
// The Context holds all of the built-in types.
|
||||
BuiltinTypes fTypes;
|
||||
|
||||
|
@ -1411,8 +1411,7 @@ void IRGenerator::convertGlobalVarDeclarations(const ASTNode& decl) {
|
||||
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 @@ void IRGenerator::findAndDeclareBuiltinVariables() {
|
||||
};
|
||||
|
||||
BuiltinVariableScanner scanner(this);
|
||||
SkASSERT(fProgramElements);
|
||||
for (auto& e : *fProgramElements) {
|
||||
scanner.visitProgramElement(*e);
|
||||
}
|
||||
@ -2131,6 +2131,8 @@ void IRGenerator::start(const ParsedModule& base,
|
||||
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 @@ void IRGenerator::start(const ParsedModule& base,
|
||||
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 @@ IRGenerator::IRBundle IRGenerator::convertProgram(
|
||||
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());
|
||||
|
@ -70,6 +70,10 @@ std::unique_ptr<Pool> Pool::Create() {
|
||||
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);
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
// the pool can be destroyed.
|
||||
static void FreeMemory(void* ptr);
|
||||
|
||||
static bool IsAttached();
|
||||
|
||||
private:
|
||||
void checkForLeaks();
|
||||
|
||||
|
@ -332,7 +332,7 @@ std::unique_ptr<ProgramElement> Rehydrator::element() {
|
||||
}
|
||||
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();
|
||||
|
@ -1278,6 +1278,8 @@ Value SkVMGenerator::writeFunctionCall(const FunctionCall& f) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,11 @@ void DSLFunction::init(const DSLType& returnType, const char* name,
|
||||
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 @@ void DSLFunction::init(const DSLType& returnType, const char* name,
|
||||
"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 @@ void DSLFunction::init(const DSLType& returnType, const char* name,
|
||||
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 @@ void DSLFunction::define(DSLBlock block) {
|
||||
DSLWriter::Compiler().setErrorCount(0);
|
||||
SkASSERT(!DSLWriter::Compiler().errorCount());
|
||||
}
|
||||
fDecl->fDefinition = function.get();
|
||||
DSLWriter::ProgramElements().push_back(std::move(function));
|
||||
}
|
||||
|
||||
|
47
src/sksl/dsl/DSLRuntimeEffects.cpp
Normal file
47
src/sksl/dsl/DSLRuntimeEffects.cpp
Normal file
@ -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
|
@ -91,6 +91,8 @@ const SkSL::Type& DSLType::skslType() const {
|
||||
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:
|
||||
|
@ -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 @@ DSLVar::DSLVar(const char* name)
|
||||
// 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 @@ DSLVar::DSLVar(DSLModifiers modifiers, DSLType type, const char* name, DSLExpres
|
||||
: 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) {
|
||||
|
@ -34,25 +34,28 @@ namespace dsl {
|
||||
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 @@ void DSLWriter::ReportError(const char* msg, PositionInfo* info) {
|
||||
|
||||
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 @@ const SkSL::Variable& DSLWriter::Var(DSLVar& var) {
|
||||
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 @@ void DSLWriter::MarkDeclared(DSLVar& var) {
|
||||
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() {
|
||||
|
@ -199,17 +199,19 @@ public:
|
||||
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;
|
||||
|
@ -182,6 +182,8 @@ private:
|
||||
bool fBuiltin;
|
||||
bool fIsMain;
|
||||
|
||||
friend class SkSL::dsl::DSLFunction;
|
||||
|
||||
using INHERITED = Symbol;
|
||||
};
|
||||
|
||||
|
@ -191,6 +191,14 @@ struct Program {
|
||||
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;
|
||||
|
@ -105,8 +105,8 @@ class GlobalVarDeclaration final : public ProgramElement {
|
||||
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 @@ public:
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -22,6 +22,7 @@ class VarDeclaration;
|
||||
|
||||
namespace dsl {
|
||||
class DSLCore;
|
||||
class DSLFunction;
|
||||
} // namespace dsl
|
||||
|
||||
enum class VariableStorage : int8_t {
|
||||
@ -89,6 +90,7 @@ private:
|
||||
using INHERITED = Symbol;
|
||||
|
||||
friend class dsl::DSLCore;
|
||||
friend class dsl::DSLFunction;
|
||||
friend class VariableReference;
|
||||
};
|
||||
|
||||
|
240
tests/SkDSLRuntimeEffectTest.cpp
Normal file
240
tests/SkDSLRuntimeEffectTest.cpp
Normal file
@ -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());
|
||||
}
|
@ -1492,7 +1492,7 @@ DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLModifiers, r, ctxInfo) {
|
||||
// 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(DSLSample, r, ctxInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user