Added basic SkSL DSL statements

(and Ternary for good measure)

Change-Id: I4afa121d54ab9ba8d0814693ce53da7cb73ef340
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353626
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
This commit is contained in:
Ethan Nicholas 2021-01-22 15:18:25 -05:00 committed by Skia Commit-Bot
parent 2a4c0fbdca
commit d6b6f3ec84
16 changed files with 466 additions and 38 deletions

View File

@ -58,8 +58,10 @@ skia_sksl_sources = [
"$_src/sksl/SkSLUtil.h",
"$_src/sksl/SkSLVMGenerator.cpp",
"$_src/sksl/SkSLVMGenerator.h",
"$_src/sksl/dsl/DSLBlock.cpp",
"$_src/sksl/dsl/DSLCore.cpp",
"$_src/sksl/dsl/DSLExpression.cpp",
"$_src/sksl/dsl/DSLStatement.cpp",
"$_src/sksl/dsl/DSLType.cpp",
"$_src/sksl/dsl/DSLVar.cpp",
"$_src/sksl/dsl/priv/DSLWriter.cpp",

View File

@ -539,8 +539,7 @@ std::unique_ptr<ModifiersDeclaration> IRGenerator::convertModifiersDeclaration(c
std::unique_ptr<Statement> IRGenerator::convertIf(const ASTNode& n) {
SkASSERT(n.fKind == ASTNode::Kind::kIf);
auto iter = n.begin();
std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*(iter++)),
*fContext.fTypes.fBool);
std::unique_ptr<Expression> test = this->convertExpression(*(iter++));
if (!test) {
return nullptr;
}
@ -564,7 +563,10 @@ std::unique_ptr<Statement> IRGenerator::convertIf(int offset, bool isStatic,
std::unique_ptr<Expression> test,
std::unique_ptr<Statement> ifTrue,
std::unique_ptr<Statement> ifFalse) {
SkASSERT(test->type().isBoolean());
test = this->coerce(std::move(test), *fContext.fTypes.fBool);
if (!test) {
return nullptr;
}
if (test->is<BoolLiteral>()) {
// Static Boolean values can fold down to a single branch.
if (test->as<BoolLiteral>().value()) {

View File

@ -33,6 +33,8 @@
namespace SkSL {
namespace dsl {
class DSLCore;
class DSLVar;
class DSLWriter;
}
@ -153,7 +155,7 @@ private:
std::unique_ptr<ModifiersPool> releaseModifiers();
void checkModifiers(int offset, const Modifiers& modifiers, int permitted);
void checkVarDeclaration(int offset, const Modifiers& modifiers,const Type* baseType,
void checkVarDeclaration(int offset, const Modifiers& modifiers, const Type* baseType,
Variable::Storage storage);
std::unique_ptr<Statement> convertVarDeclaration(int offset, const Modifiers& modifiers,
const Type* baseType, StringFragment name,
@ -299,6 +301,8 @@ private:
friend class AutoSwitchLevel;
friend class AutoDisableInline;
friend class Compiler;
friend class dsl::DSLCore;
friend class dsl::DSLVar;
friend class dsl::DSLWriter;
};

View File

@ -14,7 +14,9 @@ namespace SkSL {
namespace dsl {
using Block = DSLBlock;
using Expression = DSLExpression;
using Statement = DSLStatement;
using Var = DSLVar;
} // namespace dsl

27
src/sksl/dsl/DSLBlock.cpp Normal file
View File

@ -0,0 +1,27 @@
/*
* 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 "src/sksl/dsl/DSLBlock.h"
#include "src/sksl/dsl/DSLStatement.h"
#include "src/sksl/ir/SkSLBlock.h"
namespace SkSL {
namespace dsl {
std::unique_ptr<SkSL::Statement> DSLBlock::release() {
return std::make_unique<SkSL::Block>(/*offset=*/-1, std::move(fStatements));
}
void DSLBlock::append(DSLStatement stmt) {
fStatements.push_back(stmt.release());
}
} // namespace dsl
} // namespace SkSL

50
src/sksl/dsl/DSLBlock.h Normal file
View File

@ -0,0 +1,50 @@
/*
* 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_BLOCK
#define SKSL_DSL_BLOCK
#include "include/private/SkTArray.h"
#include "src/sksl/dsl/DSLExpression.h"
#include "src/sksl/dsl/DSLStatement.h"
#include "src/sksl/ir/SkSLIRNode.h"
#include <memory>
namespace SkSL {
class Statement;
namespace dsl {
class DSLBlock {
public:
template<class... Statements>
DSLBlock(Statements... statements) {
fStatements.reserve_back(sizeof...(statements));
(fStatements.push_back(DSLStatement(std::move(statements)).release()), ...);
}
DSLBlock(SkSL::StatementArray statements)
: fStatements(std::move(statements)) {}
void append(DSLStatement stmt);
private:
std::unique_ptr<SkSL::Statement> release();
SkSL::StatementArray fStatements;
friend class DSLStatement;
friend class DSLFunction;
};
} // namespace dsl
} // namespace SkSL
#endif

View File

@ -7,7 +7,12 @@
#include "src/sksl/dsl/DSLCore.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLIRGenerator.h"
#include "src/sksl/dsl/priv/DSLWriter.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLIfStatement.h"
namespace SkSL {
@ -26,6 +31,70 @@ void End() {
void SetErrorHandler(ErrorHandler* errorHandler) {
DSLWriter::SetErrorHandler(errorHandler);
}
class DSLCore {
public:
static DSLStatement Declare(DSLVar& var, DSLExpression initialValue) {
if (!var.fDeclaration) {
DSLWriter::ReportError("Declare failed (was the variable already declared?)");
return DSLStatement();
}
VarDeclaration& decl = var.fDeclaration->as<SkSL::VarDeclaration>();
std::unique_ptr<Expression> expr = initialValue.coerceAndRelease(decl.var().type());
if (expr) {
decl.fValue = std::move(expr);
}
return DSLStatement(std::move(var.fDeclaration));
}
static DSLStatement Do(DSLStatement stmt, DSLExpression test) {
return DSLWriter::IRGenerator().convertDo(stmt.release(), test.release());
}
static DSLStatement For(DSLStatement initializer, DSLExpression test, DSLExpression next,
DSLStatement stmt) {
return DSLWriter::IRGenerator().convertFor(/*offset=*/-1, initializer.release(),
test.release(), next.release(), stmt.release());
}
static DSLStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse) {
return DSLWriter::IRGenerator().convertIf(/*offset=*/-1, /*isStatic=*/false, test.release(),
ifTrue.release(), ifFalse.release());
}
static DSLExpression Ternary(DSLExpression test, DSLExpression ifTrue, DSLExpression ifFalse) {
return DSLWriter::IRGenerator().convertTernaryExpression(test.release(), ifTrue.release(),
ifFalse.release());
}
static DSLStatement While(DSLExpression test, DSLStatement stmt) {
return DSLWriter::IRGenerator().convertWhile(/*offset=*/-1, test.release(), stmt.release());
}
};
DSLStatement Declare(DSLVar& var, DSLExpression initialValue) {
return DSLCore::Declare(var, std::move(initialValue));
}
DSLStatement Do(DSLStatement stmt, DSLExpression test) {
return DSLCore::Do(std::move(stmt), std::move(test));
}
DSLStatement For(DSLStatement initializer, DSLExpression test, DSLExpression next,
DSLStatement stmt) {
return DSLCore::For(std::move(initializer), std::move(test), std::move(next), std::move(stmt));
}
DSLStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse) {
return DSLCore::If(std::move(test), std::move(ifTrue), std::move(ifFalse));
}
DSLExpression Ternary(DSLExpression test, DSLExpression ifTrue, DSLExpression ifFalse) {
return DSLCore::Ternary(std::move(test), std::move(ifTrue), std::move(ifFalse));
}
DSLStatement While(DSLExpression test, DSLStatement stmt) {
return DSLCore::While(std::move(test), std::move(stmt));
}
} // namespace dsl

View File

@ -8,7 +8,9 @@
#ifndef SKSL_DSL_CORE
#define SKSL_DSL_CORE
#include "src/sksl/dsl/DSLBlock.h"
#include "src/sksl/dsl/DSLExpression.h"
#include "src/sksl/dsl/DSLStatement.h"
#include "src/sksl/dsl/DSLType.h"
#include "src/sksl/dsl/DSLVar.h"
@ -28,7 +30,6 @@ public:
virtual void handleError(const char* msg) = 0;
};
#if SK_SUPPORT_GPU && !defined(SKSL_STANDALONE)
/**
* Starts DSL output on the current thread using the specified compiler. This must be called
* prior to any other DSL functions.
@ -41,7 +42,36 @@ void Start(SkSL::Compiler* compiler);
*/
void End();
#endif // SK_SUPPORT_GPU && !defined(SKSL_STANDALONE)
/**
* Creates a variable declaration statement with an initial value.
*/
DSLStatement Declare(DSLVar& var, DSLExpression initialValue = DSLExpression());
/**
* do stmt; while (test);
*/
DSLStatement Do(DSLStatement stmt, DSLExpression test);
/**
* for (initializer; test; next) stmt;
*/
DSLStatement For(DSLStatement initializer, DSLExpression test, DSLExpression next,
DSLStatement stmt);
/**
* if (test) ifTrue; [else ifFalse;]
*/
DSLStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse = DSLStatement());
/**
* test ? ifTrue : ifFalse
*/
DSLExpression Ternary(DSLExpression test, DSLExpression ifTrue, DSLExpression ifFalse);
/**
* while (test) stmt;
*/
DSLStatement While(DSLExpression test, DSLStatement stmt);
/**
* Installs an ErrorHandler which will be notified of any errors that occur during DSL calls. If

View File

@ -58,7 +58,7 @@ DSLExpression::DSLExpression(const DSLVar& var)
DSLExpression::~DSLExpression() {
SkASSERTF(fExpression == nullptr,
"Expression destroyed without being incorporated into output tree");
"Expression destroyed without being incorporated into program");
}
std::unique_ptr<SkSL::Expression> DSLExpression::release() {

View File

@ -83,6 +83,7 @@ private:
std::unique_ptr<SkSL::Expression> fExpression;
friend class DSLCore;
friend class DSLVar;
friend class DSLWriter;
};

View File

@ -0,0 +1,44 @@
/*
* 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 "src/sksl/dsl/DSLStatement.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/dsl/DSLBlock.h"
#include "src/sksl/dsl/DSLExpression.h"
#include "src/sksl/dsl/priv/DSLWriter.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
namespace SkSL {
namespace dsl {
DSLStatement::DSLStatement(DSLBlock block)
: fStatement(block.release()) {}
DSLStatement::DSLStatement(DSLExpression expr) {
std::unique_ptr<SkSL::Expression> skslExpr = expr.release();
if (skslExpr) {
fStatement = std::make_unique<SkSL::ExpressionStatement>(std::move(skslExpr));
}
}
DSLStatement::DSLStatement(std::unique_ptr<SkSL::Expression> expr)
: fStatement(std::make_unique<SkSL::ExpressionStatement>(std::move(expr))) {}
DSLStatement::DSLStatement(std::unique_ptr<SkSL::Statement> stmt)
: fStatement(std::move(stmt)) {
if (!fStatement) {
SkASSERTF(DSLWriter::Compiler().errorCount(),
"statement is null, but no errors were reported");
DSLWriter::ReportError(DSLWriter::Compiler().errorText(/*showCount=*/false).c_str());
}
}
} // namespace dsl
} // namespace SkSL

View File

@ -0,0 +1,62 @@
/*
* 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_STATEMENT
#define SKSL_DSL_STATEMENT
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "src/sksl/ir/SkSLIRNode.h"
#include <memory>
class GrGLSLShaderBuilder;
namespace SkSL {
class Statement;
namespace dsl {
class DSLBlock;
class DSLExpression;
class DSLVar;
class DSLStatement {
public:
DSLStatement() {}
DSLStatement(DSLExpression expr);
DSLStatement(DSLBlock block);
DSLStatement(DSLStatement&&) = default;
~DSLStatement() {
SkASSERTF(!fStatement, "Statement destroyed without being incorporated into program");
}
std::unique_ptr<SkSL::Statement> release() {
return std::move(fStatement);
}
private:
DSLStatement(std::unique_ptr<SkSL::Statement> stmt);
DSLStatement(std::unique_ptr<SkSL::Expression> expr);
std::unique_ptr<SkSL::Statement> fStatement;
friend class DSLBlock;
friend class DSLCore;
};
} // namespace dsl
} // namespace SkSL
#endif

View File

@ -20,38 +20,31 @@ namespace SkSL {
namespace dsl {
DSLVar::DSLVar(const char* name)
: fName(name) {}
: fName(name) {
const SkSL::Symbol* result = (*DSLWriter::SymbolTable())[fName];
SkASSERTF(result, "could not find '%s' in symbol table", fName);
fVar = &result->as<SkSL::Variable>();
}
DSLVar::DSLVar(DSLType type, const char* name)
: fName(DSLWriter::Name(name)) {
fOwnedVar = std::make_unique<SkSL::Variable>(/*offset=*/-1,
DSLWriter::Modifiers(Modifiers()),
fName,
&type.skslType(),
/*builtin=*/false,
SkSL::Variable::Storage::kLocal);
fVar = fOwnedVar.get();
}
const SkSL::Variable* DSLVar::var() const {
if (!fVar) {
const SkSL::Symbol* result = (*DSLWriter::SymbolTable())[fName];
SkASSERTF(result, "could not find '%s' in symbol table", fName);
fVar = &result->as<SkSL::Variable>();
}
return fVar;
Modifiers modifiers;
DSLWriter::IRGenerator().checkVarDeclaration(/*offset=*/-1, modifiers, &type.skslType(),
Variable::Storage::kLocal);
fDeclaration = DSLWriter::IRGenerator().convertVarDeclaration(/*offset=*/-1,
modifiers,
&type.skslType(),
fName,
/*isArray=*/false,
/*arraySize=*/nullptr,
/*value=*/nullptr,
Variable::Storage::kLocal);
fVar = &fDeclaration->as<SkSL::VarDeclaration>().var();
}
DSLExpression DSLVar::operator=(DSLExpression expr) {
const SkSL::Variable* var = this->var();
return DSLExpression(std::make_unique<SkSL::BinaryExpression>(
/*offset=*/-1,
std::make_unique<SkSL::VariableReference>(/*offset=*/-1,
var,
SkSL::VariableReference::RefKind::kWrite),
SkSL::Token::Kind::TK_EQ,
expr.coerceAndRelease(var->type()),
&var->type()));
return DSLWriter::ConvertBinary(DSLExpression(*this).release(), SkSL::Token::Kind::TK_EQ,
expr.release());
}
} // namespace dsl

View File

@ -59,18 +59,19 @@ private:
*/
DSLVar(const char* name);
const SkSL::Variable* var() const;
const SkSL::Variable* var() const {
return fVar;
}
const char* name() const {
return fName;
}
// this object owns the var until it is added to a symboltable
std::unique_ptr<SkSL::Variable> fOwnedVar;
// mutable to allow us to cache lookups of system vars
mutable const SkSL::Variable* fVar = nullptr;
std::unique_ptr<SkSL::Statement> fDeclaration;
const SkSL::Variable* fVar = nullptr;
const char* fName;
friend class DSLCore;
friend class DSLExpression;
friend class DSLWriter;
};

View File

@ -15,6 +15,11 @@
namespace SkSL {
namespace dsl {
class DSLCore;
}
/**
* A single variable declaration statement. Multiple variables declared together are expanded to
* separate (sequential) statements. For instance, the SkSL 'int x = 2, y[3];' produces two
@ -86,6 +91,8 @@ private:
int fArraySize; // zero means "not an array", Type::kUnsizedArray means var[]
std::unique_ptr<Expression> fValue;
friend class dsl::DSLCore;
using INHERITED = Statement;
};

View File

@ -53,6 +53,29 @@ private:
skiatest::Reporter* fReporter;
};
static bool whitespace_insensitive_compare(const char* a, const char* b) {
for (;;) {
while (isspace(*a)) {
++a;
}
while (isspace(*b)) {
++b;
}
if (*a != *b) {
return false;
}
if (*a == 0) {
return true;
}
++a;
++b;
}
}
static bool whitespace_insensitive_compare(DSLStatement& stmt, const char* description) {
return whitespace_insensitive_compare(stmt.release()->description().c_str(), description);
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLStartup, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Expression e1 = 1;
@ -64,6 +87,15 @@ DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLStartup, r, ctxInfo) {
Var a(kInt, "a");
Expression e4 = a;
REPORTER_ASSERT(r, e4.release()->description() == "a");
REPORTER_ASSERT(r, whitespace_insensitive_compare("", ""));
REPORTER_ASSERT(r, !whitespace_insensitive_compare("", "a"));
REPORTER_ASSERT(r, !whitespace_insensitive_compare("a", ""));
REPORTER_ASSERT(r, whitespace_insensitive_compare("a", "a"));
REPORTER_ASSERT(r, whitespace_insensitive_compare("abc", "abc"));
REPORTER_ASSERT(r, whitespace_insensitive_compare("abc", " abc "));
REPORTER_ASSERT(r, whitespace_insensitive_compare("a b c ", "\n\n\nabc"));
REPORTER_ASSERT(r, !whitespace_insensitive_compare("a b c d", "\n\n\nabc"));
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLFloat, r, ctxInfo) {
@ -851,3 +883,105 @@ DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLDecrement, r, ctxInfo) {
((a + 1)--).release();
}
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLBlock, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Statement x = Block();
REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "{ }"));
Var a(kInt, "a"), b(kInt, "b");
Statement y = Block(Declare(a, 1), Declare(b, 2), a = b);
REPORTER_ASSERT(r, whitespace_insensitive_compare(y, "{ int a = 1; int b = 2; (a = b); }"));
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLDeclare, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Var a(kHalf4, "a"), b(kHalf4, "b");
Statement x = Declare(a);
REPORTER_ASSERT(r, x.release()->description() == "half4 a;");
Statement y = Declare(b, Half4(1));
REPORTER_ASSERT(r, y.release()->description() == "half4 b = half4(1.0);");
{
Var c(kHalf4, "c");
ExpectError error(r, "error: expected 'half4', but found 'int'\n");
Declare(c, 1).release();
}
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLDo, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Statement x = Do(Block(), true);
REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "do {} while (true);"));
Var a(kFloat, "a"), b(kFloat, "b");
Statement y = Do(Block(a++, --b), a != b);
REPORTER_ASSERT(r, whitespace_insensitive_compare(y, "do { a++; --b; } while ((a != b));"));
{
ExpectError error(r, "error: expected 'bool', but found 'int'\n");
Do(Block(), 7).release();
}
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLFor, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Statement x = For(Statement(), Expression(), Expression(), Block());
REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "for (;;) {}"));
Var i(kInt, "i");
Statement y = For(Declare(i, 0), i < 10, ++i, i += 5);
REPORTER_ASSERT(r, whitespace_insensitive_compare(y,
"for (int i = 0; (i < 10); ++i) (i += 5);"));
{
ExpectError error(r, "error: expected 'bool', but found 'int'\n");
For(i = 0, i + 10, ++i, i += 5).release();
}
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLIf, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Var a(kFloat, "a"), b(kFloat, "b");
Statement x = If(a > b, a -= b);
REPORTER_ASSERT(r, x.release()->description() == "if ((a > b)) (a -= b);");
Statement y = If(a > b, a -= b, b -= a);
REPORTER_ASSERT(r, y.release()->description() == "if ((a > b)) (a -= b); else (b -= a);");
{
ExpectError error(r, "error: expected 'bool', but found 'float'\n");
If(a + b, a -= b).release();
}
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLTernary, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Var a(kInt, "a");
Expression x = Ternary(a > 0, 1, -1);
REPORTER_ASSERT(r, x.release()->description() == "((a > 0) ? 1 : -1)");
{
ExpectError error(r, "error: expected 'bool', but found 'int'\n");
Ternary(a, 1, -1).release();
}
{
ExpectError error(r, "error: ternary operator result mismatch: 'float2', 'float3'\n");
Ternary(a > 0, Float2(1), Float3(1)).release();
}
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLWhile, r, ctxInfo) {
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
Statement x = While(true, Block());
REPORTER_ASSERT(r, whitespace_insensitive_compare(x, "for (; true;) {}"));
Var a(kFloat, "a"), b(kFloat, "b");
Statement y = While(a != b, Block(a++, --b));
REPORTER_ASSERT(r, whitespace_insensitive_compare(y, "for (; (a != b);) { a++; --b; }"));
{
ExpectError error(r, "error: expected 'bool', but found 'int'\n");
While(7, Block()).release();
}
}