1 /* 2 * Copyright 2022 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SKSL_WGSLCODEGENERATOR 9 #define SKSL_WGSLCODEGENERATOR 10 11 #include "include/private/SkSLDefines.h" 12 #include "include/private/base/SkTArray.h" 13 #include "src/core/SkTHash.h" 14 #include "src/sksl/SkSLStringStream.h" 15 #include "src/sksl/codegen/SkSLCodeGenerator.h" 16 #include "src/sksl/ir/SkSLType.h" 17 18 #include <cstdint> 19 #include <initializer_list> 20 #include <string> 21 #include <string_view> 22 #include <type_traits> 23 #include <utility> 24 25 template <typename T> class SkSpan; 26 27 namespace sknonstd { 28 template <typename T> struct is_bitmask_enum; 29 } // namespace sknonstd 30 31 namespace SkSL { 32 33 class AnyConstructor; 34 class BinaryExpression; 35 class Block; 36 class Context; 37 class ConstructorCompound; 38 class ConstructorDiagonalMatrix; 39 class Expression; 40 class ExpressionStatement; 41 class FieldAccess; 42 class FunctionCall; 43 class FunctionDeclaration; 44 class FunctionDefinition; 45 class GlobalVarDeclaration; 46 class IfStatement; 47 class Literal; 48 class MemoryLayout; 49 class OutputStream; 50 class Position; 51 class ProgramElement; 52 class ReturnStatement; 53 class Statement; 54 class StructDefinition; 55 class TernaryExpression; 56 class VarDeclaration; 57 class Variable; 58 class VariableReference; 59 enum class OperatorPrecedence : uint8_t; 60 struct IndexExpression; 61 struct Modifiers; 62 struct Program; 63 struct Swizzle; 64 65 /** 66 * Convert a Program into WGSL code. 67 */ 68 class WGSLCodeGenerator : public CodeGenerator { 69 public: 70 // See https://www.w3.org/TR/WGSL/#builtin-values 71 enum class Builtin { 72 // Vertex stage: 73 kVertexIndex, // input 74 kInstanceIndex, // input 75 kPosition, // output, fragment stage input 76 77 // Fragment stage: 78 kFrontFacing, // input 79 kSampleIndex, // input 80 kFragDepth, // output 81 kSampleMask, // input, output 82 83 // Compute stage: 84 kLocalInvocationId, // input 85 kLocalInvocationIndex, // input 86 kGlobalInvocationId, // input 87 kWorkgroupId, // input 88 kNumWorkgroups, // input 89 }; 90 91 // Represents a function's dependencies that are not accessible in global scope. For instance, 92 // pipeline stage input and output parameters must be passed in as an argument. 93 // 94 // This is a bitmask enum. 95 enum class FunctionDependencies : uint8_t { 96 kNone = 0, 97 kPipelineInputs = 1, 98 kPipelineOutputs = 2, 99 }; 100 101 // Variable declarations can be terminated by: 102 // - comma (","), e.g. in struct member declarations or function parameters 103 // - semicolon (";"), e.g. in function scope variables 104 // A "none" option is provided to skip the delimiter when not needed, e.g. at the end of a list 105 // of declarations. 106 enum class Delimiter { 107 kComma, 108 kSemicolon, 109 kNone, 110 }; 111 112 struct ProgramRequirements { 113 using DepsMap = SkTHashMap<const FunctionDeclaration*, FunctionDependencies>; 114 115 ProgramRequirements() = default; ProgramRequirementsProgramRequirements116 ProgramRequirements(DepsMap dependencies, bool mainNeedsCoordsArgument) 117 : dependencies(std::move(dependencies)) 118 , mainNeedsCoordsArgument(mainNeedsCoordsArgument) {} 119 120 // Mappings used to synthesize function parameters according to dependencies on pipeline 121 // input/output variables. 122 DepsMap dependencies; 123 124 // True, if the main function takes a coordinate parameter. This is used to ensure that 125 // sk_FragCoord is declared as part of pipeline inputs. 126 bool mainNeedsCoordsArgument; 127 }; 128 WGSLCodeGenerator(const Context * context,const Program * program,OutputStream * out)129 WGSLCodeGenerator(const Context* context, const Program* program, OutputStream* out) 130 : INHERITED(context, program, out) 131 , fReservedWords({"array", 132 "FSIn", 133 "FSOut", 134 "_globalUniforms", 135 "_GlobalUniforms", 136 "_return", 137 "_stageIn", 138 "_stageOut", 139 "VSIn", 140 "VSOut"}) {} 141 142 bool generateCode() override; 143 144 private: 145 using INHERITED = CodeGenerator; 146 using Precedence = OperatorPrecedence; 147 148 // Called by generateCode() as the first step. 149 void preprocessProgram(); 150 151 // Write output content while correctly handling indentation. 152 void write(std::string_view s); 153 void writeLine(std::string_view s = std::string_view()); 154 void finishLine(); 155 void writeName(std::string_view name); 156 void writeVariableDecl(const Type& type, std::string_view name, Delimiter delimiter); 157 158 // Helpers to declare a pipeline stage IO parameter declaration. 159 void writePipelineIODeclaration(Modifiers modifiers, 160 const Type& type, 161 std::string_view name, 162 Delimiter delimiter); 163 void writeUserDefinedIODecl(const Type& type, 164 std::string_view name, 165 int location, 166 Delimiter delimiter); 167 void writeBuiltinIODecl(const Type& type, 168 std::string_view name, 169 Builtin builtin, 170 Delimiter delimiter); 171 172 // Write a function definition. 173 void writeFunction(const FunctionDefinition& f); 174 void writeFunctionDeclaration(const FunctionDeclaration& f); 175 176 // Write the program entry point. 177 void writeEntryPoint(const FunctionDefinition& f); 178 179 // Writers for supported statement types. 180 void writeStatement(const Statement& s); 181 void writeStatements(const StatementArray& statements); 182 void writeBlock(const Block& b); 183 void writeExpressionStatement(const ExpressionStatement& s); 184 void writeIfStatement(const IfStatement& s); 185 void writeReturnStatement(const ReturnStatement& s); 186 void writeVarDeclaration(const VarDeclaration& varDecl); 187 188 // Writers for expressions. 189 void writeExpression(const Expression& e, Precedence parentPrecedence); 190 void writeBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence); 191 void writeFieldAccess(const FieldAccess& f); 192 void writeFunctionCall(const FunctionCall&); 193 void writeIndexExpression(const IndexExpression& i); 194 void writeLiteral(const Literal& l); 195 void writeSwizzle(const Swizzle& swizzle); 196 void writeTernaryExpression(const TernaryExpression& t, Precedence parentPrecedence); 197 void writeVariableReference(const VariableReference& r); 198 199 // Constructor expressions 200 void writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence); 201 void writeConstructorCompound(const ConstructorCompound& c, Precedence parentPrecedence); 202 void writeConstructorCompoundVector(const ConstructorCompound& c, Precedence parentPrecedence); 203 void writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, 204 Precedence parentPrecedence); 205 206 // Synthesized helper functions for comparison operators that are not supported by WGSL. 207 void writeMatrixEquality(const Expression& left, const Expression& right); 208 209 // Generic recursive ProgramElement visitor. 210 void writeProgramElement(const ProgramElement& e); 211 void writeGlobalVarDeclaration(const GlobalVarDeclaration& d); 212 void writeStructDefinition(const StructDefinition& s); 213 214 // Writes the WGSL struct fields for SkSL structs and interface blocks. Enforces WGSL address 215 // space layout constraints 216 // (https://www.w3.org/TR/WGSL/#address-space-layout-constraints) if a `layout` is 217 // provided. A struct that does not need to be host-shareable does not require a `layout`. 218 void writeFields(SkSpan<const Type::Field> fields, 219 Position parentPos, 220 const MemoryLayout* layout = nullptr); 221 222 // We bundle all varying pipeline stage inputs and outputs in a struct. 223 void writeStageInputStruct(); 224 void writeStageOutputStruct(); 225 226 // Writes all top-level non-opaque global uniform declarations (i.e. not part of an interface 227 // block) into a single uniform block binding. 228 // 229 // In complete fragment/vertex/compute programs, uniforms will be declared only as interface 230 // blocks and global opaque types (like textures and samplers) which we expect to be declared 231 // with a unique binding and descriptor set index. However, test files that are declared as RTE 232 // programs may contain OpenGL-style global uniform declarations with no clear binding index to 233 // use for the containing synthesized block. 234 // 235 // Since we are handling these variables only to generate gold files from RTEs and never run 236 // them, we always declare them at the default bind group and binding index. 237 void writeNonBlockUniformsForTests(); 238 239 // For a given function declaration, writes out any implicitly required pipeline stage arguments 240 // based on the function's pre-determined dependencies. These are expected to be written out as 241 // the first parameters for a function that requires them. Returns true if any arguments were 242 // written. 243 bool writeFunctionDependencyArgs(const FunctionDeclaration&); 244 bool writeFunctionDependencyParams(const FunctionDeclaration&); 245 246 // Generate an out-parameter helper function for the given call and return its name. 247 std::string writeOutParamHelper(const FunctionCall&, 248 const ExpressionArray& args, 249 const SkTArray<VariableReference*>& outVars); 250 251 // Stores the disallowed identifier names. 252 SkTHashSet<std::string_view> fReservedWords; 253 ProgramRequirements fRequirements; 254 int fPipelineInputCount = 0; 255 bool fDeclaredUniformsStruct = false; 256 257 // Out-parameters to functions are declared as pointers. While we process the arguments to a 258 // out-parameter helper function, we need to temporarily track that they are re-declared as 259 // pointer-parameters in the helper, so that expression-tree processing can know to correctly 260 // dereference them when the variable is referenced. The contents of this set are expected to 261 // be uniquely scoped for each out-param helper and will be cleared every time a new out-param 262 // helper function has been emitted. 263 SkTHashSet<const Variable*> fOutParamArgVars; 264 265 // Output processing state. 266 int fIndentation = 0; 267 bool fAtLineStart = false; 268 269 int fSwizzleHelperCount = 0; 270 StringStream fExtraFunctions; // all internally synthesized helpers are written here 271 SkTHashSet<std::string> fHelpers; // all synthesized helper functions, by name 272 }; 273 274 } // namespace SkSL 275 276 namespace sknonstd { 277 template <> 278 struct is_bitmask_enum<SkSL::WGSLCodeGenerator::FunctionDependencies> : std::true_type {}; 279 } // namespace sknonstd 280 281 #endif // SKSL_WGSLCODEGENERATOR 282