From 9b0fe3d125f237d9884732a48414fa85fc71b4e3 Mon Sep 17 00:00:00 2001 From: ethannicholas Date: Mon, 12 Sep 2016 08:50:13 -0700 Subject: [PATCH] Turned on SkSL->GLSL compiler GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2288033003 Review-Url: https://codereview.chromium.org/2288033003 --- gyp/sksl.gyp | 2 +- src/gpu/gl/GrGLContext.cpp | 12 ++ src/gpu/gl/GrGLContext.h | 17 +- src/gpu/gl/GrGLGpu.cpp | 44 +---- .../gl/builders/GrGLShaderStringBuilder.cpp | 95 +++++++++- src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp | 2 +- src/gpu/glsl/GrGLSLShaderVar.h | 10 +- src/sksl/README | 34 ++++ src/sksl/SkSLCompiler.h | 2 + src/sksl/SkSLGLSLCodeGenerator.cpp | 147 +++++++++++++-- src/sksl/SkSLGLSLCodeGenerator.h | 15 +- src/sksl/SkSLIRGenerator.cpp | 16 +- src/sksl/SkSLParser.cpp | 10 +- src/sksl/SkSLUtil.cpp | 35 ++++ src/sksl/SkSLUtil.h | 14 +- src/sksl/sksl_frag.include | 2 + tests/SkSLGLSLTest.cpp | 172 +++++++++++++----- 17 files changed, 503 insertions(+), 126 deletions(-) create mode 100644 src/sksl/README diff --git a/gyp/sksl.gyp b/gyp/sksl.gyp index 609ace9ac8..f45b702a7b 100644 --- a/gyp/sksl.gyp +++ b/gyp/sksl.gyp @@ -18,7 +18,7 @@ ], 'all_dependent_settings': { 'include_dirs': [ - '../src/sksl', + '<(skia_src_path)/sksl', ], }, }, diff --git a/src/gpu/gl/GrGLContext.cpp b/src/gpu/gl/GrGLContext.cpp index 9e70b472c5..93f14cfc67 100644 --- a/src/gpu/gl/GrGLContext.cpp +++ b/src/gpu/gl/GrGLContext.cpp @@ -7,6 +7,7 @@ #include "GrGLContext.h" #include "GrGLGLSL.h" +#include "SkSLCompiler.h" //////////////////////////////////////////////////////////////////////////////// @@ -63,6 +64,17 @@ GrGLContext* GrGLContext::Create(const GrGLInterface* interface, const GrContext return new GrGLContext(args); } +GrGLContext::~GrGLContext() { + delete fCompiler; +} + +SkSL::Compiler* GrGLContext::compiler() { + if (!fCompiler) { + fCompiler = new SkSL::Compiler(); + } + return fCompiler; +} + GrGLContextInfo::GrGLContextInfo(const ConstructorArgs& args) { fInterface.reset(SkRef(args.fInterface)); fGLVersion = args.fGLVersion; diff --git a/src/gpu/gl/GrGLContext.h b/src/gpu/gl/GrGLContext.h index 6016f6859a..8a56535887 100644 --- a/src/gpu/gl/GrGLContext.h +++ b/src/gpu/gl/GrGLContext.h @@ -15,6 +15,9 @@ #include "GrGLUtil.h" struct GrContextOptions; +namespace SkSL { + class Compiler; +} /** * Encapsulates information about an OpenGL context including the OpenGL @@ -39,6 +42,8 @@ public: const GrGLExtensions& extensions() const { return fInterface->fExtensions; } + virtual ~GrGLContextInfo() {} + protected: struct ConstructorArgs { const GrGLInterface* fInterface; @@ -64,7 +69,7 @@ protected: }; /** - * Extension of GrGLContextInfo that also provides access to GrGLInterface. + * Extension of GrGLContextInfo that also provides access to GrGLInterface and SkSL::Compiler. */ class GrGLContext : public GrGLContextInfo { public: @@ -76,8 +81,16 @@ public: const GrGLInterface* interface() const { return fInterface; } + SkSL::Compiler* compiler(); + + ~GrGLContext() override; + private: - GrGLContext(const ConstructorArgs& args) : INHERITED(args) {} + GrGLContext(const ConstructorArgs& args) + : INHERITED(args) + , fCompiler(nullptr) {} + + SkSL::Compiler* fCompiler; typedef GrGLContextInfo INHERITED; }; diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index e002d5101b..e37f8430fb 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -3775,20 +3775,11 @@ bool GrGLGpu::createCopyProgram(int progIdx) { fshaderTxt.append(";"); uTexture.appendDecl(glslCaps, &fshaderTxt); fshaderTxt.append(";"); - const char* fsOutName; - if (glslCaps->mustDeclareFragmentShaderOutput()) { - oFragColor.appendDecl(glslCaps, &fshaderTxt); - fshaderTxt.append(";"); - fsOutName = oFragColor.c_str(); - } else { - fsOutName = "gl_FragColor"; - } fshaderTxt.appendf( "// Copy Program FS\n" "void main() {" - " %s = %s(u_texture, v_texCoord);" + " sk_FragColor = %s(u_texture, v_texCoord);" "}", - fsOutName, GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kSamplerTypes[progIdx], this->glslGeneration()) ); @@ -3921,14 +3912,6 @@ bool GrGLGpu::createMipmapProgram(int progIdx) { } uTexture.appendDecl(glslCaps, &fshaderTxt); fshaderTxt.append(";"); - const char* fsOutName; - if (glslCaps->mustDeclareFragmentShaderOutput()) { - oFragColor.appendDecl(glslCaps, &fshaderTxt); - fshaderTxt.append(";"); - fsOutName = oFragColor.c_str(); - } else { - fsOutName = "gl_FragColor"; - } const char* sampleFunction = GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kTexture2DSampler_GrSLType, this->glslGeneration()); @@ -3939,19 +3922,19 @@ bool GrGLGpu::createMipmapProgram(int progIdx) { if (oddWidth && oddHeight) { fshaderTxt.appendf( - " %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + " - " %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;", - fsOutName, sampleFunction, sampleFunction, sampleFunction, sampleFunction + " sk_FragColor = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + " + " %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;", + sampleFunction, sampleFunction, sampleFunction, sampleFunction ); } else if (oddWidth || oddHeight) { fshaderTxt.appendf( - " %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;", - fsOutName, sampleFunction, sampleFunction + " sk_FragColor = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;", + sampleFunction, sampleFunction ); } else { fshaderTxt.appendf( - " %s = %s(u_texture, v_texCoord0);", - fsOutName, sampleFunction + " sk_FragColor = %s(u_texture, v_texCoord0);", + sampleFunction ); } @@ -4038,20 +4021,11 @@ bool GrGLGpu::createWireRectProgram() { &fshaderTxt); uColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt); fshaderTxt.append(";"); - const char* fsOutName; - if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) { - oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt); - fshaderTxt.append(";"); - fsOutName = oFragColor.c_str(); - } else { - fsOutName = "gl_FragColor"; - } fshaderTxt.appendf( "// Write Rect Program FS\n" "void main() {" - " %s = %s;" + " sk_FragColor = %s;" "}", - fsOutName, uColor.c_str() ); diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp index d2e49a5cfb..0282fbb8a9 100644 --- a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp +++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp @@ -9,6 +9,8 @@ #include "gl/GrGLGpu.h" #include "gl/GrGLSLPrettyPrint.h" #include "SkTraceEvent.h" +#include "SkSLCompiler.h" +#include "ir/SkSLProgram.h" #define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X) #define GL_CALL_RET(R, X) GR_GL_CALL_RET(gpu->glInterface(), R, X) @@ -18,6 +20,74 @@ static const bool c_PrintShaders{false}; static void print_shader_source(const char** strings, int* lengths, int count); +static SkSL::GLCaps skslcaps_for_context(const GrGLContext& context) { + GrGLStandard standard = context.standard(); + const GrGLCaps* caps = context.caps(); + const GrGLSLCaps* glslCaps = caps->glslCaps(); + SkSL::GLCaps result; + switch (standard) { + case kGL_GrGLStandard: + result.fStandard = SkSL::GLCaps::kGL_Standard; + break; + case kGLES_GrGLStandard: + result.fStandard = SkSL::GLCaps::kGLES_Standard; + break; + default: + SkASSERT(false); + result.fStandard = SkSL::GLCaps::kGL_Standard; + } + + switch (glslCaps->generation()) { + case k110_GrGLSLGeneration: + if (kGLES_GrGLStandard == standard) { + // ES2's shader language is based on GLSL 1.20 but is version 1.00 of the ES + // language + result.fVersion = 100; + } else { + SkASSERT(kGL_GrGLStandard == standard); + result.fVersion = 110; + } + break; + case k130_GrGLSLGeneration: + SkASSERT(kGL_GrGLStandard == standard); + result.fVersion = 130; + break; + case k140_GrGLSLGeneration: + SkASSERT(kGL_GrGLStandard == standard); + result.fVersion = 140; + break; + case k150_GrGLSLGeneration: + SkASSERT(kGL_GrGLStandard == standard); + result.fVersion = 150; + break; + case k330_GrGLSLGeneration: + if (kGLES_GrGLStandard == standard) { + result.fVersion = 300; + } else { + SkASSERT(kGL_GrGLStandard == standard); + result.fVersion = 330; + } + break; + case k400_GrGLSLGeneration: + SkASSERT(kGL_GrGLStandard == standard); + result.fVersion = 400; + break; + case k310es_GrGLSLGeneration: + SkASSERT(kGLES_GrGLStandard == standard); + result.fVersion = 310; + break; + case k320es_GrGLSLGeneration: + SkASSERT(kGLES_GrGLStandard == standard); + result.fVersion = 320; + break; + } + result.fIsCoreProfile = caps->isCoreProfile(); + result.fUsesPrecisionModifiers = glslCaps->usesPrecisionModifiers(); + result.fMustDeclareFragmentShaderOutput = glslCaps->mustDeclareFragmentShaderOutput(); + result.fCanUseMinAndAbsTogether = glslCaps->canUseMinAndAbsTogether(); + return result; +} + GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx, GrGLuint programId, GrGLenum type, @@ -33,15 +103,32 @@ GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx, return 0; } + std::string sksl; #ifdef SK_DEBUG SkString prettySource = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false); - const GrGLchar* sourceStr = prettySource.c_str(); - GrGLint sourceLength = static_cast(prettySource.size()); - GR_GL_CALL(gli, ShaderSource(shaderId, 1, &sourceStr, &sourceLength)); + sksl = std::string(prettySource.c_str()); #else - GR_GL_CALL(gli, ShaderSource(shaderId, count, strings, lengths)); + for (int i = 0; i < count; i++) { + sksl.append(strings[i], lengths[i]); + } #endif + std::string glsl; + // creating Compiler is expensive, and we are single-threaded anyway, so just reuse a static one + static SkSL::Compiler compiler; + SkSL::GLCaps caps = skslcaps_for_context(glCtx); + SkASSERT(type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER); + SkDEBUGCODE(bool result = )compiler.toGLSL(type == GR_GL_VERTEX_SHADER + ? SkSL::Program::kVertex_Kind + : SkSL::Program::kFragment_Kind, + std::string(sksl.c_str()), + caps, + &glsl); + SkASSERTF(result, "SkSL errors:\n%s", compiler.errorText().c_str()); + const char* glslChars = glsl.c_str(); + GrGLint glslLength = (GrGLint) glsl.length(); + GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength)); + // If tracing is enabled in chrome then we pretty print bool traceShader; TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader); diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp index 7763f86e42..f1c655c58c 100644 --- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp +++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp @@ -321,7 +321,7 @@ void GrGLSLFragmentShaderBuilder::enableSecondaryOutput() { } const char* GrGLSLFragmentShaderBuilder::getPrimaryColorOutputName() const { - return fHasCustomColorOutput ? DeclaredColorOutputName() : "gl_FragColor"; + return fHasCustomColorOutput ? DeclaredColorOutputName() : "sk_FragColor"; } void GrGLSLFragmentBuilder::declAppendf(const char* fmt, ...) { diff --git a/src/gpu/glsl/GrGLSLShaderVar.h b/src/gpu/glsl/GrGLSLShaderVar.h index 9d162ecaa4..35ac4bcb84 100644 --- a/src/gpu/glsl/GrGLSLShaderVar.h +++ b/src/gpu/glsl/GrGLSLShaderVar.h @@ -213,24 +213,20 @@ public: private: static const char* TypeModifierString(const GrGLSLCaps* glslCaps, TypeModifier t) { - GrGLSLGeneration gen = glslCaps->generation(); switch (t) { case kNone_TypeModifier: return ""; + case kAttribute_TypeModifier: // fall through + case kVaryingIn_TypeModifier: // fall through case kIn_TypeModifier: return "in"; case kInOut_TypeModifier: return "inout"; + case kVaryingOut_TypeModifier: // fall through case kOut_TypeModifier: return "out"; case kUniform_TypeModifier: return "uniform"; - case kAttribute_TypeModifier: - return k110_GrGLSLGeneration == gen ? "attribute" : "in"; - case kVaryingIn_TypeModifier: - return k110_GrGLSLGeneration == gen ? "varying" : "in"; - case kVaryingOut_TypeModifier: - return k110_GrGLSLGeneration == gen ? "varying" : "out"; default: SkFAIL("Unknown shader variable type modifier."); return ""; // suppress warning 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 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 IRGenerator::coerce(std::unique_ptr 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> args; + args.push_back(std::move(expr)); + return std::unique_ptr(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 IRGenerator::call(Position position, std::vector> 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 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 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 Parser::directive() { Token start; if (!this->expect(Token::DIRECTIVE, "a directive", &start)) { @@ -193,7 +194,12 @@ std::unique_ptr 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::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 std::string to_string(T value) { - std::stringstream buffer; - buffer << std::setprecision(std::numeric_limits::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 diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp index 3906f67115..a589881aa4 100644 --- a/tests/SkSLGLSLTest.cpp +++ b/tests/SkSLGLSLTest.cpp @@ -26,46 +26,52 @@ static void test(skiatest::Reporter* r, const char* src, SkSL::GLCaps caps, cons } } +static SkSL::GLCaps default_caps() { + return { + 400, + SkSL::GLCaps::kGL_Standard, + false, // isCoreProfile + false, // usesPrecisionModifiers; + false, // mustDeclareFragmentShaderOutput + true // canUseMinAndAbsTogether + }; +} + DEF_TEST(SkSLHelloWorld, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, - "out vec4 fragColor; void main() { fragColor = vec4(0.75); }", - caps, + "void main() { sk_FragColor = vec4(0.75); }", + default_caps(), "#version 400\n" - "out vec4 fragColor;\n" "void main() {\n" - " fragColor = vec4(0.75);\n" + " gl_FragColor = vec4(0.75);\n" "}\n"); } DEF_TEST(SkSLControl, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, - "out vec4 fragColor;" "void main() {" - "if (1 + 2 + 3 > 5) { fragColor = vec4(0.75); } else { discard; }" + "if (1 + 2 + 3 > 5) { sk_FragColor = vec4(0.75); } else { discard; }" "int i = 0;" - "while (i < 10) fragColor *= 0.5;" - "do { fragColor += 0.01; } while (fragColor.x < 0.7);" + "while (i < 10) sk_FragColor *= 0.5;" + "do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.7);" "for (int i = 0; i < 10; i++) {" "if (i % 0 == 1) break; else continue;" "}" "return;" "}", - caps, + default_caps(), "#version 400\n" - "out vec4 fragColor;\n" "void main() {\n" " if ((1 + 2) + 3 > 5) {\n" - " fragColor = vec4(0.75);\n" + " gl_FragColor = vec4(0.75);\n" " } else {\n" " discard;\n" " }\n" " int i = 0;\n" - " while (i < 10) fragColor *= 0.5;\n" + " while (i < 10) gl_FragColor *= 0.5;\n" " do {\n" - " fragColor += 0.01;\n" - " } while (fragColor.x < 0.7);\n" + " gl_FragColor += 0.01;\n" + " } while (gl_FragColor.x < 0.7);\n" " for (int i = 0;i < 10; i++) {\n" " if (i % 0 == 1) break; else continue;\n" " }\n" @@ -74,34 +80,30 @@ DEF_TEST(SkSLControl, r) { } DEF_TEST(SkSLFunctions, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, - "out vec4 fragColor;" "float foo(float v[2]) { return v[0] * v[1]; }" "void bar(inout float x) { float y[2], z; y[0] = x; y[1] = x * 2; z = foo(y); x = z; }" - "void main() { float x = 10; bar(x); fragColor = vec4(x); }", - caps, + "void main() { float x = 10; bar(x); sk_FragColor = vec4(x); }", + default_caps(), "#version 400\n" - "out vec4 fragColor;\n" "float foo(in float[2] v) {\n" " return v[0] * v[1];\n" "}\n" "void bar(inout float x) {\n" " float y[2], z;\n" " y[0] = x;\n" - " y[1] = x * 2;\n" + " y[1] = x * 2.0;\n" " z = foo(y);\n" " x = z;\n" "}\n" "void main() {\n" - " float x = 10;\n" + " float x = 10.0;\n" " bar(x);\n" - " fragColor = vec4(x);\n" + " gl_FragColor = vec4(x);\n" "}\n"); } DEF_TEST(SkSLOperators, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, "void main() {" "float x = 1, y = 2;" @@ -123,17 +125,17 @@ DEF_TEST(SkSLOperators, r) { "z <<= 4;" "z %= 5;" "}", - caps, + default_caps(), "#version 400\n" "void main() {\n" - " float x = 1, y = 2;\n" + " float x = 1.0, y = 2.0;\n" " int z = 3;\n" " x = x + ((y * float(z)) * x) * (y - float(z));\n" " y = (x / y) / float(z);\n" " z = (((z / 2) % 3 << 4) >> 2) << 1;\n" - " bool b = x > 4 == x < 2 || (2 >= 5 && y <= float(z)) && 12 != 11;\n" - " x += 12;\n" - " x -= 12;\n" + " bool b = x > 4.0 == x < 2.0 || (2 >= 5 && y <= float(z)) && 12 != 11;\n" + " x += 12.0;\n" + " x -= 12.0;\n" " x *= (y /= float(z = 10));\n" " b ||= false;\n" " b &&= true;\n" @@ -148,7 +150,6 @@ DEF_TEST(SkSLOperators, r) { } DEF_TEST(SkSLMatrices, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, "void main() {" "mat2x4 x = mat2x4(1);" @@ -157,19 +158,18 @@ DEF_TEST(SkSLMatrices, r) { "vec3 v1 = mat3(1) * vec3(1);" "vec3 v2 = vec3(1) * mat3(1);" "}", - caps, + default_caps(), "#version 400\n" "void main() {\n" - " mat2x4 x = mat2x4(1);\n" - " mat3x2 y = mat3x2(1, 0, 0, 1, vec2(2, 2));\n" + " mat2x4 x = mat2x4(1.0);\n" + " mat3x2 y = mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n" " mat3x4 z = x * y;\n" - " vec3 v1 = mat3(1) * vec3(1);\n" - " vec3 v2 = vec3(1) * mat3(1);\n" + " vec3 v1 = mat3(1.0) * vec3(1.0);\n" + " vec3 v2 = vec3(1.0) * mat3(1.0);\n" "}\n"); } DEF_TEST(SkSLInterfaceBlock, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, "uniform testBlock {" "float x;" @@ -179,12 +179,12 @@ DEF_TEST(SkSLInterfaceBlock, r) { "};" "void main() {" "}", - caps, + default_caps(), "#version 400\n" "uniform testBlock {\n" " float x;\n" " float[2] y;\n" - " layout (binding = 12)mat3x2 z;\n" + " layout (binding = 12) mat3x2 z;\n" " bool w;\n" "};\n" "void main() {\n" @@ -192,7 +192,6 @@ DEF_TEST(SkSLInterfaceBlock, r) { } DEF_TEST(SkSLStructs, r) { - SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard }; test(r, "struct A {" "int x;" @@ -207,7 +206,7 @@ DEF_TEST(SkSLStructs, r) { "B b1, b2, b3;" "void main() {" "}", - caps, + default_caps(), "#version 400\n" "struct A {\n" " int x;\n" @@ -218,10 +217,97 @@ DEF_TEST(SkSLStructs, r) { "struct B {\n" " float x;\n" " float[2] y;\n" - " layout (binding = 1)A z;\n" + " layout (binding = 1) A z;\n" "}\n" " b1, b2, b3;\n" "void main() {\n" "}\n"); - +} + +DEF_TEST(SkSLVersion, r) { + SkSL::GLCaps caps = default_caps(); + caps.fVersion = 450; + caps.fIsCoreProfile = true; + test(r, + "in float test; void main() { sk_FragColor = vec4(0.75); }", + caps, + "#version 450 core\n" + "in float test;\n" + "void main() {\n" + " gl_FragColor = vec4(0.75);\n" + "}\n"); + caps.fVersion = 110; + caps.fIsCoreProfile = false; + test(r, + "in float test; void main() { sk_FragColor = vec4(0.75); }", + caps, + "#version 110\n" + "varying float test;\n" + "void main() {\n" + " gl_FragColor = vec4(0.75);\n" + "}\n"); +} + +DEF_TEST(SkSLDeclareOutput, r) { + SkSL::GLCaps caps = default_caps(); + caps.fMustDeclareFragmentShaderOutput = true; + test(r, + "void main() { sk_FragColor = vec4(0.75); }", + caps, + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(0.75);\n" + "}\n"); +} + +DEF_TEST(SkSLUsesPrecisionModifiers, r) { + SkSL::GLCaps caps = default_caps(); + test(r, + "void main() { float x = 0.75; highp float y = 1; }", + caps, + "#version 400\n" + "void main() {\n" + " float x = 0.75;\n" + " float y = 1.0;\n" + "}\n"); + caps.fUsesPrecisionModifiers = true; + test(r, + "void main() { float x = 0.75; highp float y = 1; }", + caps, + "#version 400\n" + "void main() {\n" + " mediump float x = 0.75;\n" + " highp float y = 1.0;\n" + "}\n"); +} + + +DEF_TEST(SkSLMinAbs, r) { + test(r, + "void main() {" + "float x = -5;" + "x = min(abs(x), 6);" + "}", + default_caps(), + "#version 400\n" + "void main() {\n" + " float x = -5.0;\n" + " x = min(abs(x), 6.0);\n" + "}\n"); + + SkSL::GLCaps caps = default_caps(); + caps.fCanUseMinAndAbsTogether = false; + test(r, + "void main() {" + "float x = -5.0;" + "x = min(abs(x), 6.0);" + "}", + caps, + "#version 400\n" + "void main() {\n" + " float minAbsHackVar0;\n" + " float x = -5.0;\n" + " x = (abs(x) > (minAbsHackVar0 = 6.0) ? minAbsHackVar0 : abs(x));\n" + "}\n"); }