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