• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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