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 // Utilities to map shader interface variables to Vulkan mappings, and transform the SPIR-V
7 // accordingly.
8 //
9
10 #include "libANGLE/renderer/vulkan/spv_utils.h"
11
12 #include <array>
13 #include <cctype>
14 #include <numeric>
15
16 #include "common/FixedVector.h"
17 #include "common/spirv/spirv_instruction_builder_autogen.h"
18 #include "common/spirv/spirv_instruction_parser_autogen.h"
19 #include "common/string_utils.h"
20 #include "common/utilities.h"
21 #include "libANGLE/Caps.h"
22 #include "libANGLE/ProgramLinkedResources.h"
23 #include "libANGLE/renderer/vulkan/ShaderInterfaceVariableInfoMap.h"
24 #include "libANGLE/renderer/vulkan/vk_cache_utils.h"
25 #include "libANGLE/trace.h"
26
27 namespace spirv = angle::spirv;
28
29 namespace rx
30 {
31 namespace
32 {
33
34 // Test if there are non-zero indices in the uniform name, returning false in that case. This
35 // happens for multi-dimensional arrays, where a uniform is created for every possible index of the
36 // array (except for the innermost dimension). When assigning decorations (set/binding/etc), only
37 // the indices corresponding to the first element of the array should be specified. This function
38 // is used to skip the other indices.
UniformNameIsIndexZero(const std::string & name)39 bool UniformNameIsIndexZero(const std::string &name)
40 {
41 size_t lastBracketClose = 0;
42
43 while (true)
44 {
45 size_t openBracket = name.find('[', lastBracketClose);
46 if (openBracket == std::string::npos)
47 {
48 break;
49 }
50 size_t closeBracket = name.find(']', openBracket);
51
52 // If the index between the brackets is not zero, ignore this uniform.
53 if (name.substr(openBracket + 1, closeBracket - openBracket - 1) != "0")
54 {
55 return false;
56 }
57 lastBracketClose = closeBracket;
58 }
59
60 return true;
61 }
62
SpvIsXfbBufferBlockId(spirv::IdRef id)63 uint32_t SpvIsXfbBufferBlockId(spirv::IdRef id)
64 {
65 return id >= sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferVarZero &&
66 id < sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferVarZero + 4;
67 }
68
69 template <typename OutputIter, typename ImplicitIter>
CountExplicitOutputs(OutputIter outputsBegin,OutputIter outputsEnd,ImplicitIter implicitsBegin,ImplicitIter implicitsEnd)70 uint32_t CountExplicitOutputs(OutputIter outputsBegin,
71 OutputIter outputsEnd,
72 ImplicitIter implicitsBegin,
73 ImplicitIter implicitsEnd)
74 {
75 auto reduce = [implicitsBegin, implicitsEnd](uint32_t count, const gl::ProgramOutput &var) {
76 bool isExplicit = std::find(implicitsBegin, implicitsEnd, var.name) == implicitsEnd;
77 return count + isExplicit;
78 };
79
80 return std::accumulate(outputsBegin, outputsEnd, 0, reduce);
81 }
82
AddResourceInfoToAllStages(ShaderInterfaceVariableInfoMap * infoMap,gl::ShaderType shaderType,uint32_t varId,uint32_t descriptorSet,uint32_t binding)83 ShaderInterfaceVariableInfo *AddResourceInfoToAllStages(ShaderInterfaceVariableInfoMap *infoMap,
84 gl::ShaderType shaderType,
85 uint32_t varId,
86 uint32_t descriptorSet,
87 uint32_t binding)
88 {
89 gl::ShaderBitSet allStages;
90 allStages.set();
91
92 ShaderInterfaceVariableInfo &info = infoMap->add(shaderType, varId);
93 info.descriptorSet = descriptorSet;
94 info.binding = binding;
95 info.activeStages = allStages;
96 return &info;
97 }
98
AddResourceInfo(ShaderInterfaceVariableInfoMap * infoMap,gl::ShaderBitSet stages,gl::ShaderType shaderType,uint32_t varId,uint32_t descriptorSet,uint32_t binding)99 ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
100 gl::ShaderBitSet stages,
101 gl::ShaderType shaderType,
102 uint32_t varId,
103 uint32_t descriptorSet,
104 uint32_t binding)
105 {
106 ShaderInterfaceVariableInfo &info = infoMap->add(shaderType, varId);
107 info.descriptorSet = descriptorSet;
108 info.binding = binding;
109 info.activeStages = stages;
110 return &info;
111 }
112
113 // Add location information for an in/out variable.
AddLocationInfo(ShaderInterfaceVariableInfoMap * infoMap,gl::ShaderType shaderType,uint32_t varId,uint32_t location,uint32_t component,uint8_t attributeComponentCount,uint8_t attributeLocationCount)114 ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
115 gl::ShaderType shaderType,
116 uint32_t varId,
117 uint32_t location,
118 uint32_t component,
119 uint8_t attributeComponentCount,
120 uint8_t attributeLocationCount)
121 {
122 // The info map for this id may or may not exist already. This function merges the
123 // location/component information.
124 ShaderInterfaceVariableInfo &info = infoMap->addOrGet(shaderType, varId);
125
126 ASSERT(info.descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
127 ASSERT(info.binding == ShaderInterfaceVariableInfo::kInvalid);
128 if (info.location != ShaderInterfaceVariableInfo::kInvalid)
129 {
130 ASSERT(info.location == location);
131 ASSERT(info.component == component);
132 }
133 ASSERT(info.component == ShaderInterfaceVariableInfo::kInvalid);
134
135 info.location = location;
136 info.component = component;
137 info.activeStages.set(shaderType);
138 info.attributeComponentCount = attributeComponentCount;
139 info.attributeLocationCount = attributeLocationCount;
140
141 return &info;
142 }
143
144 // Add location information for an in/out variable
AddVaryingLocationInfo(ShaderInterfaceVariableInfoMap * infoMap,const gl::VaryingInShaderRef & ref,const uint32_t location,const uint32_t component)145 void AddVaryingLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
146 const gl::VaryingInShaderRef &ref,
147 const uint32_t location,
148 const uint32_t component)
149 {
150 // Skip statically-unused varyings, they are already pruned by the translator
151 if (ref.varying->id != 0)
152 {
153 AddLocationInfo(infoMap, ref.stage, ref.varying->id, location, component, 0, 0);
154 }
155 }
156
157 // Modify an existing out variable and add transform feedback information.
SetXfbInfo(ShaderInterfaceVariableInfoMap * infoMap,gl::ShaderType shaderType,uint32_t varId,int fieldIndex,uint32_t xfbBuffer,uint32_t xfbOffset,uint32_t xfbStride,uint32_t arraySize,uint32_t columnCount,uint32_t rowCount,uint32_t arrayIndex,GLenum componentType)158 void SetXfbInfo(ShaderInterfaceVariableInfoMap *infoMap,
159 gl::ShaderType shaderType,
160 uint32_t varId,
161 int fieldIndex,
162 uint32_t xfbBuffer,
163 uint32_t xfbOffset,
164 uint32_t xfbStride,
165 uint32_t arraySize,
166 uint32_t columnCount,
167 uint32_t rowCount,
168 uint32_t arrayIndex,
169 GLenum componentType)
170 {
171 XFBInterfaceVariableInfo *info = infoMap->getXFBMutable(shaderType, varId);
172 ASSERT(info != nullptr);
173
174 ShaderInterfaceVariableXfbInfo *xfb = &info->xfb;
175
176 if (fieldIndex >= 0)
177 {
178 if (info->fieldXfb.size() <= static_cast<size_t>(fieldIndex))
179 {
180 info->fieldXfb.resize(fieldIndex + 1);
181 }
182 xfb = &info->fieldXfb[fieldIndex];
183 }
184
185 ASSERT(xfb->pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid);
186 ASSERT(xfb->pod.offset == ShaderInterfaceVariableXfbInfo::kInvalid);
187 ASSERT(xfb->pod.stride == ShaderInterfaceVariableXfbInfo::kInvalid);
188
189 if (arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid)
190 {
191 xfb->arrayElements.emplace_back();
192 xfb = &xfb->arrayElements.back();
193 }
194
195 xfb->pod.buffer = xfbBuffer;
196 xfb->pod.offset = xfbOffset;
197 xfb->pod.stride = xfbStride;
198 xfb->pod.arraySize = arraySize;
199 xfb->pod.columnCount = columnCount;
200 xfb->pod.rowCount = rowCount;
201 xfb->pod.arrayIndex = arrayIndex;
202 xfb->pod.componentType = componentType;
203 }
204
AssignTransformFeedbackEmulationBindings(gl::ShaderType shaderType,const gl::ProgramExecutable & programExecutable,bool isTransformFeedbackStage,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)205 void AssignTransformFeedbackEmulationBindings(gl::ShaderType shaderType,
206 const gl::ProgramExecutable &programExecutable,
207 bool isTransformFeedbackStage,
208 SpvProgramInterfaceInfo *programInterfaceInfo,
209 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
210 {
211 size_t bufferCount = 0;
212 if (isTransformFeedbackStage)
213 {
214 ASSERT(!programExecutable.getLinkedTransformFeedbackVaryings().empty());
215 const bool isInterleaved =
216 programExecutable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
217 bufferCount =
218 isInterleaved ? 1 : programExecutable.getLinkedTransformFeedbackVaryings().size();
219 }
220
221 // Add entries for the transform feedback buffers to the info map, so they can have correct
222 // set/binding.
223 for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
224 {
225 AddResourceInfo(variableInfoMapOut, gl::ShaderBitSet().set(shaderType), shaderType,
226 SpvGetXfbBufferBlockId(bufferIndex),
227 ToUnderlying(DescriptorSetIndex::UniformsAndXfb),
228 programInterfaceInfo->currentUniformBindingIndex);
229 ++programInterfaceInfo->currentUniformBindingIndex;
230 }
231
232 // Remove inactive transform feedback buffers.
233 for (uint32_t bufferIndex = static_cast<uint32_t>(bufferCount);
234 bufferIndex < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; ++bufferIndex)
235 {
236 variableInfoMapOut->add(shaderType, SpvGetXfbBufferBlockId(bufferIndex));
237 }
238 }
239
IsFirstRegisterOfVarying(const gl::PackedVaryingRegister & varyingReg,bool allowFields,uint32_t expectArrayIndex)240 bool IsFirstRegisterOfVarying(const gl::PackedVaryingRegister &varyingReg,
241 bool allowFields,
242 uint32_t expectArrayIndex)
243 {
244 const gl::PackedVarying &varying = *varyingReg.packedVarying;
245
246 // In Vulkan GLSL, struct fields are not allowed to have location assignments. The varying of a
247 // struct type is thus given a location equal to the one assigned to its first field. With I/O
248 // blocks, transform feedback can capture an arbitrary field. In that case, we need to look at
249 // every field, not just the first one.
250 if (!allowFields && varying.isStructField() &&
251 (varying.fieldIndex > 0 || varying.secondaryFieldIndex > 0))
252 {
253 return false;
254 }
255
256 // Similarly, assign array varying locations to the assigned location of the first element.
257 // Transform feedback may capture array elements, so if a specific non-zero element is
258 // requested, accept that only.
259 if (varyingReg.varyingArrayIndex != expectArrayIndex ||
260 (varying.arrayIndex != GL_INVALID_INDEX && varying.arrayIndex != expectArrayIndex))
261 {
262 return false;
263 }
264
265 // Similarly, assign matrix varying locations to the assigned location of the first row.
266 if (varyingReg.varyingRowIndex != 0)
267 {
268 return false;
269 }
270
271 return true;
272 }
273
AssignAttributeLocations(const gl::ProgramExecutable & programExecutable,gl::ShaderType shaderType,ShaderInterfaceVariableInfoMap * variableInfoMapOut)274 void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
275 gl::ShaderType shaderType,
276 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
277 {
278 const std::array<std::string, 2> implicitInputs = {"gl_VertexID", "gl_InstanceID"};
279 gl::AttributesMask isLocationAssigned;
280 bool hasAliasingAttributes = false;
281
282 // Assign attribute locations for the vertex shader.
283 for (const gl::ProgramInput &attribute : programExecutable.getProgramInputs())
284 {
285 ASSERT(attribute.isActive());
286
287 if (std::find(implicitInputs.begin(), implicitInputs.end(), attribute.name) !=
288 implicitInputs.end())
289 {
290 continue;
291 }
292
293 const uint8_t colCount = static_cast<uint8_t>(gl::VariableColumnCount(attribute.getType()));
294 const uint8_t rowCount = static_cast<uint8_t>(gl::VariableRowCount(attribute.getType()));
295 const bool isMatrix = colCount > 1 && rowCount > 1;
296
297 const uint8_t componentCount = isMatrix ? rowCount : colCount;
298 const uint8_t locationCount = isMatrix ? colCount : rowCount;
299
300 AddLocationInfo(variableInfoMapOut, shaderType, attribute.getId(), attribute.getLocation(),
301 ShaderInterfaceVariableInfo::kInvalid, componentCount, locationCount);
302
303 // Detect if there are aliasing attributes.
304 if (!hasAliasingAttributes &&
305 programExecutable.getLinkedShaderVersion(gl::ShaderType::Vertex) == 100)
306 {
307 for (uint8_t offset = 0; offset < locationCount; ++offset)
308 {
309 uint32_t location = attribute.getLocation() + offset;
310
311 // If there's aliasing, no need for futher processing.
312 if (isLocationAssigned.test(location))
313 {
314 hasAliasingAttributes = true;
315 break;
316 }
317
318 isLocationAssigned.set(location);
319 }
320 }
321 }
322
323 if (hasAliasingAttributes)
324 {
325 variableInfoMapOut->setHasAliasingAttributes();
326 }
327 }
328
AssignSecondaryOutputLocations(const gl::ProgramExecutable & programExecutable,ShaderInterfaceVariableInfoMap * variableInfoMapOut)329 void AssignSecondaryOutputLocations(const gl::ProgramExecutable &programExecutable,
330 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
331 {
332 const auto &secondaryOutputLocations = programExecutable.getSecondaryOutputLocations();
333 const auto &outputVariables = programExecutable.getOutputVariables();
334
335 // Handle EXT_blend_func_extended secondary outputs (ones with index=1)
336 for (const gl::VariableLocation &outputLocation : secondaryOutputLocations)
337 {
338 if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
339 {
340 const gl::ProgramOutput &outputVar = outputVariables[outputLocation.index];
341
342 uint32_t location = 0;
343 if (outputVar.pod.location != -1)
344 {
345 location = outputVar.pod.location;
346 }
347
348 ShaderInterfaceVariableInfo *info =
349 AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment, outputVar.pod.id,
350 location, ShaderInterfaceVariableInfo::kInvalid, 0, 0);
351
352 // Index 1 is used to specify that the color be used as the second color input to
353 // the blend equation
354 info->index = 1;
355
356 ASSERT(!outputVar.isArray() || outputVar.getOutermostArraySize() == 1);
357 info->isArray = outputVar.isArray();
358 }
359 }
360 // Handle secondary outputs for ESSL version less than 3.00
361 if (programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment) &&
362 programExecutable.getLinkedShaderVersion(gl::ShaderType::Fragment) == 100)
363 {
364 const std::array<std::string, 2> secondaryFrag = {"gl_SecondaryFragColorEXT",
365 "gl_SecondaryFragDataEXT"};
366
367 for (const gl::ProgramOutput &outputVar : outputVariables)
368 {
369 if (std::find(secondaryFrag.begin(), secondaryFrag.end(), outputVar.name) !=
370 secondaryFrag.end())
371 {
372 ShaderInterfaceVariableInfo *info =
373 AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment, outputVar.pod.id,
374 0, ShaderInterfaceVariableInfo::kInvalid, 0, 0);
375
376 info->index = 1;
377
378 ASSERT(!outputVar.isArray() || outputVar.getOutermostArraySize() == 1);
379 info->isArray = outputVar.isArray();
380
381 // SecondaryFragColor and SecondaryFragData cannot be present simultaneously.
382 break;
383 }
384 }
385 }
386 }
387
AssignOutputLocations(const gl::ProgramExecutable & programExecutable,const gl::ShaderType shaderType,ShaderInterfaceVariableInfoMap * variableInfoMapOut)388 void AssignOutputLocations(const gl::ProgramExecutable &programExecutable,
389 const gl::ShaderType shaderType,
390 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
391 {
392 // Assign output locations for the fragment shader.
393 ASSERT(shaderType == gl::ShaderType::Fragment);
394
395 const auto &outputLocations = programExecutable.getOutputLocations();
396 const auto &outputVariables = programExecutable.getOutputVariables();
397 const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask",
398 "gl_FragStencilRefARB"};
399
400 for (const gl::VariableLocation &outputLocation : outputLocations)
401 {
402 if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
403 {
404 const gl::ProgramOutput &outputVar = outputVariables[outputLocation.index];
405
406 uint32_t location = 0;
407 if (outputVar.pod.location != -1)
408 {
409 location = outputVar.pod.location;
410 }
411 else if (std::find(implicitOutputs.begin(), implicitOutputs.end(), outputVar.name) ==
412 implicitOutputs.end())
413 {
414 // If there is only one output, it is allowed not to have a location qualifier, in
415 // which case it defaults to 0. GLSL ES 3.00 spec, section 4.3.8.2.
416 ASSERT(CountExplicitOutputs(outputVariables.begin(), outputVariables.end(),
417 implicitOutputs.begin(), implicitOutputs.end()) == 1);
418 }
419
420 AddLocationInfo(variableInfoMapOut, shaderType, outputVar.pod.id, location,
421 ShaderInterfaceVariableInfo::kInvalid, 0, 0);
422 }
423 }
424 // Handle outputs for ESSL version less than 3.00
425 if (programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment) &&
426 programExecutable.getLinkedShaderVersion(gl::ShaderType::Fragment) == 100)
427 {
428 for (const gl::ProgramOutput &outputVar : outputVariables)
429 {
430 if (outputVar.name == "gl_FragColor" || outputVar.name == "gl_FragData")
431 {
432 AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment, outputVar.pod.id, 0,
433 ShaderInterfaceVariableInfo::kInvalid, 0, 0);
434 }
435 }
436 }
437
438 AssignSecondaryOutputLocations(programExecutable, variableInfoMapOut);
439 }
440
AssignVaryingLocations(const SpvSourceOptions & options,const gl::VaryingPacking & varyingPacking,const gl::ShaderType shaderType,const gl::ShaderType frontShaderType,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)441 void AssignVaryingLocations(const SpvSourceOptions &options,
442 const gl::VaryingPacking &varyingPacking,
443 const gl::ShaderType shaderType,
444 const gl::ShaderType frontShaderType,
445 SpvProgramInterfaceInfo *programInterfaceInfo,
446 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
447 {
448 uint32_t locationsUsedForEmulation = programInterfaceInfo->locationsUsedForXfbExtension;
449
450 // Assign varying locations.
451 for (const gl::PackedVaryingRegister &varyingReg : varyingPacking.getRegisterList())
452 {
453 if (!IsFirstRegisterOfVarying(varyingReg, false, 0))
454 {
455 continue;
456 }
457
458 const gl::PackedVarying &varying = *varyingReg.packedVarying;
459
460 uint32_t location = varyingReg.registerRow + locationsUsedForEmulation;
461 uint32_t component = ShaderInterfaceVariableInfo::kInvalid;
462 if (varyingReg.registerColumn > 0)
463 {
464 ASSERT(!varying.varying().isStruct());
465 ASSERT(!gl::IsMatrixType(varying.varying().type));
466 component = varyingReg.registerColumn;
467 }
468
469 if (varying.frontVarying.varying && (varying.frontVarying.stage == shaderType))
470 {
471 AddVaryingLocationInfo(variableInfoMapOut, varying.frontVarying, location, component);
472 }
473
474 if (varying.backVarying.varying && (varying.backVarying.stage == shaderType))
475 {
476 AddVaryingLocationInfo(variableInfoMapOut, varying.backVarying, location, component);
477 }
478 }
479
480 // Add an entry for inactive varyings.
481 const gl::ShaderMap<std::vector<uint32_t>> &inactiveVaryingIds =
482 varyingPacking.getInactiveVaryingIds();
483 for (const uint32_t varyingId : inactiveVaryingIds[shaderType])
484 {
485 // If id is already in the map, it will automatically have marked all other stages inactive.
486 if (variableInfoMapOut->hasVariable(shaderType, varyingId))
487 {
488 continue;
489 }
490
491 // Otherwise, add an entry for it with all locations inactive.
492 ShaderInterfaceVariableInfo &info = variableInfoMapOut->addOrGet(shaderType, varyingId);
493 ASSERT(info.location == ShaderInterfaceVariableInfo::kInvalid);
494 }
495
496 // Add an entry for gl_PerVertex, for use with transform feedback capture of built-ins.
497 ShaderInterfaceVariableInfo &info =
498 variableInfoMapOut->addOrGet(shaderType, sh::vk::spirv::kIdOutputPerVertexBlock);
499 info.activeStages.set(shaderType);
500 }
501
502 // Calculates XFB layout qualifier arguments for each transform feedback varying. Stores calculated
503 // values for the SPIR-V transformation.
AssignTransformFeedbackQualifiers(const gl::ProgramExecutable & programExecutable,const gl::VaryingPacking & varyingPacking,const gl::ShaderType shaderType,bool usesExtension,ShaderInterfaceVariableInfoMap * variableInfoMapOut)504 void AssignTransformFeedbackQualifiers(const gl::ProgramExecutable &programExecutable,
505 const gl::VaryingPacking &varyingPacking,
506 const gl::ShaderType shaderType,
507 bool usesExtension,
508 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
509 {
510 const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
511 programExecutable.getLinkedTransformFeedbackVaryings();
512 const std::vector<GLsizei> &varyingStrides = programExecutable.getTransformFeedbackStrides();
513 const bool isInterleaved =
514 programExecutable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
515
516 uint32_t currentOffset = 0;
517 uint32_t currentStride = 0;
518 uint32_t bufferIndex = 0;
519
520 for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
521 {
522 if (isInterleaved)
523 {
524 bufferIndex = 0;
525 if (varyingIndex > 0)
526 {
527 const gl::TransformFeedbackVarying &prev = tfVaryings[varyingIndex - 1];
528 currentOffset += prev.size() * gl::VariableExternalSize(prev.type);
529 }
530 currentStride = varyingStrides[0];
531 }
532 else
533 {
534 bufferIndex = varyingIndex;
535 currentOffset = 0;
536 currentStride = varyingStrides[varyingIndex];
537 }
538
539 const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
540 const gl::UniformTypeInfo &uniformInfo = gl::GetUniformTypeInfo(tfVarying.type);
541 const uint32_t varyingSize =
542 tfVarying.isArray() ? tfVarying.size() : ShaderInterfaceVariableXfbInfo::kInvalid;
543
544 if (tfVarying.isBuiltIn())
545 {
546 if (usesExtension && tfVarying.name == "gl_Position")
547 {
548 // With the extension, gl_Position is captured via a special varying.
549 SetXfbInfo(variableInfoMapOut, shaderType, sh::vk::spirv::kIdXfbExtensionPosition,
550 -1, bufferIndex, currentOffset, currentStride, varyingSize,
551 uniformInfo.columnCount, uniformInfo.rowCount,
552 ShaderInterfaceVariableXfbInfo::kInvalid, uniformInfo.componentType);
553 }
554 else
555 {
556 // gl_PerVertex is always defined as:
557 //
558 // Field 0: gl_Position
559 // Field 1: gl_PointSize
560 // Field 2: gl_ClipDistance
561 // Field 3: gl_CullDistance
562 //
563 // With the extension, all fields except gl_Position can be captured directly by
564 // decorating gl_PerVertex fields.
565 int fieldIndex = -1;
566 constexpr int kPerVertexMemberCount = 4;
567 constexpr std::array<const char *, kPerVertexMemberCount> kPerVertexMembers = {
568 "gl_Position",
569 "gl_PointSize",
570 "gl_ClipDistance",
571 "gl_CullDistance",
572 };
573 for (int index = 0; index < kPerVertexMemberCount; ++index)
574 {
575 if (tfVarying.name == kPerVertexMembers[index])
576 {
577 fieldIndex = index;
578 break;
579 }
580 }
581 ASSERT(fieldIndex != -1);
582 ASSERT(!usesExtension || fieldIndex > 0);
583
584 SetXfbInfo(variableInfoMapOut, shaderType, sh::vk::spirv::kIdOutputPerVertexBlock,
585 fieldIndex, bufferIndex, currentOffset, currentStride, varyingSize,
586 uniformInfo.columnCount, uniformInfo.rowCount,
587 ShaderInterfaceVariableXfbInfo::kInvalid, uniformInfo.componentType);
588 }
589
590 continue;
591 }
592 // Note: capturing individual array elements using the Vulkan transform feedback extension
593 // is currently not supported due to limitations in the extension.
594 // ANGLE supports capturing the whole array.
595 // http://anglebug.com/4140
596 if (usesExtension && tfVarying.isArray() && tfVarying.arrayIndex != GL_INVALID_INDEX)
597 {
598 continue;
599 }
600
601 // Find the varying with this name. If a struct is captured, we would be iterating over its
602 // fields. This is done when the first field of the struct is visited. For I/O blocks on
603 // the other hand, we need to decorate the exact member that is captured (as whole-block
604 // capture is not supported).
605 const gl::PackedVarying *originalVarying = nullptr;
606 for (const gl::PackedVaryingRegister &varyingReg : varyingPacking.getRegisterList())
607 {
608 const uint32_t arrayIndex =
609 tfVarying.arrayIndex == GL_INVALID_INDEX ? 0 : tfVarying.arrayIndex;
610 if (!IsFirstRegisterOfVarying(varyingReg, tfVarying.isShaderIOBlock, arrayIndex))
611 {
612 continue;
613 }
614
615 const gl::PackedVarying *varying = varyingReg.packedVarying;
616
617 if (tfVarying.isShaderIOBlock)
618 {
619 if (varying->frontVarying.parentStructName == tfVarying.structOrBlockName)
620 {
621 size_t pos = tfVarying.name.find_first_of(".");
622 std::string fieldName =
623 pos == std::string::npos ? tfVarying.name : tfVarying.name.substr(pos + 1);
624
625 if (fieldName == varying->frontVarying.varying->name.c_str())
626 {
627 originalVarying = varying;
628 break;
629 }
630 }
631 }
632 else if (varying->frontVarying.varying->name == tfVarying.name)
633 {
634 originalVarying = varying;
635 break;
636 }
637 }
638
639 if (originalVarying)
640 {
641 const int fieldIndex = tfVarying.isShaderIOBlock ? originalVarying->fieldIndex : -1;
642 const uint32_t arrayIndex = tfVarying.arrayIndex == GL_INVALID_INDEX
643 ? ShaderInterfaceVariableXfbInfo::kInvalid
644 : tfVarying.arrayIndex;
645
646 // Set xfb info for this varying. AssignVaryingLocations should have already added
647 // location information for these varyings.
648 SetXfbInfo(variableInfoMapOut, shaderType, originalVarying->frontVarying.varying->id,
649 fieldIndex, bufferIndex, currentOffset, currentStride, varyingSize,
650 uniformInfo.columnCount, uniformInfo.rowCount, arrayIndex,
651 uniformInfo.componentType);
652 }
653 }
654 }
655
AssignUniformBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)656 void AssignUniformBindings(const SpvSourceOptions &options,
657 const gl::ProgramExecutable &programExecutable,
658 SpvProgramInterfaceInfo *programInterfaceInfo,
659 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
660 {
661 for (const gl::ShaderType shaderType : programExecutable.getLinkedShaderStages())
662 {
663 AddResourceInfo(variableInfoMapOut, gl::ShaderBitSet().set(shaderType), shaderType,
664 sh::vk::spirv::kIdDefaultUniformsBlock,
665 ToUnderlying(DescriptorSetIndex::UniformsAndXfb),
666 programInterfaceInfo->currentUniformBindingIndex);
667 ++programInterfaceInfo->currentUniformBindingIndex;
668
669 // Assign binding to the driver uniforms block
670 AddResourceInfoToAllStages(variableInfoMapOut, shaderType,
671 sh::vk::spirv::kIdDriverUniformsBlock,
672 ToUnderlying(DescriptorSetIndex::Internal), 0);
673 }
674 }
675
AssignInputAttachmentBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)676 void AssignInputAttachmentBindings(const SpvSourceOptions &options,
677 const gl::ProgramExecutable &programExecutable,
678 SpvProgramInterfaceInfo *programInterfaceInfo,
679 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
680 {
681 if (!programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment) ||
682 !programExecutable.usesFramebufferFetch())
683 {
684 return;
685 }
686
687 const uint32_t baseInputAttachmentBindingIndex =
688 programInterfaceInfo->currentShaderResourceBindingIndex;
689 const gl::ShaderBitSet activeShaders{gl::ShaderType::Fragment};
690
691 static_assert(gl::IMPLEMENTATION_MAX_DRAW_BUFFERS <= 8,
692 "sh::vk::spirv::ReservedIds supports max 8 draw buffers");
693
694 for (size_t index : programExecutable.getFragmentInoutIndices())
695 {
696 const uint32_t inputAttachmentBindingIndex =
697 baseInputAttachmentBindingIndex + static_cast<uint32_t>(index);
698
699 AddResourceInfo(variableInfoMapOut, activeShaders, gl::ShaderType::Fragment,
700 sh::vk::spirv::kIdInputAttachment0 + static_cast<uint32_t>(index),
701 ToUnderlying(DescriptorSetIndex::ShaderResource),
702 inputAttachmentBindingIndex);
703 }
704
705 // For input attachment uniform, the descriptor set binding indices are allocated as much as
706 // the maximum draw buffers.
707 programInterfaceInfo->currentShaderResourceBindingIndex += gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
708 }
709
AssignInterfaceBlockBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,const std::vector<gl::InterfaceBlock> & blocks,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)710 void AssignInterfaceBlockBindings(const SpvSourceOptions &options,
711 const gl::ProgramExecutable &programExecutable,
712 const std::vector<gl::InterfaceBlock> &blocks,
713
714 SpvProgramInterfaceInfo *programInterfaceInfo,
715 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
716 {
717 for (uint32_t blockIndex = 0; blockIndex < blocks.size(); ++blockIndex)
718 {
719 const gl::InterfaceBlock &block = blocks[blockIndex];
720
721 // TODO: http://anglebug.com/4523: All blocks should be active
722 const gl::ShaderBitSet activeShaders =
723 programExecutable.getLinkedShaderStages() & block.activeShaders();
724 if (activeShaders.none())
725 {
726 continue;
727 }
728
729 const bool isIndexZero = !block.pod.isArray || block.pod.arrayElement == 0;
730 if (!isIndexZero)
731 {
732 continue;
733 }
734
735 variableInfoMapOut->addResource(activeShaders, block.getIds(),
736 ToUnderlying(DescriptorSetIndex::ShaderResource),
737 programInterfaceInfo->currentShaderResourceBindingIndex++);
738 }
739 }
740
AssignAtomicCounterBufferBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)741 void AssignAtomicCounterBufferBindings(const SpvSourceOptions &options,
742 const gl::ProgramExecutable &programExecutable,
743 SpvProgramInterfaceInfo *programInterfaceInfo,
744 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
745 {
746 const std::vector<gl::AtomicCounterBuffer> &buffers =
747 programExecutable.getAtomicCounterBuffers();
748
749 if (buffers.size() == 0)
750 {
751 return;
752 }
753
754 const gl::ShaderBitSet activeShaders = programExecutable.getLinkedShaderStages();
755 ASSERT(activeShaders.any());
756
757 gl::ShaderMap<uint32_t> ids = {};
758 for (const gl::ShaderType shaderType : activeShaders)
759 {
760 ids[shaderType] = sh::vk::spirv::kIdAtomicCounterBlock;
761 }
762
763 variableInfoMapOut->addResource(activeShaders, ids,
764 ToUnderlying(DescriptorSetIndex::ShaderResource),
765 programInterfaceInfo->currentShaderResourceBindingIndex++);
766 }
767
AssignImageBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)768 void AssignImageBindings(const SpvSourceOptions &options,
769 const gl::ProgramExecutable &programExecutable,
770 SpvProgramInterfaceInfo *programInterfaceInfo,
771 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
772 {
773 const std::vector<gl::LinkedUniform> &uniforms = programExecutable.getUniforms();
774 const gl::RangeUI &imageUniformRange = programExecutable.getImageUniformRange();
775 for (unsigned int uniformIndex : imageUniformRange)
776 {
777 const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
778
779 // TODO: http://anglebug.com/4523: All uniforms should be active
780 const gl::ShaderBitSet activeShaders =
781 programExecutable.getLinkedShaderStages() & imageUniform.activeShaders();
782 if (activeShaders.none())
783 {
784 continue;
785 }
786
787 const bool isIndexZero =
788 UniformNameIsIndexZero(programExecutable.getUniformNameByIndex(uniformIndex));
789 if (!isIndexZero)
790 {
791 continue;
792 }
793
794 variableInfoMapOut->addResource(activeShaders, imageUniform.getIds(),
795 ToUnderlying(DescriptorSetIndex::ShaderResource),
796 programInterfaceInfo->currentShaderResourceBindingIndex++);
797 }
798 }
799
AssignNonTextureBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)800 void AssignNonTextureBindings(const SpvSourceOptions &options,
801 const gl::ProgramExecutable &programExecutable,
802 SpvProgramInterfaceInfo *programInterfaceInfo,
803 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
804 {
805 AssignInputAttachmentBindings(options, programExecutable, programInterfaceInfo,
806 variableInfoMapOut);
807
808 const std::vector<gl::InterfaceBlock> &uniformBlocks = programExecutable.getUniformBlocks();
809 AssignInterfaceBlockBindings(options, programExecutable, uniformBlocks, programInterfaceInfo,
810 variableInfoMapOut);
811
812 const std::vector<gl::InterfaceBlock> &storageBlocks =
813 programExecutable.getShaderStorageBlocks();
814 AssignInterfaceBlockBindings(options, programExecutable, storageBlocks, programInterfaceInfo,
815 variableInfoMapOut);
816
817 AssignAtomicCounterBufferBindings(options, programExecutable, programInterfaceInfo,
818 variableInfoMapOut);
819
820 AssignImageBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
821 }
822
AssignTextureBindings(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)823 void AssignTextureBindings(const SpvSourceOptions &options,
824 const gl::ProgramExecutable &programExecutable,
825 SpvProgramInterfaceInfo *programInterfaceInfo,
826 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
827 {
828 // Assign textures to a descriptor set and binding.
829 const std::vector<gl::LinkedUniform> &uniforms = programExecutable.getUniforms();
830 const gl::RangeUI &samplerUniformRange = programExecutable.getSamplerUniformRange();
831
832 for (unsigned int uniformIndex : samplerUniformRange)
833 {
834 const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
835
836 // TODO: http://anglebug.com/4523: All uniforms should be active
837 const gl::ShaderBitSet activeShaders =
838 programExecutable.getLinkedShaderStages() & samplerUniform.activeShaders();
839 if (activeShaders.none())
840 {
841 continue;
842 }
843
844 const bool isIndexZero =
845 UniformNameIsIndexZero(programExecutable.getUniformNameByIndex(uniformIndex));
846 if (!isIndexZero)
847 {
848 continue;
849 }
850
851 variableInfoMapOut->addResource(activeShaders, samplerUniform.getIds(),
852 ToUnderlying(DescriptorSetIndex::Texture),
853 programInterfaceInfo->currentTextureBindingIndex++);
854 }
855 }
856
IsNonSemanticInstruction(const uint32_t * instruction)857 bool IsNonSemanticInstruction(const uint32_t *instruction)
858 {
859 // To avoid parsing the numerous GLSL OpExtInst instructions, take a quick peek at the set and
860 // skip instructions that aren't non-semantic.
861 return instruction[3] == sh::vk::spirv::kIdNonSemanticInstructionSet;
862 }
863
864 enum class EntryPointList
865 {
866 // Prior to SPIR-V 1.4, only the Input and Output variables are listed in OpEntryPoint.
867 InterfaceVariables,
868 // Since SPIR-V 1.4, all global variables must be listed in OpEntryPoint.
869 GlobalVariables,
870 };
871
872 // Base class for SPIR-V transformations.
873 class SpirvTransformerBase : angle::NonCopyable
874 {
875 public:
SpirvTransformerBase(const spirv::Blob & spirvBlobIn,const ShaderInterfaceVariableInfoMap & variableInfoMap,spirv::Blob * spirvBlobOut)876 SpirvTransformerBase(const spirv::Blob &spirvBlobIn,
877 const ShaderInterfaceVariableInfoMap &variableInfoMap,
878 spirv::Blob *spirvBlobOut)
879 : mSpirvBlobIn(spirvBlobIn), mVariableInfoMap(variableInfoMap), mSpirvBlobOut(spirvBlobOut)
880 {
881 gl::ShaderBitSet allStages;
882 allStages.set();
883 mBuiltinVariableInfo.activeStages = allStages;
884 }
885
getVariableInfoByIdMap()886 std::vector<const ShaderInterfaceVariableInfo *> &getVariableInfoByIdMap()
887 {
888 return mVariableInfoById;
889 }
890
891 static spirv::IdRef GetNewId(spirv::Blob *blob);
892 spirv::IdRef getNewId();
893
entryPointList() const894 EntryPointList entryPointList() const { return mEntryPointList; }
storageBufferStorageClass() const895 spv::StorageClass storageBufferStorageClass() const { return mStorageBufferStorageClass; }
896
897 protected:
898 // Common utilities
899 void onTransformBegin();
900 const uint32_t *getCurrentInstruction(spv::Op *opCodeOut, uint32_t *wordCountOut) const;
901 void copyInstruction(const uint32_t *instruction, size_t wordCount);
902
903 // SPIR-V to transform:
904 const spirv::Blob &mSpirvBlobIn;
905
906 // Input shader variable info map:
907 const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
908
909 // Transformed SPIR-V:
910 spirv::Blob *mSpirvBlobOut;
911
912 // Traversal state:
913 size_t mCurrentWord = 0;
914 bool mIsInFunctionSection = false;
915
916 // Transformation state:
917
918 // Required behavior based on SPIR-V version.
919 EntryPointList mEntryPointList = EntryPointList::InterfaceVariables;
920 spv::StorageClass mStorageBufferStorageClass = spv::StorageClassUniform;
921
922 // Shader variable info per id, if id is a shader variable.
923 std::vector<const ShaderInterfaceVariableInfo *> mVariableInfoById;
924 ShaderInterfaceVariableInfo mBuiltinVariableInfo;
925 };
926
onTransformBegin()927 void SpirvTransformerBase::onTransformBegin()
928 {
929 // The translator succeeded in outputting SPIR-V, so we assume it's valid.
930 ASSERT(mSpirvBlobIn.size() >= spirv::kHeaderIndexInstructions);
931 // Since SPIR-V comes from a local call to the translator, it necessarily has the same
932 // endianness as the running architecture, so no byte-swapping is necessary.
933 ASSERT(mSpirvBlobIn[spirv::kHeaderIndexMagic] == spv::MagicNumber);
934
935 // Make sure the transformer is not reused to avoid having to reinitialize it here.
936 ASSERT(mCurrentWord == 0);
937 ASSERT(mIsInFunctionSection == false);
938
939 // Make sure the spirv::Blob is not reused.
940 ASSERT(mSpirvBlobOut->empty());
941
942 // Copy the header to SPIR-V blob, we need that to be defined for SpirvTransformerBase::getNewId
943 // to work.
944 mSpirvBlobOut->assign(mSpirvBlobIn.begin(),
945 mSpirvBlobIn.begin() + spirv::kHeaderIndexInstructions);
946
947 mCurrentWord = spirv::kHeaderIndexInstructions;
948
949 if (mSpirvBlobIn[spirv::kHeaderIndexVersion] >= spirv::kVersion_1_4)
950 {
951 mEntryPointList = EntryPointList::GlobalVariables;
952 mStorageBufferStorageClass = spv::StorageClassStorageBuffer;
953 }
954 }
955
getCurrentInstruction(spv::Op * opCodeOut,uint32_t * wordCountOut) const956 const uint32_t *SpirvTransformerBase::getCurrentInstruction(spv::Op *opCodeOut,
957 uint32_t *wordCountOut) const
958 {
959 ASSERT(mCurrentWord < mSpirvBlobIn.size());
960 const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord];
961
962 spirv::GetInstructionOpAndLength(instruction, opCodeOut, wordCountOut);
963
964 // The translator succeeded in outputting SPIR-V, so we assume it's valid.
965 ASSERT(mCurrentWord + *wordCountOut <= mSpirvBlobIn.size());
966
967 return instruction;
968 }
969
copyInstruction(const uint32_t * instruction,size_t wordCount)970 void SpirvTransformerBase::copyInstruction(const uint32_t *instruction, size_t wordCount)
971 {
972 mSpirvBlobOut->insert(mSpirvBlobOut->end(), instruction, instruction + wordCount);
973 }
974
GetNewId(spirv::Blob * blob)975 spirv::IdRef SpirvTransformerBase::GetNewId(spirv::Blob *blob)
976 {
977 return spirv::IdRef((*blob)[spirv::kHeaderIndexIndexBound]++);
978 }
979
getNewId()980 spirv::IdRef SpirvTransformerBase::getNewId()
981 {
982 return GetNewId(mSpirvBlobOut);
983 }
984
985 enum class TransformationState
986 {
987 Transformed,
988 Unchanged,
989 };
990
991 class SpirvNonSemanticInstructions final : angle::NonCopyable
992 {
993 public:
SpirvNonSemanticInstructions(bool isLastPass)994 SpirvNonSemanticInstructions(bool isLastPass) : mIsLastPass(isLastPass) {}
995
996 // Returns whether this is a non-semantic instruction (as opposed to GLSL extended
997 // instructions). If it is non-semantic, returns the instruction code.
998 bool visitExtInst(const uint32_t *instruction, sh::vk::spirv::NonSemanticInstruction *instOut);
999
1000 // Cleans up non-semantic instructions in the last SPIR-V pass.
1001 TransformationState transformExtInst(const uint32_t *instruction);
1002
hasSampleRateShading() const1003 bool hasSampleRateShading() const
1004 {
1005 return (mOverviewFlags & sh::vk::spirv::kOverviewHasSampleRateShadingMask) != 0;
1006 }
hasSampleID() const1007 bool hasSampleID() const
1008 {
1009 return (mOverviewFlags & sh::vk::spirv::kOverviewHasSampleIDMask) != 0;
1010 }
hasOutputPerVertex() const1011 bool hasOutputPerVertex() const
1012 {
1013 return (mOverviewFlags & sh::vk::spirv::kOverviewHasOutputPerVertexMask) != 0;
1014 }
1015
1016 private:
1017 // Whether this is the last SPIR-V pass. The non-semantics instructions are removed from the
1018 // SPIR-V in the last pass.
1019 const bool mIsLastPass;
1020
1021 uint32_t mOverviewFlags;
1022 };
1023
visitExtInst(const uint32_t * instruction,sh::vk::spirv::NonSemanticInstruction * instOut)1024 bool SpirvNonSemanticInstructions::visitExtInst(const uint32_t *instruction,
1025 sh::vk::spirv::NonSemanticInstruction *instOut)
1026 {
1027 if (!IsNonSemanticInstruction(instruction))
1028 {
1029 return false;
1030 }
1031
1032 spirv::IdResultType typeId;
1033 spirv::IdResult id;
1034 spirv::IdRef set;
1035 spirv::LiteralExtInstInteger extInst;
1036 spirv::ParseExtInst(instruction, &typeId, &id, &set, &extInst, nullptr);
1037
1038 ASSERT(set == sh::vk::spirv::kIdNonSemanticInstructionSet);
1039 const uint32_t inst = extInst & sh::vk::spirv::kNonSemanticInstructionMask;
1040
1041 // Recover the additional overview flags placed in the instruction id.
1042 if (inst == sh::vk::spirv::kNonSemanticOverview)
1043 {
1044 mOverviewFlags = extInst & ~sh::vk::spirv::kNonSemanticInstructionMask;
1045 }
1046
1047 *instOut = static_cast<sh::vk::spirv::NonSemanticInstruction>(inst);
1048
1049 return true;
1050 }
1051
transformExtInst(const uint32_t * instruction)1052 TransformationState SpirvNonSemanticInstructions::transformExtInst(const uint32_t *instruction)
1053 {
1054 return IsNonSemanticInstruction(instruction) && mIsLastPass ? TransformationState::Transformed
1055 : TransformationState::Unchanged;
1056 }
1057
1058 namespace ID
1059 {
1060 namespace
1061 {
1062 [[maybe_unused]] constexpr spirv::IdRef EntryPoint(sh::vk::spirv::kIdEntryPoint);
1063 [[maybe_unused]] constexpr spirv::IdRef Void(sh::vk::spirv::kIdVoid);
1064 [[maybe_unused]] constexpr spirv::IdRef Float(sh::vk::spirv::kIdFloat);
1065 [[maybe_unused]] constexpr spirv::IdRef Vec2(sh::vk::spirv::kIdVec2);
1066 [[maybe_unused]] constexpr spirv::IdRef Vec3(sh::vk::spirv::kIdVec3);
1067 [[maybe_unused]] constexpr spirv::IdRef Vec4(sh::vk::spirv::kIdVec4);
1068 [[maybe_unused]] constexpr spirv::IdRef Mat2(sh::vk::spirv::kIdMat2);
1069 [[maybe_unused]] constexpr spirv::IdRef Mat3(sh::vk::spirv::kIdMat3);
1070 [[maybe_unused]] constexpr spirv::IdRef Mat4(sh::vk::spirv::kIdMat4);
1071 [[maybe_unused]] constexpr spirv::IdRef Int(sh::vk::spirv::kIdInt);
1072 [[maybe_unused]] constexpr spirv::IdRef IVec4(sh::vk::spirv::kIdIVec4);
1073 [[maybe_unused]] constexpr spirv::IdRef Uint(sh::vk::spirv::kIdUint);
1074 [[maybe_unused]] constexpr spirv::IdRef IntZero(sh::vk::spirv::kIdIntZero);
1075 [[maybe_unused]] constexpr spirv::IdRef IntOne(sh::vk::spirv::kIdIntOne);
1076 [[maybe_unused]] constexpr spirv::IdRef IntTwo(sh::vk::spirv::kIdIntTwo);
1077 [[maybe_unused]] constexpr spirv::IdRef IntThree(sh::vk::spirv::kIdIntThree);
1078 [[maybe_unused]] constexpr spirv::IdRef IntInputTypePointer(sh::vk::spirv::kIdIntInputTypePointer);
1079 [[maybe_unused]] constexpr spirv::IdRef Vec4OutputTypePointer(
1080 sh::vk::spirv::kIdVec4OutputTypePointer);
1081 [[maybe_unused]] constexpr spirv::IdRef IVec4FunctionTypePointer(
1082 sh::vk::spirv::kIdIVec4FunctionTypePointer);
1083 [[maybe_unused]] constexpr spirv::IdRef OutputPerVertexTypePointer(
1084 sh::vk::spirv::kIdOutputPerVertexTypePointer);
1085 [[maybe_unused]] constexpr spirv::IdRef TransformPositionFunction(
1086 sh::vk::spirv::kIdTransformPositionFunction);
1087 [[maybe_unused]] constexpr spirv::IdRef XfbEmulationGetOffsetsFunction(
1088 sh::vk::spirv::kIdXfbEmulationGetOffsetsFunction);
1089 [[maybe_unused]] constexpr spirv::IdRef SampleID(sh::vk::spirv::kIdSampleID);
1090
1091 [[maybe_unused]] constexpr spirv::IdRef InputPerVertexBlock(sh::vk::spirv::kIdInputPerVertexBlock);
1092 [[maybe_unused]] constexpr spirv::IdRef OutputPerVertexBlock(
1093 sh::vk::spirv::kIdOutputPerVertexBlock);
1094 [[maybe_unused]] constexpr spirv::IdRef OutputPerVertexVar(sh::vk::spirv::kIdOutputPerVertexVar);
1095 [[maybe_unused]] constexpr spirv::IdRef XfbExtensionPosition(
1096 sh::vk::spirv::kIdXfbExtensionPosition);
1097 [[maybe_unused]] constexpr spirv::IdRef XfbEmulationBufferBlockZero(
1098 sh::vk::spirv::kIdXfbEmulationBufferBlockZero);
1099 [[maybe_unused]] constexpr spirv::IdRef XfbEmulationBufferBlockOne(
1100 sh::vk::spirv::kIdXfbEmulationBufferBlockOne);
1101 [[maybe_unused]] constexpr spirv::IdRef XfbEmulationBufferBlockTwo(
1102 sh::vk::spirv::kIdXfbEmulationBufferBlockTwo);
1103 [[maybe_unused]] constexpr spirv::IdRef XfbEmulationBufferBlockThree(
1104 sh::vk::spirv::kIdXfbEmulationBufferBlockThree);
1105 } // anonymous namespace
1106 } // namespace ID
1107
1108 // Helper class that trims input and output gl_PerVertex declarations to remove inactive builtins.
1109 //
1110 // gl_PerVertex is unique in that it's the only builtin of struct type. This struct is pruned
1111 // by removing trailing inactive members. Note that intermediate stages, i.e. geometry and
1112 // tessellation have two gl_PerVertex declarations, one for input and one for output.
1113 class SpirvPerVertexTrimmer final : angle::NonCopyable
1114 {
1115 public:
SpirvPerVertexTrimmer(const SpvTransformOptions & options,const ShaderInterfaceVariableInfoMap & variableInfoMap)1116 SpirvPerVertexTrimmer(const SpvTransformOptions &options,
1117 const ShaderInterfaceVariableInfoMap &variableInfoMap)
1118 : mInputPerVertexMaxActiveMember{gl::PerVertexMember::Position},
1119 mOutputPerVertexMaxActiveMember{gl::PerVertexMember::Position},
1120 mInputPerVertexMaxActiveMemberIndex(0),
1121 mOutputPerVertexMaxActiveMemberIndex(0)
1122 {
1123 const gl::PerVertexMemberBitSet inputPerVertexActiveMembers =
1124 variableInfoMap.getInputPerVertexActiveMembers()[options.shaderType];
1125 const gl::PerVertexMemberBitSet outputPerVertexActiveMembers =
1126 variableInfoMap.getOutputPerVertexActiveMembers()[options.shaderType];
1127
1128 // Currently, this transformation does not trim inactive members in between two active
1129 // members.
1130 if (inputPerVertexActiveMembers.any())
1131 {
1132 mInputPerVertexMaxActiveMember = inputPerVertexActiveMembers.last();
1133 }
1134 if (outputPerVertexActiveMembers.any())
1135 {
1136 mOutputPerVertexMaxActiveMember = outputPerVertexActiveMembers.last();
1137 }
1138 }
1139
1140 void visitMemberDecorate(spirv::IdRef id,
1141 spirv::LiteralInteger member,
1142 spv::Decoration decoration,
1143 const spirv::LiteralIntegerList &valueList);
1144
1145 TransformationState transformMemberDecorate(spirv::IdRef typeId,
1146 spirv::LiteralInteger member,
1147 spv::Decoration decoration);
1148 TransformationState transformMemberName(spirv::IdRef id,
1149 spirv::LiteralInteger member,
1150 const spirv::LiteralString &name);
1151 TransformationState transformTypeStruct(spirv::IdResult id,
1152 spirv::IdRefList *memberList,
1153 spirv::Blob *blobOut);
1154
1155 private:
isPerVertex(spirv::IdRef typeId) const1156 bool isPerVertex(spirv::IdRef typeId) const
1157 {
1158 return typeId == ID::OutputPerVertexBlock || typeId == ID::InputPerVertexBlock;
1159 }
getPerVertexMaxActiveMember(spirv::IdRef typeId) const1160 uint32_t getPerVertexMaxActiveMember(spirv::IdRef typeId) const
1161 {
1162 ASSERT(isPerVertex(typeId));
1163 return typeId == ID::OutputPerVertexBlock ? mOutputPerVertexMaxActiveMemberIndex
1164 : mInputPerVertexMaxActiveMemberIndex;
1165 }
1166
1167 gl::PerVertexMember mInputPerVertexMaxActiveMember;
1168 gl::PerVertexMember mOutputPerVertexMaxActiveMember;
1169
1170 // If gl_ClipDistance and gl_CullDistance are not used, they are missing from gl_PerVertex. So
1171 // the index of gl_CullDistance may not be the same as the value of
1172 // gl::PerVertexMember::CullDistance.
1173 //
1174 // By looking at OpMemberDecorate %kIdInput/OutputPerVertexBlock <Index> BuiltIn <Member>, the
1175 // <Index> corresponding to mInput/OutputPerVertexMaxActiveMember is discovered and kept in
1176 // mInput/OutputPerVertexMaxActiveMemberIndex
1177 uint32_t mInputPerVertexMaxActiveMemberIndex;
1178 uint32_t mOutputPerVertexMaxActiveMemberIndex;
1179 };
1180
visitMemberDecorate(spirv::IdRef id,spirv::LiteralInteger member,spv::Decoration decoration,const spirv::LiteralIntegerList & valueList)1181 void SpirvPerVertexTrimmer::visitMemberDecorate(spirv::IdRef id,
1182 spirv::LiteralInteger member,
1183 spv::Decoration decoration,
1184 const spirv::LiteralIntegerList &valueList)
1185 {
1186 if (decoration != spv::DecorationBuiltIn || !isPerVertex(id))
1187 {
1188 return;
1189 }
1190
1191 // Map spv::BuiltIn to gl::PerVertexMember.
1192 ASSERT(!valueList.empty());
1193 const uint32_t builtIn = valueList[0];
1194 gl::PerVertexMember perVertexMember = gl::PerVertexMember::Position;
1195 switch (builtIn)
1196 {
1197 case spv::BuiltInPosition:
1198 perVertexMember = gl::PerVertexMember::Position;
1199 break;
1200 case spv::BuiltInPointSize:
1201 perVertexMember = gl::PerVertexMember::PointSize;
1202 break;
1203 case spv::BuiltInClipDistance:
1204 perVertexMember = gl::PerVertexMember::ClipDistance;
1205 break;
1206 case spv::BuiltInCullDistance:
1207 perVertexMember = gl::PerVertexMember::CullDistance;
1208 break;
1209 default:
1210 UNREACHABLE();
1211 }
1212
1213 if (id == ID::OutputPerVertexBlock && perVertexMember == mOutputPerVertexMaxActiveMember)
1214 {
1215 mOutputPerVertexMaxActiveMemberIndex = member;
1216 }
1217 else if (id == ID::InputPerVertexBlock && perVertexMember == mInputPerVertexMaxActiveMember)
1218 {
1219 mInputPerVertexMaxActiveMemberIndex = member;
1220 }
1221 }
1222
transformMemberDecorate(spirv::IdRef typeId,spirv::LiteralInteger member,spv::Decoration decoration)1223 TransformationState SpirvPerVertexTrimmer::transformMemberDecorate(spirv::IdRef typeId,
1224 spirv::LiteralInteger member,
1225 spv::Decoration decoration)
1226 {
1227 // Transform the following:
1228 //
1229 // - OpMemberDecorate %gl_PerVertex N BuiltIn B
1230 // - OpMemberDecorate %gl_PerVertex N Invariant
1231 // - OpMemberDecorate %gl_PerVertex N RelaxedPrecision
1232 if (!isPerVertex(typeId) ||
1233 (decoration != spv::DecorationBuiltIn && decoration != spv::DecorationInvariant &&
1234 decoration != spv::DecorationRelaxedPrecision))
1235 {
1236 return TransformationState::Unchanged;
1237 }
1238
1239 // Drop stripped fields.
1240 return member > getPerVertexMaxActiveMember(typeId) ? TransformationState::Transformed
1241 : TransformationState::Unchanged;
1242 }
1243
transformMemberName(spirv::IdRef id,spirv::LiteralInteger member,const spirv::LiteralString & name)1244 TransformationState SpirvPerVertexTrimmer::transformMemberName(spirv::IdRef id,
1245 spirv::LiteralInteger member,
1246 const spirv::LiteralString &name)
1247 {
1248 // Remove the instruction if it's a stripped member of gl_PerVertex.
1249 return isPerVertex(id) && member > getPerVertexMaxActiveMember(id)
1250 ? TransformationState::Transformed
1251 : TransformationState::Unchanged;
1252 }
1253
transformTypeStruct(spirv::IdResult id,spirv::IdRefList * memberList,spirv::Blob * blobOut)1254 TransformationState SpirvPerVertexTrimmer::transformTypeStruct(spirv::IdResult id,
1255 spirv::IdRefList *memberList,
1256 spirv::Blob *blobOut)
1257 {
1258 if (!isPerVertex(id))
1259 {
1260 return TransformationState::Unchanged;
1261 }
1262
1263 const uint32_t maxMembers = getPerVertexMaxActiveMember(id);
1264
1265 // Change the definition of the gl_PerVertex struct by stripping unused fields at the end.
1266 const uint32_t memberCount = maxMembers + 1;
1267 memberList->resize_down(memberCount);
1268
1269 spirv::WriteTypeStruct(blobOut, id, *memberList);
1270
1271 return TransformationState::Transformed;
1272 }
1273
1274 // Helper class that removes inactive varyings and replaces them with Private variables.
1275 class SpirvInactiveVaryingRemover final : angle::NonCopyable
1276 {
1277 public:
SpirvInactiveVaryingRemover()1278 SpirvInactiveVaryingRemover() {}
1279
1280 void init(size_t indexCount);
1281
1282 TransformationState transformAccessChain(spirv::IdResultType typeId,
1283 spirv::IdResult id,
1284 spirv::IdRef baseId,
1285 const spirv::IdRefList &indexList,
1286 spirv::Blob *blobOut);
1287 TransformationState transformDecorate(const ShaderInterfaceVariableInfo &info,
1288 gl::ShaderType shaderType,
1289 spirv::IdRef id,
1290 spv::Decoration decoration,
1291 const spirv::LiteralIntegerList &decorationValues,
1292 spirv::Blob *blobOut);
1293 TransformationState transformTypePointer(spirv::IdResult id,
1294 spv::StorageClass storageClass,
1295 spirv::IdRef typeId,
1296 spirv::Blob *blobOut);
1297 TransformationState transformVariable(spirv::IdResultType typeId,
1298 spirv::IdResult id,
1299 spv::StorageClass storageClass,
1300 spirv::Blob *blobOut);
1301
1302 void modifyEntryPointInterfaceList(
1303 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1304 gl::ShaderType shaderType,
1305 EntryPointList entryPointList,
1306 spirv::IdRefList *interfaceList);
1307
isInactive(spirv::IdRef id) const1308 bool isInactive(spirv::IdRef id) const { return mIsInactiveById[id]; }
1309
getTransformedPrivateType(spirv::IdRef id) const1310 spirv::IdRef getTransformedPrivateType(spirv::IdRef id) const
1311 {
1312 ASSERT(id < mTypePointerTransformedId.size());
1313 return mTypePointerTransformedId[id];
1314 }
1315
1316 private:
1317 // Each OpTypePointer instruction that defines a type with the Output storage class is
1318 // duplicated with a similar instruction but which defines a type with the Private storage
1319 // class. If inactive varyings are encountered, its type is changed to the Private one. The
1320 // following vector maps the Output type id to the corresponding Private one.
1321 std::vector<spirv::IdRef> mTypePointerTransformedId;
1322
1323 // Whether a variable has been marked inactive.
1324 std::vector<bool> mIsInactiveById;
1325 };
1326
init(size_t indexBound)1327 void SpirvInactiveVaryingRemover::init(size_t indexBound)
1328 {
1329 // Allocate storage for Output type pointer map. At index i, this vector holds the identical
1330 // type as %i except for its storage class turned to Private.
1331 mTypePointerTransformedId.resize(indexBound);
1332 mIsInactiveById.resize(indexBound, false);
1333 }
1334
transformAccessChain(spirv::IdResultType typeId,spirv::IdResult id,spirv::IdRef baseId,const spirv::IdRefList & indexList,spirv::Blob * blobOut)1335 TransformationState SpirvInactiveVaryingRemover::transformAccessChain(
1336 spirv::IdResultType typeId,
1337 spirv::IdResult id,
1338 spirv::IdRef baseId,
1339 const spirv::IdRefList &indexList,
1340 spirv::Blob *blobOut)
1341 {
1342 // Modifiy the instruction to use the private type.
1343 ASSERT(typeId < mTypePointerTransformedId.size());
1344 ASSERT(mTypePointerTransformedId[typeId].valid());
1345
1346 spirv::WriteAccessChain(blobOut, mTypePointerTransformedId[typeId], id, baseId, indexList);
1347
1348 return TransformationState::Transformed;
1349 }
1350
transformDecorate(const ShaderInterfaceVariableInfo & info,gl::ShaderType shaderType,spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & decorationValues,spirv::Blob * blobOut)1351 TransformationState SpirvInactiveVaryingRemover::transformDecorate(
1352 const ShaderInterfaceVariableInfo &info,
1353 gl::ShaderType shaderType,
1354 spirv::IdRef id,
1355 spv::Decoration decoration,
1356 const spirv::LiteralIntegerList &decorationValues,
1357 spirv::Blob *blobOut)
1358 {
1359 // If it's an inactive varying, remove the decoration altogether.
1360 return info.activeStages[shaderType] ? TransformationState::Unchanged
1361 : TransformationState::Transformed;
1362 }
1363
modifyEntryPointInterfaceList(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,gl::ShaderType shaderType,EntryPointList entryPointList,spirv::IdRefList * interfaceList)1364 void SpirvInactiveVaryingRemover::modifyEntryPointInterfaceList(
1365 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1366 gl::ShaderType shaderType,
1367 EntryPointList entryPointList,
1368 spirv::IdRefList *interfaceList)
1369 {
1370 // Nothing to do if SPIR-V 1.4, each inactive variable is replaced with a Private varaible, but
1371 // its ID is retained and stays in the variable list.
1372 if (entryPointList == EntryPointList::GlobalVariables)
1373 {
1374 return;
1375 }
1376
1377 // Filter out inactive varyings from entry point interface declaration.
1378 size_t writeIndex = 0;
1379 for (size_t index = 0; index < interfaceList->size(); ++index)
1380 {
1381 spirv::IdRef id((*interfaceList)[index]);
1382 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
1383
1384 ASSERT(info);
1385
1386 if (!info->activeStages[shaderType])
1387 {
1388 continue;
1389 }
1390
1391 (*interfaceList)[writeIndex] = id;
1392 ++writeIndex;
1393 }
1394
1395 // Update the number of interface variables.
1396 interfaceList->resize_down(writeIndex);
1397 }
1398
transformTypePointer(spirv::IdResult id,spv::StorageClass storageClass,spirv::IdRef typeId,spirv::Blob * blobOut)1399 TransformationState SpirvInactiveVaryingRemover::transformTypePointer(
1400 spirv::IdResult id,
1401 spv::StorageClass storageClass,
1402 spirv::IdRef typeId,
1403 spirv::Blob *blobOut)
1404 {
1405 // If the storage class is output, this may be used to create a variable corresponding to an
1406 // inactive varying, or if that varying is a struct, an Op*AccessChain retrieving a field of
1407 // that inactive varying.
1408 //
1409 // SPIR-V specifies the storage class both on the type and the variable declaration. Otherwise
1410 // it would have been sufficient to modify the OpVariable instruction. For simplicity, duplicate
1411 // every "OpTypePointer Output" and "OpTypePointer Input" instruction except with the Private
1412 // storage class, in case it may be necessary later.
1413
1414 // Cannot create a Private type declaration from builtins such as gl_PerVertex.
1415 if (typeId == sh::vk::spirv::kIdInputPerVertexBlock ||
1416 typeId == sh::vk::spirv::kIdOutputPerVertexBlock ||
1417 typeId == sh::vk::spirv::kIdInputPerVertexBlockArray ||
1418 typeId == sh::vk::spirv::kIdOutputPerVertexBlockArray)
1419 {
1420 return TransformationState::Unchanged;
1421 }
1422
1423 if (storageClass != spv::StorageClassOutput && storageClass != spv::StorageClassInput)
1424 {
1425 return TransformationState::Unchanged;
1426 }
1427
1428 const spirv::IdRef newPrivateTypeId(SpirvTransformerBase::GetNewId(blobOut));
1429
1430 // Write OpTypePointer for the new PrivateType.
1431 spirv::WriteTypePointer(blobOut, newPrivateTypeId, spv::StorageClassPrivate, typeId);
1432
1433 // Remember the id of the replacement.
1434 ASSERT(id < mTypePointerTransformedId.size());
1435 mTypePointerTransformedId[id] = newPrivateTypeId;
1436
1437 // The original instruction should still be present as well. At this point, we don't know
1438 // whether we will need the original or Private type.
1439 return TransformationState::Unchanged;
1440 }
1441
transformVariable(spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass,spirv::Blob * blobOut)1442 TransformationState SpirvInactiveVaryingRemover::transformVariable(spirv::IdResultType typeId,
1443 spirv::IdResult id,
1444 spv::StorageClass storageClass,
1445 spirv::Blob *blobOut)
1446 {
1447 ASSERT(storageClass == spv::StorageClassOutput || storageClass == spv::StorageClassInput);
1448
1449 ASSERT(typeId < mTypePointerTransformedId.size());
1450 ASSERT(mTypePointerTransformedId[typeId].valid());
1451 spirv::WriteVariable(blobOut, mTypePointerTransformedId[typeId], id, spv::StorageClassPrivate,
1452 nullptr);
1453
1454 mIsInactiveById[id] = true;
1455
1456 return TransformationState::Transformed;
1457 }
1458
1459 // Helper class that fixes varying precisions so they match between shader stages.
1460 class SpirvVaryingPrecisionFixer final : angle::NonCopyable
1461 {
1462 public:
SpirvVaryingPrecisionFixer()1463 SpirvVaryingPrecisionFixer() {}
1464
1465 void init(size_t indexBound);
1466
1467 void visitTypePointer(spirv::IdResult id, spv::StorageClass storageClass, spirv::IdRef typeId);
1468 void visitVariable(const ShaderInterfaceVariableInfo &info,
1469 gl::ShaderType shaderType,
1470 spirv::IdResultType typeId,
1471 spirv::IdResult id,
1472 spv::StorageClass storageClass,
1473 spirv::Blob *blobOut);
1474
1475 TransformationState transformVariable(const ShaderInterfaceVariableInfo &info,
1476 spirv::IdResultType typeId,
1477 spirv::IdResult id,
1478 spv::StorageClass storageClass,
1479 spirv::Blob *blobOut);
1480
1481 void modifyEntryPointInterfaceList(EntryPointList entryPointList,
1482 spirv::IdRefList *interfaceList);
1483 void addDecorate(spirv::IdRef replacedId, spirv::Blob *blobOut);
1484 void writeInputPreamble(
1485 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1486 gl::ShaderType shaderType,
1487 spirv::Blob *blobOut);
1488 void writeOutputPrologue(
1489 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1490 gl::ShaderType shaderType,
1491 spirv::Blob *blobOut);
1492
isReplaced(spirv::IdRef id) const1493 bool isReplaced(spirv::IdRef id) const { return mFixedVaryingId[id].valid(); }
getReplacementId(spirv::IdRef id) const1494 spirv::IdRef getReplacementId(spirv::IdRef id) const
1495 {
1496 return mFixedVaryingId[id].valid() ? mFixedVaryingId[id] : id;
1497 }
1498
1499 private:
1500 std::vector<spirv::IdRef> mTypePointerTypeId;
1501 std::vector<spirv::IdRef> mFixedVaryingId;
1502 std::vector<spirv::IdRef> mFixedVaryingTypeId;
1503 };
1504
init(size_t indexBound)1505 void SpirvVaryingPrecisionFixer::init(size_t indexBound)
1506 {
1507 // Allocate storage for precision mismatch fix up.
1508 mTypePointerTypeId.resize(indexBound);
1509 mFixedVaryingId.resize(indexBound);
1510 mFixedVaryingTypeId.resize(indexBound);
1511 }
1512
visitTypePointer(spirv::IdResult id,spv::StorageClass storageClass,spirv::IdRef typeId)1513 void SpirvVaryingPrecisionFixer::visitTypePointer(spirv::IdResult id,
1514 spv::StorageClass storageClass,
1515 spirv::IdRef typeId)
1516 {
1517 mTypePointerTypeId[id] = typeId;
1518 }
1519
visitVariable(const ShaderInterfaceVariableInfo & info,gl::ShaderType shaderType,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass,spirv::Blob * blobOut)1520 void SpirvVaryingPrecisionFixer::visitVariable(const ShaderInterfaceVariableInfo &info,
1521 gl::ShaderType shaderType,
1522 spirv::IdResultType typeId,
1523 spirv::IdResult id,
1524 spv::StorageClass storageClass,
1525 spirv::Blob *blobOut)
1526 {
1527 if (info.useRelaxedPrecision && info.activeStages[shaderType] && !mFixedVaryingId[id].valid())
1528 {
1529 mFixedVaryingId[id] = SpirvTransformerBase::GetNewId(blobOut);
1530 mFixedVaryingTypeId[id] = typeId;
1531 }
1532 }
1533
transformVariable(const ShaderInterfaceVariableInfo & info,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass,spirv::Blob * blobOut)1534 TransformationState SpirvVaryingPrecisionFixer::transformVariable(
1535 const ShaderInterfaceVariableInfo &info,
1536 spirv::IdResultType typeId,
1537 spirv::IdResult id,
1538 spv::StorageClass storageClass,
1539 spirv::Blob *blobOut)
1540 {
1541 if (info.useRelaxedPrecision &&
1542 (storageClass == spv::StorageClassOutput || storageClass == spv::StorageClassInput))
1543 {
1544 // Change existing OpVariable to use fixedVaryingId
1545 ASSERT(mFixedVaryingId[id].valid());
1546 spirv::WriteVariable(blobOut, typeId, mFixedVaryingId[id], storageClass, nullptr);
1547
1548 return TransformationState::Transformed;
1549 }
1550 return TransformationState::Unchanged;
1551 }
1552
writeInputPreamble(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,gl::ShaderType shaderType,spirv::Blob * blobOut)1553 void SpirvVaryingPrecisionFixer::writeInputPreamble(
1554 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1555 gl::ShaderType shaderType,
1556 spirv::Blob *blobOut)
1557 {
1558 if (shaderType == gl::ShaderType::Vertex || shaderType == gl::ShaderType::Compute)
1559 {
1560 return;
1561 }
1562
1563 // Copy from corrected varyings to temp global variables with original precision.
1564 for (uint32_t idIndex = spirv::kMinValidId; idIndex < variableInfoById.size(); idIndex++)
1565 {
1566 const spirv::IdRef id(idIndex);
1567 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
1568 if (info && info->useRelaxedPrecision && info->activeStages[shaderType] &&
1569 info->varyingIsInput)
1570 {
1571 // This is an input varying, need to cast the mediump value that came from
1572 // the previous stage into a highp value that the code wants to work with.
1573 ASSERT(mFixedVaryingTypeId[id].valid());
1574
1575 // Build OpLoad instruction to load the mediump value into a temporary
1576 const spirv::IdRef tempVar(SpirvTransformerBase::GetNewId(blobOut));
1577 const spirv::IdRef tempVarType(mTypePointerTypeId[mFixedVaryingTypeId[id]]);
1578 ASSERT(tempVarType.valid());
1579
1580 spirv::WriteLoad(blobOut, tempVarType, tempVar, mFixedVaryingId[id], nullptr);
1581
1582 // Build OpStore instruction to cast the mediump value to highp for use in
1583 // the function
1584 spirv::WriteStore(blobOut, id, tempVar, nullptr);
1585 }
1586 }
1587 }
1588
modifyEntryPointInterfaceList(EntryPointList entryPointList,spirv::IdRefList * interfaceList)1589 void SpirvVaryingPrecisionFixer::modifyEntryPointInterfaceList(EntryPointList entryPointList,
1590 spirv::IdRefList *interfaceList)
1591 {
1592 // With SPIR-V 1.3, modify interface list if any ID was replaced due to varying precision
1593 // mismatch.
1594 //
1595 // With SPIR-V 1.4, the original variables are changed to Private and should remain in the list.
1596 // The new variables should be added to the variable list.
1597 const size_t variableCount = interfaceList->size();
1598 for (size_t index = 0; index < variableCount; ++index)
1599 {
1600 const spirv::IdRef id = (*interfaceList)[index];
1601 const spirv::IdRef replacementId = getReplacementId(id);
1602 if (replacementId != id)
1603 {
1604 if (entryPointList == EntryPointList::InterfaceVariables)
1605 {
1606 (*interfaceList)[index] = replacementId;
1607 }
1608 else
1609 {
1610 interfaceList->push_back(replacementId);
1611 }
1612 }
1613 }
1614 }
1615
addDecorate(spirv::IdRef replacedId,spirv::Blob * blobOut)1616 void SpirvVaryingPrecisionFixer::addDecorate(spirv::IdRef replacedId, spirv::Blob *blobOut)
1617 {
1618 spirv::WriteDecorate(blobOut, replacedId, spv::DecorationRelaxedPrecision, {});
1619 }
1620
writeOutputPrologue(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,gl::ShaderType shaderType,spirv::Blob * blobOut)1621 void SpirvVaryingPrecisionFixer::writeOutputPrologue(
1622 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1623 gl::ShaderType shaderType,
1624 spirv::Blob *blobOut)
1625 {
1626 if (shaderType == gl::ShaderType::Fragment || shaderType == gl::ShaderType::Compute)
1627 {
1628 return;
1629 }
1630
1631 // Copy from temp global variables with original precision to corrected varyings.
1632 for (uint32_t idIndex = spirv::kMinValidId; idIndex < variableInfoById.size(); idIndex++)
1633 {
1634 const spirv::IdRef id(idIndex);
1635 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
1636 if (info && info->useRelaxedPrecision && info->activeStages[shaderType] &&
1637 info->varyingIsOutput)
1638 {
1639 ASSERT(mFixedVaryingTypeId[id].valid());
1640
1641 // Build OpLoad instruction to load the highp value into a temporary
1642 const spirv::IdRef tempVar(SpirvTransformerBase::GetNewId(blobOut));
1643 const spirv::IdRef tempVarType(mTypePointerTypeId[mFixedVaryingTypeId[id]]);
1644 ASSERT(tempVarType.valid());
1645
1646 spirv::WriteLoad(blobOut, tempVarType, tempVar, id, nullptr);
1647
1648 // Build OpStore instruction to cast the highp value to mediump for output
1649 spirv::WriteStore(blobOut, mFixedVaryingId[id], tempVar, nullptr);
1650 }
1651 }
1652 }
1653
1654 // Helper class that generates code for transform feedback
1655 class SpirvTransformFeedbackCodeGenerator final : angle::NonCopyable
1656 {
1657 public:
SpirvTransformFeedbackCodeGenerator(const SpvTransformOptions & options)1658 SpirvTransformFeedbackCodeGenerator(const SpvTransformOptions &options)
1659 : mIsEmulated(options.isTransformFeedbackEmulated),
1660 mHasTransformFeedbackOutput(false),
1661 mIsPositionCapturedByTransformFeedbackExtension(false)
1662 {}
1663
1664 void visitVariable(const ShaderInterfaceVariableInfo &info,
1665 const XFBInterfaceVariableInfo &xfbInfo,
1666 gl::ShaderType shaderType,
1667 spirv::IdResultType typeId,
1668 spirv::IdResult id,
1669 spv::StorageClass storageClass);
1670
1671 TransformationState transformCapability(spv::Capability capability, spirv::Blob *blobOut);
1672 TransformationState transformDecorate(const ShaderInterfaceVariableInfo *info,
1673 gl::ShaderType shaderType,
1674 spirv::IdRef id,
1675 spv::Decoration decoration,
1676 const spirv::LiteralIntegerList &decorationValues,
1677 spirv::Blob *blobOut);
1678 TransformationState transformMemberDecorate(const ShaderInterfaceVariableInfo *info,
1679 gl::ShaderType shaderType,
1680 spirv::IdRef id,
1681 spirv::LiteralInteger member,
1682 spv::Decoration decoration,
1683 spirv::Blob *blobOut);
1684 TransformationState transformName(spirv::IdRef id, spirv::LiteralString name);
1685 TransformationState transformMemberName(spirv::IdRef id,
1686 spirv::LiteralInteger member,
1687 spirv::LiteralString name);
1688 TransformationState transformTypeStruct(const ShaderInterfaceVariableInfo *info,
1689 gl::ShaderType shaderType,
1690 spirv::IdResult id,
1691 const spirv::IdRefList &memberList,
1692 spirv::Blob *blobOut);
1693 TransformationState transformTypePointer(const ShaderInterfaceVariableInfo *info,
1694 gl::ShaderType shaderType,
1695 spirv::IdResult id,
1696 spv::StorageClass storageClass,
1697 spirv::IdRef typeId,
1698 spirv::Blob *blobOut);
1699 TransformationState transformVariable(const ShaderInterfaceVariableInfo &info,
1700 const ShaderInterfaceVariableInfoMap &variableInfoMap,
1701 gl::ShaderType shaderType,
1702 spv::StorageClass storageBufferStorageClass,
1703 spirv::IdResultType typeId,
1704 spirv::IdResult id,
1705 spv::StorageClass storageClass);
1706
1707 void modifyEntryPointInterfaceList(
1708 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1709 gl::ShaderType shaderType,
1710 EntryPointList entryPointList,
1711 spirv::IdRefList *interfaceList);
1712
1713 void writePendingDeclarations(
1714 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1715 spv::StorageClass storageBufferStorageClass,
1716 spirv::Blob *blobOut);
1717 void writeTransformFeedbackExtensionOutput(spirv::IdRef positionId, spirv::Blob *blobOut);
1718 void writeTransformFeedbackEmulationOutput(
1719 const SpirvInactiveVaryingRemover &inactiveVaryingRemover,
1720 const SpirvVaryingPrecisionFixer &varyingPrecisionFixer,
1721 const bool usePrecisionFixer,
1722 spirv::Blob *blobOut);
1723 void addExecutionMode(spirv::IdRef entryPointId, spirv::Blob *blobOut);
1724 void addMemberDecorate(const XFBInterfaceVariableInfo &info,
1725 spirv::IdRef id,
1726 spirv::Blob *blobOut);
1727 void addDecorate(const XFBInterfaceVariableInfo &xfbInfo,
1728 spirv::IdRef id,
1729 spirv::Blob *blobOut);
1730
1731 private:
1732 void gatherXfbVaryings(const XFBInterfaceVariableInfo &info, spirv::IdRef id);
1733 void visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
1734 spirv::IdRef baseId,
1735 uint32_t fieldIndex);
1736 TransformationState transformTypeHelper(const ShaderInterfaceVariableInfo *info,
1737 gl::ShaderType shaderType,
1738 spirv::IdResult id);
1739 void writeIntConstant(uint32_t value, spirv::IdRef intId, spirv::Blob *blobOut);
1740 void getVaryingTypeIds(GLenum componentType,
1741 bool isPrivate,
1742 spirv::IdRef *typeIdOut,
1743 spirv::IdRef *typePtrOut);
1744 void writeGetOffsetsCall(spirv::IdRef xfbOffsets, spirv::Blob *blobOut);
1745 void writeComponentCapture(uint32_t bufferIndex,
1746 spirv::IdRef xfbOffset,
1747 spirv::IdRef varyingTypeId,
1748 spirv::IdRef varyingTypePtr,
1749 spirv::IdRef varyingBaseId,
1750 const spirv::IdRefList &accessChainIndices,
1751 GLenum componentType,
1752 spirv::Blob *blobOut);
1753
1754 static constexpr size_t kXfbDecorationCount = 3;
1755 static constexpr spv::Decoration kXfbDecorations[kXfbDecorationCount] = {
1756 spv::DecorationXfbBuffer,
1757 spv::DecorationXfbStride,
1758 spv::DecorationOffset,
1759 };
1760
1761 bool mIsEmulated;
1762 bool mHasTransformFeedbackOutput;
1763
1764 // Ids needed to generate transform feedback support code.
1765 bool mIsPositionCapturedByTransformFeedbackExtension;
1766 gl::TransformFeedbackBuffersArray<spirv::IdRef> mBufferStrides;
1767 spirv::IdRef mBufferStridesCompositeId;
1768
1769 // Type and constant ids:
1770 //
1771 // - mFloatOutputPointerId: id of OpTypePointer Output %kIdFloat
1772 // - mIntOutputPointerId: id of OpTypePointer Output %kIdInt
1773 // - mUintOutputPointerId: id of OpTypePointer Output %kIdUint
1774 // - mFloatPrivatePointerId, mIntPrivatePointerId, mUintPrivatePointerId: identical to the
1775 // above, but with the Private storage class. Used to load from varyings that have been
1776 // replaced as part of precision mismatch fixup.
1777 // - mFloatUniformPointerId: id of OpTypePointer Uniform %kIdFloat
1778 //
1779 // - mIntNIds[n]: id of OpConstant %kIdInt n
1780 spirv::IdRef mFloatOutputPointerId;
1781 spirv::IdRef mIntOutputPointerId;
1782 spirv::IdRef mUintOutputPointerId;
1783 spirv::IdRef mFloatPrivatePointerId;
1784 spirv::IdRef mIntPrivatePointerId;
1785 spirv::IdRef mUintPrivatePointerId;
1786 spirv::IdRef mFloatUniformPointerId;
1787
1788 // Id of constants such as row, column and array index. Integers 0, 1, 2 and 3 are always
1789 // defined by the compiler.
1790 angle::FastVector<spirv::IdRef, 4> mIntNIds;
1791
1792 // For transform feedback emulation, the captured elements are gathered in a list and sorted.
1793 // This allows the output generation code to always use offset += 1, thus relying on only one
1794 // constant (1).
1795 struct XfbVarying
1796 {
1797 // The varyings are sorted by info.offset.
1798 const ShaderInterfaceVariableXfbInfo *info;
1799 // Id of the base variable.
1800 spirv::IdRef baseId;
1801 // The field index, if a member of an I/O blocks
1802 uint32_t fieldIndex;
1803 };
1804 gl::TransformFeedbackBuffersArray<std::vector<XfbVarying>> mXfbVaryings;
1805 };
1806
1807 constexpr size_t SpirvTransformFeedbackCodeGenerator::kXfbDecorationCount;
1808 constexpr spv::Decoration SpirvTransformFeedbackCodeGenerator::kXfbDecorations[kXfbDecorationCount];
1809
visitVariable(const ShaderInterfaceVariableInfo & info,const XFBInterfaceVariableInfo & xfbInfo,gl::ShaderType shaderType,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass)1810 void SpirvTransformFeedbackCodeGenerator::visitVariable(const ShaderInterfaceVariableInfo &info,
1811 const XFBInterfaceVariableInfo &xfbInfo,
1812 gl::ShaderType shaderType,
1813 spirv::IdResultType typeId,
1814 spirv::IdResult id,
1815 spv::StorageClass storageClass)
1816 {
1817 if (mIsEmulated)
1818 {
1819 gatherXfbVaryings(xfbInfo, id);
1820 return;
1821 }
1822
1823 // Note if the variable is captured by transform feedback. In that case, the TransformFeedback
1824 // capability needs to be added.
1825 if ((xfbInfo.xfb.pod.buffer != ShaderInterfaceVariableInfo::kInvalid ||
1826 !xfbInfo.fieldXfb.empty()) &&
1827 info.activeStages[shaderType])
1828 {
1829 mHasTransformFeedbackOutput = true;
1830
1831 // If this is the special ANGLEXfbPosition variable, remember its id to be used for the
1832 // ANGLEXfbPosition = gl_Position; assignment code generation.
1833 if (id == ID::XfbExtensionPosition)
1834 {
1835 mIsPositionCapturedByTransformFeedbackExtension = true;
1836 }
1837 }
1838 }
1839
transformCapability(spv::Capability capability,spirv::Blob * blobOut)1840 TransformationState SpirvTransformFeedbackCodeGenerator::transformCapability(
1841 spv::Capability capability,
1842 spirv::Blob *blobOut)
1843 {
1844 if (!mHasTransformFeedbackOutput || mIsEmulated)
1845 {
1846 return TransformationState::Unchanged;
1847 }
1848
1849 // Transform feedback capability shouldn't have already been specified.
1850 ASSERT(capability != spv::CapabilityTransformFeedback);
1851
1852 // Vulkan shaders have either Shader, Geometry or Tessellation capability. We find this
1853 // capability, and add the TransformFeedback capability right before it.
1854 if (capability != spv::CapabilityShader && capability != spv::CapabilityGeometry &&
1855 capability != spv::CapabilityTessellation)
1856 {
1857 return TransformationState::Unchanged;
1858 }
1859
1860 // Write the TransformFeedback capability declaration.
1861 spirv::WriteCapability(blobOut, spv::CapabilityTransformFeedback);
1862
1863 // The original capability is retained.
1864 return TransformationState::Unchanged;
1865 }
1866
transformName(spirv::IdRef id,spirv::LiteralString name)1867 TransformationState SpirvTransformFeedbackCodeGenerator::transformName(spirv::IdRef id,
1868 spirv::LiteralString name)
1869 {
1870 // In the case of ANGLEXfbN, unconditionally remove the variable names. If transform
1871 // feedback is not active, the corresponding variables will be removed.
1872 switch (id)
1873 {
1874 case sh::vk::spirv::kIdXfbEmulationBufferBlockZero:
1875 case sh::vk::spirv::kIdXfbEmulationBufferBlockOne:
1876 case sh::vk::spirv::kIdXfbEmulationBufferBlockTwo:
1877 case sh::vk::spirv::kIdXfbEmulationBufferBlockThree:
1878 case sh::vk::spirv::kIdXfbEmulationBufferVarZero:
1879 case sh::vk::spirv::kIdXfbEmulationBufferVarOne:
1880 case sh::vk::spirv::kIdXfbEmulationBufferVarTwo:
1881 case sh::vk::spirv::kIdXfbEmulationBufferVarThree:
1882 return TransformationState::Transformed;
1883 default:
1884 return TransformationState::Unchanged;
1885 }
1886 }
1887
transformMemberName(spirv::IdRef id,spirv::LiteralInteger member,spirv::LiteralString name)1888 TransformationState SpirvTransformFeedbackCodeGenerator::transformMemberName(
1889 spirv::IdRef id,
1890 spirv::LiteralInteger member,
1891 spirv::LiteralString name)
1892 {
1893 switch (id)
1894 {
1895 case sh::vk::spirv::kIdXfbEmulationBufferBlockZero:
1896 case sh::vk::spirv::kIdXfbEmulationBufferBlockOne:
1897 case sh::vk::spirv::kIdXfbEmulationBufferBlockTwo:
1898 case sh::vk::spirv::kIdXfbEmulationBufferBlockThree:
1899 return TransformationState::Transformed;
1900 default:
1901 return TransformationState::Unchanged;
1902 }
1903 }
1904
transformTypeHelper(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdResult id)1905 TransformationState SpirvTransformFeedbackCodeGenerator::transformTypeHelper(
1906 const ShaderInterfaceVariableInfo *info,
1907 gl::ShaderType shaderType,
1908 spirv::IdResult id)
1909 {
1910 switch (id)
1911 {
1912 case sh::vk::spirv::kIdXfbEmulationBufferBlockZero:
1913 case sh::vk::spirv::kIdXfbEmulationBufferBlockOne:
1914 case sh::vk::spirv::kIdXfbEmulationBufferBlockTwo:
1915 case sh::vk::spirv::kIdXfbEmulationBufferBlockThree:
1916 ASSERT(info);
1917 return info->activeStages[shaderType] ? TransformationState::Unchanged
1918 : TransformationState::Transformed;
1919 default:
1920 return TransformationState::Unchanged;
1921 }
1922 }
1923
transformDecorate(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & decorationValues,spirv::Blob * blobOut)1924 TransformationState SpirvTransformFeedbackCodeGenerator::transformDecorate(
1925 const ShaderInterfaceVariableInfo *info,
1926 gl::ShaderType shaderType,
1927 spirv::IdRef id,
1928 spv::Decoration decoration,
1929 const spirv::LiteralIntegerList &decorationValues,
1930 spirv::Blob *blobOut)
1931 {
1932 return transformTypeHelper(info, shaderType, id);
1933 }
1934
transformMemberDecorate(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdRef id,spirv::LiteralInteger member,spv::Decoration decoration,spirv::Blob * blobOut)1935 TransformationState SpirvTransformFeedbackCodeGenerator::transformMemberDecorate(
1936 const ShaderInterfaceVariableInfo *info,
1937 gl::ShaderType shaderType,
1938 spirv::IdRef id,
1939 spirv::LiteralInteger member,
1940 spv::Decoration decoration,
1941 spirv::Blob *blobOut)
1942 {
1943 return transformTypeHelper(info, shaderType, id);
1944 }
1945
transformTypeStruct(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdResult id,const spirv::IdRefList & memberList,spirv::Blob * blobOut)1946 TransformationState SpirvTransformFeedbackCodeGenerator::transformTypeStruct(
1947 const ShaderInterfaceVariableInfo *info,
1948 gl::ShaderType shaderType,
1949 spirv::IdResult id,
1950 const spirv::IdRefList &memberList,
1951 spirv::Blob *blobOut)
1952 {
1953 return transformTypeHelper(info, shaderType, id);
1954 }
1955
transformTypePointer(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdResult id,spv::StorageClass storageClass,spirv::IdRef typeId,spirv::Blob * blobOut)1956 TransformationState SpirvTransformFeedbackCodeGenerator::transformTypePointer(
1957 const ShaderInterfaceVariableInfo *info,
1958 gl::ShaderType shaderType,
1959 spirv::IdResult id,
1960 spv::StorageClass storageClass,
1961 spirv::IdRef typeId,
1962 spirv::Blob *blobOut)
1963 {
1964 return transformTypeHelper(info, shaderType, typeId);
1965 }
1966
transformVariable(const ShaderInterfaceVariableInfo & info,const ShaderInterfaceVariableInfoMap & variableInfoMap,gl::ShaderType shaderType,spv::StorageClass storageBufferStorageClass,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass)1967 TransformationState SpirvTransformFeedbackCodeGenerator::transformVariable(
1968 const ShaderInterfaceVariableInfo &info,
1969 const ShaderInterfaceVariableInfoMap &variableInfoMap,
1970 gl::ShaderType shaderType,
1971 spv::StorageClass storageBufferStorageClass,
1972 spirv::IdResultType typeId,
1973 spirv::IdResult id,
1974 spv::StorageClass storageClass)
1975 {
1976 // This function is currently called for inactive variables.
1977 ASSERT(!info.activeStages[shaderType]);
1978
1979 if (shaderType == gl::ShaderType::Vertex && storageClass == storageBufferStorageClass)
1980 {
1981 // The ANGLEXfbN variables are unconditionally generated and may be inactive. Remove these
1982 // variables in that case.
1983 ASSERT(&info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(0)) ||
1984 &info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(1)) ||
1985 &info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(2)) ||
1986 &info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(3)));
1987
1988 // Drop the declaration.
1989 return TransformationState::Transformed;
1990 }
1991
1992 return TransformationState::Unchanged;
1993 }
1994
gatherXfbVaryings(const XFBInterfaceVariableInfo & info,spirv::IdRef id)1995 void SpirvTransformFeedbackCodeGenerator::gatherXfbVaryings(const XFBInterfaceVariableInfo &info,
1996 spirv::IdRef id)
1997 {
1998 visitXfbVarying(info.xfb, id, ShaderInterfaceVariableXfbInfo::kInvalid);
1999
2000 for (size_t fieldIndex = 0; fieldIndex < info.fieldXfb.size(); ++fieldIndex)
2001 {
2002 visitXfbVarying(info.fieldXfb[fieldIndex], id, static_cast<uint32_t>(fieldIndex));
2003 }
2004 }
2005
visitXfbVarying(const ShaderInterfaceVariableXfbInfo & xfb,spirv::IdRef baseId,uint32_t fieldIndex)2006 void SpirvTransformFeedbackCodeGenerator::visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
2007 spirv::IdRef baseId,
2008 uint32_t fieldIndex)
2009 {
2010 for (const ShaderInterfaceVariableXfbInfo &arrayElement : xfb.arrayElements)
2011 {
2012 visitXfbVarying(arrayElement, baseId, fieldIndex);
2013 }
2014
2015 if (xfb.pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
2016 {
2017 return;
2018 }
2019
2020 // Varyings captured to the same buffer have the same stride.
2021 ASSERT(mXfbVaryings[xfb.pod.buffer].empty() ||
2022 mXfbVaryings[xfb.pod.buffer][0].info->pod.stride == xfb.pod.stride);
2023
2024 mXfbVaryings[xfb.pod.buffer].push_back({&xfb, baseId, fieldIndex});
2025 }
2026
writeIntConstant(uint32_t value,spirv::IdRef intId,spirv::Blob * blobOut)2027 void SpirvTransformFeedbackCodeGenerator::writeIntConstant(uint32_t value,
2028 spirv::IdRef intId,
2029 spirv::Blob *blobOut)
2030 {
2031 if (value == ShaderInterfaceVariableXfbInfo::kInvalid)
2032 {
2033 return;
2034 }
2035
2036 if (mIntNIds.size() <= value)
2037 {
2038 // This member never resized down, so new elements can't have previous values.
2039 mIntNIds.resize_maybe_value_reuse(value + 1);
2040 }
2041 else if (mIntNIds[value].valid())
2042 {
2043 return;
2044 }
2045
2046 mIntNIds[value] = SpirvTransformerBase::GetNewId(blobOut);
2047 spirv::WriteConstant(blobOut, ID::Int, mIntNIds[value],
2048 spirv::LiteralContextDependentNumber(value));
2049 }
2050
modifyEntryPointInterfaceList(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,gl::ShaderType shaderType,EntryPointList entryPointList,spirv::IdRefList * interfaceList)2051 void SpirvTransformFeedbackCodeGenerator::modifyEntryPointInterfaceList(
2052 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2053 gl::ShaderType shaderType,
2054 EntryPointList entryPointList,
2055 spirv::IdRefList *interfaceList)
2056 {
2057 if (entryPointList == EntryPointList::GlobalVariables)
2058 {
2059 // Filter out unused xfb blocks from entry point interface declaration.
2060 size_t writeIndex = 0;
2061 for (size_t index = 0; index < interfaceList->size(); ++index)
2062 {
2063 spirv::IdRef id((*interfaceList)[index]);
2064 if (SpvIsXfbBufferBlockId(id))
2065 {
2066 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
2067 ASSERT(info);
2068
2069 if (!info->activeStages[shaderType])
2070 {
2071 continue;
2072 }
2073 }
2074
2075 (*interfaceList)[writeIndex] = id;
2076 ++writeIndex;
2077 }
2078
2079 // Update the number of interface variables.
2080 interfaceList->resize_down(writeIndex);
2081 }
2082 }
2083
writePendingDeclarations(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,spv::StorageClass storageBufferStorageClass,spirv::Blob * blobOut)2084 void SpirvTransformFeedbackCodeGenerator::writePendingDeclarations(
2085 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2086 spv::StorageClass storageBufferStorageClass,
2087 spirv::Blob *blobOut)
2088 {
2089 if (!mIsEmulated)
2090 {
2091 return;
2092 }
2093
2094 mFloatOutputPointerId = SpirvTransformerBase::GetNewId(blobOut);
2095 mFloatPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
2096 spirv::WriteTypePointer(blobOut, mFloatOutputPointerId, spv::StorageClassOutput, ID::Float);
2097 spirv::WriteTypePointer(blobOut, mFloatPrivatePointerId, spv::StorageClassPrivate, ID::Float);
2098
2099 mIntOutputPointerId = SpirvTransformerBase::GetNewId(blobOut);
2100 mIntPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
2101 spirv::WriteTypePointer(blobOut, mIntOutputPointerId, spv::StorageClassOutput, ID::Int);
2102 spirv::WriteTypePointer(blobOut, mIntPrivatePointerId, spv::StorageClassPrivate, ID::Int);
2103
2104 mUintOutputPointerId = SpirvTransformerBase::GetNewId(blobOut);
2105 mUintPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
2106 spirv::WriteTypePointer(blobOut, mUintOutputPointerId, spv::StorageClassOutput, ID::Uint);
2107 spirv::WriteTypePointer(blobOut, mUintPrivatePointerId, spv::StorageClassPrivate, ID::Uint);
2108
2109 mFloatUniformPointerId = SpirvTransformerBase::GetNewId(blobOut);
2110 spirv::WriteTypePointer(blobOut, mFloatUniformPointerId, storageBufferStorageClass, ID::Float);
2111
2112 ASSERT(mIntNIds.empty());
2113 // All new elements initialized later after the resize. Additionally mIntNIds was always empty
2114 // before this resize, so previous value reuse is not possible.
2115 mIntNIds.resize_maybe_value_reuse(4);
2116 mIntNIds[0] = ID::IntZero;
2117 mIntNIds[1] = ID::IntOne;
2118 mIntNIds[2] = ID::IntTwo;
2119 mIntNIds[3] = ID::IntThree;
2120
2121 spirv::IdRefList compositeIds;
2122 for (const std::vector<XfbVarying> &varyings : mXfbVaryings)
2123 {
2124 if (varyings.empty())
2125 {
2126 compositeIds.push_back(ID::IntZero);
2127 continue;
2128 }
2129
2130 const ShaderInterfaceVariableXfbInfo *info0 = varyings[0].info;
2131
2132 // Define the buffer stride constant
2133 ASSERT(info0->pod.stride % sizeof(float) == 0);
2134 uint32_t stride = info0->pod.stride / sizeof(float);
2135
2136 mBufferStrides[info0->pod.buffer] = SpirvTransformerBase::GetNewId(blobOut);
2137 spirv::WriteConstant(blobOut, ID::Int, mBufferStrides[info0->pod.buffer],
2138 spirv::LiteralContextDependentNumber(stride));
2139
2140 compositeIds.push_back(mBufferStrides[info0->pod.buffer]);
2141
2142 // Define all the constants that would be necessary to load the components of the varying.
2143 for (const XfbVarying &varying : varyings)
2144 {
2145 writeIntConstant(varying.fieldIndex, ID::Int, blobOut);
2146 const ShaderInterfaceVariableXfbInfo *info = varying.info;
2147 if (info->pod.arraySize == ShaderInterfaceVariableXfbInfo::kInvalid)
2148 {
2149 continue;
2150 }
2151
2152 uint32_t arrayIndexStart =
2153 varying.info->pod.arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid
2154 ? varying.info->pod.arrayIndex
2155 : 0;
2156 uint32_t arrayIndexEnd = arrayIndexStart + info->pod.arraySize;
2157
2158 for (uint32_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
2159 {
2160 writeIntConstant(arrayIndex, ID::Int, blobOut);
2161 }
2162 }
2163 }
2164
2165 mBufferStridesCompositeId = SpirvTransformerBase::GetNewId(blobOut);
2166 spirv::WriteConstantComposite(blobOut, ID::IVec4, mBufferStridesCompositeId, compositeIds);
2167 }
2168
writeTransformFeedbackExtensionOutput(spirv::IdRef positionId,spirv::Blob * blobOut)2169 void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackExtensionOutput(
2170 spirv::IdRef positionId,
2171 spirv::Blob *blobOut)
2172 {
2173 if (mIsEmulated)
2174 {
2175 return;
2176 }
2177
2178 if (mIsPositionCapturedByTransformFeedbackExtension)
2179 {
2180 spirv::WriteStore(blobOut, ID::XfbExtensionPosition, positionId, nullptr);
2181 }
2182 }
2183
2184 class AccessChainIndexListAppend final : angle::NonCopyable
2185 {
2186 public:
AccessChainIndexListAppend(bool condition,angle::FastVector<spirv::IdRef,4> intNIds,uint32_t index,spirv::IdRefList * indexList)2187 AccessChainIndexListAppend(bool condition,
2188 angle::FastVector<spirv::IdRef, 4> intNIds,
2189 uint32_t index,
2190 spirv::IdRefList *indexList)
2191 : mCondition(condition), mIndexList(indexList)
2192 {
2193 if (mCondition)
2194 {
2195 mIndexList->push_back(intNIds[index]);
2196 }
2197 }
~AccessChainIndexListAppend()2198 ~AccessChainIndexListAppend()
2199 {
2200 if (mCondition)
2201 {
2202 mIndexList->pop_back();
2203 }
2204 }
2205
2206 private:
2207 bool mCondition;
2208 spirv::IdRefList *mIndexList;
2209 };
2210
writeTransformFeedbackEmulationOutput(const SpirvInactiveVaryingRemover & inactiveVaryingRemover,const SpirvVaryingPrecisionFixer & varyingPrecisionFixer,const bool usePrecisionFixer,spirv::Blob * blobOut)2211 void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackEmulationOutput(
2212 const SpirvInactiveVaryingRemover &inactiveVaryingRemover,
2213 const SpirvVaryingPrecisionFixer &varyingPrecisionFixer,
2214 const bool usePrecisionFixer,
2215 spirv::Blob *blobOut)
2216 {
2217 if (!mIsEmulated)
2218 {
2219 return;
2220 }
2221
2222 // First, sort the varyings by offset, to simplify calculation of the output offset.
2223 for (std::vector<XfbVarying> &varyings : mXfbVaryings)
2224 {
2225 std::sort(varyings.begin(), varyings.end(),
2226 [](const XfbVarying &first, const XfbVarying &second) {
2227 return first.info->pod.offset < second.info->pod.offset;
2228 });
2229 }
2230
2231 // The following code is generated for transform feedback emulation:
2232 //
2233 // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
2234 // // For buffer N:
2235 // int xfbOffset = xfbOffsets[N]
2236 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
2237 // xfbOffset += 1;
2238 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col + 1]
2239 // xfbOffset += 1;
2240 // ...
2241 //
2242 // The following pieces of SPIR-V code are generated according to the above:
2243 //
2244 // - For the initial offsets calculation:
2245 //
2246 // %xfbOffsetsResult = OpFunctionCall %ivec4 %ANGLEGetXfbOffsets %stridesComposite
2247 // %xfbOffsetsVar = OpVariable %kIdIVec4FunctionTypePointer Function
2248 // OpStore %xfbOffsetsVar %xfbOffsetsResult
2249 // %xfbOffsets = OpLoad %ivec4 %xfbOffsetsVar
2250 //
2251 // - Initial code for each buffer N:
2252 //
2253 // %xfbOffset = OpCompositeExtract %int %xfbOffsets N
2254 //
2255 // - For each varying being captured:
2256 //
2257 // // Load the component
2258 // %componentPtr = OpAccessChain %floatOutputPtr %baseId %field %arrayIndex %row %col
2259 // %component = OpLoad %float %componentPtr
2260 // // Store in xfb output
2261 // %xfbOutPtr = OpAccessChain %floatUniformPtr %xfbBufferN %int0 %xfbOffset
2262 // OpStore %xfbOutPtr %component
2263 // // Increment offset
2264 // %xfbOffset = OpIAdd %int %xfbOffset %int1
2265 //
2266 // Note that if the varying being captured is integer, the first two instructions above would
2267 // use the intger equivalent types, and the following instruction would bitcast it to float
2268 // for storage:
2269 //
2270 // %asFloat = OpBitcast %float %component
2271 //
2272
2273 spirv::IdRef xfbOffsets;
2274
2275 if (!mXfbVaryings.empty())
2276 {
2277 xfbOffsets = SpirvTransformerBase::GetNewId(blobOut);
2278
2279 // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
2280 writeGetOffsetsCall(xfbOffsets, blobOut);
2281 }
2282
2283 // Go over the buffers one by one and capture the varyings.
2284 for (uint32_t bufferIndex = 0; bufferIndex < mXfbVaryings.size(); ++bufferIndex)
2285 {
2286 spirv::IdRef xfbOffset(SpirvTransformerBase::GetNewId(blobOut));
2287
2288 // Get the offset corresponding to this buffer:
2289 //
2290 // int xfbOffset = xfbOffsets[N]
2291 spirv::WriteCompositeExtract(blobOut, ID::Int, xfbOffset, xfbOffsets,
2292 {spirv::LiteralInteger(bufferIndex)});
2293
2294 // Track offsets for verification.
2295 uint32_t offsetForVerification = 0;
2296
2297 // Go over the varyings of this buffer in order.
2298 const std::vector<XfbVarying> &varyings = mXfbVaryings[bufferIndex];
2299 for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
2300 {
2301 const XfbVarying &varying = varyings[varyingIndex];
2302 const ShaderInterfaceVariableXfbInfo *info = varying.info;
2303 ASSERT(info->pod.buffer == bufferIndex);
2304
2305 // Each component of the varying being captured is loaded one by one. This uses the
2306 // OpAccessChain instruction that takes a chain of "indices" to end up with the
2307 // component starting from the base variable. For example:
2308 //
2309 // var.member[3][2][0]
2310 //
2311 // where member is field number 4 in var and is a mat4, the access chain would be:
2312 //
2313 // 4 3 2 0
2314 // ^ ^ ^ ^
2315 // | | | |
2316 // | | | row 0
2317 // | | column 2
2318 // | array element 3
2319 // field 4
2320 //
2321 // The following tracks the access chain as the field, array elements, columns and rows
2322 // are looped over.
2323 spirv::IdRefList indexList;
2324 AccessChainIndexListAppend appendField(
2325 varying.fieldIndex != ShaderInterfaceVariableXfbInfo::kInvalid, mIntNIds,
2326 varying.fieldIndex, &indexList);
2327
2328 // The varying being captured is either:
2329 //
2330 // - Not an array: In this case, no entry is added in the access chain
2331 // - An element of the array
2332 // - The whole array
2333 //
2334 uint32_t arrayIndexStart = 0;
2335 uint32_t arrayIndexEnd = info->pod.arraySize;
2336 const bool isArray = info->pod.arraySize != ShaderInterfaceVariableXfbInfo::kInvalid;
2337 if (varying.info->pod.arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid)
2338 {
2339 // Capturing a single element.
2340 arrayIndexStart = varying.info->pod.arrayIndex;
2341 arrayIndexEnd = arrayIndexStart + 1;
2342 }
2343 else if (!isArray)
2344 {
2345 // Not an array.
2346 arrayIndexEnd = 1;
2347 }
2348
2349 // Sorting the varyings should have ensured that offsets are in order and that writing
2350 // to the output buffer sequentially ends up using the correct offsets.
2351 ASSERT(info->pod.offset == offsetForVerification);
2352 offsetForVerification += (arrayIndexEnd - arrayIndexStart) * info->pod.rowCount *
2353 info->pod.columnCount * sizeof(float);
2354
2355 // Determine the type of the component being captured. OpBitcast is used (the
2356 // implementation of intBitsToFloat() and uintBitsToFloat() for non-float types).
2357 spirv::IdRef varyingTypeId;
2358 spirv::IdRef varyingTypePtr;
2359 const bool isPrivate =
2360 inactiveVaryingRemover.isInactive(varying.baseId) ||
2361 (usePrecisionFixer && varyingPrecisionFixer.isReplaced(varying.baseId));
2362 getVaryingTypeIds(info->pod.componentType, isPrivate, &varyingTypeId, &varyingTypePtr);
2363
2364 for (uint32_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
2365 {
2366 AccessChainIndexListAppend appendArrayIndex(isArray, mIntNIds, arrayIndex,
2367 &indexList);
2368 for (uint32_t col = 0; col < info->pod.columnCount; ++col)
2369 {
2370 AccessChainIndexListAppend appendColumn(info->pod.columnCount > 1, mIntNIds,
2371 col, &indexList);
2372 for (uint32_t row = 0; row < info->pod.rowCount; ++row)
2373 {
2374 AccessChainIndexListAppend appendRow(info->pod.rowCount > 1, mIntNIds, row,
2375 &indexList);
2376
2377 // Generate the code to capture a single component of the varying:
2378 //
2379 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
2380 writeComponentCapture(bufferIndex, xfbOffset, varyingTypeId, varyingTypePtr,
2381 varying.baseId, indexList, info->pod.componentType,
2382 blobOut);
2383
2384 // Increment the offset:
2385 //
2386 // xfbOffset += 1;
2387 //
2388 // which translates to:
2389 //
2390 // %newOffsetId = OpIAdd %int %currentOffsetId %int1
2391 spirv::IdRef nextOffset(SpirvTransformerBase::GetNewId(blobOut));
2392 spirv::WriteIAdd(blobOut, ID::Int, nextOffset, xfbOffset, ID::IntOne);
2393 xfbOffset = nextOffset;
2394 }
2395 }
2396 }
2397 }
2398 }
2399 }
2400
getVaryingTypeIds(GLenum componentType,bool isPrivate,spirv::IdRef * typeIdOut,spirv::IdRef * typePtrOut)2401 void SpirvTransformFeedbackCodeGenerator::getVaryingTypeIds(GLenum componentType,
2402 bool isPrivate,
2403 spirv::IdRef *typeIdOut,
2404 spirv::IdRef *typePtrOut)
2405 {
2406 switch (componentType)
2407 {
2408 case GL_INT:
2409 *typeIdOut = ID::Int;
2410 *typePtrOut = isPrivate ? mIntPrivatePointerId : mIntOutputPointerId;
2411 break;
2412 case GL_UNSIGNED_INT:
2413 *typeIdOut = ID::Uint;
2414 *typePtrOut = isPrivate ? mUintPrivatePointerId : mUintOutputPointerId;
2415 break;
2416 case GL_FLOAT:
2417 *typeIdOut = ID::Float;
2418 *typePtrOut = isPrivate ? mFloatPrivatePointerId : mFloatOutputPointerId;
2419 break;
2420 default:
2421 UNREACHABLE();
2422 }
2423
2424 ASSERT(typeIdOut->valid());
2425 ASSERT(typePtrOut->valid());
2426 }
2427
writeGetOffsetsCall(spirv::IdRef xfbOffsets,spirv::Blob * blobOut)2428 void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOffsets,
2429 spirv::Blob *blobOut)
2430 {
2431 const spirv::IdRef xfbOffsetsResult(SpirvTransformerBase::GetNewId(blobOut));
2432 const spirv::IdRef xfbOffsetsVar(SpirvTransformerBase::GetNewId(blobOut));
2433
2434 // Generate code for the following:
2435 //
2436 // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
2437
2438 // Create a variable to hold the result.
2439 spirv::WriteVariable(blobOut, ID::IVec4FunctionTypePointer, xfbOffsetsVar,
2440 spv::StorageClassFunction, nullptr);
2441 // Call a helper function generated by the translator to calculate the offsets for the current
2442 // vertex.
2443 spirv::WriteFunctionCall(blobOut, ID::IVec4, xfbOffsetsResult,
2444 ID::XfbEmulationGetOffsetsFunction, {mBufferStridesCompositeId});
2445 // Store the results.
2446 spirv::WriteStore(blobOut, xfbOffsetsVar, xfbOffsetsResult, nullptr);
2447 // Load from the variable for use in expressions.
2448 spirv::WriteLoad(blobOut, ID::IVec4, xfbOffsets, xfbOffsetsVar, nullptr);
2449 }
2450
writeComponentCapture(uint32_t bufferIndex,spirv::IdRef xfbOffset,spirv::IdRef varyingTypeId,spirv::IdRef varyingTypePtr,spirv::IdRef varyingBaseId,const spirv::IdRefList & accessChainIndices,GLenum componentType,spirv::Blob * blobOut)2451 void SpirvTransformFeedbackCodeGenerator::writeComponentCapture(
2452 uint32_t bufferIndex,
2453 spirv::IdRef xfbOffset,
2454 spirv::IdRef varyingTypeId,
2455 spirv::IdRef varyingTypePtr,
2456 spirv::IdRef varyingBaseId,
2457 const spirv::IdRefList &accessChainIndices,
2458 GLenum componentType,
2459 spirv::Blob *blobOut)
2460 {
2461 spirv::IdRef component(SpirvTransformerBase::GetNewId(blobOut));
2462 spirv::IdRef xfbOutPtr(SpirvTransformerBase::GetNewId(blobOut));
2463
2464 // Generate code for the following:
2465 //
2466 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
2467
2468 // Load from the component traversing the base variable with the given indices. If there are no
2469 // indices, the variable can be loaded directly.
2470 spirv::IdRef loadPtr = varyingBaseId;
2471 if (!accessChainIndices.empty())
2472 {
2473 loadPtr = SpirvTransformerBase::GetNewId(blobOut);
2474 spirv::WriteAccessChain(blobOut, varyingTypePtr, loadPtr, varyingBaseId,
2475 accessChainIndices);
2476 }
2477 spirv::WriteLoad(blobOut, varyingTypeId, component, loadPtr, nullptr);
2478
2479 // If the varying is int or uint, bitcast it to float to store in the float[] array used to
2480 // capture transform feedback output.
2481 spirv::IdRef asFloat = component;
2482 if (componentType != GL_FLOAT)
2483 {
2484 asFloat = SpirvTransformerBase::GetNewId(blobOut);
2485 spirv::WriteBitcast(blobOut, ID::Float, asFloat, component);
2486 }
2487
2488 // Store into the transform feedback capture buffer at the current offset. Note that this
2489 // buffer has only one field (xfbOut), hence ANGLEXfbN.xfbOut[xfbOffset] translates to ANGLEXfbN
2490 // with access chain {0, xfbOffset}.
2491 static_assert(gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS == 4);
2492 static_assert(sh::vk::spirv::kIdXfbEmulationBufferVarOne ==
2493 sh::vk::spirv::kIdXfbEmulationBufferVarZero + 1);
2494 static_assert(sh::vk::spirv::kIdXfbEmulationBufferVarTwo ==
2495 sh::vk::spirv::kIdXfbEmulationBufferVarZero + 2);
2496 static_assert(sh::vk::spirv::kIdXfbEmulationBufferVarThree ==
2497 sh::vk::spirv::kIdXfbEmulationBufferVarZero + 3);
2498 spirv::WriteAccessChain(blobOut, mFloatUniformPointerId, xfbOutPtr,
2499 spirv::IdRef(sh::vk::spirv::kIdXfbEmulationBufferVarZero + bufferIndex),
2500 {ID::IntZero, xfbOffset});
2501 spirv::WriteStore(blobOut, xfbOutPtr, asFloat, nullptr);
2502 }
2503
addExecutionMode(spirv::IdRef entryPointId,spirv::Blob * blobOut)2504 void SpirvTransformFeedbackCodeGenerator::addExecutionMode(spirv::IdRef entryPointId,
2505 spirv::Blob *blobOut)
2506 {
2507 if (mIsEmulated)
2508 {
2509 return;
2510 }
2511
2512 if (mHasTransformFeedbackOutput)
2513 {
2514 spirv::WriteExecutionMode(blobOut, entryPointId, spv::ExecutionModeXfb, {});
2515 }
2516 }
2517
addMemberDecorate(const XFBInterfaceVariableInfo & info,spirv::IdRef id,spirv::Blob * blobOut)2518 void SpirvTransformFeedbackCodeGenerator::addMemberDecorate(const XFBInterfaceVariableInfo &info,
2519 spirv::IdRef id,
2520 spirv::Blob *blobOut)
2521 {
2522 if (mIsEmulated || info.fieldXfb.empty())
2523 {
2524 return;
2525 }
2526
2527 for (uint32_t fieldIndex = 0; fieldIndex < info.fieldXfb.size(); ++fieldIndex)
2528 {
2529 const ShaderInterfaceVariableXfbInfo &xfb = info.fieldXfb[fieldIndex];
2530
2531 if (xfb.pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
2532 {
2533 continue;
2534 }
2535
2536 ASSERT(xfb.pod.stride != ShaderInterfaceVariableXfbInfo::kInvalid);
2537 ASSERT(xfb.pod.offset != ShaderInterfaceVariableXfbInfo::kInvalid);
2538
2539 const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
2540 xfb.pod.buffer,
2541 xfb.pod.stride,
2542 xfb.pod.offset,
2543 };
2544
2545 // Generate the following three instructions:
2546 //
2547 // OpMemberDecorate %id fieldIndex XfbBuffer xfb.buffer
2548 // OpMemberDecorate %id fieldIndex XfbStride xfb.stride
2549 // OpMemberDecorate %id fieldIndex Offset xfb.offset
2550 for (size_t i = 0; i < kXfbDecorationCount; ++i)
2551 {
2552 spirv::WriteMemberDecorate(blobOut, id, spirv::LiteralInteger(fieldIndex),
2553 kXfbDecorations[i],
2554 {spirv::LiteralInteger(xfbDecorationValues[i])});
2555 }
2556 }
2557 }
2558
addDecorate(const XFBInterfaceVariableInfo & info,spirv::IdRef id,spirv::Blob * blobOut)2559 void SpirvTransformFeedbackCodeGenerator::addDecorate(const XFBInterfaceVariableInfo &info,
2560 spirv::IdRef id,
2561 spirv::Blob *blobOut)
2562 {
2563 if (mIsEmulated || info.xfb.pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
2564 {
2565 return;
2566 }
2567
2568 ASSERT(info.xfb.pod.stride != ShaderInterfaceVariableXfbInfo::kInvalid);
2569 ASSERT(info.xfb.pod.offset != ShaderInterfaceVariableXfbInfo::kInvalid);
2570
2571 const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
2572 info.xfb.pod.buffer,
2573 info.xfb.pod.stride,
2574 info.xfb.pod.offset,
2575 };
2576
2577 // Generate the following three instructions:
2578 //
2579 // OpDecorate %id XfbBuffer xfb.buffer
2580 // OpDecorate %id XfbStride xfb.stride
2581 // OpDecorate %id Offset xfb.offset
2582 for (size_t i = 0; i < kXfbDecorationCount; ++i)
2583 {
2584 spirv::WriteDecorate(blobOut, id, kXfbDecorations[i],
2585 {spirv::LiteralInteger(xfbDecorationValues[i])});
2586 }
2587 }
2588
2589 // Helper class that generates code for gl_Position transformations
2590 class SpirvPositionTransformer final : angle::NonCopyable
2591 {
2592 public:
SpirvPositionTransformer(const SpvTransformOptions & options)2593 SpirvPositionTransformer(const SpvTransformOptions &options) : mOptions(options) {}
2594
2595 void writePositionTransformation(spirv::IdRef positionPointerId,
2596 spirv::IdRef positionId,
2597 spirv::Blob *blobOut);
2598
2599 private:
2600 SpvTransformOptions mOptions;
2601 };
2602
writePositionTransformation(spirv::IdRef positionPointerId,spirv::IdRef positionId,spirv::Blob * blobOut)2603 void SpirvPositionTransformer::writePositionTransformation(spirv::IdRef positionPointerId,
2604 spirv::IdRef positionId,
2605 spirv::Blob *blobOut)
2606 {
2607 // Generate the following SPIR-V for prerotation and depth transformation:
2608 //
2609 // // Transform position based on uniforms by making a call to the ANGLETransformPosition
2610 // // function that the translator has already provided.
2611 // %transformed = OpFunctionCall %kIdVec4 %kIdTransformPositionFunction %position
2612 //
2613 // // Store the results back in gl_Position
2614 // OpStore %PositionPointer %transformedPosition
2615 //
2616 const spirv::IdRef transformedPositionId(SpirvTransformerBase::GetNewId(blobOut));
2617
2618 spirv::WriteFunctionCall(blobOut, ID::Vec4, transformedPositionId,
2619 ID::TransformPositionFunction, {positionId});
2620 spirv::WriteStore(blobOut, positionPointerId, transformedPositionId, nullptr);
2621 }
2622
2623 // A transformation to handle both the isMultisampledFramebufferFetch and enableSampleShading
2624 // options. The common transformation between these two options is the addition of the
2625 // SampleRateShading capability.
2626 class SpirvMultisampleTransformer final : angle::NonCopyable
2627 {
2628 public:
SpirvMultisampleTransformer(const SpvTransformOptions & options)2629 SpirvMultisampleTransformer(const SpvTransformOptions &options)
2630 : mOptions(options), mSampleIDDecorationsAdded(false), mAnyImageTypesModified(false)
2631 {}
~SpirvMultisampleTransformer()2632 ~SpirvMultisampleTransformer()
2633 {
2634 ASSERT(!mOptions.isMultisampledFramebufferFetch || mAnyImageTypesModified);
2635 }
2636
2637 void init(size_t indexBound);
2638
2639 void visitDecorate(spirv::IdRef id,
2640 spv::Decoration decoration,
2641 const spirv::LiteralIntegerList &valueList);
2642
2643 void visitMemberDecorate(spirv::IdRef id,
2644 spirv::LiteralInteger member,
2645 spv::Decoration decoration);
2646
2647 void visitTypeStruct(spirv::IdResult id, const spirv::IdRefList &memberList);
2648
2649 void visitTypePointer(gl::ShaderType shaderType,
2650 spirv::IdResult id,
2651 spv::StorageClass storageClass,
2652 spirv::IdRef typeId);
2653
2654 void visitVariable(gl::ShaderType shaderType,
2655 spirv::IdResultType typeId,
2656 spirv::IdResult id,
2657 spv::StorageClass storageClass);
2658
2659 TransformationState transformCapability(const SpirvNonSemanticInstructions &nonSemantic,
2660 spv::Capability capability,
2661 spirv::Blob *blobOut);
2662
2663 TransformationState transformTypeImage(const uint32_t *instruction, spirv::Blob *blobOut);
2664
2665 void modifyEntryPointInterfaceList(const SpirvNonSemanticInstructions &nonSemantic,
2666 EntryPointList entryPointList,
2667 spirv::IdRefList *interfaceList,
2668 spirv::Blob *blobOut);
2669
2670 void writePendingDeclarations(
2671 const SpirvNonSemanticInstructions &nonSemantic,
2672 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2673 spirv::Blob *blobOut);
2674
2675 TransformationState transformDecorate(const SpirvNonSemanticInstructions &nonSemantic,
2676 const ShaderInterfaceVariableInfo &info,
2677 gl::ShaderType shaderType,
2678 spirv::IdRef id,
2679 spv::Decoration &decoration,
2680 spirv::Blob *blobOut);
2681
2682 TransformationState transformImageRead(const uint32_t *instruction, spirv::Blob *blobOut);
2683
2684 private:
2685 void visitVarying(gl::ShaderType shaderType, spirv::IdRef id, spv::StorageClass storageClass);
2686 bool skipSampleDecoration(spv::Decoration decoration);
2687
2688 SpvTransformOptions mOptions;
2689 // Used to assert that the transformation is not unnecessarily run.
2690 bool mSampleIDDecorationsAdded;
2691 bool mAnyImageTypesModified;
2692
2693 struct VaryingInfo
2694 {
2695 // Whether any variable is a varying
2696 bool isVarying = false;
2697 // Whether any variable or its members are already sample-, centroid- or flat-qualified.
2698 bool skipSampleDecoration = false;
2699 std::vector<bool> skipMemberSampleDecoration;
2700 };
2701 std::vector<VaryingInfo> mVaryingInfoById;
2702 };
2703
init(size_t indexBound)2704 void SpirvMultisampleTransformer::init(size_t indexBound)
2705 {
2706 mVaryingInfoById.resize(indexBound);
2707 }
2708
transformImageRead(const uint32_t * instruction,spirv::Blob * blobOut)2709 TransformationState SpirvMultisampleTransformer::transformImageRead(const uint32_t *instruction,
2710 spirv::Blob *blobOut)
2711 {
2712 // Transform the following:
2713 // %21 = OpImageRead %v4float %13 %20
2714 // to
2715 // %21 = OpImageRead %v4float %13 %20 Sample %17
2716 // where
2717 // %17 = OpLoad %int %gl_SampleID
2718
2719 if (!mOptions.isMultisampledFramebufferFetch)
2720 {
2721 return TransformationState::Unchanged;
2722 }
2723
2724 spirv::IdResultType idResultType;
2725 spirv::IdResult idResult;
2726 spirv::IdRef image;
2727 spirv::IdRef coordinate;
2728 spv::ImageOperandsMask imageOperands;
2729 spirv::IdRefList imageOperandIdsList;
2730
2731 spirv::ParseImageRead(instruction, &idResultType, &idResult, &image, &coordinate,
2732 &imageOperands, &imageOperandIdsList);
2733
2734 ASSERT(ID::Int.valid());
2735
2736 spirv::IdRef builtInSampleIDOpLoad = SpirvTransformerBase::GetNewId(blobOut);
2737
2738 spirv::WriteLoad(blobOut, ID::Int, builtInSampleIDOpLoad, ID::SampleID, nullptr);
2739
2740 imageOperands = spv::ImageOperandsMask::ImageOperandsSampleMask;
2741 imageOperandIdsList.push_back(builtInSampleIDOpLoad);
2742 spirv::WriteImageRead(blobOut, idResultType, idResult, image, coordinate, &imageOperands,
2743 imageOperandIdsList);
2744 return TransformationState::Transformed;
2745 }
2746
writePendingDeclarations(const SpirvNonSemanticInstructions & nonSemantic,const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,spirv::Blob * blobOut)2747 void SpirvMultisampleTransformer::writePendingDeclarations(
2748 const SpirvNonSemanticInstructions &nonSemantic,
2749 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2750 spirv::Blob *blobOut)
2751 {
2752 // Add following declarations if they are not available yet
2753
2754 // %int = OpTypeInt 32 1
2755 // %_ptr_Input_int = OpTypePointer Input %int
2756 // %gl_SampleID = OpVariable %_ptr_Input_int Input
2757
2758 if (!mOptions.isMultisampledFramebufferFetch)
2759 {
2760 return;
2761 }
2762
2763 if (nonSemantic.hasSampleID())
2764 {
2765 return;
2766 }
2767
2768 spirv::WriteVariable(blobOut, ID::IntInputTypePointer, ID::SampleID, spv::StorageClassInput,
2769 nullptr);
2770 }
2771
transformTypeImage(const uint32_t * instruction,spirv::Blob * blobOut)2772 TransformationState SpirvMultisampleTransformer::transformTypeImage(const uint32_t *instruction,
2773 spirv::Blob *blobOut)
2774 {
2775 // Transform the following
2776 // %10 = OpTypeImage %float SubpassData 0 0 0 2
2777 // To
2778 // %10 = OpTypeImage %float SubpassData 0 0 1 2
2779
2780 if (!mOptions.isMultisampledFramebufferFetch)
2781 {
2782 return TransformationState::Unchanged;
2783 }
2784
2785 spirv::IdResult idResult;
2786 spirv::IdRef sampledType;
2787 spv::Dim dim;
2788 spirv::LiteralInteger depth;
2789 spirv::LiteralInteger arrayed;
2790 spirv::LiteralInteger ms;
2791 spirv::LiteralInteger sampled;
2792 spv::ImageFormat imageFormat;
2793 spv::AccessQualifier accessQualifier;
2794 spirv::ParseTypeImage(instruction, &idResult, &sampledType, &dim, &depth, &arrayed, &ms,
2795 &sampled, &imageFormat, &accessQualifier);
2796
2797 // Only transform input attachment image types.
2798 if (dim != spv::DimSubpassData)
2799 {
2800 return TransformationState::Unchanged;
2801 }
2802
2803 ms = spirv::LiteralInteger(1);
2804 spirv::WriteTypeImage(blobOut, idResult, sampledType, dim, depth, arrayed, ms, sampled,
2805 imageFormat, nullptr);
2806
2807 mAnyImageTypesModified = true;
2808
2809 return TransformationState::Transformed;
2810 }
2811
2812 namespace
2813 {
verifyEntryPointsContainsID(const spirv::IdRefList & interfaceList)2814 bool verifyEntryPointsContainsID(const spirv::IdRefList &interfaceList)
2815 {
2816 for (spirv::IdRef interfaceId : interfaceList)
2817 {
2818 if (interfaceId == ID::SampleID)
2819 {
2820 return true;
2821 }
2822 }
2823 return false;
2824 }
2825 } // namespace
2826
modifyEntryPointInterfaceList(const SpirvNonSemanticInstructions & nonSemantic,EntryPointList entryPointList,spirv::IdRefList * interfaceList,spirv::Blob * blobOut)2827 void SpirvMultisampleTransformer::modifyEntryPointInterfaceList(
2828 const SpirvNonSemanticInstructions &nonSemantic,
2829 EntryPointList entryPointList,
2830 spirv::IdRefList *interfaceList,
2831 spirv::Blob *blobOut)
2832 {
2833 // Append %gl_sampleID to OpEntryPoint
2834 // Transform the following
2835 //
2836 // OpEntryPoint Fragment %main "main" %_uo_color
2837 //
2838 // To
2839 //
2840 // OpEntryPoint Fragment %main "main" %_uo_color %gl_SampleID
2841
2842 if (!mOptions.isMultisampledFramebufferFetch)
2843 {
2844 return;
2845 }
2846
2847 // Nothing to do if the shader had already declared SampleID
2848 if (nonSemantic.hasSampleID())
2849 {
2850 ASSERT(verifyEntryPointsContainsID(*interfaceList));
2851 return;
2852 }
2853
2854 // Add the SampleID id to the interfaceList. The variable will later be decalred in
2855 // writePendingDeclarations.
2856 interfaceList->push_back(ID::SampleID);
2857 return;
2858 }
2859
transformCapability(const SpirvNonSemanticInstructions & nonSemantic,const spv::Capability capability,spirv::Blob * blobOut)2860 TransformationState SpirvMultisampleTransformer::transformCapability(
2861 const SpirvNonSemanticInstructions &nonSemantic,
2862 const spv::Capability capability,
2863 spirv::Blob *blobOut)
2864 {
2865 // Add a new OpCapability line:
2866 //
2867 // OpCapability SampleRateShading
2868 //
2869 // right before the following instruction
2870 //
2871 // OpCapability InputAttachment
2872
2873 if (!mOptions.isMultisampledFramebufferFetch && !mOptions.enableSampleShading)
2874 {
2875 return TransformationState::Unchanged;
2876 }
2877
2878 // Do not add the capability if the SPIR-V already has it
2879 if (nonSemantic.hasSampleRateShading())
2880 {
2881 return TransformationState::Unchanged;
2882 }
2883
2884 // Make sure no duplicates
2885 ASSERT(capability != spv::CapabilitySampleRateShading);
2886
2887 // Make sure we only add the new line on top of "OpCapability Shader"
2888 if (capability != spv::CapabilityShader)
2889 {
2890 return TransformationState::Unchanged;
2891 }
2892
2893 spirv::WriteCapability(blobOut, spv::CapabilitySampleRateShading);
2894
2895 // Leave the original OpCapability untouched
2896 return TransformationState::Unchanged;
2897 }
2898
transformDecorate(const SpirvNonSemanticInstructions & nonSemantic,const ShaderInterfaceVariableInfo & info,gl::ShaderType shaderType,spirv::IdRef id,spv::Decoration & decoration,spirv::Blob * blobOut)2899 TransformationState SpirvMultisampleTransformer::transformDecorate(
2900 const SpirvNonSemanticInstructions &nonSemantic,
2901 const ShaderInterfaceVariableInfo &info,
2902 gl::ShaderType shaderType,
2903 spirv::IdRef id,
2904 spv::Decoration &decoration,
2905 spirv::Blob *blobOut)
2906 {
2907 if (mOptions.isMultisampledFramebufferFetch &&
2908 decoration == spv::DecorationInputAttachmentIndex && !nonSemantic.hasSampleID() &&
2909 !mSampleIDDecorationsAdded)
2910 {
2911 // Add the following instructions if they are not available yet:
2912 //
2913 // OpDecorate %gl_SampleID RelaxedPrecision
2914 // OpDecorate %gl_SampleID Flat
2915 // OpDecorate %gl_SampleID BuiltIn SampleId
2916
2917 spirv::WriteDecorate(blobOut, ID::SampleID, spv::DecorationRelaxedPrecision, {});
2918 spirv::WriteDecorate(blobOut, ID::SampleID, spv::DecorationFlat, {});
2919 spirv::WriteDecorate(blobOut, ID::SampleID, spv::DecorationBuiltIn,
2920 {spirv::LiteralInteger(spv::BuiltIn::BuiltInSampleId)});
2921
2922 mSampleIDDecorationsAdded = true;
2923 }
2924 if (mOptions.enableSampleShading && mVaryingInfoById[id].isVarying &&
2925 !mVaryingInfoById[id].skipSampleDecoration)
2926 {
2927 if (decoration == spv::DecorationLocation && info.activeStages[shaderType])
2928 {
2929 // Add the following instructions when the Location decoration is met, if the varying is
2930 // not already decorated with Sample:
2931 //
2932 // OpDecorate %id Sample
2933 spirv::WriteDecorate(blobOut, id, spv::DecorationSample, {});
2934 }
2935 else if (decoration == spv::DecorationBlock)
2936 {
2937 // Add the following instructions when the Block decoration is met, for any member that
2938 // is not already decorated with Sample:
2939 //
2940 // OpMemberDecorate %id member Sample
2941 for (uint32_t member = 0;
2942 member < mVaryingInfoById[id].skipMemberSampleDecoration.size(); ++member)
2943 {
2944 if (!mVaryingInfoById[id].skipMemberSampleDecoration[member])
2945 {
2946 spirv::WriteMemberDecorate(blobOut, id, spirv::LiteralInteger(member),
2947 spv::DecorationSample, {});
2948 }
2949 }
2950 }
2951 }
2952
2953 return TransformationState::Unchanged;
2954 }
2955
skipSampleDecoration(spv::Decoration decoration)2956 bool SpirvMultisampleTransformer::skipSampleDecoration(spv::Decoration decoration)
2957 {
2958 // If a variable is already decorated with Sample, Patch or Centroid, it shouldn't be decorated
2959 // with Sample. BuiltIns are also excluded.
2960 return decoration == spv::DecorationPatch || decoration == spv::DecorationCentroid ||
2961 decoration == spv::DecorationSample || decoration == spv::DecorationBuiltIn;
2962 }
2963
visitDecorate(spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & valueList)2964 void SpirvMultisampleTransformer::visitDecorate(spirv::IdRef id,
2965 spv::Decoration decoration,
2966 const spirv::LiteralIntegerList &valueList)
2967 {
2968 if (mOptions.enableSampleShading)
2969 {
2970 // Determine whether the id is already decorated with Sample.
2971 if (skipSampleDecoration(decoration))
2972 {
2973 mVaryingInfoById[id].skipSampleDecoration = true;
2974 }
2975 }
2976 return;
2977 }
2978
visitMemberDecorate(spirv::IdRef id,spirv::LiteralInteger member,spv::Decoration decoration)2979 void SpirvMultisampleTransformer::visitMemberDecorate(spirv::IdRef id,
2980 spirv::LiteralInteger member,
2981 spv::Decoration decoration)
2982 {
2983 if (!mOptions.enableSampleShading)
2984 {
2985 return;
2986 }
2987
2988 if (mVaryingInfoById[id].skipMemberSampleDecoration.size() <= member)
2989 {
2990 mVaryingInfoById[id].skipMemberSampleDecoration.resize(member + 1, false);
2991 }
2992 // Determine whether the member is already decorated with Sample.
2993 if (skipSampleDecoration(decoration))
2994 {
2995 mVaryingInfoById[id].skipMemberSampleDecoration[member] = true;
2996 }
2997 }
2998
visitVarying(gl::ShaderType shaderType,spirv::IdRef id,spv::StorageClass storageClass)2999 void SpirvMultisampleTransformer::visitVarying(gl::ShaderType shaderType,
3000 spirv::IdRef id,
3001 spv::StorageClass storageClass)
3002 {
3003 if (!mOptions.enableSampleShading)
3004 {
3005 return;
3006 }
3007
3008 // Vertex input and fragment output variables are not varyings and don't need to be decorated
3009 // with Sample.
3010 if ((shaderType == gl::ShaderType::Fragment && storageClass == spv::StorageClassOutput) ||
3011 (shaderType == gl::ShaderType::Vertex && storageClass == spv::StorageClassInput))
3012 {
3013 return;
3014 }
3015
3016 const bool isVarying =
3017 storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput;
3018 mVaryingInfoById[id].isVarying = isVarying;
3019 }
3020
visitTypeStruct(spirv::IdResult id,const spirv::IdRefList & memberList)3021 void SpirvMultisampleTransformer::visitTypeStruct(spirv::IdResult id,
3022 const spirv::IdRefList &memberList)
3023 {
3024 if (mOptions.enableSampleShading)
3025 {
3026 mVaryingInfoById[id].skipMemberSampleDecoration.resize(memberList.size(), false);
3027 }
3028 }
3029
visitTypePointer(gl::ShaderType shaderType,spirv::IdResult id,spv::StorageClass storageClass,spirv::IdRef typeId)3030 void SpirvMultisampleTransformer::visitTypePointer(gl::ShaderType shaderType,
3031 spirv::IdResult id,
3032 spv::StorageClass storageClass,
3033 spirv::IdRef typeId)
3034 {
3035 // For I/O blocks, the Sample decoration should be specified on the members of the struct type.
3036 // For that purpose, we consider the struct type as the varying instead.
3037 visitVarying(shaderType, typeId, storageClass);
3038 }
3039
visitVariable(gl::ShaderType shaderType,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass)3040 void SpirvMultisampleTransformer::visitVariable(gl::ShaderType shaderType,
3041 spirv::IdResultType typeId,
3042 spirv::IdResult id,
3043 spv::StorageClass storageClass)
3044 {
3045 visitVarying(shaderType, id, storageClass);
3046 }
3047
3048 // Helper class that flattens secondary fragment output arrays.
3049 class SpirvSecondaryOutputTransformer final : angle::NonCopyable
3050 {
3051 public:
SpirvSecondaryOutputTransformer()3052 SpirvSecondaryOutputTransformer() {}
3053
3054 void init(size_t indexBound);
3055
3056 void visitTypeArray(spirv::IdResult id, spirv::IdRef typeId);
3057
3058 void visitTypePointer(spirv::IdResult id, spirv::IdRef typeId);
3059
3060 TransformationState transformAccessChain(spirv::IdResultType typeId,
3061 spirv::IdResult id,
3062 spirv::IdRef baseId,
3063 const spirv::IdRefList &indexList,
3064 spirv::Blob *blobOut);
3065
3066 TransformationState transformDecorate(spirv::IdRef id,
3067 spv::Decoration decoration,
3068 const spirv::LiteralIntegerList &decorationValues,
3069 spirv::Blob *blobOut);
3070
3071 TransformationState transformVariable(spirv::IdResultType typeId,
3072 spirv::IdResultType privateTypeId,
3073 spirv::IdResult id,
3074 spirv::Blob *blobOut);
3075
3076 void modifyEntryPointInterfaceList(
3077 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3078 EntryPointList entryPointList,
3079 spirv::IdRefList *interfaceList,
3080 spirv::Blob *blobOut);
3081
3082 void writeOutputPrologue(
3083 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3084 spirv::Blob *blobOut);
3085
3086 static_assert(gl::IMPLEMENTATION_MAX_DUAL_SOURCE_DRAW_BUFFERS == 1,
3087 "This transformer is incompatible with two or more dual-source draw buffers");
3088
3089 private:
visitTypeHelper(spirv::IdResult id,spirv::IdRef typeId)3090 void visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId) { mTypeCache[id] = typeId; }
3091
3092 // This list is filled during visitTypePointer and visitTypeArray steps,
3093 // to resolve the element type ID of the original output array variable.
3094 std::vector<spirv::IdRef> mTypeCache;
3095 spirv::IdRef mElementTypeId;
3096 spirv::IdRef mArrayVariableId;
3097 spirv::IdRef mReplacementVariableId;
3098 spirv::IdRef mElementPointerTypeId;
3099 };
3100
init(size_t indexBound)3101 void SpirvSecondaryOutputTransformer::init(size_t indexBound)
3102 {
3103 mTypeCache.resize(indexBound);
3104 }
3105
visitTypeArray(spirv::IdResult id,spirv::IdRef typeId)3106 void SpirvSecondaryOutputTransformer::visitTypeArray(spirv::IdResult id, spirv::IdRef typeId)
3107 {
3108 visitTypeHelper(id, typeId);
3109 }
3110
visitTypePointer(spirv::IdResult id,spirv::IdRef typeId)3111 void SpirvSecondaryOutputTransformer::visitTypePointer(spirv::IdResult id, spirv::IdRef typeId)
3112 {
3113 visitTypeHelper(id, typeId);
3114 }
3115
modifyEntryPointInterfaceList(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,EntryPointList entryPointList,spirv::IdRefList * interfaceList,spirv::Blob * blobOut)3116 void SpirvSecondaryOutputTransformer::modifyEntryPointInterfaceList(
3117 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3118 EntryPointList entryPointList,
3119 spirv::IdRefList *interfaceList,
3120 spirv::Blob *blobOut)
3121 {
3122 // Flatten a secondary output array (if any).
3123 for (size_t index = 0; index < interfaceList->size(); ++index)
3124 {
3125 const spirv::IdRef id((*interfaceList)[index]);
3126 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
3127
3128 if (info == nullptr || info->index != 1 || !info->isArray)
3129 {
3130 continue;
3131 }
3132
3133 mArrayVariableId = id;
3134 mReplacementVariableId = SpirvTransformerBase::GetNewId(blobOut);
3135
3136 // With SPIR-V 1.3, modify interface list with the replacement ID.
3137 //
3138 // With SPIR-V 1.4, the original variable is changed to Private and should remain in the
3139 // list. The new variable should be added to the variable list.
3140 if (entryPointList == EntryPointList::InterfaceVariables)
3141 {
3142 (*interfaceList)[index] = mReplacementVariableId;
3143 }
3144 else
3145 {
3146 interfaceList->push_back(mReplacementVariableId);
3147 }
3148 break;
3149 }
3150 }
3151
transformAccessChain(spirv::IdResultType typeId,spirv::IdResult id,spirv::IdRef baseId,const spirv::IdRefList & indexList,spirv::Blob * blobOut)3152 TransformationState SpirvSecondaryOutputTransformer::transformAccessChain(
3153 spirv::IdResultType typeId,
3154 spirv::IdResult id,
3155 spirv::IdRef baseId,
3156 const spirv::IdRefList &indexList,
3157 spirv::Blob *blobOut)
3158 {
3159 if (baseId != mArrayVariableId)
3160 {
3161 return TransformationState::Unchanged;
3162 }
3163 ASSERT(typeId.valid());
3164 spirv::WriteAccessChain(blobOut, typeId, id, baseId, indexList);
3165 return TransformationState::Transformed;
3166 }
3167
transformDecorate(spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & decorationValues,spirv::Blob * blobOut)3168 TransformationState SpirvSecondaryOutputTransformer::transformDecorate(
3169 spirv::IdRef id,
3170 spv::Decoration decoration,
3171 const spirv::LiteralIntegerList &decorationValues,
3172 spirv::Blob *blobOut)
3173 {
3174 if (id != mArrayVariableId)
3175 {
3176 return TransformationState::Unchanged;
3177 }
3178 ASSERT(mReplacementVariableId.valid());
3179 if (decoration == spv::DecorationLocation)
3180 {
3181 // Drop the Location decoration from the original variable and add
3182 // it together with an Index decoration to the replacement variable.
3183 spirv::WriteDecorate(blobOut, mReplacementVariableId, spv::DecorationLocation,
3184 {spirv::LiteralInteger(0)});
3185 spirv::WriteDecorate(blobOut, mReplacementVariableId, spv::DecorationIndex,
3186 {spirv::LiteralInteger(1)});
3187 }
3188 else
3189 {
3190 // Apply other decorations, such as RelaxedPrecision, to both variables.
3191 spirv::WriteDecorate(blobOut, id, decoration, decorationValues);
3192 spirv::WriteDecorate(blobOut, mReplacementVariableId, decoration, decorationValues);
3193 }
3194 return TransformationState::Transformed;
3195 }
3196
transformVariable(spirv::IdResultType typeId,spirv::IdResultType privateTypeId,spirv::IdResult id,spirv::Blob * blobOut)3197 TransformationState SpirvSecondaryOutputTransformer::transformVariable(
3198 spirv::IdResultType typeId,
3199 spirv::IdResultType privateTypeId,
3200 spirv::IdResult id,
3201 spirv::Blob *blobOut)
3202 {
3203 if (id != mArrayVariableId)
3204 {
3205 return TransformationState::Unchanged;
3206 }
3207
3208 // Change the original variable to use private storage.
3209 ASSERT(privateTypeId.valid());
3210 spirv::WriteVariable(blobOut, privateTypeId, id, spv::StorageClassPrivate, nullptr);
3211
3212 ASSERT(!mElementTypeId.valid());
3213 mElementTypeId = mTypeCache[mTypeCache[typeId]];
3214 ASSERT(mElementTypeId.valid());
3215
3216 // Pointer type for accessing the array element value.
3217 mElementPointerTypeId = SpirvTransformerBase::GetNewId(blobOut);
3218 spirv::WriteTypePointer(blobOut, mElementPointerTypeId, spv::StorageClassPrivate,
3219 mElementTypeId);
3220
3221 // Pointer type for the replacement output variable.
3222 const spirv::IdRef outputPointerTypeId(SpirvTransformerBase::GetNewId(blobOut));
3223 spirv::WriteTypePointer(blobOut, outputPointerTypeId, spv::StorageClassOutput, mElementTypeId);
3224
3225 ASSERT(mReplacementVariableId.valid());
3226 spirv::WriteVariable(blobOut, outputPointerTypeId, mReplacementVariableId,
3227 spv::StorageClassOutput, nullptr);
3228
3229 return TransformationState::Transformed;
3230 }
3231
writeOutputPrologue(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,spirv::Blob * blobOut)3232 void SpirvSecondaryOutputTransformer::writeOutputPrologue(
3233 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3234 spirv::Blob *blobOut)
3235 {
3236 if (mArrayVariableId.valid())
3237 {
3238 const spirv::IdRef accessChainId(SpirvTransformerBase::GetNewId(blobOut));
3239 spirv::WriteAccessChain(blobOut, mElementPointerTypeId, accessChainId, mArrayVariableId,
3240 {ID::IntZero});
3241
3242 ASSERT(mElementTypeId.valid());
3243 const spirv::IdRef loadId(SpirvTransformerBase::GetNewId(blobOut));
3244 spirv::WriteLoad(blobOut, mElementTypeId, loadId, accessChainId, nullptr);
3245
3246 ASSERT(mReplacementVariableId.valid());
3247 spirv::WriteStore(blobOut, mReplacementVariableId, loadId, nullptr);
3248 }
3249 }
3250
3251 // A SPIR-V transformer. It walks the instructions and modifies them as necessary, for example to
3252 // assign bindings or locations.
3253 class SpirvTransformer final : public SpirvTransformerBase
3254 {
3255 public:
SpirvTransformer(const spirv::Blob & spirvBlobIn,const SpvTransformOptions & options,bool isLastPass,const ShaderInterfaceVariableInfoMap & variableInfoMap,spirv::Blob * spirvBlobOut)3256 SpirvTransformer(const spirv::Blob &spirvBlobIn,
3257 const SpvTransformOptions &options,
3258 bool isLastPass,
3259 const ShaderInterfaceVariableInfoMap &variableInfoMap,
3260 spirv::Blob *spirvBlobOut)
3261 : SpirvTransformerBase(spirvBlobIn, variableInfoMap, spirvBlobOut),
3262 mOptions(options),
3263 mOverviewFlags(0),
3264 mNonSemanticInstructions(isLastPass),
3265 mPerVertexTrimmer(options, variableInfoMap),
3266 mXfbCodeGenerator(options),
3267 mPositionTransformer(options),
3268 mMultisampleTransformer(options)
3269 {}
3270
3271 void transform();
3272
3273 private:
3274 // A prepass to resolve interesting ids:
3275 void resolveVariableIds();
3276
3277 // Transform instructions:
3278 void transformInstruction();
3279
3280 // Instructions that are purely informational:
3281 void visitDecorate(const uint32_t *instruction);
3282 void visitMemberDecorate(const uint32_t *instruction);
3283 void visitTypeArray(const uint32_t *instruction);
3284 void visitTypePointer(const uint32_t *instruction);
3285 void visitTypeStruct(const uint32_t *instruction);
3286 void visitVariable(const uint32_t *instruction);
3287 void visitCapability(const uint32_t *instruction);
3288 bool visitExtInst(const uint32_t *instruction);
3289
3290 // Instructions that potentially need transformation. They return true if the instruction is
3291 // transformed. If false is returned, the instruction should be copied as-is.
3292 TransformationState transformAccessChain(const uint32_t *instruction);
3293 TransformationState transformCapability(const uint32_t *instruction);
3294 TransformationState transformEntryPoint(const uint32_t *instruction);
3295 TransformationState transformExtension(const uint32_t *instruction);
3296 TransformationState transformExtInstImport(const uint32_t *instruction);
3297 TransformationState transformExtInst(const uint32_t *instruction);
3298 TransformationState transformDecorate(const uint32_t *instruction);
3299 TransformationState transformMemberDecorate(const uint32_t *instruction);
3300 TransformationState transformName(const uint32_t *instruction);
3301 TransformationState transformMemberName(const uint32_t *instruction);
3302 TransformationState transformTypePointer(const uint32_t *instruction);
3303 TransformationState transformTypeStruct(const uint32_t *instruction);
3304 TransformationState transformVariable(const uint32_t *instruction);
3305 TransformationState transformTypeImage(const uint32_t *instruction);
3306 TransformationState transformImageRead(const uint32_t *instruction);
3307
3308 // Helpers:
3309 void visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId);
3310 void writePendingDeclarations();
3311 void writeInputPreamble();
3312 void writeOutputPrologue();
3313
3314 // Special flags:
3315 SpvTransformOptions mOptions;
3316
3317 // Traversal state:
3318 spirv::IdRef mCurrentFunctionId;
3319
3320 // Transformation state:
3321
3322 uint32_t mOverviewFlags;
3323 SpirvNonSemanticInstructions mNonSemanticInstructions;
3324 SpirvPerVertexTrimmer mPerVertexTrimmer;
3325 SpirvInactiveVaryingRemover mInactiveVaryingRemover;
3326 SpirvVaryingPrecisionFixer mVaryingPrecisionFixer;
3327 SpirvTransformFeedbackCodeGenerator mXfbCodeGenerator;
3328 SpirvPositionTransformer mPositionTransformer;
3329 SpirvMultisampleTransformer mMultisampleTransformer;
3330 SpirvSecondaryOutputTransformer mSecondaryOutputTransformer;
3331 };
3332
transform()3333 void SpirvTransformer::transform()
3334 {
3335 onTransformBegin();
3336
3337 // First, find all necessary ids and associate them with the information required to transform
3338 // their decorations. This is mostly derived from |mVariableInfoMap|, but may have additional
3339 // mappings; for example |mVariableInfoMap| maps an interface block's type ID to info, but the
3340 // transformer needs to discover the variable associated with that block and map it to the same
3341 // info.
3342 resolveVariableIds();
3343
3344 while (mCurrentWord < mSpirvBlobIn.size())
3345 {
3346 transformInstruction();
3347 }
3348 }
3349
resolveVariableIds()3350 void SpirvTransformer::resolveVariableIds()
3351 {
3352 const size_t indexBound = mSpirvBlobIn[spirv::kHeaderIndexIndexBound];
3353
3354 mInactiveVaryingRemover.init(indexBound);
3355 if (mOptions.useSpirvVaryingPrecisionFixer)
3356 {
3357 mVaryingPrecisionFixer.init(indexBound);
3358 }
3359 if (mOptions.isMultisampledFramebufferFetch || mOptions.enableSampleShading)
3360 {
3361 mMultisampleTransformer.init(indexBound);
3362 }
3363 if (mOptions.shaderType == gl::ShaderType::Fragment)
3364 {
3365 mSecondaryOutputTransformer.init(indexBound);
3366 }
3367
3368 // Allocate storage for id-to-info map. If %i is an id in mVariableInfoMap, index i in this
3369 // vector will hold a pointer to the ShaderInterfaceVariableInfo object associated with that
3370 // name in mVariableInfoMap.
3371 mVariableInfoById.resize(indexBound, nullptr);
3372
3373 // Pre-populate from mVariableInfoMap.
3374 {
3375 const ShaderInterfaceVariableInfoMap::VariableInfoArray &data = mVariableInfoMap.getData();
3376 const ShaderInterfaceVariableInfoMap::IdToIndexMap &idToIndexMap =
3377 mVariableInfoMap.getIdToIndexMap()[mOptions.shaderType];
3378
3379 for (uint32_t hashedId = 0; hashedId < idToIndexMap.size(); ++hashedId)
3380 {
3381 const uint32_t id = hashedId + sh::vk::spirv::kIdShaderVariablesBegin;
3382 const VariableIndex &variableIndex = idToIndexMap.at(hashedId);
3383 if (variableIndex.index == VariableIndex::kInvalid)
3384 {
3385 continue;
3386 }
3387
3388 const ShaderInterfaceVariableInfo &info = data[variableIndex.index];
3389
3390 ASSERT(id < mVariableInfoById.size());
3391 mVariableInfoById[id] = &info;
3392 }
3393 }
3394
3395 size_t currentWord = spirv::kHeaderIndexInstructions;
3396
3397 while (currentWord < mSpirvBlobIn.size())
3398 {
3399 const uint32_t *instruction = &mSpirvBlobIn[currentWord];
3400
3401 uint32_t wordCount;
3402 spv::Op opCode;
3403 spirv::GetInstructionOpAndLength(instruction, &opCode, &wordCount);
3404
3405 switch (opCode)
3406 {
3407 case spv::OpDecorate:
3408 visitDecorate(instruction);
3409 break;
3410 case spv::OpMemberDecorate:
3411 visitMemberDecorate(instruction);
3412 break;
3413 case spv::OpTypeArray:
3414 visitTypeArray(instruction);
3415 break;
3416 case spv::OpTypePointer:
3417 visitTypePointer(instruction);
3418 break;
3419 case spv::OpTypeStruct:
3420 visitTypeStruct(instruction);
3421 break;
3422 case spv::OpVariable:
3423 visitVariable(instruction);
3424 break;
3425 case spv::OpExtInst:
3426 if (visitExtInst(instruction))
3427 {
3428 return;
3429 }
3430 break;
3431 default:
3432 break;
3433 }
3434
3435 currentWord += wordCount;
3436 }
3437 UNREACHABLE();
3438 }
3439
transformInstruction()3440 void SpirvTransformer::transformInstruction()
3441 {
3442 uint32_t wordCount;
3443 spv::Op opCode;
3444 const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);
3445
3446 if (opCode == spv::OpFunction)
3447 {
3448 spirv::IdResultType id;
3449 spv::FunctionControlMask functionControl;
3450 spirv::IdRef functionType;
3451 spirv::ParseFunction(instruction, &id, &mCurrentFunctionId, &functionControl,
3452 &functionType);
3453
3454 // SPIR-V is structured in sections. Function declarations come last. Only a few
3455 // instructions such as Op*Access* or OpEmitVertex opcodes inside functions need to be
3456 // inspected.
3457 mIsInFunctionSection = true;
3458 }
3459
3460 // Only look at interesting instructions.
3461 TransformationState transformationState = TransformationState::Unchanged;
3462
3463 if (mIsInFunctionSection)
3464 {
3465 // Look at in-function opcodes.
3466 switch (opCode)
3467 {
3468 case spv::OpExtInst:
3469 transformationState = transformExtInst(instruction);
3470 break;
3471 case spv::OpAccessChain:
3472 case spv::OpInBoundsAccessChain:
3473 case spv::OpPtrAccessChain:
3474 case spv::OpInBoundsPtrAccessChain:
3475 transformationState = transformAccessChain(instruction);
3476 break;
3477 case spv::OpImageRead:
3478 transformationState = transformImageRead(instruction);
3479 break;
3480 default:
3481 break;
3482 }
3483 }
3484 else
3485 {
3486 // Look at global declaration opcodes.
3487 switch (opCode)
3488 {
3489 case spv::OpExtension:
3490 transformationState = transformExtension(instruction);
3491 break;
3492 case spv::OpExtInstImport:
3493 transformationState = transformExtInstImport(instruction);
3494 break;
3495 case spv::OpExtInst:
3496 transformationState = transformExtInst(instruction);
3497 break;
3498 case spv::OpName:
3499 transformationState = transformName(instruction);
3500 break;
3501 case spv::OpMemberName:
3502 transformationState = transformMemberName(instruction);
3503 break;
3504 case spv::OpCapability:
3505 transformationState = transformCapability(instruction);
3506 break;
3507 case spv::OpEntryPoint:
3508 transformationState = transformEntryPoint(instruction);
3509 break;
3510 case spv::OpDecorate:
3511 transformationState = transformDecorate(instruction);
3512 break;
3513 case spv::OpMemberDecorate:
3514 transformationState = transformMemberDecorate(instruction);
3515 break;
3516 case spv::OpTypeImage:
3517 transformationState = transformTypeImage(instruction);
3518 break;
3519 case spv::OpTypePointer:
3520 transformationState = transformTypePointer(instruction);
3521 break;
3522 case spv::OpTypeStruct:
3523 transformationState = transformTypeStruct(instruction);
3524 break;
3525 case spv::OpVariable:
3526 transformationState = transformVariable(instruction);
3527 break;
3528 default:
3529 break;
3530 }
3531 }
3532
3533 // If the instruction was not transformed, copy it to output as is.
3534 if (transformationState == TransformationState::Unchanged)
3535 {
3536 copyInstruction(instruction, wordCount);
3537 }
3538
3539 // Advance to next instruction.
3540 mCurrentWord += wordCount;
3541 }
3542
3543 // Called at the end of the declarations section. Any declarations that are necessary but weren't
3544 // present in the original shader need to be done here.
writePendingDeclarations()3545 void SpirvTransformer::writePendingDeclarations()
3546 {
3547 mMultisampleTransformer.writePendingDeclarations(mNonSemanticInstructions, mVariableInfoById,
3548 mSpirvBlobOut);
3549
3550 // Pre-rotation and transformation of depth to Vulkan clip space require declarations that may
3551 // not necessarily be in the shader. Transform feedback emulation additionally requires a few
3552 // overlapping ids.
3553 if (!mOptions.isLastPreFragmentStage)
3554 {
3555 return;
3556 }
3557
3558 if (mOptions.isTransformFeedbackStage)
3559 {
3560 mXfbCodeGenerator.writePendingDeclarations(mVariableInfoById, storageBufferStorageClass(),
3561 mSpirvBlobOut);
3562 }
3563 }
3564
3565 // Called by transformInstruction to insert necessary instructions for casting varyings.
writeInputPreamble()3566 void SpirvTransformer::writeInputPreamble()
3567 {
3568 if (mOptions.useSpirvVaryingPrecisionFixer)
3569 {
3570 mVaryingPrecisionFixer.writeInputPreamble(mVariableInfoById, mOptions.shaderType,
3571 mSpirvBlobOut);
3572 }
3573 }
3574
3575 // Called by transformInstruction to insert necessary instructions for casting varyings and
3576 // modifying gl_Position.
writeOutputPrologue()3577 void SpirvTransformer::writeOutputPrologue()
3578 {
3579 if (mOptions.useSpirvVaryingPrecisionFixer)
3580 {
3581 mVaryingPrecisionFixer.writeOutputPrologue(mVariableInfoById, mOptions.shaderType,
3582 mSpirvBlobOut);
3583 }
3584 if (mOptions.shaderType == gl::ShaderType::Fragment)
3585 {
3586 mSecondaryOutputTransformer.writeOutputPrologue(mVariableInfoById, mSpirvBlobOut);
3587 }
3588 if (!mNonSemanticInstructions.hasOutputPerVertex())
3589 {
3590 return;
3591 }
3592
3593 // Whether gl_Position should be transformed to account for pre-rotation and Vulkan clip space.
3594 const bool transformPosition = mOptions.isLastPreFragmentStage;
3595 const bool isXfbExtensionStage =
3596 mOptions.isTransformFeedbackStage && !mOptions.isTransformFeedbackEmulated;
3597 if (!transformPosition && !isXfbExtensionStage)
3598 {
3599 return;
3600 }
3601
3602 // Load gl_Position with the following SPIR-V:
3603 //
3604 // // Create an access chain to gl_PerVertex.gl_Position, which is always at index 0.
3605 // %PositionPointer = OpAccessChain %kIdVec4OutputTypePointer %kIdOutputPerVertexVar
3606 // %kIdIntZero
3607 // // Load gl_Position
3608 // %Position = OpLoad %kIdVec4 %PositionPointer
3609 //
3610 const spirv::IdRef positionPointerId(getNewId());
3611 const spirv::IdRef positionId(getNewId());
3612
3613 spirv::WriteAccessChain(mSpirvBlobOut, ID::Vec4OutputTypePointer, positionPointerId,
3614 ID::OutputPerVertexVar, {ID::IntZero});
3615 spirv::WriteLoad(mSpirvBlobOut, ID::Vec4, positionId, positionPointerId, nullptr);
3616
3617 // Write transform feedback output before modifying gl_Position.
3618 if (isXfbExtensionStage)
3619 {
3620 mXfbCodeGenerator.writeTransformFeedbackExtensionOutput(positionId, mSpirvBlobOut);
3621 }
3622
3623 if (transformPosition)
3624 {
3625 mPositionTransformer.writePositionTransformation(positionPointerId, positionId,
3626 mSpirvBlobOut);
3627 }
3628 }
3629
visitDecorate(const uint32_t * instruction)3630 void SpirvTransformer::visitDecorate(const uint32_t *instruction)
3631 {
3632 spirv::IdRef id;
3633 spv::Decoration decoration;
3634 spirv::LiteralIntegerList valueList;
3635 spirv::ParseDecorate(instruction, &id, &decoration, &valueList);
3636
3637 mMultisampleTransformer.visitDecorate(id, decoration, valueList);
3638 }
3639
visitMemberDecorate(const uint32_t * instruction)3640 void SpirvTransformer::visitMemberDecorate(const uint32_t *instruction)
3641 {
3642 spirv::IdRef typeId;
3643 spirv::LiteralInteger member;
3644 spv::Decoration decoration;
3645 spirv::LiteralIntegerList valueList;
3646 spirv::ParseMemberDecorate(instruction, &typeId, &member, &decoration, &valueList);
3647
3648 mPerVertexTrimmer.visitMemberDecorate(typeId, member, decoration, valueList);
3649 mMultisampleTransformer.visitMemberDecorate(typeId, member, decoration);
3650 }
3651
visitTypeHelper(spirv::IdResult id,spirv::IdRef typeId)3652 void SpirvTransformer::visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId)
3653 {
3654 // Carry forward the mapping of typeId->info to id->info. For interface block, it's the block
3655 // id that is mapped to the info, so this is necessary to eventually be able to map the variable
3656 // itself to the info.
3657 mVariableInfoById[id] = mVariableInfoById[typeId];
3658 }
3659
visitTypeArray(const uint32_t * instruction)3660 void SpirvTransformer::visitTypeArray(const uint32_t *instruction)
3661 {
3662 spirv::IdResult id;
3663 spirv::IdRef elementType;
3664 spirv::IdRef length;
3665 spirv::ParseTypeArray(instruction, &id, &elementType, &length);
3666
3667 visitTypeHelper(id, elementType);
3668 if (mOptions.shaderType == gl::ShaderType::Fragment)
3669 {
3670 mSecondaryOutputTransformer.visitTypeArray(id, elementType);
3671 }
3672 }
3673
visitTypePointer(const uint32_t * instruction)3674 void SpirvTransformer::visitTypePointer(const uint32_t *instruction)
3675 {
3676 spirv::IdResult id;
3677 spv::StorageClass storageClass;
3678 spirv::IdRef typeId;
3679 spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);
3680
3681 visitTypeHelper(id, typeId);
3682 if (mOptions.useSpirvVaryingPrecisionFixer)
3683 {
3684 mVaryingPrecisionFixer.visitTypePointer(id, storageClass, typeId);
3685 }
3686 mMultisampleTransformer.visitTypePointer(mOptions.shaderType, id, storageClass, typeId);
3687 if (mOptions.shaderType == gl::ShaderType::Fragment)
3688 {
3689 mSecondaryOutputTransformer.visitTypePointer(id, typeId);
3690 }
3691 }
3692
visitTypeStruct(const uint32_t * instruction)3693 void SpirvTransformer::visitTypeStruct(const uint32_t *instruction)
3694 {
3695 spirv::IdResult id;
3696 spirv::IdRefList memberList;
3697 ParseTypeStruct(instruction, &id, &memberList);
3698
3699 mMultisampleTransformer.visitTypeStruct(id, memberList);
3700 }
3701
visitVariable(const uint32_t * instruction)3702 void SpirvTransformer::visitVariable(const uint32_t *instruction)
3703 {
3704 spirv::IdResultType typeId;
3705 spirv::IdResult id;
3706 spv::StorageClass storageClass;
3707 spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);
3708
3709 // If storage class indicates that this is not a shader interface variable, ignore it.
3710 const bool isInterfaceBlockVariable =
3711 storageClass == spv::StorageClassUniform || storageClass == spv::StorageClassStorageBuffer;
3712 const bool isOpaqueUniform = storageClass == spv::StorageClassUniformConstant;
3713 const bool isInOut =
3714 storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput;
3715
3716 if (!isInterfaceBlockVariable && !isOpaqueUniform && !isInOut)
3717 {
3718 return;
3719 }
3720
3721 // If no info is already associated with this id, carry that forward from the type. This
3722 // happens for interface blocks, where the id->info association is done on the type id.
3723 ASSERT(mVariableInfoById[id] == nullptr || mVariableInfoById[typeId] == nullptr);
3724 if (mVariableInfoById[id] == nullptr)
3725 {
3726 mVariableInfoById[id] = mVariableInfoById[typeId];
3727 }
3728
3729 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
3730
3731 // If this is an interface variable but no info is associated with it, it must be a built-in.
3732 if (info == nullptr)
3733 {
3734 // Make all builtins point to this no-op info. Adding this entry allows us to ASSERT that
3735 // every shader interface variable is processed during the SPIR-V transformation. This is
3736 // done when iterating the ids provided by OpEntryPoint.
3737 mVariableInfoById[id] = &mBuiltinVariableInfo;
3738 return;
3739 }
3740
3741 if (mOptions.useSpirvVaryingPrecisionFixer)
3742 {
3743 mVaryingPrecisionFixer.visitVariable(*info, mOptions.shaderType, typeId, id, storageClass,
3744 mSpirvBlobOut);
3745 }
3746 if (mOptions.isTransformFeedbackStage && mVariableInfoById[id]->hasTransformFeedback)
3747 {
3748 const XFBInterfaceVariableInfo &xfbInfo =
3749 mVariableInfoMap.getXFBDataForVariableInfo(mVariableInfoById[id]);
3750 mXfbCodeGenerator.visitVariable(*info, xfbInfo, mOptions.shaderType, typeId, id,
3751 storageClass);
3752 }
3753
3754 mMultisampleTransformer.visitVariable(mOptions.shaderType, typeId, id, storageClass);
3755 }
3756
visitExtInst(const uint32_t * instruction)3757 bool SpirvTransformer::visitExtInst(const uint32_t *instruction)
3758 {
3759 sh::vk::spirv::NonSemanticInstruction inst;
3760 if (!mNonSemanticInstructions.visitExtInst(instruction, &inst))
3761 {
3762 return false;
3763 }
3764
3765 switch (inst)
3766 {
3767 case sh::vk::spirv::kNonSemanticOverview:
3768 // SPIR-V is structured in sections (SPIR-V 1.0 Section 2.4 Logical Layout of a Module).
3769 // Names appear before decorations, which are followed by type+variables and finally
3770 // functions. We are only interested in name and variable declarations (as well as type
3771 // declarations for the sake of nameless interface blocks). Early out when the function
3772 // declaration section is met.
3773 //
3774 // This non-semantic instruction marks the beginning of the functions section.
3775 return true;
3776 default:
3777 UNREACHABLE();
3778 }
3779
3780 return false;
3781 }
3782
transformDecorate(const uint32_t * instruction)3783 TransformationState SpirvTransformer::transformDecorate(const uint32_t *instruction)
3784 {
3785 spirv::IdRef id;
3786 spv::Decoration decoration;
3787 spirv::LiteralIntegerList decorationValues;
3788 spirv::ParseDecorate(instruction, &id, &decoration, &decorationValues);
3789
3790 ASSERT(id < mVariableInfoById.size());
3791 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
3792
3793 // If variable is not a shader interface variable that needs modification, there's nothing to
3794 // do.
3795 if (info == nullptr)
3796 {
3797 return TransformationState::Unchanged;
3798 }
3799
3800 if (mOptions.shaderType == gl::ShaderType::Fragment)
3801 {
3802 // Handle decorations for the secondary fragment output array.
3803 if (mSecondaryOutputTransformer.transformDecorate(id, decoration, decorationValues,
3804 mSpirvBlobOut) ==
3805 TransformationState::Transformed)
3806 {
3807 return TransformationState::Transformed;
3808 }
3809 }
3810
3811 mMultisampleTransformer.transformDecorate(mNonSemanticInstructions, *info, mOptions.shaderType,
3812 id, decoration, mSpirvBlobOut);
3813
3814 if (mXfbCodeGenerator.transformDecorate(info, mOptions.shaderType, id, decoration,
3815 decorationValues,
3816 mSpirvBlobOut) == TransformationState::Transformed)
3817 {
3818 return TransformationState::Transformed;
3819 }
3820
3821 if (mInactiveVaryingRemover.transformDecorate(*info, mOptions.shaderType, id, decoration,
3822 decorationValues, mSpirvBlobOut) ==
3823 TransformationState::Transformed)
3824 {
3825 return TransformationState::Transformed;
3826 }
3827
3828 // If using relaxed precision, generate instructions for the replacement id instead.
3829 if (mOptions.useSpirvVaryingPrecisionFixer)
3830 {
3831 id = mVaryingPrecisionFixer.getReplacementId(id);
3832 }
3833
3834 uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid;
3835
3836 switch (decoration)
3837 {
3838 case spv::DecorationLocation:
3839 newDecorationValue = info->location;
3840 break;
3841 case spv::DecorationBinding:
3842 newDecorationValue = info->binding;
3843 break;
3844 case spv::DecorationDescriptorSet:
3845 newDecorationValue = info->descriptorSet;
3846 break;
3847 case spv::DecorationFlat:
3848 case spv::DecorationNoPerspective:
3849 case spv::DecorationCentroid:
3850 case spv::DecorationSample:
3851 if (mOptions.useSpirvVaryingPrecisionFixer && info->useRelaxedPrecision)
3852 {
3853 // Change the id to replacement variable
3854 spirv::WriteDecorate(mSpirvBlobOut, id, decoration, decorationValues);
3855 return TransformationState::Transformed;
3856 }
3857 break;
3858 case spv::DecorationBlock:
3859 // If this is the Block decoration of a shader I/O block, add the transform feedback
3860 // decorations to its members right away.
3861 if (mOptions.isTransformFeedbackStage && info->hasTransformFeedback)
3862 {
3863 const XFBInterfaceVariableInfo &xfbInfo =
3864 mVariableInfoMap.getXFBDataForVariableInfo(info);
3865 mXfbCodeGenerator.addMemberDecorate(xfbInfo, id, mSpirvBlobOut);
3866 }
3867 break;
3868 case spv::DecorationInvariant:
3869 spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationInvariant, {});
3870 return TransformationState::Transformed;
3871 default:
3872 break;
3873 }
3874
3875 // If the decoration is not something we care about modifying, there's nothing to do.
3876 if (newDecorationValue == ShaderInterfaceVariableInfo::kInvalid)
3877 {
3878 return TransformationState::Unchanged;
3879 }
3880
3881 // Modify the decoration value.
3882 ASSERT(decorationValues.size() == 1);
3883 spirv::WriteDecorate(mSpirvBlobOut, id, decoration,
3884 {spirv::LiteralInteger(newDecorationValue)});
3885
3886 // If there are decorations to be added, add them right after the Location decoration is
3887 // encountered.
3888 if (decoration != spv::DecorationLocation)
3889 {
3890 return TransformationState::Transformed;
3891 }
3892
3893 // If any, the replacement variable is always reduced precision so add that decoration to
3894 // fixedVaryingId.
3895 if (mOptions.useSpirvVaryingPrecisionFixer && info->useRelaxedPrecision)
3896 {
3897 mVaryingPrecisionFixer.addDecorate(id, mSpirvBlobOut);
3898 }
3899
3900 // Add component decoration, if any.
3901 if (info->component != ShaderInterfaceVariableInfo::kInvalid)
3902 {
3903 spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationComponent,
3904 {spirv::LiteralInteger(info->component)});
3905 }
3906
3907 // Add index decoration, if any.
3908 if (info->index != ShaderInterfaceVariableInfo::kInvalid)
3909 {
3910 spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationIndex,
3911 {spirv::LiteralInteger(info->index)});
3912 }
3913
3914 // Add Xfb decorations, if any.
3915 if (mOptions.isTransformFeedbackStage && info->hasTransformFeedback)
3916 {
3917 const XFBInterfaceVariableInfo &xfbInfo = mVariableInfoMap.getXFBDataForVariableInfo(info);
3918 mXfbCodeGenerator.addDecorate(xfbInfo, id, mSpirvBlobOut);
3919 }
3920
3921 return TransformationState::Transformed;
3922 }
3923
transformMemberDecorate(const uint32_t * instruction)3924 TransformationState SpirvTransformer::transformMemberDecorate(const uint32_t *instruction)
3925 {
3926 spirv::IdRef typeId;
3927 spirv::LiteralInteger member;
3928 spv::Decoration decoration;
3929 spirv::ParseMemberDecorate(instruction, &typeId, &member, &decoration, nullptr);
3930
3931 if (mPerVertexTrimmer.transformMemberDecorate(typeId, member, decoration) ==
3932 TransformationState::Transformed)
3933 {
3934 return TransformationState::Transformed;
3935 }
3936
3937 ASSERT(typeId < mVariableInfoById.size());
3938 const ShaderInterfaceVariableInfo *info = mVariableInfoById[typeId];
3939
3940 return mXfbCodeGenerator.transformMemberDecorate(info, mOptions.shaderType, typeId, member,
3941 decoration, mSpirvBlobOut);
3942 }
3943
transformCapability(const uint32_t * instruction)3944 TransformationState SpirvTransformer::transformCapability(const uint32_t *instruction)
3945 {
3946 spv::Capability capability;
3947 spirv::ParseCapability(instruction, &capability);
3948
3949 TransformationState xfbTransformState =
3950 mXfbCodeGenerator.transformCapability(capability, mSpirvBlobOut);
3951 ASSERT(xfbTransformState == TransformationState::Unchanged);
3952
3953 TransformationState multiSampleTransformState = mMultisampleTransformer.transformCapability(
3954 mNonSemanticInstructions, capability, mSpirvBlobOut);
3955 ASSERT(multiSampleTransformState == TransformationState::Unchanged);
3956
3957 return TransformationState::Unchanged;
3958 }
3959
transformName(const uint32_t * instruction)3960 TransformationState SpirvTransformer::transformName(const uint32_t *instruction)
3961 {
3962 spirv::IdRef id;
3963 spirv::LiteralString name;
3964 spirv::ParseName(instruction, &id, &name);
3965
3966 return mXfbCodeGenerator.transformName(id, name);
3967 }
3968
transformMemberName(const uint32_t * instruction)3969 TransformationState SpirvTransformer::transformMemberName(const uint32_t *instruction)
3970 {
3971 spirv::IdRef id;
3972 spirv::LiteralInteger member;
3973 spirv::LiteralString name;
3974 spirv::ParseMemberName(instruction, &id, &member, &name);
3975
3976 if (mXfbCodeGenerator.transformMemberName(id, member, name) == TransformationState::Transformed)
3977 {
3978 return TransformationState::Transformed;
3979 }
3980
3981 return mPerVertexTrimmer.transformMemberName(id, member, name);
3982 }
3983
transformEntryPoint(const uint32_t * instruction)3984 TransformationState SpirvTransformer::transformEntryPoint(const uint32_t *instruction)
3985 {
3986 spv::ExecutionModel executionModel;
3987 spirv::IdRef entryPointId;
3988 spirv::LiteralString name;
3989 spirv::IdRefList interfaceList;
3990 spirv::ParseEntryPoint(instruction, &executionModel, &entryPointId, &name, &interfaceList);
3991
3992 // Should only have one EntryPoint
3993 ASSERT(entryPointId == ID::EntryPoint);
3994
3995 mInactiveVaryingRemover.modifyEntryPointInterfaceList(mVariableInfoById, mOptions.shaderType,
3996 entryPointList(), &interfaceList);
3997
3998 if (mOptions.shaderType == gl::ShaderType::Fragment)
3999 {
4000 mSecondaryOutputTransformer.modifyEntryPointInterfaceList(
4001 mVariableInfoById, entryPointList(), &interfaceList, mSpirvBlobOut);
4002 }
4003
4004 if (mOptions.useSpirvVaryingPrecisionFixer)
4005 {
4006 mVaryingPrecisionFixer.modifyEntryPointInterfaceList(entryPointList(), &interfaceList);
4007 }
4008
4009 mMultisampleTransformer.modifyEntryPointInterfaceList(
4010 mNonSemanticInstructions, entryPointList(), &interfaceList, mSpirvBlobOut);
4011 mXfbCodeGenerator.modifyEntryPointInterfaceList(mVariableInfoById, mOptions.shaderType,
4012 entryPointList(), &interfaceList);
4013
4014 // Write the entry point with the inactive interface variables removed.
4015 spirv::WriteEntryPoint(mSpirvBlobOut, executionModel, ID::EntryPoint, name, interfaceList);
4016
4017 // Add an OpExecutionMode Xfb instruction if necessary.
4018 mXfbCodeGenerator.addExecutionMode(ID::EntryPoint, mSpirvBlobOut);
4019
4020 return TransformationState::Transformed;
4021 }
4022
transformTypePointer(const uint32_t * instruction)4023 TransformationState SpirvTransformer::transformTypePointer(const uint32_t *instruction)
4024 {
4025 spirv::IdResult id;
4026 spv::StorageClass storageClass;
4027 spirv::IdRef typeId;
4028 spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);
4029
4030 if (mInactiveVaryingRemover.transformTypePointer(id, storageClass, typeId, mSpirvBlobOut) ==
4031 TransformationState::Transformed)
4032 {
4033 return TransformationState::Transformed;
4034 }
4035
4036 ASSERT(id < mVariableInfoById.size());
4037 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4038
4039 return mXfbCodeGenerator.transformTypePointer(info, mOptions.shaderType, id, storageClass,
4040 typeId, mSpirvBlobOut);
4041 }
4042
transformExtension(const uint32_t * instruction)4043 TransformationState SpirvTransformer::transformExtension(const uint32_t *instruction)
4044 {
4045 // Drop the OpExtension "SPV_KHR_non_semantic_info" extension instruction.
4046 // SPV_KHR_non_semantic_info is used purely as a means of communication between the compiler and
4047 // the SPIR-V transformer, and is stripped away before the SPIR-V is handed off to the driver.
4048 spirv::LiteralString name;
4049 spirv::ParseExtension(instruction, &name);
4050
4051 return strcmp(name, "SPV_KHR_non_semantic_info") == 0 ? TransformationState::Transformed
4052 : TransformationState::Unchanged;
4053 }
4054
transformExtInstImport(const uint32_t * instruction)4055 TransformationState SpirvTransformer::transformExtInstImport(const uint32_t *instruction)
4056 {
4057 // Drop the OpExtInstImport "NonSemantic.ANGLE" instruction.
4058 spirv::IdResult id;
4059 spirv::LiteralString name;
4060 ParseExtInstImport(instruction, &id, &name);
4061
4062 return id == sh::vk::spirv::kIdNonSemanticInstructionSet ? TransformationState::Transformed
4063 : TransformationState::Unchanged;
4064 }
4065
transformExtInst(const uint32_t * instruction)4066 TransformationState SpirvTransformer::transformExtInst(const uint32_t *instruction)
4067 {
4068 sh::vk::spirv::NonSemanticInstruction inst;
4069 if (!mNonSemanticInstructions.visitExtInst(instruction, &inst))
4070 {
4071 return TransformationState::Unchanged;
4072 }
4073
4074 switch (inst)
4075 {
4076 case sh::vk::spirv::kNonSemanticOverview:
4077 // Declare anything that we need but didn't find there already.
4078 writePendingDeclarations();
4079 break;
4080 case sh::vk::spirv::kNonSemanticEnter:
4081 // If there are any precision mismatches that need to be handled, temporary global
4082 // variables are created with the original precision. Initialize those variables from
4083 // the varyings at the beginning of the shader.
4084 writeInputPreamble();
4085 break;
4086 case sh::vk::spirv::kNonSemanticOutput:
4087 // Generate gl_Position transformations and transform feedback capture (through
4088 // extension) before return or EmitVertex(). Additionally, if there are any precision
4089 // mismatches that need to be ahendled, write the temporary variables that hold varyings
4090 // data. Copy a secondary fragment output value if it was declared as an array.
4091 writeOutputPrologue();
4092 break;
4093 case sh::vk::spirv::kNonSemanticTransformFeedbackEmulation:
4094 // Transform feedback emulation is written to a designated function. Allow its code to
4095 // be generated if this is the right function.
4096 if (mOptions.isTransformFeedbackStage)
4097 {
4098 mXfbCodeGenerator.writeTransformFeedbackEmulationOutput(
4099 mInactiveVaryingRemover, mVaryingPrecisionFixer,
4100 mOptions.useSpirvVaryingPrecisionFixer, mSpirvBlobOut);
4101 }
4102 break;
4103 default:
4104 UNREACHABLE();
4105 break;
4106 }
4107
4108 // Drop the instruction if this is the last pass
4109 return mNonSemanticInstructions.transformExtInst(instruction);
4110 }
4111
transformTypeStruct(const uint32_t * instruction)4112 TransformationState SpirvTransformer::transformTypeStruct(const uint32_t *instruction)
4113 {
4114 spirv::IdResult id;
4115 spirv::IdRefList memberList;
4116 ParseTypeStruct(instruction, &id, &memberList);
4117
4118 if (mPerVertexTrimmer.transformTypeStruct(id, &memberList, mSpirvBlobOut) ==
4119 TransformationState::Transformed)
4120 {
4121 return TransformationState::Transformed;
4122 }
4123
4124 ASSERT(id < mVariableInfoById.size());
4125 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4126
4127 return mXfbCodeGenerator.transformTypeStruct(info, mOptions.shaderType, id, memberList,
4128 mSpirvBlobOut);
4129 }
4130
transformVariable(const uint32_t * instruction)4131 TransformationState SpirvTransformer::transformVariable(const uint32_t *instruction)
4132 {
4133 spirv::IdResultType typeId;
4134 spirv::IdResult id;
4135 spv::StorageClass storageClass;
4136 spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);
4137
4138 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4139
4140 // If variable is not a shader interface variable that needs modification, there's nothing to
4141 // do.
4142 if (info == nullptr)
4143 {
4144 return TransformationState::Unchanged;
4145 }
4146
4147 if (mOptions.shaderType == gl::ShaderType::Fragment && storageClass == spv::StorageClassOutput)
4148 {
4149 // If present, make the secondary fragment output array
4150 // private and declare a non-array output instead.
4151 if (mSecondaryOutputTransformer.transformVariable(
4152 typeId, mInactiveVaryingRemover.getTransformedPrivateType(typeId), id,
4153 mSpirvBlobOut) == TransformationState::Transformed)
4154 {
4155 return TransformationState::Transformed;
4156 }
4157 }
4158
4159 // Furthermore, if it's not an inactive varying output, there's nothing to do. Note that
4160 // inactive varying inputs are already pruned by the translator.
4161 // However, input or output storage class for interface block will not be pruned when a shader
4162 // is compiled separately.
4163 if (info->activeStages[mOptions.shaderType])
4164 {
4165 if (mOptions.useSpirvVaryingPrecisionFixer &&
4166 mVaryingPrecisionFixer.transformVariable(
4167 *info, typeId, id, storageClass, mSpirvBlobOut) == TransformationState::Transformed)
4168 {
4169 // Make original variable a private global
4170 return mInactiveVaryingRemover.transformVariable(typeId, id, storageClass,
4171 mSpirvBlobOut);
4172 }
4173 return TransformationState::Unchanged;
4174 }
4175
4176 if (mXfbCodeGenerator.transformVariable(*info, mVariableInfoMap, mOptions.shaderType,
4177 storageBufferStorageClass(), typeId, id,
4178 storageClass) == TransformationState::Transformed)
4179 {
4180 return TransformationState::Transformed;
4181 }
4182
4183 // The variable is inactive. Output a modified variable declaration, where the type is the
4184 // corresponding type with the Private storage class.
4185 return mInactiveVaryingRemover.transformVariable(typeId, id, storageClass, mSpirvBlobOut);
4186 }
4187
transformTypeImage(const uint32_t * instruction)4188 TransformationState SpirvTransformer::transformTypeImage(const uint32_t *instruction)
4189 {
4190 return mMultisampleTransformer.transformTypeImage(instruction, mSpirvBlobOut);
4191 }
4192
transformImageRead(const uint32_t * instruction)4193 TransformationState SpirvTransformer::transformImageRead(const uint32_t *instruction)
4194 {
4195 return mMultisampleTransformer.transformImageRead(instruction, mSpirvBlobOut);
4196 }
4197
transformAccessChain(const uint32_t * instruction)4198 TransformationState SpirvTransformer::transformAccessChain(const uint32_t *instruction)
4199 {
4200 spirv::IdResultType typeId;
4201 spirv::IdResult id;
4202 spirv::IdRef baseId;
4203 spirv::IdRefList indexList;
4204 spirv::ParseAccessChain(instruction, &typeId, &id, &baseId, &indexList);
4205
4206 // If not accessing an inactive output varying, nothing to do.
4207 const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
4208 if (info == nullptr)
4209 {
4210 return TransformationState::Unchanged;
4211 }
4212
4213 if (mOptions.shaderType == gl::ShaderType::Fragment)
4214 {
4215 // Update the type used for accessing the secondary fragment output array.
4216 if (mSecondaryOutputTransformer.transformAccessChain(
4217 mInactiveVaryingRemover.getTransformedPrivateType(typeId), id, baseId, indexList,
4218 mSpirvBlobOut) == TransformationState::Transformed)
4219 {
4220 return TransformationState::Transformed;
4221 }
4222 }
4223
4224 if (mOptions.useSpirvVaryingPrecisionFixer)
4225 {
4226 if (info->activeStages[mOptions.shaderType] && !info->useRelaxedPrecision)
4227 {
4228 return TransformationState::Unchanged;
4229 }
4230 }
4231 else
4232 {
4233 if (info->activeStages[mOptions.shaderType])
4234 {
4235 return TransformationState::Unchanged;
4236 }
4237 }
4238
4239 return mInactiveVaryingRemover.transformAccessChain(typeId, id, baseId, indexList,
4240 mSpirvBlobOut);
4241 }
4242
4243 struct AliasingAttributeMap
4244 {
4245 // The SPIR-V id of the aliasing attribute with the most components. This attribute will be
4246 // used to read from this location instead of every aliasing one.
4247 spirv::IdRef attribute;
4248
4249 // SPIR-V ids of aliasing attributes.
4250 std::vector<spirv::IdRef> aliasingAttributes;
4251 };
4252
ValidateShaderInterfaceVariableIsAttribute(const ShaderInterfaceVariableInfo * info)4253 void ValidateShaderInterfaceVariableIsAttribute(const ShaderInterfaceVariableInfo *info)
4254 {
4255 ASSERT(info);
4256 ASSERT(info->activeStages[gl::ShaderType::Vertex]);
4257 ASSERT(info->attributeComponentCount > 0);
4258 ASSERT(info->attributeLocationCount > 0);
4259 ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
4260 }
4261
ValidateIsAliasingAttribute(const AliasingAttributeMap * aliasingMap,uint32_t id)4262 void ValidateIsAliasingAttribute(const AliasingAttributeMap *aliasingMap, uint32_t id)
4263 {
4264 ASSERT(id != aliasingMap->attribute);
4265 ASSERT(std::find(aliasingMap->aliasingAttributes.begin(), aliasingMap->aliasingAttributes.end(),
4266 id) != aliasingMap->aliasingAttributes.end());
4267 }
4268
4269 // A transformation that resolves vertex attribute aliases. Note that vertex attribute aliasing is
4270 // only allowed in GLSL ES 100, where the attribute types can only be one of float, vec2, vec3,
4271 // vec4, mat2, mat3, and mat4. Matrix attributes are handled by expanding them to multiple vector
4272 // attributes, each occupying one location.
4273 class SpirvVertexAttributeAliasingTransformer final : public SpirvTransformerBase
4274 {
4275 public:
SpirvVertexAttributeAliasingTransformer(const spirv::Blob & spirvBlobIn,const ShaderInterfaceVariableInfoMap & variableInfoMap,std::vector<const ShaderInterfaceVariableInfo * > && variableInfoById,spirv::Blob * spirvBlobOut)4276 SpirvVertexAttributeAliasingTransformer(
4277 const spirv::Blob &spirvBlobIn,
4278 const ShaderInterfaceVariableInfoMap &variableInfoMap,
4279 std::vector<const ShaderInterfaceVariableInfo *> &&variableInfoById,
4280 spirv::Blob *spirvBlobOut)
4281 : SpirvTransformerBase(spirvBlobIn, variableInfoMap, spirvBlobOut),
4282 mNonSemanticInstructions(true)
4283 {
4284 mVariableInfoById = std::move(variableInfoById);
4285 }
4286
4287 void transform();
4288
4289 private:
4290 // Preprocess aliasing attributes in preparation for their removal.
4291 void preprocessAliasingAttributes();
4292
4293 // Transform instructions:
4294 void transformInstruction();
4295
4296 // Helpers:
4297 spirv::IdRef getAliasingAttributeReplacementId(spirv::IdRef aliasingId, uint32_t offset) const;
4298 bool isMatrixAttribute(spirv::IdRef id) const;
4299
4300 // Instructions that are purely informational:
4301 void visitTypePointer(const uint32_t *instruction);
4302
4303 // Instructions that potentially need transformation. They return true if the instruction is
4304 // transformed. If false is returned, the instruction should be copied as-is.
4305 TransformationState transformEntryPoint(const uint32_t *instruction);
4306 TransformationState transformExtInst(const uint32_t *instruction);
4307 TransformationState transformName(const uint32_t *instruction);
4308 TransformationState transformDecorate(const uint32_t *instruction);
4309 TransformationState transformVariable(const uint32_t *instruction);
4310 TransformationState transformAccessChain(const uint32_t *instruction);
4311 void transformLoadHelper(spirv::IdRef pointerId,
4312 spirv::IdRef typeId,
4313 spirv::IdRef replacementId,
4314 spirv::IdRef resultId);
4315 TransformationState transformLoad(const uint32_t *instruction);
4316
4317 void declareExpandedMatrixVectors();
4318 void writeExpandedMatrixInitialization();
4319
4320 // Transformation state:
4321
4322 // Map of aliasing attributes per location.
4323 gl::AttribArray<AliasingAttributeMap> mAliasingAttributeMap;
4324
4325 // For each id, this map indicates whether it refers to an aliasing attribute that needs to be
4326 // removed.
4327 std::vector<bool> mIsAliasingAttributeById;
4328
4329 // Matrix attributes are split into vectors, each occupying one location. The SPIR-V
4330 // declaration would need to change from:
4331 //
4332 // %type = OpTypeMatrix %vectorType N
4333 // %matrixType = OpTypePointer Input %type
4334 // %matrix = OpVariable %matrixType Input
4335 //
4336 // to:
4337 //
4338 // %matrixType = OpTypePointer Private %type
4339 // %matrix = OpVariable %matrixType Private
4340 //
4341 // %vecType = OpTypePointer Input %vectorType
4342 //
4343 // %vec0 = OpVariable %vecType Input
4344 // ...
4345 // %vecN-1 = OpVariable %vecType Input
4346 //
4347 // For each id %matrix (which corresponds to a matrix attribute), this map contains %vec0. The
4348 // ids of the split vectors are consecutive, so %veci == %vec0 + i. %veciType is taken from
4349 // mInputTypePointers.
4350 std::vector<spirv::IdRef> mExpandedMatrixFirstVectorIdById;
4351
4352 // Id of attribute types; float and veci.
floatType(uint32_t componentCount)4353 spirv::IdRef floatType(uint32_t componentCount)
4354 {
4355 static_assert(sh::vk::spirv::kIdVec2 == sh::vk::spirv::kIdFloat + 1);
4356 static_assert(sh::vk::spirv::kIdVec3 == sh::vk::spirv::kIdFloat + 2);
4357 static_assert(sh::vk::spirv::kIdVec4 == sh::vk::spirv::kIdFloat + 3);
4358 ASSERT(componentCount <= 4);
4359 return spirv::IdRef(sh::vk::spirv::kIdFloat + (componentCount - 1));
4360 }
4361
4362 // Id of matrix attribute types. Note that only square matrices are possible as attributes in
4363 // GLSL ES 1.00.
matrixType(uint32_t dimension)4364 spirv::IdRef matrixType(uint32_t dimension)
4365 {
4366 static_assert(sh::vk::spirv::kIdMat3 == sh::vk::spirv::kIdMat2 + 1);
4367 static_assert(sh::vk::spirv::kIdMat4 == sh::vk::spirv::kIdMat2 + 2);
4368 ASSERT(dimension >= 2 && dimension <= 4);
4369 return spirv::IdRef(sh::vk::spirv::kIdMat2 + (dimension - 2));
4370 }
4371
4372 // Corresponding to floatType(), [i]: id of OpTypePointer Input %floatType(i). [0] is unused.
4373 std::array<spirv::IdRef, 5> mInputTypePointers;
4374
4375 // Corresponding to floatType(), [i]: id of OpTypePointer Private %floatType(i). [0] is
4376 // unused.
4377 std::array<spirv::IdRef, 5> mPrivateFloatTypePointers;
4378
4379 // Corresponding to matrixType(), [i]: id of OpTypePointer Private %matrixType(i). [0] and
4380 // [1] are unused.
4381 std::array<spirv::IdRef, 5> mPrivateMatrixTypePointers;
4382
4383 SpirvNonSemanticInstructions mNonSemanticInstructions;
4384 };
4385
transform()4386 void SpirvVertexAttributeAliasingTransformer::transform()
4387 {
4388 onTransformBegin();
4389
4390 preprocessAliasingAttributes();
4391
4392 while (mCurrentWord < mSpirvBlobIn.size())
4393 {
4394 transformInstruction();
4395 }
4396 }
4397
preprocessAliasingAttributes()4398 void SpirvVertexAttributeAliasingTransformer::preprocessAliasingAttributes()
4399 {
4400 const uint32_t indexBound = mSpirvBlobIn[spirv::kHeaderIndexIndexBound];
4401
4402 mVariableInfoById.resize(indexBound, nullptr);
4403 mIsAliasingAttributeById.resize(indexBound, false);
4404 mExpandedMatrixFirstVectorIdById.resize(indexBound);
4405
4406 // Go through attributes and find out which alias which.
4407 for (uint32_t idIndex = spirv::kMinValidId; idIndex < indexBound; ++idIndex)
4408 {
4409 const spirv::IdRef id(idIndex);
4410
4411 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4412
4413 // Ignore non attribute ids.
4414 if (info == nullptr || info->attributeComponentCount == 0)
4415 {
4416 continue;
4417 }
4418
4419 ASSERT(info->activeStages[gl::ShaderType::Vertex]);
4420 ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
4421
4422 const bool isMatrixAttribute = info->attributeLocationCount > 1;
4423
4424 for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
4425 {
4426 uint32_t location = info->location + offset;
4427 ASSERT(location < mAliasingAttributeMap.size());
4428
4429 spirv::IdRef attributeId(id);
4430
4431 // If this is a matrix attribute, expand it to vectors.
4432 if (isMatrixAttribute)
4433 {
4434 const spirv::IdRef matrixId(id);
4435
4436 // Get a new id for this location and associate it with the matrix.
4437 attributeId = getNewId();
4438 if (offset == 0)
4439 {
4440 mExpandedMatrixFirstVectorIdById[matrixId] = attributeId;
4441 }
4442 // The ids are consecutive.
4443 ASSERT(attributeId == mExpandedMatrixFirstVectorIdById[matrixId] + offset);
4444
4445 mIsAliasingAttributeById.resize(attributeId + 1, false);
4446 mVariableInfoById.resize(attributeId + 1, nullptr);
4447 mVariableInfoById[attributeId] = info;
4448 }
4449
4450 AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[location];
4451
4452 // If this is the first attribute in this location, remember it.
4453 if (!aliasingMap->attribute.valid())
4454 {
4455 aliasingMap->attribute = attributeId;
4456 continue;
4457 }
4458
4459 // Otherwise, either add it to the list of aliasing attributes, or replace the main
4460 // attribute (and add that to the list of aliasing attributes). The one with the
4461 // largest number of components is used as the main attribute.
4462 const ShaderInterfaceVariableInfo *curMainAttribute =
4463 mVariableInfoById[aliasingMap->attribute];
4464 ASSERT(curMainAttribute != nullptr && curMainAttribute->attributeComponentCount > 0);
4465
4466 spirv::IdRef aliasingId;
4467 if (info->attributeComponentCount > curMainAttribute->attributeComponentCount)
4468 {
4469 aliasingId = aliasingMap->attribute;
4470 aliasingMap->attribute = attributeId;
4471 }
4472 else
4473 {
4474 aliasingId = attributeId;
4475 }
4476
4477 aliasingMap->aliasingAttributes.push_back(aliasingId);
4478 ASSERT(!mIsAliasingAttributeById[aliasingId]);
4479 mIsAliasingAttributeById[aliasingId] = true;
4480 }
4481 }
4482 }
4483
transformInstruction()4484 void SpirvVertexAttributeAliasingTransformer::transformInstruction()
4485 {
4486 uint32_t wordCount;
4487 spv::Op opCode;
4488 const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);
4489
4490 if (opCode == spv::OpFunction)
4491 {
4492 // SPIR-V is structured in sections. Function declarations come last.
4493 mIsInFunctionSection = true;
4494 }
4495
4496 // Only look at interesting instructions.
4497 TransformationState transformationState = TransformationState::Unchanged;
4498
4499 if (mIsInFunctionSection)
4500 {
4501 // Look at in-function opcodes.
4502 switch (opCode)
4503 {
4504 case spv::OpExtInst:
4505 transformationState = transformExtInst(instruction);
4506 break;
4507 case spv::OpAccessChain:
4508 case spv::OpInBoundsAccessChain:
4509 transformationState = transformAccessChain(instruction);
4510 break;
4511 case spv::OpLoad:
4512 transformationState = transformLoad(instruction);
4513 break;
4514 default:
4515 break;
4516 }
4517 }
4518 else
4519 {
4520 // Look at global declaration opcodes.
4521 switch (opCode)
4522 {
4523 // Informational instructions:
4524 case spv::OpTypePointer:
4525 visitTypePointer(instruction);
4526 break;
4527 // Instructions that may need transformation:
4528 case spv::OpEntryPoint:
4529 transformationState = transformEntryPoint(instruction);
4530 break;
4531 case spv::OpExtInst:
4532 transformationState = transformExtInst(instruction);
4533 break;
4534 case spv::OpName:
4535 transformationState = transformName(instruction);
4536 break;
4537 case spv::OpDecorate:
4538 transformationState = transformDecorate(instruction);
4539 break;
4540 case spv::OpVariable:
4541 transformationState = transformVariable(instruction);
4542 break;
4543 default:
4544 break;
4545 }
4546 }
4547
4548 // If the instruction was not transformed, copy it to output as is.
4549 if (transformationState == TransformationState::Unchanged)
4550 {
4551 copyInstruction(instruction, wordCount);
4552 }
4553
4554 // Advance to next instruction.
4555 mCurrentWord += wordCount;
4556 }
4557
getAliasingAttributeReplacementId(spirv::IdRef aliasingId,uint32_t offset) const4558 spirv::IdRef SpirvVertexAttributeAliasingTransformer::getAliasingAttributeReplacementId(
4559 spirv::IdRef aliasingId,
4560 uint32_t offset) const
4561 {
4562 // Get variable info corresponding to the aliasing attribute.
4563 const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[aliasingId];
4564 ValidateShaderInterfaceVariableIsAttribute(aliasingInfo);
4565
4566 // Find the replacement attribute.
4567 const AliasingAttributeMap *aliasingMap =
4568 &mAliasingAttributeMap[aliasingInfo->location + offset];
4569 ValidateIsAliasingAttribute(aliasingMap, aliasingId);
4570
4571 const spirv::IdRef replacementId(aliasingMap->attribute);
4572 ASSERT(replacementId.valid() && replacementId < mIsAliasingAttributeById.size());
4573 ASSERT(!mIsAliasingAttributeById[replacementId]);
4574
4575 return replacementId;
4576 }
4577
isMatrixAttribute(spirv::IdRef id) const4578 bool SpirvVertexAttributeAliasingTransformer::isMatrixAttribute(spirv::IdRef id) const
4579 {
4580 return mExpandedMatrixFirstVectorIdById[id].valid();
4581 }
4582
visitTypePointer(const uint32_t * instruction)4583 void SpirvVertexAttributeAliasingTransformer::visitTypePointer(const uint32_t *instruction)
4584 {
4585 spirv::IdResult id;
4586 spv::StorageClass storageClass;
4587 spirv::IdRef typeId;
4588 spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);
4589
4590 // Only interested in OpTypePointer Input %vecN, where %vecN is the id of OpTypeVector %f32 N,
4591 // as well as OpTypePointer Private %matN, where %matN is the id of OpTypeMatrix %vecN N.
4592 // This is only for matN types (as allowed by GLSL ES 1.00), so N >= 2.
4593 if (storageClass == spv::StorageClassInput)
4594 {
4595 for (uint32_t n = 2; n <= 4; ++n)
4596 {
4597 if (typeId == floatType(n))
4598 {
4599 ASSERT(!mInputTypePointers[n].valid());
4600 mInputTypePointers[n] = id;
4601 break;
4602 }
4603 }
4604 }
4605 else if (storageClass == spv::StorageClassPrivate)
4606 {
4607 for (uint32_t n = 2; n <= 4; ++n)
4608 {
4609 // Note that Private types may not be unique, as the previous transformation can
4610 // generate duplicates.
4611 if (typeId == floatType(n))
4612 {
4613 mPrivateFloatTypePointers[n] = id;
4614 break;
4615 }
4616 if (typeId == matrixType(n))
4617 {
4618 mPrivateMatrixTypePointers[n] = id;
4619 break;
4620 }
4621 }
4622 }
4623 }
4624
transformEntryPoint(const uint32_t * instruction)4625 TransformationState SpirvVertexAttributeAliasingTransformer::transformEntryPoint(
4626 const uint32_t *instruction)
4627 {
4628 // Remove aliasing attributes from the shader interface declaration.
4629 spv::ExecutionModel executionModel;
4630 spirv::IdRef entryPointId;
4631 spirv::LiteralString name;
4632 spirv::IdRefList interfaceList;
4633 spirv::ParseEntryPoint(instruction, &executionModel, &entryPointId, &name, &interfaceList);
4634
4635 // Should only have one EntryPoint
4636 ASSERT(entryPointId == ID::EntryPoint);
4637
4638 // As a first pass, filter out matrix attributes and append their replacement vectors.
4639 size_t originalInterfaceListSize = interfaceList.size();
4640 for (size_t index = 0; index < originalInterfaceListSize; ++index)
4641 {
4642 const spirv::IdRef matrixId(interfaceList[index]);
4643
4644 if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
4645 {
4646 continue;
4647 }
4648
4649 const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
4650 ValidateShaderInterfaceVariableIsAttribute(info);
4651
4652 // Replace the matrix id with its first vector id.
4653 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
4654 interfaceList[index] = vec0Id;
4655
4656 // Append the rest of the vectors to the entry point.
4657 for (uint32_t offset = 1; offset < info->attributeLocationCount; ++offset)
4658 {
4659 const spirv::IdRef vecId(vec0Id + offset);
4660 interfaceList.push_back(vecId);
4661 }
4662
4663 // With SPIR-V 1.4, keep the Private variable in the interface list.
4664 if (entryPointList() == EntryPointList::GlobalVariables)
4665 {
4666 interfaceList.push_back(matrixId);
4667 }
4668 }
4669
4670 // Filter out aliasing attributes from entry point interface declaration.
4671 size_t writeIndex = 0;
4672 for (size_t index = 0; index < interfaceList.size(); ++index)
4673 {
4674 const spirv::IdRef id(interfaceList[index]);
4675
4676 // If this is an attribute that's aliasing another one in the same location, remove it.
4677 if (mIsAliasingAttributeById[id])
4678 {
4679 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4680 ValidateShaderInterfaceVariableIsAttribute(info);
4681
4682 // The following assertion is only valid for non-matrix attributes.
4683 if (info->attributeLocationCount == 1)
4684 {
4685 const AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[info->location];
4686 ValidateIsAliasingAttribute(aliasingMap, id);
4687 }
4688
4689 continue;
4690 }
4691
4692 interfaceList[writeIndex] = id;
4693 ++writeIndex;
4694 }
4695
4696 // Update the number of interface variables.
4697 interfaceList.resize_down(writeIndex);
4698
4699 // Write the entry point with the aliasing attributes removed.
4700 spirv::WriteEntryPoint(mSpirvBlobOut, executionModel, ID::EntryPoint, name, interfaceList);
4701
4702 return TransformationState::Transformed;
4703 }
4704
transformExtInst(const uint32_t * instruction)4705 TransformationState SpirvVertexAttributeAliasingTransformer::transformExtInst(
4706 const uint32_t *instruction)
4707 {
4708 sh::vk::spirv::NonSemanticInstruction inst;
4709 if (!mNonSemanticInstructions.visitExtInst(instruction, &inst))
4710 {
4711 return TransformationState::Unchanged;
4712 }
4713
4714 switch (inst)
4715 {
4716 case sh::vk::spirv::kNonSemanticOverview:
4717 // Declare the expanded matrix variables
4718 declareExpandedMatrixVectors();
4719 break;
4720 case sh::vk::spirv::kNonSemanticEnter:
4721 // The matrix attribute declarations have been changed to have Private storage class,
4722 // and they are initialized from the expanded (and potentially aliased) Input vectors.
4723 // This is done at the beginning of the entry point.
4724 writeExpandedMatrixInitialization();
4725 break;
4726 case sh::vk::spirv::kNonSemanticOutput:
4727 case sh::vk::spirv::kNonSemanticTransformFeedbackEmulation:
4728 // Unused by this transformation
4729 break;
4730 default:
4731 UNREACHABLE();
4732 break;
4733 }
4734
4735 // Drop the instruction if this is the last pass
4736 return mNonSemanticInstructions.transformExtInst(instruction);
4737 }
4738
transformName(const uint32_t * instruction)4739 TransformationState SpirvVertexAttributeAliasingTransformer::transformName(
4740 const uint32_t *instruction)
4741 {
4742 spirv::IdRef id;
4743 spirv::LiteralString name;
4744 spirv::ParseName(instruction, &id, &name);
4745
4746 // If id is not that of an aliasing attribute, there's nothing to do.
4747 ASSERT(id < mIsAliasingAttributeById.size());
4748 if (!mIsAliasingAttributeById[id])
4749 {
4750 return TransformationState::Unchanged;
4751 }
4752
4753 // Drop debug annotations for this id.
4754 return TransformationState::Transformed;
4755 }
4756
transformDecorate(const uint32_t * instruction)4757 TransformationState SpirvVertexAttributeAliasingTransformer::transformDecorate(
4758 const uint32_t *instruction)
4759 {
4760 spirv::IdRef id;
4761 spv::Decoration decoration;
4762 spirv::ParseDecorate(instruction, &id, &decoration, nullptr);
4763
4764 if (isMatrixAttribute(id))
4765 {
4766 // If it's a matrix attribute, it's expanded to multiple vectors. Insert the Location
4767 // decorations for these vectors here.
4768
4769 // Keep all decorations except for Location.
4770 if (decoration != spv::DecorationLocation)
4771 {
4772 return TransformationState::Unchanged;
4773 }
4774
4775 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4776 ValidateShaderInterfaceVariableIsAttribute(info);
4777
4778 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[id]);
4779 ASSERT(vec0Id.valid());
4780
4781 for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
4782 {
4783 const spirv::IdRef vecId(vec0Id + offset);
4784 if (mIsAliasingAttributeById[vecId])
4785 {
4786 continue;
4787 }
4788
4789 spirv::WriteDecorate(mSpirvBlobOut, vecId, decoration,
4790 {spirv::LiteralInteger(info->location + offset)});
4791 }
4792 }
4793 else
4794 {
4795 // If id is not that of an active attribute, there's nothing to do.
4796 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4797 if (info == nullptr || info->attributeComponentCount == 0 ||
4798 !info->activeStages[gl::ShaderType::Vertex])
4799 {
4800 return TransformationState::Unchanged;
4801 }
4802
4803 // Always drop RelaxedPrecision from input attributes. The temporary variable the attribute
4804 // is loaded into has RelaxedPrecision and will implicitly convert.
4805 if (decoration == spv::DecorationRelaxedPrecision)
4806 {
4807 return TransformationState::Transformed;
4808 }
4809
4810 // If id is not that of an aliasing attribute, there's nothing else to do.
4811 ASSERT(id < mIsAliasingAttributeById.size());
4812 if (!mIsAliasingAttributeById[id])
4813 {
4814 return TransformationState::Unchanged;
4815 }
4816 }
4817
4818 // Drop every decoration for this id.
4819 return TransformationState::Transformed;
4820 }
4821
transformVariable(const uint32_t * instruction)4822 TransformationState SpirvVertexAttributeAliasingTransformer::transformVariable(
4823 const uint32_t *instruction)
4824 {
4825 spirv::IdResultType typeId;
4826 spirv::IdResult id;
4827 spv::StorageClass storageClass;
4828 spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);
4829
4830 if (!isMatrixAttribute(id))
4831 {
4832 // If id is not that of an aliasing attribute, there's nothing to do. Note that matrix
4833 // declarations are always replaced.
4834 ASSERT(id < mIsAliasingAttributeById.size());
4835 if (!mIsAliasingAttributeById[id])
4836 {
4837 return TransformationState::Unchanged;
4838 }
4839 }
4840
4841 ASSERT(storageClass == spv::StorageClassInput);
4842
4843 // Drop the declaration.
4844 return TransformationState::Transformed;
4845 }
4846
transformAccessChain(const uint32_t * instruction)4847 TransformationState SpirvVertexAttributeAliasingTransformer::transformAccessChain(
4848 const uint32_t *instruction)
4849 {
4850 spirv::IdResultType typeId;
4851 spirv::IdResult id;
4852 spirv::IdRef baseId;
4853 spirv::IdRefList indexList;
4854 spirv::ParseAccessChain(instruction, &typeId, &id, &baseId, &indexList);
4855
4856 if (isMatrixAttribute(baseId))
4857 {
4858 // Write a modified OpAccessChain instruction. Only modification is that the %type is
4859 // replaced with the Private version of it. If there is one %index, that would be a vector
4860 // type, and if there are two %index'es, it's a float type.
4861 spirv::IdRef replacementTypeId;
4862
4863 if (indexList.size() == 1)
4864 {
4865 // If indexed once, it uses a vector type.
4866 const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
4867 ValidateShaderInterfaceVariableIsAttribute(info);
4868
4869 const uint32_t componentCount = info->attributeComponentCount;
4870
4871 // %type must have been the Input vector type with the matrice's component size.
4872 ASSERT(typeId == mInputTypePointers[componentCount]);
4873
4874 // Replace the type with the corresponding Private one.
4875 replacementTypeId = mPrivateFloatTypePointers[componentCount];
4876 }
4877 else
4878 {
4879 // If indexed twice, it uses the float type.
4880 ASSERT(indexList.size() == 2);
4881
4882 // Replace the type with the Private pointer to float32.
4883 replacementTypeId = mPrivateFloatTypePointers[1];
4884 }
4885
4886 spirv::WriteAccessChain(mSpirvBlobOut, replacementTypeId, id, baseId, indexList);
4887 }
4888 else
4889 {
4890 // If base id is not that of an aliasing attribute, there's nothing to do.
4891 ASSERT(baseId < mIsAliasingAttributeById.size());
4892 if (!mIsAliasingAttributeById[baseId])
4893 {
4894 return TransformationState::Unchanged;
4895 }
4896
4897 // Find the replacement attribute for the aliasing one.
4898 const spirv::IdRef replacementId(getAliasingAttributeReplacementId(baseId, 0));
4899
4900 // Get variable info corresponding to the replacement attribute.
4901 const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
4902 ValidateShaderInterfaceVariableIsAttribute(replacementInfo);
4903
4904 // Write a modified OpAccessChain instruction. Currently, the instruction is:
4905 //
4906 // %id = OpAccessChain %type %base %index
4907 //
4908 // This is modified to:
4909 //
4910 // %id = OpAccessChain %type %replacement %index
4911 //
4912 // Note that the replacement has at least as many components as the aliasing attribute,
4913 // and both attributes start at component 0 (GLSL ES restriction). So, indexing the
4914 // replacement attribute with the same index yields the same result and type.
4915 spirv::WriteAccessChain(mSpirvBlobOut, typeId, id, replacementId, indexList);
4916 }
4917
4918 return TransformationState::Transformed;
4919 }
4920
transformLoadHelper(spirv::IdRef pointerId,spirv::IdRef typeId,spirv::IdRef replacementId,spirv::IdRef resultId)4921 void SpirvVertexAttributeAliasingTransformer::transformLoadHelper(spirv::IdRef pointerId,
4922 spirv::IdRef typeId,
4923 spirv::IdRef replacementId,
4924 spirv::IdRef resultId)
4925 {
4926 // Get variable info corresponding to the replacement attribute.
4927 const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
4928 ValidateShaderInterfaceVariableIsAttribute(replacementInfo);
4929
4930 // Currently, the instruction is:
4931 //
4932 // %id = OpLoad %type %pointer
4933 //
4934 // This is modified to:
4935 //
4936 // %newId = OpLoad %replacementType %replacement
4937 //
4938 const spirv::IdRef loadResultId(getNewId());
4939 const spirv::IdRef replacementTypeId(floatType(replacementInfo->attributeComponentCount));
4940 ASSERT(replacementTypeId.valid());
4941
4942 spirv::WriteLoad(mSpirvBlobOut, replacementTypeId, loadResultId, replacementId, nullptr);
4943
4944 // If swizzle is not necessary, assign %newId to %resultId.
4945 const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[pointerId];
4946 if (aliasingInfo->attributeComponentCount == replacementInfo->attributeComponentCount)
4947 {
4948 spirv::WriteCopyObject(mSpirvBlobOut, typeId, resultId, loadResultId);
4949 return;
4950 }
4951
4952 // Take as many components from the replacement as the aliasing attribute wanted. This is done
4953 // by either of the following instructions:
4954 //
4955 // - If aliasing attribute has only one component:
4956 //
4957 // %resultId = OpCompositeExtract %floatType %newId 0
4958 //
4959 // - If aliasing attribute has more than one component:
4960 //
4961 // %resultId = OpVectorShuffle %vecType %newId %newId 0 1 ...
4962 //
4963 ASSERT(aliasingInfo->attributeComponentCount < replacementInfo->attributeComponentCount);
4964 ASSERT(floatType(aliasingInfo->attributeComponentCount) == typeId);
4965
4966 if (aliasingInfo->attributeComponentCount == 1)
4967 {
4968 spirv::WriteCompositeExtract(mSpirvBlobOut, typeId, resultId, loadResultId,
4969 {spirv::LiteralInteger(0)});
4970 }
4971 else
4972 {
4973 spirv::LiteralIntegerList swizzle = {spirv::LiteralInteger(0), spirv::LiteralInteger(1),
4974 spirv::LiteralInteger(2), spirv::LiteralInteger(3)};
4975 swizzle.resize_down(aliasingInfo->attributeComponentCount);
4976
4977 spirv::WriteVectorShuffle(mSpirvBlobOut, typeId, resultId, loadResultId, loadResultId,
4978 swizzle);
4979 }
4980 }
4981
transformLoad(const uint32_t * instruction)4982 TransformationState SpirvVertexAttributeAliasingTransformer::transformLoad(
4983 const uint32_t *instruction)
4984 {
4985 spirv::IdResultType typeId;
4986 spirv::IdResult id;
4987 spirv::IdRef pointerId;
4988 ParseLoad(instruction, &typeId, &id, &pointerId, nullptr);
4989
4990 // Currently, the instruction is:
4991 //
4992 // %id = OpLoad %type %pointer
4993 //
4994 // If non-matrix, this is modifed to load from the aliasing vector instead if aliasing.
4995 //
4996 // If matrix, this is modified such that %type points to the Private version of it.
4997 //
4998 if (isMatrixAttribute(pointerId))
4999 {
5000 const ShaderInterfaceVariableInfo *info = mVariableInfoById[pointerId];
5001 ValidateShaderInterfaceVariableIsAttribute(info);
5002
5003 const spirv::IdRef replacementTypeId(matrixType(info->attributeLocationCount));
5004
5005 spirv::WriteLoad(mSpirvBlobOut, replacementTypeId, id, pointerId, nullptr);
5006 }
5007 else
5008 {
5009 // If pointer id is not that of an aliasing attribute, there's nothing to do.
5010 ASSERT(pointerId < mIsAliasingAttributeById.size());
5011 if (!mIsAliasingAttributeById[pointerId])
5012 {
5013 return TransformationState::Unchanged;
5014 }
5015
5016 // Find the replacement attribute for the aliasing one.
5017 const spirv::IdRef replacementId(getAliasingAttributeReplacementId(pointerId, 0));
5018
5019 // Replace the load instruction by a load from the replacement id.
5020 transformLoadHelper(pointerId, typeId, replacementId, id);
5021 }
5022
5023 return TransformationState::Transformed;
5024 }
5025
declareExpandedMatrixVectors()5026 void SpirvVertexAttributeAliasingTransformer::declareExpandedMatrixVectors()
5027 {
5028 // Go through matrix attributes and expand them.
5029 for (uint32_t matrixIdIndex = spirv::kMinValidId;
5030 matrixIdIndex < mExpandedMatrixFirstVectorIdById.size(); ++matrixIdIndex)
5031 {
5032 const spirv::IdRef matrixId(matrixIdIndex);
5033
5034 if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
5035 {
5036 continue;
5037 }
5038
5039 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
5040
5041 const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
5042 ValidateShaderInterfaceVariableIsAttribute(info);
5043
5044 // Need to generate the following:
5045 //
5046 // %privateType = OpTypePointer Private %matrixType
5047 // %id = OpVariable %privateType Private
5048 // %vecType = OpTypePointer %vecType Input
5049 // %vec0 = OpVariable %vecType Input
5050 // ...
5051 // %vecN-1 = OpVariable %vecType Input
5052 const uint32_t componentCount = info->attributeComponentCount;
5053 const uint32_t locationCount = info->attributeLocationCount;
5054 ASSERT(componentCount == locationCount);
5055
5056 // OpTypePointer Private %matrixType
5057 spirv::IdRef privateType(mPrivateMatrixTypePointers[locationCount]);
5058 if (!privateType.valid())
5059 {
5060 privateType = getNewId();
5061 mPrivateMatrixTypePointers[locationCount] = privateType;
5062 spirv::WriteTypePointer(mSpirvBlobOut, privateType, spv::StorageClassPrivate,
5063 matrixType(locationCount));
5064 }
5065
5066 // OpVariable %privateType Private
5067 spirv::WriteVariable(mSpirvBlobOut, privateType, matrixId, spv::StorageClassPrivate,
5068 nullptr);
5069
5070 // If the OpTypePointer is not declared for the vector type corresponding to each location,
5071 // declare it now.
5072 //
5073 // %vecType = OpTypePointer %vecType Input
5074 spirv::IdRef inputType(mInputTypePointers[componentCount]);
5075 if (!inputType.valid())
5076 {
5077 inputType = getNewId();
5078 mInputTypePointers[componentCount] = inputType;
5079 spirv::WriteTypePointer(mSpirvBlobOut, inputType, spv::StorageClassInput,
5080 floatType(componentCount));
5081 }
5082
5083 // Declare a vector for each column of the matrix.
5084 for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
5085 {
5086 const spirv::IdRef vecId(vec0Id + offset);
5087 if (!mIsAliasingAttributeById[vecId])
5088 {
5089 spirv::WriteVariable(mSpirvBlobOut, inputType, vecId, spv::StorageClassInput,
5090 nullptr);
5091 }
5092 }
5093 }
5094
5095 // Additionally, declare OpTypePointer Private %floatType(i) in case needed (used in
5096 // Op*AccessChain instructions, if any).
5097 for (uint32_t n = 1; n <= 4; ++n)
5098 {
5099 if (!mPrivateFloatTypePointers[n].valid())
5100 {
5101 const spirv::IdRef privateType(getNewId());
5102 mPrivateFloatTypePointers[n] = privateType;
5103 spirv::WriteTypePointer(mSpirvBlobOut, privateType, spv::StorageClassPrivate,
5104 floatType(n));
5105 }
5106 }
5107 }
5108
writeExpandedMatrixInitialization()5109 void SpirvVertexAttributeAliasingTransformer::writeExpandedMatrixInitialization()
5110 {
5111 // Go through matrix attributes and initialize them. Note that their declaration is replaced
5112 // with a Private storage class, but otherwise has the same id.
5113 for (uint32_t matrixIdIndex = spirv::kMinValidId;
5114 matrixIdIndex < mExpandedMatrixFirstVectorIdById.size(); ++matrixIdIndex)
5115 {
5116 const spirv::IdRef matrixId(matrixIdIndex);
5117
5118 if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
5119 {
5120 continue;
5121 }
5122
5123 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
5124
5125 // For every matrix, need to generate the following:
5126 //
5127 // %vec0Id = OpLoad %vecType %vec0Pointer
5128 // ...
5129 // %vecN-1Id = OpLoad %vecType %vecN-1Pointer
5130 // %mat = OpCompositeConstruct %matrixType %vec0 ... %vecN-1
5131 // OpStore %matrixId %mat
5132
5133 const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
5134 ValidateShaderInterfaceVariableIsAttribute(info);
5135
5136 spirv::IdRefList vecLoadIds;
5137 const uint32_t locationCount = info->attributeLocationCount;
5138 for (uint32_t offset = 0; offset < locationCount; ++offset)
5139 {
5140 const spirv::IdRef vecId(vec0Id + offset);
5141
5142 // Load into temporary, potentially through an aliasing vector.
5143 spirv::IdRef replacementId(vecId);
5144 ASSERT(vecId < mIsAliasingAttributeById.size());
5145 if (mIsAliasingAttributeById[vecId])
5146 {
5147 replacementId = getAliasingAttributeReplacementId(vecId, offset);
5148 }
5149
5150 // Write a load instruction from the replacement id.
5151 vecLoadIds.push_back(getNewId());
5152 transformLoadHelper(matrixId, floatType(info->attributeComponentCount), replacementId,
5153 vecLoadIds.back());
5154 }
5155
5156 // Aggregate the vector loads into a matrix.
5157 const spirv::IdRef compositeId(getNewId());
5158 spirv::WriteCompositeConstruct(mSpirvBlobOut, matrixType(locationCount), compositeId,
5159 vecLoadIds);
5160
5161 // Store it in the private variable.
5162 spirv::WriteStore(mSpirvBlobOut, matrixId, compositeId, nullptr);
5163 }
5164 }
5165 } // anonymous namespace
5166
SpvCreateSourceOptions(const angle::FeaturesVk & features)5167 SpvSourceOptions SpvCreateSourceOptions(const angle::FeaturesVk &features)
5168 {
5169 SpvSourceOptions options;
5170
5171 options.supportsTransformFeedbackExtension =
5172 features.supportsTransformFeedbackExtension.enabled;
5173 options.supportsTransformFeedbackEmulation = features.emulateTransformFeedback.enabled;
5174 options.enableTransformFeedbackEmulation = options.supportsTransformFeedbackEmulation;
5175
5176 return options;
5177 }
5178
SpvGetXfbBufferBlockId(const uint32_t bufferIndex)5179 uint32_t SpvGetXfbBufferBlockId(const uint32_t bufferIndex)
5180 {
5181 ASSERT(bufferIndex < 4);
5182 static_assert(sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockOne ==
5183 sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + 1);
5184 static_assert(sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockTwo ==
5185 sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + 2);
5186 static_assert(sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockThree ==
5187 sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + 3);
5188
5189 return sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + bufferIndex;
5190 }
5191
SpvAssignLocations(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ProgramVaryingPacking & varyingPacking,const gl::ShaderType transformFeedbackStage,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)5192 void SpvAssignLocations(const SpvSourceOptions &options,
5193 const gl::ProgramExecutable &programExecutable,
5194 const gl::ProgramVaryingPacking &varyingPacking,
5195 const gl::ShaderType transformFeedbackStage,
5196 SpvProgramInterfaceInfo *programInterfaceInfo,
5197 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
5198 {
5199 const gl::ShaderBitSet shaderStages = programExecutable.getLinkedShaderStages();
5200
5201 // Assign outputs to the fragment shader, if any.
5202 if (shaderStages[gl::ShaderType::Fragment] &&
5203 programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment))
5204 {
5205 AssignOutputLocations(programExecutable, gl::ShaderType::Fragment, variableInfoMapOut);
5206 }
5207
5208 // Assign attributes to the vertex shader, if any.
5209 if (shaderStages[gl::ShaderType::Vertex] &&
5210 programExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex))
5211 {
5212 AssignAttributeLocations(programExecutable, gl::ShaderType::Vertex, variableInfoMapOut);
5213
5214 if (options.supportsTransformFeedbackEmulation)
5215 {
5216 // If transform feedback emulation is not enabled, mark all transform feedback output
5217 // buffers as inactive.
5218 const bool isTransformFeedbackStage =
5219 transformFeedbackStage == gl::ShaderType::Vertex &&
5220 options.enableTransformFeedbackEmulation &&
5221 !programExecutable.getLinkedTransformFeedbackVaryings().empty();
5222
5223 AssignTransformFeedbackEmulationBindings(gl::ShaderType::Vertex, programExecutable,
5224 isTransformFeedbackStage, programInterfaceInfo,
5225 variableInfoMapOut);
5226 }
5227 }
5228
5229 gl::ShaderType frontShaderType = gl::ShaderType::InvalidEnum;
5230 for (const gl::ShaderType shaderType : shaderStages)
5231 {
5232 if (programExecutable.hasLinkedGraphicsShader())
5233 {
5234 const gl::VaryingPacking &inputPacking = varyingPacking.getInputPacking(shaderType);
5235 const gl::VaryingPacking &outputPacking = varyingPacking.getOutputPacking(shaderType);
5236
5237 // Assign varying locations.
5238 if (shaderType != gl::ShaderType::Vertex)
5239 {
5240 AssignVaryingLocations(options, inputPacking, shaderType, frontShaderType,
5241 programInterfaceInfo, variableInfoMapOut);
5242
5243 // Record active members of in gl_PerVertex.
5244 if (shaderType != gl::ShaderType::Fragment &&
5245 frontShaderType != gl::ShaderType::InvalidEnum)
5246 {
5247 // If an output builtin is active in the previous stage, assume it's active in
5248 // the input of the current stage as well.
5249 const gl::ShaderMap<gl::PerVertexMemberBitSet> &outputPerVertexActiveMembers =
5250 inputPacking.getOutputPerVertexActiveMembers();
5251 variableInfoMapOut->setInputPerVertexActiveMembers(
5252 shaderType, outputPerVertexActiveMembers[frontShaderType]);
5253 }
5254 }
5255 if (shaderType != gl::ShaderType::Fragment)
5256 {
5257 AssignVaryingLocations(options, outputPacking, shaderType, frontShaderType,
5258 programInterfaceInfo, variableInfoMapOut);
5259
5260 // Record active members of out gl_PerVertex.
5261 const gl::ShaderMap<gl::PerVertexMemberBitSet> &outputPerVertexActiveMembers =
5262 outputPacking.getOutputPerVertexActiveMembers();
5263 variableInfoMapOut->setOutputPerVertexActiveMembers(
5264 shaderType, outputPerVertexActiveMembers[shaderType]);
5265 }
5266
5267 // Assign qualifiers to all varyings captured by transform feedback
5268 if (!programExecutable.getLinkedTransformFeedbackVaryings().empty() &&
5269 shaderType == programExecutable.getLinkedTransformFeedbackStage())
5270 {
5271 AssignTransformFeedbackQualifiers(programExecutable, outputPacking, shaderType,
5272 options.supportsTransformFeedbackExtension,
5273 variableInfoMapOut);
5274 }
5275 }
5276
5277 frontShaderType = shaderType;
5278 }
5279
5280 AssignUniformBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
5281 AssignTextureBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
5282 AssignNonTextureBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
5283 }
5284
SpvAssignTransformFeedbackLocations(gl::ShaderType shaderType,const gl::ProgramExecutable & programExecutable,bool isTransformFeedbackStage,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)5285 void SpvAssignTransformFeedbackLocations(gl::ShaderType shaderType,
5286 const gl::ProgramExecutable &programExecutable,
5287 bool isTransformFeedbackStage,
5288 SpvProgramInterfaceInfo *programInterfaceInfo,
5289 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
5290 {
5291 // The only varying that requires additional resources is gl_Position, as it's indirectly
5292 // captured through ANGLEXfbPosition.
5293
5294 const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
5295 programExecutable.getLinkedTransformFeedbackVaryings();
5296
5297 bool capturesPosition = false;
5298
5299 if (isTransformFeedbackStage)
5300 {
5301 for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
5302 {
5303 const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
5304 const std::string &tfVaryingName = tfVarying.name;
5305
5306 if (tfVaryingName == "gl_Position")
5307 {
5308 ASSERT(tfVarying.isBuiltIn());
5309 capturesPosition = true;
5310 break;
5311 }
5312 }
5313 }
5314
5315 if (capturesPosition)
5316 {
5317 AddLocationInfo(variableInfoMapOut, shaderType, sh::vk::spirv::kIdXfbExtensionPosition,
5318 programInterfaceInfo->locationsUsedForXfbExtension, 0, 0, 0);
5319 ++programInterfaceInfo->locationsUsedForXfbExtension;
5320 }
5321 else
5322 {
5323 // Make sure this varying is removed from the other stages, or if position is not captured
5324 // at all.
5325 variableInfoMapOut->add(shaderType, sh::vk::spirv::kIdXfbExtensionPosition);
5326 }
5327 }
5328
SpvGetShaderSpirvCode(const gl::ProgramState & programState,gl::ShaderMap<const spirv::Blob * > * spirvBlobsOut)5329 void SpvGetShaderSpirvCode(const gl::ProgramState &programState,
5330 gl::ShaderMap<const spirv::Blob *> *spirvBlobsOut)
5331 {
5332 for (const gl::ShaderType shaderType : gl::AllShaderTypes())
5333 {
5334 const gl::SharedCompiledShaderState &glShader = programState.getAttachedShader(shaderType);
5335 (*spirvBlobsOut)[shaderType] = glShader ? &glShader->compiledBinary : nullptr;
5336 }
5337 }
5338
SpvAssignAllLocations(const SpvSourceOptions & options,const gl::ProgramState & programState,const gl::ProgramLinkedResources & resources,ShaderInterfaceVariableInfoMap * variableInfoMapOut)5339 void SpvAssignAllLocations(const SpvSourceOptions &options,
5340 const gl::ProgramState &programState,
5341 const gl::ProgramLinkedResources &resources,
5342 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
5343 {
5344 SpvProgramInterfaceInfo spvProgramInterfaceInfo = {};
5345 const gl::ProgramExecutable &programExecutable = programState.getExecutable();
5346 gl::ShaderType xfbStage = programState.getAttachedTransformFeedbackStage();
5347
5348 // This should be done before assigning varying location. Otherwise, We can encounter shader
5349 // interface mismatching problem in case the transformFeedback stage is not Vertex stage.
5350 for (const gl::ShaderType shaderType : programExecutable.getLinkedShaderStages())
5351 {
5352 // Assign location to varyings generated for transform feedback capture
5353 const bool isXfbStage = shaderType == xfbStage &&
5354 !programExecutable.getLinkedTransformFeedbackVaryings().empty();
5355 if (options.supportsTransformFeedbackExtension &&
5356 gl::ShaderTypeSupportsTransformFeedback(shaderType))
5357 {
5358 SpvAssignTransformFeedbackLocations(shaderType, programExecutable, isXfbStage,
5359 &spvProgramInterfaceInfo, variableInfoMapOut);
5360 }
5361 }
5362
5363 SpvAssignLocations(options, programExecutable, resources.varyingPacking, xfbStage,
5364 &spvProgramInterfaceInfo, variableInfoMapOut);
5365 }
5366
SpvTransformSpirvCode(const SpvTransformOptions & options,const ShaderInterfaceVariableInfoMap & variableInfoMap,const spirv::Blob & initialSpirvBlob,spirv::Blob * spirvBlobOut)5367 angle::Result SpvTransformSpirvCode(const SpvTransformOptions &options,
5368 const ShaderInterfaceVariableInfoMap &variableInfoMap,
5369 const spirv::Blob &initialSpirvBlob,
5370 spirv::Blob *spirvBlobOut)
5371 {
5372 if (initialSpirvBlob.empty())
5373 {
5374 return angle::Result::Continue;
5375 }
5376
5377 const bool hasAliasingAttributes =
5378 options.shaderType == gl::ShaderType::Vertex && variableInfoMap.hasAliasingAttributes();
5379
5380 // Transform the SPIR-V code by assigning location/set/binding values.
5381 SpirvTransformer transformer(initialSpirvBlob, options, !hasAliasingAttributes, variableInfoMap,
5382 spirvBlobOut);
5383 transformer.transform();
5384
5385 // If there are aliasing vertex attributes, transform the SPIR-V again to remove them.
5386 if (hasAliasingAttributes)
5387 {
5388 spirv::Blob preTransformBlob = std::move(*spirvBlobOut);
5389 SpirvVertexAttributeAliasingTransformer aliasingTransformer(
5390 preTransformBlob, variableInfoMap, std::move(transformer.getVariableInfoByIdMap()),
5391 spirvBlobOut);
5392 aliasingTransformer.transform();
5393 }
5394
5395 spirvBlobOut->shrink_to_fit();
5396
5397 if (options.validate)
5398 {
5399 ASSERT(spirv::Validate(*spirvBlobOut));
5400 }
5401
5402 return angle::Result::Continue;
5403 }
5404 } // namespace rx
5405