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