/* * 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/SkSLCPPCodeGenerator.h" #include "src/sksl/SkSLAnalysis.h" #include "src/sksl/SkSLCPPUniformCTypes.h" #include "src/sksl/SkSLCompiler.h" #include "src/sksl/codegen/SkSLHCodeGenerator.h" #include "src/sksl/ir/SkSLEnum.h" #include #if defined(SKSL_STANDALONE) || GR_TEST_UTILS namespace SkSL { static bool needs_uniform_var(const Variable& var) { return (var.modifiers().fFlags & Modifiers::kUniform_Flag) && var.type().typeKind() != Type::TypeKind::kSampler; } CPPCodeGenerator::CPPCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors, String name, OutputStream* out) : INHERITED(context, program, errors, out) , fName(std::move(name)) , fFullName(String::printf("Gr%s", fName.c_str())) , fSectionAndParameterHelper(program, *errors) { fLineEnding = "\n"; fTextureFunctionOverride = "sample"; } void CPPCodeGenerator::writef(const char* s, va_list va) { static constexpr int BUFFER_SIZE = 1024; va_list copy; va_copy(copy, va); char buffer[BUFFER_SIZE]; int length = std::vsnprintf(buffer, BUFFER_SIZE, s, va); if (length < BUFFER_SIZE) { fOut->write(buffer, length); } else { std::unique_ptr heap(new char[length + 1]); vsprintf(heap.get(), s, copy); fOut->write(heap.get(), length); } va_end(copy); } void CPPCodeGenerator::writef(const char* s, ...) { va_list va; va_start(va, s); this->writef(s, va); va_end(va); } void CPPCodeGenerator::writeHeader() { } bool CPPCodeGenerator::usesPrecisionModifiers() const { return false; } String CPPCodeGenerator::getTypeName(const Type& type) { return type.name(); } void CPPCodeGenerator::writeBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence) { const Expression& left = *b.left(); const Expression& right = *b.right(); Operator op = b.getOperator(); if (op.kind() == Token::Kind::TK_PERCENT) { // need to use "%%" instead of "%" b/c the code will be inside of a printf Precedence precedence = op.getBinaryPrecedence(); if (precedence >= parentPrecedence) { this->write("("); } this->writeExpression(left, precedence); this->write(" %% "); this->writeExpression(right, precedence); if (precedence >= parentPrecedence) { this->write(")"); } } else { INHERITED::writeBinaryExpression(b, parentPrecedence); } } static String default_value(const Type& type) { if (type.isBoolean()) { return "false"; } switch (type.typeKind()) { case Type::TypeKind::kScalar: return "0"; case Type::TypeKind::kVector: return type.name() + "(0)"; case Type::TypeKind::kMatrix: return type.name() + "(1)"; default: SK_ABORT("unsupported default_value type"); } } static String default_value(const Variable& var) { if (var.modifiers().fLayout.fCType == SkSL::Layout::CType::kSkPMColor4f) { return "{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}"; } return default_value(var.type()); } static bool is_private(const Variable& var) { const Modifiers& modifiers = var.modifiers(); return !(modifiers.fFlags & Modifiers::kUniform_Flag) && !(modifiers.fFlags & Modifiers::kIn_Flag) && var.storage() == Variable::Storage::kGlobal && modifiers.fLayout.fBuiltin == -1; } static bool is_uniform_in(const Variable& var) { const Modifiers& modifiers = var.modifiers(); return (modifiers.fFlags & Modifiers::kUniform_Flag) && (modifiers.fFlags & Modifiers::kIn_Flag) && var.type().typeKind() != Type::TypeKind::kSampler; } String CPPCodeGenerator::formatRuntimeValue(const Type& type, const Layout& layout, const String& cppCode, std::vector* formatArgs) { if (type.isArray()) { String result("["); const char* separator = ""; for (int i = 0; i < type.columns(); i++) { result += separator + this->formatRuntimeValue(type.componentType(), layout, "(" + cppCode + ")[" + to_string(i) + "]", formatArgs); separator = ","; } result += "]"; return result; } if (type.isFloat()) { formatArgs->push_back(cppCode); return "%f"; } if (type == *fContext.fTypes.fInt) { formatArgs->push_back(cppCode); return "%d"; } if (type == *fContext.fTypes.fBool) { formatArgs->push_back("(" + cppCode + " ? \"true\" : \"false\")"); return "%s"; } if (type == *fContext.fTypes.fFloat2 || type == *fContext.fTypes.fHalf2) { formatArgs->push_back(cppCode + ".fX"); formatArgs->push_back(cppCode + ".fY"); return type.name() + "(%f, %f)"; } if (type == *fContext.fTypes.fFloat3 || type == *fContext.fTypes.fHalf3) { formatArgs->push_back(cppCode + ".fX"); formatArgs->push_back(cppCode + ".fY"); formatArgs->push_back(cppCode + ".fZ"); return type.name() + "(%f, %f, %f)"; } if (type == *fContext.fTypes.fFloat4 || type == *fContext.fTypes.fHalf4) { switch (layout.fCType) { case Layout::CType::kSkPMColor: formatArgs->push_back("SkGetPackedR32(" + cppCode + ") / 255.0"); formatArgs->push_back("SkGetPackedG32(" + cppCode + ") / 255.0"); formatArgs->push_back("SkGetPackedB32(" + cppCode + ") / 255.0"); formatArgs->push_back("SkGetPackedA32(" + cppCode + ") / 255.0"); break; case Layout::CType::kSkPMColor4f: formatArgs->push_back(cppCode + ".fR"); formatArgs->push_back(cppCode + ".fG"); formatArgs->push_back(cppCode + ".fB"); formatArgs->push_back(cppCode + ".fA"); break; case Layout::CType::kSkV4: formatArgs->push_back(cppCode + ".x"); formatArgs->push_back(cppCode + ".y"); formatArgs->push_back(cppCode + ".z"); formatArgs->push_back(cppCode + ".w"); break; case Layout::CType::kSkRect: case Layout::CType::kDefault: formatArgs->push_back(cppCode + ".left()"); formatArgs->push_back(cppCode + ".top()"); formatArgs->push_back(cppCode + ".right()"); formatArgs->push_back(cppCode + ".bottom()"); break; default: SkASSERT(false); } return type.name() + "(%f, %f, %f, %f)"; } if (type.isMatrix()) { SkASSERT(type.componentType() == *fContext.fTypes.fFloat || type.componentType() == *fContext.fTypes.fHalf); String format = type.name() + "("; for (int c = 0; c < type.columns(); ++c) { for (int r = 0; r < type.rows(); ++r) { formatArgs->push_back(String::printf("%s.rc(%d, %d)", cppCode.c_str(), r, c)); format += "%f, "; } } // Replace trailing ", " with ")". format.pop_back(); format.back() = ')'; return format; } if (type.isEnum()) { formatArgs->push_back("(int) " + cppCode); return "%d"; } if (type == *fContext.fTypes.fInt4 || type == *fContext.fTypes.fShort4) { formatArgs->push_back(cppCode + ".left()"); formatArgs->push_back(cppCode + ".top()"); formatArgs->push_back(cppCode + ".right()"); formatArgs->push_back(cppCode + ".bottom()"); return type.name() + "(%d, %d, %d, %d)"; } SkDEBUGFAILF("unsupported runtime value type '%s'\n", String(type.name()).c_str()); return ""; } void CPPCodeGenerator::writeRuntimeValue(const Type& type, const Layout& layout, const String& cppCode) { this->write(this->formatRuntimeValue(type, layout, cppCode, &fFormatArgs)); } void CPPCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) { if (is_private(var)) { this->writeRuntimeValue(var.type(), var.modifiers().fLayout, var.name()); } else { this->writeExpression(value, Precedence::kTopLevel); } } String CPPCodeGenerator::getSamplerHandle(const Variable& var) { int samplerCount = 0; for (const auto param : fSectionAndParameterHelper.getParameters()) { if (&var == param) { return "args.fTexSamplers[" + to_string(samplerCount) + "]"; } if (param->type().typeKind() == Type::TypeKind::kSampler) { ++samplerCount; } } SK_ABORT("should have found sampler in parameters\n"); } void CPPCodeGenerator::writeIntLiteral(const IntLiteral& i) { this->write(to_string(i.value())); } void CPPCodeGenerator::writeSwizzle(const Swizzle& swizzle) { if (fCPPMode) { // no support for multiple swizzle components yet SkASSERT(swizzle.components().size() == 1); this->writeExpression(*swizzle.base(), Precedence::kPostfix); switch (swizzle.components()[0]) { case 0: this->write(".left()"); break; case 1: this->write(".top()"); break; case 2: this->write(".right()"); break; case 3: this->write(".bottom()"); break; } } else { INHERITED::writeSwizzle(swizzle); } } void CPPCodeGenerator::writeVariableReference(const VariableReference& ref) { if (fCPPMode) { this->write(ref.variable()->name()); return; } switch (ref.variable()->modifiers().fLayout.fBuiltin) { case SK_MAIN_COORDS_BUILTIN: this->write("%s"); fFormatArgs.push_back(String("args.fSampleCoord")); fAccessSampleCoordsDirectly = true; break; default: const Variable& var = *ref.variable(); if (var.type().typeKind() == Type::TypeKind::kSampler) { this->write("%s"); fFormatArgs.push_back("fragBuilder->getProgramBuilder()->samplerVariable(" + this->getSamplerHandle(*ref.variable()) + ")"); return; } if (var.modifiers().fFlags & Modifiers::kUniform_Flag) { this->write("%s"); String name = var.name(); String varCode = String::printf("args.fUniformHandler->getUniformCStr(%sVar)", HCodeGenerator::FieldName(name.c_str()).c_str()); String code; if (var.modifiers().fLayout.fWhen.fLength) { code = String::printf("%sVar.isValid() ? %s : \"%s\"", HCodeGenerator::FieldName(name.c_str()).c_str(), varCode.c_str(), default_value(var.type()).c_str()); } else { code = varCode; } fFormatArgs.push_back(code); } else if (SectionAndParameterHelper::IsParameter(var)) { String name(var.name()); this->writeRuntimeValue(var.type(), var.modifiers().fLayout, String::printf("_outer.%s", name.c_str()).c_str()); } else { this->write(var.name()); } } } void CPPCodeGenerator::writeIfStatement(const IfStatement& s) { if (s.isStatic()) { this->write("@"); } INHERITED::writeIfStatement(s); } void CPPCodeGenerator::writeReturnStatement(const ReturnStatement& s) { INHERITED::writeReturnStatement(s); } void CPPCodeGenerator::writeSwitchStatement(const SwitchStatement& s) { if (s.isStatic()) { this->write("@"); } INHERITED::writeSwitchStatement(s); } int CPPCodeGenerator::getChildFPIndex(const Variable& var) const { int index = 0; for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const VarDeclaration& decl = p->as().declaration()->as(); if (&decl.var() == &var) { return index; } else if (decl.var().type().isFragmentProcessor()) { ++index; } } } SkDEBUGFAIL("child fragment processor not found"); return 0; } String CPPCodeGenerator::getSampleVarName(const char* prefix, int sampleCounter) { return String::printf("%s%d", prefix, sampleCounter); } void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) { const FunctionDeclaration& function = c.function(); const ExpressionArray& arguments = c.arguments(); if (function.isBuiltin() && function.name() == "sample" && arguments[0]->type().typeKind() != Type::TypeKind::kSampler) { int sampleCounter = fSampleCounter++; // Validity checks that are detected by function definition in sksl_fp.inc SkASSERT(arguments.size() >= 1 && arguments.size() <= 3); SkASSERT(arguments[0]->type().isFragmentProcessor()); // Actually fail during compilation if arguments with valid types are // provided that are not variable references, since sample() is a // special function that impacts code emission. if (!arguments[0]->is()) { fErrors.error(arguments[0]->fOffset, "sample()'s fragmentProcessor argument must be a variable reference\n"); return; } const Variable& child = *arguments[0]->as().variable(); // Start a new extra emit code section so that the emitted child processor can depend on // sksl variables defined in earlier sksl code. this->newExtraEmitCodeBlock(); // inputColor is an optional argument that always appears last String inputColor; if (arguments.back()->type().name() == "half4") { // Use the invokeChild() variant that accepts an input color, so convert the 2nd // argument's expression into C++ code that produces sksl stored in an SkString. String inputColorName = this->getSampleVarName("_input", sampleCounter); addExtraEmitCodeLine(convertSKSLExpressionToCPP(*arguments.back(), inputColorName)); // invokeChild() needs a char* and a pre-pended comma inputColor = ", " + inputColorName + ".c_str()"; } // If coords are present, they're float2. They appear right after the fp. String inputCoord; if (arguments.size() > 1) { if (arguments[1]->type().name() == "float2") { // Invoking child with explicit coordinates at this call site inputCoord = this->getSampleVarName("_coords", sampleCounter); addExtraEmitCodeLine(convertSKSLExpressionToCPP(*arguments[1], inputCoord)); inputCoord.append(".c_str()"); } } if (!inputCoord.empty()) { inputCoord = ", " + inputCoord; } // Write the output handling after the possible input handling String childName = this->getSampleVarName("_sample", sampleCounter); String childIndexStr = to_string(this->getChildFPIndex(child)); addExtraEmitCodeLine("SkString " + childName + " = this->invokeChild(" + childIndexStr + inputColor + ", args" + inputCoord + ");"); this->write("%s"); fFormatArgs.push_back(childName + ".c_str()"); return; } if (function.isBuiltin()) { INHERITED::writeFunctionCall(c); } else { this->write("%s"); fFormatArgs.push_back((String(function.name()) + "_name.c_str()").c_str()); this->write("("); const char* separator = ""; for (const auto& arg : arguments) { this->write(separator); separator = ", "; this->writeExpression(*arg, Precedence::kSequence); } this->write(")"); } if (function.isBuiltin() && function.name() == "sample") { this->write(".%s"); SkASSERT(arguments.size() >= 1); SkASSERT(arguments[0]->is()); String sampler = this->getSamplerHandle(*arguments[0]->as().variable()); fFormatArgs.push_back("fragBuilder->getProgramBuilder()->samplerSwizzle(" + sampler + ").asString().c_str()"); } } static const char* glsltype_string(const Context& context, const Type& type) { // If a new GrSL type is added, this function will need to be updated. static_assert(kGrSLTypeCount == 49); if (type == *context.fTypes.fVoid ) { return "kVoid_GrSLType"; } if (type == *context.fTypes.fBool ) { return "kBool_GrSLType"; } if (type == *context.fTypes.fBool2 ) { return "kBool2_GrSLType"; } if (type == *context.fTypes.fBool3 ) { return "kBool3_GrSLType"; } if (type == *context.fTypes.fBool4 ) { return "kBool4_GrSLType"; } if (type == *context.fTypes.fShort ) { return "kShort_GrSLType"; } if (type == *context.fTypes.fShort2 ) { return "kShort2_GrSLType"; } if (type == *context.fTypes.fShort3 ) { return "kShort3_GrSLType"; } if (type == *context.fTypes.fShort4 ) { return "kShort4_GrSLType"; } if (type == *context.fTypes.fUShort ) { return "kUShort_GrSLType"; } if (type == *context.fTypes.fUShort2 ) { return "kUShort2_GrSLType"; } if (type == *context.fTypes.fUShort3 ) { return "kUShort3_GrSLType"; } if (type == *context.fTypes.fUShort4 ) { return "kUShort4_GrSLType"; } if (type == *context.fTypes.fFloat ) { return "kFloat_GrSLType"; } if (type == *context.fTypes.fFloat2 ) { return "kFloat2_GrSLType"; } if (type == *context.fTypes.fFloat3 ) { return "kFloat3_GrSLType"; } if (type == *context.fTypes.fFloat4 ) { return "kFloat4_GrSLType"; } if (type == *context.fTypes.fFloat2x2) { return "kFloat2x2_GrSLType"; } if (type == *context.fTypes.fFloat3x3) { return "kFloat3x3_GrSLType"; } if (type == *context.fTypes.fFloat4x4) { return "kFloat4x4_GrSLType"; } if (type == *context.fTypes.fHalf ) { return "kHalf_GrSLType"; } if (type == *context.fTypes.fHalf2 ) { return "kHalf2_GrSLType"; } if (type == *context.fTypes.fHalf3 ) { return "kHalf3_GrSLType"; } if (type == *context.fTypes.fHalf4 ) { return "kHalf4_GrSLType"; } if (type == *context.fTypes.fHalf2x2 ) { return "kHalf2x2_GrSLType"; } if (type == *context.fTypes.fHalf3x3 ) { return "kHalf3x3_GrSLType"; } if (type == *context.fTypes.fHalf4x4 ) { return "kHalf4x4_GrSLType"; } if (type == *context.fTypes.fInt ) { return "kInt_GrSLType"; } if (type == *context.fTypes.fInt2 ) { return "kInt2_GrSLType"; } if (type == *context.fTypes.fInt3 ) { return "kInt3_GrSLType"; } if (type == *context.fTypes.fInt4 ) { return "kInt4_GrSLType"; } if (type == *context.fTypes.fUInt ) { return "kUint_GrSLType"; } if (type == *context.fTypes.fUInt2 ) { return "kUint2_GrSLType"; } if (type == *context.fTypes.fUInt3 ) { return "kUint3_GrSLType"; } if (type == *context.fTypes.fUInt4 ) { return "kUint4_GrSLType"; } if (type.isEnum()) { return "kInt_GrSLType"; } SkDEBUGFAILF("unsupported type: %s", type.description().c_str()); return nullptr; } void CPPCodeGenerator::prepareHelperFunction(const FunctionDeclaration& decl) { if (decl.isBuiltin() || decl.isMain()) { return; } String funcName = decl.name(); this->addExtraEmitCodeLine( String::printf("SkString %s_name = fragBuilder->getMangledFunctionName(\"%s\");", funcName.c_str(), funcName.c_str())); String args = String::printf("const GrShaderVar %s_args[] = { ", funcName.c_str()); const char* separator = ""; for (const Variable* param : decl.parameters()) { String paramName = param->name(); args.appendf("%sGrShaderVar(\"%s\", %s)", separator, paramName.c_str(), glsltype_string(fContext, param->type())); separator = ", "; } args += " };"; this->addExtraEmitCodeLine(args.c_str()); } void CPPCodeGenerator::prototypeHelperFunction(const FunctionDeclaration& decl) { String funcName = decl.name(); this->addExtraEmitCodeLine(String::printf( "fragBuilder->emitFunctionPrototype(%s, %s_name.c_str(), {%s_args, %zu});", glsltype_string(fContext, decl.returnType()), funcName.c_str(), funcName.c_str(), decl.parameters().size())); } void CPPCodeGenerator::writeFunction(const FunctionDefinition& f) { const FunctionDeclaration& decl = f.declaration(); if (decl.isBuiltin()) { return; } fFunctionHeader.clear(); OutputStream* oldOut = fOut; StringStream buffer; fOut = &buffer; if (decl.isMain()) { fInMain = true; for (const std::unique_ptr& s : f.body()->as().children()) { this->writeStatement(*s); this->writeLine(); } fInMain = false; fOut = oldOut; this->write(fFunctionHeader); this->write(buffer.str()); } else { for (const std::unique_ptr& s : f.body()->as().children()) { this->writeStatement(*s); this->writeLine(); } fOut = oldOut; String funcName = decl.name(); String funcImpl; if (!fFormatArgs.empty()) { this->addExtraEmitCodeLine("const String " + funcName + "_impl = String::printf(" + assembleCodeAndFormatArgPrintf(buffer.str()).c_str() + ");"); funcImpl = String::printf(" %s_impl.c_str()", funcName.c_str()); } else { funcImpl = "\nR\"SkSL(" + buffer.str() + ")SkSL\""; } this->addExtraEmitCodeLine(String::printf( "fragBuilder->emitFunction(%s, %s_name.c_str(), {%s_args, %zu},%s);", glsltype_string(fContext, decl.returnType()), funcName.c_str(), funcName.c_str(), decl.parameters().size(), funcImpl.c_str())); } } void CPPCodeGenerator::writeSetting(const Setting& s) { this->writef("sk_Caps.%s", s.name().c_str()); } bool CPPCodeGenerator::writeSection(const char* name, const char* prefix) { const Section* s = fSectionAndParameterHelper.getSection(name); if (s) { this->writef("%s%s", prefix, s->text().c_str()); return true; } return false; } void CPPCodeGenerator::writeProgramElement(const ProgramElement& p) { switch (p.kind()) { case ProgramElement::Kind::kSection: return; case ProgramElement::Kind::kGlobalVar: { const GlobalVarDeclaration& decl = p.as(); const Variable& var = decl.declaration()->as().var(); if (var.modifiers().fFlags & (Modifiers::kIn_Flag | Modifiers::kUniform_Flag) || -1 != var.modifiers().fLayout.fBuiltin) { return; } break; } case ProgramElement::Kind::kFunctionPrototype: { // Function prototypes are handled at the C++ level (in writeEmitCode). // We don't want prototypes to be emitted inside the FP's main() function. return; } default: break; } INHERITED::writeProgramElement(p); } void CPPCodeGenerator::addUniform(const Variable& var) { if (!needs_uniform_var(var)) { return; } if (var.modifiers().fLayout.fWhen.fLength) { this->writef(" if (%s) {\n ", String(var.modifiers().fLayout.fWhen).c_str()); } String name(var.name()); if (!var.type().isArray()) { this->writef(" %sVar = args.fUniformHandler->addUniform(&_outer, " "kFragment_GrShaderFlag, %s, \"%s\");\n", HCodeGenerator::FieldName(name.c_str()).c_str(), glsltype_string(fContext, var.type()), name.c_str()); } else { this->writef(" %sVar = args.fUniformHandler->addUniformArray(&_outer, " "kFragment_GrShaderFlag, %s, \"%s\", %d);\n", HCodeGenerator::FieldName(name.c_str()).c_str(), glsltype_string(fContext, var.type().componentType()), name.c_str(), var.type().columns()); } if (var.modifiers().fLayout.fWhen.fLength) { this->write(" }\n"); } } void CPPCodeGenerator::writeInputVars() { } void CPPCodeGenerator::writePrivateVars() { for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const GlobalVarDeclaration& global = p->as(); const Variable& var = global.declaration()->as().var(); if (is_private(var)) { if (var.type().isFragmentProcessor()) { fErrors.error(global.fOffset, "fragmentProcessor variables must be declared 'in'"); return; } this->writef("%s %s = %s;\n", HCodeGenerator::FieldType(fContext, var.type(), var.modifiers().fLayout).c_str(), String(var.name()).c_str(), default_value(var).c_str()); } else if (var.modifiers().fLayout.fFlags & Layout::kTracked_Flag) { // An auto-tracked uniform in variable, so add a field to hold onto the prior // state. Note that tracked variables must be uniform in's and that is validated // before writePrivateVars() is called. const UniformCTypeMapper* mapper = UniformCTypeMapper::Get(fContext, var); SkASSERT(mapper); String name = HCodeGenerator::FieldName(String(var.name()).c_str()); // The member statement is different if the mapper reports a default value if (mapper->defaultValue().size() > 0) { this->writef("%s %sPrev = %s;\n", Layout::CTypeToStr(mapper->ctype()), name.c_str(), mapper->defaultValue().c_str()); } else { this->writef("%s %sPrev;\n", Layout::CTypeToStr(mapper->ctype()), name.c_str()); } } } } } void CPPCodeGenerator::writePrivateVarValues() { for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const GlobalVarDeclaration& global = p->as(); const VarDeclaration& decl = global.declaration()->as(); if (is_private(decl.var()) && decl.value()) { this->writef("%s = ", String(decl.var().name()).c_str()); fCPPMode = true; this->writeExpression(*decl.value(), Precedence::kAssignment); fCPPMode = false; this->write(";\n"); } } } } static bool is_accessible(const Variable& var) { const Type& type = var.type(); return !type.isFragmentProcessor() && Type::TypeKind::kSampler != type.typeKind() && Type::TypeKind::kOther != type.typeKind(); } void CPPCodeGenerator::newExtraEmitCodeBlock() { // This should only be called when emitting SKSL for emitCode(), which can be detected if the // cpp buffer is not null, and the cpp buffer is not the current output. SkASSERT(fCPPBuffer && fCPPBuffer != fOut); // Start a new block as an empty string fExtraEmitCodeBlocks.push_back(""); // Mark its location in the output buffer, uses ${\d} for the token since ${} will not occur in // valid sksl and makes detection trivial. this->writef("${%zu}", fExtraEmitCodeBlocks.size() - 1); } void CPPCodeGenerator::addExtraEmitCodeLine(const String& toAppend) { SkASSERT(fExtraEmitCodeBlocks.size() > 0); String& currentBlock = fExtraEmitCodeBlocks[fExtraEmitCodeBlocks.size() - 1]; // Automatically add indentation and newline currentBlock += " " + toAppend + "\n"; } void CPPCodeGenerator::flushEmittedCode() { if (fCPPBuffer == nullptr) { // Not actually within writeEmitCode() so nothing to flush return; } StringStream* skslBuffer = static_cast(fOut); String sksl = skslBuffer->str(); // Empty the accumulation buffer since its current contents are consumed. skslBuffer->reset(); // Switch to the cpp buffer fOut = fCPPBuffer; // Iterate through the sksl, keeping track of where the last statement ended (e.g. the latest // encountered ';', '{', or '}'). If an extra emit code block token is encountered then the // code from 0 to last statement end is sent to writeCodeAppend, the extra code block is // appended to the cpp buffer, and then the sksl string is trimmed to start where the last // statement left off (minus the encountered token). size_t i = 0; int flushPoint = -1; int tokenStart = -1; while (i < sksl.size()) { if (tokenStart >= 0) { // Looking for the end of the token if (sksl[i] == '}') { // Must append the sksl from 0 to flushPoint (inclusive) then the extra code // accumulated in the block with index parsed from chars [tokenStart+2, i-1] String toFlush = String(sksl.c_str(), flushPoint + 1); // writeCodeAppend automatically removes the format args that it consumed, so // fFormatArgs will be in a valid state for any future sksl this->writeCodeAppend(toFlush); SKSL_INT codeBlock; SkAssertResult( stoi(StringFragment(sksl.c_str() + tokenStart + 2, i - tokenStart - 2), &codeBlock)); SkASSERT((size_t)codeBlock < fExtraEmitCodeBlocks.size()); if (fExtraEmitCodeBlocks[codeBlock].size() > 0) { this->write(fExtraEmitCodeBlocks[codeBlock].c_str()); } // Now reset the sksl buffer to start after the flush point, but remove the token. String compacted = String(sksl.c_str() + flushPoint + 1, tokenStart - flushPoint - 1); if (i < sksl.size() - 1) { compacted += String(sksl.c_str() + i + 1, sksl.size() - i - 1); } sksl = compacted; // And reset iteration i = -1; flushPoint = -1; tokenStart = -1; } } else { // Looking for the start of extra emit block tokens, and tracking when statements end if (sksl[i] == ';' || sksl[i] == '{' || sksl[i] == '}') { flushPoint = i; } else if (i < sksl.size() - 1 && sksl[i] == '$' && sksl[i + 1] == '{') { // found an extra emit code block token tokenStart = i++; } } i++; } // Once we've gone through the sksl string to this point, there are no remaining extra emit // code blocks to interleave, so append the remainder as usual. this->writeCodeAppend(sksl); // After appending, switch back to the emptied sksl buffer and reset the extra code blocks fOut = skslBuffer; fExtraEmitCodeBlocks.clear(); } String CPPCodeGenerator::assembleCodeAndFormatArgPrintf(const String& code) { // Count % format specifiers. size_t argCount = 0; for (size_t index = 0; index < code.size(); ++index) { if ('%' == code[index]) { if (index == code.size() - 1) { SkDEBUGFAIL("found a dangling format specifier at the end of a string"); break; } if (code[index + 1] == '%') { // %% indicates a literal % sign, not a format argument. Skip over the next // character to avoid mistakenly counting that one as an argument. ++index; } else { // Count the format argument that we found. ++argCount; } } } // Assemble the printf arguments. String result = String::printf("R\"SkSL(%s)SkSL\"\n", code.c_str()); for (size_t i = 0; i < argCount; ++i) { result += ", "; result += fFormatArgs[i].c_str(); } // argCount is equal to the number of fFormatArgs that were consumed, so they should be // removed from the list. if (argCount > 0) { fFormatArgs.erase(fFormatArgs.begin(), fFormatArgs.begin() + argCount); } return result; } void CPPCodeGenerator::writeCodeAppend(const String& code) { if (!code.empty()) { this->write(" fragBuilder->codeAppendf(\n"); this->write(assembleCodeAndFormatArgPrintf(code)); this->write(");\n"); } } String CPPCodeGenerator::convertSKSLExpressionToCPP(const Expression& e, const String& cppVar) { // To do this conversion, we temporarily switch the sksl output stream // to an empty stringstream and reset the format args to empty. OutputStream* oldSKSL = fOut; StringStream exprBuffer; fOut = &exprBuffer; std::vector oldArgs(fFormatArgs); fFormatArgs.clear(); // Convert the argument expression into a format string and args this->writeExpression(e, Precedence::kTopLevel); std::vector newArgs(fFormatArgs); String expr = exprBuffer.str(); // After generating, restore the original output stream and format args fFormatArgs = oldArgs; fOut = oldSKSL; // The sksl written to exprBuffer is not processed by flushEmittedCode(), so any extra emit code // block tokens won't get handled. So we need to strip them from the expression and stick them // to the end of the original sksl stream. String exprFormat = ""; int tokenStart = -1; for (size_t i = 0; i < expr.size(); i++) { if (tokenStart >= 0) { if (expr[i] == '}') { // End of the token, so append the token to fOut fOut->write(expr.c_str() + tokenStart, i - tokenStart + 1); tokenStart = -1; } } else { if (i < expr.size() - 1 && expr[i] == '$' && expr[i + 1] == '{') { tokenStart = i++; } else { exprFormat += expr[i]; } } } // Now build the final C++ code snippet from the format string and args String cppExpr; if (newArgs.empty()) { // This was a static expression, so we can simplify the input // color declaration in the emitted code to just a static string cppExpr = "SkString " + cppVar + "(\"" + exprFormat + "\");"; } else if (newArgs.size() == 1 && exprFormat == "%s") { // If the format expression is simply "%s", we can avoid an expensive call to printf. // This happens fairly often in codegen so it is worth simplifying. cppExpr = "SkString " + cppVar + "(" + newArgs[0] + ");"; } else { // String formatting must occur dynamically, so have the C++ declaration // use SkStringPrintf with the format args that were accumulated // when the expression was written. cppExpr = "SkString " + cppVar + " = SkStringPrintf(\"" + exprFormat + "\""; for (size_t i = 0; i < newArgs.size(); i++) { cppExpr += ", " + newArgs[i]; } cppExpr += ");"; } return cppExpr; } bool CPPCodeGenerator::writeEmitCode(std::vector& uniforms) { this->write(" void emitCode(EmitArgs& args) override {\n" " GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n"); this->writef(" const %s& _outer = args.fFp.cast<%s>();\n" " (void) _outer;\n", fFullName.c_str(), fFullName.c_str()); for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const GlobalVarDeclaration& global = p->as(); const VarDeclaration& decl = global.declaration()->as(); String nameString(decl.var().name()); const char* name = nameString.c_str(); if (SectionAndParameterHelper::IsParameter(decl.var()) && is_accessible(decl.var())) { this->writef(" auto %s = _outer.%s;\n" " (void) %s;\n", name, name, name); } } } this->writePrivateVarValues(); for (const auto u : uniforms) { this->addUniform(*u); } this->writeSection(kEmitCodeSection); // Save original buffer as the CPP buffer for flushEmittedCode() fCPPBuffer = fOut; StringStream skslBuffer; fOut = &skslBuffer; this->newExtraEmitCodeBlock(); // Generate mangled names and argument lists for helper functions. std::unordered_set definedHelpers; for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const FunctionDeclaration* decl = &p->as().declaration(); definedHelpers.insert(decl); this->prepareHelperFunction(*decl); } } // Emit prototypes for defined helper functions that originally had prototypes in the FP file. // (If a function was prototyped but never defined, we skip it, since it wasn't prepared above.) for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const FunctionDeclaration* decl = &p->as().declaration(); if (definedHelpers.find(decl) != definedHelpers.end()) { this->prototypeHelperFunction(*decl); } } } bool result = INHERITED::generateCode(); this->flushEmittedCode(); // Then restore the original CPP buffer and close the function fOut = fCPPBuffer; fCPPBuffer = nullptr; this->write(" }\n"); return result; } void CPPCodeGenerator::writeSetData(std::vector& uniforms) { const char* fullName = fFullName.c_str(); const Section* section = fSectionAndParameterHelper.getSection(kSetDataSection); const char* pdman = section ? section->argument().c_str() : "pdman"; this->writef(" void onSetData(const GrGLSLProgramDataManager& %s, " "const GrFragmentProcessor& _proc) override {\n", pdman); bool wroteProcessor = false; for (const Variable* u : uniforms) { if (is_uniform_in(*u)) { if (!wroteProcessor) { this->writef(" const %s& _outer = _proc.cast<%s>();\n", fullName, fullName); wroteProcessor = true; this->writef(" {\n"); } const UniformCTypeMapper* mapper = UniformCTypeMapper::Get(fContext, *u); SkASSERT(mapper); String nameString(u->name()); const char* name = nameString.c_str(); // Switches for setData behavior in the generated code bool conditionalUniform = u->modifiers().fLayout.fWhen != ""; bool isTracked = u->modifiers().fLayout.fFlags & Layout::kTracked_Flag; bool needsValueDeclaration = isTracked || !mapper->canInlineUniformValue(); String uniformName = HCodeGenerator::FieldName(name) + "Var"; String indent = " "; // 8 by default, 12 when nested for conditional uniforms if (conditionalUniform) { // Add a pre-check to make sure the uniform was emitted // before trying to send any data to the GPU this->writef(" if (%s.isValid()) {\n", uniformName.c_str()); indent += " "; } String valueVar = ""; if (needsValueDeclaration) { valueVar.appendf("%sValue", name); // Use AccessType since that will match the return type of _outer's public API. String valueType = HCodeGenerator::AccessType(fContext, u->type(), u->modifiers().fLayout); this->writef("%s%s %s = _outer.%s;\n", indent.c_str(), valueType.c_str(), valueVar.c_str(), name); } else { // Not tracked and the mapper only needs to use the value once // so send it a safe expression instead of the variable name valueVar.appendf("(_outer.%s)", name); } if (isTracked) { String prevVar = HCodeGenerator::FieldName(name) + "Prev"; this->writef("%sif (%s) {\n" "%s %s;\n" "%s %s;\n" "%s}\n", indent.c_str(), mapper->dirtyExpression(valueVar, prevVar).c_str(), indent.c_str(), mapper->saveState(valueVar, prevVar).c_str(), indent.c_str(), mapper->setUniform(pdman, uniformName, valueVar).c_str(), indent.c_str()); } else { this->writef("%s%s;\n", indent.c_str(), mapper->setUniform(pdman, uniformName, valueVar).c_str()); } if (conditionalUniform) { // Close the earlier precheck block this->writef(" }\n"); } } } if (wroteProcessor) { this->writef(" }\n"); } if (section) { int samplerIndex = 0; for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const GlobalVarDeclaration& global = p->as(); const VarDeclaration& decl = global.declaration()->as(); const Variable& variable = decl.var(); String nameString(variable.name()); const char* name = nameString.c_str(); if (variable.type().typeKind() == Type::TypeKind::kSampler) { this->writef(" const GrSurfaceProxyView& %sView = " "_outer.textureSampler(%d).view();\n", name, samplerIndex); this->writef(" GrTexture& %s = *%sView.proxy()->peekTexture();\n", name, name); this->writef(" (void) %s;\n", name); ++samplerIndex; } else if (needs_uniform_var(variable)) { this->writef(" UniformHandle& %s = %sVar;\n" " (void) %s;\n", name, HCodeGenerator::FieldName(name).c_str(), name); } else if (SectionAndParameterHelper::IsParameter(variable) && !variable.type().isFragmentProcessor()) { if (!wroteProcessor) { this->writef(" const %s& _outer = _proc.cast<%s>();\n", fullName, fullName); wroteProcessor = true; } if (!variable.type().isFragmentProcessor()) { this->writef(" auto %s = _outer.%s;\n" " (void) %s;\n", name, name, name); } } } } this->writeSection(kSetDataSection); } this->write(" }\n"); } void CPPCodeGenerator::writeOnTextureSampler() { bool foundSampler = false; for (const auto& param : fSectionAndParameterHelper.getParameters()) { if (param->type().typeKind() == Type::TypeKind::kSampler) { if (!foundSampler) { this->writef( "const GrFragmentProcessor::TextureSampler& %s::onTextureSampler(int " "index) const {\n", fFullName.c_str()); this->writef(" return IthTextureSampler(index, %s", HCodeGenerator::FieldName(String(param->name()).c_str()).c_str()); foundSampler = true; } else { this->writef(", %s", HCodeGenerator::FieldName(String(param->name()).c_str()).c_str()); } } } if (foundSampler) { this->write(");\n}\n"); } } void CPPCodeGenerator::writeClone() { if (!this->writeSection(kCloneSection)) { if (fSectionAndParameterHelper.getSection(kFieldsSection)) { fErrors.error(/*offset=*/0, "fragment processors with custom @fields must also have a " "custom @clone"); } this->writef("%s::%s(const %s& src)\n" ": INHERITED(k%s_ClassID, src.optimizationFlags())", fFullName.c_str(), fFullName.c_str(), fFullName.c_str(), fFullName.c_str()); for (const Variable* param : fSectionAndParameterHelper.getParameters()) { String fieldName = HCodeGenerator::FieldName(String(param->name()).c_str()); if (!param->type().isFragmentProcessor()) { this->writef("\n, %s(src.%s)", fieldName.c_str(), fieldName.c_str()); } } this->writef(" {\n"); this->writef(" this->cloneAndRegisterAllChildProcessors(src);\n"); int samplerCount = 0; for (const auto& param : fSectionAndParameterHelper.getParameters()) { if (param->type().typeKind() == Type::TypeKind::kSampler) { ++samplerCount; } } if (samplerCount) { this->writef(" this->setTextureSamplerCnt(%d);", samplerCount); } if (fAccessSampleCoordsDirectly) { this->writef(" this->setUsesSampleCoordsDirectly();\n"); } this->write("}\n"); this->writef("std::unique_ptr %s::clone() const {\n", fFullName.c_str()); this->writef(" return std::make_unique<%s>(*this);\n", fFullName.c_str()); this->write("}\n"); } } void CPPCodeGenerator::writeDumpInfo() { this->writef("#if GR_TEST_UTILS\n" "SkString %s::onDumpInfo() const {\n", fFullName.c_str()); if (!this->writeSection(kDumpInfoSection)) { if (fSectionAndParameterHelper.getSection(kFieldsSection)) { fErrors.error(/*offset=*/0, "fragment processors with custom @fields must also have a " "custom @dumpInfo"); } String formatString; std::vector argumentList; for (const Variable* param : fSectionAndParameterHelper.getParameters()) { // dumpInfo() doesn't need to log child FPs. if (param->type().isFragmentProcessor()) { continue; } // Add this field onto the format string and argument list. String fieldName = HCodeGenerator::FieldName(String(param->name()).c_str()); String runtimeValue = this->formatRuntimeValue(param->type(), param->modifiers().fLayout, param->name(), &argumentList); formatString.appendf("%s%s=%s", formatString.empty() ? "" : ", ", fieldName.c_str(), runtimeValue.c_str()); } if (!formatString.empty()) { // Emit the finished format string and associated arguments. this->writef(" return SkStringPrintf(\"(%s)\"", formatString.c_str()); for (const String& argument : argumentList) { this->writef(", %s", argument.c_str()); } this->write(");"); } else { // No fields to dump at all; just return an empty string. this->write(" return SkString();"); } } this->write("\n" "}\n" "#endif\n"); } void CPPCodeGenerator::writeTest() { const Section* test = fSectionAndParameterHelper.getSection(kTestCodeSection); if (test) { this->writef( "GR_DEFINE_FRAGMENT_PROCESSOR_TEST(%s);\n" "#if GR_TEST_UTILS\n" "std::unique_ptr %s::TestCreate(GrProcessorTestData* %s) {\n", fFullName.c_str(), fFullName.c_str(), test->argument().c_str()); this->writeSection(kTestCodeSection); this->write("}\n" "#endif\n"); } } static int bits_needed(uint32_t v) { int bits = 1; while (v >= (1u << bits)) { bits++; } return bits; } void CPPCodeGenerator::writeGetKey() { auto bitsForEnum = [&](const Type& type) { for (const ProgramElement* e : fProgram.elements()) { if (!e->is() || type.name() != e->as().typeName()) { continue; } SKSL_INT minVal = 0, maxVal = 0; auto gatherEnumRange = [&](StringFragment, SKSL_INT value) { minVal = std::min(minVal, value); maxVal = std::max(maxVal, value); }; e->as().foreach(gatherEnumRange); if (minVal < 0) { // Found a negative value in the enum, just use 32 bits return 32; } SkASSERT(SkTFitsIn(maxVal)); return bits_needed(maxVal); } SK_ABORT("Didn't find declaring element for enum type!"); return 32; }; this->writef("void %s::onGetGLSLProcessorKey(const GrShaderCaps& caps, " "GrProcessorKeyBuilder* b) const {\n", fFullName.c_str()); for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const GlobalVarDeclaration& global = p->as(); const VarDeclaration& decl = global.declaration()->as(); const Variable& var = decl.var(); const Type& varType = var.type(); String nameString(var.name()); const char* name = nameString.c_str(); if (var.modifiers().fLayout.fFlags & Layout::kKey_Flag) { if (var.modifiers().fFlags & Modifiers::kUniform_Flag) { fErrors.error(var.fOffset, "layout(key) may not be specified on uniforms"); } if (is_private(var)) { this->writef( "%s %s =", HCodeGenerator::FieldType(fContext, varType, var.modifiers().fLayout) .c_str(), String(var.name()).c_str()); if (decl.value()) { fCPPMode = true; this->writeExpression(*decl.value(), Precedence::kAssignment); fCPPMode = false; } else { this->writef("%s", default_value(var).c_str()); } this->write(";\n"); } if (var.modifiers().fLayout.fWhen.fLength) { this->writef("if (%s) {", String(var.modifiers().fLayout.fWhen).c_str()); } if (varType == *fContext.fTypes.fHalf4) { this->writef(" uint16_t red = SkFloatToHalf(%s.fR);\n", HCodeGenerator::FieldName(name).c_str()); this->writef(" uint16_t green = SkFloatToHalf(%s.fG);\n", HCodeGenerator::FieldName(name).c_str()); this->writef(" uint16_t blue = SkFloatToHalf(%s.fB);\n", HCodeGenerator::FieldName(name).c_str()); this->writef(" uint16_t alpha = SkFloatToHalf(%s.fA);\n", HCodeGenerator::FieldName(name).c_str()); this->writef(" b->add32(((uint32_t)red << 16) | green, \"%s.rg\");\n", name); this->writef(" b->add32(((uint32_t)blue << 16) | alpha, \"%s.ba\");\n", name); } else if (varType == *fContext.fTypes.fHalf || varType == *fContext.fTypes.fFloat) { this->writef(" b->add32(sk_bit_cast(%s), \"%s\");\n", HCodeGenerator::FieldName(name).c_str(), name); } else if (varType.isBoolean()) { this->writef(" b->addBool(%s, \"%s\");\n", HCodeGenerator::FieldName(name).c_str(), name); } else if (varType.isEnum()) { this->writef(" b->addBits(%d, (uint32_t) %s, \"%s\");\n", bitsForEnum(varType), HCodeGenerator::FieldName(name).c_str(), name); } else if (varType.isInteger()) { this->writef(" b->add32((uint32_t) %s, \"%s\");\n", HCodeGenerator::FieldName(name).c_str(), name); } else { SK_ABORT("NOT YET IMPLEMENTED: automatic key handling for %s\n", varType.displayName().c_str()); } if (var.modifiers().fLayout.fWhen.fLength) { this->write("}"); } } } } this->write("}\n"); } bool CPPCodeGenerator::generateCode() { std::vector uniforms; for (const ProgramElement* p : fProgram.elements()) { if (p->is()) { const GlobalVarDeclaration& global = p->as(); const VarDeclaration& decl = global.declaration()->as(); if ((decl.var().modifiers().fFlags & Modifiers::kUniform_Flag) && decl.var().type().typeKind() != Type::TypeKind::kSampler) { uniforms.push_back(&decl.var()); } if (is_uniform_in(decl.var())) { // Validate the "uniform in" declarations to make sure they are fully supported, // instead of generating surprising C++ const UniformCTypeMapper* mapper = UniformCTypeMapper::Get(fContext, decl.var()); if (mapper == nullptr) { fErrors.error(decl.fOffset, String(decl.var().name()) + "'s type is not supported for use as a 'uniform in'"); return false; } } else { // If it's not a uniform_in, it's an error to be tracked if (decl.var().modifiers().fLayout.fFlags & Layout::kTracked_Flag) { fErrors.error(decl.fOffset, "Non-'in uniforms' cannot be tracked"); return false; } } } } const char* baseName = fName.c_str(); const char* fullName = fFullName.c_str(); this->writef("%s\n", HCodeGenerator::GetHeader(fProgram, fErrors).c_str()); this->writef(kFragmentProcessorHeader, fullName); this->writef("#include \"%s.h\"\n\n", fullName); this->writeSection(kCppSection); this->writef("#include \"src/core/SkUtils.h\"\n" "#include \"src/gpu/GrTexture.h\"\n" "#include \"src/gpu/glsl/GrGLSLFragmentProcessor.h\"\n" "#include \"src/gpu/glsl/GrGLSLFragmentShaderBuilder.h\"\n" "#include \"src/gpu/glsl/GrGLSLProgramBuilder.h\"\n" "#include \"src/sksl/SkSLCPP.h\"\n" "#include \"src/sksl/SkSLUtil.h\"\n" "class GrGLSL%s : public GrGLSLFragmentProcessor {\n" "public:\n" " GrGLSL%s() {}\n", baseName, baseName); bool result = this->writeEmitCode(uniforms); this->write("private:\n"); this->writeSetData(uniforms); this->writePrivateVars(); for (const auto& u : uniforms) { if (needs_uniform_var(*u) && !(u->modifiers().fFlags & Modifiers::kIn_Flag)) { this->writef(" UniformHandle %sVar;\n", HCodeGenerator::FieldName(String(u->name()).c_str()).c_str()); } } for (const auto& param : fSectionAndParameterHelper.getParameters()) { if (needs_uniform_var(*param)) { this->writef(" UniformHandle %sVar;\n", HCodeGenerator::FieldName(String(param->name()).c_str()).c_str()); } } this->writef("};\n" "std::unique_ptr %s::onMakeProgramImpl() const {\n" " return std::make_unique();\n" "}\n", fullName, baseName); this->writeGetKey(); this->writef("bool %s::onIsEqual(const GrFragmentProcessor& other) const {\n" " const %s& that = other.cast<%s>();\n" " (void) that;\n", fullName, fullName, fullName); for (const auto& param : fSectionAndParameterHelper.getParameters()) { if (param->type().isFragmentProcessor()) { continue; } String nameString(param->name()); const char* name = nameString.c_str(); this->writef(" if (%s != that.%s) return false;\n", HCodeGenerator::FieldName(name).c_str(), HCodeGenerator::FieldName(name).c_str()); } this->write(" return true;\n" "}\n"); this->writeClone(); this->writeDumpInfo(); this->writeOnTextureSampler(); this->writeTest(); this->writeSection(kCppEndSection); result &= 0 == fErrors.errorCount(); return result; } } // namespace SkSL #endif // defined(SKSL_STANDALONE) || GR_TEST_UTILS