sksl support for static ifs & switches
Bug: skia: Change-Id: Ic9e01a3a18efddb19bab26e92bfb473cad294fc1 Reviewed-on: https://skia-review.googlesource.com/16144 Commit-Queue: Ethan Nicholas <ethannicholas@google.com> Reviewed-by: Ben Wagner <benjaminwagner@google.com>
This commit is contained in:
parent
37c5a96bbd
commit
5ac13c2362
@ -217,6 +217,7 @@ tests_sources = [
|
||||
"$_tests/SkSLErrorTest.cpp",
|
||||
"$_tests/SkSLGLSLTest.cpp",
|
||||
"$_tests/SkSLMemoryLayoutTest.cpp",
|
||||
"$_tests/SkSLSPIRVTest.cpp",
|
||||
"$_tests/SortTest.cpp",
|
||||
"$_tests/SpecialImageTest.cpp",
|
||||
"$_tests/SpecialSurfaceTest.cpp",
|
||||
|
@ -18,6 +18,9 @@ differences (for instance, you always use "in" and "out", and skslc will handle
|
||||
translating them to "varying" and "attribute" as appropriate). Be aware of the
|
||||
following differences between SkSL and GLSL:
|
||||
|
||||
* "@if" and "@switch" are static versions of if and switch. They behave exactly
|
||||
the same as if and switch in all respects other than it being a compile-time
|
||||
error to use a non-constant expression as a test.
|
||||
* GLSL caps can be referenced via the syntax 'sk_Caps.<name>', e.g.
|
||||
sk_Caps.sampleVariablesSupport. The value will be a constant boolean or int,
|
||||
as appropriate. As SkSL supports constant folding and branch elimination, this
|
||||
|
@ -560,6 +560,8 @@ void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) {
|
||||
case Statement::kSwitch_Kind: {
|
||||
SwitchStatement& ss = (SwitchStatement&) **s;
|
||||
this->addExpression(cfg, &ss.fValue, true);
|
||||
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
||||
nullptr, s });
|
||||
BlockId start = cfg.fCurrent;
|
||||
BlockId switchExit = cfg.newIsolatedBlock();
|
||||
fLoopExits.push(switchExit);
|
||||
|
@ -764,12 +764,70 @@ void Compiler::simplifyExpression(DefinitionMap& definitions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns true if this statement could potentially execute a break at the current level (we ignore
|
||||
// nested loops and switches, since any breaks inside of them will merely break the loop / switch)
|
||||
static bool contains_break(Statement& s) {
|
||||
switch (s.fKind) {
|
||||
case Statement::kBlock_Kind:
|
||||
for (const auto& sub : ((Block&) s).fStatements) {
|
||||
if (contains_break(*sub)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case Statement::kBreak_Kind:
|
||||
return true;
|
||||
case Statement::kIf_Kind: {
|
||||
const IfStatement& i = (IfStatement&) s;
|
||||
return contains_break(*i.fIfTrue) || (i.fIfFalse && contains_break(*i.fIfFalse));
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a block containing all of the statements that will be run if the given case matches
|
||||
// (which, owing to the statements being owned by unique_ptrs, means the switch itself will be
|
||||
// broken by this call and must then be discarded).
|
||||
// Returns null (and leaves the switch unmodified) if no such simple reduction is possible, such as
|
||||
// when break statements appear inside conditionals.
|
||||
static std::unique_ptr<Statement> block_for_case(SwitchStatement* s, SwitchCase* c) {
|
||||
bool capturing = false;
|
||||
std::vector<std::unique_ptr<Statement>*> statementPtrs;
|
||||
for (const auto& current : s->fCases) {
|
||||
if (current.get() == c) {
|
||||
capturing = true;
|
||||
}
|
||||
if (capturing) {
|
||||
for (auto& stmt : current->fStatements) {
|
||||
if (stmt->fKind == Statement::kBreak_Kind) {
|
||||
capturing = false;
|
||||
break;
|
||||
}
|
||||
if (contains_break(*stmt)) {
|
||||
return nullptr;
|
||||
}
|
||||
statementPtrs.push_back(&stmt);
|
||||
}
|
||||
if (!capturing) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<std::unique_ptr<Statement>> statements;
|
||||
for (const auto& s : statementPtrs) {
|
||||
statements.push_back(std::move(*s));
|
||||
}
|
||||
return std::unique_ptr<Statement>(new Block(Position(), std::move(statements)));
|
||||
}
|
||||
|
||||
void Compiler::simplifyStatement(DefinitionMap& definitions,
|
||||
BasicBlock& b,
|
||||
std::vector<BasicBlock::Node>::iterator* iter,
|
||||
std::unordered_set<const Variable*>* undefinedVariables,
|
||||
bool* outUpdated,
|
||||
bool* outNeedsRescan) {
|
||||
BasicBlock& b,
|
||||
std::vector<BasicBlock::Node>::iterator* iter,
|
||||
std::unordered_set<const Variable*>* undefinedVariables,
|
||||
bool* outUpdated,
|
||||
bool* outNeedsRescan) {
|
||||
Statement* stmt = (*iter)->statement()->get();
|
||||
switch (stmt->fKind) {
|
||||
case Statement::kVarDeclarations_Kind: {
|
||||
@ -798,6 +856,22 @@ void Compiler::simplifyStatement(DefinitionMap& definitions,
|
||||
}
|
||||
case Statement::kIf_Kind: {
|
||||
IfStatement& i = (IfStatement&) *stmt;
|
||||
if (i.fTest->fKind == Expression::kBoolLiteral_Kind) {
|
||||
// constant if, collapse down to a single branch
|
||||
if (((BoolLiteral&) *i.fTest).fValue) {
|
||||
ASSERT(i.fIfTrue);
|
||||
(*iter)->setStatement(std::move(i.fIfTrue));
|
||||
} else {
|
||||
if (i.fIfFalse) {
|
||||
(*iter)->setStatement(std::move(i.fIfFalse));
|
||||
} else {
|
||||
(*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
|
||||
}
|
||||
}
|
||||
*outUpdated = true;
|
||||
*outNeedsRescan = true;
|
||||
break;
|
||||
}
|
||||
if (i.fIfFalse && i.fIfFalse->isEmpty()) {
|
||||
// else block doesn't do anything, remove it
|
||||
i.fIfFalse.reset();
|
||||
@ -820,6 +894,57 @@ void Compiler::simplifyStatement(DefinitionMap& definitions,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Statement::kSwitch_Kind: {
|
||||
SwitchStatement& s = (SwitchStatement&) *stmt;
|
||||
if (s.fValue->isConstant()) {
|
||||
// switch is constant, replace it with the case that matches
|
||||
bool found = false;
|
||||
SwitchCase* defaultCase = nullptr;
|
||||
for (const auto& c : s.fCases) {
|
||||
if (!c->fValue) {
|
||||
defaultCase = c.get();
|
||||
continue;
|
||||
}
|
||||
ASSERT(c->fValue->fKind == s.fValue->fKind);
|
||||
found = c->fValue->compareConstant(fContext, *s.fValue);
|
||||
if (found) {
|
||||
std::unique_ptr<Statement> newBlock = block_for_case(&s, c.get());
|
||||
if (newBlock) {
|
||||
(*iter)->setStatement(std::move(newBlock));
|
||||
break;
|
||||
} else {
|
||||
if (s.fIsStatic) {
|
||||
this->error(s.fPosition,
|
||||
"static switch contains non-static conditional break");
|
||||
s.fIsStatic = false;
|
||||
}
|
||||
return; // can't simplify
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// no matching case. use default if it exists, or kill the whole thing
|
||||
if (defaultCase) {
|
||||
std::unique_ptr<Statement> newBlock = block_for_case(&s, defaultCase);
|
||||
if (newBlock) {
|
||||
(*iter)->setStatement(std::move(newBlock));
|
||||
} else {
|
||||
if (s.fIsStatic) {
|
||||
this->error(s.fPosition,
|
||||
"static switch contains non-static conditional break");
|
||||
s.fIsStatic = false;
|
||||
}
|
||||
return; // can't simplify
|
||||
}
|
||||
} else {
|
||||
(*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
|
||||
}
|
||||
}
|
||||
*outUpdated = true;
|
||||
*outNeedsRescan = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Statement::kExpression_Kind: {
|
||||
ExpressionStatement& e = (ExpressionStatement&) *stmt;
|
||||
ASSERT((*iter)->statement()->get() == &e);
|
||||
@ -892,6 +1017,31 @@ void Compiler::scanCFG(FunctionDefinition& f) {
|
||||
} while (updated);
|
||||
ASSERT(!needsRescan);
|
||||
|
||||
// verify static ifs & switches
|
||||
for (BasicBlock& b : cfg.fBlocks) {
|
||||
DefinitionMap definitions = b.fBefore;
|
||||
|
||||
for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() && !needsRescan; ++iter) {
|
||||
if (iter->fKind == BasicBlock::Node::kStatement_Kind) {
|
||||
const Statement& s = **iter->statement();
|
||||
switch (s.fKind) {
|
||||
case Statement::kIf_Kind:
|
||||
if (((const IfStatement&) s).fIsStatic) {
|
||||
this->error(s.fPosition, "static if has non-static test");
|
||||
}
|
||||
break;
|
||||
case Statement::kSwitch_Kind:
|
||||
if (((const SwitchStatement&) s).fIsStatic) {
|
||||
this->error(s.fPosition, "static switch has non-static test");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for missing return
|
||||
if (f.fDeclaration.fReturnType != *fContext.fVoid_Type) {
|
||||
if (cfg.fBlocks[cfg.fExit].fEntrances.size()) {
|
||||
|
@ -311,7 +311,7 @@ std::unique_ptr<Statement> IRGenerator::convertIf(const ASTIfStatement& s) {
|
||||
fSymbolTable));
|
||||
}
|
||||
}
|
||||
return std::unique_ptr<Statement>(new IfStatement(s.fPosition, std::move(test),
|
||||
return std::unique_ptr<Statement>(new IfStatement(s.fPosition, s.fIsStatic, std::move(test),
|
||||
std::move(ifTrue), std::move(ifFalse)));
|
||||
}
|
||||
|
||||
@ -429,8 +429,8 @@ std::unique_ptr<Statement> IRGenerator::convertSwitch(const ASTSwitchStatement&
|
||||
cases.emplace_back(new SwitchCase(c->fPosition, std::move(caseValue),
|
||||
std::move(statements)));
|
||||
}
|
||||
return std::unique_ptr<Statement>(new SwitchStatement(s.fPosition, std::move(value),
|
||||
std::move(cases)));
|
||||
return std::unique_ptr<Statement>(new SwitchStatement(s.fPosition, s.fIsStatic,
|
||||
std::move(value), std::move(cases)));
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> IRGenerator::convertExpressionStatement(
|
||||
|
@ -167,7 +167,11 @@ Token Parser::nextToken() {
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return Token(Position(skslget_lineno(fScanner), -1), (Token::Kind) token, text);
|
||||
Position p = Position(skslget_lineno(fScanner), -1);
|
||||
if (token == Token::INVALID_TOKEN) {
|
||||
this->error(p, "invalid token: '" + text + "'");
|
||||
}
|
||||
return Token(p, (Token::Kind) token, text);
|
||||
}
|
||||
|
||||
void Parser::pushback(Token t) {
|
||||
@ -746,7 +750,8 @@ Modifiers Parser::modifiersWithDefaults(int defaultFlags) {
|
||||
std::unique_ptr<ASTStatement> Parser::statement() {
|
||||
Token start = this->peek();
|
||||
switch (start.fKind) {
|
||||
case Token::IF:
|
||||
case Token::IF: // fall through
|
||||
case Token::STATIC_IF:
|
||||
return this->ifStatement();
|
||||
case Token::FOR:
|
||||
return this->forStatement();
|
||||
@ -754,7 +759,8 @@ std::unique_ptr<ASTStatement> Parser::statement() {
|
||||
return this->doStatement();
|
||||
case Token::WHILE:
|
||||
return this->whileStatement();
|
||||
case Token::SWITCH:
|
||||
case Token::SWITCH: // fall through
|
||||
case Token::STATIC_SWITCH:
|
||||
return this->switchStatement();
|
||||
case Token::RETURN:
|
||||
return this->returnStatement();
|
||||
@ -872,7 +878,8 @@ std::unique_ptr<ASTDeclaration> Parser::interfaceBlock(Modifiers mods) {
|
||||
/* IF LPAREN expression RPAREN statement (ELSE statement)? */
|
||||
std::unique_ptr<ASTIfStatement> Parser::ifStatement() {
|
||||
Token start;
|
||||
if (!this->expect(Token::IF, "'if'", &start)) {
|
||||
bool isStatic = this->checkNext(Token::STATIC_IF, &start);
|
||||
if (!isStatic && !this->expect(Token::IF, "'if'", &start)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!this->expect(Token::LPAREN, "'('")) {
|
||||
@ -896,7 +903,9 @@ std::unique_ptr<ASTIfStatement> Parser::ifStatement() {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return std::unique_ptr<ASTIfStatement>(new ASTIfStatement(start.fPosition, std::move(test),
|
||||
return std::unique_ptr<ASTIfStatement>(new ASTIfStatement(start.fPosition,
|
||||
isStatic,
|
||||
std::move(test),
|
||||
std::move(ifTrue),
|
||||
std::move(ifFalse)));
|
||||
}
|
||||
@ -986,7 +995,8 @@ std::unique_ptr<ASTSwitchCase> Parser::switchCase() {
|
||||
/* SWITCH LPAREN expression RPAREN LBRACE switchCase* (DEFAULT COLON statement*)? RBRACE */
|
||||
std::unique_ptr<ASTStatement> Parser::switchStatement() {
|
||||
Token start;
|
||||
if (!this->expect(Token::SWITCH, "'switch'", &start)) {
|
||||
bool isStatic = this->checkNext(Token::STATIC_SWITCH, &start);
|
||||
if (!isStatic && !this->expect(Token::SWITCH, "'switch'", &start)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!this->expect(Token::LPAREN, "'('")) {
|
||||
@ -1033,6 +1043,7 @@ std::unique_ptr<ASTStatement> Parser::switchStatement() {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<ASTStatement>(new ASTSwitchStatement(start.fPosition,
|
||||
isStatic,
|
||||
std::move(value),
|
||||
std::move(cases)));
|
||||
}
|
||||
|
@ -78,11 +78,13 @@ struct Token {
|
||||
LOGICALANDEQ,
|
||||
SEMICOLON,
|
||||
IF,
|
||||
STATIC_IF,
|
||||
ELSE,
|
||||
FOR,
|
||||
WHILE,
|
||||
DO,
|
||||
SWITCH,
|
||||
STATIC_SWITCH,
|
||||
CASE,
|
||||
DEFAULT,
|
||||
RETURN,
|
||||
|
@ -16,15 +16,20 @@ namespace SkSL {
|
||||
* An 'if' statement.
|
||||
*/
|
||||
struct ASTIfStatement : public ASTStatement {
|
||||
ASTIfStatement(Position position, std::unique_ptr<ASTExpression> test,
|
||||
ASTIfStatement(Position position, bool isStatic, std::unique_ptr<ASTExpression> test,
|
||||
std::unique_ptr<ASTStatement> ifTrue, std::unique_ptr<ASTStatement> ifFalse)
|
||||
: INHERITED(position, kIf_Kind)
|
||||
, fIsStatic(isStatic)
|
||||
, fTest(std::move(test))
|
||||
, fIfTrue(std::move(ifTrue))
|
||||
, fIfFalse(std::move(ifFalse)) {}
|
||||
|
||||
String description() const override {
|
||||
String result("if (");
|
||||
String result;
|
||||
if (fIsStatic) {
|
||||
result += "@";
|
||||
}
|
||||
result += "if (";
|
||||
result += fTest->description();
|
||||
result += ") ";
|
||||
result += fIfTrue->description();
|
||||
@ -35,6 +40,7 @@ struct ASTIfStatement : public ASTStatement {
|
||||
return result;
|
||||
}
|
||||
|
||||
const bool fIsStatic;
|
||||
const std::unique_ptr<ASTExpression> fTest;
|
||||
const std::unique_ptr<ASTStatement> fIfTrue;
|
||||
const std::unique_ptr<ASTStatement> fIfFalse;
|
||||
|
@ -17,14 +17,19 @@ namespace SkSL {
|
||||
* A 'switch' statement.
|
||||
*/
|
||||
struct ASTSwitchStatement : public ASTStatement {
|
||||
ASTSwitchStatement(Position position, std::unique_ptr<ASTExpression> value,
|
||||
ASTSwitchStatement(Position position, bool isStatic, std::unique_ptr<ASTExpression> value,
|
||||
std::vector<std::unique_ptr<ASTSwitchCase>> cases)
|
||||
: INHERITED(position, kSwitch_Kind)
|
||||
, fIsStatic(isStatic)
|
||||
, fValue(std::move(value))
|
||||
, fCases(std::move(cases)) {}
|
||||
|
||||
String description() const override {
|
||||
String result = String::printf("switch (%s) {\n", + fValue->description().c_str());
|
||||
String result;
|
||||
if (fIsStatic) {
|
||||
result += "@";
|
||||
}
|
||||
result += String::printf("switch (%s) {\n", fValue->description().c_str());
|
||||
for (const auto& c : fCases) {
|
||||
result += c->description();
|
||||
}
|
||||
@ -32,6 +37,7 @@ struct ASTSwitchStatement : public ASTStatement {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fIsStatic;
|
||||
const std::unique_ptr<ASTExpression> fValue;
|
||||
const std::vector<std::unique_ptr<ASTSwitchCase>> fCases;
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace SkSL {
|
||||
*/
|
||||
struct Block : public Statement {
|
||||
Block(Position position, std::vector<std::unique_ptr<Statement>> statements,
|
||||
const std::shared_ptr<SymbolTable> symbols)
|
||||
const std::shared_ptr<SymbolTable> symbols = nullptr)
|
||||
: INHERITED(position, kBlock_Kind)
|
||||
, fSymbols(std::move(symbols))
|
||||
, fStatements(std::move(statements)) {}
|
||||
|
@ -17,21 +17,27 @@ namespace SkSL {
|
||||
* An 'if' statement.
|
||||
*/
|
||||
struct IfStatement : public Statement {
|
||||
IfStatement(Position position, std::unique_ptr<Expression> test,
|
||||
IfStatement(Position position, bool isStatic, std::unique_ptr<Expression> test,
|
||||
std::unique_ptr<Statement> ifTrue, std::unique_ptr<Statement> ifFalse)
|
||||
: INHERITED(position, kIf_Kind)
|
||||
, fIsStatic(isStatic)
|
||||
, fTest(std::move(test))
|
||||
, fIfTrue(std::move(ifTrue))
|
||||
, fIfFalse(std::move(ifFalse)) {}
|
||||
|
||||
String description() const override {
|
||||
String result = "if (" + fTest->description() + ") " + fIfTrue->description();
|
||||
String result;
|
||||
if (fIsStatic) {
|
||||
result += "@";
|
||||
}
|
||||
result += "if (" + fTest->description() + ") " + fIfTrue->description();
|
||||
if (fIfFalse) {
|
||||
result += " else " + fIfFalse->description();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fIsStatic;
|
||||
std::unique_ptr<Expression> fTest;
|
||||
std::unique_ptr<Statement> fIfTrue;
|
||||
// may be null
|
||||
|
@ -17,14 +17,19 @@ namespace SkSL {
|
||||
* A 'switch' statement.
|
||||
*/
|
||||
struct SwitchStatement : public Statement {
|
||||
SwitchStatement(Position position, std::unique_ptr<Expression> value,
|
||||
SwitchStatement(Position position, bool isStatic, std::unique_ptr<Expression> value,
|
||||
std::vector<std::unique_ptr<SwitchCase>> cases)
|
||||
: INHERITED(position, kSwitch_Kind)
|
||||
, fIsStatic(isStatic)
|
||||
, fValue(std::move(value))
|
||||
, fCases(std::move(cases)) {}
|
||||
|
||||
String description() const override {
|
||||
String result = String::printf("switch (%s) {\n", + fValue->description().c_str());
|
||||
String result;
|
||||
if (fIsStatic) {
|
||||
result += "@";
|
||||
}
|
||||
result += String::printf("switch (%s) {\n", fValue->description().c_str());
|
||||
for (const auto& c : fCases) {
|
||||
result += c->description();
|
||||
}
|
||||
@ -32,6 +37,7 @@ struct SwitchStatement : public Statement {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fIsStatic;
|
||||
std::unique_ptr<Expression> fValue;
|
||||
std::vector<std::unique_ptr<SwitchCase>> fCases;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -45,6 +45,8 @@ false { return SkSL::Token::FALSE_LITERAL; }
|
||||
|
||||
if { return SkSL::Token::IF; }
|
||||
|
||||
@if { return SkSL::Token::STATIC_IF; }
|
||||
|
||||
else { return SkSL::Token::ELSE; }
|
||||
|
||||
for { return SkSL::Token::FOR; }
|
||||
@ -55,6 +57,8 @@ do { return SkSL::Token::DO; }
|
||||
|
||||
switch { return SkSL::Token::SWITCH; }
|
||||
|
||||
@switch { return SkSL::Token::STATIC_SWITCH; }
|
||||
|
||||
case { return SkSL::Token::CASE; }
|
||||
|
||||
default { return SkSL::Token::DEFAULT; }
|
||||
|
@ -16,12 +16,7 @@ static void test_failure(skiatest::Reporter* r, const char* src, const char* err
|
||||
SkSL::Program::Settings settings;
|
||||
sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
|
||||
settings.fCaps = caps.get();
|
||||
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
|
||||
SkString(src), settings);
|
||||
if (program) {
|
||||
SkSL::String ignored;
|
||||
compiler.toSPIRV(*program, &ignored);
|
||||
}
|
||||
compiler.convertProgram(SkSL::Program::kFragment_Kind, SkString(src), settings);
|
||||
SkSL::String skError(error);
|
||||
if (compiler.errorText() != skError) {
|
||||
SkDebugf("SKSL ERROR:\n source: %s\n expected: %s received: %s", src, error,
|
||||
@ -38,8 +33,6 @@ static void test_success(skiatest::Reporter* r, const char* src) {
|
||||
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
|
||||
SkString(src), settings);
|
||||
REPORTER_ASSERT(r, program);
|
||||
SkSL::String ignored;
|
||||
REPORTER_ASSERT(r, compiler.toSPIRV(*program, &ignored));
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLUndefinedSymbol, r) {
|
||||
@ -405,15 +398,6 @@ DEF_TEST(SkSLBadCap, r) {
|
||||
"error: 1: unknown capability flag 'bugFreeDriver'\n1 error\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLBadOffset, r) {
|
||||
test_failure(r,
|
||||
"struct Bad { layout (offset = 5) int x; } bad; void main() { bad.x = 5; }",
|
||||
"error: 1: offset of field 'x' must be a multiple of 4\n1 error\n");
|
||||
test_failure(r,
|
||||
"struct Bad { int x; layout (offset = 0) int y; } bad; void main() { bad.x = 5; }",
|
||||
"error: 1: offset of field 'y' must be at least 4\n1 error\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLDivByZero, r) {
|
||||
test_failure(r,
|
||||
"int x = 1 / 0;",
|
||||
@ -466,4 +450,43 @@ DEF_TEST(SkSLFieldAfterRuntimeArray, r) {
|
||||
"array\n1 error\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLStaticIf, r) {
|
||||
test_success(r,
|
||||
"void main() { float x = 5; float y = 10;"
|
||||
"@if (x < y) { sk_FragColor = vec4(1); } }");
|
||||
test_failure(r,
|
||||
"void main() { float x = sqrt(25); float y = 10;"
|
||||
"@if (x < y) { sk_FragColor = vec4(1); } }",
|
||||
"error: 1: static if has non-static test\n1 error\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLStaticSwitch, r) {
|
||||
test_success(r,
|
||||
"void main() {"
|
||||
"int x = 1;"
|
||||
"@switch (x) {"
|
||||
"case 1: sk_FragColor = vec4(1); break;"
|
||||
"default: sk_FragColor = vec4(0);"
|
||||
"}"
|
||||
"}");
|
||||
test_failure(r,
|
||||
"void main() {"
|
||||
"int x = int(sqrt(1));"
|
||||
"@switch (x) {"
|
||||
"case 1: sk_FragColor = vec4(1); break;"
|
||||
"default: sk_FragColor = vec4(0);"
|
||||
"}"
|
||||
"}",
|
||||
"error: 1: static switch has non-static test\n1 error\n");
|
||||
test_failure(r,
|
||||
"void main() {"
|
||||
"int x = 1;"
|
||||
"@switch (x) {"
|
||||
"case 1: sk_FragColor = vec4(1); if (sqrt(0) < sqrt(1)) break;"
|
||||
"default: sk_FragColor = vec4(0);"
|
||||
"}"
|
||||
"}",
|
||||
"error: 1: static switch contains non-static conditional break\n1 error\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -831,7 +831,7 @@ DEF_TEST(SkSLMatFolding, r) {
|
||||
"}\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLStaticIf, r) {
|
||||
DEF_TEST(SkSLConstantIf, r) {
|
||||
test(r,
|
||||
"void main() {"
|
||||
"int x;"
|
||||
@ -1067,10 +1067,11 @@ DEF_TEST(SkSLGeometry, r) {
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLSwitch, r) {
|
||||
// basic "does a switch even work" test
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x;"
|
||||
" switch (1) {"
|
||||
" switch (int(sqrt(1))) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" break;"
|
||||
@ -1087,7 +1088,7 @@ DEF_TEST(SkSLSwitch, r) {
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" float x;\n"
|
||||
" switch (1) {\n"
|
||||
" switch (int(sqrt(1.0))) {\n"
|
||||
" case 0:\n"
|
||||
" x = 0.0;\n"
|
||||
" break;\n"
|
||||
@ -1099,10 +1100,11 @@ DEF_TEST(SkSLSwitch, r) {
|
||||
" }\n"
|
||||
" sk_FragColor = vec4(x);\n"
|
||||
"}\n");
|
||||
// dead code inside of switch
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x;"
|
||||
" switch (2) {"
|
||||
" switch (int(sqrt(2))) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" case 1:"
|
||||
@ -1116,7 +1118,7 @@ DEF_TEST(SkSLSwitch, r) {
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" switch (2) {\n"
|
||||
" switch (int(sqrt(2.0))) {\n"
|
||||
" case 0:\n"
|
||||
" ;\n"
|
||||
" case 1:\n"
|
||||
@ -1126,10 +1128,11 @@ DEF_TEST(SkSLSwitch, r) {
|
||||
" }\n"
|
||||
" sk_FragColor = vec4(2.0);\n"
|
||||
"}\n");
|
||||
// non-static test w/ fallthrough
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x = 0.0;"
|
||||
" switch (3) {"
|
||||
" switch (int(sqrt(3))) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" case 1:"
|
||||
@ -1142,7 +1145,7 @@ DEF_TEST(SkSLSwitch, r) {
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" float x = 0.0;\n"
|
||||
" switch (3) {\n"
|
||||
" switch (int(sqrt(3.0))) {\n"
|
||||
" case 0:\n"
|
||||
" x = 0.0;\n"
|
||||
" case 1:\n"
|
||||
@ -1150,6 +1153,107 @@ DEF_TEST(SkSLSwitch, r) {
|
||||
" }\n"
|
||||
" sk_FragColor = vec4(x);\n"
|
||||
"}\n");
|
||||
// static test w/ fallthrough
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x = 0.0;"
|
||||
" switch (0) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" case 1:"
|
||||
" x = 1.0;"
|
||||
" }"
|
||||
" sk_FragColor = vec4(x);"
|
||||
"}",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" sk_FragColor = vec4(1.0);\n"
|
||||
"}\n");
|
||||
// static test w/ fallthrough, different entry point
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x = 0.0;"
|
||||
" switch (1) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" case 1:"
|
||||
" x = 1.0;"
|
||||
" }"
|
||||
" sk_FragColor = vec4(x);"
|
||||
"}",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" sk_FragColor = vec4(1.0);\n"
|
||||
"}\n");
|
||||
// static test w/ break
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x = 0.0;"
|
||||
" switch (0) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" break;"
|
||||
" case 1:"
|
||||
" x = 1.0;"
|
||||
" }"
|
||||
" sk_FragColor = vec4(x);"
|
||||
"}",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" sk_FragColor = vec4(0.0);\n"
|
||||
"}\n");
|
||||
// static test w/ static conditional break
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x = 0.0;"
|
||||
" switch (0) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" if (x < 1) break;"
|
||||
" case 1:"
|
||||
" x = 1.0;"
|
||||
" }"
|
||||
" sk_FragColor = vec4(x);"
|
||||
"}",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" sk_FragColor = vec4(0.0);\n"
|
||||
"}\n");
|
||||
// static test w/ non-static conditional break
|
||||
test(r,
|
||||
"void main() {"
|
||||
" float x = 0.0;"
|
||||
" switch (0) {"
|
||||
" case 0:"
|
||||
" x = 0.0;"
|
||||
" if (x < sqrt(1)) break;"
|
||||
" case 1:"
|
||||
" x = 1.0;"
|
||||
" }"
|
||||
" sk_FragColor = vec4(x);"
|
||||
"}",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" float x = 0.0;\n"
|
||||
" switch (0) {\n"
|
||||
" case 0:\n"
|
||||
" x = 0.0;\n"
|
||||
" if (0.0 < sqrt(1.0)) break;\n"
|
||||
" case 1:\n"
|
||||
" x = 1.0;\n"
|
||||
" }\n"
|
||||
" sk_FragColor = vec4(x);\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLRectangleTexture, r) {
|
||||
|
42
tests/SkSLSPIRVTest.cpp
Normal file
42
tests/SkSLSPIRVTest.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkSLCompiler.h"
|
||||
|
||||
#include "Test.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
|
||||
SkSL::Compiler compiler;
|
||||
SkSL::Program::Settings settings;
|
||||
sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
|
||||
settings.fCaps = caps.get();
|
||||
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
|
||||
SkString(src), settings);
|
||||
if (program) {
|
||||
SkSL::String ignored;
|
||||
compiler.toSPIRV(*program, &ignored);
|
||||
}
|
||||
SkSL::String skError(error);
|
||||
if (compiler.errorText() != skError) {
|
||||
SkDebugf("SKSL ERROR:\n source: %s\n expected: %s received: %s", src, error,
|
||||
compiler.errorText().c_str());
|
||||
}
|
||||
REPORTER_ASSERT(r, compiler.errorText() == skError);
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLBadOffset, r) {
|
||||
test_failure(r,
|
||||
"struct Bad { layout (offset = 5) int x; } bad; void main() { bad.x = 5; }",
|
||||
"error: 1: offset of field 'x' must be a multiple of 4\n1 error\n");
|
||||
test_failure(r,
|
||||
"struct Bad { int x; layout (offset = 0) int y; } bad; void main() { bad.x = 5; }",
|
||||
"error: 1: offset of field 'y' must be at least 4\n1 error\n");
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user