diff options
author | ethannicholas <ethannicholas@google.com> | 2016-09-12 08:50:13 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-09-12 08:50:13 -0700 |
commit | 9b0fe3d125f237d9884732a48414fa85fc71b4e3 (patch) | |
tree | bf5470aacb44419cc3f5df2eded4f170be5a532a /src/sksl | |
parent | d99858ad46ca2c3b5c38061be8b006b25d24c6e0 (diff) |
Turned on SkSL->GLSL compiler
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2288033003
Review-Url: https://codereview.chromium.org/2288033003
Diffstat (limited to 'src/sksl')
-rw-r--r-- | src/sksl/README | 34 | ||||
-rw-r--r-- | src/sksl/SkSLCompiler.h | 2 | ||||
-rw-r--r-- | src/sksl/SkSLGLSLCodeGenerator.cpp | 147 | ||||
-rw-r--r-- | src/sksl/SkSLGLSLCodeGenerator.h | 15 | ||||
-rw-r--r-- | src/sksl/SkSLIRGenerator.cpp | 16 | ||||
-rw-r--r-- | src/sksl/SkSLParser.cpp | 10 | ||||
-rw-r--r-- | src/sksl/SkSLUtil.cpp | 35 | ||||
-rw-r--r-- | src/sksl/SkSLUtil.h | 14 | ||||
-rw-r--r-- | src/sksl/sksl_frag.include | 2 |
9 files changed, 242 insertions, 33 deletions
diff --git a/src/sksl/README b/src/sksl/README new file mode 100644 index 0000000000..da426923e3 --- /dev/null +++ b/src/sksl/README @@ -0,0 +1,34 @@ +Overview +======== + +SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's +internal shading language. SkSL is, at its heart, a single standardized version +of GLSL which avoids all of the various version and dialect differences found +in GLSL "in the wild", but it does bring a few of its own changes to the table. + +Skia uses the SkSL compiler to convert SkSL code to GLSL, GLSL ES, or SPIR-V +before handing it over to the graphics driver. + +Differences from GLSL +===================== + +SkSL is based on GLSL 4.5. For the most part, write SkSL exactly as you would +desktop GLSL, and the SkSL compiler will take care of version and dialect +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: + +* no #version or "precision" statement is required, and they will be ignored if + present +* the output color is sk_FragColor (do not declare it) +* lowp, mediump, and highp are always permitted (but will only be respected if + you run on a GLES device) +* you do not need to include ".0" to make a number a float (meaning that + "vec2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would have + to be expressed "vec2(x, y) * 4.0". There is no performance penalty for this, + as the number is converted to a float at compile time) +* some built-in functions and one or two rarely-used language features are not + yet supported (sorry!) + +SkSL is still under development, and is expected to diverge further from GLSL +over time. diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h index 9cd1eac3f9..3f76aec3a0 100644 --- a/src/sksl/SkSLCompiler.h +++ b/src/sksl/SkSLCompiler.h @@ -24,6 +24,8 @@ class IRGenerator; * file into an abstract syntax tree (a tree of ASTNodes), then performs semantic analysis to * produce a Program (a tree of IRNodes), then feeds the Program into a CodeGenerator to produce * compiled output. + * + * See the README for information about SkSL. */ class Compiler : public ErrorReporter { public: diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp index da0bcb903c..f989decdee 100644 --- a/src/sksl/SkSLGLSLCodeGenerator.cpp +++ b/src/sksl/SkSLGLSLCodeGenerator.cpp @@ -16,6 +16,8 @@ #include "ir/SkSLIndexExpression.h" #include "ir/SkSLVariableReference.h" +#define SK_FRAGCOLOR_BUILTIN 10001 + namespace SkSL { void GLSLCodeGenerator::write(const char* s) { @@ -66,7 +68,7 @@ void GLSLCodeGenerator::writeType(const Type& type) { this->writeLine("struct " + type.name() + " {"); fIndentation++; for (const auto& f : type.fields()) { - this->writeModifiers(f.fModifiers); + this->writeModifiers(f.fModifiers, false); // sizes (which must be static in structs) are part of the type name here this->writeType(*f.fType); this->writeLine(" " + f.fName + ";"); @@ -124,7 +126,40 @@ void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence paren } } +static bool is_abs(Expression& expr) { + if (expr.fKind != Expression::kFunctionCall_Kind) { + return false; + } + return ((FunctionCall&) expr).fFunction.fName == "abs"; +} + +// turns min(abs(x), y) into (abs(x) > (tmpVar = y) ? tmpVar : abs(x)) to avoid a Tegra3 compiler +// bug. +void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) { + ASSERT(!fCaps.fCanUseMinAndAbsTogether); + std::string varName = "minAbsHackVar" + to_string(fVarCount++); + this->fFunctionHeader += " " + otherExpr.fType.name() + " " + varName + ";\n"; + this->write("("); + this->writeExpression(absExpr, kTopLevel_Precedence); + this->write(" > (" + varName + " = "); + this->writeExpression(otherExpr, kRelational_Precedence); + this->write(") ? " + varName + " : "); + this->writeExpression(absExpr, kTernary_Precedence); + this->write(")"); +} + void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { + if (!fCaps.fCanUseMinAndAbsTogether && c.fFunction.fName == "min") { + ASSERT(c.fArguments.size() == 2); + if (is_abs(*c.fArguments[0])) { + this->writeMinAbsHack(*c.fArguments[0], *c.fArguments[1]); + return; + } + if (is_abs(*c.fArguments[1])) { + this->writeMinAbsHack(*c.fArguments[1], *c.fArguments[0]); + return; + } + } this->write(c.fFunction.fName + "("); const char* separator = ""; for (const auto& arg : c.fArguments) { @@ -147,7 +182,15 @@ void GLSLCodeGenerator::writeConstructor(const Constructor& c) { } void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) { - this->write(ref.fVariable.fName); + if (ref.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN) { + if (fCaps.fMustDeclareFragmentShaderOutput) { + this->write("sk_FragColor"); + } else { + this->write("gl_FragColor"); + } + } else { + this->write(ref.fVariable.fName); + } } void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) { @@ -284,28 +327,89 @@ void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) { for (const auto& param : f.fDeclaration.fParameters) { this->write(separator); separator = ", "; - this->writeModifiers(param->fModifiers); + this->writeModifiers(param->fModifiers, false); this->writeType(param->fType); this->write(" " + param->fName); } - this->write(") "); - this->writeBlock(*f.fBody); - this->writeLine(); + this->writeLine(") {"); + + fFunctionHeader = ""; + std::ostream* oldOut = fOut; + std::stringstream buffer; + fOut = &buffer; + fIndentation++; + for (const auto& s : f.fBody->fStatements) { + this->writeStatement(*s); + this->writeLine(); + } + fIndentation--; + this->writeLine("}"); + + fOut = oldOut; + this->write(fFunctionHeader); + this->write(buffer.str()); } -void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers) { - this->write(modifiers.description()); +void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers, + bool globalContext) { + if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) { + this->write("noperspective "); + } + if (modifiers.fFlags & Modifiers::kFlat_Flag) { + this->write("flat "); + } + std::string layout = modifiers.fLayout.description(); + if (layout.length()) { + this->write(layout + " "); + } + if ((modifiers.fFlags & Modifiers::kIn_Flag) && + (modifiers.fFlags & Modifiers::kOut_Flag)) { + this->write("inout "); + } else if (modifiers.fFlags & Modifiers::kIn_Flag) { + if (globalContext && fCaps.fVersion < 130) { + this->write(fProgramKind == Program::kVertex_Kind ? "attribute " + : "varying "); + } else { + this->write("in "); + } + } else if (modifiers.fFlags & Modifiers::kOut_Flag) { + if (globalContext && fCaps.fVersion < 130) { + this->write("varying "); + } else { + this->write("out "); + } + } + if (modifiers.fFlags & Modifiers::kUniform_Flag) { + this->write("uniform "); + } + if (modifiers.fFlags & Modifiers::kConst_Flag) { + this->write("const "); + } + if (fCaps.fUsesPrecisionModifiers) { + bool modifier = false; + if (modifiers.fFlags & Modifiers::kLowp_Flag) { + this->write("lowp "); + modifier = true; + } + if (modifiers.fFlags & Modifiers::kHighp_Flag) { + this->write("highp "); + modifier = true; + } + if (!modifier) { + this->write("mediump "); + } + } } void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) { if (intf.fVariable.fName == "gl_PerVertex") { return; } - this->writeModifiers(intf.fVariable.fModifiers); + this->writeModifiers(intf.fVariable.fModifiers, true); this->writeLine(intf.fVariable.fType.name() + " {"); fIndentation++; for (const auto& f : intf.fVariable.fType.fields()) { - this->writeModifiers(f.fModifiers); + this->writeModifiers(f.fModifiers, false); this->writeType(*f.fType); this->writeLine(" " + f.fName + ";"); } @@ -313,9 +417,9 @@ void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) { this->writeLine("};"); } -void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl) { +void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) { ASSERT(decl.fVars.size() > 0); - this->writeModifiers(decl.fVars[0].fVar->fModifiers); + this->writeModifiers(decl.fVars[0].fVar->fModifiers, global); this->writeType(decl.fBaseType); std::string separator = " "; for (const auto& var : decl.fVars) { @@ -349,7 +453,7 @@ void GLSLCodeGenerator::writeStatement(const Statement& s) { this->writeReturnStatement((ReturnStatement&) s); break; case Statement::kVarDeclarations_Kind: - this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration); + this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false); break; case Statement::kIf_Kind: this->writeIfStatement((IfStatement&) s); @@ -444,9 +548,12 @@ void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) { void GLSLCodeGenerator::generateCode(const Program& program, std::ostream& out) { ASSERT(fOut == nullptr); fOut = &out; + fProgramKind = program.fKind; this->write("#version " + to_string(fCaps.fVersion)); if (fCaps.fStandard == GLCaps::kGLES_Standard) { this->write(" es"); + } else if (fCaps.fIsCoreProfile) { + this->write(" core"); } this->writeLine(); for (const auto& e : program.fElements) { @@ -456,10 +563,16 @@ void GLSLCodeGenerator::generateCode(const Program& program, std::ostream& out) break; case ProgramElement::kVar_Kind: { VarDeclarations& decl = (VarDeclarations&) *e; - if (decl.fVars.size() > 0 && - decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin == -1) { - this->writeVarDeclarations(decl); - this->writeLine(); + if (decl.fVars.size() > 0) { + int builtin = decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin; + if (builtin == -1) { + // normal var + this->writeVarDeclarations(decl, true); + this->writeLine(); + } else if (builtin == SK_FRAGCOLOR_BUILTIN && + fCaps.fMustDeclareFragmentShaderOutput) { + this->writeLine("out vec4 sk_FragColor;"); + } } break; } diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h index 3534affccc..63fab05e08 100644 --- a/src/sksl/SkSLGLSLCodeGenerator.h +++ b/src/sksl/SkSLGLSLCodeGenerator.h @@ -50,6 +50,11 @@ struct GLCaps { kGL_Standard, kGLES_Standard } fStandard; + bool fIsCoreProfile; + bool fUsesPrecisionModifiers; + bool fMustDeclareFragmentShaderOutput; + // The Tegra3 compiler will sometimes never return if we have min(abs(x), y) + bool fCanUseMinAndAbsTogether; }; /** @@ -81,6 +86,7 @@ public: GLSLCodeGenerator(const Context* context, GLCaps caps) : fContext(*context) , fCaps(caps) + , fVarCount(0) , fIndentation(0) , fAtLineStart(true) {} @@ -111,11 +117,11 @@ private: void writeLayout(const Layout& layout); - void writeModifiers(const Modifiers& modifiers); + void writeModifiers(const Modifiers& modifiers, bool globalContext); void writeGlobalVars(const VarDeclaration& vs); - void writeVarDeclarations(const VarDeclarations& decl); + void writeVarDeclarations(const VarDeclarations& decl, bool global); void writeVariableReference(const VariableReference& ref); @@ -123,6 +129,8 @@ private: void writeIntrinsicCall(const FunctionCall& c); + void writeMinAbsHack(Expression& absExpr, Expression& otherExpr); + void writeFunctionCall(const FunctionCall& c); void writeConstructor(const Constructor& c); @@ -164,6 +172,9 @@ private: const Context& fContext; const GLCaps fCaps; std::ostream* fOut; + std::string fFunctionHeader; + Program::Kind fProgramKind; + int fVarCount; int fIndentation; bool fAtLineStart; // Keeps track of which struct types we have written. Given that we are unlikely to ever write diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp index c30cac17d7..5e136a4d2f 100644 --- a/src/sksl/SkSLIRGenerator.cpp +++ b/src/sksl/SkSLIRGenerator.cpp @@ -419,8 +419,9 @@ std::unique_ptr<FunctionDefinition> IRGenerator::convertFunction(const ASTFuncti for (size_t i = 0; i < parameters.size(); i++) { if (parameters[i]->fModifiers != other->fParameters[i]->fModifiers) { fErrors.error(f.fPosition, "modifiers on parameter " + - to_string(i + 1) + " differ between " + - "declaration and definition"); + to_string((uint64_t) i + 1) + + " differ between declaration and " + "definition"); return nullptr; } } @@ -616,8 +617,9 @@ std::unique_ptr<Expression> IRGenerator::coerce(std::unique_ptr<Expression> expr ASSERT(ctor); return this->call(Position(), std::move(ctor), std::move(args)); } - ABORT("cannot coerce %s to %s", expr->fType.description().c_str(), - type.description().c_str()); + std::vector<std::unique_ptr<Expression>> args; + args.push_back(std::move(expr)); + return std::unique_ptr<Expression>(new Constructor(Position(), type, std::move(args))); } static bool is_matrix_multiply(const Type& left, const Type& right) { @@ -832,12 +834,12 @@ std::unique_ptr<Expression> IRGenerator::call(Position position, std::vector<std::unique_ptr<Expression>> arguments) { if (function.fParameters.size() != arguments.size()) { std::string msg = "call to '" + function.fName + "' expected " + - to_string(function.fParameters.size()) + + to_string((uint64_t) function.fParameters.size()) + " argument"; if (function.fParameters.size() != 1) { msg += "s"; } - msg += ", but found " + to_string(arguments.size()); + msg += ", but found " + to_string((uint64_t) arguments.size()); fErrors.error(position, msg); return nullptr; } @@ -938,7 +940,7 @@ std::unique_ptr<Expression> IRGenerator::convertConstructor( if (args.size() != 1) { fErrors.error(position, "invalid arguments to '" + type.description() + "' constructor, (expected exactly 1 argument, but found " + - to_string(args.size()) + ")"); + to_string((uint64_t) args.size()) + ")"); } if (args[0]->fType == *fContext.fBool_Type) { std::unique_ptr<IntLiteral> zero(new IntLiteral(fContext, position, 0)); diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp index b240e4501e..f9e7498ee5 100644 --- a/src/sksl/SkSLParser.cpp +++ b/src/sksl/SkSLParser.cpp @@ -185,7 +185,8 @@ void Parser::precision() { this->expect(Token::SEMICOLON, "';'"); } -/* DIRECTIVE(#version) INT_LITERAL | DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */ +/* DIRECTIVE(#version) INT_LITERAL ("es" | "compatibility")? | + DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */ std::unique_ptr<ASTDeclaration> Parser::directive() { Token start; if (!this->expect(Token::DIRECTIVE, "a directive", &start)) { @@ -193,7 +194,12 @@ std::unique_ptr<ASTDeclaration> Parser::directive() { } if (start.fText == "#version") { this->expect(Token::INT_LITERAL, "a version number"); - // ignored for now + Token next = this->peek(); + if (next.fText == "es" || next.fText == "compatibility") { + this->nextToken(); + } + // version is ignored for now; it will eventually become an error when we stop pretending + // to be GLSL return nullptr; } else if (start.fText == "#extension") { Token name; diff --git a/src/sksl/SkSLUtil.cpp b/src/sksl/SkSLUtil.cpp index 327bffe4f1..aefad69367 100644 --- a/src/sksl/SkSLUtil.cpp +++ b/src/sksl/SkSLUtil.cpp @@ -9,6 +9,41 @@ namespace SkSL { +std::string to_string(double value) { + std::stringstream buffer; + buffer << std::setprecision(std::numeric_limits<double>::digits10) << value; + std::string result = buffer.str(); + if (result.find_last_of(".") == std::string::npos && + result.find_last_of("e") == std::string::npos) { + result += ".0"; + } + return result; +} + +std::string to_string(int32_t value) { + std::stringstream buffer; + buffer << value; + return buffer.str(); +} + +std::string to_string(uint32_t value) { + std::stringstream buffer; + buffer << value; + return buffer.str(); +} + +std::string to_string(int64_t value) { + std::stringstream buffer; + buffer << value; + return buffer.str(); +} + +std::string to_string(uint64_t value) { + std::stringstream buffer; + buffer << value; + return buffer.str(); +} + int stoi(std::string s) { return atoi(s.c_str()); } diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h index 33611cde02..efffaaec01 100644 --- a/src/sksl/SkSLUtil.h +++ b/src/sksl/SkSLUtil.h @@ -19,11 +19,15 @@ namespace SkSL { // our own definitions of certain std:: functions, because they are not always present on Android -template <typename T> std::string to_string(T value) { - std::stringstream buffer; - buffer << std::setprecision(std::numeric_limits<T>::digits10) << value; - return buffer.str(); -} +std::string to_string(double value); + +std::string to_string(int32_t value); + +std::string to_string(uint32_t value); + +std::string to_string(int64_t value); + +std::string to_string(uint64_t value); #if _MSC_VER #define NORETURN __declspec(noreturn) diff --git a/src/sksl/sksl_frag.include b/src/sksl/sksl_frag.include index b4047fe091..d6c438910f 100644 --- a/src/sksl/sksl_frag.include +++ b/src/sksl/sksl_frag.include @@ -4,4 +4,6 @@ STRINGIFY( layout(builtin=15) in vec4 gl_FragCoord; +layout(builtin=10001) vec4 sk_FragColor; + )
\ No newline at end of file |