• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "compiler/translator/wgsl/Utils.h"
8 
9 #include "common/log_utils.h"
10 #include "compiler/translator/BaseTypes.h"
11 #include "compiler/translator/Common.h"
12 #include "compiler/translator/ImmutableStringBuilder.h"
13 #include "compiler/translator/Symbol.h"
14 #include "compiler/translator/Types.h"
15 #include "compiler/translator/util.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 const char kWrappedPrefix[] = "ANGLE_wrapped_";
23 }
24 
25 template <typename StringStreamType>
WriteWgslBareTypeName(StringStreamType & output,const TType & type,const EmitTypeConfig & config)26 void WriteWgslBareTypeName(StringStreamType &output,
27                            const TType &type,
28                            const EmitTypeConfig &config)
29 {
30     const TBasicType basicType = type.getBasicType();
31 
32     switch (basicType)
33     {
34         case TBasicType::EbtVoid:
35         case TBasicType::EbtBool:
36             output << type.getBasicString();
37             break;
38         // TODO(anglebug.com/42267100): is there double precision (f64) in GLSL? It doesn't really
39         // exist in WGSL (i.e. f64 does not exist but AbstractFloat can handle 64 bits???) Metal
40         // does not have 64 bit double precision types. It's being implemented in WGPU:
41         // https://github.com/gpuweb/gpuweb/issues/2805
42         case TBasicType::EbtFloat:
43             output << "f32";
44             break;
45         case TBasicType::EbtInt:
46             output << "i32";
47             break;
48         case TBasicType::EbtUInt:
49             output << "u32";
50             break;
51 
52         case TBasicType::EbtStruct:
53             WriteNameOf(output, *type.getStruct());
54             break;
55 
56         case TBasicType::EbtInterfaceBlock:
57             WriteNameOf(output, *type.getInterfaceBlock());
58             break;
59 
60         default:
61             if (IsSampler(basicType))
62             {
63                 // Variables of sampler type should be written elsewhere since they require special
64                 // handling; they are split into two different variables in WGSL.
65 
66                 // TODO(anglebug.com/389145696): this is reachable if a sampler is passed as a
67                 // function parameter. They should be monomorphized.
68                 UNIMPLEMENTED();
69             }
70             else if (IsImage(basicType))
71             {
72                 // GLSL's image types are not implemented in this backend.
73                 UNIMPLEMENTED();
74 
75                 output << "texture_storage_2d<";
76                 switch (type.getBasicType())
77                 {
78                     case EbtImage2D:
79                         output << "f32";
80                         break;
81                     case EbtIImage2D:
82                         output << "i32";
83                         break;
84                     case EbtUImage2D:
85                         output << "u32";
86                         break;
87                     default:
88                         UNIMPLEMENTED();
89                         break;
90                 }
91                 if (type.getMemoryQualifier().readonly || type.getMemoryQualifier().writeonly)
92                 {
93                     UNIMPLEMENTED();
94                 }
95                 output << ">";
96             }
97             else
98             {
99                 UNREACHABLE();
100             }
101             break;
102     }
103 }
104 
105 template <typename StringStreamType>
WriteNameOf(StringStreamType & output,SymbolType symbolType,const ImmutableString & name)106 void WriteNameOf(StringStreamType &output, SymbolType symbolType, const ImmutableString &name)
107 {
108     switch (symbolType)
109     {
110         case SymbolType::BuiltIn:
111             output << name;
112             break;
113         case SymbolType::UserDefined:
114             output << kUserDefinedNamePrefix << name;
115             break;
116         case SymbolType::AngleInternal:
117             output << name;
118             break;
119         case SymbolType::Empty:
120             // TODO(anglebug.com/42267100): support this if necessary
121             UNREACHABLE();
122     }
123 }
124 
125 template <typename StringStreamType>
WriteWgslType(StringStreamType & output,const TType & type,const EmitTypeConfig & config)126 void WriteWgslType(StringStreamType &output, const TType &type, const EmitTypeConfig &config)
127 {
128     if (type.isArray())
129     {
130         // WGSL does not support samplers anywhere inside structs or arrays.
131         ASSERT(!type.isSampler() && !type.isStructureContainingSamplers());
132 
133         // Examples:
134         // array<f32, 5>
135         // array<array<u32, 5>, 10>
136         output << "array<";
137         TType innerType = type;
138         innerType.toArrayElementType();
139         if (ElementTypeNeedsUniformWrapperStruct(config.addressSpace == WgslAddressSpace::Uniform,
140                                                  &type))
141         {
142             // Multidimensional arrays not currently supported in uniforms in the WebGPU backend
143             ASSERT(!innerType.isArray());
144 
145             // Due to uniform address space layout constraints, certain array element types must
146             // be wrapped in a wrapper struct.
147             // Example: array<ANGLE_wrapped_f32, 5>
148             output << MakeUniformWrapperStructName(&innerType);
149         }
150         else
151         {
152             WriteWgslType(output, innerType, config);
153         }
154         output << ", " << type.getOutermostArraySize() << ">";
155     }
156     else if (type.isVector())
157     {
158         output << "vec" << static_cast<uint32_t>(type.getNominalSize()) << "<";
159         WriteWgslBareTypeName(output, type, config);
160         output << ">";
161     }
162     else if (type.isMatrix())
163     {
164         if (config.addressSpace == WgslAddressSpace::Uniform && type.getRows() == 2)
165         {
166             // matCx2 in the uniform address space is too packed for std140, and so they will be
167             // represented by an array<ANGLE_wrapped_vec2, C>.
168             output << "array<" << kWrappedPrefix << "vec2, "
169                    << static_cast<uint32_t>(type.getCols()) << ">";
170         }
171         else
172         {
173             output << "mat" << static_cast<uint32_t>(type.getCols()) << "x"
174                    << static_cast<uint32_t>(type.getRows()) << "<";
175             WriteWgslBareTypeName(output, type, config);
176             output << ">";
177         }
178     }
179     else
180     {
181         // This type has no dimensions and is equivalent to its bare type.
182         WriteWgslBareTypeName(output, type, config);
183     }
184 }
185 
186 template void WriteWgslBareTypeName<TInfoSinkBase>(TInfoSinkBase &output,
187                                                    const TType &type,
188                                                    const EmitTypeConfig &config);
189 template void WriteNameOf<TInfoSinkBase>(TInfoSinkBase &output,
190                                          SymbolType symbolType,
191                                          const ImmutableString &name);
192 template void WriteWgslType<TInfoSinkBase>(TInfoSinkBase &output,
193                                            const TType &type,
194                                            const EmitTypeConfig &config);
195 
196 template void WriteWgslBareTypeName<TStringStream>(TStringStream &output,
197                                                    const TType &type,
198                                                    const EmitTypeConfig &config);
199 template void WriteNameOf<TStringStream>(TStringStream &output,
200                                          SymbolType symbolType,
201                                          const ImmutableString &name);
202 template void WriteWgslType<TStringStream>(TStringStream &output,
203                                            const TType &type,
204                                            const EmitTypeConfig &config);
205 
206 template <typename StringStreamType>
WriteWgslSamplerType(StringStreamType & output,const TType & type,WgslSamplerTypeConfig samplerType)207 void WriteWgslSamplerType(StringStreamType &output,
208                           const TType &type,
209                           WgslSamplerTypeConfig samplerType)
210 {
211     ASSERT(type.isSampler());
212     if (samplerType == WgslSamplerTypeConfig::Texture)
213     {
214         output << "texture";
215         if (IsShadowSampler(type.getBasicType()))
216         {
217             output << "_depth";
218         }
219 
220         if (IsSamplerMS(type.getBasicType()))
221         {
222             output << "_multisampled";
223             ASSERT(IsSampler2D(type.getBasicType()));
224             // Unsupported in wGSL, it seems.
225             ASSERT(!IsSampler2DMSArray(type.getBasicType()));
226         }
227 
228         if (IsSampler2D(type.getBasicType()) || IsSampler2DArray(type.getBasicType()))
229         {
230             output << "_2d";
231         }
232         else if (IsSampler3D(type.getBasicType()))
233         {
234             output << "_3d";
235         }
236         else if (IsSamplerCube(type.getBasicType()))
237         {
238             output << "_cube";
239         }
240 
241         if (IsSamplerArray(type.getBasicType()))
242         {
243             ASSERT(!IsSampler3D(type.getBasicType()));
244             output << "_array";
245         }
246 
247         // Shadow samplers are always floating point in both GLSL and WGSL and don't need to be
248         // parameterized.
249         if (!IsShadowSampler(type.getBasicType()))
250         {
251             output << "<";
252             if (!IsIntegerSampler(type.getBasicType()))
253             {
254                 output << "f32";
255             }
256             else if (!IsIntegerSamplerUnsigned(type.getBasicType()))
257             {
258                 output << "i32";
259             }
260             else
261             {
262                 output << "u32";
263             }
264             output << ">";
265         }
266         if (type.getMemoryQualifier().readonly || type.getMemoryQualifier().writeonly)
267         {
268             // TODO(anglebug.com/42267100): implement memory qualifiers.
269             UNIMPLEMENTED();
270         }
271     }
272     else
273     {
274         ASSERT(samplerType == WgslSamplerTypeConfig::Sampler);
275         // sampler or sampler_comparison.
276         if (IsShadowSampler(type.getBasicType()))
277         {
278             output << "sampler_comparison";
279         }
280         else
281         {
282             output << "sampler";
283         }
284     }
285 }
286 
287 template void WriteWgslSamplerType<TInfoSinkBase>(TInfoSinkBase &output,
288                                                   const TType &type,
289                                                   WgslSamplerTypeConfig samplerType);
290 
291 template void WriteWgslSamplerType<TStringStream>(TStringStream &output,
292                                                   const TType &type,
293                                                   WgslSamplerTypeConfig samplerType);
294 
MakeUniformWrapperStructName(const TType * type)295 ImmutableString MakeUniformWrapperStructName(const TType *type)
296 {
297     return BuildConcatenatedImmutableString(kWrappedPrefix, type->getBuiltInTypeNameString());
298 }
299 
ElementTypeNeedsUniformWrapperStruct(bool inUniformAddressSpace,const TType * type)300 bool ElementTypeNeedsUniformWrapperStruct(bool inUniformAddressSpace, const TType *type)
301 {
302     // Only types that are used as array element types in the uniform address space need wrapper
303     // structs. If the array element type is a struct it does not need to be wrapped in another
304     // layer of struct.
305     if (!inUniformAddressSpace || !type->isArray() || type->getStruct())
306     {
307         return false;
308     }
309 
310     TType elementType = *type;
311     elementType.toArrayElementType();
312     // If the array element type's stride is already a multiple of 16, it does not need a wrapper
313     // struct.
314     //
315     // The remaining possible element types are scalars, vectors, matrices, and other arrays.
316     // - Scalars need to be aligned to 16.
317     // - vec3 and vec4 are already aligned to 16, but vec2 needs to be aligned.
318     // - Matrices are aligned to 16 automatically, except matCx2 which already needs to be handled
319     // by specialized code anyway.
320     // TODO(anglebug.com/376553328): re-enable this ASSERT once matCx2 are handled.
321     // ASSERT(!type->isMatrix() || type->getRows() != 2);
322     // - WebGL2 doesn't support nested arrays so this won't either, though support wouldn't be hard.
323     ASSERT(!elementType.isArray());
324 
325     return elementType.isScalar() || (elementType.isVector() && elementType.getNominalSize() == 2);
326 }
327 
FindGlobalVars(TIntermBlock * root)328 GlobalVars FindGlobalVars(TIntermBlock *root)
329 {
330     GlobalVars globals;
331     for (TIntermNode *node : *root->getSequence())
332     {
333         if (TIntermDeclaration *declNode = node->getAsDeclarationNode())
334         {
335             Declaration decl = ViewDeclaration(*declNode);
336             globals.insert({decl.symbol.variable().name(), declNode});
337         }
338     }
339     return globals;
340 }
341 
342 }  // namespace sh
343