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 const size_t variableCount = interfaceList->size();
1625 for (size_t index = 0; index < variableCount; ++index)
1626 {
1627 const spirv::IdRef id = (*interfaceList)[index];
1628 const spirv::IdRef replacementId = getReplacementId(id);
1629 if (replacementId != id)
1630 {
1631 if (entryPointList == EntryPointList::InterfaceVariables)
1632 {
1633 (*interfaceList)[index] = replacementId;
1634 }
1635 else
1636 {
1637 interfaceList->push_back(replacementId);
1638 }
1639 }
1640 }
1641 }
1642
addDecorate(spirv::IdRef replacedId,spirv::Blob * blobOut)1643 void SpirvVaryingPrecisionFixer::addDecorate(spirv::IdRef replacedId, spirv::Blob *blobOut)
1644 {
1645 spirv::WriteDecorate(blobOut, replacedId, spv::DecorationRelaxedPrecision, {});
1646 }
1647
writeOutputPrologue(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,gl::ShaderType shaderType,spirv::Blob * blobOut)1648 void SpirvVaryingPrecisionFixer::writeOutputPrologue(
1649 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1650 gl::ShaderType shaderType,
1651 spirv::Blob *blobOut)
1652 {
1653 if (shaderType == gl::ShaderType::Fragment || shaderType == gl::ShaderType::Compute)
1654 {
1655 return;
1656 }
1657
1658 // Copy from temp global variables with original precision to corrected varyings.
1659 for (uint32_t idIndex = spirv::kMinValidId; idIndex < variableInfoById.size(); idIndex++)
1660 {
1661 const spirv::IdRef id(idIndex);
1662 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
1663 if (info && info->useRelaxedPrecision && info->activeStages[shaderType] &&
1664 info->varyingIsOutput)
1665 {
1666 ASSERT(mFixedVaryingTypeId[id].valid());
1667
1668 // Build OpLoad instruction to load the highp value into a temporary
1669 const spirv::IdRef tempVar(SpirvTransformerBase::GetNewId(blobOut));
1670 const spirv::IdRef tempVarType(mTypePointerTypeId[mFixedVaryingTypeId[id]]);
1671 ASSERT(tempVarType.valid());
1672
1673 spirv::WriteLoad(blobOut, tempVarType, tempVar, id, nullptr);
1674
1675 // Build OpStore instruction to cast the highp value to mediump for output
1676 spirv::WriteStore(blobOut, mFixedVaryingId[id], tempVar, nullptr);
1677 }
1678 }
1679 }
1680
1681 // Helper class that generates code for transform feedback
1682 class SpirvTransformFeedbackCodeGenerator final : angle::NonCopyable
1683 {
1684 public:
SpirvTransformFeedbackCodeGenerator(const SpvTransformOptions & options)1685 SpirvTransformFeedbackCodeGenerator(const SpvTransformOptions &options)
1686 : mIsEmulated(options.isTransformFeedbackEmulated),
1687 mHasTransformFeedbackOutput(false),
1688 mIsPositionCapturedByTransformFeedbackExtension(false)
1689 {}
1690
1691 void visitVariable(const ShaderInterfaceVariableInfo &info,
1692 const XFBInterfaceVariableInfo &xfbInfo,
1693 gl::ShaderType shaderType,
1694 spirv::IdResultType typeId,
1695 spirv::IdResult id,
1696 spv::StorageClass storageClass);
1697
1698 TransformationState transformCapability(spv::Capability capability, spirv::Blob *blobOut);
1699 TransformationState transformDecorate(const ShaderInterfaceVariableInfo *info,
1700 gl::ShaderType shaderType,
1701 spirv::IdRef id,
1702 spv::Decoration decoration,
1703 const spirv::LiteralIntegerList &decorationValues,
1704 spirv::Blob *blobOut);
1705 TransformationState transformMemberDecorate(const ShaderInterfaceVariableInfo *info,
1706 gl::ShaderType shaderType,
1707 spirv::IdRef id,
1708 spirv::LiteralInteger member,
1709 spv::Decoration decoration,
1710 spirv::Blob *blobOut);
1711 TransformationState transformName(spirv::IdRef id, spirv::LiteralString name);
1712 TransformationState transformMemberName(spirv::IdRef id,
1713 spirv::LiteralInteger member,
1714 spirv::LiteralString name);
1715 TransformationState transformTypeStruct(const ShaderInterfaceVariableInfo *info,
1716 gl::ShaderType shaderType,
1717 spirv::IdResult id,
1718 const spirv::IdRefList &memberList,
1719 spirv::Blob *blobOut);
1720 TransformationState transformTypePointer(const ShaderInterfaceVariableInfo *info,
1721 gl::ShaderType shaderType,
1722 spirv::IdResult id,
1723 spv::StorageClass storageClass,
1724 spirv::IdRef typeId,
1725 spirv::Blob *blobOut);
1726 TransformationState transformVariable(const ShaderInterfaceVariableInfo &info,
1727 const ShaderInterfaceVariableInfoMap &variableInfoMap,
1728 gl::ShaderType shaderType,
1729 spv::StorageClass storageBufferStorageClass,
1730 spirv::IdResultType typeId,
1731 spirv::IdResult id,
1732 spv::StorageClass storageClass);
1733
1734 void modifyEntryPointInterfaceList(
1735 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1736 gl::ShaderType shaderType,
1737 EntryPointList entryPointList,
1738 spirv::IdRefList *interfaceList);
1739
1740 void writePendingDeclarations(
1741 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
1742 spv::StorageClass storageBufferStorageClass,
1743 spirv::Blob *blobOut);
1744 void writeTransformFeedbackExtensionOutput(spirv::IdRef positionId, spirv::Blob *blobOut);
1745 void writeTransformFeedbackEmulationOutput(
1746 const SpirvInactiveVaryingRemover &inactiveVaryingRemover,
1747 const SpirvVaryingPrecisionFixer &varyingPrecisionFixer,
1748 const bool usePrecisionFixer,
1749 spirv::Blob *blobOut);
1750 void addExecutionMode(spirv::IdRef entryPointId, spirv::Blob *blobOut);
1751 void addMemberDecorate(const XFBInterfaceVariableInfo &info,
1752 spirv::IdRef id,
1753 spirv::Blob *blobOut);
1754 void addDecorate(const XFBInterfaceVariableInfo &xfbInfo,
1755 spirv::IdRef id,
1756 spirv::Blob *blobOut);
1757
1758 private:
1759 void gatherXfbVaryings(const XFBInterfaceVariableInfo &info, spirv::IdRef id);
1760 void visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
1761 spirv::IdRef baseId,
1762 uint32_t fieldIndex);
1763 TransformationState transformTypeHelper(const ShaderInterfaceVariableInfo *info,
1764 gl::ShaderType shaderType,
1765 spirv::IdResult id);
1766 void writeIntConstant(uint32_t value, spirv::IdRef intId, spirv::Blob *blobOut);
1767 void getVaryingTypeIds(GLenum componentType,
1768 bool isPrivate,
1769 spirv::IdRef *typeIdOut,
1770 spirv::IdRef *typePtrOut);
1771 void writeGetOffsetsCall(spirv::IdRef xfbOffsets, spirv::Blob *blobOut);
1772 void writeComponentCapture(uint32_t bufferIndex,
1773 spirv::IdRef xfbOffset,
1774 spirv::IdRef varyingTypeId,
1775 spirv::IdRef varyingTypePtr,
1776 spirv::IdRef varyingBaseId,
1777 const spirv::IdRefList &accessChainIndices,
1778 GLenum componentType,
1779 spirv::Blob *blobOut);
1780
1781 static constexpr size_t kXfbDecorationCount = 3;
1782 static constexpr spv::Decoration kXfbDecorations[kXfbDecorationCount] = {
1783 spv::DecorationXfbBuffer,
1784 spv::DecorationXfbStride,
1785 spv::DecorationOffset,
1786 };
1787
1788 bool mIsEmulated;
1789 bool mHasTransformFeedbackOutput;
1790
1791 // Ids needed to generate transform feedback support code.
1792 bool mIsPositionCapturedByTransformFeedbackExtension;
1793 gl::TransformFeedbackBuffersArray<spirv::IdRef> mBufferStrides;
1794 spirv::IdRef mBufferStridesCompositeId;
1795
1796 // Type and constant ids:
1797 //
1798 // - mFloatOutputPointerId: id of OpTypePointer Output %kIdFloat
1799 // - mIntOutputPointerId: id of OpTypePointer Output %kIdInt
1800 // - mUintOutputPointerId: id of OpTypePointer Output %kIdUint
1801 // - mFloatPrivatePointerId, mIntPrivatePointerId, mUintPrivatePointerId: identical to the
1802 // above, but with the Private storage class. Used to load from varyings that have been
1803 // replaced as part of precision mismatch fixup.
1804 // - mFloatUniformPointerId: id of OpTypePointer Uniform %kIdFloat
1805 //
1806 // - mIntNIds[n]: id of OpConstant %kIdInt n
1807 spirv::IdRef mFloatOutputPointerId;
1808 spirv::IdRef mIntOutputPointerId;
1809 spirv::IdRef mUintOutputPointerId;
1810 spirv::IdRef mFloatPrivatePointerId;
1811 spirv::IdRef mIntPrivatePointerId;
1812 spirv::IdRef mUintPrivatePointerId;
1813 spirv::IdRef mFloatUniformPointerId;
1814
1815 // Id of constants such as row, column and array index. Integers 0, 1, 2 and 3 are always
1816 // defined by the compiler.
1817 angle::FastVector<spirv::IdRef, 4> mIntNIds;
1818
1819 // For transform feedback emulation, the captured elements are gathered in a list and sorted.
1820 // This allows the output generation code to always use offset += 1, thus relying on only one
1821 // constant (1).
1822 struct XfbVarying
1823 {
1824 // The varyings are sorted by info.offset.
1825 const ShaderInterfaceVariableXfbInfo *info;
1826 // Id of the base variable.
1827 spirv::IdRef baseId;
1828 // The field index, if a member of an I/O blocks
1829 uint32_t fieldIndex;
1830 };
1831 gl::TransformFeedbackBuffersArray<std::vector<XfbVarying>> mXfbVaryings;
1832 };
1833
1834 constexpr size_t SpirvTransformFeedbackCodeGenerator::kXfbDecorationCount;
1835 constexpr spv::Decoration SpirvTransformFeedbackCodeGenerator::kXfbDecorations[kXfbDecorationCount];
1836
visitVariable(const ShaderInterfaceVariableInfo & info,const XFBInterfaceVariableInfo & xfbInfo,gl::ShaderType shaderType,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass)1837 void SpirvTransformFeedbackCodeGenerator::visitVariable(const ShaderInterfaceVariableInfo &info,
1838 const XFBInterfaceVariableInfo &xfbInfo,
1839 gl::ShaderType shaderType,
1840 spirv::IdResultType typeId,
1841 spirv::IdResult id,
1842 spv::StorageClass storageClass)
1843 {
1844 if (mIsEmulated)
1845 {
1846 gatherXfbVaryings(xfbInfo, id);
1847 return;
1848 }
1849
1850 // Note if the variable is captured by transform feedback. In that case, the TransformFeedback
1851 // capability needs to be added.
1852 if ((xfbInfo.xfb.pod.buffer != ShaderInterfaceVariableInfo::kInvalid ||
1853 !xfbInfo.fieldXfb.empty()) &&
1854 info.activeStages[shaderType])
1855 {
1856 mHasTransformFeedbackOutput = true;
1857
1858 // If this is the special ANGLEXfbPosition variable, remember its id to be used for the
1859 // ANGLEXfbPosition = gl_Position; assignment code generation.
1860 if (id == ID::XfbExtensionPosition)
1861 {
1862 mIsPositionCapturedByTransformFeedbackExtension = true;
1863 }
1864 }
1865 }
1866
transformCapability(spv::Capability capability,spirv::Blob * blobOut)1867 TransformationState SpirvTransformFeedbackCodeGenerator::transformCapability(
1868 spv::Capability capability,
1869 spirv::Blob *blobOut)
1870 {
1871 if (!mHasTransformFeedbackOutput || mIsEmulated)
1872 {
1873 return TransformationState::Unchanged;
1874 }
1875
1876 // Transform feedback capability shouldn't have already been specified.
1877 ASSERT(capability != spv::CapabilityTransformFeedback);
1878
1879 // Vulkan shaders have either Shader, Geometry or Tessellation capability. We find this
1880 // capability, and add the TransformFeedback capability right before it.
1881 if (capability != spv::CapabilityShader && capability != spv::CapabilityGeometry &&
1882 capability != spv::CapabilityTessellation)
1883 {
1884 return TransformationState::Unchanged;
1885 }
1886
1887 // Write the TransformFeedback capability declaration.
1888 spirv::WriteCapability(blobOut, spv::CapabilityTransformFeedback);
1889
1890 // The original capability is retained.
1891 return TransformationState::Unchanged;
1892 }
1893
transformName(spirv::IdRef id,spirv::LiteralString name)1894 TransformationState SpirvTransformFeedbackCodeGenerator::transformName(spirv::IdRef id,
1895 spirv::LiteralString name)
1896 {
1897 // In the case of ANGLEXfbN, unconditionally remove the variable names. If transform
1898 // feedback is not active, the corresponding variables will be removed.
1899 switch (id)
1900 {
1901 case sh::vk::spirv::kIdXfbEmulationBufferBlockZero:
1902 case sh::vk::spirv::kIdXfbEmulationBufferBlockOne:
1903 case sh::vk::spirv::kIdXfbEmulationBufferBlockTwo:
1904 case sh::vk::spirv::kIdXfbEmulationBufferBlockThree:
1905 case sh::vk::spirv::kIdXfbEmulationBufferVarZero:
1906 case sh::vk::spirv::kIdXfbEmulationBufferVarOne:
1907 case sh::vk::spirv::kIdXfbEmulationBufferVarTwo:
1908 case sh::vk::spirv::kIdXfbEmulationBufferVarThree:
1909 return TransformationState::Transformed;
1910 default:
1911 return TransformationState::Unchanged;
1912 }
1913 }
1914
transformMemberName(spirv::IdRef id,spirv::LiteralInteger member,spirv::LiteralString name)1915 TransformationState SpirvTransformFeedbackCodeGenerator::transformMemberName(
1916 spirv::IdRef id,
1917 spirv::LiteralInteger member,
1918 spirv::LiteralString name)
1919 {
1920 switch (id)
1921 {
1922 case sh::vk::spirv::kIdXfbEmulationBufferBlockZero:
1923 case sh::vk::spirv::kIdXfbEmulationBufferBlockOne:
1924 case sh::vk::spirv::kIdXfbEmulationBufferBlockTwo:
1925 case sh::vk::spirv::kIdXfbEmulationBufferBlockThree:
1926 return TransformationState::Transformed;
1927 default:
1928 return TransformationState::Unchanged;
1929 }
1930 }
1931
transformTypeHelper(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdResult id)1932 TransformationState SpirvTransformFeedbackCodeGenerator::transformTypeHelper(
1933 const ShaderInterfaceVariableInfo *info,
1934 gl::ShaderType shaderType,
1935 spirv::IdResult id)
1936 {
1937 switch (id)
1938 {
1939 case sh::vk::spirv::kIdXfbEmulationBufferBlockZero:
1940 case sh::vk::spirv::kIdXfbEmulationBufferBlockOne:
1941 case sh::vk::spirv::kIdXfbEmulationBufferBlockTwo:
1942 case sh::vk::spirv::kIdXfbEmulationBufferBlockThree:
1943 ASSERT(info);
1944 return info->activeStages[shaderType] ? TransformationState::Unchanged
1945 : TransformationState::Transformed;
1946 default:
1947 return TransformationState::Unchanged;
1948 }
1949 }
1950
transformDecorate(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & decorationValues,spirv::Blob * blobOut)1951 TransformationState SpirvTransformFeedbackCodeGenerator::transformDecorate(
1952 const ShaderInterfaceVariableInfo *info,
1953 gl::ShaderType shaderType,
1954 spirv::IdRef id,
1955 spv::Decoration decoration,
1956 const spirv::LiteralIntegerList &decorationValues,
1957 spirv::Blob *blobOut)
1958 {
1959 return transformTypeHelper(info, shaderType, id);
1960 }
1961
transformMemberDecorate(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdRef id,spirv::LiteralInteger member,spv::Decoration decoration,spirv::Blob * blobOut)1962 TransformationState SpirvTransformFeedbackCodeGenerator::transformMemberDecorate(
1963 const ShaderInterfaceVariableInfo *info,
1964 gl::ShaderType shaderType,
1965 spirv::IdRef id,
1966 spirv::LiteralInteger member,
1967 spv::Decoration decoration,
1968 spirv::Blob *blobOut)
1969 {
1970 return transformTypeHelper(info, shaderType, id);
1971 }
1972
transformTypeStruct(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdResult id,const spirv::IdRefList & memberList,spirv::Blob * blobOut)1973 TransformationState SpirvTransformFeedbackCodeGenerator::transformTypeStruct(
1974 const ShaderInterfaceVariableInfo *info,
1975 gl::ShaderType shaderType,
1976 spirv::IdResult id,
1977 const spirv::IdRefList &memberList,
1978 spirv::Blob *blobOut)
1979 {
1980 return transformTypeHelper(info, shaderType, id);
1981 }
1982
transformTypePointer(const ShaderInterfaceVariableInfo * info,gl::ShaderType shaderType,spirv::IdResult id,spv::StorageClass storageClass,spirv::IdRef typeId,spirv::Blob * blobOut)1983 TransformationState SpirvTransformFeedbackCodeGenerator::transformTypePointer(
1984 const ShaderInterfaceVariableInfo *info,
1985 gl::ShaderType shaderType,
1986 spirv::IdResult id,
1987 spv::StorageClass storageClass,
1988 spirv::IdRef typeId,
1989 spirv::Blob *blobOut)
1990 {
1991 return transformTypeHelper(info, shaderType, typeId);
1992 }
1993
transformVariable(const ShaderInterfaceVariableInfo & info,const ShaderInterfaceVariableInfoMap & variableInfoMap,gl::ShaderType shaderType,spv::StorageClass storageBufferStorageClass,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass)1994 TransformationState SpirvTransformFeedbackCodeGenerator::transformVariable(
1995 const ShaderInterfaceVariableInfo &info,
1996 const ShaderInterfaceVariableInfoMap &variableInfoMap,
1997 gl::ShaderType shaderType,
1998 spv::StorageClass storageBufferStorageClass,
1999 spirv::IdResultType typeId,
2000 spirv::IdResult id,
2001 spv::StorageClass storageClass)
2002 {
2003 // This function is currently called for inactive variables.
2004 ASSERT(!info.activeStages[shaderType]);
2005
2006 if (shaderType == gl::ShaderType::Vertex && storageClass == storageBufferStorageClass)
2007 {
2008 // The ANGLEXfbN variables are unconditionally generated and may be inactive. Remove these
2009 // variables in that case.
2010 ASSERT(&info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(0)) ||
2011 &info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(1)) ||
2012 &info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(2)) ||
2013 &info == &variableInfoMap.getVariableById(shaderType, SpvGetXfbBufferBlockId(3)));
2014
2015 // Drop the declaration.
2016 return TransformationState::Transformed;
2017 }
2018
2019 return TransformationState::Unchanged;
2020 }
2021
gatherXfbVaryings(const XFBInterfaceVariableInfo & info,spirv::IdRef id)2022 void SpirvTransformFeedbackCodeGenerator::gatherXfbVaryings(const XFBInterfaceVariableInfo &info,
2023 spirv::IdRef id)
2024 {
2025 visitXfbVarying(info.xfb, id, ShaderInterfaceVariableXfbInfo::kInvalid);
2026
2027 for (size_t fieldIndex = 0; fieldIndex < info.fieldXfb.size(); ++fieldIndex)
2028 {
2029 visitXfbVarying(info.fieldXfb[fieldIndex], id, static_cast<uint32_t>(fieldIndex));
2030 }
2031 }
2032
visitXfbVarying(const ShaderInterfaceVariableXfbInfo & xfb,spirv::IdRef baseId,uint32_t fieldIndex)2033 void SpirvTransformFeedbackCodeGenerator::visitXfbVarying(const ShaderInterfaceVariableXfbInfo &xfb,
2034 spirv::IdRef baseId,
2035 uint32_t fieldIndex)
2036 {
2037 for (const ShaderInterfaceVariableXfbInfo &arrayElement : xfb.arrayElements)
2038 {
2039 visitXfbVarying(arrayElement, baseId, fieldIndex);
2040 }
2041
2042 if (xfb.pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
2043 {
2044 return;
2045 }
2046
2047 // Varyings captured to the same buffer have the same stride.
2048 ASSERT(mXfbVaryings[xfb.pod.buffer].empty() ||
2049 mXfbVaryings[xfb.pod.buffer][0].info->pod.stride == xfb.pod.stride);
2050
2051 mXfbVaryings[xfb.pod.buffer].push_back({&xfb, baseId, fieldIndex});
2052 }
2053
writeIntConstant(uint32_t value,spirv::IdRef intId,spirv::Blob * blobOut)2054 void SpirvTransformFeedbackCodeGenerator::writeIntConstant(uint32_t value,
2055 spirv::IdRef intId,
2056 spirv::Blob *blobOut)
2057 {
2058 if (value == ShaderInterfaceVariableXfbInfo::kInvalid)
2059 {
2060 return;
2061 }
2062
2063 if (mIntNIds.size() <= value)
2064 {
2065 // This member never resized down, so new elements can't have previous values.
2066 mIntNIds.resize_maybe_value_reuse(value + 1);
2067 }
2068 else if (mIntNIds[value].valid())
2069 {
2070 return;
2071 }
2072
2073 mIntNIds[value] = SpirvTransformerBase::GetNewId(blobOut);
2074 spirv::WriteConstant(blobOut, ID::Int, mIntNIds[value],
2075 spirv::LiteralContextDependentNumber(value));
2076 }
2077
modifyEntryPointInterfaceList(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,gl::ShaderType shaderType,EntryPointList entryPointList,spirv::IdRefList * interfaceList)2078 void SpirvTransformFeedbackCodeGenerator::modifyEntryPointInterfaceList(
2079 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2080 gl::ShaderType shaderType,
2081 EntryPointList entryPointList,
2082 spirv::IdRefList *interfaceList)
2083 {
2084 if (entryPointList == EntryPointList::GlobalVariables)
2085 {
2086 // Filter out unused xfb blocks from entry point interface declaration.
2087 size_t writeIndex = 0;
2088 for (size_t index = 0; index < interfaceList->size(); ++index)
2089 {
2090 spirv::IdRef id((*interfaceList)[index]);
2091 if (SpvIsXfbBufferBlockId(id))
2092 {
2093 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
2094 ASSERT(info);
2095
2096 if (!info->activeStages[shaderType])
2097 {
2098 continue;
2099 }
2100 }
2101
2102 (*interfaceList)[writeIndex] = id;
2103 ++writeIndex;
2104 }
2105
2106 // Update the number of interface variables.
2107 interfaceList->resize_down(writeIndex);
2108 }
2109 }
2110
writePendingDeclarations(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,spv::StorageClass storageBufferStorageClass,spirv::Blob * blobOut)2111 void SpirvTransformFeedbackCodeGenerator::writePendingDeclarations(
2112 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2113 spv::StorageClass storageBufferStorageClass,
2114 spirv::Blob *blobOut)
2115 {
2116 if (!mIsEmulated)
2117 {
2118 return;
2119 }
2120
2121 mFloatOutputPointerId = SpirvTransformerBase::GetNewId(blobOut);
2122 mFloatPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
2123 spirv::WriteTypePointer(blobOut, mFloatOutputPointerId, spv::StorageClassOutput, ID::Float);
2124 spirv::WriteTypePointer(blobOut, mFloatPrivatePointerId, spv::StorageClassPrivate, ID::Float);
2125
2126 mIntOutputPointerId = SpirvTransformerBase::GetNewId(blobOut);
2127 mIntPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
2128 spirv::WriteTypePointer(blobOut, mIntOutputPointerId, spv::StorageClassOutput, ID::Int);
2129 spirv::WriteTypePointer(blobOut, mIntPrivatePointerId, spv::StorageClassPrivate, ID::Int);
2130
2131 mUintOutputPointerId = SpirvTransformerBase::GetNewId(blobOut);
2132 mUintPrivatePointerId = SpirvTransformerBase::GetNewId(blobOut);
2133 spirv::WriteTypePointer(blobOut, mUintOutputPointerId, spv::StorageClassOutput, ID::Uint);
2134 spirv::WriteTypePointer(blobOut, mUintPrivatePointerId, spv::StorageClassPrivate, ID::Uint);
2135
2136 mFloatUniformPointerId = SpirvTransformerBase::GetNewId(blobOut);
2137 spirv::WriteTypePointer(blobOut, mFloatUniformPointerId, storageBufferStorageClass, ID::Float);
2138
2139 ASSERT(mIntNIds.empty());
2140 // All new elements initialized later after the resize. Additionally mIntNIds was always empty
2141 // before this resize, so previous value reuse is not possible.
2142 mIntNIds.resize_maybe_value_reuse(4);
2143 mIntNIds[0] = ID::IntZero;
2144 mIntNIds[1] = ID::IntOne;
2145 mIntNIds[2] = ID::IntTwo;
2146 mIntNIds[3] = ID::IntThree;
2147
2148 spirv::IdRefList compositeIds;
2149 for (const std::vector<XfbVarying> &varyings : mXfbVaryings)
2150 {
2151 if (varyings.empty())
2152 {
2153 compositeIds.push_back(ID::IntZero);
2154 continue;
2155 }
2156
2157 const ShaderInterfaceVariableXfbInfo *info0 = varyings[0].info;
2158
2159 // Define the buffer stride constant
2160 ASSERT(info0->pod.stride % sizeof(float) == 0);
2161 uint32_t stride = info0->pod.stride / sizeof(float);
2162
2163 mBufferStrides[info0->pod.buffer] = SpirvTransformerBase::GetNewId(blobOut);
2164 spirv::WriteConstant(blobOut, ID::Int, mBufferStrides[info0->pod.buffer],
2165 spirv::LiteralContextDependentNumber(stride));
2166
2167 compositeIds.push_back(mBufferStrides[info0->pod.buffer]);
2168
2169 // Define all the constants that would be necessary to load the components of the varying.
2170 for (const XfbVarying &varying : varyings)
2171 {
2172 writeIntConstant(varying.fieldIndex, ID::Int, blobOut);
2173 const ShaderInterfaceVariableXfbInfo *info = varying.info;
2174 if (info->pod.arraySize == ShaderInterfaceVariableXfbInfo::kInvalid)
2175 {
2176 continue;
2177 }
2178
2179 uint32_t arrayIndexStart =
2180 varying.info->pod.arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid
2181 ? varying.info->pod.arrayIndex
2182 : 0;
2183 uint32_t arrayIndexEnd = arrayIndexStart + info->pod.arraySize;
2184
2185 for (uint32_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
2186 {
2187 writeIntConstant(arrayIndex, ID::Int, blobOut);
2188 }
2189 }
2190 }
2191
2192 mBufferStridesCompositeId = SpirvTransformerBase::GetNewId(blobOut);
2193 spirv::WriteConstantComposite(blobOut, ID::IVec4, mBufferStridesCompositeId, compositeIds);
2194 }
2195
writeTransformFeedbackExtensionOutput(spirv::IdRef positionId,spirv::Blob * blobOut)2196 void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackExtensionOutput(
2197 spirv::IdRef positionId,
2198 spirv::Blob *blobOut)
2199 {
2200 if (mIsEmulated)
2201 {
2202 return;
2203 }
2204
2205 if (mIsPositionCapturedByTransformFeedbackExtension)
2206 {
2207 spirv::WriteStore(blobOut, ID::XfbExtensionPosition, positionId, nullptr);
2208 }
2209 }
2210
2211 class AccessChainIndexListAppend final : angle::NonCopyable
2212 {
2213 public:
AccessChainIndexListAppend(bool condition,angle::FastVector<spirv::IdRef,4> intNIds,uint32_t index,spirv::IdRefList * indexList)2214 AccessChainIndexListAppend(bool condition,
2215 angle::FastVector<spirv::IdRef, 4> intNIds,
2216 uint32_t index,
2217 spirv::IdRefList *indexList)
2218 : mCondition(condition), mIndexList(indexList)
2219 {
2220 if (mCondition)
2221 {
2222 mIndexList->push_back(intNIds[index]);
2223 }
2224 }
~AccessChainIndexListAppend()2225 ~AccessChainIndexListAppend()
2226 {
2227 if (mCondition)
2228 {
2229 mIndexList->pop_back();
2230 }
2231 }
2232
2233 private:
2234 bool mCondition;
2235 spirv::IdRefList *mIndexList;
2236 };
2237
writeTransformFeedbackEmulationOutput(const SpirvInactiveVaryingRemover & inactiveVaryingRemover,const SpirvVaryingPrecisionFixer & varyingPrecisionFixer,const bool usePrecisionFixer,spirv::Blob * blobOut)2238 void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackEmulationOutput(
2239 const SpirvInactiveVaryingRemover &inactiveVaryingRemover,
2240 const SpirvVaryingPrecisionFixer &varyingPrecisionFixer,
2241 const bool usePrecisionFixer,
2242 spirv::Blob *blobOut)
2243 {
2244 if (!mIsEmulated)
2245 {
2246 return;
2247 }
2248
2249 // First, sort the varyings by offset, to simplify calculation of the output offset.
2250 for (std::vector<XfbVarying> &varyings : mXfbVaryings)
2251 {
2252 std::sort(varyings.begin(), varyings.end(),
2253 [](const XfbVarying &first, const XfbVarying &second) {
2254 return first.info->pod.offset < second.info->pod.offset;
2255 });
2256 }
2257
2258 // The following code is generated for transform feedback emulation:
2259 //
2260 // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
2261 // // For buffer N:
2262 // int xfbOffset = xfbOffsets[N]
2263 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
2264 // xfbOffset += 1;
2265 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col + 1]
2266 // xfbOffset += 1;
2267 // ...
2268 //
2269 // The following pieces of SPIR-V code are generated according to the above:
2270 //
2271 // - For the initial offsets calculation:
2272 //
2273 // %xfbOffsetsResult = OpFunctionCall %ivec4 %ANGLEGetXfbOffsets %stridesComposite
2274 // %xfbOffsetsVar = OpVariable %kIdIVec4FunctionTypePointer Function
2275 // OpStore %xfbOffsetsVar %xfbOffsetsResult
2276 // %xfbOffsets = OpLoad %ivec4 %xfbOffsetsVar
2277 //
2278 // - Initial code for each buffer N:
2279 //
2280 // %xfbOffset = OpCompositeExtract %int %xfbOffsets N
2281 //
2282 // - For each varying being captured:
2283 //
2284 // // Load the component
2285 // %componentPtr = OpAccessChain %floatOutputPtr %baseId %field %arrayIndex %row %col
2286 // %component = OpLoad %float %componentPtr
2287 // // Store in xfb output
2288 // %xfbOutPtr = OpAccessChain %floatUniformPtr %xfbBufferN %int0 %xfbOffset
2289 // OpStore %xfbOutPtr %component
2290 // // Increment offset
2291 // %xfbOffset = OpIAdd %int %xfbOffset %int1
2292 //
2293 // Note that if the varying being captured is integer, the first two instructions above would
2294 // use the intger equivalent types, and the following instruction would bitcast it to float
2295 // for storage:
2296 //
2297 // %asFloat = OpBitcast %float %component
2298 //
2299
2300 spirv::IdRef xfbOffsets;
2301
2302 if (!mXfbVaryings.empty())
2303 {
2304 xfbOffsets = SpirvTransformerBase::GetNewId(blobOut);
2305
2306 // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
2307 writeGetOffsetsCall(xfbOffsets, blobOut);
2308 }
2309
2310 // Go over the buffers one by one and capture the varyings.
2311 for (uint32_t bufferIndex = 0; bufferIndex < mXfbVaryings.size(); ++bufferIndex)
2312 {
2313 spirv::IdRef xfbOffset(SpirvTransformerBase::GetNewId(blobOut));
2314
2315 // Get the offset corresponding to this buffer:
2316 //
2317 // int xfbOffset = xfbOffsets[N]
2318 spirv::WriteCompositeExtract(blobOut, ID::Int, xfbOffset, xfbOffsets,
2319 {spirv::LiteralInteger(bufferIndex)});
2320
2321 // Track offsets for verification.
2322 uint32_t offsetForVerification = 0;
2323
2324 // Go over the varyings of this buffer in order.
2325 const std::vector<XfbVarying> &varyings = mXfbVaryings[bufferIndex];
2326 for (size_t varyingIndex = 0; varyingIndex < varyings.size(); ++varyingIndex)
2327 {
2328 const XfbVarying &varying = varyings[varyingIndex];
2329 const ShaderInterfaceVariableXfbInfo *info = varying.info;
2330 ASSERT(info->pod.buffer == bufferIndex);
2331
2332 // Each component of the varying being captured is loaded one by one. This uses the
2333 // OpAccessChain instruction that takes a chain of "indices" to end up with the
2334 // component starting from the base variable. For example:
2335 //
2336 // var.member[3][2][0]
2337 //
2338 // where member is field number 4 in var and is a mat4, the access chain would be:
2339 //
2340 // 4 3 2 0
2341 // ^ ^ ^ ^
2342 // | | | |
2343 // | | | row 0
2344 // | | column 2
2345 // | array element 3
2346 // field 4
2347 //
2348 // The following tracks the access chain as the field, array elements, columns and rows
2349 // are looped over.
2350 spirv::IdRefList indexList;
2351 AccessChainIndexListAppend appendField(
2352 varying.fieldIndex != ShaderInterfaceVariableXfbInfo::kInvalid, mIntNIds,
2353 varying.fieldIndex, &indexList);
2354
2355 // The varying being captured is either:
2356 //
2357 // - Not an array: In this case, no entry is added in the access chain
2358 // - An element of the array
2359 // - The whole array
2360 //
2361 uint32_t arrayIndexStart = 0;
2362 uint32_t arrayIndexEnd = info->pod.arraySize;
2363 const bool isArray = info->pod.arraySize != ShaderInterfaceVariableXfbInfo::kInvalid;
2364 if (varying.info->pod.arrayIndex != ShaderInterfaceVariableXfbInfo::kInvalid)
2365 {
2366 // Capturing a single element.
2367 arrayIndexStart = varying.info->pod.arrayIndex;
2368 arrayIndexEnd = arrayIndexStart + 1;
2369 }
2370 else if (!isArray)
2371 {
2372 // Not an array.
2373 arrayIndexEnd = 1;
2374 }
2375
2376 // Sorting the varyings should have ensured that offsets are in order and that writing
2377 // to the output buffer sequentially ends up using the correct offsets.
2378 ASSERT(info->pod.offset == offsetForVerification);
2379 offsetForVerification += (arrayIndexEnd - arrayIndexStart) * info->pod.rowCount *
2380 info->pod.columnCount * sizeof(float);
2381
2382 // Determine the type of the component being captured. OpBitcast is used (the
2383 // implementation of intBitsToFloat() and uintBitsToFloat() for non-float types).
2384 spirv::IdRef varyingTypeId;
2385 spirv::IdRef varyingTypePtr;
2386 const bool isPrivate =
2387 inactiveVaryingRemover.isInactive(varying.baseId) ||
2388 (usePrecisionFixer && varyingPrecisionFixer.isReplaced(varying.baseId));
2389 getVaryingTypeIds(info->pod.componentType, isPrivate, &varyingTypeId, &varyingTypePtr);
2390
2391 for (uint32_t arrayIndex = arrayIndexStart; arrayIndex < arrayIndexEnd; ++arrayIndex)
2392 {
2393 AccessChainIndexListAppend appendArrayIndex(isArray, mIntNIds, arrayIndex,
2394 &indexList);
2395 for (uint32_t col = 0; col < info->pod.columnCount; ++col)
2396 {
2397 AccessChainIndexListAppend appendColumn(info->pod.columnCount > 1, mIntNIds,
2398 col, &indexList);
2399 for (uint32_t row = 0; row < info->pod.rowCount; ++row)
2400 {
2401 AccessChainIndexListAppend appendRow(info->pod.rowCount > 1, mIntNIds, row,
2402 &indexList);
2403
2404 // Generate the code to capture a single component of the varying:
2405 //
2406 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
2407 writeComponentCapture(bufferIndex, xfbOffset, varyingTypeId, varyingTypePtr,
2408 varying.baseId, indexList, info->pod.componentType,
2409 blobOut);
2410
2411 // Increment the offset:
2412 //
2413 // xfbOffset += 1;
2414 //
2415 // which translates to:
2416 //
2417 // %newOffsetId = OpIAdd %int %currentOffsetId %int1
2418 spirv::IdRef nextOffset(SpirvTransformerBase::GetNewId(blobOut));
2419 spirv::WriteIAdd(blobOut, ID::Int, nextOffset, xfbOffset, ID::IntOne);
2420 xfbOffset = nextOffset;
2421 }
2422 }
2423 }
2424 }
2425 }
2426 }
2427
getVaryingTypeIds(GLenum componentType,bool isPrivate,spirv::IdRef * typeIdOut,spirv::IdRef * typePtrOut)2428 void SpirvTransformFeedbackCodeGenerator::getVaryingTypeIds(GLenum componentType,
2429 bool isPrivate,
2430 spirv::IdRef *typeIdOut,
2431 spirv::IdRef *typePtrOut)
2432 {
2433 switch (componentType)
2434 {
2435 case GL_INT:
2436 *typeIdOut = ID::Int;
2437 *typePtrOut = isPrivate ? mIntPrivatePointerId : mIntOutputPointerId;
2438 break;
2439 case GL_UNSIGNED_INT:
2440 *typeIdOut = ID::Uint;
2441 *typePtrOut = isPrivate ? mUintPrivatePointerId : mUintOutputPointerId;
2442 break;
2443 case GL_FLOAT:
2444 *typeIdOut = ID::Float;
2445 *typePtrOut = isPrivate ? mFloatPrivatePointerId : mFloatOutputPointerId;
2446 break;
2447 default:
2448 UNREACHABLE();
2449 }
2450
2451 ASSERT(typeIdOut->valid());
2452 ASSERT(typePtrOut->valid());
2453 }
2454
writeGetOffsetsCall(spirv::IdRef xfbOffsets,spirv::Blob * blobOut)2455 void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOffsets,
2456 spirv::Blob *blobOut)
2457 {
2458 const spirv::IdRef xfbOffsetsResult(SpirvTransformerBase::GetNewId(blobOut));
2459 const spirv::IdRef xfbOffsetsVar(SpirvTransformerBase::GetNewId(blobOut));
2460
2461 // Generate code for the following:
2462 //
2463 // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
2464
2465 // Create a variable to hold the result.
2466 spirv::WriteVariable(blobOut, ID::IVec4FunctionTypePointer, xfbOffsetsVar,
2467 spv::StorageClassFunction, nullptr);
2468 // Call a helper function generated by the translator to calculate the offsets for the current
2469 // vertex.
2470 spirv::WriteFunctionCall(blobOut, ID::IVec4, xfbOffsetsResult,
2471 ID::XfbEmulationGetOffsetsFunction, {mBufferStridesCompositeId});
2472 // Store the results.
2473 spirv::WriteStore(blobOut, xfbOffsetsVar, xfbOffsetsResult, nullptr);
2474 // Load from the variable for use in expressions.
2475 spirv::WriteLoad(blobOut, ID::IVec4, xfbOffsets, xfbOffsetsVar, nullptr);
2476 }
2477
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)2478 void SpirvTransformFeedbackCodeGenerator::writeComponentCapture(
2479 uint32_t bufferIndex,
2480 spirv::IdRef xfbOffset,
2481 spirv::IdRef varyingTypeId,
2482 spirv::IdRef varyingTypePtr,
2483 spirv::IdRef varyingBaseId,
2484 const spirv::IdRefList &accessChainIndices,
2485 GLenum componentType,
2486 spirv::Blob *blobOut)
2487 {
2488 spirv::IdRef component(SpirvTransformerBase::GetNewId(blobOut));
2489 spirv::IdRef xfbOutPtr(SpirvTransformerBase::GetNewId(blobOut));
2490
2491 // Generate code for the following:
2492 //
2493 // ANGLEXfbN.xfbOut[xfbOffset] = tfVarying0.field[index][row][col]
2494
2495 // Load from the component traversing the base variable with the given indices. If there are no
2496 // indices, the variable can be loaded directly.
2497 spirv::IdRef loadPtr = varyingBaseId;
2498 if (!accessChainIndices.empty())
2499 {
2500 loadPtr = SpirvTransformerBase::GetNewId(blobOut);
2501 spirv::WriteAccessChain(blobOut, varyingTypePtr, loadPtr, varyingBaseId,
2502 accessChainIndices);
2503 }
2504 spirv::WriteLoad(blobOut, varyingTypeId, component, loadPtr, nullptr);
2505
2506 // If the varying is int or uint, bitcast it to float to store in the float[] array used to
2507 // capture transform feedback output.
2508 spirv::IdRef asFloat = component;
2509 if (componentType != GL_FLOAT)
2510 {
2511 asFloat = SpirvTransformerBase::GetNewId(blobOut);
2512 spirv::WriteBitcast(blobOut, ID::Float, asFloat, component);
2513 }
2514
2515 // Store into the transform feedback capture buffer at the current offset. Note that this
2516 // buffer has only one field (xfbOut), hence ANGLEXfbN.xfbOut[xfbOffset] translates to ANGLEXfbN
2517 // with access chain {0, xfbOffset}.
2518 static_assert(gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS == 4);
2519 static_assert(sh::vk::spirv::kIdXfbEmulationBufferVarOne ==
2520 sh::vk::spirv::kIdXfbEmulationBufferVarZero + 1);
2521 static_assert(sh::vk::spirv::kIdXfbEmulationBufferVarTwo ==
2522 sh::vk::spirv::kIdXfbEmulationBufferVarZero + 2);
2523 static_assert(sh::vk::spirv::kIdXfbEmulationBufferVarThree ==
2524 sh::vk::spirv::kIdXfbEmulationBufferVarZero + 3);
2525 spirv::WriteAccessChain(blobOut, mFloatUniformPointerId, xfbOutPtr,
2526 spirv::IdRef(sh::vk::spirv::kIdXfbEmulationBufferVarZero + bufferIndex),
2527 {ID::IntZero, xfbOffset});
2528 spirv::WriteStore(blobOut, xfbOutPtr, asFloat, nullptr);
2529 }
2530
addExecutionMode(spirv::IdRef entryPointId,spirv::Blob * blobOut)2531 void SpirvTransformFeedbackCodeGenerator::addExecutionMode(spirv::IdRef entryPointId,
2532 spirv::Blob *blobOut)
2533 {
2534 if (mIsEmulated)
2535 {
2536 return;
2537 }
2538
2539 if (mHasTransformFeedbackOutput)
2540 {
2541 spirv::WriteExecutionMode(blobOut, entryPointId, spv::ExecutionModeXfb, {});
2542 }
2543 }
2544
addMemberDecorate(const XFBInterfaceVariableInfo & info,spirv::IdRef id,spirv::Blob * blobOut)2545 void SpirvTransformFeedbackCodeGenerator::addMemberDecorate(const XFBInterfaceVariableInfo &info,
2546 spirv::IdRef id,
2547 spirv::Blob *blobOut)
2548 {
2549 if (mIsEmulated || info.fieldXfb.empty())
2550 {
2551 return;
2552 }
2553
2554 for (uint32_t fieldIndex = 0; fieldIndex < info.fieldXfb.size(); ++fieldIndex)
2555 {
2556 const ShaderInterfaceVariableXfbInfo &xfb = info.fieldXfb[fieldIndex];
2557
2558 if (xfb.pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
2559 {
2560 continue;
2561 }
2562
2563 ASSERT(xfb.pod.stride != ShaderInterfaceVariableXfbInfo::kInvalid);
2564 ASSERT(xfb.pod.offset != ShaderInterfaceVariableXfbInfo::kInvalid);
2565
2566 const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
2567 xfb.pod.buffer,
2568 xfb.pod.stride,
2569 xfb.pod.offset,
2570 };
2571
2572 // Generate the following three instructions:
2573 //
2574 // OpMemberDecorate %id fieldIndex XfbBuffer xfb.buffer
2575 // OpMemberDecorate %id fieldIndex XfbStride xfb.stride
2576 // OpMemberDecorate %id fieldIndex Offset xfb.offset
2577 for (size_t i = 0; i < kXfbDecorationCount; ++i)
2578 {
2579 spirv::WriteMemberDecorate(blobOut, id, spirv::LiteralInteger(fieldIndex),
2580 kXfbDecorations[i],
2581 {spirv::LiteralInteger(xfbDecorationValues[i])});
2582 }
2583 }
2584 }
2585
addDecorate(const XFBInterfaceVariableInfo & info,spirv::IdRef id,spirv::Blob * blobOut)2586 void SpirvTransformFeedbackCodeGenerator::addDecorate(const XFBInterfaceVariableInfo &info,
2587 spirv::IdRef id,
2588 spirv::Blob *blobOut)
2589 {
2590 if (mIsEmulated || info.xfb.pod.buffer == ShaderInterfaceVariableXfbInfo::kInvalid)
2591 {
2592 return;
2593 }
2594
2595 ASSERT(info.xfb.pod.stride != ShaderInterfaceVariableXfbInfo::kInvalid);
2596 ASSERT(info.xfb.pod.offset != ShaderInterfaceVariableXfbInfo::kInvalid);
2597
2598 const uint32_t xfbDecorationValues[kXfbDecorationCount] = {
2599 info.xfb.pod.buffer,
2600 info.xfb.pod.stride,
2601 info.xfb.pod.offset,
2602 };
2603
2604 // Generate the following three instructions:
2605 //
2606 // OpDecorate %id XfbBuffer xfb.buffer
2607 // OpDecorate %id XfbStride xfb.stride
2608 // OpDecorate %id Offset xfb.offset
2609 for (size_t i = 0; i < kXfbDecorationCount; ++i)
2610 {
2611 spirv::WriteDecorate(blobOut, id, kXfbDecorations[i],
2612 {spirv::LiteralInteger(xfbDecorationValues[i])});
2613 }
2614 }
2615
2616 // Helper class that generates code for gl_Position transformations
2617 class SpirvPositionTransformer final : angle::NonCopyable
2618 {
2619 public:
SpirvPositionTransformer(const SpvTransformOptions & options)2620 SpirvPositionTransformer(const SpvTransformOptions &options) : mOptions(options) {}
2621
2622 void writePositionTransformation(spirv::IdRef positionPointerId,
2623 spirv::IdRef positionId,
2624 spirv::Blob *blobOut);
2625
2626 private:
2627 SpvTransformOptions mOptions;
2628 };
2629
writePositionTransformation(spirv::IdRef positionPointerId,spirv::IdRef positionId,spirv::Blob * blobOut)2630 void SpirvPositionTransformer::writePositionTransformation(spirv::IdRef positionPointerId,
2631 spirv::IdRef positionId,
2632 spirv::Blob *blobOut)
2633 {
2634 // Generate the following SPIR-V for prerotation and depth transformation:
2635 //
2636 // // Transform position based on uniforms by making a call to the ANGLETransformPosition
2637 // // function that the translator has already provided.
2638 // %transformed = OpFunctionCall %kIdVec4 %kIdTransformPositionFunction %position
2639 //
2640 // // Store the results back in gl_Position
2641 // OpStore %PositionPointer %transformedPosition
2642 //
2643 const spirv::IdRef transformedPositionId(SpirvTransformerBase::GetNewId(blobOut));
2644
2645 spirv::WriteFunctionCall(blobOut, ID::Vec4, transformedPositionId,
2646 ID::TransformPositionFunction, {positionId});
2647 spirv::WriteStore(blobOut, positionPointerId, transformedPositionId, nullptr);
2648 }
2649
2650 // A transformation to handle both the isMultisampledFramebufferFetch and enableSampleShading
2651 // options. The common transformation between these two options is the addition of the
2652 // SampleRateShading capability.
2653 class SpirvMultisampleTransformer final : angle::NonCopyable
2654 {
2655 public:
SpirvMultisampleTransformer(const SpvTransformOptions & options)2656 SpirvMultisampleTransformer(const SpvTransformOptions &options)
2657 : mOptions(options), mSampleIDDecorationsAdded(false), mAnyImageTypesModified(false)
2658 {}
~SpirvMultisampleTransformer()2659 ~SpirvMultisampleTransformer()
2660 {
2661 ASSERT(!mOptions.isMultisampledFramebufferFetch || mAnyImageTypesModified);
2662 }
2663
2664 void init(size_t indexBound);
2665
2666 void visitDecorate(spirv::IdRef id,
2667 spv::Decoration decoration,
2668 const spirv::LiteralIntegerList &valueList);
2669
2670 void visitMemberDecorate(spirv::IdRef id,
2671 spirv::LiteralInteger member,
2672 spv::Decoration decoration);
2673
2674 void visitTypeStruct(spirv::IdResult id, const spirv::IdRefList &memberList);
2675
2676 void visitTypePointer(gl::ShaderType shaderType,
2677 spirv::IdResult id,
2678 spv::StorageClass storageClass,
2679 spirv::IdRef typeId);
2680
2681 void visitVariable(gl::ShaderType shaderType,
2682 spirv::IdResultType typeId,
2683 spirv::IdResult id,
2684 spv::StorageClass storageClass);
2685
2686 TransformationState transformCapability(const SpirvNonSemanticInstructions &nonSemantic,
2687 spv::Capability capability,
2688 spirv::Blob *blobOut);
2689
2690 TransformationState transformTypeImage(const uint32_t *instruction, spirv::Blob *blobOut);
2691
2692 void modifyEntryPointInterfaceList(const SpirvNonSemanticInstructions &nonSemantic,
2693 EntryPointList entryPointList,
2694 spirv::IdRefList *interfaceList,
2695 spirv::Blob *blobOut);
2696
2697 void writePendingDeclarations(
2698 const SpirvNonSemanticInstructions &nonSemantic,
2699 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2700 spirv::Blob *blobOut);
2701
2702 TransformationState transformDecorate(const SpirvNonSemanticInstructions &nonSemantic,
2703 const ShaderInterfaceVariableInfo &info,
2704 gl::ShaderType shaderType,
2705 spirv::IdRef id,
2706 spv::Decoration &decoration,
2707 spirv::Blob *blobOut);
2708
2709 TransformationState transformImageRead(const uint32_t *instruction, spirv::Blob *blobOut);
2710
2711 private:
2712 void visitVarying(gl::ShaderType shaderType, spirv::IdRef id, spv::StorageClass storageClass);
2713 bool skipSampleDecoration(spv::Decoration decoration);
2714
2715 SpvTransformOptions mOptions;
2716 // Used to assert that the transformation is not unnecessarily run.
2717 bool mSampleIDDecorationsAdded;
2718 bool mAnyImageTypesModified;
2719
2720 struct VaryingInfo
2721 {
2722 // Whether any variable is a varying
2723 bool isVarying = false;
2724 // Whether any variable or its members are already sample-, centroid- or flat-qualified.
2725 bool skipSampleDecoration = false;
2726 std::vector<bool> skipMemberSampleDecoration;
2727 };
2728 std::vector<VaryingInfo> mVaryingInfoById;
2729 };
2730
init(size_t indexBound)2731 void SpirvMultisampleTransformer::init(size_t indexBound)
2732 {
2733 mVaryingInfoById.resize(indexBound);
2734 }
2735
transformImageRead(const uint32_t * instruction,spirv::Blob * blobOut)2736 TransformationState SpirvMultisampleTransformer::transformImageRead(const uint32_t *instruction,
2737 spirv::Blob *blobOut)
2738 {
2739 // Transform the following:
2740 // %21 = OpImageRead %v4float %13 %20
2741 // to
2742 // %21 = OpImageRead %v4float %13 %20 Sample %17
2743 // where
2744 // %17 = OpLoad %int %gl_SampleID
2745
2746 if (!mOptions.isMultisampledFramebufferFetch)
2747 {
2748 return TransformationState::Unchanged;
2749 }
2750
2751 spirv::IdResultType idResultType;
2752 spirv::IdResult idResult;
2753 spirv::IdRef image;
2754 spirv::IdRef coordinate;
2755 spv::ImageOperandsMask imageOperands;
2756 spirv::IdRefList imageOperandIdsList;
2757
2758 spirv::ParseImageRead(instruction, &idResultType, &idResult, &image, &coordinate,
2759 &imageOperands, &imageOperandIdsList);
2760
2761 ASSERT(ID::Int.valid());
2762
2763 spirv::IdRef builtInSampleIDOpLoad = SpirvTransformerBase::GetNewId(blobOut);
2764
2765 spirv::WriteLoad(blobOut, ID::Int, builtInSampleIDOpLoad, ID::SampleID, nullptr);
2766
2767 imageOperands = spv::ImageOperandsMask::ImageOperandsSampleMask;
2768 imageOperandIdsList.push_back(builtInSampleIDOpLoad);
2769 spirv::WriteImageRead(blobOut, idResultType, idResult, image, coordinate, &imageOperands,
2770 imageOperandIdsList);
2771 return TransformationState::Transformed;
2772 }
2773
writePendingDeclarations(const SpirvNonSemanticInstructions & nonSemantic,const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,spirv::Blob * blobOut)2774 void SpirvMultisampleTransformer::writePendingDeclarations(
2775 const SpirvNonSemanticInstructions &nonSemantic,
2776 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
2777 spirv::Blob *blobOut)
2778 {
2779 // Add following declarations if they are not available yet
2780
2781 // %int = OpTypeInt 32 1
2782 // %_ptr_Input_int = OpTypePointer Input %int
2783 // %gl_SampleID = OpVariable %_ptr_Input_int Input
2784
2785 if (!mOptions.isMultisampledFramebufferFetch)
2786 {
2787 return;
2788 }
2789
2790 if (nonSemantic.hasSampleID())
2791 {
2792 return;
2793 }
2794
2795 spirv::WriteVariable(blobOut, ID::IntInputTypePointer, ID::SampleID, spv::StorageClassInput,
2796 nullptr);
2797 }
2798
transformTypeImage(const uint32_t * instruction,spirv::Blob * blobOut)2799 TransformationState SpirvMultisampleTransformer::transformTypeImage(const uint32_t *instruction,
2800 spirv::Blob *blobOut)
2801 {
2802 // Transform the following
2803 // %10 = OpTypeImage %float SubpassData 0 0 0 2
2804 // To
2805 // %10 = OpTypeImage %float SubpassData 0 0 1 2
2806
2807 if (!mOptions.isMultisampledFramebufferFetch)
2808 {
2809 return TransformationState::Unchanged;
2810 }
2811
2812 spirv::IdResult idResult;
2813 spirv::IdRef sampledType;
2814 spv::Dim dim;
2815 spirv::LiteralInteger depth;
2816 spirv::LiteralInteger arrayed;
2817 spirv::LiteralInteger ms;
2818 spirv::LiteralInteger sampled;
2819 spv::ImageFormat imageFormat;
2820 spv::AccessQualifier accessQualifier;
2821 spirv::ParseTypeImage(instruction, &idResult, &sampledType, &dim, &depth, &arrayed, &ms,
2822 &sampled, &imageFormat, &accessQualifier);
2823
2824 // Only transform input attachment image types.
2825 if (dim != spv::DimSubpassData)
2826 {
2827 return TransformationState::Unchanged;
2828 }
2829
2830 ms = spirv::LiteralInteger(1);
2831 spirv::WriteTypeImage(blobOut, idResult, sampledType, dim, depth, arrayed, ms, sampled,
2832 imageFormat, nullptr);
2833
2834 mAnyImageTypesModified = true;
2835
2836 return TransformationState::Transformed;
2837 }
2838
2839 namespace
2840 {
verifyEntryPointsContainsID(const spirv::IdRefList & interfaceList)2841 bool verifyEntryPointsContainsID(const spirv::IdRefList &interfaceList)
2842 {
2843 for (spirv::IdRef interfaceId : interfaceList)
2844 {
2845 if (interfaceId == ID::SampleID)
2846 {
2847 return true;
2848 }
2849 }
2850 return false;
2851 }
2852 } // namespace
2853
modifyEntryPointInterfaceList(const SpirvNonSemanticInstructions & nonSemantic,EntryPointList entryPointList,spirv::IdRefList * interfaceList,spirv::Blob * blobOut)2854 void SpirvMultisampleTransformer::modifyEntryPointInterfaceList(
2855 const SpirvNonSemanticInstructions &nonSemantic,
2856 EntryPointList entryPointList,
2857 spirv::IdRefList *interfaceList,
2858 spirv::Blob *blobOut)
2859 {
2860 // Append %gl_sampleID to OpEntryPoint
2861 // Transform the following
2862 //
2863 // OpEntryPoint Fragment %main "main" %_uo_color
2864 //
2865 // To
2866 //
2867 // OpEntryPoint Fragment %main "main" %_uo_color %gl_SampleID
2868
2869 if (!mOptions.isMultisampledFramebufferFetch)
2870 {
2871 return;
2872 }
2873
2874 // Nothing to do if the shader had already declared SampleID
2875 if (nonSemantic.hasSampleID())
2876 {
2877 ASSERT(verifyEntryPointsContainsID(*interfaceList));
2878 return;
2879 }
2880
2881 // Add the SampleID id to the interfaceList. The variable will later be decalred in
2882 // writePendingDeclarations.
2883 interfaceList->push_back(ID::SampleID);
2884 return;
2885 }
2886
transformCapability(const SpirvNonSemanticInstructions & nonSemantic,const spv::Capability capability,spirv::Blob * blobOut)2887 TransformationState SpirvMultisampleTransformer::transformCapability(
2888 const SpirvNonSemanticInstructions &nonSemantic,
2889 const spv::Capability capability,
2890 spirv::Blob *blobOut)
2891 {
2892 // Add a new OpCapability line:
2893 //
2894 // OpCapability SampleRateShading
2895 //
2896 // right before the following instruction
2897 //
2898 // OpCapability InputAttachment
2899
2900 if (!mOptions.isMultisampledFramebufferFetch && !mOptions.enableSampleShading)
2901 {
2902 return TransformationState::Unchanged;
2903 }
2904
2905 // Do not add the capability if the SPIR-V already has it
2906 if (nonSemantic.hasSampleRateShading())
2907 {
2908 return TransformationState::Unchanged;
2909 }
2910
2911 // Make sure no duplicates
2912 ASSERT(capability != spv::CapabilitySampleRateShading);
2913
2914 // Make sure we only add the new line on top of "OpCapability Shader"
2915 if (capability != spv::CapabilityShader)
2916 {
2917 return TransformationState::Unchanged;
2918 }
2919
2920 spirv::WriteCapability(blobOut, spv::CapabilitySampleRateShading);
2921
2922 // Leave the original OpCapability untouched
2923 return TransformationState::Unchanged;
2924 }
2925
transformDecorate(const SpirvNonSemanticInstructions & nonSemantic,const ShaderInterfaceVariableInfo & info,gl::ShaderType shaderType,spirv::IdRef id,spv::Decoration & decoration,spirv::Blob * blobOut)2926 TransformationState SpirvMultisampleTransformer::transformDecorate(
2927 const SpirvNonSemanticInstructions &nonSemantic,
2928 const ShaderInterfaceVariableInfo &info,
2929 gl::ShaderType shaderType,
2930 spirv::IdRef id,
2931 spv::Decoration &decoration,
2932 spirv::Blob *blobOut)
2933 {
2934 if (mOptions.isMultisampledFramebufferFetch && !nonSemantic.hasSampleID() &&
2935 !mSampleIDDecorationsAdded)
2936 {
2937 // Add the following instructions if they are not available yet:
2938 //
2939 // OpDecorate %gl_SampleID RelaxedPrecision
2940 // OpDecorate %gl_SampleID Flat
2941 // OpDecorate %gl_SampleID BuiltIn SampleId
2942
2943 spirv::WriteDecorate(blobOut, ID::SampleID, spv::DecorationRelaxedPrecision, {});
2944 spirv::WriteDecorate(blobOut, ID::SampleID, spv::DecorationFlat, {});
2945 spirv::WriteDecorate(blobOut, ID::SampleID, spv::DecorationBuiltIn,
2946 {spirv::LiteralInteger(spv::BuiltIn::BuiltInSampleId)});
2947
2948 mSampleIDDecorationsAdded = true;
2949 }
2950 if (mOptions.enableSampleShading && mVaryingInfoById[id].isVarying &&
2951 !mVaryingInfoById[id].skipSampleDecoration)
2952 {
2953 if (decoration == spv::DecorationLocation && info.activeStages[shaderType])
2954 {
2955 // Add the following instructions when the Location decoration is met, if the varying is
2956 // not already decorated with Sample:
2957 //
2958 // OpDecorate %id Sample
2959 spirv::WriteDecorate(blobOut, id, spv::DecorationSample, {});
2960 }
2961 else if (decoration == spv::DecorationBlock)
2962 {
2963 // Add the following instructions when the Block decoration is met, for any member that
2964 // is not already decorated with Sample:
2965 //
2966 // OpMemberDecorate %id member Sample
2967 for (uint32_t member = 0;
2968 member < mVaryingInfoById[id].skipMemberSampleDecoration.size(); ++member)
2969 {
2970 if (!mVaryingInfoById[id].skipMemberSampleDecoration[member])
2971 {
2972 spirv::WriteMemberDecorate(blobOut, id, spirv::LiteralInteger(member),
2973 spv::DecorationSample, {});
2974 }
2975 }
2976 }
2977 }
2978
2979 return TransformationState::Unchanged;
2980 }
2981
skipSampleDecoration(spv::Decoration decoration)2982 bool SpirvMultisampleTransformer::skipSampleDecoration(spv::Decoration decoration)
2983 {
2984 // If a variable is already decorated with Sample, Patch or Centroid, it shouldn't be decorated
2985 // with Sample. BuiltIns are also excluded.
2986 return decoration == spv::DecorationPatch || decoration == spv::DecorationCentroid ||
2987 decoration == spv::DecorationSample || decoration == spv::DecorationBuiltIn;
2988 }
2989
visitDecorate(spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & valueList)2990 void SpirvMultisampleTransformer::visitDecorate(spirv::IdRef id,
2991 spv::Decoration decoration,
2992 const spirv::LiteralIntegerList &valueList)
2993 {
2994 if (mOptions.enableSampleShading)
2995 {
2996 // Determine whether the id is already decorated with Sample.
2997 if (skipSampleDecoration(decoration))
2998 {
2999 mVaryingInfoById[id].skipSampleDecoration = true;
3000 }
3001 }
3002 return;
3003 }
3004
visitMemberDecorate(spirv::IdRef id,spirv::LiteralInteger member,spv::Decoration decoration)3005 void SpirvMultisampleTransformer::visitMemberDecorate(spirv::IdRef id,
3006 spirv::LiteralInteger member,
3007 spv::Decoration decoration)
3008 {
3009 if (!mOptions.enableSampleShading)
3010 {
3011 return;
3012 }
3013
3014 if (mVaryingInfoById[id].skipMemberSampleDecoration.size() <= member)
3015 {
3016 mVaryingInfoById[id].skipMemberSampleDecoration.resize(member + 1, false);
3017 }
3018 // Determine whether the member is already decorated with Sample.
3019 if (skipSampleDecoration(decoration))
3020 {
3021 mVaryingInfoById[id].skipMemberSampleDecoration[member] = true;
3022 }
3023 }
3024
visitVarying(gl::ShaderType shaderType,spirv::IdRef id,spv::StorageClass storageClass)3025 void SpirvMultisampleTransformer::visitVarying(gl::ShaderType shaderType,
3026 spirv::IdRef id,
3027 spv::StorageClass storageClass)
3028 {
3029 if (!mOptions.enableSampleShading)
3030 {
3031 return;
3032 }
3033
3034 // Vertex input and fragment output variables are not varyings and don't need to be decorated
3035 // with Sample.
3036 if ((shaderType == gl::ShaderType::Fragment && storageClass == spv::StorageClassOutput) ||
3037 (shaderType == gl::ShaderType::Vertex && storageClass == spv::StorageClassInput))
3038 {
3039 return;
3040 }
3041
3042 const bool isVarying =
3043 storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput;
3044 mVaryingInfoById[id].isVarying = isVarying;
3045 }
3046
visitTypeStruct(spirv::IdResult id,const spirv::IdRefList & memberList)3047 void SpirvMultisampleTransformer::visitTypeStruct(spirv::IdResult id,
3048 const spirv::IdRefList &memberList)
3049 {
3050 if (mOptions.enableSampleShading)
3051 {
3052 mVaryingInfoById[id].skipMemberSampleDecoration.resize(memberList.size(), false);
3053 }
3054 }
3055
visitTypePointer(gl::ShaderType shaderType,spirv::IdResult id,spv::StorageClass storageClass,spirv::IdRef typeId)3056 void SpirvMultisampleTransformer::visitTypePointer(gl::ShaderType shaderType,
3057 spirv::IdResult id,
3058 spv::StorageClass storageClass,
3059 spirv::IdRef typeId)
3060 {
3061 // For I/O blocks, the Sample decoration should be specified on the members of the struct type.
3062 // For that purpose, we consider the struct type as the varying instead.
3063 visitVarying(shaderType, typeId, storageClass);
3064 }
3065
visitVariable(gl::ShaderType shaderType,spirv::IdResultType typeId,spirv::IdResult id,spv::StorageClass storageClass)3066 void SpirvMultisampleTransformer::visitVariable(gl::ShaderType shaderType,
3067 spirv::IdResultType typeId,
3068 spirv::IdResult id,
3069 spv::StorageClass storageClass)
3070 {
3071 visitVarying(shaderType, id, storageClass);
3072 }
3073
3074 // Helper class that flattens secondary fragment output arrays.
3075 class SpirvSecondaryOutputTransformer final : angle::NonCopyable
3076 {
3077 public:
SpirvSecondaryOutputTransformer()3078 SpirvSecondaryOutputTransformer() {}
3079
3080 void init(size_t indexBound);
3081
3082 void visitTypeArray(spirv::IdResult id, spirv::IdRef typeId);
3083
3084 void visitTypePointer(spirv::IdResult id, spirv::IdRef typeId);
3085
3086 TransformationState transformAccessChain(spirv::IdResultType typeId,
3087 spirv::IdResult id,
3088 spirv::IdRef baseId,
3089 const spirv::IdRefList &indexList,
3090 spirv::Blob *blobOut);
3091
3092 TransformationState transformDecorate(spirv::IdRef id,
3093 spv::Decoration decoration,
3094 const spirv::LiteralIntegerList &decorationValues,
3095 spirv::Blob *blobOut);
3096
3097 TransformationState transformVariable(spirv::IdResultType typeId,
3098 spirv::IdResultType privateTypeId,
3099 spirv::IdResult id,
3100 spirv::Blob *blobOut);
3101
3102 void modifyEntryPointInterfaceList(
3103 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3104 EntryPointList entryPointList,
3105 spirv::IdRefList *interfaceList,
3106 spirv::Blob *blobOut);
3107
3108 void writeOutputPrologue(
3109 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3110 spirv::Blob *blobOut);
3111
3112 static_assert(gl::IMPLEMENTATION_MAX_DUAL_SOURCE_DRAW_BUFFERS == 1,
3113 "This transformer is incompatible with two or more dual-source draw buffers");
3114
3115 private:
visitTypeHelper(spirv::IdResult id,spirv::IdRef typeId)3116 void visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId) { mTypeCache[id] = typeId; }
3117
3118 // This list is filled during visitTypePointer and visitTypeArray steps,
3119 // to resolve the element type ID of the original output array variable.
3120 std::vector<spirv::IdRef> mTypeCache;
3121 spirv::IdRef mElementTypeId;
3122 spirv::IdRef mArrayVariableId;
3123 spirv::IdRef mReplacementVariableId;
3124 spirv::IdRef mElementPointerTypeId;
3125 };
3126
init(size_t indexBound)3127 void SpirvSecondaryOutputTransformer::init(size_t indexBound)
3128 {
3129 mTypeCache.resize(indexBound);
3130 }
3131
visitTypeArray(spirv::IdResult id,spirv::IdRef typeId)3132 void SpirvSecondaryOutputTransformer::visitTypeArray(spirv::IdResult id, spirv::IdRef typeId)
3133 {
3134 visitTypeHelper(id, typeId);
3135 }
3136
visitTypePointer(spirv::IdResult id,spirv::IdRef typeId)3137 void SpirvSecondaryOutputTransformer::visitTypePointer(spirv::IdResult id, spirv::IdRef typeId)
3138 {
3139 visitTypeHelper(id, typeId);
3140 }
3141
modifyEntryPointInterfaceList(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,EntryPointList entryPointList,spirv::IdRefList * interfaceList,spirv::Blob * blobOut)3142 void SpirvSecondaryOutputTransformer::modifyEntryPointInterfaceList(
3143 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3144 EntryPointList entryPointList,
3145 spirv::IdRefList *interfaceList,
3146 spirv::Blob *blobOut)
3147 {
3148 // Flatten a secondary output array (if any).
3149 for (size_t index = 0; index < interfaceList->size(); ++index)
3150 {
3151 const spirv::IdRef id((*interfaceList)[index]);
3152 const ShaderInterfaceVariableInfo *info = variableInfoById[id];
3153
3154 if (info == nullptr || info->index != 1 || !info->isArray)
3155 {
3156 continue;
3157 }
3158
3159 mArrayVariableId = id;
3160 mReplacementVariableId = SpirvTransformerBase::GetNewId(blobOut);
3161
3162 // With SPIR-V 1.3, modify interface list with the replacement ID.
3163 //
3164 // With SPIR-V 1.4, the original variable is changed to Private and should remain in the
3165 // list. The new variable should be added to the variable list.
3166 if (entryPointList == EntryPointList::InterfaceVariables)
3167 {
3168 (*interfaceList)[index] = mReplacementVariableId;
3169 }
3170 else
3171 {
3172 interfaceList->push_back(mReplacementVariableId);
3173 }
3174 break;
3175 }
3176 }
3177
transformAccessChain(spirv::IdResultType typeId,spirv::IdResult id,spirv::IdRef baseId,const spirv::IdRefList & indexList,spirv::Blob * blobOut)3178 TransformationState SpirvSecondaryOutputTransformer::transformAccessChain(
3179 spirv::IdResultType typeId,
3180 spirv::IdResult id,
3181 spirv::IdRef baseId,
3182 const spirv::IdRefList &indexList,
3183 spirv::Blob *blobOut)
3184 {
3185 if (baseId != mArrayVariableId)
3186 {
3187 return TransformationState::Unchanged;
3188 }
3189 ASSERT(typeId.valid());
3190 spirv::WriteAccessChain(blobOut, typeId, id, baseId, indexList);
3191 return TransformationState::Transformed;
3192 }
3193
transformDecorate(spirv::IdRef id,spv::Decoration decoration,const spirv::LiteralIntegerList & decorationValues,spirv::Blob * blobOut)3194 TransformationState SpirvSecondaryOutputTransformer::transformDecorate(
3195 spirv::IdRef id,
3196 spv::Decoration decoration,
3197 const spirv::LiteralIntegerList &decorationValues,
3198 spirv::Blob *blobOut)
3199 {
3200 if (id != mArrayVariableId)
3201 {
3202 return TransformationState::Unchanged;
3203 }
3204 ASSERT(mReplacementVariableId.valid());
3205 if (decoration == spv::DecorationLocation)
3206 {
3207 // Drop the Location decoration from the original variable and add
3208 // it together with an Index decoration to the replacement variable.
3209 spirv::WriteDecorate(blobOut, mReplacementVariableId, spv::DecorationLocation,
3210 {spirv::LiteralInteger(0)});
3211 spirv::WriteDecorate(blobOut, mReplacementVariableId, spv::DecorationIndex,
3212 {spirv::LiteralInteger(1)});
3213 }
3214 else
3215 {
3216 // Apply other decorations, such as RelaxedPrecision, to both variables.
3217 spirv::WriteDecorate(blobOut, id, decoration, decorationValues);
3218 spirv::WriteDecorate(blobOut, mReplacementVariableId, decoration, decorationValues);
3219 }
3220 return TransformationState::Transformed;
3221 }
3222
transformVariable(spirv::IdResultType typeId,spirv::IdResultType privateTypeId,spirv::IdResult id,spirv::Blob * blobOut)3223 TransformationState SpirvSecondaryOutputTransformer::transformVariable(
3224 spirv::IdResultType typeId,
3225 spirv::IdResultType privateTypeId,
3226 spirv::IdResult id,
3227 spirv::Blob *blobOut)
3228 {
3229 if (id != mArrayVariableId)
3230 {
3231 return TransformationState::Unchanged;
3232 }
3233
3234 // Change the original variable to use private storage.
3235 ASSERT(privateTypeId.valid());
3236 spirv::WriteVariable(blobOut, privateTypeId, id, spv::StorageClassPrivate, nullptr);
3237
3238 ASSERT(!mElementTypeId.valid());
3239 mElementTypeId = mTypeCache[mTypeCache[typeId]];
3240 ASSERT(mElementTypeId.valid());
3241
3242 // Pointer type for accessing the array element value.
3243 mElementPointerTypeId = SpirvTransformerBase::GetNewId(blobOut);
3244 spirv::WriteTypePointer(blobOut, mElementPointerTypeId, spv::StorageClassPrivate,
3245 mElementTypeId);
3246
3247 // Pointer type for the replacement output variable.
3248 const spirv::IdRef outputPointerTypeId(SpirvTransformerBase::GetNewId(blobOut));
3249 spirv::WriteTypePointer(blobOut, outputPointerTypeId, spv::StorageClassOutput, mElementTypeId);
3250
3251 ASSERT(mReplacementVariableId.valid());
3252 spirv::WriteVariable(blobOut, outputPointerTypeId, mReplacementVariableId,
3253 spv::StorageClassOutput, nullptr);
3254
3255 return TransformationState::Transformed;
3256 }
3257
writeOutputPrologue(const std::vector<const ShaderInterfaceVariableInfo * > & variableInfoById,spirv::Blob * blobOut)3258 void SpirvSecondaryOutputTransformer::writeOutputPrologue(
3259 const std::vector<const ShaderInterfaceVariableInfo *> &variableInfoById,
3260 spirv::Blob *blobOut)
3261 {
3262 if (mArrayVariableId.valid())
3263 {
3264 const spirv::IdRef accessChainId(SpirvTransformerBase::GetNewId(blobOut));
3265 spirv::WriteAccessChain(blobOut, mElementPointerTypeId, accessChainId, mArrayVariableId,
3266 {ID::IntZero});
3267
3268 ASSERT(mElementTypeId.valid());
3269 const spirv::IdRef loadId(SpirvTransformerBase::GetNewId(blobOut));
3270 spirv::WriteLoad(blobOut, mElementTypeId, loadId, accessChainId, nullptr);
3271
3272 ASSERT(mReplacementVariableId.valid());
3273 spirv::WriteStore(blobOut, mReplacementVariableId, loadId, nullptr);
3274 }
3275 }
3276
3277 // A SPIR-V transformer. It walks the instructions and modifies them as necessary, for example to
3278 // assign bindings or locations.
3279 class SpirvTransformer final : public SpirvTransformerBase
3280 {
3281 public:
SpirvTransformer(const spirv::Blob & spirvBlobIn,const SpvTransformOptions & options,bool isLastPass,const ShaderInterfaceVariableInfoMap & variableInfoMap,spirv::Blob * spirvBlobOut)3282 SpirvTransformer(const spirv::Blob &spirvBlobIn,
3283 const SpvTransformOptions &options,
3284 bool isLastPass,
3285 const ShaderInterfaceVariableInfoMap &variableInfoMap,
3286 spirv::Blob *spirvBlobOut)
3287 : SpirvTransformerBase(spirvBlobIn, variableInfoMap, spirvBlobOut),
3288 mOptions(options),
3289 mOverviewFlags(0),
3290 mNonSemanticInstructions(isLastPass),
3291 mPerVertexTrimmer(options, variableInfoMap),
3292 mXfbCodeGenerator(options),
3293 mPositionTransformer(options),
3294 mMultisampleTransformer(options)
3295 {}
3296
3297 void transform();
3298
3299 private:
3300 // A prepass to resolve interesting ids:
3301 void resolveVariableIds();
3302
3303 // Transform instructions:
3304 void transformInstruction();
3305
3306 // Instructions that are purely informational:
3307 void visitDecorate(const uint32_t *instruction);
3308 void visitMemberDecorate(const uint32_t *instruction);
3309 void visitTypeArray(const uint32_t *instruction);
3310 void visitTypePointer(const uint32_t *instruction);
3311 void visitTypeStruct(const uint32_t *instruction);
3312 void visitVariable(const uint32_t *instruction);
3313 void visitCapability(const uint32_t *instruction);
3314 bool visitExtInst(const uint32_t *instruction);
3315
3316 // Instructions that potentially need transformation. They return true if the instruction is
3317 // transformed. If false is returned, the instruction should be copied as-is.
3318 TransformationState transformAccessChain(const uint32_t *instruction);
3319 TransformationState transformCapability(const uint32_t *instruction);
3320 TransformationState transformEntryPoint(const uint32_t *instruction);
3321 TransformationState transformExtension(const uint32_t *instruction);
3322 TransformationState transformExtInstImport(const uint32_t *instruction);
3323 TransformationState transformExtInst(const uint32_t *instruction);
3324 TransformationState transformDecorate(const uint32_t *instruction);
3325 TransformationState transformMemberDecorate(const uint32_t *instruction);
3326 TransformationState transformName(const uint32_t *instruction);
3327 TransformationState transformMemberName(const uint32_t *instruction);
3328 TransformationState transformTypePointer(const uint32_t *instruction);
3329 TransformationState transformTypeStruct(const uint32_t *instruction);
3330 TransformationState transformVariable(const uint32_t *instruction);
3331 TransformationState transformTypeImage(const uint32_t *instruction);
3332 TransformationState transformImageRead(const uint32_t *instruction);
3333
3334 // Helpers:
3335 void visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId);
3336 void writePendingDeclarations();
3337 void writeInputPreamble();
3338 void writeOutputPrologue();
3339
3340 // Special flags:
3341 SpvTransformOptions mOptions;
3342
3343 // Traversal state:
3344 spirv::IdRef mCurrentFunctionId;
3345
3346 // Transformation state:
3347
3348 uint32_t mOverviewFlags;
3349 SpirvNonSemanticInstructions mNonSemanticInstructions;
3350 SpirvPerVertexTrimmer mPerVertexTrimmer;
3351 SpirvInactiveVaryingRemover mInactiveVaryingRemover;
3352 SpirvVaryingPrecisionFixer mVaryingPrecisionFixer;
3353 SpirvTransformFeedbackCodeGenerator mXfbCodeGenerator;
3354 SpirvPositionTransformer mPositionTransformer;
3355 SpirvMultisampleTransformer mMultisampleTransformer;
3356 SpirvSecondaryOutputTransformer mSecondaryOutputTransformer;
3357 };
3358
transform()3359 void SpirvTransformer::transform()
3360 {
3361 onTransformBegin();
3362
3363 // First, find all necessary ids and associate them with the information required to transform
3364 // their decorations. This is mostly derived from |mVariableInfoMap|, but may have additional
3365 // mappings; for example |mVariableInfoMap| maps an interface block's type ID to info, but the
3366 // transformer needs to discover the variable associated with that block and map it to the same
3367 // info.
3368 resolveVariableIds();
3369
3370 while (mCurrentWord < mSpirvBlobIn.size())
3371 {
3372 transformInstruction();
3373 }
3374 }
3375
resolveVariableIds()3376 void SpirvTransformer::resolveVariableIds()
3377 {
3378 const size_t indexBound = mSpirvBlobIn[spirv::kHeaderIndexIndexBound];
3379
3380 mInactiveVaryingRemover.init(indexBound);
3381 if (mOptions.useSpirvVaryingPrecisionFixer)
3382 {
3383 mVaryingPrecisionFixer.init(indexBound);
3384 }
3385 if (mOptions.isMultisampledFramebufferFetch || mOptions.enableSampleShading)
3386 {
3387 mMultisampleTransformer.init(indexBound);
3388 }
3389 if (mOptions.shaderType == gl::ShaderType::Fragment)
3390 {
3391 mSecondaryOutputTransformer.init(indexBound);
3392 }
3393
3394 // Allocate storage for id-to-info map. If %i is an id in mVariableInfoMap, index i in this
3395 // vector will hold a pointer to the ShaderInterfaceVariableInfo object associated with that
3396 // name in mVariableInfoMap.
3397 mVariableInfoById.resize(indexBound, nullptr);
3398
3399 // Pre-populate from mVariableInfoMap.
3400 {
3401 const ShaderInterfaceVariableInfoMap::VariableInfoArray &data = mVariableInfoMap.getData();
3402 const ShaderInterfaceVariableInfoMap::IdToIndexMap &idToIndexMap =
3403 mVariableInfoMap.getIdToIndexMap()[mOptions.shaderType];
3404
3405 for (uint32_t hashedId = 0; hashedId < idToIndexMap.size(); ++hashedId)
3406 {
3407 const uint32_t id = hashedId + sh::vk::spirv::kIdShaderVariablesBegin;
3408 const VariableIndex &variableIndex = idToIndexMap.at(hashedId);
3409 if (variableIndex.index == VariableIndex::kInvalid)
3410 {
3411 continue;
3412 }
3413
3414 const ShaderInterfaceVariableInfo &info = data[variableIndex.index];
3415
3416 ASSERT(id < mVariableInfoById.size());
3417 mVariableInfoById[id] = &info;
3418 }
3419 }
3420
3421 size_t currentWord = spirv::kHeaderIndexInstructions;
3422
3423 while (currentWord < mSpirvBlobIn.size())
3424 {
3425 const uint32_t *instruction = &mSpirvBlobIn[currentWord];
3426
3427 uint32_t wordCount;
3428 spv::Op opCode;
3429 spirv::GetInstructionOpAndLength(instruction, &opCode, &wordCount);
3430
3431 switch (opCode)
3432 {
3433 case spv::OpDecorate:
3434 visitDecorate(instruction);
3435 break;
3436 case spv::OpMemberDecorate:
3437 visitMemberDecorate(instruction);
3438 break;
3439 case spv::OpTypeArray:
3440 visitTypeArray(instruction);
3441 break;
3442 case spv::OpTypePointer:
3443 visitTypePointer(instruction);
3444 break;
3445 case spv::OpTypeStruct:
3446 visitTypeStruct(instruction);
3447 break;
3448 case spv::OpVariable:
3449 visitVariable(instruction);
3450 break;
3451 case spv::OpExtInst:
3452 if (visitExtInst(instruction))
3453 {
3454 return;
3455 }
3456 break;
3457 default:
3458 break;
3459 }
3460
3461 currentWord += wordCount;
3462 }
3463 UNREACHABLE();
3464 }
3465
transformInstruction()3466 void SpirvTransformer::transformInstruction()
3467 {
3468 uint32_t wordCount;
3469 spv::Op opCode;
3470 const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);
3471
3472 if (opCode == spv::OpFunction)
3473 {
3474 spirv::IdResultType id;
3475 spv::FunctionControlMask functionControl;
3476 spirv::IdRef functionType;
3477 spirv::ParseFunction(instruction, &id, &mCurrentFunctionId, &functionControl,
3478 &functionType);
3479
3480 // SPIR-V is structured in sections. Function declarations come last. Only a few
3481 // instructions such as Op*Access* or OpEmitVertex opcodes inside functions need to be
3482 // inspected.
3483 mIsInFunctionSection = true;
3484 }
3485
3486 // Only look at interesting instructions.
3487 TransformationState transformationState = TransformationState::Unchanged;
3488
3489 if (mIsInFunctionSection)
3490 {
3491 // Look at in-function opcodes.
3492 switch (opCode)
3493 {
3494 case spv::OpExtInst:
3495 transformationState = transformExtInst(instruction);
3496 break;
3497 case spv::OpAccessChain:
3498 case spv::OpInBoundsAccessChain:
3499 case spv::OpPtrAccessChain:
3500 case spv::OpInBoundsPtrAccessChain:
3501 transformationState = transformAccessChain(instruction);
3502 break;
3503 case spv::OpImageRead:
3504 transformationState = transformImageRead(instruction);
3505 break;
3506 default:
3507 break;
3508 }
3509 }
3510 else
3511 {
3512 // Look at global declaration opcodes.
3513 switch (opCode)
3514 {
3515 case spv::OpExtension:
3516 transformationState = transformExtension(instruction);
3517 break;
3518 case spv::OpExtInstImport:
3519 transformationState = transformExtInstImport(instruction);
3520 break;
3521 case spv::OpExtInst:
3522 transformationState = transformExtInst(instruction);
3523 break;
3524 case spv::OpName:
3525 transformationState = transformName(instruction);
3526 break;
3527 case spv::OpMemberName:
3528 transformationState = transformMemberName(instruction);
3529 break;
3530 case spv::OpCapability:
3531 transformationState = transformCapability(instruction);
3532 break;
3533 case spv::OpEntryPoint:
3534 transformationState = transformEntryPoint(instruction);
3535 break;
3536 case spv::OpDecorate:
3537 transformationState = transformDecorate(instruction);
3538 break;
3539 case spv::OpMemberDecorate:
3540 transformationState = transformMemberDecorate(instruction);
3541 break;
3542 case spv::OpTypeImage:
3543 transformationState = transformTypeImage(instruction);
3544 break;
3545 case spv::OpTypePointer:
3546 transformationState = transformTypePointer(instruction);
3547 break;
3548 case spv::OpTypeStruct:
3549 transformationState = transformTypeStruct(instruction);
3550 break;
3551 case spv::OpVariable:
3552 transformationState = transformVariable(instruction);
3553 break;
3554 default:
3555 break;
3556 }
3557 }
3558
3559 // If the instruction was not transformed, copy it to output as is.
3560 if (transformationState == TransformationState::Unchanged)
3561 {
3562 copyInstruction(instruction, wordCount);
3563 }
3564
3565 // Advance to next instruction.
3566 mCurrentWord += wordCount;
3567 }
3568
3569 // Called at the end of the declarations section. Any declarations that are necessary but weren't
3570 // present in the original shader need to be done here.
writePendingDeclarations()3571 void SpirvTransformer::writePendingDeclarations()
3572 {
3573 mMultisampleTransformer.writePendingDeclarations(mNonSemanticInstructions, mVariableInfoById,
3574 mSpirvBlobOut);
3575
3576 // Pre-rotation and transformation of depth to Vulkan clip space require declarations that may
3577 // not necessarily be in the shader. Transform feedback emulation additionally requires a few
3578 // overlapping ids.
3579 if (!mOptions.isLastPreFragmentStage)
3580 {
3581 return;
3582 }
3583
3584 if (mOptions.isTransformFeedbackStage)
3585 {
3586 mXfbCodeGenerator.writePendingDeclarations(mVariableInfoById, storageBufferStorageClass(),
3587 mSpirvBlobOut);
3588 }
3589 }
3590
3591 // Called by transformInstruction to insert necessary instructions for casting varyings.
writeInputPreamble()3592 void SpirvTransformer::writeInputPreamble()
3593 {
3594 if (mOptions.useSpirvVaryingPrecisionFixer)
3595 {
3596 mVaryingPrecisionFixer.writeInputPreamble(mVariableInfoById, mOptions.shaderType,
3597 mSpirvBlobOut);
3598 }
3599 }
3600
3601 // Called by transformInstruction to insert necessary instructions for casting varyings and
3602 // modifying gl_Position.
writeOutputPrologue()3603 void SpirvTransformer::writeOutputPrologue()
3604 {
3605 if (mOptions.useSpirvVaryingPrecisionFixer)
3606 {
3607 mVaryingPrecisionFixer.writeOutputPrologue(mVariableInfoById, mOptions.shaderType,
3608 mSpirvBlobOut);
3609 }
3610 if (mOptions.shaderType == gl::ShaderType::Fragment)
3611 {
3612 mSecondaryOutputTransformer.writeOutputPrologue(mVariableInfoById, mSpirvBlobOut);
3613 }
3614 if (!mNonSemanticInstructions.hasOutputPerVertex())
3615 {
3616 return;
3617 }
3618
3619 // Whether gl_Position should be transformed to account for pre-rotation and Vulkan clip space.
3620 const bool transformPosition = mOptions.isLastPreFragmentStage;
3621 const bool isXfbExtensionStage =
3622 mOptions.isTransformFeedbackStage && !mOptions.isTransformFeedbackEmulated;
3623 if (!transformPosition && !isXfbExtensionStage)
3624 {
3625 return;
3626 }
3627
3628 // Load gl_Position with the following SPIR-V:
3629 //
3630 // // Create an access chain to gl_PerVertex.gl_Position, which is always at index 0.
3631 // %PositionPointer = OpAccessChain %kIdVec4OutputTypePointer %kIdOutputPerVertexVar
3632 // %kIdIntZero
3633 // // Load gl_Position
3634 // %Position = OpLoad %kIdVec4 %PositionPointer
3635 //
3636 const spirv::IdRef positionPointerId(getNewId());
3637 const spirv::IdRef positionId(getNewId());
3638
3639 spirv::WriteAccessChain(mSpirvBlobOut, ID::Vec4OutputTypePointer, positionPointerId,
3640 ID::OutputPerVertexVar, {ID::IntZero});
3641 spirv::WriteLoad(mSpirvBlobOut, ID::Vec4, positionId, positionPointerId, nullptr);
3642
3643 // Write transform feedback output before modifying gl_Position.
3644 if (isXfbExtensionStage)
3645 {
3646 mXfbCodeGenerator.writeTransformFeedbackExtensionOutput(positionId, mSpirvBlobOut);
3647 }
3648
3649 if (transformPosition)
3650 {
3651 mPositionTransformer.writePositionTransformation(positionPointerId, positionId,
3652 mSpirvBlobOut);
3653 }
3654 }
3655
visitDecorate(const uint32_t * instruction)3656 void SpirvTransformer::visitDecorate(const uint32_t *instruction)
3657 {
3658 spirv::IdRef id;
3659 spv::Decoration decoration;
3660 spirv::LiteralIntegerList valueList;
3661 spirv::ParseDecorate(instruction, &id, &decoration, &valueList);
3662
3663 mMultisampleTransformer.visitDecorate(id, decoration, valueList);
3664 }
3665
visitMemberDecorate(const uint32_t * instruction)3666 void SpirvTransformer::visitMemberDecorate(const uint32_t *instruction)
3667 {
3668 spirv::IdRef typeId;
3669 spirv::LiteralInteger member;
3670 spv::Decoration decoration;
3671 spirv::LiteralIntegerList valueList;
3672 spirv::ParseMemberDecorate(instruction, &typeId, &member, &decoration, &valueList);
3673
3674 mPerVertexTrimmer.visitMemberDecorate(typeId, member, decoration, valueList);
3675 mMultisampleTransformer.visitMemberDecorate(typeId, member, decoration);
3676 }
3677
visitTypeHelper(spirv::IdResult id,spirv::IdRef typeId)3678 void SpirvTransformer::visitTypeHelper(spirv::IdResult id, spirv::IdRef typeId)
3679 {
3680 // Carry forward the mapping of typeId->info to id->info. For interface block, it's the block
3681 // id that is mapped to the info, so this is necessary to eventually be able to map the variable
3682 // itself to the info.
3683 mVariableInfoById[id] = mVariableInfoById[typeId];
3684 }
3685
visitTypeArray(const uint32_t * instruction)3686 void SpirvTransformer::visitTypeArray(const uint32_t *instruction)
3687 {
3688 spirv::IdResult id;
3689 spirv::IdRef elementType;
3690 spirv::IdRef length;
3691 spirv::ParseTypeArray(instruction, &id, &elementType, &length);
3692
3693 visitTypeHelper(id, elementType);
3694 if (mOptions.shaderType == gl::ShaderType::Fragment)
3695 {
3696 mSecondaryOutputTransformer.visitTypeArray(id, elementType);
3697 }
3698 }
3699
visitTypePointer(const uint32_t * instruction)3700 void SpirvTransformer::visitTypePointer(const uint32_t *instruction)
3701 {
3702 spirv::IdResult id;
3703 spv::StorageClass storageClass;
3704 spirv::IdRef typeId;
3705 spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);
3706
3707 visitTypeHelper(id, typeId);
3708 if (mOptions.useSpirvVaryingPrecisionFixer)
3709 {
3710 mVaryingPrecisionFixer.visitTypePointer(id, storageClass, typeId);
3711 }
3712 mMultisampleTransformer.visitTypePointer(mOptions.shaderType, id, storageClass, typeId);
3713 if (mOptions.shaderType == gl::ShaderType::Fragment)
3714 {
3715 mSecondaryOutputTransformer.visitTypePointer(id, typeId);
3716 }
3717 }
3718
visitTypeStruct(const uint32_t * instruction)3719 void SpirvTransformer::visitTypeStruct(const uint32_t *instruction)
3720 {
3721 spirv::IdResult id;
3722 spirv::IdRefList memberList;
3723 ParseTypeStruct(instruction, &id, &memberList);
3724
3725 mMultisampleTransformer.visitTypeStruct(id, memberList);
3726 }
3727
visitVariable(const uint32_t * instruction)3728 void SpirvTransformer::visitVariable(const uint32_t *instruction)
3729 {
3730 spirv::IdResultType typeId;
3731 spirv::IdResult id;
3732 spv::StorageClass storageClass;
3733 spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);
3734
3735 // If storage class indicates that this is not a shader interface variable, ignore it.
3736 const bool isInterfaceBlockVariable =
3737 storageClass == spv::StorageClassUniform || storageClass == spv::StorageClassStorageBuffer;
3738 const bool isOpaqueUniform = storageClass == spv::StorageClassUniformConstant;
3739 const bool isInOut =
3740 storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput;
3741
3742 if (!isInterfaceBlockVariable && !isOpaqueUniform && !isInOut)
3743 {
3744 return;
3745 }
3746
3747 // If no info is already associated with this id, carry that forward from the type. This
3748 // happens for interface blocks, where the id->info association is done on the type id.
3749 ASSERT(mVariableInfoById[id] == nullptr || mVariableInfoById[typeId] == nullptr);
3750 if (mVariableInfoById[id] == nullptr)
3751 {
3752 mVariableInfoById[id] = mVariableInfoById[typeId];
3753 }
3754
3755 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
3756
3757 // If this is an interface variable but no info is associated with it, it must be a built-in.
3758 if (info == nullptr)
3759 {
3760 // Make all builtins point to this no-op info. Adding this entry allows us to ASSERT that
3761 // every shader interface variable is processed during the SPIR-V transformation. This is
3762 // done when iterating the ids provided by OpEntryPoint.
3763 mVariableInfoById[id] = &mBuiltinVariableInfo;
3764 return;
3765 }
3766
3767 if (mOptions.useSpirvVaryingPrecisionFixer)
3768 {
3769 mVaryingPrecisionFixer.visitVariable(*info, mOptions.shaderType, typeId, id, storageClass,
3770 mSpirvBlobOut);
3771 }
3772 if (mOptions.isTransformFeedbackStage && mVariableInfoById[id]->hasTransformFeedback)
3773 {
3774 const XFBInterfaceVariableInfo &xfbInfo =
3775 mVariableInfoMap.getXFBDataForVariableInfo(mVariableInfoById[id]);
3776 mXfbCodeGenerator.visitVariable(*info, xfbInfo, mOptions.shaderType, typeId, id,
3777 storageClass);
3778 }
3779
3780 mMultisampleTransformer.visitVariable(mOptions.shaderType, typeId, id, storageClass);
3781 }
3782
visitExtInst(const uint32_t * instruction)3783 bool SpirvTransformer::visitExtInst(const uint32_t *instruction)
3784 {
3785 sh::vk::spirv::NonSemanticInstruction inst;
3786 if (!mNonSemanticInstructions.visitExtInst(instruction, &inst))
3787 {
3788 return false;
3789 }
3790
3791 switch (inst)
3792 {
3793 case sh::vk::spirv::kNonSemanticOverview:
3794 // SPIR-V is structured in sections (SPIR-V 1.0 Section 2.4 Logical Layout of a Module).
3795 // Names appear before decorations, which are followed by type+variables and finally
3796 // functions. We are only interested in name and variable declarations (as well as type
3797 // declarations for the sake of nameless interface blocks). Early out when the function
3798 // declaration section is met.
3799 //
3800 // This non-semantic instruction marks the beginning of the functions section.
3801 return true;
3802 default:
3803 UNREACHABLE();
3804 }
3805
3806 return false;
3807 }
3808
transformDecorate(const uint32_t * instruction)3809 TransformationState SpirvTransformer::transformDecorate(const uint32_t *instruction)
3810 {
3811 spirv::IdRef id;
3812 spv::Decoration decoration;
3813 spirv::LiteralIntegerList decorationValues;
3814 spirv::ParseDecorate(instruction, &id, &decoration, &decorationValues);
3815
3816 ASSERT(id < mVariableInfoById.size());
3817 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
3818
3819 // If variable is not a shader interface variable that needs modification, there's nothing to
3820 // do.
3821 if (info == nullptr)
3822 {
3823 return TransformationState::Unchanged;
3824 }
3825
3826 if (mOptions.shaderType == gl::ShaderType::Fragment)
3827 {
3828 // Handle decorations for the secondary fragment output array.
3829 if (mSecondaryOutputTransformer.transformDecorate(id, decoration, decorationValues,
3830 mSpirvBlobOut) ==
3831 TransformationState::Transformed)
3832 {
3833 return TransformationState::Transformed;
3834 }
3835 }
3836
3837 mMultisampleTransformer.transformDecorate(mNonSemanticInstructions, *info, mOptions.shaderType,
3838 id, decoration, mSpirvBlobOut);
3839
3840 if (mXfbCodeGenerator.transformDecorate(info, mOptions.shaderType, id, decoration,
3841 decorationValues,
3842 mSpirvBlobOut) == TransformationState::Transformed)
3843 {
3844 return TransformationState::Transformed;
3845 }
3846
3847 if (mInactiveVaryingRemover.transformDecorate(*info, mOptions.shaderType, id, decoration,
3848 decorationValues, mSpirvBlobOut) ==
3849 TransformationState::Transformed)
3850 {
3851 return TransformationState::Transformed;
3852 }
3853
3854 // If using relaxed precision, generate instructions for the replacement id instead.
3855 if (mOptions.useSpirvVaryingPrecisionFixer)
3856 {
3857 id = mVaryingPrecisionFixer.getReplacementId(id);
3858 }
3859
3860 uint32_t newDecorationValue = ShaderInterfaceVariableInfo::kInvalid;
3861
3862 switch (decoration)
3863 {
3864 case spv::DecorationLocation:
3865 newDecorationValue = info->location;
3866 break;
3867 case spv::DecorationBinding:
3868 newDecorationValue = info->binding;
3869 break;
3870 case spv::DecorationDescriptorSet:
3871 newDecorationValue = info->descriptorSet;
3872 break;
3873 case spv::DecorationFlat:
3874 case spv::DecorationNoPerspective:
3875 case spv::DecorationCentroid:
3876 case spv::DecorationSample:
3877 if (mOptions.useSpirvVaryingPrecisionFixer && info->useRelaxedPrecision)
3878 {
3879 // Change the id to replacement variable
3880 spirv::WriteDecorate(mSpirvBlobOut, id, decoration, decorationValues);
3881 return TransformationState::Transformed;
3882 }
3883 break;
3884 case spv::DecorationBlock:
3885 // If this is the Block decoration of a shader I/O block, add the transform feedback
3886 // decorations to its members right away.
3887 if (mOptions.isTransformFeedbackStage && info->hasTransformFeedback)
3888 {
3889 const XFBInterfaceVariableInfo &xfbInfo =
3890 mVariableInfoMap.getXFBDataForVariableInfo(info);
3891 mXfbCodeGenerator.addMemberDecorate(xfbInfo, id, mSpirvBlobOut);
3892 }
3893 break;
3894 case spv::DecorationInvariant:
3895 spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationInvariant, {});
3896 return TransformationState::Transformed;
3897 default:
3898 break;
3899 }
3900
3901 // If the decoration is not something we care about modifying, there's nothing to do.
3902 if (newDecorationValue == ShaderInterfaceVariableInfo::kInvalid)
3903 {
3904 return TransformationState::Unchanged;
3905 }
3906
3907 // Modify the decoration value.
3908 ASSERT(decorationValues.size() == 1);
3909 spirv::WriteDecorate(mSpirvBlobOut, id, decoration,
3910 {spirv::LiteralInteger(newDecorationValue)});
3911
3912 // If there are decorations to be added, add them right after the Location decoration is
3913 // encountered.
3914 if (decoration != spv::DecorationLocation)
3915 {
3916 return TransformationState::Transformed;
3917 }
3918
3919 // If any, the replacement variable is always reduced precision so add that decoration to
3920 // fixedVaryingId.
3921 if (mOptions.useSpirvVaryingPrecisionFixer && info->useRelaxedPrecision)
3922 {
3923 mVaryingPrecisionFixer.addDecorate(id, mSpirvBlobOut);
3924 }
3925
3926 // Add component decoration, if any.
3927 if (info->component != ShaderInterfaceVariableInfo::kInvalid)
3928 {
3929 spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationComponent,
3930 {spirv::LiteralInteger(info->component)});
3931 }
3932
3933 // Add index decoration, if any.
3934 if (info->index != ShaderInterfaceVariableInfo::kInvalid)
3935 {
3936 spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationIndex,
3937 {spirv::LiteralInteger(info->index)});
3938 }
3939
3940 // Add Xfb decorations, if any.
3941 if (mOptions.isTransformFeedbackStage && info->hasTransformFeedback)
3942 {
3943 const XFBInterfaceVariableInfo &xfbInfo = mVariableInfoMap.getXFBDataForVariableInfo(info);
3944 mXfbCodeGenerator.addDecorate(xfbInfo, id, mSpirvBlobOut);
3945 }
3946
3947 return TransformationState::Transformed;
3948 }
3949
transformMemberDecorate(const uint32_t * instruction)3950 TransformationState SpirvTransformer::transformMemberDecorate(const uint32_t *instruction)
3951 {
3952 spirv::IdRef typeId;
3953 spirv::LiteralInteger member;
3954 spv::Decoration decoration;
3955 spirv::ParseMemberDecorate(instruction, &typeId, &member, &decoration, nullptr);
3956
3957 if (mPerVertexTrimmer.transformMemberDecorate(typeId, member, decoration) ==
3958 TransformationState::Transformed)
3959 {
3960 return TransformationState::Transformed;
3961 }
3962
3963 ASSERT(typeId < mVariableInfoById.size());
3964 const ShaderInterfaceVariableInfo *info = mVariableInfoById[typeId];
3965
3966 return mXfbCodeGenerator.transformMemberDecorate(info, mOptions.shaderType, typeId, member,
3967 decoration, mSpirvBlobOut);
3968 }
3969
transformCapability(const uint32_t * instruction)3970 TransformationState SpirvTransformer::transformCapability(const uint32_t *instruction)
3971 {
3972 spv::Capability capability;
3973 spirv::ParseCapability(instruction, &capability);
3974
3975 TransformationState xfbTransformState =
3976 mXfbCodeGenerator.transformCapability(capability, mSpirvBlobOut);
3977 ASSERT(xfbTransformState == TransformationState::Unchanged);
3978
3979 TransformationState multiSampleTransformState = mMultisampleTransformer.transformCapability(
3980 mNonSemanticInstructions, capability, mSpirvBlobOut);
3981 ASSERT(multiSampleTransformState == TransformationState::Unchanged);
3982
3983 return TransformationState::Unchanged;
3984 }
3985
transformName(const uint32_t * instruction)3986 TransformationState SpirvTransformer::transformName(const uint32_t *instruction)
3987 {
3988 spirv::IdRef id;
3989 spirv::LiteralString name;
3990 spirv::ParseName(instruction, &id, &name);
3991
3992 return mXfbCodeGenerator.transformName(id, name);
3993 }
3994
transformMemberName(const uint32_t * instruction)3995 TransformationState SpirvTransformer::transformMemberName(const uint32_t *instruction)
3996 {
3997 spirv::IdRef id;
3998 spirv::LiteralInteger member;
3999 spirv::LiteralString name;
4000 spirv::ParseMemberName(instruction, &id, &member, &name);
4001
4002 if (mXfbCodeGenerator.transformMemberName(id, member, name) == TransformationState::Transformed)
4003 {
4004 return TransformationState::Transformed;
4005 }
4006
4007 return mPerVertexTrimmer.transformMemberName(id, member, name);
4008 }
4009
transformEntryPoint(const uint32_t * instruction)4010 TransformationState SpirvTransformer::transformEntryPoint(const uint32_t *instruction)
4011 {
4012 spv::ExecutionModel executionModel;
4013 spirv::IdRef entryPointId;
4014 spirv::LiteralString name;
4015 spirv::IdRefList interfaceList;
4016 spirv::ParseEntryPoint(instruction, &executionModel, &entryPointId, &name, &interfaceList);
4017
4018 // Should only have one EntryPoint
4019 ASSERT(entryPointId == ID::EntryPoint);
4020
4021 mInactiveVaryingRemover.modifyEntryPointInterfaceList(mVariableInfoById, mOptions.shaderType,
4022 entryPointList(), &interfaceList);
4023
4024 if (mOptions.shaderType == gl::ShaderType::Fragment)
4025 {
4026 mSecondaryOutputTransformer.modifyEntryPointInterfaceList(
4027 mVariableInfoById, entryPointList(), &interfaceList, mSpirvBlobOut);
4028 }
4029
4030 if (mOptions.useSpirvVaryingPrecisionFixer)
4031 {
4032 mVaryingPrecisionFixer.modifyEntryPointInterfaceList(entryPointList(), &interfaceList);
4033 }
4034
4035 mMultisampleTransformer.modifyEntryPointInterfaceList(
4036 mNonSemanticInstructions, entryPointList(), &interfaceList, mSpirvBlobOut);
4037 mXfbCodeGenerator.modifyEntryPointInterfaceList(mVariableInfoById, mOptions.shaderType,
4038 entryPointList(), &interfaceList);
4039
4040 // Write the entry point with the inactive interface variables removed.
4041 spirv::WriteEntryPoint(mSpirvBlobOut, executionModel, ID::EntryPoint, name, interfaceList);
4042
4043 // Add an OpExecutionMode Xfb instruction if necessary.
4044 mXfbCodeGenerator.addExecutionMode(ID::EntryPoint, mSpirvBlobOut);
4045
4046 return TransformationState::Transformed;
4047 }
4048
transformTypePointer(const uint32_t * instruction)4049 TransformationState SpirvTransformer::transformTypePointer(const uint32_t *instruction)
4050 {
4051 spirv::IdResult id;
4052 spv::StorageClass storageClass;
4053 spirv::IdRef typeId;
4054 spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);
4055
4056 if (mInactiveVaryingRemover.transformTypePointer(id, storageClass, typeId, mSpirvBlobOut) ==
4057 TransformationState::Transformed)
4058 {
4059 return TransformationState::Transformed;
4060 }
4061
4062 ASSERT(id < mVariableInfoById.size());
4063 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4064
4065 return mXfbCodeGenerator.transformTypePointer(info, mOptions.shaderType, id, storageClass,
4066 typeId, mSpirvBlobOut);
4067 }
4068
transformExtension(const uint32_t * instruction)4069 TransformationState SpirvTransformer::transformExtension(const uint32_t *instruction)
4070 {
4071 // Drop the OpExtension "SPV_KHR_non_semantic_info" extension instruction.
4072 // SPV_KHR_non_semantic_info is used purely as a means of communication between the compiler and
4073 // the SPIR-V transformer, and is stripped away before the SPIR-V is handed off to the driver.
4074 spirv::LiteralString name;
4075 spirv::ParseExtension(instruction, &name);
4076
4077 return strcmp(name, "SPV_KHR_non_semantic_info") == 0 ? TransformationState::Transformed
4078 : TransformationState::Unchanged;
4079 }
4080
transformExtInstImport(const uint32_t * instruction)4081 TransformationState SpirvTransformer::transformExtInstImport(const uint32_t *instruction)
4082 {
4083 // Drop the OpExtInstImport "NonSemantic.ANGLE" instruction.
4084 spirv::IdResult id;
4085 spirv::LiteralString name;
4086 ParseExtInstImport(instruction, &id, &name);
4087
4088 return id == sh::vk::spirv::kIdNonSemanticInstructionSet ? TransformationState::Transformed
4089 : TransformationState::Unchanged;
4090 }
4091
transformExtInst(const uint32_t * instruction)4092 TransformationState SpirvTransformer::transformExtInst(const uint32_t *instruction)
4093 {
4094 sh::vk::spirv::NonSemanticInstruction inst;
4095 if (!mNonSemanticInstructions.visitExtInst(instruction, &inst))
4096 {
4097 return TransformationState::Unchanged;
4098 }
4099
4100 switch (inst)
4101 {
4102 case sh::vk::spirv::kNonSemanticOverview:
4103 // Declare anything that we need but didn't find there already.
4104 writePendingDeclarations();
4105 break;
4106 case sh::vk::spirv::kNonSemanticEnter:
4107 // If there are any precision mismatches that need to be handled, temporary global
4108 // variables are created with the original precision. Initialize those variables from
4109 // the varyings at the beginning of the shader.
4110 writeInputPreamble();
4111 break;
4112 case sh::vk::spirv::kNonSemanticOutput:
4113 // Generate gl_Position transformations and transform feedback capture (through
4114 // extension) before return or EmitVertex(). Additionally, if there are any precision
4115 // mismatches that need to be ahendled, write the temporary variables that hold varyings
4116 // data. Copy a secondary fragment output value if it was declared as an array.
4117 writeOutputPrologue();
4118 break;
4119 case sh::vk::spirv::kNonSemanticTransformFeedbackEmulation:
4120 // Transform feedback emulation is written to a designated function. Allow its code to
4121 // be generated if this is the right function.
4122 if (mOptions.isTransformFeedbackStage)
4123 {
4124 mXfbCodeGenerator.writeTransformFeedbackEmulationOutput(
4125 mInactiveVaryingRemover, mVaryingPrecisionFixer,
4126 mOptions.useSpirvVaryingPrecisionFixer, mSpirvBlobOut);
4127 }
4128 break;
4129 default:
4130 UNREACHABLE();
4131 break;
4132 }
4133
4134 // Drop the instruction if this is the last pass
4135 return mNonSemanticInstructions.transformExtInst(instruction);
4136 }
4137
transformTypeStruct(const uint32_t * instruction)4138 TransformationState SpirvTransformer::transformTypeStruct(const uint32_t *instruction)
4139 {
4140 spirv::IdResult id;
4141 spirv::IdRefList memberList;
4142 ParseTypeStruct(instruction, &id, &memberList);
4143
4144 if (mPerVertexTrimmer.transformTypeStruct(id, &memberList, mSpirvBlobOut) ==
4145 TransformationState::Transformed)
4146 {
4147 return TransformationState::Transformed;
4148 }
4149
4150 ASSERT(id < mVariableInfoById.size());
4151 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4152
4153 return mXfbCodeGenerator.transformTypeStruct(info, mOptions.shaderType, id, memberList,
4154 mSpirvBlobOut);
4155 }
4156
transformVariable(const uint32_t * instruction)4157 TransformationState SpirvTransformer::transformVariable(const uint32_t *instruction)
4158 {
4159 spirv::IdResultType typeId;
4160 spirv::IdResult id;
4161 spv::StorageClass storageClass;
4162 spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);
4163
4164 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4165
4166 // If variable is not a shader interface variable that needs modification, there's nothing to
4167 // do.
4168 if (info == nullptr)
4169 {
4170 return TransformationState::Unchanged;
4171 }
4172
4173 if (mOptions.shaderType == gl::ShaderType::Fragment && storageClass == spv::StorageClassOutput)
4174 {
4175 // If present, make the secondary fragment output array
4176 // private and declare a non-array output instead.
4177 if (mSecondaryOutputTransformer.transformVariable(
4178 typeId, mInactiveVaryingRemover.getTransformedPrivateType(typeId), id,
4179 mSpirvBlobOut) == TransformationState::Transformed)
4180 {
4181 return TransformationState::Transformed;
4182 }
4183 }
4184
4185 // Furthermore, if it's not an inactive varying output, there's nothing to do. Note that
4186 // inactive varying inputs are already pruned by the translator.
4187 // However, input or output storage class for interface block will not be pruned when a shader
4188 // is compiled separately.
4189 if (info->activeStages[mOptions.shaderType])
4190 {
4191 if (mOptions.useSpirvVaryingPrecisionFixer &&
4192 mVaryingPrecisionFixer.transformVariable(
4193 *info, typeId, id, storageClass, mSpirvBlobOut) == TransformationState::Transformed)
4194 {
4195 // Make original variable a private global
4196 return mInactiveVaryingRemover.transformVariable(typeId, id, storageClass,
4197 mSpirvBlobOut);
4198 }
4199 return TransformationState::Unchanged;
4200 }
4201
4202 if (mXfbCodeGenerator.transformVariable(*info, mVariableInfoMap, mOptions.shaderType,
4203 storageBufferStorageClass(), typeId, id,
4204 storageClass) == TransformationState::Transformed)
4205 {
4206 return TransformationState::Transformed;
4207 }
4208
4209 // The variable is inactive. Output a modified variable declaration, where the type is the
4210 // corresponding type with the Private storage class.
4211 return mInactiveVaryingRemover.transformVariable(typeId, id, storageClass, mSpirvBlobOut);
4212 }
4213
transformTypeImage(const uint32_t * instruction)4214 TransformationState SpirvTransformer::transformTypeImage(const uint32_t *instruction)
4215 {
4216 return mMultisampleTransformer.transformTypeImage(instruction, mSpirvBlobOut);
4217 }
4218
transformImageRead(const uint32_t * instruction)4219 TransformationState SpirvTransformer::transformImageRead(const uint32_t *instruction)
4220 {
4221 return mMultisampleTransformer.transformImageRead(instruction, mSpirvBlobOut);
4222 }
4223
transformAccessChain(const uint32_t * instruction)4224 TransformationState SpirvTransformer::transformAccessChain(const uint32_t *instruction)
4225 {
4226 spirv::IdResultType typeId;
4227 spirv::IdResult id;
4228 spirv::IdRef baseId;
4229 spirv::IdRefList indexList;
4230 spirv::ParseAccessChain(instruction, &typeId, &id, &baseId, &indexList);
4231
4232 // If not accessing an inactive output varying, nothing to do.
4233 const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
4234 if (info == nullptr)
4235 {
4236 return TransformationState::Unchanged;
4237 }
4238
4239 if (mOptions.shaderType == gl::ShaderType::Fragment)
4240 {
4241 // Update the type used for accessing the secondary fragment output array.
4242 if (mSecondaryOutputTransformer.transformAccessChain(
4243 mInactiveVaryingRemover.getTransformedPrivateType(typeId), id, baseId, indexList,
4244 mSpirvBlobOut) == TransformationState::Transformed)
4245 {
4246 return TransformationState::Transformed;
4247 }
4248 }
4249
4250 if (mOptions.useSpirvVaryingPrecisionFixer)
4251 {
4252 if (info->activeStages[mOptions.shaderType] && !info->useRelaxedPrecision)
4253 {
4254 return TransformationState::Unchanged;
4255 }
4256 }
4257 else
4258 {
4259 if (info->activeStages[mOptions.shaderType])
4260 {
4261 return TransformationState::Unchanged;
4262 }
4263 }
4264
4265 return mInactiveVaryingRemover.transformAccessChain(typeId, id, baseId, indexList,
4266 mSpirvBlobOut);
4267 }
4268
4269 struct AliasingAttributeMap
4270 {
4271 // The SPIR-V id of the aliasing attribute with the most components. This attribute will be
4272 // used to read from this location instead of every aliasing one.
4273 spirv::IdRef attribute;
4274
4275 // SPIR-V ids of aliasing attributes.
4276 std::vector<spirv::IdRef> aliasingAttributes;
4277 };
4278
ValidateShaderInterfaceVariableIsAttribute(const ShaderInterfaceVariableInfo * info)4279 void ValidateShaderInterfaceVariableIsAttribute(const ShaderInterfaceVariableInfo *info)
4280 {
4281 ASSERT(info);
4282 ASSERT(info->activeStages[gl::ShaderType::Vertex]);
4283 ASSERT(info->attributeComponentCount > 0);
4284 ASSERT(info->attributeLocationCount > 0);
4285 ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
4286 }
4287
ValidateIsAliasingAttribute(const AliasingAttributeMap * aliasingMap,uint32_t id)4288 void ValidateIsAliasingAttribute(const AliasingAttributeMap *aliasingMap, uint32_t id)
4289 {
4290 ASSERT(id != aliasingMap->attribute);
4291 ASSERT(std::find(aliasingMap->aliasingAttributes.begin(), aliasingMap->aliasingAttributes.end(),
4292 id) != aliasingMap->aliasingAttributes.end());
4293 }
4294
4295 // A transformation that resolves vertex attribute aliases. Note that vertex attribute aliasing is
4296 // only allowed in GLSL ES 100, where the attribute types can only be one of float, vec2, vec3,
4297 // vec4, mat2, mat3, and mat4. Matrix attributes are handled by expanding them to multiple vector
4298 // attributes, each occupying one location.
4299 class SpirvVertexAttributeAliasingTransformer final : public SpirvTransformerBase
4300 {
4301 public:
SpirvVertexAttributeAliasingTransformer(const spirv::Blob & spirvBlobIn,const ShaderInterfaceVariableInfoMap & variableInfoMap,std::vector<const ShaderInterfaceVariableInfo * > && variableInfoById,spirv::Blob * spirvBlobOut)4302 SpirvVertexAttributeAliasingTransformer(
4303 const spirv::Blob &spirvBlobIn,
4304 const ShaderInterfaceVariableInfoMap &variableInfoMap,
4305 std::vector<const ShaderInterfaceVariableInfo *> &&variableInfoById,
4306 spirv::Blob *spirvBlobOut)
4307 : SpirvTransformerBase(spirvBlobIn, variableInfoMap, spirvBlobOut),
4308 mNonSemanticInstructions(true)
4309 {
4310 mVariableInfoById = std::move(variableInfoById);
4311 }
4312
4313 void transform();
4314
4315 private:
4316 // Preprocess aliasing attributes in preparation for their removal.
4317 void preprocessAliasingAttributes();
4318
4319 // Transform instructions:
4320 void transformInstruction();
4321
4322 // Helpers:
4323 spirv::IdRef getAliasingAttributeReplacementId(spirv::IdRef aliasingId, uint32_t offset) const;
4324 bool isMatrixAttribute(spirv::IdRef id) const;
4325
4326 // Instructions that are purely informational:
4327 void visitTypePointer(const uint32_t *instruction);
4328
4329 // Instructions that potentially need transformation. They return true if the instruction is
4330 // transformed. If false is returned, the instruction should be copied as-is.
4331 TransformationState transformEntryPoint(const uint32_t *instruction);
4332 TransformationState transformExtInst(const uint32_t *instruction);
4333 TransformationState transformName(const uint32_t *instruction);
4334 TransformationState transformDecorate(const uint32_t *instruction);
4335 TransformationState transformVariable(const uint32_t *instruction);
4336 TransformationState transformAccessChain(const uint32_t *instruction);
4337 void transformLoadHelper(spirv::IdRef pointerId,
4338 spirv::IdRef typeId,
4339 spirv::IdRef replacementId,
4340 spirv::IdRef resultId);
4341 TransformationState transformLoad(const uint32_t *instruction);
4342
4343 void declareExpandedMatrixVectors();
4344 void writeExpandedMatrixInitialization();
4345
4346 // Transformation state:
4347
4348 // Map of aliasing attributes per location.
4349 gl::AttribArray<AliasingAttributeMap> mAliasingAttributeMap;
4350
4351 // For each id, this map indicates whether it refers to an aliasing attribute that needs to be
4352 // removed.
4353 std::vector<bool> mIsAliasingAttributeById;
4354
4355 // Matrix attributes are split into vectors, each occupying one location. The SPIR-V
4356 // declaration would need to change from:
4357 //
4358 // %type = OpTypeMatrix %vectorType N
4359 // %matrixType = OpTypePointer Input %type
4360 // %matrix = OpVariable %matrixType Input
4361 //
4362 // to:
4363 //
4364 // %matrixType = OpTypePointer Private %type
4365 // %matrix = OpVariable %matrixType Private
4366 //
4367 // %vecType = OpTypePointer Input %vectorType
4368 //
4369 // %vec0 = OpVariable %vecType Input
4370 // ...
4371 // %vecN-1 = OpVariable %vecType Input
4372 //
4373 // For each id %matrix (which corresponds to a matrix attribute), this map contains %vec0. The
4374 // ids of the split vectors are consecutive, so %veci == %vec0 + i. %veciType is taken from
4375 // mInputTypePointers.
4376 std::vector<spirv::IdRef> mExpandedMatrixFirstVectorIdById;
4377
4378 // Id of attribute types; float and veci.
floatType(uint32_t componentCount)4379 spirv::IdRef floatType(uint32_t componentCount)
4380 {
4381 static_assert(sh::vk::spirv::kIdVec2 == sh::vk::spirv::kIdFloat + 1);
4382 static_assert(sh::vk::spirv::kIdVec3 == sh::vk::spirv::kIdFloat + 2);
4383 static_assert(sh::vk::spirv::kIdVec4 == sh::vk::spirv::kIdFloat + 3);
4384 ASSERT(componentCount <= 4);
4385 return spirv::IdRef(sh::vk::spirv::kIdFloat + (componentCount - 1));
4386 }
4387
4388 // Id of matrix attribute types. Note that only square matrices are possible as attributes in
4389 // GLSL ES 1.00.
matrixType(uint32_t dimension)4390 spirv::IdRef matrixType(uint32_t dimension)
4391 {
4392 static_assert(sh::vk::spirv::kIdMat3 == sh::vk::spirv::kIdMat2 + 1);
4393 static_assert(sh::vk::spirv::kIdMat4 == sh::vk::spirv::kIdMat2 + 2);
4394 ASSERT(dimension >= 2 && dimension <= 4);
4395 return spirv::IdRef(sh::vk::spirv::kIdMat2 + (dimension - 2));
4396 }
4397
4398 // Corresponding to floatType(), [i]: id of OpTypePointer Input %floatType(i). [0] is unused.
4399 std::array<spirv::IdRef, 5> mInputTypePointers;
4400
4401 // Corresponding to floatType(), [i]: id of OpTypePointer Private %floatType(i). [0] is
4402 // unused.
4403 std::array<spirv::IdRef, 5> mPrivateFloatTypePointers;
4404
4405 // Corresponding to matrixType(), [i]: id of OpTypePointer Private %matrixType(i). [0] and
4406 // [1] are unused.
4407 std::array<spirv::IdRef, 5> mPrivateMatrixTypePointers;
4408
4409 SpirvNonSemanticInstructions mNonSemanticInstructions;
4410 };
4411
transform()4412 void SpirvVertexAttributeAliasingTransformer::transform()
4413 {
4414 onTransformBegin();
4415
4416 preprocessAliasingAttributes();
4417
4418 while (mCurrentWord < mSpirvBlobIn.size())
4419 {
4420 transformInstruction();
4421 }
4422 }
4423
preprocessAliasingAttributes()4424 void SpirvVertexAttributeAliasingTransformer::preprocessAliasingAttributes()
4425 {
4426 const uint32_t indexBound = mSpirvBlobIn[spirv::kHeaderIndexIndexBound];
4427
4428 mVariableInfoById.resize(indexBound, nullptr);
4429 mIsAliasingAttributeById.resize(indexBound, false);
4430 mExpandedMatrixFirstVectorIdById.resize(indexBound);
4431
4432 // Go through attributes and find out which alias which.
4433 for (uint32_t idIndex = spirv::kMinValidId; idIndex < indexBound; ++idIndex)
4434 {
4435 const spirv::IdRef id(idIndex);
4436
4437 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4438
4439 // Ignore non attribute ids.
4440 if (info == nullptr || info->attributeComponentCount == 0)
4441 {
4442 continue;
4443 }
4444
4445 ASSERT(info->activeStages[gl::ShaderType::Vertex]);
4446 ASSERT(info->location != ShaderInterfaceVariableInfo::kInvalid);
4447
4448 const bool isMatrixAttribute = info->attributeLocationCount > 1;
4449
4450 for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
4451 {
4452 uint32_t location = info->location + offset;
4453 ASSERT(location < mAliasingAttributeMap.size());
4454
4455 spirv::IdRef attributeId(id);
4456
4457 // If this is a matrix attribute, expand it to vectors.
4458 if (isMatrixAttribute)
4459 {
4460 const spirv::IdRef matrixId(id);
4461
4462 // Get a new id for this location and associate it with the matrix.
4463 attributeId = getNewId();
4464 if (offset == 0)
4465 {
4466 mExpandedMatrixFirstVectorIdById[matrixId] = attributeId;
4467 }
4468 // The ids are consecutive.
4469 ASSERT(attributeId == mExpandedMatrixFirstVectorIdById[matrixId] + offset);
4470
4471 mIsAliasingAttributeById.resize(attributeId + 1, false);
4472 mVariableInfoById.resize(attributeId + 1, nullptr);
4473 mVariableInfoById[attributeId] = info;
4474 }
4475
4476 AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[location];
4477
4478 // If this is the first attribute in this location, remember it.
4479 if (!aliasingMap->attribute.valid())
4480 {
4481 aliasingMap->attribute = attributeId;
4482 continue;
4483 }
4484
4485 // Otherwise, either add it to the list of aliasing attributes, or replace the main
4486 // attribute (and add that to the list of aliasing attributes). The one with the
4487 // largest number of components is used as the main attribute.
4488 const ShaderInterfaceVariableInfo *curMainAttribute =
4489 mVariableInfoById[aliasingMap->attribute];
4490 ASSERT(curMainAttribute != nullptr && curMainAttribute->attributeComponentCount > 0);
4491
4492 spirv::IdRef aliasingId;
4493 if (info->attributeComponentCount > curMainAttribute->attributeComponentCount)
4494 {
4495 aliasingId = aliasingMap->attribute;
4496 aliasingMap->attribute = attributeId;
4497 }
4498 else
4499 {
4500 aliasingId = attributeId;
4501 }
4502
4503 aliasingMap->aliasingAttributes.push_back(aliasingId);
4504 ASSERT(!mIsAliasingAttributeById[aliasingId]);
4505 mIsAliasingAttributeById[aliasingId] = true;
4506 }
4507 }
4508 }
4509
transformInstruction()4510 void SpirvVertexAttributeAliasingTransformer::transformInstruction()
4511 {
4512 uint32_t wordCount;
4513 spv::Op opCode;
4514 const uint32_t *instruction = getCurrentInstruction(&opCode, &wordCount);
4515
4516 if (opCode == spv::OpFunction)
4517 {
4518 // SPIR-V is structured in sections. Function declarations come last.
4519 mIsInFunctionSection = true;
4520 }
4521
4522 // Only look at interesting instructions.
4523 TransformationState transformationState = TransformationState::Unchanged;
4524
4525 if (mIsInFunctionSection)
4526 {
4527 // Look at in-function opcodes.
4528 switch (opCode)
4529 {
4530 case spv::OpExtInst:
4531 transformationState = transformExtInst(instruction);
4532 break;
4533 case spv::OpAccessChain:
4534 case spv::OpInBoundsAccessChain:
4535 transformationState = transformAccessChain(instruction);
4536 break;
4537 case spv::OpLoad:
4538 transformationState = transformLoad(instruction);
4539 break;
4540 default:
4541 break;
4542 }
4543 }
4544 else
4545 {
4546 // Look at global declaration opcodes.
4547 switch (opCode)
4548 {
4549 // Informational instructions:
4550 case spv::OpTypePointer:
4551 visitTypePointer(instruction);
4552 break;
4553 // Instructions that may need transformation:
4554 case spv::OpEntryPoint:
4555 transformationState = transformEntryPoint(instruction);
4556 break;
4557 case spv::OpExtInst:
4558 transformationState = transformExtInst(instruction);
4559 break;
4560 case spv::OpName:
4561 transformationState = transformName(instruction);
4562 break;
4563 case spv::OpDecorate:
4564 transformationState = transformDecorate(instruction);
4565 break;
4566 case spv::OpVariable:
4567 transformationState = transformVariable(instruction);
4568 break;
4569 default:
4570 break;
4571 }
4572 }
4573
4574 // If the instruction was not transformed, copy it to output as is.
4575 if (transformationState == TransformationState::Unchanged)
4576 {
4577 copyInstruction(instruction, wordCount);
4578 }
4579
4580 // Advance to next instruction.
4581 mCurrentWord += wordCount;
4582 }
4583
getAliasingAttributeReplacementId(spirv::IdRef aliasingId,uint32_t offset) const4584 spirv::IdRef SpirvVertexAttributeAliasingTransformer::getAliasingAttributeReplacementId(
4585 spirv::IdRef aliasingId,
4586 uint32_t offset) const
4587 {
4588 // Get variable info corresponding to the aliasing attribute.
4589 const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[aliasingId];
4590 ValidateShaderInterfaceVariableIsAttribute(aliasingInfo);
4591
4592 // Find the replacement attribute.
4593 const AliasingAttributeMap *aliasingMap =
4594 &mAliasingAttributeMap[aliasingInfo->location + offset];
4595 ValidateIsAliasingAttribute(aliasingMap, aliasingId);
4596
4597 const spirv::IdRef replacementId(aliasingMap->attribute);
4598 ASSERT(replacementId.valid() && replacementId < mIsAliasingAttributeById.size());
4599 ASSERT(!mIsAliasingAttributeById[replacementId]);
4600
4601 return replacementId;
4602 }
4603
isMatrixAttribute(spirv::IdRef id) const4604 bool SpirvVertexAttributeAliasingTransformer::isMatrixAttribute(spirv::IdRef id) const
4605 {
4606 return mExpandedMatrixFirstVectorIdById[id].valid();
4607 }
4608
visitTypePointer(const uint32_t * instruction)4609 void SpirvVertexAttributeAliasingTransformer::visitTypePointer(const uint32_t *instruction)
4610 {
4611 spirv::IdResult id;
4612 spv::StorageClass storageClass;
4613 spirv::IdRef typeId;
4614 spirv::ParseTypePointer(instruction, &id, &storageClass, &typeId);
4615
4616 // Only interested in OpTypePointer Input %vecN, where %vecN is the id of OpTypeVector %f32 N,
4617 // as well as OpTypePointer Private %matN, where %matN is the id of OpTypeMatrix %vecN N.
4618 // This is only for matN types (as allowed by GLSL ES 1.00), so N >= 2.
4619 if (storageClass == spv::StorageClassInput)
4620 {
4621 for (uint32_t n = 2; n <= 4; ++n)
4622 {
4623 if (typeId == floatType(n))
4624 {
4625 ASSERT(!mInputTypePointers[n].valid());
4626 mInputTypePointers[n] = id;
4627 break;
4628 }
4629 }
4630 }
4631 else if (storageClass == spv::StorageClassPrivate)
4632 {
4633 for (uint32_t n = 2; n <= 4; ++n)
4634 {
4635 // Note that Private types may not be unique, as the previous transformation can
4636 // generate duplicates.
4637 if (typeId == floatType(n))
4638 {
4639 mPrivateFloatTypePointers[n] = id;
4640 break;
4641 }
4642 if (typeId == matrixType(n))
4643 {
4644 mPrivateMatrixTypePointers[n] = id;
4645 break;
4646 }
4647 }
4648 }
4649 }
4650
transformEntryPoint(const uint32_t * instruction)4651 TransformationState SpirvVertexAttributeAliasingTransformer::transformEntryPoint(
4652 const uint32_t *instruction)
4653 {
4654 // Remove aliasing attributes from the shader interface declaration.
4655 spv::ExecutionModel executionModel;
4656 spirv::IdRef entryPointId;
4657 spirv::LiteralString name;
4658 spirv::IdRefList interfaceList;
4659 spirv::ParseEntryPoint(instruction, &executionModel, &entryPointId, &name, &interfaceList);
4660
4661 // Should only have one EntryPoint
4662 ASSERT(entryPointId == ID::EntryPoint);
4663
4664 // As a first pass, filter out matrix attributes and append their replacement vectors.
4665 size_t originalInterfaceListSize = interfaceList.size();
4666 for (size_t index = 0; index < originalInterfaceListSize; ++index)
4667 {
4668 const spirv::IdRef matrixId(interfaceList[index]);
4669
4670 if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
4671 {
4672 continue;
4673 }
4674
4675 const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
4676 ValidateShaderInterfaceVariableIsAttribute(info);
4677
4678 // Replace the matrix id with its first vector id.
4679 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
4680 interfaceList[index] = vec0Id;
4681
4682 // Append the rest of the vectors to the entry point.
4683 for (uint32_t offset = 1; offset < info->attributeLocationCount; ++offset)
4684 {
4685 const spirv::IdRef vecId(vec0Id + offset);
4686 interfaceList.push_back(vecId);
4687 }
4688
4689 // With SPIR-V 1.4, keep the Private variable in the interface list.
4690 if (entryPointList() == EntryPointList::GlobalVariables)
4691 {
4692 interfaceList.push_back(matrixId);
4693 }
4694 }
4695
4696 // Filter out aliasing attributes from entry point interface declaration.
4697 size_t writeIndex = 0;
4698 for (size_t index = 0; index < interfaceList.size(); ++index)
4699 {
4700 const spirv::IdRef id(interfaceList[index]);
4701
4702 // If this is an attribute that's aliasing another one in the same location, remove it.
4703 if (mIsAliasingAttributeById[id])
4704 {
4705 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4706 ValidateShaderInterfaceVariableIsAttribute(info);
4707
4708 // The following assertion is only valid for non-matrix attributes.
4709 if (info->attributeLocationCount == 1)
4710 {
4711 const AliasingAttributeMap *aliasingMap = &mAliasingAttributeMap[info->location];
4712 ValidateIsAliasingAttribute(aliasingMap, id);
4713 }
4714
4715 continue;
4716 }
4717
4718 interfaceList[writeIndex] = id;
4719 ++writeIndex;
4720 }
4721
4722 // Update the number of interface variables.
4723 interfaceList.resize_down(writeIndex);
4724
4725 // Write the entry point with the aliasing attributes removed.
4726 spirv::WriteEntryPoint(mSpirvBlobOut, executionModel, ID::EntryPoint, name, interfaceList);
4727
4728 return TransformationState::Transformed;
4729 }
4730
transformExtInst(const uint32_t * instruction)4731 TransformationState SpirvVertexAttributeAliasingTransformer::transformExtInst(
4732 const uint32_t *instruction)
4733 {
4734 sh::vk::spirv::NonSemanticInstruction inst;
4735 if (!mNonSemanticInstructions.visitExtInst(instruction, &inst))
4736 {
4737 return TransformationState::Unchanged;
4738 }
4739
4740 switch (inst)
4741 {
4742 case sh::vk::spirv::kNonSemanticOverview:
4743 // Declare the expanded matrix variables
4744 declareExpandedMatrixVectors();
4745 break;
4746 case sh::vk::spirv::kNonSemanticEnter:
4747 // The matrix attribute declarations have been changed to have Private storage class,
4748 // and they are initialized from the expanded (and potentially aliased) Input vectors.
4749 // This is done at the beginning of the entry point.
4750 writeExpandedMatrixInitialization();
4751 break;
4752 case sh::vk::spirv::kNonSemanticOutput:
4753 case sh::vk::spirv::kNonSemanticTransformFeedbackEmulation:
4754 // Unused by this transformation
4755 break;
4756 default:
4757 UNREACHABLE();
4758 break;
4759 }
4760
4761 // Drop the instruction if this is the last pass
4762 return mNonSemanticInstructions.transformExtInst(instruction);
4763 }
4764
transformName(const uint32_t * instruction)4765 TransformationState SpirvVertexAttributeAliasingTransformer::transformName(
4766 const uint32_t *instruction)
4767 {
4768 spirv::IdRef id;
4769 spirv::LiteralString name;
4770 spirv::ParseName(instruction, &id, &name);
4771
4772 // If id is not that of an aliasing attribute, there's nothing to do.
4773 ASSERT(id < mIsAliasingAttributeById.size());
4774 if (!mIsAliasingAttributeById[id])
4775 {
4776 return TransformationState::Unchanged;
4777 }
4778
4779 // Drop debug annotations for this id.
4780 return TransformationState::Transformed;
4781 }
4782
transformDecorate(const uint32_t * instruction)4783 TransformationState SpirvVertexAttributeAliasingTransformer::transformDecorate(
4784 const uint32_t *instruction)
4785 {
4786 spirv::IdRef id;
4787 spv::Decoration decoration;
4788 spirv::ParseDecorate(instruction, &id, &decoration, nullptr);
4789
4790 if (isMatrixAttribute(id))
4791 {
4792 // If it's a matrix attribute, it's expanded to multiple vectors. Insert the Location
4793 // decorations for these vectors here.
4794
4795 // Keep all decorations except for Location.
4796 if (decoration != spv::DecorationLocation)
4797 {
4798 return TransformationState::Unchanged;
4799 }
4800
4801 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4802 ValidateShaderInterfaceVariableIsAttribute(info);
4803
4804 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[id]);
4805 ASSERT(vec0Id.valid());
4806
4807 for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
4808 {
4809 const spirv::IdRef vecId(vec0Id + offset);
4810 if (mIsAliasingAttributeById[vecId])
4811 {
4812 continue;
4813 }
4814
4815 spirv::WriteDecorate(mSpirvBlobOut, vecId, decoration,
4816 {spirv::LiteralInteger(info->location + offset)});
4817 }
4818 }
4819 else
4820 {
4821 // If id is not that of an active attribute, there's nothing to do.
4822 const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
4823 if (info == nullptr || info->attributeComponentCount == 0 ||
4824 !info->activeStages[gl::ShaderType::Vertex])
4825 {
4826 return TransformationState::Unchanged;
4827 }
4828
4829 // Always drop RelaxedPrecision from input attributes. The temporary variable the attribute
4830 // is loaded into has RelaxedPrecision and will implicitly convert.
4831 if (decoration == spv::DecorationRelaxedPrecision)
4832 {
4833 return TransformationState::Transformed;
4834 }
4835
4836 // If id is not that of an aliasing attribute, there's nothing else to do.
4837 ASSERT(id < mIsAliasingAttributeById.size());
4838 if (!mIsAliasingAttributeById[id])
4839 {
4840 return TransformationState::Unchanged;
4841 }
4842 }
4843
4844 // Drop every decoration for this id.
4845 return TransformationState::Transformed;
4846 }
4847
transformVariable(const uint32_t * instruction)4848 TransformationState SpirvVertexAttributeAliasingTransformer::transformVariable(
4849 const uint32_t *instruction)
4850 {
4851 spirv::IdResultType typeId;
4852 spirv::IdResult id;
4853 spv::StorageClass storageClass;
4854 spirv::ParseVariable(instruction, &typeId, &id, &storageClass, nullptr);
4855
4856 if (!isMatrixAttribute(id))
4857 {
4858 // If id is not that of an aliasing attribute, there's nothing to do. Note that matrix
4859 // declarations are always replaced.
4860 ASSERT(id < mIsAliasingAttributeById.size());
4861 if (!mIsAliasingAttributeById[id])
4862 {
4863 return TransformationState::Unchanged;
4864 }
4865 }
4866
4867 ASSERT(storageClass == spv::StorageClassInput);
4868
4869 // Drop the declaration.
4870 return TransformationState::Transformed;
4871 }
4872
transformAccessChain(const uint32_t * instruction)4873 TransformationState SpirvVertexAttributeAliasingTransformer::transformAccessChain(
4874 const uint32_t *instruction)
4875 {
4876 spirv::IdResultType typeId;
4877 spirv::IdResult id;
4878 spirv::IdRef baseId;
4879 spirv::IdRefList indexList;
4880 spirv::ParseAccessChain(instruction, &typeId, &id, &baseId, &indexList);
4881
4882 if (isMatrixAttribute(baseId))
4883 {
4884 // Write a modified OpAccessChain instruction. Only modification is that the %type is
4885 // replaced with the Private version of it. If there is one %index, that would be a vector
4886 // type, and if there are two %index'es, it's a float type.
4887 spirv::IdRef replacementTypeId;
4888
4889 if (indexList.size() == 1)
4890 {
4891 // If indexed once, it uses a vector type.
4892 const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
4893 ValidateShaderInterfaceVariableIsAttribute(info);
4894
4895 const uint32_t componentCount = info->attributeComponentCount;
4896
4897 // %type must have been the Input vector type with the matrice's component size.
4898 ASSERT(typeId == mInputTypePointers[componentCount]);
4899
4900 // Replace the type with the corresponding Private one.
4901 replacementTypeId = mPrivateFloatTypePointers[componentCount];
4902 }
4903 else
4904 {
4905 // If indexed twice, it uses the float type.
4906 ASSERT(indexList.size() == 2);
4907
4908 // Replace the type with the Private pointer to float32.
4909 replacementTypeId = mPrivateFloatTypePointers[1];
4910 }
4911
4912 spirv::WriteAccessChain(mSpirvBlobOut, replacementTypeId, id, baseId, indexList);
4913 }
4914 else
4915 {
4916 // If base id is not that of an aliasing attribute, there's nothing to do.
4917 ASSERT(baseId < mIsAliasingAttributeById.size());
4918 if (!mIsAliasingAttributeById[baseId])
4919 {
4920 return TransformationState::Unchanged;
4921 }
4922
4923 // Find the replacement attribute for the aliasing one.
4924 const spirv::IdRef replacementId(getAliasingAttributeReplacementId(baseId, 0));
4925
4926 // Get variable info corresponding to the replacement attribute.
4927 const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
4928 ValidateShaderInterfaceVariableIsAttribute(replacementInfo);
4929
4930 // Write a modified OpAccessChain instruction. Currently, the instruction is:
4931 //
4932 // %id = OpAccessChain %type %base %index
4933 //
4934 // This is modified to:
4935 //
4936 // %id = OpAccessChain %type %replacement %index
4937 //
4938 // Note that the replacement has at least as many components as the aliasing attribute,
4939 // and both attributes start at component 0 (GLSL ES restriction). So, indexing the
4940 // replacement attribute with the same index yields the same result and type.
4941 spirv::WriteAccessChain(mSpirvBlobOut, typeId, id, replacementId, indexList);
4942 }
4943
4944 return TransformationState::Transformed;
4945 }
4946
transformLoadHelper(spirv::IdRef pointerId,spirv::IdRef typeId,spirv::IdRef replacementId,spirv::IdRef resultId)4947 void SpirvVertexAttributeAliasingTransformer::transformLoadHelper(spirv::IdRef pointerId,
4948 spirv::IdRef typeId,
4949 spirv::IdRef replacementId,
4950 spirv::IdRef resultId)
4951 {
4952 // Get variable info corresponding to the replacement attribute.
4953 const ShaderInterfaceVariableInfo *replacementInfo = mVariableInfoById[replacementId];
4954 ValidateShaderInterfaceVariableIsAttribute(replacementInfo);
4955
4956 // Currently, the instruction is:
4957 //
4958 // %id = OpLoad %type %pointer
4959 //
4960 // This is modified to:
4961 //
4962 // %newId = OpLoad %replacementType %replacement
4963 //
4964 const spirv::IdRef loadResultId(getNewId());
4965 const spirv::IdRef replacementTypeId(floatType(replacementInfo->attributeComponentCount));
4966 ASSERT(replacementTypeId.valid());
4967
4968 spirv::WriteLoad(mSpirvBlobOut, replacementTypeId, loadResultId, replacementId, nullptr);
4969
4970 // If swizzle is not necessary, assign %newId to %resultId.
4971 const ShaderInterfaceVariableInfo *aliasingInfo = mVariableInfoById[pointerId];
4972 if (aliasingInfo->attributeComponentCount == replacementInfo->attributeComponentCount)
4973 {
4974 spirv::WriteCopyObject(mSpirvBlobOut, typeId, resultId, loadResultId);
4975 return;
4976 }
4977
4978 // Take as many components from the replacement as the aliasing attribute wanted. This is done
4979 // by either of the following instructions:
4980 //
4981 // - If aliasing attribute has only one component:
4982 //
4983 // %resultId = OpCompositeExtract %floatType %newId 0
4984 //
4985 // - If aliasing attribute has more than one component:
4986 //
4987 // %resultId = OpVectorShuffle %vecType %newId %newId 0 1 ...
4988 //
4989 ASSERT(aliasingInfo->attributeComponentCount < replacementInfo->attributeComponentCount);
4990 ASSERT(floatType(aliasingInfo->attributeComponentCount) == typeId);
4991
4992 if (aliasingInfo->attributeComponentCount == 1)
4993 {
4994 spirv::WriteCompositeExtract(mSpirvBlobOut, typeId, resultId, loadResultId,
4995 {spirv::LiteralInteger(0)});
4996 }
4997 else
4998 {
4999 spirv::LiteralIntegerList swizzle = {spirv::LiteralInteger(0), spirv::LiteralInteger(1),
5000 spirv::LiteralInteger(2), spirv::LiteralInteger(3)};
5001 swizzle.resize_down(aliasingInfo->attributeComponentCount);
5002
5003 spirv::WriteVectorShuffle(mSpirvBlobOut, typeId, resultId, loadResultId, loadResultId,
5004 swizzle);
5005 }
5006 }
5007
transformLoad(const uint32_t * instruction)5008 TransformationState SpirvVertexAttributeAliasingTransformer::transformLoad(
5009 const uint32_t *instruction)
5010 {
5011 spirv::IdResultType typeId;
5012 spirv::IdResult id;
5013 spirv::IdRef pointerId;
5014 ParseLoad(instruction, &typeId, &id, &pointerId, nullptr);
5015
5016 // Currently, the instruction is:
5017 //
5018 // %id = OpLoad %type %pointer
5019 //
5020 // If non-matrix, this is modifed to load from the aliasing vector instead if aliasing.
5021 //
5022 // If matrix, this is modified such that %type points to the Private version of it.
5023 //
5024 if (isMatrixAttribute(pointerId))
5025 {
5026 const ShaderInterfaceVariableInfo *info = mVariableInfoById[pointerId];
5027 ValidateShaderInterfaceVariableIsAttribute(info);
5028
5029 const spirv::IdRef replacementTypeId(matrixType(info->attributeLocationCount));
5030
5031 spirv::WriteLoad(mSpirvBlobOut, replacementTypeId, id, pointerId, nullptr);
5032 }
5033 else
5034 {
5035 // If pointer id is not that of an aliasing attribute, there's nothing to do.
5036 ASSERT(pointerId < mIsAliasingAttributeById.size());
5037 if (!mIsAliasingAttributeById[pointerId])
5038 {
5039 return TransformationState::Unchanged;
5040 }
5041
5042 // Find the replacement attribute for the aliasing one.
5043 const spirv::IdRef replacementId(getAliasingAttributeReplacementId(pointerId, 0));
5044
5045 // Replace the load instruction by a load from the replacement id.
5046 transformLoadHelper(pointerId, typeId, replacementId, id);
5047 }
5048
5049 return TransformationState::Transformed;
5050 }
5051
declareExpandedMatrixVectors()5052 void SpirvVertexAttributeAliasingTransformer::declareExpandedMatrixVectors()
5053 {
5054 // Go through matrix attributes and expand them.
5055 for (uint32_t matrixIdIndex = spirv::kMinValidId;
5056 matrixIdIndex < mExpandedMatrixFirstVectorIdById.size(); ++matrixIdIndex)
5057 {
5058 const spirv::IdRef matrixId(matrixIdIndex);
5059
5060 if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
5061 {
5062 continue;
5063 }
5064
5065 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
5066
5067 const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
5068 ValidateShaderInterfaceVariableIsAttribute(info);
5069
5070 // Need to generate the following:
5071 //
5072 // %privateType = OpTypePointer Private %matrixType
5073 // %id = OpVariable %privateType Private
5074 // %vecType = OpTypePointer %vecType Input
5075 // %vec0 = OpVariable %vecType Input
5076 // ...
5077 // %vecN-1 = OpVariable %vecType Input
5078 const uint32_t componentCount = info->attributeComponentCount;
5079 const uint32_t locationCount = info->attributeLocationCount;
5080 ASSERT(componentCount == locationCount);
5081
5082 // OpTypePointer Private %matrixType
5083 spirv::IdRef privateType(mPrivateMatrixTypePointers[locationCount]);
5084 if (!privateType.valid())
5085 {
5086 privateType = getNewId();
5087 mPrivateMatrixTypePointers[locationCount] = privateType;
5088 spirv::WriteTypePointer(mSpirvBlobOut, privateType, spv::StorageClassPrivate,
5089 matrixType(locationCount));
5090 }
5091
5092 // OpVariable %privateType Private
5093 spirv::WriteVariable(mSpirvBlobOut, privateType, matrixId, spv::StorageClassPrivate,
5094 nullptr);
5095
5096 // If the OpTypePointer is not declared for the vector type corresponding to each location,
5097 // declare it now.
5098 //
5099 // %vecType = OpTypePointer %vecType Input
5100 spirv::IdRef inputType(mInputTypePointers[componentCount]);
5101 if (!inputType.valid())
5102 {
5103 inputType = getNewId();
5104 mInputTypePointers[componentCount] = inputType;
5105 spirv::WriteTypePointer(mSpirvBlobOut, inputType, spv::StorageClassInput,
5106 floatType(componentCount));
5107 }
5108
5109 // Declare a vector for each column of the matrix.
5110 for (uint32_t offset = 0; offset < info->attributeLocationCount; ++offset)
5111 {
5112 const spirv::IdRef vecId(vec0Id + offset);
5113 if (!mIsAliasingAttributeById[vecId])
5114 {
5115 spirv::WriteVariable(mSpirvBlobOut, inputType, vecId, spv::StorageClassInput,
5116 nullptr);
5117 }
5118 }
5119 }
5120
5121 // Additionally, declare OpTypePointer Private %floatType(i) in case needed (used in
5122 // Op*AccessChain instructions, if any).
5123 for (uint32_t n = 1; n <= 4; ++n)
5124 {
5125 if (!mPrivateFloatTypePointers[n].valid())
5126 {
5127 const spirv::IdRef privateType(getNewId());
5128 mPrivateFloatTypePointers[n] = privateType;
5129 spirv::WriteTypePointer(mSpirvBlobOut, privateType, spv::StorageClassPrivate,
5130 floatType(n));
5131 }
5132 }
5133 }
5134
writeExpandedMatrixInitialization()5135 void SpirvVertexAttributeAliasingTransformer::writeExpandedMatrixInitialization()
5136 {
5137 // Go through matrix attributes and initialize them. Note that their declaration is replaced
5138 // with a Private storage class, but otherwise has the same id.
5139 for (uint32_t matrixIdIndex = spirv::kMinValidId;
5140 matrixIdIndex < mExpandedMatrixFirstVectorIdById.size(); ++matrixIdIndex)
5141 {
5142 const spirv::IdRef matrixId(matrixIdIndex);
5143
5144 if (!mExpandedMatrixFirstVectorIdById[matrixId].valid())
5145 {
5146 continue;
5147 }
5148
5149 const spirv::IdRef vec0Id(mExpandedMatrixFirstVectorIdById[matrixId]);
5150
5151 // For every matrix, need to generate the following:
5152 //
5153 // %vec0Id = OpLoad %vecType %vec0Pointer
5154 // ...
5155 // %vecN-1Id = OpLoad %vecType %vecN-1Pointer
5156 // %mat = OpCompositeConstruct %matrixType %vec0 ... %vecN-1
5157 // OpStore %matrixId %mat
5158
5159 const ShaderInterfaceVariableInfo *info = mVariableInfoById[matrixId];
5160 ValidateShaderInterfaceVariableIsAttribute(info);
5161
5162 spirv::IdRefList vecLoadIds;
5163 const uint32_t locationCount = info->attributeLocationCount;
5164 for (uint32_t offset = 0; offset < locationCount; ++offset)
5165 {
5166 const spirv::IdRef vecId(vec0Id + offset);
5167
5168 // Load into temporary, potentially through an aliasing vector.
5169 spirv::IdRef replacementId(vecId);
5170 ASSERT(vecId < mIsAliasingAttributeById.size());
5171 if (mIsAliasingAttributeById[vecId])
5172 {
5173 replacementId = getAliasingAttributeReplacementId(vecId, offset);
5174 }
5175
5176 // Write a load instruction from the replacement id.
5177 vecLoadIds.push_back(getNewId());
5178 transformLoadHelper(matrixId, floatType(info->attributeComponentCount), replacementId,
5179 vecLoadIds.back());
5180 }
5181
5182 // Aggregate the vector loads into a matrix.
5183 const spirv::IdRef compositeId(getNewId());
5184 spirv::WriteCompositeConstruct(mSpirvBlobOut, matrixType(locationCount), compositeId,
5185 vecLoadIds);
5186
5187 // Store it in the private variable.
5188 spirv::WriteStore(mSpirvBlobOut, matrixId, compositeId, nullptr);
5189 }
5190 }
5191 } // anonymous namespace
5192
SpvCreateSourceOptions(const angle::FeaturesVk & features,uint32_t maxColorInputAttachmentCount)5193 SpvSourceOptions SpvCreateSourceOptions(const angle::FeaturesVk &features,
5194 uint32_t maxColorInputAttachmentCount)
5195 {
5196 SpvSourceOptions options;
5197
5198 options.maxColorInputAttachmentCount = maxColorInputAttachmentCount;
5199 options.supportsTransformFeedbackExtension =
5200 features.supportsTransformFeedbackExtension.enabled;
5201 options.supportsTransformFeedbackEmulation = features.emulateTransformFeedback.enabled;
5202 options.enableTransformFeedbackEmulation = options.supportsTransformFeedbackEmulation;
5203 options.supportsDepthStencilInputAttachments =
5204 features.supportsShaderFramebufferFetchDepthStencil.enabled;
5205
5206 return options;
5207 }
5208
SpvGetXfbBufferBlockId(const uint32_t bufferIndex)5209 uint32_t SpvGetXfbBufferBlockId(const uint32_t bufferIndex)
5210 {
5211 ASSERT(bufferIndex < 4);
5212 static_assert(sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockOne ==
5213 sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + 1);
5214 static_assert(sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockTwo ==
5215 sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + 2);
5216 static_assert(sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockThree ==
5217 sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + 3);
5218
5219 return sh::vk::spirv::ReservedIds::kIdXfbEmulationBufferBlockZero + bufferIndex;
5220 }
5221
SpvAssignLocations(const SpvSourceOptions & options,const gl::ProgramExecutable & programExecutable,const gl::ProgramVaryingPacking & varyingPacking,const gl::ShaderType transformFeedbackStage,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)5222 void SpvAssignLocations(const SpvSourceOptions &options,
5223 const gl::ProgramExecutable &programExecutable,
5224 const gl::ProgramVaryingPacking &varyingPacking,
5225 const gl::ShaderType transformFeedbackStage,
5226 SpvProgramInterfaceInfo *programInterfaceInfo,
5227 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
5228 {
5229 const gl::ShaderBitSet shaderStages = programExecutable.getLinkedShaderStages();
5230
5231 // Assign outputs to the fragment shader, if any.
5232 if (shaderStages[gl::ShaderType::Fragment] &&
5233 programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment))
5234 {
5235 AssignOutputLocations(programExecutable, gl::ShaderType::Fragment, variableInfoMapOut);
5236 }
5237
5238 // Assign attributes to the vertex shader, if any.
5239 if (shaderStages[gl::ShaderType::Vertex] &&
5240 programExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex))
5241 {
5242 AssignAttributeLocations(programExecutable, gl::ShaderType::Vertex, variableInfoMapOut);
5243
5244 if (options.supportsTransformFeedbackEmulation)
5245 {
5246 // If transform feedback emulation is not enabled, mark all transform feedback output
5247 // buffers as inactive.
5248 const bool isTransformFeedbackStage =
5249 transformFeedbackStage == gl::ShaderType::Vertex &&
5250 options.enableTransformFeedbackEmulation &&
5251 !programExecutable.getLinkedTransformFeedbackVaryings().empty();
5252
5253 AssignTransformFeedbackEmulationBindings(gl::ShaderType::Vertex, programExecutable,
5254 isTransformFeedbackStage, programInterfaceInfo,
5255 variableInfoMapOut);
5256 }
5257 }
5258
5259 gl::ShaderType frontShaderType = gl::ShaderType::InvalidEnum;
5260 for (const gl::ShaderType shaderType : shaderStages)
5261 {
5262 if (programExecutable.hasLinkedGraphicsShader())
5263 {
5264 const gl::VaryingPacking &inputPacking = varyingPacking.getInputPacking(shaderType);
5265 const gl::VaryingPacking &outputPacking = varyingPacking.getOutputPacking(shaderType);
5266
5267 // Assign varying locations.
5268 if (shaderType != gl::ShaderType::Vertex)
5269 {
5270 AssignVaryingLocations(options, inputPacking, shaderType, frontShaderType,
5271 programInterfaceInfo, variableInfoMapOut);
5272
5273 // Record active members of in gl_PerVertex.
5274 if (shaderType != gl::ShaderType::Fragment &&
5275 frontShaderType != gl::ShaderType::InvalidEnum)
5276 {
5277 // If an output builtin is active in the previous stage, assume it's active in
5278 // the input of the current stage as well.
5279 const gl::ShaderMap<gl::PerVertexMemberBitSet> &outputPerVertexActiveMembers =
5280 inputPacking.getOutputPerVertexActiveMembers();
5281 variableInfoMapOut->setInputPerVertexActiveMembers(
5282 shaderType, outputPerVertexActiveMembers[frontShaderType]);
5283 }
5284 }
5285 if (shaderType != gl::ShaderType::Fragment)
5286 {
5287 AssignVaryingLocations(options, outputPacking, shaderType, frontShaderType,
5288 programInterfaceInfo, variableInfoMapOut);
5289
5290 // Record active members of out gl_PerVertex.
5291 const gl::ShaderMap<gl::PerVertexMemberBitSet> &outputPerVertexActiveMembers =
5292 outputPacking.getOutputPerVertexActiveMembers();
5293 variableInfoMapOut->setOutputPerVertexActiveMembers(
5294 shaderType, outputPerVertexActiveMembers[shaderType]);
5295 }
5296
5297 // Assign qualifiers to all varyings captured by transform feedback
5298 if (!programExecutable.getLinkedTransformFeedbackVaryings().empty() &&
5299 shaderType == programExecutable.getLinkedTransformFeedbackStage())
5300 {
5301 AssignTransformFeedbackQualifiers(programExecutable, outputPacking, shaderType,
5302 options.supportsTransformFeedbackExtension,
5303 variableInfoMapOut);
5304 }
5305 }
5306
5307 frontShaderType = shaderType;
5308 }
5309
5310 AssignUniformBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
5311 AssignTextureBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
5312 AssignNonTextureBindings(options, programExecutable, programInterfaceInfo, variableInfoMapOut);
5313 }
5314
SpvAssignTransformFeedbackLocations(gl::ShaderType shaderType,const gl::ProgramExecutable & programExecutable,bool isTransformFeedbackStage,SpvProgramInterfaceInfo * programInterfaceInfo,ShaderInterfaceVariableInfoMap * variableInfoMapOut)5315 void SpvAssignTransformFeedbackLocations(gl::ShaderType shaderType,
5316 const gl::ProgramExecutable &programExecutable,
5317 bool isTransformFeedbackStage,
5318 SpvProgramInterfaceInfo *programInterfaceInfo,
5319 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
5320 {
5321 // The only varying that requires additional resources is gl_Position, as it's indirectly
5322 // captured through ANGLEXfbPosition.
5323
5324 const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
5325 programExecutable.getLinkedTransformFeedbackVaryings();
5326
5327 bool capturesPosition = false;
5328
5329 if (isTransformFeedbackStage)
5330 {
5331 for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
5332 {
5333 const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
5334 const std::string &tfVaryingName = tfVarying.name;
5335
5336 if (tfVaryingName == "gl_Position")
5337 {
5338 ASSERT(tfVarying.isBuiltIn());
5339 capturesPosition = true;
5340 break;
5341 }
5342 }
5343 }
5344
5345 if (capturesPosition)
5346 {
5347 AddLocationInfo(variableInfoMapOut, shaderType, sh::vk::spirv::kIdXfbExtensionPosition,
5348 programInterfaceInfo->locationsUsedForXfbExtension, 0, 0, 0);
5349 ++programInterfaceInfo->locationsUsedForXfbExtension;
5350 }
5351 else
5352 {
5353 // Make sure this varying is removed from the other stages, or if position is not captured
5354 // at all.
5355 variableInfoMapOut->add(shaderType, sh::vk::spirv::kIdXfbExtensionPosition);
5356 }
5357 }
5358
SpvGetShaderSpirvCode(const gl::ProgramState & programState,gl::ShaderMap<const spirv::Blob * > * spirvBlobsOut)5359 void SpvGetShaderSpirvCode(const gl::ProgramState &programState,
5360 gl::ShaderMap<const spirv::Blob *> *spirvBlobsOut)
5361 {
5362 for (const gl::ShaderType shaderType : gl::AllShaderTypes())
5363 {
5364 const gl::SharedCompiledShaderState &glShader = programState.getAttachedShader(shaderType);
5365 (*spirvBlobsOut)[shaderType] = glShader ? &glShader->compiledBinary : nullptr;
5366 }
5367 }
5368
SpvAssignAllLocations(const SpvSourceOptions & options,const gl::ProgramState & programState,const gl::ProgramLinkedResources & resources,ShaderInterfaceVariableInfoMap * variableInfoMapOut)5369 void SpvAssignAllLocations(const SpvSourceOptions &options,
5370 const gl::ProgramState &programState,
5371 const gl::ProgramLinkedResources &resources,
5372 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
5373 {
5374 SpvProgramInterfaceInfo spvProgramInterfaceInfo = {};
5375 const gl::ProgramExecutable &programExecutable = programState.getExecutable();
5376 gl::ShaderType xfbStage = programState.getAttachedTransformFeedbackStage();
5377
5378 // This should be done before assigning varying location. Otherwise, We can encounter shader
5379 // interface mismatching problem in case the transformFeedback stage is not Vertex stage.
5380 for (const gl::ShaderType shaderType : programExecutable.getLinkedShaderStages())
5381 {
5382 // Assign location to varyings generated for transform feedback capture
5383 const bool isXfbStage = shaderType == xfbStage &&
5384 !programExecutable.getLinkedTransformFeedbackVaryings().empty();
5385 if (options.supportsTransformFeedbackExtension &&
5386 gl::ShaderTypeSupportsTransformFeedback(shaderType))
5387 {
5388 SpvAssignTransformFeedbackLocations(shaderType, programExecutable, isXfbStage,
5389 &spvProgramInterfaceInfo, variableInfoMapOut);
5390 }
5391 }
5392
5393 SpvAssignLocations(options, programExecutable, resources.varyingPacking, xfbStage,
5394 &spvProgramInterfaceInfo, variableInfoMapOut);
5395 }
5396
SpvTransformSpirvCode(const SpvTransformOptions & options,const ShaderInterfaceVariableInfoMap & variableInfoMap,const spirv::Blob & initialSpirvBlob,spirv::Blob * spirvBlobOut)5397 angle::Result SpvTransformSpirvCode(const SpvTransformOptions &options,
5398 const ShaderInterfaceVariableInfoMap &variableInfoMap,
5399 const spirv::Blob &initialSpirvBlob,
5400 spirv::Blob *spirvBlobOut)
5401 {
5402 if (initialSpirvBlob.empty())
5403 {
5404 return angle::Result::Continue;
5405 }
5406
5407 const bool hasAliasingAttributes =
5408 options.shaderType == gl::ShaderType::Vertex && variableInfoMap.hasAliasingAttributes();
5409
5410 // Transform the SPIR-V code by assigning location/set/binding values.
5411 SpirvTransformer transformer(initialSpirvBlob, options, !hasAliasingAttributes, variableInfoMap,
5412 spirvBlobOut);
5413 transformer.transform();
5414
5415 // If there are aliasing vertex attributes, transform the SPIR-V again to remove them.
5416 if (hasAliasingAttributes)
5417 {
5418 spirv::Blob preTransformBlob = std::move(*spirvBlobOut);
5419 SpirvVertexAttributeAliasingTransformer aliasingTransformer(
5420 preTransformBlob, variableInfoMap, std::move(transformer.getVariableInfoByIdMap()),
5421 spirvBlobOut);
5422 aliasingTransformer.transform();
5423 }
5424
5425 spirvBlobOut->shrink_to_fit();
5426
5427 if (options.validate)
5428 {
5429 ASSERT(spirv::Validate(*spirvBlobOut));
5430 }
5431
5432 return angle::Result::Continue;
5433 }
5434 } // namespace rx
5435