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:
Ethan Nicholas 2017-05-10 15:06:17 -04:00 committed by Skia Commit-Bot
parent 37c5a96bbd
commit 5ac13c2362
17 changed files with 701 additions and 322 deletions

View File

@ -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",

View File

@ -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

View File

@ -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);

View File

@ -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()) {

View File

@ -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(

View File

@ -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)));
}

View File

@ -78,11 +78,13 @@ struct Token {
LOGICALANDEQ,
SEMICOLON,
IF,
STATIC_IF,
ELSE,
FOR,
WHILE,
DO,
SWITCH,
STATIC_SWITCH,
CASE,
DEFAULT,
RETURN,

View File

@ -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;

View File

@ -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;

View File

@ -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)) {}

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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

View File

@ -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
View 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