| /* | |
| * 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 "src/sksl/codegen/SkSLGLSLCodeGenerator.h" | |
| #include <memory> | |
| #include "src/sksl/SkSLCompiler.h" | |
| #include "src/sksl/ir/SkSLExpressionStatement.h" | |
| #include "src/sksl/ir/SkSLExtension.h" | |
| #include "src/sksl/ir/SkSLIndexExpression.h" | |
| #include "src/sksl/ir/SkSLModifiersDeclaration.h" | |
| #include "src/sksl/ir/SkSLNop.h" | |
| #include "src/sksl/ir/SkSLStructDefinition.h" | |
| #include "src/sksl/ir/SkSLVariableReference.h" | |
| #ifndef SKSL_STANDALONE | |
| #include "include/private/SkOnce.h" | |
| #endif | |
| namespace SkSL { | |
| void GLSLCodeGenerator::write(skstd::string_view s) { | |
| if (!s.length()) { | |
| return; | |
| } | |
| if (fAtLineStart) { | |
| for (int i = 0; i < fIndentation; i++) { | |
| fOut->writeText(" "); | |
| } | |
| } | |
| fOut->write(s.data(), s.length()); | |
| fAtLineStart = false; | |
| } | |
| void GLSLCodeGenerator::writeLine(skstd::string_view s) { | |
| this->write(s); | |
| fOut->writeText(fLineEnding); | |
| fAtLineStart = true; | |
| } | |
| void GLSLCodeGenerator::finishLine() { | |
| if (!fAtLineStart) { | |
| this->writeLine(); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeExtension(skstd::string_view name, bool require) { | |
| fExtensions.writeText("#extension "); | |
| fExtensions.write(name.data(), name.length()); | |
| fExtensions.writeText(require ? " : require\n" : " : enable\n"); | |
| } | |
| bool GLSLCodeGenerator::usesPrecisionModifiers() const { | |
| return this->caps().usesPrecisionModifiers(); | |
| } | |
| // Returns the name of the type with array dimensions, e.g. `float[2]`. | |
| String GLSLCodeGenerator::getTypeName(const Type& type) { | |
| switch (type.typeKind()) { | |
| case Type::TypeKind::kVector: { | |
| const Type& component = type.componentType(); | |
| String result; | |
| if (component == *fContext.fTypes.fFloat || component == *fContext.fTypes.fHalf) { | |
| result = "vec"; | |
| } | |
| else if (component.isSigned()) { | |
| result = "ivec"; | |
| } | |
| else if (component.isUnsigned()) { | |
| result = "uvec"; | |
| } | |
| else if (component == *fContext.fTypes.fBool) { | |
| result = "bvec"; | |
| } | |
| else { | |
| SK_ABORT("unsupported vector type"); | |
| } | |
| result += to_string(type.columns()); | |
| return result; | |
| } | |
| case Type::TypeKind::kMatrix: { | |
| String result; | |
| const Type& component = type.componentType(); | |
| if (component == *fContext.fTypes.fFloat || component == *fContext.fTypes.fHalf) { | |
| result = "mat"; | |
| } | |
| else { | |
| SK_ABORT("unsupported matrix type"); | |
| } | |
| result += to_string(type.columns()); | |
| if (type.columns() != type.rows()) { | |
| result += "x"; | |
| result += to_string(type.rows()); | |
| } | |
| return result; | |
| } | |
| case Type::TypeKind::kArray: { | |
| String baseTypeName = this->getTypeName(type.componentType()); | |
| return (type.columns() == Type::kUnsizedArray) | |
| ? String::printf("%s[]", baseTypeName.c_str()) | |
| : String::printf("%s[%d]", baseTypeName.c_str(), type.columns()); | |
| } | |
| case Type::TypeKind::kScalar: { | |
| if (type == *fContext.fTypes.fHalf) { | |
| return "float"; | |
| } | |
| else if (type == *fContext.fTypes.fShort) { | |
| return "int"; | |
| } | |
| else if (type == *fContext.fTypes.fUShort) { | |
| return "uint"; | |
| } | |
| else { | |
| return String(type.name()); | |
| } | |
| break; | |
| } | |
| default: | |
| return String(type.name()); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeStructDefinition(const StructDefinition& s) { | |
| const Type& type = s.type(); | |
| this->write("struct "); | |
| this->write(type.name()); | |
| this->writeLine(" {"); | |
| fIndentation++; | |
| for (const auto& f : type.fields()) { | |
| this->writeModifiers(f.fModifiers, false); | |
| this->writeTypePrecision(*f.fType); | |
| const Type& baseType = f.fType->isArray() ? f.fType->componentType() : *f.fType; | |
| this->writeType(baseType); | |
| this->write(" "); | |
| this->write(f.fName); | |
| if (f.fType->isArray()) { | |
| this->write("[" + to_string(f.fType->columns()) + "]"); | |
| } | |
| this->writeLine(";"); | |
| } | |
| fIndentation--; | |
| this->writeLine("};"); | |
| } | |
| void GLSLCodeGenerator::writeType(const Type& type) { | |
| this->write(this->getTypeName(type)); | |
| } | |
| void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) { | |
| switch (expr.kind()) { | |
| case Expression::Kind::kBinary: | |
| this->writeBinaryExpression(expr.as<BinaryExpression>(), parentPrecedence); | |
| break; | |
| case Expression::Kind::kBoolLiteral: | |
| this->writeBoolLiteral(expr.as<BoolLiteral>()); | |
| break; | |
| case Expression::Kind::kConstructorDiagonalMatrix: | |
| this->writeConstructorDiagonalMatrix(expr.as<ConstructorDiagonalMatrix>(), | |
| parentPrecedence); | |
| break; | |
| case Expression::Kind::kConstructorArray: | |
| case Expression::Kind::kConstructorCompound: | |
| case Expression::Kind::kConstructorMatrixResize: | |
| case Expression::Kind::kConstructorSplat: | |
| case Expression::Kind::kConstructorStruct: | |
| this->writeAnyConstructor(expr.asAnyConstructor(), parentPrecedence); | |
| break; | |
| case Expression::Kind::kConstructorScalarCast: | |
| case Expression::Kind::kConstructorCompoundCast: | |
| this->writeCastConstructor(expr.asAnyConstructor(), parentPrecedence); | |
| break; | |
| case Expression::Kind::kIntLiteral: | |
| this->writeIntLiteral(expr.as<IntLiteral>()); | |
| break; | |
| case Expression::Kind::kFieldAccess: | |
| this->writeFieldAccess(expr.as<FieldAccess>()); | |
| break; | |
| case Expression::Kind::kFloatLiteral: | |
| this->writeFloatLiteral(expr.as<FloatLiteral>()); | |
| break; | |
| case Expression::Kind::kFunctionCall: | |
| this->writeFunctionCall(expr.as<FunctionCall>()); | |
| break; | |
| case Expression::Kind::kPrefix: | |
| this->writePrefixExpression(expr.as<PrefixExpression>(), parentPrecedence); | |
| break; | |
| case Expression::Kind::kPostfix: | |
| this->writePostfixExpression(expr.as<PostfixExpression>(), parentPrecedence); | |
| break; | |
| case Expression::Kind::kSetting: | |
| this->writeSetting(expr.as<Setting>()); | |
| break; | |
| case Expression::Kind::kSwizzle: | |
| this->writeSwizzle(expr.as<Swizzle>()); | |
| break; | |
| case Expression::Kind::kVariableReference: | |
| this->writeVariableReference(expr.as<VariableReference>()); | |
| break; | |
| case Expression::Kind::kTernary: | |
| this->writeTernaryExpression(expr.as<TernaryExpression>(), parentPrecedence); | |
| break; | |
| case Expression::Kind::kIndex: | |
| this->writeIndexExpression(expr.as<IndexExpression>()); | |
| break; | |
| default: | |
| SkDEBUGFAILF("unsupported expression: %s", expr.description().c_str()); | |
| break; | |
| } | |
| } | |
| static bool is_abs(Expression& expr) { | |
| return expr.is<FunctionCall>() && | |
| expr.as<FunctionCall>().function().intrinsicKind() == k_abs_IntrinsicKind; | |
| } | |
| // turns min(abs(x), y) into ((tmpVar1 = abs(x)) < (tmpVar2 = y) ? tmpVar1 : tmpVar2) to avoid a | |
| // Tegra3 compiler bug. | |
| void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) { | |
| SkASSERT(!this->caps().canUseMinAndAbsTogether()); | |
| String tmpVar1 = "minAbsHackVar" + to_string(fVarCount++); | |
| String tmpVar2 = "minAbsHackVar" + to_string(fVarCount++); | |
| this->fFunctionHeader += String(" ") + this->getTypePrecision(absExpr.type()) + | |
| this->getTypeName(absExpr.type()) + " " + tmpVar1 + ";\n"; | |
| this->fFunctionHeader += String(" ") + this->getTypePrecision(otherExpr.type()) + | |
| this->getTypeName(otherExpr.type()) + " " + tmpVar2 + ";\n"; | |
| this->write("((" + tmpVar1 + " = "); | |
| this->writeExpression(absExpr, Precedence::kTopLevel); | |
| this->write(") < (" + tmpVar2 + " = "); | |
| this->writeExpression(otherExpr, Precedence::kAssignment); | |
| this->write(") ? " + tmpVar1 + " : " + tmpVar2 + ")"); | |
| } | |
| void GLSLCodeGenerator::writeInverseSqrtHack(const Expression& x) { | |
| this->write("(1.0 / sqrt("); | |
| this->writeExpression(x, Precedence::kTopLevel); | |
| this->write("))"); | |
| } | |
| void GLSLCodeGenerator::writeDeterminantHack(const Expression& mat) { | |
| String name; | |
| const Type& type = mat.type(); | |
| if (type == *fContext.fTypes.fFloat2x2 || type == *fContext.fTypes.fHalf2x2) { | |
| name = "_determinant2"; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "float " + name + "(mat2 m) {" | |
| " return m[0][0] * m[1][1] - m[0][1] * m[1][0];" | |
| "}" | |
| ).c_str()); | |
| } | |
| } | |
| else if (type == *fContext.fTypes.fFloat3x3 || type == *fContext.fTypes.fHalf3x3) { | |
| name = "_determinant3"; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "float " + name + "(mat3 m) {" | |
| " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];" | |
| " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];" | |
| " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];" | |
| " float b01 = a22 * a11 - a12 * a21;" | |
| " float b11 = -a22 * a10 + a12 * a20;" | |
| " float b21 = a21 * a10 - a11 * a20;" | |
| " return a00 * b01 + a01 * b11 + a02 * b21;" | |
| "}" | |
| ).c_str()); | |
| } | |
| } | |
| else if (type == *fContext.fTypes.fFloat4x4 || type == *fContext.fTypes.fHalf4x4) { | |
| name = "_determinant4"; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "mat4 " + name + "(mat4 m) {" | |
| " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];" | |
| " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];" | |
| " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];" | |
| " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];" | |
| " float b00 = a00 * a11 - a01 * a10;" | |
| " float b01 = a00 * a12 - a02 * a10;" | |
| " float b02 = a00 * a13 - a03 * a10;" | |
| " float b03 = a01 * a12 - a02 * a11;" | |
| " float b04 = a01 * a13 - a03 * a11;" | |
| " float b05 = a02 * a13 - a03 * a12;" | |
| " float b06 = a20 * a31 - a21 * a30;" | |
| " float b07 = a20 * a32 - a22 * a30;" | |
| " float b08 = a20 * a33 - a23 * a30;" | |
| " float b09 = a21 * a32 - a22 * a31;" | |
| " float b10 = a21 * a33 - a23 * a31;" | |
| " float b11 = a22 * a33 - a23 * a32;" | |
| " return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;" | |
| "}" | |
| ).c_str()); | |
| } | |
| } | |
| else { | |
| SkASSERT(false); | |
| } | |
| this->write(name + "("); | |
| this->writeExpression(mat, Precedence::kTopLevel); | |
| this->write(")"); | |
| } | |
| void GLSLCodeGenerator::writeInverseHack(const Expression& mat) { | |
| String name; | |
| const Type& type = mat.type(); | |
| if (type == *fContext.fTypes.fFloat2x2 || type == *fContext.fTypes.fHalf2x2) { | |
| name = "_inverse2"; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "mat2 " + name + "(mat2 m) {" | |
| " return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / " | |
| "(m[0][0] * m[1][1] - m[0][1] * m[1][0]);" | |
| "}" | |
| ).c_str()); | |
| } | |
| } | |
| else if (type == *fContext.fTypes.fFloat3x3 || type == *fContext.fTypes.fHalf3x3) { | |
| name = "_inverse3"; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "mat3 " + name + "(mat3 m) {" | |
| " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];" | |
| " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];" | |
| " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];" | |
| " float b01 = a22 * a11 - a12 * a21;" | |
| " float b11 = -a22 * a10 + a12 * a20;" | |
| " float b21 = a21 * a10 - a11 * a20;" | |
| " float det = a00 * b01 + a01 * b11 + a02 * b21;" | |
| " return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11)," | |
| " b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10)," | |
| " b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det;" | |
| "}" | |
| ).c_str()); | |
| } | |
| } | |
| else if (type == *fContext.fTypes.fFloat4x4 || type == *fContext.fTypes.fHalf4x4) { | |
| name = "_inverse4"; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "mat4 " + name + "(mat4 m) {" | |
| " float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];" | |
| " float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];" | |
| " float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];" | |
| " float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];" | |
| " float b00 = a00 * a11 - a01 * a10;" | |
| " float b01 = a00 * a12 - a02 * a10;" | |
| " float b02 = a00 * a13 - a03 * a10;" | |
| " float b03 = a01 * a12 - a02 * a11;" | |
| " float b04 = a01 * a13 - a03 * a11;" | |
| " float b05 = a02 * a13 - a03 * a12;" | |
| " float b06 = a20 * a31 - a21 * a30;" | |
| " float b07 = a20 * a32 - a22 * a30;" | |
| " float b08 = a20 * a33 - a23 * a30;" | |
| " float b09 = a21 * a32 - a22 * a31;" | |
| " float b10 = a21 * a33 - a23 * a31;" | |
| " float b11 = a22 * a33 - a23 * a32;" | |
| " float det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - " | |
| " b04 * b07 + b05 * b06;" | |
| " return mat4(" | |
| " a11 * b11 - a12 * b10 + a13 * b09," | |
| " a02 * b10 - a01 * b11 - a03 * b09," | |
| " a31 * b05 - a32 * b04 + a33 * b03," | |
| " a22 * b04 - a21 * b05 - a23 * b03," | |
| " a12 * b08 - a10 * b11 - a13 * b07," | |
| " a00 * b11 - a02 * b08 + a03 * b07," | |
| " a32 * b02 - a30 * b05 - a33 * b01," | |
| " a20 * b05 - a22 * b02 + a23 * b01," | |
| " a10 * b10 - a11 * b08 + a13 * b06," | |
| " a01 * b08 - a00 * b10 - a03 * b06," | |
| " a30 * b04 - a31 * b02 + a33 * b00," | |
| " a21 * b02 - a20 * b04 - a23 * b00," | |
| " a11 * b07 - a10 * b09 - a12 * b06," | |
| " a00 * b09 - a01 * b07 + a02 * b06," | |
| " a31 * b01 - a30 * b03 - a32 * b00," | |
| " a20 * b03 - a21 * b01 + a22 * b00) / det;" | |
| "}" | |
| ).c_str()); | |
| } | |
| } | |
| else { | |
| SkASSERT(false); | |
| } | |
| this->write(name + "("); | |
| this->writeExpression(mat, Precedence::kTopLevel); | |
| this->write(")"); | |
| } | |
| void GLSLCodeGenerator::writeTransposeHack(const Expression& mat) { | |
| const Type& type = mat.type(); | |
| String name = "transpose" + to_string(type.columns()) + to_string(type.rows()); | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| String typeName = this->getTypeName(type); | |
| const Type& base = type.componentType(); | |
| String transposed = this->getTypeName(base.toCompound(fContext, | |
| type.rows(), | |
| type.columns())); | |
| fExtraFunctions.writeText((transposed + " " + name + "(" + typeName + " m) {\nreturn " + | |
| transposed + "(").c_str()); | |
| const char* separator = ""; | |
| for (int row = 0; row < type.rows(); ++row) { | |
| for (int column = 0; column < type.columns(); ++column) { | |
| fExtraFunctions.writeText(separator); | |
| fExtraFunctions.writeText(("m[" + to_string(column) + "][" + to_string(row) + | |
| "]").c_str()); | |
| separator = ", "; | |
| } | |
| } | |
| fExtraFunctions.writeText("); }"); | |
| } | |
| this->write(name + "("); | |
| this->writeExpression(mat, Precedence::kTopLevel); | |
| this->write(")"); | |
| } | |
| void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { | |
| const FunctionDeclaration& function = c.function(); | |
| const ExpressionArray& arguments = c.arguments(); | |
| bool isTextureFunctionWithBias = false; | |
| bool nameWritten = false; | |
| switch (c.function().intrinsicKind()) { | |
| case k_abs_IntrinsicKind: { | |
| if (!this->caps().emulateAbsIntFunction()) | |
| break; | |
| SkASSERT(arguments.size() == 1); | |
| if (arguments[0]->type() != *fContext.fTypes.fInt) { | |
| break; | |
| } | |
| // abs(int) on Intel OSX is incorrect, so emulate it: | |
| String name = "_absemulation"; | |
| this->write(name); | |
| nameWritten = true; | |
| if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) { | |
| fWrittenIntrinsics.insert(name); | |
| fExtraFunctions.writeText(( | |
| "int " + name + "(int x) {\n" | |
| " return x * sign(x);\n" | |
| "}\n" | |
| ).c_str()); | |
| } | |
| break; | |
| } | |
| case k_atan_IntrinsicKind: | |
| if (this->caps().mustForceNegatedAtanParamToFloat() && | |
| arguments.size() == 2 && | |
| arguments[1]->kind() == Expression::Kind::kPrefix) { | |
| const PrefixExpression& p = (PrefixExpression&) *arguments[1]; | |
| if (p.getOperator().kind() == Token::Kind::TK_MINUS) { | |
| this->write("atan("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write(", -1.0 * "); | |
| this->writeExpression(*p.operand(), Precedence::kMultiplicative); | |
| this->write(")"); | |
| return; | |
| } | |
| } | |
| break; | |
| case k_ldexp_IntrinsicKind: | |
| if (this->caps().mustForceNegatedLdexpParamToMultiply() && | |
| arguments.size() == 2 && | |
| arguments[1]->is<PrefixExpression>()) { | |
| const PrefixExpression& p = arguments[1]->as<PrefixExpression>(); | |
| if (p.getOperator().kind() == Token::Kind::TK_MINUS) { | |
| this->write("ldexp("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write(", "); | |
| this->writeExpression(*p.operand(), Precedence::kMultiplicative); | |
| this->write(" * -1)"); | |
| return; | |
| } | |
| } | |
| break; | |
| case k_dFdy_IntrinsicKind: | |
| // Flipping Y also negates the Y derivatives. | |
| this->write(SKSL_RTFLIP_NAME ".y * dFdy"); | |
| nameWritten = true; | |
| [[fallthrough]]; | |
| case k_dFdx_IntrinsicKind: | |
| case k_fwidth_IntrinsicKind: | |
| if (!fFoundDerivatives && | |
| this->caps().shaderDerivativeExtensionString()) { | |
| this->writeExtension(this->caps().shaderDerivativeExtensionString()); | |
| fFoundDerivatives = true; | |
| } | |
| break; | |
| case k_determinant_IntrinsicKind: | |
| if (!this->caps().builtinDeterminantSupport()) { | |
| SkASSERT(arguments.size() == 1); | |
| this->writeDeterminantHack(*arguments[0]); | |
| return; | |
| } | |
| break; | |
| case k_fma_IntrinsicKind: | |
| if (!this->caps().builtinFMASupport()) { | |
| SkASSERT(arguments.size() == 3); | |
| this->write("(("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write(") * ("); | |
| this->writeExpression(*arguments[1], Precedence::kSequence); | |
| this->write(") + ("); | |
| this->writeExpression(*arguments[2], Precedence::kSequence); | |
| this->write("))"); | |
| return; | |
| } | |
| break; | |
| case k_fract_IntrinsicKind: | |
| if (!this->caps().canUseFractForNegativeValues()) { | |
| SkASSERT(arguments.size() == 1); | |
| this->write("(0.5 - sign("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write(") * (0.5 - fract(abs("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write("))))"); | |
| return; | |
| } | |
| break; | |
| case k_inverse_IntrinsicKind: | |
| if (this->caps().generation() < k140_GrGLSLGeneration) { | |
| SkASSERT(arguments.size() == 1); | |
| this->writeInverseHack(*arguments[0]); | |
| return; | |
| } | |
| break; | |
| case k_inversesqrt_IntrinsicKind: | |
| if (this->caps().generation() < k130_GrGLSLGeneration) { | |
| SkASSERT(arguments.size() == 1); | |
| this->writeInverseSqrtHack(*arguments[0]); | |
| return; | |
| } | |
| break; | |
| case k_min_IntrinsicKind: | |
| if (!this->caps().canUseMinAndAbsTogether()) { | |
| SkASSERT(arguments.size() == 2); | |
| if (is_abs(*arguments[0])) { | |
| this->writeMinAbsHack(*arguments[0], *arguments[1]); | |
| return; | |
| } | |
| if (is_abs(*arguments[1])) { | |
| // note that this violates the GLSL left-to-right evaluation semantics. | |
| // I doubt it will ever end up mattering, but it's worth calling out. | |
| this->writeMinAbsHack(*arguments[1], *arguments[0]); | |
| return; | |
| } | |
| } | |
| break; | |
| case k_pow_IntrinsicKind: | |
| if (!this->caps().removePowWithConstantExponent()) { | |
| break; | |
| } | |
| // pow(x, y) on some NVIDIA drivers causes crashes if y is a | |
| // constant. It's hard to tell what constitutes "constant" here | |
| // so just replace in all cases. | |
| // Change pow(x, y) into exp2(y * log2(x)) | |
| this->write("exp2("); | |
| this->writeExpression(*arguments[1], Precedence::kMultiplicative); | |
| this->write(" * log2("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write("))"); | |
| return; | |
| case k_saturate_IntrinsicKind: | |
| SkASSERT(arguments.size() == 1); | |
| this->write("clamp("); | |
| this->writeExpression(*arguments[0], Precedence::kSequence); | |
| this->write(", 0.0, 1.0)"); | |
| return; | |
| case k_sample_IntrinsicKind: { | |
| const char* dim = ""; | |
| bool proj = false; | |
| const Type& arg0Type = arguments[0]->type(); | |
| const Type& arg1Type = arguments[1]->type(); | |
| switch (arg0Type.dimensions()) { | |
| case SpvDim1D: | |
| dim = "1D"; | |
| isTextureFunctionWithBias = true; | |
| if (arg1Type == *fContext.fTypes.fFloat) { | |
| proj = false; | |
| } else { | |
| SkASSERT(arg1Type == *fContext.fTypes.fFloat2); | |
| proj = true; | |
| } | |
| break; | |
| case SpvDim2D: | |
| dim = "2D"; | |
| if (arg0Type != *fContext.fTypes.fSamplerExternalOES) { | |
| isTextureFunctionWithBias = true; | |
| } | |
| if (arg1Type == *fContext.fTypes.fFloat2) { | |
| proj = false; | |
| } else { | |
| SkASSERT(arg1Type == *fContext.fTypes.fFloat3); | |
| proj = true; | |
| } | |
| break; | |
| case SpvDim3D: | |
| dim = "3D"; | |
| isTextureFunctionWithBias = true; | |
| if (arg1Type == *fContext.fTypes.fFloat3) { | |
| proj = false; | |
| } else { | |
| SkASSERT(arg1Type == *fContext.fTypes.fFloat4); | |
| proj = true; | |
| } | |
| break; | |
| case SpvDimCube: | |
| dim = "Cube"; | |
| isTextureFunctionWithBias = true; | |
| proj = false; | |
| break; | |
| case SpvDimRect: | |
| dim = "2DRect"; | |
| proj = false; | |
| break; | |
| case SpvDimBuffer: | |
| SkASSERT(false); // doesn't exist | |
| dim = "Buffer"; | |
| proj = false; | |
| break; | |
| case SpvDimSubpassData: | |
| SkASSERT(false); // doesn't exist | |
| dim = "SubpassData"; | |
| proj = false; | |
| break; | |
| } | |
| if (!fTextureFunctionOverride.empty()) { | |
| this->write(fTextureFunctionOverride.c_str()); | |
| } else { | |
| this->write("texture"); | |
| if (this->caps().generation() < k130_GrGLSLGeneration) { | |
| this->write(dim); | |
| } | |
| if (proj) { | |
| this->write("Proj"); | |
| } | |
| } | |
| nameWritten = true; | |
| break; | |
| } | |
| case k_transpose_IntrinsicKind: | |
| if (this->caps().generation() < k130_GrGLSLGeneration) { | |
| SkASSERT(arguments.size() == 1); | |
| this->writeTransposeHack(*arguments[0]); | |
| return; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| if (!nameWritten) { | |
| this->write(function.mangledName()); | |
| } | |
| this->write("("); | |
| const char* separator = ""; | |
| for (const auto& arg : arguments) { | |
| this->write(separator); | |
| separator = ", "; | |
| this->writeExpression(*arg, Precedence::kSequence); | |
| } | |
| if (fProgram.fConfig->fSettings.fSharpenTextures && isTextureFunctionWithBias) { | |
| this->write(", -0.5"); | |
| } | |
| this->write(")"); | |
| } | |
| void GLSLCodeGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, | |
| Precedence parentPrecedence) { | |
| if (c.type().columns() == 4 && c.type().rows() == 2) { | |
| // Due to a longstanding bug in glslang and Mesa, several GPU drivers generate diagonal 4x2 | |
| // matrices incorrectly. (skia:12003, https://github.com/KhronosGroup/glslang/pull/2646) | |
| // We can work around this issue by multiplying a scalar by the identity matrix. | |
| // In practice, this doesn't come up naturally in real code and we don't know every affected | |
| // driver, so we just apply this workaround everywhere. | |
| this->write("("); | |
| this->writeType(c.type()); | |
| this->write("(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0) * "); | |
| this->writeExpression(*c.argument(), Precedence::kMultiplicative); | |
| this->write(")"); | |
| return; | |
| } | |
| this->writeAnyConstructor(c, parentPrecedence); | |
| } | |
| void GLSLCodeGenerator::writeCastConstructor(const AnyConstructor& c, Precedence parentPrecedence) { | |
| const auto arguments = c.argumentSpan(); | |
| SkASSERT(arguments.size() == 1); | |
| const Expression& argument = *arguments.front(); | |
| if ((this->getTypeName(c.type()) == this->getTypeName(argument.type()) || | |
| (argument.type() == *fContext.fTypes.fFloatLiteral))) { | |
| // In cases like half(float), they're different types as far as SkSL is concerned but | |
| // the same type as far as GLSL is concerned. We avoid a redundant float(float) by just | |
| // writing out the inner expression here. | |
| this->writeExpression(argument, parentPrecedence); | |
| return; | |
| } | |
| // This cast should be emitted as-is. | |
| return this->writeAnyConstructor(c, parentPrecedence); | |
| } | |
| void GLSLCodeGenerator::writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence) { | |
| this->writeType(c.type()); | |
| this->write("("); | |
| const char* separator = ""; | |
| for (const auto& arg : c.argumentSpan()) { | |
| this->write(separator); | |
| separator = ", "; | |
| this->writeExpression(*arg, Precedence::kSequence); | |
| } | |
| this->write(")"); | |
| } | |
| void GLSLCodeGenerator::writeFragCoord() { | |
| if (!this->caps().canUseFragCoord()) { | |
| if (!fSetupFragCoordWorkaround) { | |
| const char* precision = usesPrecisionModifiers() ? "highp " : ""; | |
| fFunctionHeader += precision; | |
| fFunctionHeader += " float sk_FragCoord_InvW = 1. / sk_FragCoord_Workaround.w;\n"; | |
| fFunctionHeader += precision; | |
| fFunctionHeader += " vec4 sk_FragCoord_Resolved = " | |
| "vec4(sk_FragCoord_Workaround.xyz * sk_FragCoord_InvW, sk_FragCoord_InvW);\n"; | |
| // Ensure that we get exact .5 values for x and y. | |
| fFunctionHeader += " sk_FragCoord_Resolved.xy = floor(sk_FragCoord_Resolved.xy) + " | |
| "vec2(.5);\n"; | |
| fSetupFragCoordWorkaround = true; | |
| } | |
| this->write("sk_FragCoord_Resolved"); | |
| return; | |
| } | |
| if (!fSetupFragPosition) { | |
| fFunctionHeader += usesPrecisionModifiers() ? "highp " : ""; | |
| fFunctionHeader += " vec4 sk_FragCoord = vec4(" | |
| "gl_FragCoord.x, " | |
| SKSL_RTFLIP_NAME ".x + " SKSL_RTFLIP_NAME ".y * gl_FragCoord.y, " | |
| "gl_FragCoord.z, " | |
| "gl_FragCoord.w);\n"; | |
| fSetupFragPosition = true; | |
| } | |
| this->write("sk_FragCoord"); | |
| } | |
| void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) { | |
| switch (ref.variable()->modifiers().fLayout.fBuiltin) { | |
| case SK_FRAGCOLOR_BUILTIN: | |
| if (this->caps().mustDeclareFragmentShaderOutput()) { | |
| this->write("sk_FragColor"); | |
| } else { | |
| this->write("gl_FragColor"); | |
| } | |
| break; | |
| case SK_FRAGCOORD_BUILTIN: | |
| this->writeFragCoord(); | |
| break; | |
| case SK_CLOCKWISE_BUILTIN: | |
| if (!fSetupClockwise) { | |
| fFunctionHeader += | |
| " bool sk_Clockwise = gl_FrontFacing;\n" | |
| " if (" SKSL_RTFLIP_NAME ".y < 0.0) {\n" | |
| " sk_Clockwise = !sk_Clockwise;\n" | |
| " }\n"; | |
| fSetupClockwise = true; | |
| } | |
| this->write("sk_Clockwise"); | |
| break; | |
| case SK_VERTEXID_BUILTIN: | |
| this->write("gl_VertexID"); | |
| break; | |
| case SK_INSTANCEID_BUILTIN: | |
| this->write("gl_InstanceID"); | |
| break; | |
| case SK_IN_BUILTIN: | |
| this->write("gl_in"); | |
| break; | |
| case SK_INVOCATIONID_BUILTIN: | |
| this->write("gl_InvocationID"); | |
| break; | |
| case SK_LASTFRAGCOLOR_BUILTIN: | |
| this->write(this->caps().fbFetchColorName()); | |
| break; | |
| default: | |
| this->write(ref.variable()->name()); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) { | |
| this->writeExpression(*expr.base(), Precedence::kPostfix); | |
| this->write("["); | |
| this->writeExpression(*expr.index(), Precedence::kTopLevel); | |
| this->write("]"); | |
| } | |
| bool is_sk_position(const FieldAccess& f) { | |
| return "sk_Position" == f.base()->type().fields()[f.fieldIndex()].fName; | |
| } | |
| void GLSLCodeGenerator::writeFieldAccess(const FieldAccess& f) { | |
| if (f.ownerKind() == FieldAccess::OwnerKind::kDefault) { | |
| this->writeExpression(*f.base(), Precedence::kPostfix); | |
| this->write("."); | |
| } | |
| const Type& baseType = f.base()->type(); | |
| skstd::string_view name = baseType.fields()[f.fieldIndex()].fName; | |
| if (name == "sk_Position") { | |
| this->write("gl_Position"); | |
| } else if (name == "sk_PointSize") { | |
| this->write("gl_PointSize"); | |
| } else { | |
| this->write(baseType.fields()[f.fieldIndex()].fName); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) { | |
| this->writeExpression(*swizzle.base(), Precedence::kPostfix); | |
| this->write("."); | |
| for (int c : swizzle.components()) { | |
| SkASSERT(c >= 0 && c <= 3); | |
| this->write(&("x\0y\0z\0w\0"[c * 2])); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeMatrixComparisonWorkaround(const BinaryExpression& b) { | |
| const Expression& left = *b.left(); | |
| const Expression& right = *b.right(); | |
| Operator op = b.getOperator(); | |
| SkASSERT(op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ); | |
| SkASSERT(left.type().isMatrix()); | |
| SkASSERT(right.type().isMatrix()); | |
| String tempMatrix1 = "_tempMatrix" + to_string(fVarCount++); | |
| String tempMatrix2 = "_tempMatrix" + to_string(fVarCount++); | |
| this->fFunctionHeader += String(" ") + this->getTypePrecision(left.type()) + | |
| this->getTypeName(left.type()) + " " + tempMatrix1 + ";\n " + | |
| this->getTypePrecision(right.type()) + | |
| this->getTypeName(right.type()) + " " + tempMatrix2 + ";\n"; | |
| this->write("((" + tempMatrix1 + " = "); | |
| this->writeExpression(left, Precedence::kAssignment); | |
| this->write("), (" + tempMatrix2 + " = "); | |
| this->writeExpression(right, Precedence::kAssignment); | |
| this->write("), (" + tempMatrix1 + " "); | |
| this->write(op.operatorName()); | |
| this->write(" " + tempMatrix2 + "))"); | |
| } | |
| void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b, | |
| Precedence parentPrecedence) { | |
| const Expression& left = *b.left(); | |
| const Expression& right = *b.right(); | |
| Operator op = b.getOperator(); | |
| if (this->caps().unfoldShortCircuitAsTernary() && | |
| (op.kind() == Token::Kind::TK_LOGICALAND || op.kind() == Token::Kind::TK_LOGICALOR)) { | |
| this->writeShortCircuitWorkaroundExpression(b, parentPrecedence); | |
| return; | |
| } | |
| if (this->caps().rewriteMatrixComparisons() && | |
| left.type().isMatrix() && right.type().isMatrix() && | |
| (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ)) { | |
| this->writeMatrixComparisonWorkaround(b); | |
| return; | |
| } | |
| Precedence precedence = op.getBinaryPrecedence(); | |
| if (precedence >= parentPrecedence) { | |
| this->write("("); | |
| } | |
| bool positionWorkaround = fProgram.fConfig->fKind == ProgramKind::kVertex && | |
| op.isAssignment() && | |
| left.is<FieldAccess>() && | |
| is_sk_position(left.as<FieldAccess>()) && | |
| !right.containsRTAdjust() && | |
| !this->caps().canUseFragCoord(); | |
| if (positionWorkaround) { | |
| this->write("sk_FragCoord_Workaround = ("); | |
| } | |
| this->writeExpression(left, precedence); | |
| this->write(" "); | |
| this->write(op.operatorName()); | |
| this->write(" "); | |
| this->writeExpression(right, precedence); | |
| if (positionWorkaround) { | |
| this->write(")"); | |
| } | |
| if (precedence >= parentPrecedence) { | |
| this->write(")"); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeShortCircuitWorkaroundExpression(const BinaryExpression& b, | |
| Precedence parentPrecedence) { | |
| if (Precedence::kTernary >= parentPrecedence) { | |
| this->write("("); | |
| } | |
| // Transform: | |
| // a && b => a ? b : false | |
| // a || b => a ? true : b | |
| this->writeExpression(*b.left(), Precedence::kTernary); | |
| this->write(" ? "); | |
| if (b.getOperator().kind() == Token::Kind::TK_LOGICALAND) { | |
| this->writeExpression(*b.right(), Precedence::kTernary); | |
| } else { | |
| BoolLiteral boolTrue(/*offset=*/-1, /*value=*/true, fContext.fTypes.fBool.get()); | |
| this->writeBoolLiteral(boolTrue); | |
| } | |
| this->write(" : "); | |
| if (b.getOperator().kind() == Token::Kind::TK_LOGICALAND) { | |
| BoolLiteral boolFalse(/*offset=*/-1, /*value=*/false, fContext.fTypes.fBool.get()); | |
| this->writeBoolLiteral(boolFalse); | |
| } else { | |
| this->writeExpression(*b.right(), Precedence::kTernary); | |
| } | |
| if (Precedence::kTernary >= parentPrecedence) { | |
| this->write(")"); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeTernaryExpression(const TernaryExpression& t, | |
| Precedence parentPrecedence) { | |
| if (Precedence::kTernary >= parentPrecedence) { | |
| this->write("("); | |
| } | |
| this->writeExpression(*t.test(), Precedence::kTernary); | |
| this->write(" ? "); | |
| this->writeExpression(*t.ifTrue(), Precedence::kTernary); | |
| this->write(" : "); | |
| this->writeExpression(*t.ifFalse(), Precedence::kTernary); | |
| if (Precedence::kTernary >= parentPrecedence) { | |
| this->write(")"); | |
| } | |
| } | |
| void GLSLCodeGenerator::writePrefixExpression(const PrefixExpression& p, | |
| Precedence parentPrecedence) { | |
| if (Precedence::kPrefix >= parentPrecedence) { | |
| this->write("("); | |
| } | |
| this->write(p.getOperator().operatorName()); | |
| this->writeExpression(*p.operand(), Precedence::kPrefix); | |
| if (Precedence::kPrefix >= parentPrecedence) { | |
| this->write(")"); | |
| } | |
| } | |
| void GLSLCodeGenerator::writePostfixExpression(const PostfixExpression& p, | |
| Precedence parentPrecedence) { | |
| if (Precedence::kPostfix >= parentPrecedence) { | |
| this->write("("); | |
| } | |
| this->writeExpression(*p.operand(), Precedence::kPostfix); | |
| this->write(p.getOperator().operatorName()); | |
| if (Precedence::kPostfix >= parentPrecedence) { | |
| this->write(")"); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeBoolLiteral(const BoolLiteral& b) { | |
| this->write(b.value() ? "true" : "false"); | |
| } | |
| void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) { | |
| const Type& type = i.type(); | |
| if (type == *fContext.fTypes.fUInt) { | |
| this->write(to_string(i.value() & 0xffffffff) + "u"); | |
| } else if (type == *fContext.fTypes.fUShort) { | |
| this->write(to_string(i.value() & 0xffff) + "u"); | |
| } else { | |
| this->write(to_string(i.value())); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeFloatLiteral(const FloatLiteral& f) { | |
| this->write(to_string(f.value())); | |
| } | |
| void GLSLCodeGenerator::writeSetting(const Setting& s) { | |
| SK_ABORT("internal error; setting was not folded to a constant during compilation\n"); | |
| } | |
| void GLSLCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f) { | |
| this->writeTypePrecision(f.returnType()); | |
| this->writeType(f.returnType()); | |
| this->write(" " + f.mangledName() + "("); | |
| const char* separator = ""; | |
| for (const auto& param : f.parameters()) { | |
| // This is a workaround for our test files. They use the runtime effect signature, so main | |
| // takes a coords parameter. The IR generator tags those with a builtin ID (sk_FragCoord), | |
| // and we omit them from the declaration here, so the function is valid GLSL. | |
| if (f.isMain() && param->modifiers().fLayout.fBuiltin != -1) { | |
| continue; | |
| } | |
| this->write(separator); | |
| separator = ", "; | |
| this->writeModifiers(param->modifiers(), false); | |
| std::vector<int> sizes; | |
| const Type* type = ¶m->type(); | |
| if (type->isArray()) { | |
| sizes.push_back(type->columns()); | |
| type = &type->componentType(); | |
| } | |
| this->writeTypePrecision(*type); | |
| this->writeType(*type); | |
| this->write(" " + param->name()); | |
| for (int s : sizes) { | |
| if (s == Type::kUnsizedArray) { | |
| this->write("[]"); | |
| } else { | |
| this->write("[" + to_string(s) + "]"); | |
| } | |
| } | |
| } | |
| this->write(")"); | |
| } | |
| void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) { | |
| fSetupFragPosition = false; | |
| fSetupFragCoordWorkaround = false; | |
| this->writeFunctionDeclaration(f.declaration()); | |
| this->writeLine(" {"); | |
| fIndentation++; | |
| fFunctionHeader.clear(); | |
| OutputStream* oldOut = fOut; | |
| StringStream buffer; | |
| fOut = &buffer; | |
| for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) { | |
| if (!stmt->isEmpty()) { | |
| this->writeStatement(*stmt); | |
| this->finishLine(); | |
| } | |
| } | |
| fIndentation--; | |
| this->writeLine("}"); | |
| fOut = oldOut; | |
| this->write(fFunctionHeader); | |
| this->write(buffer.str()); | |
| } | |
| void GLSLCodeGenerator::writeFunctionPrototype(const FunctionPrototype& f) { | |
| this->writeFunctionDeclaration(f.declaration()); | |
| this->writeLine(";"); | |
| } | |
| void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers, | |
| bool globalContext) { | |
| if (modifiers.fFlags & Modifiers::kFlat_Flag) { | |
| this->write("flat "); | |
| } | |
| if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) { | |
| this->write("noperspective "); | |
| } | |
| String layout = modifiers.fLayout.description(); | |
| if (layout.size()) { | |
| 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 && | |
| this->caps().generation() < GrGLSLGeneration::k130_GrGLSLGeneration) { | |
| this->write(fProgram.fConfig->fKind == ProgramKind::kVertex ? "attribute " | |
| : "varying "); | |
| } else { | |
| this->write("in "); | |
| } | |
| } else if (modifiers.fFlags & Modifiers::kOut_Flag) { | |
| if (globalContext && | |
| this->caps().generation() < GrGLSLGeneration::k130_GrGLSLGeneration) { | |
| 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 "); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) { | |
| if (intf.typeName() == "sk_PerVertex") { | |
| return; | |
| } | |
| this->writeModifiers(intf.variable().modifiers(), true); | |
| this->writeLine(intf.typeName() + " {"); | |
| fIndentation++; | |
| const Type* structType = &intf.variable().type(); | |
| if (structType->isArray()) { | |
| structType = &structType->componentType(); | |
| } | |
| for (const auto& f : structType->fields()) { | |
| this->writeModifiers(f.fModifiers, false); | |
| this->writeTypePrecision(*f.fType); | |
| this->writeType(*f.fType); | |
| this->writeLine(" " + f.fName + ";"); | |
| } | |
| fIndentation--; | |
| this->write("}"); | |
| if (intf.instanceName().size()) { | |
| this->write(" "); | |
| this->write(intf.instanceName()); | |
| if (intf.arraySize() > 0) { | |
| this->write("["); | |
| this->write(to_string(intf.arraySize())); | |
| this->write("]"); | |
| } else if (intf.arraySize() == Type::kUnsizedArray){ | |
| this->write("[]"); | |
| } | |
| } | |
| this->writeLine(";"); | |
| } | |
| void GLSLCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) { | |
| this->writeExpression(value, Precedence::kTopLevel); | |
| } | |
| const char* GLSLCodeGenerator::getTypePrecision(const Type& type) { | |
| if (usesPrecisionModifiers()) { | |
| switch (type.typeKind()) { | |
| case Type::TypeKind::kScalar: | |
| if (type == *fContext.fTypes.fShort || type == *fContext.fTypes.fUShort) { | |
| if (fProgram.fConfig->fSettings.fForceHighPrecision || | |
| this->caps().incompleteShortIntPrecision()) { | |
| return "highp "; | |
| } | |
| return "mediump "; | |
| } | |
| if (type == *fContext.fTypes.fHalf) { | |
| return fProgram.fConfig->fSettings.fForceHighPrecision ? "highp " : "mediump "; | |
| } | |
| if (type == *fContext.fTypes.fFloat || type == *fContext.fTypes.fInt || | |
| type == *fContext.fTypes.fUInt) { | |
| return "highp "; | |
| } | |
| return ""; | |
| case Type::TypeKind::kVector: // fall through | |
| case Type::TypeKind::kMatrix: | |
| case Type::TypeKind::kArray: | |
| return this->getTypePrecision(type.componentType()); | |
| default: | |
| break; | |
| } | |
| } | |
| return ""; | |
| } | |
| void GLSLCodeGenerator::writeTypePrecision(const Type& type) { | |
| this->write(this->getTypePrecision(type)); | |
| } | |
| void GLSLCodeGenerator::writeVarDeclaration(const VarDeclaration& var, bool global) { | |
| this->writeModifiers(var.var().modifiers(), global); | |
| this->writeTypePrecision(var.baseType()); | |
| this->writeType(var.baseType()); | |
| this->write(" "); | |
| this->write(var.var().name()); | |
| if (var.arraySize() > 0) { | |
| this->write("["); | |
| this->write(to_string(var.arraySize())); | |
| this->write("]"); | |
| } else if (var.arraySize() == Type::kUnsizedArray){ | |
| this->write("[]"); | |
| } | |
| if (var.value()) { | |
| this->write(" = "); | |
| this->writeVarInitializer(var.var(), *var.value()); | |
| } | |
| if (!fFoundExternalSamplerDecl && var.var().type() == *fContext.fTypes.fSamplerExternalOES) { | |
| if (this->caps().externalTextureExtensionString()) { | |
| this->writeExtension(this->caps().externalTextureExtensionString()); | |
| } | |
| if (this->caps().secondExternalTextureExtensionString()) { | |
| this->writeExtension(this->caps().secondExternalTextureExtensionString()); | |
| } | |
| fFoundExternalSamplerDecl = true; | |
| } | |
| if (!fFoundRectSamplerDecl && var.var().type() == *fContext.fTypes.fSampler2DRect) { | |
| fFoundRectSamplerDecl = true; | |
| } | |
| this->write(";"); | |
| } | |
| void GLSLCodeGenerator::writeStatement(const Statement& s) { | |
| switch (s.kind()) { | |
| case Statement::Kind::kBlock: | |
| this->writeBlock(s.as<Block>()); | |
| break; | |
| case Statement::Kind::kExpression: | |
| this->writeExpression(*s.as<ExpressionStatement>().expression(), Precedence::kTopLevel); | |
| this->write(";"); | |
| break; | |
| case Statement::Kind::kReturn: | |
| this->writeReturnStatement(s.as<ReturnStatement>()); | |
| break; | |
| case Statement::Kind::kVarDeclaration: | |
| this->writeVarDeclaration(s.as<VarDeclaration>(), false); | |
| break; | |
| case Statement::Kind::kIf: | |
| this->writeIfStatement(s.as<IfStatement>()); | |
| break; | |
| case Statement::Kind::kFor: | |
| this->writeForStatement(s.as<ForStatement>()); | |
| break; | |
| case Statement::Kind::kDo: | |
| this->writeDoStatement(s.as<DoStatement>()); | |
| break; | |
| case Statement::Kind::kSwitch: | |
| this->writeSwitchStatement(s.as<SwitchStatement>()); | |
| break; | |
| case Statement::Kind::kBreak: | |
| this->write("break;"); | |
| break; | |
| case Statement::Kind::kContinue: | |
| this->write("continue;"); | |
| break; | |
| case Statement::Kind::kDiscard: | |
| this->write("discard;"); | |
| break; | |
| case Statement::Kind::kInlineMarker: | |
| case Statement::Kind::kNop: | |
| this->write(";"); | |
| break; | |
| default: | |
| SkDEBUGFAILF("unsupported statement: %s", s.description().c_str()); | |
| break; | |
| } | |
| } | |
| void GLSLCodeGenerator::writeBlock(const Block& b) { | |
| // Write scope markers if this block is a scope, or if the block is empty (since we need to emit | |
| // something here to make the code valid). | |
| bool isScope = b.isScope() || b.isEmpty(); | |
| if (isScope) { | |
| this->writeLine("{"); | |
| fIndentation++; | |
| } | |
| for (const std::unique_ptr<Statement>& stmt : b.children()) { | |
| if (!stmt->isEmpty()) { | |
| this->writeStatement(*stmt); | |
| this->finishLine(); | |
| } | |
| } | |
| if (isScope) { | |
| fIndentation--; | |
| this->write("}"); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeIfStatement(const IfStatement& stmt) { | |
| this->write("if ("); | |
| this->writeExpression(*stmt.test(), Precedence::kTopLevel); | |
| this->write(") "); | |
| this->writeStatement(*stmt.ifTrue()); | |
| if (stmt.ifFalse()) { | |
| this->write(" else "); | |
| this->writeStatement(*stmt.ifFalse()); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeForStatement(const ForStatement& f) { | |
| // Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started | |
| if (!f.initializer() && f.test() && !f.next()) { | |
| this->write("while ("); | |
| this->writeExpression(*f.test(), Precedence::kTopLevel); | |
| this->write(") "); | |
| this->writeStatement(*f.statement()); | |
| return; | |
| } | |
| this->write("for ("); | |
| if (f.initializer() && !f.initializer()->isEmpty()) { | |
| this->writeStatement(*f.initializer()); | |
| } else { | |
| this->write("; "); | |
| } | |
| if (f.test()) { | |
| if (this->caps().addAndTrueToLoopCondition()) { | |
| std::unique_ptr<Expression> and_true(new BinaryExpression( | |
| /*offset=*/-1, f.test()->clone(), Token::Kind::TK_LOGICALAND, | |
| BoolLiteral::Make(fContext, /*offset=*/-1, /*value=*/true), | |
| fContext.fTypes.fBool.get())); | |
| this->writeExpression(*and_true, Precedence::kTopLevel); | |
| } else { | |
| this->writeExpression(*f.test(), Precedence::kTopLevel); | |
| } | |
| } | |
| this->write("; "); | |
| if (f.next()) { | |
| this->writeExpression(*f.next(), Precedence::kTopLevel); | |
| } | |
| this->write(") "); | |
| this->writeStatement(*f.statement()); | |
| } | |
| void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) { | |
| if (!this->caps().rewriteDoWhileLoops()) { | |
| this->write("do "); | |
| this->writeStatement(*d.statement()); | |
| this->write(" while ("); | |
| this->writeExpression(*d.test(), Precedence::kTopLevel); | |
| this->write(");"); | |
| return; | |
| } | |
| // Otherwise, do the do while loop workaround, to rewrite loops of the form: | |
| // do { | |
| // CODE; | |
| // } while (CONDITION) | |
| // | |
| // to loops of the form | |
| // bool temp = false; | |
| // while (true) { | |
| // if (temp) { | |
| // if (!CONDITION) { | |
| // break; | |
| // } | |
| // } | |
| // temp = true; | |
| // CODE; | |
| // } | |
| String tmpVar = "_tmpLoopSeenOnce" + to_string(fVarCount++); | |
| this->write("bool "); | |
| this->write(tmpVar); | |
| this->writeLine(" = false;"); | |
| this->writeLine("while (true) {"); | |
| fIndentation++; | |
| this->write("if ("); | |
| this->write(tmpVar); | |
| this->writeLine(") {"); | |
| fIndentation++; | |
| this->write("if (!"); | |
| this->writeExpression(*d.test(), Precedence::kPrefix); | |
| this->writeLine(") {"); | |
| fIndentation++; | |
| this->writeLine("break;"); | |
| fIndentation--; | |
| this->writeLine("}"); | |
| fIndentation--; | |
| this->writeLine("}"); | |
| this->write(tmpVar); | |
| this->writeLine(" = true;"); | |
| this->writeStatement(*d.statement()); | |
| this->finishLine(); | |
| fIndentation--; | |
| this->write("}"); | |
| } | |
| void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) { | |
| this->write("switch ("); | |
| this->writeExpression(*s.value(), Precedence::kTopLevel); | |
| this->writeLine(") {"); | |
| fIndentation++; | |
| for (const std::unique_ptr<Statement>& stmt : s.cases()) { | |
| const SwitchCase& c = stmt->as<SwitchCase>(); | |
| if (c.value()) { | |
| this->write("case "); | |
| this->writeExpression(*c.value(), Precedence::kTopLevel); | |
| this->writeLine(":"); | |
| } else { | |
| this->writeLine("default:"); | |
| } | |
| if (!c.statement()->isEmpty()) { | |
| fIndentation++; | |
| this->writeStatement(*c.statement()); | |
| this->finishLine(); | |
| fIndentation--; | |
| } | |
| } | |
| fIndentation--; | |
| this->finishLine(); | |
| this->write("}"); | |
| } | |
| void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) { | |
| this->write("return"); | |
| if (r.expression()) { | |
| this->write(" "); | |
| this->writeExpression(*r.expression(), Precedence::kTopLevel); | |
| } | |
| this->write(";"); | |
| } | |
| void GLSLCodeGenerator::writeHeader() { | |
| if (this->caps().versionDeclString()) { | |
| this->write(this->caps().versionDeclString()); | |
| this->finishLine(); | |
| } | |
| } | |
| void GLSLCodeGenerator::writeProgramElement(const ProgramElement& e) { | |
| switch (e.kind()) { | |
| case ProgramElement::Kind::kExtension: | |
| this->writeExtension(e.as<Extension>().name()); | |
| break; | |
| case ProgramElement::Kind::kGlobalVar: { | |
| const VarDeclaration& decl = | |
| e.as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>(); | |
| int builtin = decl.var().modifiers().fLayout.fBuiltin; | |
| if (builtin == -1) { | |
| // normal var | |
| this->writeVarDeclaration(decl, true); | |
| this->finishLine(); | |
| } else if (builtin == SK_FRAGCOLOR_BUILTIN && | |
| this->caps().mustDeclareFragmentShaderOutput()) { | |
| if (fProgram.fConfig->fSettings.fFragColorIsInOut) { | |
| this->write("inout "); | |
| } else { | |
| this->write("out "); | |
| } | |
| if (usesPrecisionModifiers()) { | |
| this->write("mediump "); | |
| } | |
| this->writeLine("vec4 sk_FragColor;"); | |
| } | |
| break; | |
| } | |
| case ProgramElement::Kind::kInterfaceBlock: | |
| this->writeInterfaceBlock(e.as<InterfaceBlock>()); | |
| break; | |
| case ProgramElement::Kind::kFunction: | |
| this->writeFunction(e.as<FunctionDefinition>()); | |
| break; | |
| case ProgramElement::Kind::kFunctionPrototype: | |
| this->writeFunctionPrototype(e.as<FunctionPrototype>()); | |
| break; | |
| case ProgramElement::Kind::kModifiers: { | |
| const Modifiers& modifiers = e.as<ModifiersDeclaration>().modifiers(); | |
| if (!fFoundGSInvocations && modifiers.fLayout.fInvocations >= 0) { | |
| if (this->caps().gsInvocationsExtensionString()) { | |
| this->writeExtension(this->caps().gsInvocationsExtensionString()); | |
| } | |
| fFoundGSInvocations = true; | |
| } | |
| this->writeModifiers(modifiers, true); | |
| this->writeLine(";"); | |
| break; | |
| } | |
| case ProgramElement::Kind::kStructDefinition: | |
| this->writeStructDefinition(e.as<StructDefinition>()); | |
| break; | |
| default: | |
| SkDEBUGFAILF("unsupported program element %s\n", e.description().c_str()); | |
| break; | |
| } | |
| } | |
| void GLSLCodeGenerator::writeInputVars() { | |
| if (fProgram.fInputs.fUseFlipRTUniform) { | |
| const char* precision = usesPrecisionModifiers() ? "highp " : ""; | |
| fGlobals.writeText("uniform "); | |
| fGlobals.writeText(precision); | |
| fGlobals.writeText("vec2 " SKSL_RTFLIP_NAME ";\n"); | |
| } | |
| } | |
| bool GLSLCodeGenerator::generateCode() { | |
| this->writeHeader(); | |
| if (fProgram.fConfig->fKind == ProgramKind::kGeometry && | |
| this->caps().geometryShaderExtensionString()) { | |
| this->writeExtension(this->caps().geometryShaderExtensionString()); | |
| } | |
| OutputStream* rawOut = fOut; | |
| StringStream body; | |
| fOut = &body; | |
| // Write all the program elements except for functions. | |
| for (const ProgramElement* e : fProgram.elements()) { | |
| if (!e->is<FunctionDefinition>()) { | |
| this->writeProgramElement(*e); | |
| } | |
| } | |
| // Write the functions last. | |
| // Why don't we write things in their original order? Because the Inliner likes to move function | |
| // bodies around. After inlining, code can inadvertently move upwards, above ProgramElements | |
| // that the code relies on. | |
| for (const ProgramElement* e : fProgram.elements()) { | |
| if (e->is<FunctionDefinition>()) { | |
| this->writeProgramElement(*e); | |
| } | |
| } | |
| fOut = rawOut; | |
| write_stringstream(fExtensions, *rawOut); | |
| this->writeInputVars(); | |
| write_stringstream(fGlobals, *rawOut); | |
| if (!this->caps().canUseFragCoord()) { | |
| Layout layout; | |
| switch (fProgram.fConfig->fKind) { | |
| case ProgramKind::kVertex: { | |
| Modifiers modifiers(layout, Modifiers::kOut_Flag); | |
| this->writeModifiers(modifiers, true); | |
| if (this->usesPrecisionModifiers()) { | |
| this->write("highp "); | |
| } | |
| this->write("vec4 sk_FragCoord_Workaround;\n"); | |
| break; | |
| } | |
| case ProgramKind::kFragment: { | |
| Modifiers modifiers(layout, Modifiers::kIn_Flag); | |
| this->writeModifiers(modifiers, true); | |
| if (this->usesPrecisionModifiers()) { | |
| this->write("highp "); | |
| } | |
| this->write("vec4 sk_FragCoord_Workaround;\n"); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| } | |
| if (this->usesPrecisionModifiers()) { | |
| const char* precision = | |
| fProgram.fConfig->fSettings.fForceHighPrecision ? "highp" : "mediump"; | |
| this->write(String::printf("precision %s float;\n", precision)); | |
| this->write(String::printf("precision %s sampler2D;\n", precision)); | |
| if (fFoundExternalSamplerDecl && !this->caps().noDefaultPrecisionForExternalSamplers()) { | |
| this->write(String::printf("precision %s samplerExternalOES;\n", precision)); | |
| } | |
| if (fFoundRectSamplerDecl) { | |
| this->write(String::printf("precision %s sampler2DRect;\n", precision)); | |
| } | |
| } | |
| write_stringstream(fExtraFunctions, *rawOut); | |
| write_stringstream(body, *rawOut); | |
| return 0 == fErrors.errorCount(); | |
| } | |
| } // namespace SkSL |