1 //
2 // Copyright 2019 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 // Wrapper for Khronos glslang compiler.
7 //
8
9 #include "libANGLE/renderer/glslang_wrapper_utils.h"
10
11 // glslang has issues with some specific warnings.
12 ANGLE_DISABLE_EXTRA_SEMI_WARNING
13 ANGLE_DISABLE_SHADOWING_WARNING
14
15 // glslang's version of ShaderLang.h, not to be confused with ANGLE's.
16 #include <glslang/Public/ShaderLang.h>
17
18 // Other glslang includes.
19 #include <SPIRV/GlslangToSpv.h>
20 #include <StandAlone/ResourceLimits.h>
21
22 ANGLE_REENABLE_SHADOWING_WARNING
23 ANGLE_REENABLE_EXTRA_SEMI_WARNING
24
25 // SPIR-V headers include for AST transformation.
26 #include <spirv/unified1/spirv.hpp>
27
28 // SPIR-V tools include for AST validation.
29 #include <spirv-tools/libspirv.hpp>
30
31 #include <array>
32 #include <numeric>
33
34 #include "common/FixedVector.h"
35 #include "common/string_utils.h"
36 #include "common/utilities.h"
37 #include "libANGLE/Caps.h"
38 #include "libANGLE/ProgramLinkedResources.h"
39
40 #define ANGLE_GLSLANG_CHECK(CALLBACK, TEST, ERR) \
41 do \
42 { \
43 if (ANGLE_UNLIKELY(!(TEST))) \
44 { \
45 return CALLBACK(ERR); \
46 } \
47 \
48 } while (0)
49
50 namespace rx
51 {
52 namespace
53 {
54 constexpr char kXfbDeclMarker[] = "@@ XFB-DECL @@";
55 constexpr char kXfbOutMarker[] = "@@ XFB-OUT @@;";
56 constexpr char kXfbBuiltInPrefix[] = "xfbANGLE";
57
58 template <size_t N>
ConstStrLen(const char (&)[N])59 constexpr size_t ConstStrLen(const char (&)[N])
60 {
61 static_assert(N > 0, "C++ shouldn't allow N to be zero");
62
63 // The length of a string defined as a char array is the size of the array minus 1 (the
64 // terminating '\0').
65 return N - 1;
66 }
67
GetBuiltInResourcesFromCaps(const gl::Caps & caps,TBuiltInResource * outBuiltInResources)68 void GetBuiltInResourcesFromCaps(const gl::Caps &caps, TBuiltInResource *outBuiltInResources)
69 {
70 outBuiltInResources->maxDrawBuffers = caps.maxDrawBuffers;
71 outBuiltInResources->maxAtomicCounterBindings = caps.maxAtomicCounterBufferBindings;
72 outBuiltInResources->maxAtomicCounterBufferSize = caps.maxAtomicCounterBufferSize;
73 outBuiltInResources->maxClipPlanes = caps.maxClipPlanes;
74 outBuiltInResources->maxCombinedAtomicCounterBuffers = caps.maxCombinedAtomicCounterBuffers;
75 outBuiltInResources->maxCombinedAtomicCounters = caps.maxCombinedAtomicCounters;
76 outBuiltInResources->maxCombinedImageUniforms = caps.maxCombinedImageUniforms;
77 outBuiltInResources->maxCombinedTextureImageUnits = caps.maxCombinedTextureImageUnits;
78 outBuiltInResources->maxCombinedShaderOutputResources = caps.maxCombinedShaderOutputResources;
79 outBuiltInResources->maxComputeWorkGroupCountX = caps.maxComputeWorkGroupCount[0];
80 outBuiltInResources->maxComputeWorkGroupCountY = caps.maxComputeWorkGroupCount[1];
81 outBuiltInResources->maxComputeWorkGroupCountZ = caps.maxComputeWorkGroupCount[2];
82 outBuiltInResources->maxComputeWorkGroupSizeX = caps.maxComputeWorkGroupSize[0];
83 outBuiltInResources->maxComputeWorkGroupSizeY = caps.maxComputeWorkGroupSize[1];
84 outBuiltInResources->maxComputeWorkGroupSizeZ = caps.maxComputeWorkGroupSize[2];
85 outBuiltInResources->minProgramTexelOffset = caps.minProgramTexelOffset;
86 outBuiltInResources->maxFragmentUniformVectors = caps.maxFragmentUniformVectors;
87 outBuiltInResources->maxFragmentInputComponents = caps.maxFragmentInputComponents;
88 outBuiltInResources->maxGeometryInputComponents = caps.maxGeometryInputComponents;
89 outBuiltInResources->maxGeometryOutputComponents = caps.maxGeometryOutputComponents;
90 outBuiltInResources->maxGeometryOutputVertices = caps.maxGeometryOutputVertices;
91 outBuiltInResources->maxGeometryTotalOutputComponents = caps.maxGeometryTotalOutputComponents;
92 outBuiltInResources->maxLights = caps.maxLights;
93 outBuiltInResources->maxProgramTexelOffset = caps.maxProgramTexelOffset;
94 outBuiltInResources->maxVaryingComponents = caps.maxVaryingComponents;
95 outBuiltInResources->maxVaryingVectors = caps.maxVaryingVectors;
96 outBuiltInResources->maxVertexAttribs = caps.maxVertexAttributes;
97 outBuiltInResources->maxVertexOutputComponents = caps.maxVertexOutputComponents;
98 outBuiltInResources->maxVertexUniformVectors = caps.maxVertexUniformVectors;
99 outBuiltInResources->maxClipDistances = caps.maxClipDistances;
100 }
101
102 // Test if there are non-zero indices in the uniform name, returning false in that case. This
103 // happens for multi-dimensional arrays, where a uniform is created for every possible index of the
104 // array (except for the innermost dimension). When assigning decorations (set/binding/etc), only
105 // the indices corresponding to the first element of the array should be specified. This function
106 // is used to skip the other indices.
107 //
108 // If useOldRewriteStructSamplers, there are multiple samplers extracted out of struct arrays
109 // though, so the above only applies to the sampler array defined in the struct.
UniformNameIsIndexZero(const std::string & name,bool excludeCheckForOwningStructArrays)110 bool UniformNameIsIndexZero(const std::string &name, bool excludeCheckForOwningStructArrays)
111 {
112 size_t lastBracketClose = 0;
113
114 if (excludeCheckForOwningStructArrays)
115 {
116 size_t lastDot = name.find_last_of('.');
117 if (lastDot != std::string::npos)
118 {
119 lastBracketClose = lastDot;
120 }
121 }
122
123 while (true)
124 {
125 size_t openBracket = name.find('[', lastBracketClose);
126 if (openBracket == std::string::npos)
127 {
128 break;
129 }
130 size_t closeBracket = name.find(']', openBracket);
131
132 // If the index between the brackets is not zero, ignore this uniform.
133 if (name.substr(openBracket + 1, closeBracket - openBracket - 1) != "0")
134 {
135 return false;
136 }
137 lastBracketClose = closeBracket;
138 }
139
140 return true;
141 }
142
MappedSamplerNameNeedsUserDefinedPrefix(const std::string & originalName)143 bool MappedSamplerNameNeedsUserDefinedPrefix(const std::string &originalName)
144 {
145 return originalName.find('.') == std::string::npos;
146 }
147
148 template <typename OutputIter, typename ImplicitIter>
CountExplicitOutputs(OutputIter outputsBegin,OutputIter outputsEnd,ImplicitIter implicitsBegin,ImplicitIter implicitsEnd)149 uint32_t CountExplicitOutputs(OutputIter outputsBegin,
150 OutputIter outputsEnd,
151 ImplicitIter implicitsBegin,
152 ImplicitIter implicitsEnd)
153 {
154 auto reduce = [implicitsBegin, implicitsEnd](uint32_t count, const sh::ShaderVariable &var) {
155 bool isExplicit = std::find(implicitsBegin, implicitsEnd, var.name) == implicitsEnd;
156 return count + isExplicit;
157 };
158
159 return std::accumulate(outputsBegin, outputsEnd, 0, reduce);
160 }
161
AddShaderInterfaceVariable(ShaderInterfaceVariableInfoMap * infoMap,const std::string & varName)162 ShaderInterfaceVariableInfo *AddShaderInterfaceVariable(ShaderInterfaceVariableInfoMap *infoMap,
163 const std::string &varName)
164 {
165 ASSERT(infoMap->find(varName) == infoMap->end());
166 return &(*infoMap)[varName];
167 }
168
GetShaderInterfaceVariable(ShaderInterfaceVariableInfoMap * infoMap,const std::string & varName)169 ShaderInterfaceVariableInfo *GetShaderInterfaceVariable(ShaderInterfaceVariableInfoMap *infoMap,
170 const std::string &varName)
171 {
172 ASSERT(infoMap->find(varName) != infoMap->end());
173 return &(*infoMap)[varName];
174 }
175
AddResourceInfoToAllStages(ShaderInterfaceVariableInfoMap * infoMap,const std::string & varName,uint32_t descriptorSet,uint32_t binding)176 ShaderInterfaceVariableInfo *AddResourceInfoToAllStages(ShaderInterfaceVariableInfoMap *infoMap,
177 const std::string &varName,
178 uint32_t descriptorSet,
179 uint32_t binding)
180 {
181 gl::ShaderBitSet allStages;
182 allStages.set();
183
184 ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName);
185 info->descriptorSet = descriptorSet;
186 info->binding = binding;
187 info->activeStages = allStages;
188 return info;
189 }
190
AddResourceInfo(ShaderInterfaceVariableInfoMap * infoMap,const std::string & varName,uint32_t descriptorSet,uint32_t binding,const gl::ShaderType shaderType)191 ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
192 const std::string &varName,
193 uint32_t descriptorSet,
194 uint32_t binding,
195 const gl::ShaderType shaderType)
196 {
197 gl::ShaderBitSet stages;
198 stages.set(shaderType);
199
200 ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName);
201 info->descriptorSet = descriptorSet;
202 info->binding = binding;
203 info->activeStages = stages;
204 return info;
205 }
206
207 // Add location information for an in/out variable.
AddLocationInfo(ShaderInterfaceVariableInfoMap * infoMap,const std::string & varName,uint32_t location,uint32_t component,gl::ShaderType stage)208 ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
209 const std::string &varName,
210 uint32_t location,
211 uint32_t component,
212 gl::ShaderType stage)
213 {
214 // The info map for this name may or may not exist already. This function merges the
215 // location/component information.
216 ShaderInterfaceVariableInfo *info = &(*infoMap)[varName];
217
218 ASSERT(info->descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
219 ASSERT(info->binding == ShaderInterfaceVariableInfo::kInvalid);
220 ASSERT(info->location == ShaderInterfaceVariableInfo::kInvalid);
221 ASSERT(info->component == ShaderInterfaceVariableInfo::kInvalid);
222
223 info->location = location;
224 info->component = component;
225 info->activeStages.set(stage);
226
227 return info;
228 }
229
230 // Modify an existing out variable and add transform feedback information.
SetXfbInfo(ShaderInterfaceVariableInfoMap * infoMap,const std::string & varName,uint32_t xfbBuffer,uint32_t xfbOffset,uint32_t xfbStride)231 ShaderInterfaceVariableInfo *SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
232 const std::string &varName,
233 uint32_t xfbBuffer,
234 uint32_t xfbOffset,
235 uint32_t xfbStride)
236 {
237 ShaderInterfaceVariableInfo *info = GetShaderInterfaceVariable(infoMap, varName);
238
239 ASSERT(info->xfbBuffer == ShaderInterfaceVariableInfo::kInvalid);
240 ASSERT(info->xfbOffset == ShaderInterfaceVariableInfo::kInvalid);
241 ASSERT(info->xfbStride == ShaderInterfaceVariableInfo::kInvalid);
242
243 info->xfbBuffer = xfbBuffer;
244 info->xfbOffset = xfbOffset;
245 info->xfbStride = xfbStride;
246 return info;
247 }
248
SubstituteTransformFeedbackMarkers(const std::string & originalSource,const std::string & xfbDecl,const std::string & xfbOut)249 std::string SubstituteTransformFeedbackMarkers(const std::string &originalSource,
250 const std::string &xfbDecl,
251 const std::string &xfbOut)
252 {
253 const size_t xfbDeclMarkerStart = originalSource.find(kXfbDeclMarker);
254 const size_t xfbDeclMarkerEnd = xfbDeclMarkerStart + ConstStrLen(kXfbDeclMarker);
255
256 const size_t xfbOutMarkerStart = originalSource.find(kXfbOutMarker, xfbDeclMarkerStart);
257 const size_t xfbOutMarkerEnd = xfbOutMarkerStart + ConstStrLen(kXfbOutMarker);
258
259 // The shader is the following form:
260 //
261 // ..part1..
262 // @@ XFB-DECL @@
263 // ..part2..
264 // @@ XFB-OUT @@;
265 // ..part3..
266 //
267 // Construct the string by concatenating these five pieces, replacing the markers with the given
268 // values.
269 std::string result;
270
271 result.append(&originalSource[0], &originalSource[xfbDeclMarkerStart]);
272 result.append(xfbDecl);
273 result.append(&originalSource[xfbDeclMarkerEnd], &originalSource[xfbOutMarkerStart]);
274 result.append(xfbOut);
275 result.append(&originalSource[xfbOutMarkerEnd], &originalSource[originalSource.size()]);
276
277 return result;
278 }
279
GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVarying & varying,const gl::UniformTypeInfo & info,size_t strideBytes,size_t offset,const std::string & bufferIndex)280 std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVarying &varying,
281 const gl::UniformTypeInfo &info,
282 size_t strideBytes,
283 size_t offset,
284 const std::string &bufferIndex)
285 {
286 std::ostringstream result;
287
288 ASSERT(strideBytes % 4 == 0);
289 size_t stride = strideBytes / 4;
290
291 const size_t arrayIndexStart = varying.arrayIndex == GL_INVALID_INDEX ? 0 : varying.arrayIndex;
292 const size_t arrayIndexEnd = arrayIndexStart + varying.size();
293
294 for (size_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
295 {
296 for (int col = 0; col < info.columnCount; ++col)
297 {
298 for (int row = 0; row < info.rowCount; ++row)
299 {
300 result << "xfbOut" << bufferIndex << "[" << sh::vk::kDriverUniformsVarName
301 << ".xfbBufferOffsets[" << bufferIndex
302 << "] + (gl_VertexIndex + gl_InstanceIndex * "
303 << sh::vk::kDriverUniformsVarName << ".xfbVerticesPerDraw) * " << stride
304 << " + " << offset << "] = " << info.glslAsFloat << "("
305 << varying.mappedName;
306
307 if (varying.isArray())
308 {
309 result << "[" << arrayIndex << "]";
310 }
311
312 if (info.columnCount > 1)
313 {
314 result << "[" << col << "]";
315 }
316
317 if (info.rowCount > 1)
318 {
319 result << "[" << row << "]";
320 }
321
322 result << ");\n";
323 ++offset;
324 }
325 }
326 }
327
328 return result.str();
329 }
330
GenerateTransformFeedbackEmulationOutputs(GlslangSourceOptions & options,const gl::ProgramState & programState,GlslangProgramInterfaceInfo * programInterfaceInfo,std::string * vertexShader,ShaderInterfaceVariableInfoMap * variableInfoMapOut)331 void GenerateTransformFeedbackEmulationOutputs(GlslangSourceOptions &options,
332 const gl::ProgramState &programState,
333 GlslangProgramInterfaceInfo *programInterfaceInfo,
334 std::string *vertexShader,
335 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
336 {
337 const std::vector<gl::TransformFeedbackVarying> &varyings =
338 programState.getLinkedTransformFeedbackVaryings();
339 const std::vector<GLsizei> &bufferStrides = programState.getTransformFeedbackStrides();
340 const bool isInterleaved =
341 programState.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
342 const size_t bufferCount = isInterleaved ? 1 : varyings.size();
343
344 const std::string xfbSet = Str(programInterfaceInfo->uniformsAndXfbDescriptorSetIndex);
345 std::vector<std::string> xfbIndices(bufferCount);
346
347 std::string xfbDecl;
348
349 for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
350 {
351 const std::string xfbBinding = Str(programInterfaceInfo->currentUniformBindingIndex);
352 xfbIndices[bufferIndex] = Str(bufferIndex);
353
354 std::string bufferName = GetXfbBufferName(bufferIndex);
355
356 xfbDecl += "layout(set = " + xfbSet + ", binding = " + xfbBinding + ") buffer " +
357 bufferName + " { float xfbOut" + Str(bufferIndex) + "[]; };\n";
358
359 // Add this entry to the info map, so we can easily assert that every resource has an entry
360 // in this map.
361 AddResourceInfo(variableInfoMapOut, bufferName,
362 programInterfaceInfo->uniformsAndXfbDescriptorSetIndex,
363 programInterfaceInfo->currentUniformBindingIndex, gl::ShaderType::Vertex);
364 ++programInterfaceInfo->currentUniformBindingIndex;
365 }
366
367 std::string xfbOut =
368 "if (" + std::string(sh::vk::kDriverUniformsVarName) + ".xfbActiveUnpaused != 0)\n{\n";
369 size_t outputOffset = 0;
370 for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
371 {
372 const size_t bufferIndex = isInterleaved ? 0 : varyingIndex;
373 const gl::TransformFeedbackVarying &varying = varyings[varyingIndex];
374
375 // For every varying, output to the respective buffer packed. If interleaved, the output is
376 // always to the same buffer, but at different offsets.
377 const gl::UniformTypeInfo &info = gl::GetUniformTypeInfo(varying.type);
378 xfbOut += GenerateTransformFeedbackVaryingOutput(varying, info, bufferStrides[bufferIndex],
379 outputOffset, xfbIndices[bufferIndex]);
380
381 if (isInterleaved)
382 {
383 outputOffset += info.columnCount * info.rowCount * varying.size();
384 }
385 }
386 xfbOut += "}\n";
387
388 *vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, xfbDecl, xfbOut);
389 }
390
IsFirstRegisterOfVarying(const gl::PackedVaryingRegister & varyingReg)391 bool IsFirstRegisterOfVarying(const gl::PackedVaryingRegister &varyingReg)
392 {
393 const gl::PackedVarying &varying = *varyingReg.packedVarying;
394
395 // In Vulkan GLSL, struct fields are not allowed to have location assignments. The varying of a
396 // struct type is thus given a location equal to the one assigned to its first field.
397 if (varying.isStructField() && varying.fieldIndex > 0)
398 {
399 return false;
400 }
401
402 // Similarly, assign array varying locations to the assigned location of the first element.
403 if (varyingReg.varyingArrayIndex != 0 || (varying.isArrayElement() && varying.arrayIndex != 0))
404 {
405 return false;
406 }
407
408 // Similarly, assign matrix varying locations to the assigned location of the first row.
409 if (varyingReg.varyingRowIndex != 0)
410 {
411 return false;
412 }
413
414 return true;
415 }
416
417 // Calculates XFB layout qualifier arguments for each tranform feedback varying. Stores calculated
418 // values for the SPIR-V transformation.
GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState & programState,const gl::ProgramLinkedResources & resources,std::string * vertexShader,uint32_t * locationsUsedForXfbExtensionOut)419 void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programState,
420 const gl::ProgramLinkedResources &resources,
421 std::string *vertexShader,
422 uint32_t *locationsUsedForXfbExtensionOut)
423 {
424 const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
425 programState.getLinkedTransformFeedbackVaryings();
426
427 std::string xfbDecl;
428 std::string xfbOut;
429
430 for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
431 {
432 const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
433 const std::string &tfVaryingName = tfVarying.mappedName;
434
435 if (tfVarying.isBuiltIn())
436 {
437 // For simplicity, create a copy of every builtin that's captured so xfb qualifiers
438 // could be added to that instead. This allows the SPIR-V transformation to ignore
439 // OpMemberName and OpMemberDecorate instructions. Note that capturing gl_Position
440 // already requires such a copy, since the translator modifies this value at the end of
441 // main. Capturing the rest of the built-ins are niche enough that the inefficiency
442 // involved in doing this is not a concern.
443
444 uint32_t xfbVaryingLocation = resources.varyingPacking.getMaxSemanticIndex() +
445 ++(*locationsUsedForXfbExtensionOut);
446
447 std::string xfbVaryingName = kXfbBuiltInPrefix + tfVaryingName;
448
449 // Add declaration and initialization code for the new varying.
450 std::string varyingType = gl::GetGLSLTypeString(tfVarying.type);
451 xfbDecl += "layout(location = " + Str(xfbVaryingLocation) + ") out " + varyingType +
452 " " + xfbVaryingName + ";\n";
453 xfbOut += xfbVaryingName + " = " + tfVaryingName + ";\n";
454 }
455 }
456
457 *vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, xfbDecl, xfbOut);
458 }
459
AssignAttributeLocations(const gl::ProgramExecutable & programExecutable,gl::ShaderType stage,ShaderInterfaceVariableInfoMap * variableInfoMapOut)460 void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
461 gl::ShaderType stage,
462 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
463 {
464 // Assign attribute locations for the vertex shader.
465 for (const sh::ShaderVariable &attribute : programExecutable.getProgramInputs())
466 {
467 ASSERT(attribute.active);
468
469 AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location,
470 ShaderInterfaceVariableInfo::kInvalid, stage);
471 }
472 }
473
AssignOutputLocations(const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,ShaderInterfaceVariableInfoMap * variableInfoMapOut)474 void AssignOutputLocations(const gl::ProgramExecutable &programExecutable,
475 const gl::ShaderType shaderType,
476 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
477 {
478 // Assign output locations for the fragment shader.
479 ASSERT(shaderType == gl::ShaderType::Fragment);
480 // TODO(syoussefi): Add support for EXT_blend_func_extended. http://anglebug.com/3385
481 const auto &outputLocations = programExecutable.getOutputLocations();
482 const auto &outputVariables = programExecutable.getOutputVariables();
483 const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask",
484 "gl_FragStencilRefARB"};
485
486 for (const gl::VariableLocation &outputLocation : outputLocations)
487 {
488 if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
489 {
490 const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];
491
492 uint32_t location = 0;
493 if (outputVar.location != -1)
494 {
495 location = outputVar.location;
496 }
497 else if (std::find(implicitOutputs.begin(), implicitOutputs.end(), outputVar.name) ==
498 implicitOutputs.end())
499 {
500 // If there is only one output, it is allowed not to have a location qualifier, in
501 // which case it defaults to 0. GLSL ES 3.00 spec, section 4.3.8.2.
502 ASSERT(CountExplicitOutputs(outputVariables.begin(), outputVariables.end(),
503 implicitOutputs.begin(), implicitOutputs.end()) == 1);
504 }
505
506 AddLocationInfo(variableInfoMapOut, outputVar.mappedName, location,
507 ShaderInterfaceVariableInfo::kInvalid, shaderType);
508 }
509 }
510
511 // When no fragment output is specified by the shader, the translator outputs webgl_FragColor or
512 // webgl_FragData. Add an entry for these. Even though the translator is already assigning
513 // location 0 to these entries, adding an entry for them here allows us to ASSERT that every
514 // shader interface variable is processed during the SPIR-V transformation. This is done when
515 // iterating the ids provided by OpEntryPoint.
516 AddLocationInfo(variableInfoMapOut, "webgl_FragColor", 0, 0, shaderType);
517 AddLocationInfo(variableInfoMapOut, "webgl_FragData", 0, 0, shaderType);
518 }
519
AssignVaryingLocations(const GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)520 void AssignVaryingLocations(const GlslangSourceOptions &options,
521 const gl::ProgramExecutable &programExecutable,
522 const gl::ShaderType shaderType,
523 GlslangProgramInterfaceInfo *programInterfaceInfo,
524 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
525 {
526 uint32_t locationsUsedForEmulation = programInterfaceInfo->locationsUsedForXfbExtension;
527
528 // Substitute layout and qualifier strings for the position varying added for line raster
529 // emulation.
530 if (options.emulateBresenhamLines)
531 {
532 uint32_t lineRasterEmulationPositionLocation = locationsUsedForEmulation++;
533
534 AddLocationInfo(&(*variableInfoMapOut)[shaderType], sh::vk::kLineRasterEmulationPosition,
535 lineRasterEmulationPositionLocation, ShaderInterfaceVariableInfo::kInvalid,
536 shaderType);
537 }
538
539 // Assign varying locations.
540 for (const gl::PackedVaryingRegister &varyingReg :
541 programExecutable.getResources().varyingPacking.getRegisterList())
542 {
543 if (!IsFirstRegisterOfVarying(varyingReg))
544 {
545 continue;
546 }
547
548 const gl::PackedVarying &varying = *varyingReg.packedVarying;
549
550 uint32_t location = varyingReg.registerRow + locationsUsedForEmulation;
551 uint32_t component = ShaderInterfaceVariableInfo::kInvalid;
552 if (varyingReg.registerColumn > 0)
553 {
554 ASSERT(!varying.varying().isStruct());
555 ASSERT(!gl::IsMatrixType(varying.varying().type));
556 component = varyingReg.registerColumn;
557 }
558
559 // In the following:
560 //
561 // struct S { vec4 field; };
562 // out S varStruct;
563 //
564 // "_uvarStruct" is found through |parentStructMappedName|, with |varying->mappedName|
565 // being "_ufield". In such a case, use |parentStructMappedName|.
566 if (varying.frontVarying.varying && (varying.frontVarying.stage == shaderType))
567 {
568 const std::string &name = varying.isStructField()
569 ? varying.frontVarying.parentStructMappedName
570 : varying.frontVarying.varying->mappedName;
571 AddLocationInfo(&(*variableInfoMapOut)[varying.frontVarying.stage], name, location,
572 component, varying.frontVarying.stage);
573 }
574 if (varying.backVarying.varying && (varying.backVarying.stage == shaderType))
575 {
576 const std::string &name = varying.isStructField()
577 ? varying.backVarying.parentStructMappedName
578 : varying.backVarying.varying->mappedName;
579 AddLocationInfo(&(*variableInfoMapOut)[varying.backVarying.stage], name, location,
580 component, varying.backVarying.stage);
581 }
582 }
583
584 // Add an entry for inactive varyings.
585 const gl::ShaderMap<std::vector<std::string>> &inactiveVaryingMappedNames =
586 programExecutable.getResources().varyingPacking.getInactiveVaryingMappedNames();
587 for (const std::string &varyingName : inactiveVaryingMappedNames[shaderType])
588 {
589 bool isBuiltin = angle::BeginsWith(varyingName, "gl_");
590 if (isBuiltin)
591 {
592 continue;
593 }
594
595 // If name is already in the map, it will automatically have marked all other stages
596 // inactive.
597 if ((*variableInfoMapOut)[shaderType].find(varyingName) !=
598 (*variableInfoMapOut)[shaderType].end())
599 {
600 continue;
601 }
602
603 // Otherwise, add an entry for it with all locations inactive.
604 ShaderInterfaceVariableInfo *info = &(*variableInfoMapOut)[shaderType][varyingName];
605 ASSERT(info->location == ShaderInterfaceVariableInfo::kInvalid);
606 }
607 }
608
609 // Calculates XFB layout qualifier arguments for each tranform feedback varying. Stores calculated
610 // values for the SPIR-V transformation.
AssignTransformFeedbackExtensionQualifiers(const gl::ProgramExecutable & programExecutable,uint32_t locationsUsedForXfbExtension,const gl::ShaderType shaderType,ShaderInterfaceVariableInfoMap * variableInfoMapOut)611 void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramExecutable &programExecutable,
612 uint32_t locationsUsedForXfbExtension,
613 const gl::ShaderType shaderType,
614 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
615 {
616 const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
617 programExecutable.getLinkedTransformFeedbackVaryings();
618 const std::vector<GLsizei> &varyingStrides = programExecutable.getTransformFeedbackStrides();
619 const bool isInterleaved =
620 programExecutable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
621
622 std::string xfbDecl;
623 std::string xfbOut;
624 uint32_t currentOffset = 0;
625 uint32_t currentStride = 0;
626 uint32_t bufferIndex = 0;
627 uint32_t currentBuiltinLocation = 0;
628
629 for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
630 {
631 if (isInterleaved)
632 {
633 bufferIndex = 0;
634 if (varyingIndex > 0)
635 {
636 const gl::TransformFeedbackVarying &prev = tfVaryings[varyingIndex - 1];
637 currentOffset += prev.size() * gl::VariableExternalSize(prev.type);
638 }
639 currentStride = varyingStrides[0];
640 }
641 else
642 {
643 bufferIndex = varyingIndex;
644 currentOffset = 0;
645 currentStride = varyingStrides[varyingIndex];
646 }
647
648 const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
649 const std::string &tfVaryingName = tfVarying.mappedName;
650
651 if (tfVarying.isBuiltIn())
652 {
653 uint32_t xfbVaryingLocation = currentBuiltinLocation++;
654 std::string xfbVaryingName = kXfbBuiltInPrefix + tfVaryingName;
655
656 ASSERT(xfbVaryingLocation < locationsUsedForXfbExtension);
657
658 AddLocationInfo(variableInfoMapOut, xfbVaryingName, xfbVaryingLocation,
659 ShaderInterfaceVariableInfo::kInvalid, shaderType);
660 SetXfbInfo(variableInfoMapOut, xfbVaryingName, bufferIndex, currentOffset,
661 currentStride);
662 }
663 else if (!tfVarying.isArray() || tfVarying.arrayIndex == 0)
664 {
665 // Note: capturing individual array elements using the Vulkan transform feedback
666 // extension is not supported, and it unlikely to be ever supported (on the contrary, it
667 // may be removed from the GLES spec). http://anglebug.com/4140
668
669 // Find the varying with this name. If a struct is captured, we would be iterating over
670 // its fields, and the name of the varying is found through parentStructMappedName. Not
671 // only that, but also we should only do this for the first field of the struct.
672 const gl::PackedVarying *originalVarying = nullptr;
673 for (const gl::PackedVaryingRegister &varyingReg :
674 programExecutable.getResources().varyingPacking.getRegisterList())
675 {
676 if (!IsFirstRegisterOfVarying(varyingReg))
677 {
678 continue;
679 }
680
681 const gl::PackedVarying *varying = varyingReg.packedVarying;
682
683 if (varying->frontVarying.varying->name == tfVarying.name)
684 {
685 originalVarying = varying;
686 break;
687 }
688 }
689
690 if (originalVarying)
691 {
692 const std::string &mappedName =
693 originalVarying->isStructField()
694 ? originalVarying->frontVarying.parentStructMappedName
695 : originalVarying->frontVarying.varying->mappedName;
696
697 // Set xfb info for this varying. AssignVaryingLocations should have already added
698 // location information for these varyings.
699 SetXfbInfo(variableInfoMapOut, mappedName, bufferIndex, currentOffset,
700 currentStride);
701 }
702 }
703 }
704 }
705
AssignUniformBindings(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)706 void AssignUniformBindings(GlslangSourceOptions &options,
707 const gl::ProgramExecutable &programExecutable,
708 const gl::ShaderType shaderType,
709 GlslangProgramInterfaceInfo *programInterfaceInfo,
710 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
711 {
712 if (programExecutable.hasLinkedShaderStage(shaderType))
713 {
714 AddResourceInfo(&(*variableInfoMapOut)[shaderType], kDefaultUniformNames[shaderType],
715 programInterfaceInfo->uniformsAndXfbDescriptorSetIndex,
716 programInterfaceInfo->currentUniformBindingIndex, shaderType);
717 ++programInterfaceInfo->currentUniformBindingIndex;
718
719 // Assign binding to the driver uniforms block
720 AddResourceInfoToAllStages(&(*variableInfoMapOut)[shaderType],
721 sh::vk::kDriverUniformsBlockName,
722 programInterfaceInfo->driverUniformsDescriptorSetIndex, 0);
723 }
724 }
725
726 // TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
727 // shader stages.
AssignInterfaceBlockBindings(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const std::vector<gl::InterfaceBlock> & blocks,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)728 void AssignInterfaceBlockBindings(GlslangSourceOptions &options,
729 const gl::ProgramExecutable &programExecutable,
730 const std::vector<gl::InterfaceBlock> &blocks,
731 const gl::ShaderType shaderType,
732 GlslangProgramInterfaceInfo *programInterfaceInfo,
733 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
734 {
735 for (const gl::InterfaceBlock &block : blocks)
736 {
737 if (!block.isArray || block.arrayElement == 0)
738 {
739 // TODO: http://anglebug.com/4523: All blocks should be active
740 if (programExecutable.hasLinkedShaderStage(shaderType) && block.isActive(shaderType))
741 {
742 AddResourceInfo(&(*variableInfoMapOut)[shaderType], block.mappedName,
743 programInterfaceInfo->shaderResourceDescriptorSetIndex,
744 programInterfaceInfo->currentShaderResourceBindingIndex,
745 shaderType);
746 ++programInterfaceInfo->currentShaderResourceBindingIndex;
747 }
748 }
749 }
750 }
751
752 // TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
753 // shader stages.
AssignAtomicCounterBufferBindings(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const std::vector<gl::AtomicCounterBuffer> & buffers,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)754 void AssignAtomicCounterBufferBindings(GlslangSourceOptions &options,
755 const gl::ProgramExecutable &programExecutable,
756 const std::vector<gl::AtomicCounterBuffer> &buffers,
757 const gl::ShaderType shaderType,
758 GlslangProgramInterfaceInfo *programInterfaceInfo,
759 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
760 {
761 if (buffers.size() == 0)
762 {
763 return;
764 }
765
766 if (programExecutable.hasLinkedShaderStage(shaderType))
767 {
768 AddResourceInfo(&(*variableInfoMapOut)[shaderType], sh::vk::kAtomicCountersBlockName,
769 programInterfaceInfo->shaderResourceDescriptorSetIndex,
770 programInterfaceInfo->currentShaderResourceBindingIndex, shaderType);
771 ++programInterfaceInfo->currentShaderResourceBindingIndex;
772 }
773 }
774
775 // TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
776 // shader stages.
AssignImageBindings(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const std::vector<gl::LinkedUniform> & uniforms,const gl::RangeUI & imageUniformRange,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)777 void AssignImageBindings(GlslangSourceOptions &options,
778 const gl::ProgramExecutable &programExecutable,
779 const std::vector<gl::LinkedUniform> &uniforms,
780 const gl::RangeUI &imageUniformRange,
781 const gl::ShaderType shaderType,
782 GlslangProgramInterfaceInfo *programInterfaceInfo,
783 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
784 {
785 for (unsigned int uniformIndex : imageUniformRange)
786 {
787 const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
788
789 std::string name = imageUniform.mappedName;
790 if (GetImageNameWithoutIndices(&name))
791 {
792 if (programExecutable.hasLinkedShaderStage(shaderType))
793 {
794 AddResourceInfo(&(*variableInfoMapOut)[shaderType], name,
795 programInterfaceInfo->shaderResourceDescriptorSetIndex,
796 programInterfaceInfo->currentShaderResourceBindingIndex,
797 shaderType);
798 ++programInterfaceInfo->currentShaderResourceBindingIndex;
799 }
800 }
801 }
802 }
803
AssignNonTextureBindings(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)804 void AssignNonTextureBindings(GlslangSourceOptions &options,
805 const gl::ProgramExecutable &programExecutable,
806 const gl::ShaderType shaderType,
807 GlslangProgramInterfaceInfo *programInterfaceInfo,
808 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
809 {
810 const std::vector<gl::InterfaceBlock> &uniformBlocks = programExecutable.getUniformBlocks();
811 AssignInterfaceBlockBindings(options, programExecutable, uniformBlocks, shaderType,
812 programInterfaceInfo, variableInfoMapOut);
813
814 const std::vector<gl::InterfaceBlock> &storageBlocks =
815 programExecutable.getShaderStorageBlocks();
816 AssignInterfaceBlockBindings(options, programExecutable, storageBlocks, shaderType,
817 programInterfaceInfo, variableInfoMapOut);
818
819 const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
820 programExecutable.getAtomicCounterBuffers();
821 AssignAtomicCounterBufferBindings(options, programExecutable, atomicCounterBuffers, shaderType,
822 programInterfaceInfo, variableInfoMapOut);
823
824 const std::vector<gl::LinkedUniform> &uniforms = programExecutable.getUniforms();
825 const gl::RangeUI &imageUniformRange = programExecutable.getImageUniformRange();
826 AssignImageBindings(options, programExecutable, uniforms, imageUniformRange, shaderType,
827 programInterfaceInfo, variableInfoMapOut);
828 }
829
830 // TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
831 // shader stages.
AssignTextureBindings(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)832 void AssignTextureBindings(GlslangSourceOptions &options,
833 const gl::ProgramExecutable &programExecutable,
834 const gl::ShaderType shaderType,
835 GlslangProgramInterfaceInfo *programInterfaceInfo,
836 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
837 {
838 // Assign textures to a descriptor set and binding.
839 const std::vector<gl::LinkedUniform> &uniforms = programExecutable.getUniforms();
840
841 for (unsigned int uniformIndex : programExecutable.getSamplerUniformRange())
842 {
843 const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
844
845 if (!options.useOldRewriteStructSamplers &&
846 gl::SamplerNameContainsNonZeroArrayElement(samplerUniform.name))
847 {
848 continue;
849 }
850
851 if (UniformNameIsIndexZero(samplerUniform.name, options.useOldRewriteStructSamplers))
852 {
853 // Samplers in structs are extracted and renamed.
854 const std::string samplerName = options.useOldRewriteStructSamplers
855 ? GetMappedSamplerNameOld(samplerUniform.name)
856 : GlslangGetMappedSamplerName(samplerUniform.name);
857
858 // TODO: http://anglebug.com/4523: All uniforms should be active
859 if (programExecutable.hasLinkedShaderStage(shaderType) &&
860 samplerUniform.isActive(shaderType))
861 {
862 AddResourceInfo(&(*variableInfoMapOut)[shaderType], samplerName,
863 programInterfaceInfo->textureDescriptorSetIndex,
864 programInterfaceInfo->currentTextureBindingIndex, shaderType);
865 ++programInterfaceInfo->currentTextureBindingIndex;
866 }
867 }
868 }
869 }
870
871 constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
872 {gl::ShaderType::Vertex, EShLangVertex},
873 {gl::ShaderType::Geometry, EShLangGeometry},
874 {gl::ShaderType::Fragment, EShLangFragment},
875 {gl::ShaderType::Compute, EShLangCompute},
876 };
877
GetShaderSpirvCode(GlslangErrorCallback callback,const gl::Caps & glCaps,const gl::ShaderMap<std::string> & shaderSources,gl::ShaderMap<std::vector<uint32_t>> * spirvBlobsOut)878 angle::Result GetShaderSpirvCode(GlslangErrorCallback callback,
879 const gl::Caps &glCaps,
880 const gl::ShaderMap<std::string> &shaderSources,
881 gl::ShaderMap<std::vector<uint32_t>> *spirvBlobsOut)
882 {
883 // Enable SPIR-V and Vulkan rules when parsing GLSL
884 EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
885
886 TBuiltInResource builtInResources(glslang::DefaultTBuiltInResource);
887 GetBuiltInResourcesFromCaps(glCaps, &builtInResources);
888
889 glslang::TShader vertexShader(EShLangVertex);
890 glslang::TShader fragmentShader(EShLangFragment);
891 glslang::TShader geometryShader(EShLangGeometry);
892 glslang::TShader computeShader(EShLangCompute);
893
894 gl::ShaderMap<glslang::TShader *> shaders = {
895 {gl::ShaderType::Vertex, &vertexShader},
896 {gl::ShaderType::Fragment, &fragmentShader},
897 {gl::ShaderType::Geometry, &geometryShader},
898 {gl::ShaderType::Compute, &computeShader},
899 };
900 glslang::TProgram program;
901
902 for (const gl::ShaderType shaderType : gl::AllShaderTypes())
903 {
904 if (shaderSources[shaderType].empty())
905 {
906 continue;
907 }
908
909 const char *shaderString = shaderSources[shaderType].c_str();
910 int shaderLength = static_cast<int>(shaderSources[shaderType].size());
911
912 glslang::TShader *shader = shaders[shaderType];
913 shader->setStringsWithLengths(&shaderString, &shaderLength, 1);
914 shader->setEntryPoint("main");
915
916 bool result = shader->parse(&builtInResources, 450, ECoreProfile, false, false, messages);
917 if (!result)
918 {
919 ERR() << "Internal error parsing Vulkan shader corresponding to " << shaderType << ":\n"
920 << shader->getInfoLog() << "\n"
921 << shader->getInfoDebugLog() << "\n";
922 ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
923 }
924
925 program.addShader(shader);
926 }
927
928 bool linkResult = program.link(messages);
929 if (!linkResult)
930 {
931 ERR() << "Internal error linking Vulkan shaders:\n" << program.getInfoLog() << "\n";
932 ANGLE_GLSLANG_CHECK(callback, false, GlslangError::InvalidShader);
933 }
934
935 for (const gl::ShaderType shaderType : gl::AllShaderTypes())
936 {
937 if (shaderSources[shaderType].empty())
938 {
939 continue;
940 }
941
942 glslang::TIntermediate *intermediate = program.getIntermediate(kShLanguageMap[shaderType]);
943 glslang::GlslangToSpv(*intermediate, (*spirvBlobsOut)[shaderType]);
944 }
945
946 return angle::Result::Continue;
947 }
948
ValidateSpirvMessage(spv_message_level_t level,const char * source,const spv_position_t & position,const char * message)949 void ValidateSpirvMessage(spv_message_level_t level,
950 const char *source,
951 const spv_position_t &position,
952 const char *message)
953 {
954 WARN() << "Level" << level << ": " << message;
955 }
956
ValidateSpirv(const std::vector<uint32_t> & spirvBlob)957 bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob)
958 {
959 spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
960
961 spirvTools.SetMessageConsumer(ValidateSpirvMessage);
962 bool result = spirvTools.Validate(spirvBlob);
963
964 if (!result)
965 {
966 std::string readableSpirv;
967 spirvTools.Disassemble(spirvBlob, &readableSpirv, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
968 WARN() << "Invalid SPIR-V:\n" << readableSpirv;
969 }
970
971 return result;
972 }
973
974 // A SPIR-V transformer. It walks the instructions and modifies them as necessary, for example to
975 // assign bindings or locations.
976 class SpirvTransformer final : angle::NonCopyable
977 {
978 public:
SpirvTransformer(const std::vector<uint32_t> & spirvBlobIn,bool removeEarlyFragmentTestsOptimization,const ShaderInterfaceVariableInfoMap & variableInfoMap,gl::ShaderType shaderType,SpirvBlob * spirvBlobOut)979 SpirvTransformer(const std::vector<uint32_t> &spirvBlobIn,
980 bool removeEarlyFragmentTestsOptimization,
981 const ShaderInterfaceVariableInfoMap &variableInfoMap,
982 gl::ShaderType shaderType,
983 SpirvBlob *spirvBlobOut)
984 : mSpirvBlobIn(spirvBlobIn),
985 mShaderType(shaderType),
986 mHasTransformFeedbackOutput(false),
987 mVariableInfoMap(variableInfoMap),
988 mSpirvBlobOut(spirvBlobOut)
989 {
990 gl::ShaderBitSet allStages;
991 allStages.set();
992 mRemoveEarlyFragmentTestsOptimization = removeEarlyFragmentTestsOptimization;
993 mBuiltinVariableInfo.activeStages = allStages;
994 }
995
996 bool transform();
997
998 private:
999 // SPIR-V 1.0 Table 1: First Words of Physical Layout
1000 enum HeaderIndex
1001 {
1002 kHeaderIndexMagic = 0,
1003 kHeaderIndexVersion = 1,
1004 kHeaderIndexGenerator = 2,
1005 kHeaderIndexIndexBound = 3,
1006 kHeaderIndexSchema = 4,
1007 kHeaderIndexInstructions = 5,
1008 };
1009
1010 // A prepass to resolve interesting ids:
1011 void resolveVariableIds();
1012
1013 // Transform instructions:
1014 void transformInstruction();
1015
1016 // Instructions that are purely informational:
1017 void visitName(const uint32_t *instruction);
1018 void visitTypeHelper(const uint32_t *instruction, size_t idIndex, size_t typeIdIndex);
1019 void visitTypeArray(const uint32_t *instruction);
1020 void visitTypePointer(const uint32_t *instruction);
1021 void visitVariable(const uint32_t *instruction);
1022
1023 // Instructions that potentially need transformation. They return true if the instruction is
1024 // transformed. If false is returned, the instruction should be copied as-is.
1025 bool transformAccessChain(const uint32_t *instruction, size_t wordCount);
1026 bool transformCapability(const uint32_t *instruction, size_t wordCount);
1027 bool transformEntryPoint(const uint32_t *instruction, size_t wordCount);
1028 bool transformDecorate(const uint32_t *instruction, size_t wordCount);
1029 bool transformTypePointer(const uint32_t *instruction, size_t wordCount);
1030 bool transformVariable(const uint32_t *instruction, size_t wordCount);
1031 bool transformExecutionMode(const uint32_t *instruction, size_t wordCount);
1032
1033 // Any other instructions:
1034 size_t copyInstruction(const uint32_t *instruction, size_t wordCount);
1035 uint32_t getNewId();
1036
1037 // SPIR-V to transform:
1038 const std::vector<uint32_t> &mSpirvBlobIn;
1039 const gl::ShaderType mShaderType;
1040 bool mHasTransformFeedbackOutput;
1041
1042 bool mRemoveEarlyFragmentTestsOptimization;
1043
1044 // Input shader variable info map:
1045 const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
1046 ShaderInterfaceVariableInfo mBuiltinVariableInfo;
1047
1048 // Transformed SPIR-V:
1049 SpirvBlob *mSpirvBlobOut;
1050
1051 // Traversal state:
1052 size_t mCurrentWord = 0;
1053 bool mIsInFunctionSection = false;
1054
1055 // Transformation state:
1056
1057 // Names associated with ids through OpName. The same name may be assigned to multiple ids, but
1058 // not all names are interesting (for example function arguments). When the variable
1059 // declaration is met (OpVariable), the variable info is matched with the corresponding id's
1060 // name based on the Storage Class.
1061 std::vector<const char *> mNamesById;
1062
1063 // Shader variable info per id, if id is a shader variable.
1064 std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
1065
1066 // Each OpTypePointer instruction that defines a type with the Output storage class is
1067 // duplicated with a similar instruction but which defines a type with the Private storage
1068 // class. If inactive varyings are encountered, its type is changed to the Private one. The
1069 // following vector maps the Output type id to the corresponding Private one.
1070 std::vector<uint32_t> mTypePointerTransformedId;
1071 };
1072
transform()1073 bool SpirvTransformer::transform()
1074 {
1075 // Glslang succeeded in outputting SPIR-V, so we assume it's valid.
1076 ASSERT(mSpirvBlobIn.size() >= kHeaderIndexInstructions);
1077 // Since SPIR-V comes from a local call to glslang, it necessarily has the same endianness as
1078 // the running architecture, so no byte-swapping is necessary.
1079 ASSERT(mSpirvBlobIn[kHeaderIndexMagic] == spv::MagicNumber);
1080
1081 // Make sure the transformer is not reused to avoid having to reinitialize it here.
1082 ASSERT(mCurrentWord == 0);
1083 ASSERT(mIsInFunctionSection == false);
1084
1085 // Make sure the SpirvBlob is not reused.
1086 ASSERT(mSpirvBlobOut->empty());
1087
1088 // First, find all necessary ids and associate them with the information required to transform
1089 // their decorations.
1090 resolveVariableIds();
1091
1092 // Copy the header to SpirvBlob
1093 mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);
1094
1095 mCurrentWord = kHeaderIndexInstructions;
1096 while (mCurrentWord < mSpirvBlobIn.size())
1097 {
1098 transformInstruction();
1099 }
1100
1101 return true;
1102 }
1103
1104 // SPIR-V 1.0 Table 2: Instruction Physical Layout
GetSpirvInstructionLength(const uint32_t * instruction)1105 uint32_t GetSpirvInstructionLength(const uint32_t *instruction)
1106 {
1107 return instruction[0] >> 16;
1108 }
1109
GetSpirvInstructionOp(const uint32_t * instruction)1110 uint32_t GetSpirvInstructionOp(const uint32_t *instruction)
1111 {
1112 constexpr uint32_t kOpMask = 0xFFFFu;
1113 return instruction[0] & kOpMask;
1114 }
1115
SetSpirvInstructionLength(uint32_t * instruction,size_t length)1116 void SetSpirvInstructionLength(uint32_t *instruction, size_t length)
1117 {
1118 ASSERT(length < 0xFFFFu);
1119
1120 constexpr uint32_t kLengthMask = 0xFFFF0000u;
1121 instruction[0] &= ~kLengthMask;
1122 instruction[0] |= length << 16;
1123 }
1124
SetSpirvInstructionOp(uint32_t * instruction,uint32_t op)1125 void SetSpirvInstructionOp(uint32_t *instruction, uint32_t op)
1126 {
1127 constexpr uint32_t kOpMask = 0xFFFFu;
1128 instruction[0] &= ~kOpMask;
1129 instruction[0] |= op;
1130 }
1131
resolveVariableIds()1132 void SpirvTransformer::resolveVariableIds()
1133 {
1134 size_t indexBound = mSpirvBlobIn[kHeaderIndexIndexBound];
1135
1136 // Allocate storage for id-to-name map. Used to associate ShaderInterfaceVariableInfo with ids
1137 // based on name, but only when it's determined that the name corresponds to a shader interface
1138 // variable.
1139 mNamesById.resize(indexBound + 1, nullptr);
1140
1141 // Allocate storage for id-to-info map. If %i is the id of a name in mVariableInfoMap, index i
1142 // in this vector will hold a pointer to the ShaderInterfaceVariableInfo object associated with
1143 // that name in mVariableInfoMap.
1144 mVariableInfoById.resize(indexBound + 1, nullptr);
1145
1146 // Allocate storage for Output type pointer map. At index i, this vector holds the identical
1147 // type as %i except for its storage class turned to Private.
1148 mTypePointerTransformedId.resize(indexBound + 1, 0);
1149
1150 size_t currentWord = kHeaderIndexInstructions;
1151
1152 while (currentWord < mSpirvBlobIn.size())
1153 {
1154 const uint32_t *instruction = &mSpirvBlobIn[currentWord];
1155
1156 const uint32_t wordCount = GetSpirvInstructionLength(instruction);
1157 const uint32_t opCode = GetSpirvInstructionOp(instruction);
1158
1159 switch (opCode)
1160 {
1161 case spv::OpName:
1162 visitName(instruction);
1163 break;
1164 case spv::OpTypeArray:
1165 visitTypeArray(instruction);
1166 break;
1167 case spv::OpTypePointer:
1168 visitTypePointer(instruction);
1169 break;
1170 case spv::OpVariable:
1171 visitVariable(instruction);
1172 break;
1173 case spv::OpFunction:
1174 // SPIR-V is structured in sections (SPIR-V 1.0 Section 2.4 Logical Layout of a
1175 // Module). Names appear before decorations, which are followed by type+variables
1176 // and finally functions. We are only interested in name and variable declarations
1177 // (as well as type declarations for the sake of nameless interface blocks). Early
1178 // out when the function declaration section is met.
1179 return;
1180 default:
1181 break;
1182 }
1183
1184 currentWord += wordCount;
1185 }
1186 }
1187
transformInstruction()1188 void SpirvTransformer::transformInstruction()
1189 {
1190 const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord];
1191
1192 const uint32_t wordCount = GetSpirvInstructionLength(instruction);
1193 const uint32_t opCode = GetSpirvInstructionOp(instruction);
1194
1195 // Since glslang succeeded in producing SPIR-V, we assume it to be valid.
1196 ASSERT(mCurrentWord + wordCount <= mSpirvBlobIn.size());
1197
1198 if (opCode == spv::OpFunction)
1199 {
1200 // SPIR-V is structured in sections. Function declarations come last. Only Op*Access*
1201 // opcodes inside functions need to be inspected.
1202 mIsInFunctionSection = true;
1203 }
1204
1205 // Only look at interesting instructions.
1206 bool transformed = false;
1207
1208 if (mIsInFunctionSection)
1209 {
1210 // Look at in-function opcodes.
1211 switch (opCode)
1212 {
1213 case spv::OpAccessChain:
1214 case spv::OpInBoundsAccessChain:
1215 case spv::OpPtrAccessChain:
1216 case spv::OpInBoundsPtrAccessChain:
1217 transformed = transformAccessChain(instruction, wordCount);
1218 break;
1219 default:
1220 break;
1221 }
1222 }
1223 else
1224 {
1225 // Look at global declaration opcodes.
1226 switch (opCode)
1227 {
1228 case spv::OpCapability:
1229 transformed = transformCapability(instruction, wordCount);
1230 break;
1231 case spv::OpEntryPoint:
1232 transformed = transformEntryPoint(instruction, wordCount);
1233 break;
1234 case spv::OpDecorate:
1235 transformed = transformDecorate(instruction, wordCount);
1236 break;
1237 case spv::OpTypePointer:
1238 transformed = transformTypePointer(instruction, wordCount);
1239 break;
1240 case spv::OpVariable:
1241 transformed = transformVariable(instruction, wordCount);
1242 break;
1243 case spv::OpExecutionMode:
1244 transformed = transformExecutionMode(instruction, wordCount);
1245 break;
1246 default:
1247 break;
1248 }
1249 }
1250
1251 // If the instruction was not transformed, copy it to output as is.
1252 if (!transformed)
1253 {
1254 copyInstruction(instruction, wordCount);
1255 }
1256
1257 // Advance to next instruction.
1258 mCurrentWord += wordCount;
1259 }
1260
visitName(const uint32_t * instruction)1261 void SpirvTransformer::visitName(const uint32_t *instruction)
1262 {
1263 // We currently don't have any big-endian devices in the list of supported platforms. Literal
1264 // strings in SPIR-V are stored little-endian (SPIR-V 1.0 Section 2.2.1, Literal String), so if
1265 // a big-endian device is to be supported, the string matching here should be specialized.
1266 ASSERT(IsLittleEndian());
1267
1268 // SPIR-V 1.0 Section 3.32 Instructions, OpName
1269 constexpr size_t kIdIndex = 1;
1270 constexpr size_t kNameIndex = 2;
1271
1272 const uint32_t id = instruction[kIdIndex];
1273 const char *name = reinterpret_cast<const char *>(&instruction[kNameIndex]);
1274
1275 // The names and ids are unique
1276 ASSERT(id < mNamesById.size());
1277 ASSERT(mNamesById[id] == nullptr);
1278
1279 mNamesById[id] = name;
1280 }
1281
visitTypeHelper(const uint32_t * instruction,const size_t idIndex,const size_t typeIdIndex)1282 void SpirvTransformer::visitTypeHelper(const uint32_t *instruction,
1283 const size_t idIndex,
1284 const size_t typeIdIndex)
1285 {
1286 const uint32_t id = instruction[idIndex];
1287 const uint32_t typeId = instruction[typeIdIndex];
1288
1289 // Every type id is declared only once.
1290 ASSERT(id < mNamesById.size());
1291 ASSERT(mNamesById[id] == nullptr);
1292
1293 // Carry the name forward from the base type. This is only necessary for interface blocks,
1294 // as the variable info is associated with the block name instead of the variable name (to
1295 // support nameless interface blocks). When the variable declaration is met, either the
1296 // type name or the variable name is used to associate with info based on the variable's
1297 // storage class.
1298 ASSERT(typeId < mNamesById.size());
1299
1300 mNamesById[id] = mNamesById[typeId];
1301 }
1302
visitTypeArray(const uint32_t * instruction)1303 void SpirvTransformer::visitTypeArray(const uint32_t *instruction)
1304 {
1305 // SPIR-V 1.0 Section 3.32 Instructions, OpTypeArray
1306 constexpr size_t kIdIndex = 1;
1307 constexpr size_t kElementTypeIdIndex = 2;
1308
1309 visitTypeHelper(instruction, kIdIndex, kElementTypeIdIndex);
1310 }
1311
visitTypePointer(const uint32_t * instruction)1312 void SpirvTransformer::visitTypePointer(const uint32_t *instruction)
1313 {
1314 // SPIR-V 1.0 Section 3.32 Instructions, OpTypePointer
1315 constexpr size_t kIdIndex = 1;
1316 constexpr size_t kTypeIdIndex = 3;
1317
1318 visitTypeHelper(instruction, kIdIndex, kTypeIdIndex);
1319 }
1320
visitVariable(const uint32_t * instruction)1321 void SpirvTransformer::visitVariable(const uint32_t *instruction)
1322 {
1323 // SPIR-V 1.0 Section 3.32 Instructions, OpVariable
1324 constexpr size_t kTypeIdIndex = 1;
1325 constexpr size_t kIdIndex = 2;
1326 constexpr size_t kStorageClassIndex = 3;
1327
1328 // All resources that take set/binding should be transformed.
1329 const uint32_t typeId = instruction[kTypeIdIndex];
1330 const uint32_t id = instruction[kIdIndex];
1331 const uint32_t storageClass = instruction[kStorageClassIndex];
1332
1333 ASSERT(typeId < mNamesById.size());
1334 ASSERT(id < mNamesById.size());
1335
1336 // If storage class indicates that this is not a shader interface variable, ignore it.
1337 const bool isInterfaceBlockVariable =
1338 storageClass == spv::StorageClassUniform || storageClass == spv::StorageClassStorageBuffer;
1339 const bool isOpaqueUniform = storageClass == spv::StorageClassUniformConstant;
1340 const bool isInOut =
1341 storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput;
1342
1343 if (!isInterfaceBlockVariable && !isOpaqueUniform && !isInOut)
1344 {
1345 return;
1346 }
1347
1348 // The ids are unique.
1349 ASSERT(id < mVariableInfoById.size());
1350 ASSERT(mVariableInfoById[id] == nullptr);
1351
1352 // For interface block variables, the name that's used to associate info is the block name
1353 // rather than the variable name.
1354 const char *name = mNamesById[isInterfaceBlockVariable ? typeId : id];
1355 ASSERT(name != nullptr);
1356
1357 // Handle builtins, which all start with "gl_". Either the variable name could be an indication
1358 // of a builtin variable (such as with gl_FragCoord) or the type name (such as with
1359 // gl_PerVertex).
1360 const bool isNameBuiltin = isInOut && angle::BeginsWith(name, "gl_");
1361 const bool isTypeBuiltin =
1362 isInOut && mNamesById[typeId] != nullptr && angle::BeginsWith(mNamesById[typeId], "gl_");
1363 if (isNameBuiltin || isTypeBuiltin)
1364 {
1365 // Make all builtins point to this no-op info. Adding this entry allows us to ASSERT that
1366 // every shader interface variable is processed during the SPIR-V transformation. This is
1367 // done when iterating the ids provided by OpEntryPoint.
1368 mVariableInfoById[id] = &mBuiltinVariableInfo;
1369 return;
1370 }
1371
1372 // Every shader interface variable should have an associated data.
1373 auto infoIter = mVariableInfoMap.find(name);
1374 ASSERT(infoIter != mVariableInfoMap.end());
1375
1376 const ShaderInterfaceVariableInfo *info = &infoIter->second;
1377
1378 // Associate the id of this name with its info.
1379 mVariableInfoById[id] = info;
1380
1381 // Note if the variable is captured by transform feedback. In that case, the TransformFeedback
1382 // capability needs to be added.
1383 if (mShaderType != gl::ShaderType::Fragment &&
1384 info->xfbBuffer != ShaderInterfaceVariableInfo::kInvalid && info->activeStages[mShaderType])
1385 {
1386 mHasTransformFeedbackOutput = true;
1387 }
1388 }
1389
transformDecorate(const uint32_t * instruction,size_t wordCount)1390 bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wordCount)
1391 {
1392 // SPIR-V 1.0 Section 3.32 Instructions, OpDecorate
1393 constexpr size_t kIdIndex = 1;
1394 constexpr size_t kDecorationIndex = 2;
1395 constexpr size_t kDecorationValueIndex = 3;
1396
1397 uint32_t id = instruction[kIdIndex];
1398 uint32_t decoration = instruction[kDecorationIndex];
1399
1400 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
1401
1402 // If variable is not a shader interface variable that needs modification, there's nothing to
1403 // do.
1404 if (info == nullptr)
1405 {
1406 return false;
1407 }
1408
1409 // If it's an inactive varying, remove the decoration altogether.
1410 if (!info->activeStages[mShaderType])
1411 {
1412 return true;
1413 }
1414
1415 uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid;
1416
1417 switch (decoration)
1418 {
1419 case spv::DecorationLocation:
1420 newDecorationValue = info->location;
1421 break;
1422 case spv::DecorationBinding:
1423 newDecorationValue = info->binding;
1424 break;
1425 case spv::DecorationDescriptorSet:
1426 newDecorationValue = info->descriptorSet;
1427 break;
1428 default:
1429 break;
1430 }
1431
1432 // If the decoration is not something we care about modifying, there's nothing to do.
1433 if (newDecorationValue == ShaderInterfaceVariableInfo::kInvalid)
1434 {
1435 return false;
1436 }
1437
1438 // Copy the decoration declaration and modify it.
1439 const size_t instructionOffset = copyInstruction(instruction, wordCount);
1440 (*mSpirvBlobOut)[instructionOffset + kDecorationValueIndex] = newDecorationValue;
1441
1442 // If there are decorations to be added, add them right after the Location decoration is
1443 // encountered.
1444 if (decoration != spv::DecorationLocation)
1445 {
1446 return true;
1447 }
1448
1449 // Add component decoration, if any.
1450 if (info->component != ShaderInterfaceVariableInfo::kInvalid)
1451 {
1452 // Copy the location decoration declaration and modify it to contain the Component
1453 // decoration.
1454 const size_t instOffset = copyInstruction(instruction, wordCount);
1455 (*mSpirvBlobOut)[instOffset + kDecorationIndex] = spv::DecorationComponent;
1456 (*mSpirvBlobOut)[instOffset + kDecorationValueIndex] = info->component;
1457 }
1458
1459 // Add Xfb decorations, if any.
1460 if (mShaderType != gl::ShaderType::Fragment &&
1461 info->xfbBuffer != ShaderInterfaceVariableInfo::kInvalid)
1462 {
1463 ASSERT(info->xfbStride != ShaderInterfaceVariableInfo::kInvalid);
1464 ASSERT(info->xfbOffset != ShaderInterfaceVariableInfo::kInvalid);
1465
1466 constexpr size_t kXfbDecorationCount = 3;
1467 constexpr uint32_t xfbDecorations[kXfbDecorationCount] = {
1468 spv::DecorationXfbBuffer,
1469 spv::DecorationXfbStride,
1470 spv::DecorationOffset,
1471 };
1472 const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
1473 info->xfbBuffer,
1474 info->xfbStride,
1475 info->xfbOffset,
1476 };
1477
1478 // Copy the location decoration declaration three times, and modify them to contain the
1479 // XfbBuffer, XfbStride and Offset decorations.
1480 for (size_t i = 0; i < kXfbDecorationCount; ++i)
1481 {
1482 const size_t xfbInstructionOffset = copyInstruction(instruction, wordCount);
1483 (*mSpirvBlobOut)[xfbInstructionOffset + kDecorationIndex] = xfbDecorations[i];
1484 (*mSpirvBlobOut)[xfbInstructionOffset + kDecorationValueIndex] = xfbDecorationValues[i];
1485 }
1486 }
1487
1488 return true;
1489 }
1490
transformCapability(const uint32_t * instruction,size_t wordCount)1491 bool SpirvTransformer::transformCapability(const uint32_t *instruction, size_t wordCount)
1492 {
1493 if (!mHasTransformFeedbackOutput)
1494 {
1495 return false;
1496 }
1497
1498 // SPIR-V 1.0 Section 3.32 Instructions, OpCapability
1499 constexpr size_t kCapabilityIndex = 1;
1500
1501 uint32_t capability = instruction[kCapabilityIndex];
1502
1503 // Transform feedback capability shouldn't have already been specified.
1504 ASSERT(capability != spv::CapabilityTransformFeedback);
1505
1506 // Vulkan shaders have either Shader, Geometry or Tessellation capability. We find this
1507 // capability, and add the TransformFeedback capability after it.
1508 if (capability != spv::CapabilityShader && capability != spv::CapabilityGeometry &&
1509 capability != spv::CapabilityTessellation)
1510 {
1511 return false;
1512 }
1513
1514 // Copy the original capability declaration.
1515 copyInstruction(instruction, wordCount);
1516
1517 // Create the TransformFeedback capability declaration.
1518
1519 // SPIR-V 1.0 Section 3.32 Instructions, OpCapability
1520 constexpr size_t kCapabilityInstructionLength = 2;
1521
1522 std::array<uint32_t, kCapabilityInstructionLength> newCapabilityDeclaration = {
1523 instruction[0], // length+opcode is identical
1524 };
1525 // Fill the fields.
1526 newCapabilityDeclaration[kCapabilityIndex] = spv::CapabilityTransformFeedback;
1527
1528 copyInstruction(newCapabilityDeclaration.data(), kCapabilityInstructionLength);
1529
1530 return true;
1531 }
1532
transformEntryPoint(const uint32_t * instruction,size_t wordCount)1533 bool SpirvTransformer::transformEntryPoint(const uint32_t *instruction, size_t wordCount)
1534 {
1535 // Remove inactive varyings from the shader interface declaration.
1536
1537 // SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
1538 constexpr size_t kNameIndex = 3;
1539
1540 // Calculate the length of entry point name in words. Note that endianness of the string
1541 // doesn't matter, since we are looking for the '\0' character and rounding up to the word size.
1542 // This calculates (strlen(name)+1+3) / 4, which is equal to strlen(name)/4+1.
1543 const size_t nameLength =
1544 strlen(reinterpret_cast<const char *>(&instruction[kNameIndex])) / 4 + 1;
1545 const uint32_t instructionLength = GetSpirvInstructionLength(instruction);
1546 const size_t interfaceStart = kNameIndex + nameLength;
1547 const size_t interfaceCount = instructionLength - interfaceStart;
1548
1549 // Create a copy of the entry point for modification.
1550 std::vector<uint32_t> filteredEntryPoint(instruction, instruction + wordCount);
1551
1552 // Filter out inactive varyings from entry point interface declaration.
1553 size_t writeIndex = interfaceStart;
1554 for (size_t index = 0; index < interfaceCount; ++index)
1555 {
1556 uint32_t id = instruction[interfaceStart + index];
1557 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
1558
1559 ASSERT(info);
1560
1561 if (!info->activeStages[mShaderType])
1562 {
1563 continue;
1564 }
1565
1566 filteredEntryPoint[writeIndex] = id;
1567 ++writeIndex;
1568 }
1569
1570 // Update the length of the instruction.
1571 const size_t newLength = writeIndex;
1572 SetSpirvInstructionLength(filteredEntryPoint.data(), newLength);
1573
1574 // Copy to output.
1575 copyInstruction(filteredEntryPoint.data(), newLength);
1576
1577 // Add an OpExecutionMode Xfb instruction if necessary.
1578 if (!mHasTransformFeedbackOutput)
1579 {
1580 return true;
1581 }
1582
1583 // SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
1584 constexpr size_t kEntryPointIdIndex = 2;
1585
1586 // SPIR-V 1.0 Section 3.32 Instructions, OpExecutionMode
1587 constexpr size_t kExecutionModeInstructionLength = 3;
1588 constexpr size_t kExecutionModeIdIndex = 1;
1589 constexpr size_t kExecutionModeExecutionModeIndex = 2;
1590
1591 std::array<uint32_t, kExecutionModeInstructionLength> newExecutionModeDeclaration = {};
1592
1593 // Fill the fields.
1594 SetSpirvInstructionOp(newExecutionModeDeclaration.data(), spv::OpExecutionMode);
1595 SetSpirvInstructionLength(newExecutionModeDeclaration.data(), kExecutionModeInstructionLength);
1596 newExecutionModeDeclaration[kExecutionModeIdIndex] = instruction[kEntryPointIdIndex];
1597 newExecutionModeDeclaration[kExecutionModeExecutionModeIndex] = spv::ExecutionModeXfb;
1598
1599 copyInstruction(newExecutionModeDeclaration.data(), kExecutionModeInstructionLength);
1600
1601 return true;
1602 }
1603
transformTypePointer(const uint32_t * instruction,size_t wordCount)1604 bool SpirvTransformer::transformTypePointer(const uint32_t *instruction, size_t wordCount)
1605 {
1606 // SPIR-V 1.0 Section 3.32 Instructions, OpTypePointer
1607 constexpr size_t kIdIndex = 1;
1608 constexpr size_t kStorageClassIndex = 2;
1609 constexpr size_t kTypeIdIndex = 3;
1610
1611 const uint32_t id = instruction[kIdIndex];
1612 const uint32_t storageClass = instruction[kStorageClassIndex];
1613 const uint32_t typeId = instruction[kTypeIdIndex];
1614
1615 // If the storage class is output, this may be used to create a variable corresponding to an
1616 // inactive varying, or if that varying is a struct, an Op*AccessChain retrieving a field of
1617 // that inactive varying.
1618 //
1619 // Unfortunately, SPIR-V specifies the storage class both on the type and the variable
1620 // declaration. Otherwise it would have been sufficient to modify the OpVariable instruction.
1621 // For simplicty, copy every "OpTypePointer Output" instruction except with the Private storage
1622 // class, in case it may be necessary later.
1623
1624 if (storageClass != spv::StorageClassOutput)
1625 {
1626 return false;
1627 }
1628
1629 // Cannot create a Private type declaration from builtins such as gl_PerVertex.
1630 if (mNamesById[typeId] != nullptr && angle::BeginsWith(mNamesById[typeId], "gl_"))
1631 {
1632 return false;
1633 }
1634
1635 // Copy the type declaration for modification.
1636 const size_t instructionOffset = copyInstruction(instruction, wordCount);
1637
1638 const uint32_t newTypeId = getNewId();
1639 (*mSpirvBlobOut)[instructionOffset + kIdIndex] = newTypeId;
1640 (*mSpirvBlobOut)[instructionOffset + kStorageClassIndex] = spv::StorageClassPrivate;
1641
1642 // Remember the id of the replacement.
1643 ASSERT(id < mTypePointerTransformedId.size());
1644 mTypePointerTransformedId[id] = newTypeId;
1645
1646 // The original instruction should still be present as well. At this point, we don't know
1647 // whether we will need the Output or Private type.
1648 return false;
1649 }
1650
transformVariable(const uint32_t * instruction,size_t wordCount)1651 bool SpirvTransformer::transformVariable(const uint32_t *instruction, size_t wordCount)
1652 {
1653 // SPIR-V 1.0 Section 3.32 Instructions, OpVariable
1654 constexpr size_t kTypeIdIndex = 1;
1655 constexpr size_t kIdIndex = 2;
1656 constexpr size_t kStorageClassIndex = 3;
1657
1658 const uint32_t id = instruction[kIdIndex];
1659 const uint32_t typeId = instruction[kTypeIdIndex];
1660 const uint32_t storageClass = instruction[kStorageClassIndex];
1661
1662 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
1663
1664 // If variable is not a shader interface variable that needs modification, there's nothing to
1665 // do.
1666 if (info == nullptr)
1667 {
1668 return false;
1669 }
1670
1671 // Furthermore, if it's not an inactive varying output, there's nothing to do. Note that
1672 // inactive varying inputs are already pruned by the translator.
1673 ASSERT(storageClass != spv::StorageClassInput || info->activeStages[mShaderType]);
1674 if (info->activeStages[mShaderType])
1675 {
1676 return false;
1677 }
1678
1679 ASSERT(storageClass == spv::StorageClassOutput);
1680
1681 // Copy the variable declaration for modification. Change its type to the corresponding type
1682 // with the Private storage class, as well as changing the storage class respecified in this
1683 // instruction.
1684 const size_t instructionOffset = copyInstruction(instruction, wordCount);
1685
1686 ASSERT(typeId < mTypePointerTransformedId.size());
1687 ASSERT(mTypePointerTransformedId[typeId] != 0);
1688
1689 (*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] = mTypePointerTransformedId[typeId];
1690 (*mSpirvBlobOut)[instructionOffset + kStorageClassIndex] = spv::StorageClassPrivate;
1691
1692 return true;
1693 }
1694
transformAccessChain(const uint32_t * instruction,size_t wordCount)1695 bool SpirvTransformer::transformAccessChain(const uint32_t *instruction, size_t wordCount)
1696 {
1697 // SPIR-V 1.0 Section 3.32 Instructions, OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain,
1698 // OpInBoundsPtrAccessChain
1699 constexpr size_t kTypeIdIndex = 1;
1700 constexpr size_t kBaseIdIndex = 3;
1701
1702 const uint32_t typeId = instruction[kTypeIdIndex];
1703 const uint32_t baseId = instruction[kBaseIdIndex];
1704
1705 // If not accessing an inactive output varying, nothing to do.
1706 const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
1707 if (info == nullptr || info->activeStages[mShaderType])
1708 {
1709 return false;
1710 }
1711
1712 // Copy the instruction for modification.
1713 const size_t instructionOffset = copyInstruction(instruction, wordCount);
1714
1715 ASSERT(typeId < mTypePointerTransformedId.size());
1716 ASSERT(mTypePointerTransformedId[typeId] != 0);
1717
1718 (*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] = mTypePointerTransformedId[typeId];
1719
1720 return true;
1721 }
1722
transformExecutionMode(const uint32_t * instruction,size_t wordCount)1723 bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_t wordCount)
1724 {
1725 // SPIR-V 1.0 Section 3.32 Instructions, OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain,
1726 // OpInBoundsPtrAccessChain
1727 constexpr size_t kModeIndex = 2;
1728 const uint32_t executionMode = instruction[kModeIndex];
1729
1730 if (executionMode == spv::ExecutionModeEarlyFragmentTests &&
1731 mRemoveEarlyFragmentTestsOptimization)
1732 {
1733 // skip the copy
1734 return true;
1735 }
1736 return false;
1737 }
1738
copyInstruction(const uint32_t * instruction,size_t wordCount)1739 size_t SpirvTransformer::copyInstruction(const uint32_t *instruction, size_t wordCount)
1740 {
1741 size_t instructionOffset = mSpirvBlobOut->size();
1742 mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
1743 return instructionOffset;
1744 }
1745
getNewId()1746 uint32_t SpirvTransformer::getNewId()
1747 {
1748 return (*mSpirvBlobOut)[kHeaderIndexIndexBound]++;
1749 }
1750 } // anonymous namespace
1751
1752 const uint32_t ShaderInterfaceVariableInfo::kInvalid;
1753
ShaderInterfaceVariableInfo()1754 ShaderInterfaceVariableInfo::ShaderInterfaceVariableInfo() {}
1755
GlslangInitialize()1756 void GlslangInitialize()
1757 {
1758 int result = ShInitialize();
1759 ASSERT(result != 0);
1760 }
1761
GlslangRelease()1762 void GlslangRelease()
1763 {
1764 int result = ShFinalize();
1765 ASSERT(result != 0);
1766 }
1767
1768 // Strip indices from the name. If there are non-zero indices, return false to indicate that this
1769 // image uniform doesn't require set/binding. That is done on index 0.
GetImageNameWithoutIndices(std::string * name)1770 bool GetImageNameWithoutIndices(std::string *name)
1771 {
1772 if (name->back() != ']')
1773 {
1774 return true;
1775 }
1776
1777 if (!UniformNameIsIndexZero(*name, false))
1778 {
1779 return false;
1780 }
1781
1782 // Strip all indices
1783 *name = name->substr(0, name->find('['));
1784 return true;
1785 }
1786
GetMappedSamplerNameOld(const std::string & originalName)1787 std::string GetMappedSamplerNameOld(const std::string &originalName)
1788 {
1789 std::string samplerName = gl::ParseResourceName(originalName, nullptr);
1790
1791 // Samplers in structs are extracted.
1792 std::replace(samplerName.begin(), samplerName.end(), '.', '_');
1793
1794 // Samplers in arrays of structs are also extracted.
1795 std::replace(samplerName.begin(), samplerName.end(), '[', '_');
1796 samplerName.erase(std::remove(samplerName.begin(), samplerName.end(), ']'), samplerName.end());
1797
1798 if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
1799 {
1800 samplerName = sh::kUserDefinedNamePrefix + samplerName;
1801 }
1802
1803 return samplerName;
1804 }
1805
GlslangGetMappedSamplerName(const std::string & originalName)1806 std::string GlslangGetMappedSamplerName(const std::string &originalName)
1807 {
1808 std::string samplerName = originalName;
1809
1810 // Samplers in structs are extracted.
1811 std::replace(samplerName.begin(), samplerName.end(), '.', '_');
1812
1813 // Remove array elements
1814 auto out = samplerName.begin();
1815 for (auto in = samplerName.begin(); in != samplerName.end(); in++)
1816 {
1817 if (*in == '[')
1818 {
1819 while (*in != ']')
1820 {
1821 in++;
1822 ASSERT(in != samplerName.end());
1823 }
1824 }
1825 else
1826 {
1827 *out++ = *in;
1828 }
1829 }
1830
1831 samplerName.erase(out, samplerName.end());
1832
1833 if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
1834 {
1835 samplerName = sh::kUserDefinedNamePrefix + samplerName;
1836 }
1837
1838 return samplerName;
1839 }
1840
GetXfbBufferName(const uint32_t bufferIndex)1841 std::string GetXfbBufferName(const uint32_t bufferIndex)
1842 {
1843 return "xfbBuffer" + Str(bufferIndex);
1844 }
1845
GlslangAssignLocations(GlslangSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,GlslangProgramInterfaceInfo * programInterfaceInfo,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)1846 void GlslangAssignLocations(GlslangSourceOptions &options,
1847 const gl::ProgramExecutable &programExecutable,
1848 const gl::ShaderType shaderType,
1849 GlslangProgramInterfaceInfo *programInterfaceInfo,
1850 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
1851 {
1852 // Assign outputs to the fragment shader, if any.
1853 if ((shaderType == gl::ShaderType::Fragment) &&
1854 programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment))
1855 {
1856 AssignOutputLocations(programExecutable, gl::ShaderType::Fragment,
1857 &(*variableInfoMapOut)[gl::ShaderType::Fragment]);
1858 }
1859
1860 // Assign attributes to the vertex shader, if any.
1861 if ((shaderType == gl::ShaderType::Vertex) &&
1862 programExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex))
1863 {
1864 AssignAttributeLocations(programExecutable, gl::ShaderType::Vertex,
1865 &(*variableInfoMapOut)[gl::ShaderType::Vertex]);
1866 }
1867
1868 if (!programExecutable.hasLinkedShaderStage(gl::ShaderType::Compute))
1869 {
1870 // Assign varying locations.
1871 AssignVaryingLocations(options, programExecutable, shaderType, programInterfaceInfo,
1872 variableInfoMapOut);
1873
1874 if (!programExecutable.getLinkedTransformFeedbackVaryings().empty() &&
1875 options.supportsTransformFeedbackExtension && (shaderType == gl::ShaderType::Vertex))
1876 {
1877 AssignTransformFeedbackExtensionQualifiers(
1878 programExecutable, programInterfaceInfo->locationsUsedForXfbExtension,
1879 gl::ShaderType::Vertex, &(*variableInfoMapOut)[gl::ShaderType::Vertex]);
1880 }
1881 }
1882
1883 AssignUniformBindings(options, programExecutable, shaderType, programInterfaceInfo,
1884 variableInfoMapOut);
1885 AssignTextureBindings(options, programExecutable, shaderType, programInterfaceInfo,
1886 variableInfoMapOut);
1887 AssignNonTextureBindings(options, programExecutable, shaderType, programInterfaceInfo,
1888 variableInfoMapOut);
1889 }
1890
GlslangGetShaderSource(GlslangSourceOptions & options,const gl::ProgramState & programState,const gl::ProgramLinkedResources & resources,GlslangProgramInterfaceInfo * programInterfaceInfo,gl::ShaderMap<std::string> * shaderSourcesOut,ShaderMapInterfaceVariableInfoMap * variableInfoMapOut)1891 void GlslangGetShaderSource(GlslangSourceOptions &options,
1892 const gl::ProgramState &programState,
1893 const gl::ProgramLinkedResources &resources,
1894 GlslangProgramInterfaceInfo *programInterfaceInfo,
1895 gl::ShaderMap<std::string> *shaderSourcesOut,
1896 ShaderMapInterfaceVariableInfoMap *variableInfoMapOut)
1897 {
1898 for (const gl::ShaderType shaderType : gl::AllShaderTypes())
1899 {
1900 gl::Shader *glShader = programState.getAttachedShader(shaderType);
1901 (*shaderSourcesOut)[shaderType] = glShader ? glShader->getTranslatedSource() : "";
1902 }
1903
1904 std::string *vertexSource = &(*shaderSourcesOut)[gl::ShaderType::Vertex];
1905
1906 // Write transform feedback output code.
1907 if (!vertexSource->empty())
1908 {
1909 if (programState.getLinkedTransformFeedbackVaryings().empty())
1910 {
1911 *vertexSource = SubstituteTransformFeedbackMarkers(*vertexSource, "", "");
1912 }
1913 else
1914 {
1915 if (options.supportsTransformFeedbackExtension)
1916 {
1917 GenerateTransformFeedbackExtensionOutputs(
1918 programState, resources, vertexSource,
1919 &programInterfaceInfo->locationsUsedForXfbExtension);
1920 }
1921 else if (options.emulateTransformFeedback)
1922 {
1923 GenerateTransformFeedbackEmulationOutputs(
1924 options, programState, programInterfaceInfo, vertexSource,
1925 &(*variableInfoMapOut)[gl::ShaderType::Vertex]);
1926 }
1927 }
1928 }
1929
1930 for (const gl::ShaderType shaderType : programState.getExecutable().getLinkedShaderStages())
1931 {
1932 GlslangAssignLocations(options, programState.getExecutable(), shaderType,
1933 programInterfaceInfo, variableInfoMapOut);
1934 }
1935 }
1936
GlslangTransformSpirvCode(const GlslangErrorCallback & callback,const gl::ShaderType shaderType,bool removeEarlyFragmentTestsOptimization,const ShaderInterfaceVariableInfoMap & variableInfoMap,const SpirvBlob & initialSpirvBlob,SpirvBlob * spirvBlobOut)1937 angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback,
1938 const gl::ShaderType shaderType,
1939 bool removeEarlyFragmentTestsOptimization,
1940 const ShaderInterfaceVariableInfoMap &variableInfoMap,
1941 const SpirvBlob &initialSpirvBlob,
1942 SpirvBlob *spirvBlobOut)
1943 {
1944 if (initialSpirvBlob.empty())
1945 {
1946 return angle::Result::Continue;
1947 }
1948
1949 // Transform the SPIR-V code by assigning location/set/binding values.
1950 SpirvTransformer transformer(initialSpirvBlob, removeEarlyFragmentTestsOptimization,
1951 variableInfoMap, shaderType, spirvBlobOut);
1952 ANGLE_GLSLANG_CHECK(callback, transformer.transform(), GlslangError::InvalidSpirv);
1953
1954 ASSERT(ValidateSpirv(*spirvBlobOut));
1955
1956 return angle::Result::Continue;
1957 }
1958
GlslangGetShaderSpirvCode(const GlslangErrorCallback & callback,const gl::ShaderBitSet & linkedShaderStages,const gl::Caps & glCaps,const gl::ShaderMap<std::string> & shaderSources,const ShaderMapInterfaceVariableInfoMap & variableInfoMap,gl::ShaderMap<SpirvBlob> * spirvBlobsOut)1959 angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
1960 const gl::ShaderBitSet &linkedShaderStages,
1961 const gl::Caps &glCaps,
1962 const gl::ShaderMap<std::string> &shaderSources,
1963 const ShaderMapInterfaceVariableInfoMap &variableInfoMap,
1964 gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
1965 {
1966 gl::ShaderMap<SpirvBlob> initialSpirvBlobs;
1967 ANGLE_TRY(GetShaderSpirvCode(callback, glCaps, shaderSources, &initialSpirvBlobs));
1968
1969 for (const gl::ShaderType shaderType : linkedShaderStages)
1970 {
1971 // we pass in false here to skip modifications related to early fragment tests
1972 // optimizations and line rasterization. These are done in the initProgram time since they
1973 // are related to context state. We must keep original untouched spriv blobs here because we
1974 // do not have ability to add back in at initProgram time.
1975 angle::Result status =
1976 GlslangTransformSpirvCode(callback, shaderType, false, variableInfoMap[shaderType],
1977 initialSpirvBlobs[shaderType], &(*spirvBlobsOut)[shaderType]);
1978 if (status != angle::Result::Continue)
1979 {
1980 return status;
1981 }
1982 }
1983
1984 return angle::Result::Continue;
1985 }
1986 } // namespace rx
1987