bcf35f86d5
This reverts commit 7833466da4
.
Reason for revert: Vulkan assertion failure
Original change's description:
> skslc can now be compiled with no Skia dependencies, in preparation for
> its eventual role in Skia's build process.
>
> Bug: skia:
> Change-Id: Iaa9933f4fc4a64bec60aa897c509a3513f457a78
> Reviewed-on: https://skia-review.googlesource.com/10282
> Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
> Reviewed-by: Ben Wagner <benjaminwagner@google.com>
>
TBR=egdaniel@google.com,benjaminwagner@google.com,ethannicholas@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
Change-Id: Ic64cac2395abb406116885ddd725f74a434c8c49
Reviewed-on: https://skia-review.googlesource.com/10758
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
866 lines
31 KiB
C++
866 lines
31 KiB
C++
/*
|
|
* 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 "SkSLGLSLCodeGenerator.h"
|
|
|
|
#include "string.h"
|
|
|
|
#include "GLSL.std.450.h"
|
|
|
|
#include "SkSLCompiler.h"
|
|
#include "ir/SkSLExpressionStatement.h"
|
|
#include "ir/SkSLExtension.h"
|
|
#include "ir/SkSLIndexExpression.h"
|
|
#include "ir/SkSLModifiersDeclaration.h"
|
|
#include "ir/SkSLVariableReference.h"
|
|
|
|
namespace SkSL {
|
|
|
|
void GLSLCodeGenerator::write(const char* s) {
|
|
if (s[0] == 0) {
|
|
return;
|
|
}
|
|
if (fAtLineStart) {
|
|
for (int i = 0; i < fIndentation; i++) {
|
|
fOut->writeText(" ");
|
|
}
|
|
}
|
|
fOut->writeText(s);
|
|
fAtLineStart = false;
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeLine(const char* s) {
|
|
this->write(s);
|
|
fOut->writeText("\n");
|
|
fAtLineStart = true;
|
|
}
|
|
|
|
void GLSLCodeGenerator::write(const SkString& s) {
|
|
this->write(s.c_str());
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeLine(const SkString& s) {
|
|
this->writeLine(s.c_str());
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeLine() {
|
|
this->writeLine("");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeExtension(const Extension& ext) {
|
|
this->writeLine("#extension " + ext.fName + " : enable");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeType(const Type& type) {
|
|
if (type.kind() == Type::kStruct_Kind) {
|
|
for (const Type* search : fWrittenStructs) {
|
|
if (*search == type) {
|
|
// already written
|
|
this->write(type.name());
|
|
return;
|
|
}
|
|
}
|
|
fWrittenStructs.push_back(&type);
|
|
this->writeLine("struct " + type.name() + " {");
|
|
fIndentation++;
|
|
for (const auto& f : type.fields()) {
|
|
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 + ";");
|
|
}
|
|
fIndentation--;
|
|
this->write("}");
|
|
} else {
|
|
this->write(type.name());
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence parentPrecedence) {
|
|
switch (expr.fKind) {
|
|
case Expression::kBinary_Kind:
|
|
this->writeBinaryExpression((BinaryExpression&) expr, parentPrecedence);
|
|
break;
|
|
case Expression::kBoolLiteral_Kind:
|
|
this->writeBoolLiteral((BoolLiteral&) expr);
|
|
break;
|
|
case Expression::kConstructor_Kind:
|
|
this->writeConstructor((Constructor&) expr);
|
|
break;
|
|
case Expression::kIntLiteral_Kind:
|
|
this->writeIntLiteral((IntLiteral&) expr);
|
|
break;
|
|
case Expression::kFieldAccess_Kind:
|
|
this->writeFieldAccess(((FieldAccess&) expr));
|
|
break;
|
|
case Expression::kFloatLiteral_Kind:
|
|
this->writeFloatLiteral(((FloatLiteral&) expr));
|
|
break;
|
|
case Expression::kFunctionCall_Kind:
|
|
this->writeFunctionCall((FunctionCall&) expr);
|
|
break;
|
|
case Expression::kPrefix_Kind:
|
|
this->writePrefixExpression((PrefixExpression&) expr, parentPrecedence);
|
|
break;
|
|
case Expression::kPostfix_Kind:
|
|
this->writePostfixExpression((PostfixExpression&) expr, parentPrecedence);
|
|
break;
|
|
case Expression::kSwizzle_Kind:
|
|
this->writeSwizzle((Swizzle&) expr);
|
|
break;
|
|
case Expression::kVariableReference_Kind:
|
|
this->writeVariableReference((VariableReference&) expr);
|
|
break;
|
|
case Expression::kTernary_Kind:
|
|
this->writeTernaryExpression((TernaryExpression&) expr, parentPrecedence);
|
|
break;
|
|
case Expression::kIndex_Kind:
|
|
this->writeIndexExpression((IndexExpression&) expr);
|
|
break;
|
|
default:
|
|
ABORT("unsupported expression: %s", expr.description().c_str());
|
|
}
|
|
}
|
|
|
|
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 ((tmpVar1 = abs(x)) < (tmpVar2 = y) ? tmpVar1 : tmpVar2) to avoid a
|
|
// Tegra3 compiler bug.
|
|
void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) {
|
|
ASSERT(!fProgram.fSettings.fCaps->canUseMinAndAbsTogether());
|
|
SkString tmpVar1 = "minAbsHackVar" + to_string(fVarCount++);
|
|
SkString tmpVar2 = "minAbsHackVar" + to_string(fVarCount++);
|
|
this->fFunctionHeader += " " + absExpr.fType.name() + " " + tmpVar1 + ";\n";
|
|
this->fFunctionHeader += " " + otherExpr.fType.name() + " " + tmpVar2 + ";\n";
|
|
this->write("((" + tmpVar1 + " = ");
|
|
this->writeExpression(absExpr, kTopLevel_Precedence);
|
|
this->write(") < (" + tmpVar2 + " = ");
|
|
this->writeExpression(otherExpr, kAssignment_Precedence);
|
|
this->write(") ? " + tmpVar1 + " : " + tmpVar2 + ")");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
|
|
if (!fProgram.fSettings.fCaps->canUseMinAndAbsTogether() && c.fFunction.fName == "min" &&
|
|
c.fFunction.fBuiltin) {
|
|
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])) {
|
|
// 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(*c.fArguments[1], *c.fArguments[0]);
|
|
return;
|
|
}
|
|
}
|
|
if (fProgram.fSettings.fCaps->mustForceNegatedAtanParamToFloat() &&
|
|
c.fFunction.fName == "atan" &&
|
|
c.fFunction.fBuiltin && c.fArguments.size() == 2 &&
|
|
c.fArguments[1]->fKind == Expression::kPrefix_Kind) {
|
|
const PrefixExpression& p = (PrefixExpression&) *c.fArguments[1];
|
|
if (p.fOperator == Token::MINUS) {
|
|
this->write("atan(");
|
|
this->writeExpression(*c.fArguments[0], kSequence_Precedence);
|
|
this->write(", -1.0 * ");
|
|
this->writeExpression(*p.fOperand, kMultiplicative_Precedence);
|
|
this->write(")");
|
|
return;
|
|
}
|
|
}
|
|
if (!fFoundDerivatives && (c.fFunction.fName == "dFdx" || c.fFunction.fName == "dFdy") &&
|
|
c.fFunction.fBuiltin && fProgram.fSettings.fCaps->shaderDerivativeExtensionString()) {
|
|
ASSERT(fProgram.fSettings.fCaps->shaderDerivativeSupport());
|
|
fHeader.writeText("#extension ");
|
|
fHeader.writeText(fProgram.fSettings.fCaps->shaderDerivativeExtensionString());
|
|
fHeader.writeText(" : require\n");
|
|
fFoundDerivatives = true;
|
|
}
|
|
if (c.fFunction.fName == "texture" && c.fFunction.fBuiltin) {
|
|
const char* dim = "";
|
|
bool proj = false;
|
|
switch (c.fArguments[0]->fType.dimensions()) {
|
|
case SpvDim1D:
|
|
dim = "1D";
|
|
if (c.fArguments[1]->fType == *fContext.fFloat_Type) {
|
|
proj = false;
|
|
} else {
|
|
ASSERT(c.fArguments[1]->fType == *fContext.fVec2_Type);
|
|
proj = true;
|
|
}
|
|
break;
|
|
case SpvDim2D:
|
|
dim = "2D";
|
|
if (c.fArguments[1]->fType == *fContext.fVec2_Type) {
|
|
proj = false;
|
|
} else {
|
|
ASSERT(c.fArguments[1]->fType == *fContext.fVec3_Type);
|
|
proj = true;
|
|
}
|
|
break;
|
|
case SpvDim3D:
|
|
dim = "3D";
|
|
if (c.fArguments[1]->fType == *fContext.fVec3_Type) {
|
|
proj = false;
|
|
} else {
|
|
ASSERT(c.fArguments[1]->fType == *fContext.fVec4_Type);
|
|
proj = true;
|
|
}
|
|
break;
|
|
case SpvDimCube:
|
|
dim = "Cube";
|
|
proj = false;
|
|
break;
|
|
case SpvDimRect:
|
|
dim = "Rect";
|
|
proj = false;
|
|
break;
|
|
case SpvDimBuffer:
|
|
ASSERT(false); // doesn't exist
|
|
dim = "Buffer";
|
|
proj = false;
|
|
break;
|
|
case SpvDimSubpassData:
|
|
ASSERT(false); // doesn't exist
|
|
dim = "SubpassData";
|
|
proj = false;
|
|
break;
|
|
}
|
|
this->write("texture");
|
|
if (fProgram.fSettings.fCaps->generation() < k130_GrGLSLGeneration) {
|
|
this->write(dim);
|
|
}
|
|
if (proj) {
|
|
this->write("Proj");
|
|
}
|
|
|
|
} else {
|
|
this->write(c.fFunction.fName);
|
|
}
|
|
this->write("(");
|
|
const char* separator = "";
|
|
for (const auto& arg : c.fArguments) {
|
|
this->write(separator);
|
|
separator = ", ";
|
|
this->writeExpression(*arg, kSequence_Precedence);
|
|
}
|
|
this->write(")");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeConstructor(const Constructor& c) {
|
|
this->write(c.fType.name() + "(");
|
|
const char* separator = "";
|
|
for (const auto& arg : c.fArguments) {
|
|
this->write(separator);
|
|
separator = ", ";
|
|
this->writeExpression(*arg, kSequence_Precedence);
|
|
}
|
|
this->write(")");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeFragCoord() {
|
|
// We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers
|
|
// to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the
|
|
// declaration varies in earlier GLSL specs. So it is simpler to omit it.
|
|
if (!fProgram.fSettings.fFlipY) {
|
|
this->write("gl_FragCoord");
|
|
} else if (const char* extension =
|
|
fProgram.fSettings.fCaps->fragCoordConventionsExtensionString()) {
|
|
if (!fSetupFragPositionGlobal) {
|
|
if (fProgram.fSettings.fCaps->generation() < k150_GrGLSLGeneration) {
|
|
fHeader.writeText("#extension ");
|
|
fHeader.writeText(extension);
|
|
fHeader.writeText(" : require\n");
|
|
}
|
|
fHeader.writeText("layout(origin_upper_left) in vec4 gl_FragCoord;\n");
|
|
fSetupFragPositionGlobal = true;
|
|
}
|
|
this->write("gl_FragCoord");
|
|
} else {
|
|
if (!fSetupFragPositionGlobal) {
|
|
// The Adreno compiler seems to be very touchy about access to "gl_FragCoord".
|
|
// Accessing glFragCoord.zw can cause a program to fail to link. Additionally,
|
|
// depending on the surrounding code, accessing .xy with a uniform involved can
|
|
// do the same thing. Copying gl_FragCoord.xy into a temp vec2 beforehand
|
|
// (and only accessing .xy) seems to "fix" things.
|
|
const char* precision = fProgram.fSettings.fCaps->usesPrecisionModifiers() ? "highp "
|
|
: "";
|
|
fHeader.writeText("uniform ");
|
|
fHeader.writeText(precision);
|
|
fHeader.writeText("float " SKSL_RTHEIGHT_NAME ";\n");
|
|
fSetupFragPositionGlobal = true;
|
|
}
|
|
if (!fSetupFragPositionLocal) {
|
|
const char* precision = fProgram.fSettings.fCaps->usesPrecisionModifiers() ? "highp "
|
|
: "";
|
|
fFunctionHeader += precision;
|
|
fFunctionHeader += " vec2 _sktmpCoord = gl_FragCoord.xy;\n";
|
|
fFunctionHeader += precision;
|
|
fFunctionHeader += " vec4 sk_FragCoord = vec4(_sktmpCoord.x, " SKSL_RTHEIGHT_NAME
|
|
" - _sktmpCoord.y, 1.0, 1.0);\n";
|
|
fSetupFragPositionLocal = true;
|
|
}
|
|
this->write("sk_FragCoord");
|
|
}
|
|
}
|
|
|
|
|
|
void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
|
|
switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
|
|
case SK_FRAGCOLOR_BUILTIN:
|
|
if (fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput()) {
|
|
this->write("sk_FragColor");
|
|
} else {
|
|
this->write("gl_FragColor");
|
|
}
|
|
break;
|
|
case SK_FRAGCOORD_BUILTIN:
|
|
this->writeFragCoord();
|
|
break;
|
|
case SK_VERTEXID_BUILTIN:
|
|
this->write("gl_VertexID");
|
|
break;
|
|
case SK_CLIPDISTANCE_BUILTIN:
|
|
this->write("gl_ClipDistance");
|
|
break;
|
|
case SK_IN_BUILTIN:
|
|
this->write("gl_in");
|
|
break;
|
|
case SK_INVOCATIONID_BUILTIN:
|
|
this->write("gl_InvocationID");
|
|
break;
|
|
default:
|
|
this->write(ref.fVariable.fName);
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
|
|
this->writeExpression(*expr.fBase, kPostfix_Precedence);
|
|
this->write("[");
|
|
this->writeExpression(*expr.fIndex, kTopLevel_Precedence);
|
|
this->write("]");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeFieldAccess(const FieldAccess& f) {
|
|
if (f.fOwnerKind == FieldAccess::kDefault_OwnerKind) {
|
|
this->writeExpression(*f.fBase, kPostfix_Precedence);
|
|
this->write(".");
|
|
}
|
|
switch (f.fBase->fType.fields()[f.fFieldIndex].fModifiers.fLayout.fBuiltin) {
|
|
case SK_CLIPDISTANCE_BUILTIN:
|
|
this->write("gl_ClipDistance");
|
|
break;
|
|
default:
|
|
this->write(f.fBase->fType.fields()[f.fFieldIndex].fName);
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
|
|
this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
|
|
this->write(".");
|
|
for (int c : swizzle.fComponents) {
|
|
this->write(&("x\0y\0z\0w\0"[c * 2]));
|
|
}
|
|
}
|
|
|
|
static GLSLCodeGenerator::Precedence get_binary_precedence(Token::Kind op) {
|
|
switch (op) {
|
|
case Token::STAR: // fall through
|
|
case Token::SLASH: // fall through
|
|
case Token::PERCENT: return GLSLCodeGenerator::kMultiplicative_Precedence;
|
|
case Token::PLUS: // fall through
|
|
case Token::MINUS: return GLSLCodeGenerator::kAdditive_Precedence;
|
|
case Token::SHL: // fall through
|
|
case Token::SHR: return GLSLCodeGenerator::kShift_Precedence;
|
|
case Token::LT: // fall through
|
|
case Token::GT: // fall through
|
|
case Token::LTEQ: // fall through
|
|
case Token::GTEQ: return GLSLCodeGenerator::kRelational_Precedence;
|
|
case Token::EQEQ: // fall through
|
|
case Token::NEQ: return GLSLCodeGenerator::kEquality_Precedence;
|
|
case Token::BITWISEAND: return GLSLCodeGenerator::kBitwiseAnd_Precedence;
|
|
case Token::BITWISEXOR: return GLSLCodeGenerator::kBitwiseXor_Precedence;
|
|
case Token::BITWISEOR: return GLSLCodeGenerator::kBitwiseOr_Precedence;
|
|
case Token::LOGICALAND: return GLSLCodeGenerator::kLogicalAnd_Precedence;
|
|
case Token::LOGICALXOR: return GLSLCodeGenerator::kLogicalXor_Precedence;
|
|
case Token::LOGICALOR: return GLSLCodeGenerator::kLogicalOr_Precedence;
|
|
case Token::EQ: // fall through
|
|
case Token::PLUSEQ: // fall through
|
|
case Token::MINUSEQ: // fall through
|
|
case Token::STAREQ: // fall through
|
|
case Token::SLASHEQ: // fall through
|
|
case Token::PERCENTEQ: // fall through
|
|
case Token::SHLEQ: // fall through
|
|
case Token::SHREQ: // fall through
|
|
case Token::LOGICALANDEQ: // fall through
|
|
case Token::LOGICALXOREQ: // fall through
|
|
case Token::LOGICALOREQ: // fall through
|
|
case Token::BITWISEANDEQ: // fall through
|
|
case Token::BITWISEXOREQ: // fall through
|
|
case Token::BITWISEOREQ: return GLSLCodeGenerator::kAssignment_Precedence;
|
|
default: ABORT("unsupported binary operator");
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
|
|
Precedence parentPrecedence) {
|
|
Precedence precedence = get_binary_precedence(b.fOperator);
|
|
if (precedence >= parentPrecedence) {
|
|
this->write("(");
|
|
}
|
|
this->writeExpression(*b.fLeft, precedence);
|
|
this->write(" " + Token::OperatorName(b.fOperator) + " ");
|
|
this->writeExpression(*b.fRight, precedence);
|
|
if (precedence >= parentPrecedence) {
|
|
this->write(")");
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
|
|
Precedence parentPrecedence) {
|
|
if (kTernary_Precedence >= parentPrecedence) {
|
|
this->write("(");
|
|
}
|
|
this->writeExpression(*t.fTest, kTernary_Precedence);
|
|
this->write(" ? ");
|
|
this->writeExpression(*t.fIfTrue, kTernary_Precedence);
|
|
this->write(" : ");
|
|
this->writeExpression(*t.fIfFalse, kTernary_Precedence);
|
|
if (kTernary_Precedence >= parentPrecedence) {
|
|
this->write(")");
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writePrefixExpression(const PrefixExpression& p,
|
|
Precedence parentPrecedence) {
|
|
if (kPrefix_Precedence >= parentPrecedence) {
|
|
this->write("(");
|
|
}
|
|
this->write(Token::OperatorName(p.fOperator));
|
|
this->writeExpression(*p.fOperand, kPrefix_Precedence);
|
|
if (kPrefix_Precedence >= parentPrecedence) {
|
|
this->write(")");
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writePostfixExpression(const PostfixExpression& p,
|
|
Precedence parentPrecedence) {
|
|
if (kPostfix_Precedence >= parentPrecedence) {
|
|
this->write("(");
|
|
}
|
|
this->writeExpression(*p.fOperand, kPostfix_Precedence);
|
|
this->write(Token::OperatorName(p.fOperator));
|
|
if (kPostfix_Precedence >= parentPrecedence) {
|
|
this->write(")");
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
|
|
this->write(b.fValue ? "true" : "false");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) {
|
|
if (i.fType == *fContext.fUInt_Type) {
|
|
this->write(to_string(i.fValue & 0xffffffff) + "u");
|
|
} else {
|
|
this->write(to_string((int32_t) i.fValue));
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
|
|
this->write(to_string(f.fValue));
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
|
|
this->writeType(f.fDeclaration.fReturnType);
|
|
this->write(" " + f.fDeclaration.fName + "(");
|
|
const char* separator = "";
|
|
for (const auto& param : f.fDeclaration.fParameters) {
|
|
this->write(separator);
|
|
separator = ", ";
|
|
this->writeModifiers(param->fModifiers, false);
|
|
std::vector<int> sizes;
|
|
const Type* type = ¶m->fType;
|
|
while (type->kind() == Type::kArray_Kind) {
|
|
sizes.push_back(type->columns());
|
|
type = &type->componentType();
|
|
}
|
|
this->writeType(*type);
|
|
this->write(" " + param->fName);
|
|
for (int s : sizes) {
|
|
if (s <= 0) {
|
|
this->write("[]");
|
|
} else {
|
|
this->write("[" + to_string(s) + "]");
|
|
}
|
|
}
|
|
}
|
|
this->writeLine(") {");
|
|
|
|
fFunctionHeader = "";
|
|
SkWStream* oldOut = fOut;
|
|
SkDynamicMemoryWStream buffer;
|
|
fOut = &buffer;
|
|
fIndentation++;
|
|
for (const auto& s : f.fBody->fStatements) {
|
|
this->writeStatement(*s);
|
|
this->writeLine();
|
|
}
|
|
fIndentation--;
|
|
this->writeLine("}");
|
|
|
|
fOut = oldOut;
|
|
this->write(fFunctionHeader);
|
|
sk_sp<SkData> data(buffer.detachAsData());
|
|
this->write(SkString((const char*) data->data(), data->size()));
|
|
}
|
|
|
|
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 ");
|
|
}
|
|
SkString layout = modifiers.fLayout.description();
|
|
if (layout.size()) {
|
|
this->write(layout + " ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kReadOnly_Flag) {
|
|
this->write("readonly ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kWriteOnly_Flag) {
|
|
this->write("writeonly ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kCoherent_Flag) {
|
|
this->write("coherent ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kVolatile_Flag) {
|
|
this->write("volatile ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kRestrict_Flag) {
|
|
this->write("restrict ");
|
|
}
|
|
if ((modifiers.fFlags & Modifiers::kIn_Flag) &&
|
|
(modifiers.fFlags & Modifiers::kOut_Flag)) {
|
|
this->write("inout ");
|
|
} else if (modifiers.fFlags & Modifiers::kIn_Flag) {
|
|
if (globalContext &&
|
|
fProgram.fSettings.fCaps->generation() < GrGLSLGeneration::k130_GrGLSLGeneration) {
|
|
this->write(fProgramKind == Program::kVertex_Kind ? "attribute "
|
|
: "varying ");
|
|
} else {
|
|
this->write("in ");
|
|
}
|
|
} else if (modifiers.fFlags & Modifiers::kOut_Flag) {
|
|
if (globalContext &&
|
|
fProgram.fSettings.fCaps->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 ");
|
|
}
|
|
if (fProgram.fSettings.fCaps->usesPrecisionModifiers()) {
|
|
if (modifiers.fFlags & Modifiers::kLowp_Flag) {
|
|
this->write("lowp ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kMediump_Flag) {
|
|
this->write("mediump ");
|
|
}
|
|
if (modifiers.fFlags & Modifiers::kHighp_Flag) {
|
|
this->write("highp ");
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
|
|
if (intf.fTypeName == "sk_PerVertex") {
|
|
return;
|
|
}
|
|
this->writeModifiers(intf.fVariable.fModifiers, true);
|
|
this->writeLine(intf.fTypeName + " {");
|
|
fIndentation++;
|
|
const Type* structType = &intf.fVariable.fType;
|
|
while (structType->kind() == Type::kArray_Kind) {
|
|
structType = &structType->componentType();
|
|
}
|
|
for (const auto& f : structType->fields()) {
|
|
this->writeModifiers(f.fModifiers, false);
|
|
this->writeType(*f.fType);
|
|
this->writeLine(" " + f.fName + ";");
|
|
}
|
|
fIndentation--;
|
|
this->write("}");
|
|
if (intf.fInstanceName.size()) {
|
|
this->write(" ");
|
|
this->write(intf.fInstanceName);
|
|
for (const auto& size : intf.fSizes) {
|
|
this->write("[");
|
|
if (size) {
|
|
this->writeExpression(*size, kTopLevel_Precedence);
|
|
}
|
|
this->write("]");
|
|
}
|
|
}
|
|
this->writeLine(";");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
|
|
ASSERT(decl.fVars.size() > 0);
|
|
this->writeModifiers(decl.fVars[0].fVar->fModifiers, global);
|
|
this->writeType(decl.fBaseType);
|
|
SkString separator(" ");
|
|
for (const auto& var : decl.fVars) {
|
|
ASSERT(var.fVar->fModifiers == decl.fVars[0].fVar->fModifiers);
|
|
this->write(separator);
|
|
separator = SkString(", ");
|
|
this->write(var.fVar->fName);
|
|
for (const auto& size : var.fSizes) {
|
|
this->write("[");
|
|
if (size) {
|
|
this->writeExpression(*size, kTopLevel_Precedence);
|
|
}
|
|
this->write("]");
|
|
}
|
|
if (var.fValue) {
|
|
this->write(" = ");
|
|
this->writeExpression(*var.fValue, kTopLevel_Precedence);
|
|
}
|
|
if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
|
|
if (fProgram.fSettings.fCaps->imageLoadStoreExtensionString()) {
|
|
fHeader.writeText("#extension ");
|
|
fHeader.writeText(fProgram.fSettings.fCaps->imageLoadStoreExtensionString());
|
|
fHeader.writeText(" : require\n");
|
|
}
|
|
fFoundImageDecl = true;
|
|
}
|
|
}
|
|
this->write(";");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeStatement(const Statement& s) {
|
|
switch (s.fKind) {
|
|
case Statement::kBlock_Kind:
|
|
this->writeBlock((Block&) s);
|
|
break;
|
|
case Statement::kExpression_Kind:
|
|
this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
|
|
this->write(";");
|
|
break;
|
|
case Statement::kReturn_Kind:
|
|
this->writeReturnStatement((ReturnStatement&) s);
|
|
break;
|
|
case Statement::kVarDeclarations_Kind:
|
|
this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
|
|
break;
|
|
case Statement::kIf_Kind:
|
|
this->writeIfStatement((IfStatement&) s);
|
|
break;
|
|
case Statement::kFor_Kind:
|
|
this->writeForStatement((ForStatement&) s);
|
|
break;
|
|
case Statement::kWhile_Kind:
|
|
this->writeWhileStatement((WhileStatement&) s);
|
|
break;
|
|
case Statement::kDo_Kind:
|
|
this->writeDoStatement((DoStatement&) s);
|
|
break;
|
|
case Statement::kSwitch_Kind:
|
|
this->writeSwitchStatement((SwitchStatement&) s);
|
|
break;
|
|
case Statement::kBreak_Kind:
|
|
this->write("break;");
|
|
break;
|
|
case Statement::kContinue_Kind:
|
|
this->write("continue;");
|
|
break;
|
|
case Statement::kDiscard_Kind:
|
|
this->write("discard;");
|
|
break;
|
|
default:
|
|
ABORT("unsupported statement: %s", s.description().c_str());
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeBlock(const Block& b) {
|
|
this->writeLine("{");
|
|
fIndentation++;
|
|
for (const auto& s : b.fStatements) {
|
|
this->writeStatement(*s);
|
|
this->writeLine();
|
|
}
|
|
fIndentation--;
|
|
this->write("}");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeIfStatement(const IfStatement& stmt) {
|
|
this->write("if (");
|
|
this->writeExpression(*stmt.fTest, kTopLevel_Precedence);
|
|
this->write(") ");
|
|
this->writeStatement(*stmt.fIfTrue);
|
|
if (stmt.fIfFalse) {
|
|
this->write(" else ");
|
|
this->writeStatement(*stmt.fIfFalse);
|
|
}
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeForStatement(const ForStatement& f) {
|
|
this->write("for (");
|
|
if (f.fInitializer) {
|
|
this->writeStatement(*f.fInitializer);
|
|
} else {
|
|
this->write("; ");
|
|
}
|
|
if (f.fTest) {
|
|
this->writeExpression(*f.fTest, kTopLevel_Precedence);
|
|
}
|
|
this->write("; ");
|
|
if (f.fNext) {
|
|
this->writeExpression(*f.fNext, kTopLevel_Precedence);
|
|
}
|
|
this->write(") ");
|
|
this->writeStatement(*f.fStatement);
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeWhileStatement(const WhileStatement& w) {
|
|
this->write("while (");
|
|
this->writeExpression(*w.fTest, kTopLevel_Precedence);
|
|
this->write(") ");
|
|
this->writeStatement(*w.fStatement);
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) {
|
|
this->write("do ");
|
|
this->writeStatement(*d.fStatement);
|
|
this->write(" while (");
|
|
this->writeExpression(*d.fTest, kTopLevel_Precedence);
|
|
this->write(");");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
|
|
this->write("switch (");
|
|
this->writeExpression(*s.fValue, kTopLevel_Precedence);
|
|
this->writeLine(") {");
|
|
fIndentation++;
|
|
for (const auto& c : s.fCases) {
|
|
if (c->fValue) {
|
|
this->write("case ");
|
|
this->writeExpression(*c->fValue, kTopLevel_Precedence);
|
|
this->writeLine(":");
|
|
} else {
|
|
this->writeLine("default:");
|
|
}
|
|
fIndentation++;
|
|
for (const auto& stmt : c->fStatements) {
|
|
this->writeStatement(*stmt);
|
|
this->writeLine();
|
|
}
|
|
fIndentation--;
|
|
}
|
|
fIndentation--;
|
|
this->write("}");
|
|
}
|
|
|
|
void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
|
|
this->write("return");
|
|
if (r.fExpression) {
|
|
this->write(" ");
|
|
this->writeExpression(*r.fExpression, kTopLevel_Precedence);
|
|
}
|
|
this->write(";");
|
|
}
|
|
|
|
bool GLSLCodeGenerator::generateCode() {
|
|
SkWStream* rawOut = fOut;
|
|
fOut = &fHeader;
|
|
fProgramKind = fProgram.fKind;
|
|
this->write(fProgram.fSettings.fCaps->versionDeclString());
|
|
this->writeLine();
|
|
for (const auto& e : fProgram.fElements) {
|
|
if (e->fKind == ProgramElement::kExtension_Kind) {
|
|
this->writeExtension((Extension&) *e);
|
|
}
|
|
}
|
|
SkDynamicMemoryWStream body;
|
|
fOut = &body;
|
|
if (fProgram.fSettings.fCaps->usesPrecisionModifiers()) {
|
|
this->write("precision ");
|
|
switch (fProgram.fDefaultPrecision) {
|
|
case Modifiers::kLowp_Flag:
|
|
this->write("lowp");
|
|
break;
|
|
case Modifiers::kMediump_Flag:
|
|
this->write("mediump");
|
|
break;
|
|
case Modifiers::kHighp_Flag:
|
|
this->write("highp");
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
this->write("<error>");
|
|
}
|
|
this->writeLine(" float;");
|
|
}
|
|
for (const auto& e : fProgram.fElements) {
|
|
switch (e->fKind) {
|
|
case ProgramElement::kExtension_Kind:
|
|
break;
|
|
case ProgramElement::kVar_Kind: {
|
|
VarDeclarations& decl = (VarDeclarations&) *e;
|
|
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 &&
|
|
fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput()) {
|
|
this->write("out ");
|
|
if (fProgram.fSettings.fCaps->usesPrecisionModifiers()) {
|
|
this->write("mediump ");
|
|
}
|
|
this->writeLine("vec4 sk_FragColor;");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ProgramElement::kInterfaceBlock_Kind:
|
|
this->writeInterfaceBlock((InterfaceBlock&) *e);
|
|
break;
|
|
case ProgramElement::kFunction_Kind:
|
|
this->writeFunction((FunctionDefinition&) *e);
|
|
break;
|
|
case ProgramElement::kModifiers_Kind:
|
|
this->writeModifiers(((ModifiersDeclaration&) *e).fModifiers, true);
|
|
this->writeLine(";");
|
|
break;
|
|
default:
|
|
printf("%s\n", e->description().c_str());
|
|
ABORT("unsupported program element");
|
|
}
|
|
}
|
|
fOut = nullptr;
|
|
|
|
write_data(*fHeader.detachAsData(), *rawOut);
|
|
write_data(*body.detachAsData(), *rawOut);
|
|
return true;
|
|
}
|
|
|
|
}
|