blob: 9241cbcc50730b226360be8cf4369a66dc78df34 [file] [log] [blame]
sfricke-samsung962cad92021-04-13 00:46:29 -07001/* Copyright (c) 2021 The Khronos Group Inc.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 *
15 * Author: Spencer Fricke <s.fricke@samsung.com>
16 */
17
18#include "shader_module.h"
19
20#include <sstream>
21#include <string>
22
23#include "vk_layer_data.h"
24#include "vk_layer_utils.h"
Jeremy Gebben5d970742021-05-31 16:04:14 -060025#include "pipeline_state.h"
26#include "descriptor_sets.h"
sfricke-samsung962cad92021-04-13 00:46:29 -070027
28void decoration_set::merge(decoration_set const &other) {
29 if (other.flags & location_bit) location = other.location;
30 if (other.flags & component_bit) component = other.component;
31 if (other.flags & input_attachment_index_bit) input_attachment_index = other.input_attachment_index;
32 if (other.flags & descriptor_set_bit) descriptor_set = other.descriptor_set;
33 if (other.flags & binding_bit) binding = other.binding;
34 if (other.flags & builtin_bit) builtin = other.builtin;
35 flags |= other.flags;
36}
37
38void decoration_set::add(uint32_t decoration, uint32_t value) {
39 switch (decoration) {
40 case spv::DecorationLocation:
41 flags |= location_bit;
42 location = value;
43 break;
44 case spv::DecorationPatch:
45 flags |= patch_bit;
46 break;
47 case spv::DecorationRelaxedPrecision:
48 flags |= relaxed_precision_bit;
49 break;
50 case spv::DecorationBlock:
51 flags |= block_bit;
52 break;
53 case spv::DecorationBufferBlock:
54 flags |= buffer_block_bit;
55 break;
56 case spv::DecorationComponent:
57 flags |= component_bit;
58 component = value;
59 break;
60 case spv::DecorationInputAttachmentIndex:
61 flags |= input_attachment_index_bit;
62 input_attachment_index = value;
63 break;
64 case spv::DecorationDescriptorSet:
65 flags |= descriptor_set_bit;
66 descriptor_set = value;
67 break;
68 case spv::DecorationBinding:
69 flags |= binding_bit;
70 binding = value;
71 break;
72 case spv::DecorationNonWritable:
73 flags |= nonwritable_bit;
74 break;
75 case spv::DecorationBuiltIn:
76 flags |= builtin_bit;
77 builtin = value;
78 break;
Lionel Landwerlin38d2e122021-07-21 14:21:47 +030079 case spv::DecorationNonReadable:
80 flags |= nonreadable_bit;
81 break;
ziga-lunarg9e94e112021-09-27 00:21:10 +020082 case spv::DecorationPerVertexNV:
83 flags |= per_vertex_bit;
84 break;
85 case spv::DecorationPassthroughNV:
86 flags |= passthrough_bit;
87 break;
sfricke-samsung962cad92021-04-13 00:46:29 -070088 }
89}
90
91std::string shader_struct_member::GetLocationDesc(uint32_t index_used_bytes) const {
92 std::string desc = "";
93 if (array_length_hierarchy.size() > 0) {
94 desc += " index:";
95 for (const auto block_size : array_block_size) {
96 desc += "[";
97 desc += std::to_string(index_used_bytes / (block_size * size));
98 desc += "]";
99 index_used_bytes = index_used_bytes % (block_size * size);
100 }
101 }
102 const int struct_members_size = static_cast<int>(struct_members.size());
103 if (struct_members_size > 0) {
104 desc += " member:";
105 for (int i = struct_members_size - 1; i >= 0; --i) {
106 if (index_used_bytes > struct_members[i].offset) {
107 desc += std::to_string(i);
108 desc += struct_members[i].GetLocationDesc(index_used_bytes - struct_members[i].offset);
109 break;
110 }
111 }
112 } else {
113 desc += " offset:";
114 desc += std::to_string(index_used_bytes);
115 }
116 return desc;
117}
118
119static unsigned ExecutionModelToShaderStageFlagBits(unsigned mode) {
120 switch (mode) {
121 case spv::ExecutionModelVertex:
122 return VK_SHADER_STAGE_VERTEX_BIT;
123 case spv::ExecutionModelTessellationControl:
124 return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
125 case spv::ExecutionModelTessellationEvaluation:
126 return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
127 case spv::ExecutionModelGeometry:
128 return VK_SHADER_STAGE_GEOMETRY_BIT;
129 case spv::ExecutionModelFragment:
130 return VK_SHADER_STAGE_FRAGMENT_BIT;
131 case spv::ExecutionModelGLCompute:
132 return VK_SHADER_STAGE_COMPUTE_BIT;
133 case spv::ExecutionModelRayGenerationNV:
134 return VK_SHADER_STAGE_RAYGEN_BIT_NV;
135 case spv::ExecutionModelAnyHitNV:
136 return VK_SHADER_STAGE_ANY_HIT_BIT_NV;
137 case spv::ExecutionModelClosestHitNV:
138 return VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV;
139 case spv::ExecutionModelMissNV:
140 return VK_SHADER_STAGE_MISS_BIT_NV;
141 case spv::ExecutionModelIntersectionNV:
142 return VK_SHADER_STAGE_INTERSECTION_BIT_NV;
143 case spv::ExecutionModelCallableNV:
144 return VK_SHADER_STAGE_CALLABLE_BIT_NV;
145 case spv::ExecutionModelTaskNV:
146 return VK_SHADER_STAGE_TASK_BIT_NV;
147 case spv::ExecutionModelMeshNV:
148 return VK_SHADER_STAGE_MESH_BIT_NV;
149 default:
150 return 0;
151 }
152}
153
154// For some analyses, we need to know about all ids referenced by the static call tree of a particular entrypoint. This is
155// important for identifying the set of shader resources actually used by an entrypoint, for example.
156// Note: we only explore parts of the image which might actually contain ids we care about for the above analyses.
157// - NOT the shader input/output interfaces.
158//
159// TODO: The set of interesting opcodes here was determined by eyeballing the SPIRV spec. It might be worth
160// converting parts of this to be generated from the machine-readable spec instead.
161layer_data::unordered_set<uint32_t> SHADER_MODULE_STATE::MarkAccessibleIds(spirv_inst_iter entrypoint) const {
162 layer_data::unordered_set<uint32_t> ids;
Jeremy Gebben84b838b2021-08-23 08:41:39 -0600163 if (entrypoint == end() || !has_valid_spirv) {
164 return ids;
165 }
sfricke-samsung962cad92021-04-13 00:46:29 -0700166 layer_data::unordered_set<uint32_t> worklist;
167 worklist.insert(entrypoint.word(2));
168
169 while (!worklist.empty()) {
170 auto id_iter = worklist.begin();
171 auto id = *id_iter;
172 worklist.erase(id_iter);
173
174 auto insn = get_def(id);
175 if (insn == end()) {
176 // ID is something we didn't collect in BuildDefIndex. that's OK -- we'll stumble across all kinds of things here
177 // that we may not care about.
178 continue;
179 }
180
181 // Try to add to the output set
182 if (!ids.insert(id).second) {
183 continue; // If we already saw this id, we don't want to walk it again.
184 }
185
186 switch (insn.opcode()) {
187 case spv::OpFunction:
188 // Scan whole body of the function, enlisting anything interesting
189 while (++insn, insn.opcode() != spv::OpFunctionEnd) {
190 switch (insn.opcode()) {
191 case spv::OpLoad:
192 worklist.insert(insn.word(3)); // ptr
193 break;
194 case spv::OpStore:
195 worklist.insert(insn.word(1)); // ptr
196 break;
197 case spv::OpAccessChain:
198 case spv::OpInBoundsAccessChain:
199 worklist.insert(insn.word(3)); // base ptr
200 break;
201 case spv::OpSampledImage:
202 case spv::OpImageSampleImplicitLod:
203 case spv::OpImageSampleExplicitLod:
204 case spv::OpImageSampleDrefImplicitLod:
205 case spv::OpImageSampleDrefExplicitLod:
206 case spv::OpImageSampleProjImplicitLod:
207 case spv::OpImageSampleProjExplicitLod:
208 case spv::OpImageSampleProjDrefImplicitLod:
209 case spv::OpImageSampleProjDrefExplicitLod:
210 case spv::OpImageFetch:
211 case spv::OpImageGather:
212 case spv::OpImageDrefGather:
213 case spv::OpImageRead:
214 case spv::OpImage:
215 case spv::OpImageQueryFormat:
216 case spv::OpImageQueryOrder:
217 case spv::OpImageQuerySizeLod:
218 case spv::OpImageQuerySize:
219 case spv::OpImageQueryLod:
220 case spv::OpImageQueryLevels:
221 case spv::OpImageQuerySamples:
222 case spv::OpImageSparseSampleImplicitLod:
223 case spv::OpImageSparseSampleExplicitLod:
224 case spv::OpImageSparseSampleDrefImplicitLod:
225 case spv::OpImageSparseSampleDrefExplicitLod:
226 case spv::OpImageSparseSampleProjImplicitLod:
227 case spv::OpImageSparseSampleProjExplicitLod:
228 case spv::OpImageSparseSampleProjDrefImplicitLod:
229 case spv::OpImageSparseSampleProjDrefExplicitLod:
230 case spv::OpImageSparseFetch:
231 case spv::OpImageSparseGather:
232 case spv::OpImageSparseDrefGather:
233 case spv::OpImageTexelPointer:
234 worklist.insert(insn.word(3)); // Image or sampled image
235 break;
236 case spv::OpImageWrite:
237 worklist.insert(insn.word(1)); // Image -- different operand order to above
238 break;
239 case spv::OpFunctionCall:
240 for (uint32_t i = 3; i < insn.len(); i++) {
241 worklist.insert(insn.word(i)); // fn itself, and all args
242 }
243 break;
244
245 case spv::OpExtInst:
246 for (uint32_t i = 5; i < insn.len(); i++) {
247 worklist.insert(insn.word(i)); // Operands to ext inst
248 }
249 break;
250
251 default: {
252 if (AtomicOperation(insn.opcode())) {
253 if (insn.opcode() == spv::OpAtomicStore) {
254 worklist.insert(insn.word(1)); // ptr
255 } else {
256 worklist.insert(insn.word(3)); // ptr
257 }
258 }
259 break;
260 }
261 }
262 }
263 break;
264 }
265 }
266
267 return ids;
268}
269
Jeremy Gebben84b838b2021-08-23 08:41:39 -0600270layer_data::optional<VkPrimitiveTopology> SHADER_MODULE_STATE::GetTopology(const spirv_inst_iter &entrypoint) const {
271 layer_data::optional<VkPrimitiveTopology> result;
272
sfricke-samsung962cad92021-04-13 00:46:29 -0700273 auto entrypoint_id = entrypoint.word(2);
274 bool is_point_mode = false;
275
276 auto it = execution_mode_inst.find(entrypoint_id);
277 if (it != execution_mode_inst.end()) {
278 for (auto insn : it->second) {
279 switch (insn.word(2)) {
280 case spv::ExecutionModePointMode:
281 // In tessellation shaders, PointMode is separate and trumps the tessellation topology.
282 is_point_mode = true;
283 break;
284
285 case spv::ExecutionModeOutputPoints:
Jeremy Gebben84b838b2021-08-23 08:41:39 -0600286 result.emplace(VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
sfricke-samsung962cad92021-04-13 00:46:29 -0700287 break;
288
289 case spv::ExecutionModeIsolines:
290 case spv::ExecutionModeOutputLineStrip:
Ricardo Garcia122f8f02021-09-28 16:47:19 +0200291 case spv::ExecutionModeOutputLinesNV:
Jeremy Gebben84b838b2021-08-23 08:41:39 -0600292 result.emplace(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP);
sfricke-samsung962cad92021-04-13 00:46:29 -0700293 break;
294
295 case spv::ExecutionModeTriangles:
296 case spv::ExecutionModeQuads:
297 case spv::ExecutionModeOutputTriangleStrip:
298 case spv::ExecutionModeOutputTrianglesNV:
Jeremy Gebben84b838b2021-08-23 08:41:39 -0600299 result.emplace(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
sfricke-samsung962cad92021-04-13 00:46:29 -0700300 break;
301 }
302 }
303 }
304
Jeremy Gebben84b838b2021-08-23 08:41:39 -0600305 if (is_point_mode) {
306 result.emplace(VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
307 }
308
309 return result;
sfricke-samsung962cad92021-04-13 00:46:29 -0700310}
311
312void SHADER_MODULE_STATE::BuildDefIndex() {
313 function_set func_set = {};
314 EntryPoint *entry_point = nullptr;
315
316 for (auto insn : *this) {
317 // offset is not 0, it means it's updated and the offset is in a Function.
318 if (func_set.offset) {
319 func_set.op_lists.emplace(insn.opcode(), insn.offset());
320 } else if (entry_point) {
321 entry_point->decorate_list.emplace(insn.opcode(), insn.offset());
322 }
323
324 switch (insn.opcode()) {
325 // Types
326 case spv::OpTypeVoid:
327 case spv::OpTypeBool:
328 case spv::OpTypeInt:
329 case spv::OpTypeFloat:
330 case spv::OpTypeVector:
331 case spv::OpTypeMatrix:
332 case spv::OpTypeImage:
333 case spv::OpTypeSampler:
334 case spv::OpTypeSampledImage:
335 case spv::OpTypeArray:
336 case spv::OpTypeRuntimeArray:
337 case spv::OpTypeStruct:
338 case spv::OpTypeOpaque:
339 case spv::OpTypePointer:
340 case spv::OpTypeFunction:
341 case spv::OpTypeEvent:
342 case spv::OpTypeDeviceEvent:
343 case spv::OpTypeReserveId:
344 case spv::OpTypeQueue:
345 case spv::OpTypePipe:
346 case spv::OpTypeAccelerationStructureNV:
347 case spv::OpTypeCooperativeMatrixNV:
348 def_index[insn.word(1)] = insn.offset();
349 break;
350
351 // Fixed constants
352 case spv::OpConstantTrue:
353 case spv::OpConstantFalse:
354 case spv::OpConstant:
355 case spv::OpConstantComposite:
356 case spv::OpConstantSampler:
357 case spv::OpConstantNull:
358 def_index[insn.word(2)] = insn.offset();
359 break;
360
361 // Specialization constants
362 case spv::OpSpecConstantTrue:
363 case spv::OpSpecConstantFalse:
364 case spv::OpSpecConstant:
365 case spv::OpSpecConstantComposite:
366 case spv::OpSpecConstantOp:
367 def_index[insn.word(2)] = insn.offset();
368 break;
369
sfricke-samsung58b84352021-07-31 21:41:04 -0700370 // Have a result that can be a pointer
sfricke-samsung962cad92021-04-13 00:46:29 -0700371 case spv::OpVariable:
sfricke-samsung58b84352021-07-31 21:41:04 -0700372 case spv::OpAccessChain:
373 case spv::OpInBoundsAccessChain:
374 case spv::OpFunctionParameter:
375 case spv::OpImageTexelPointer:
sfricke-samsung962cad92021-04-13 00:46:29 -0700376 def_index[insn.word(2)] = insn.offset();
377 break;
378
379 // Functions
380 case spv::OpFunction:
381 def_index[insn.word(2)] = insn.offset();
382 func_set.id = insn.word(2);
383 func_set.offset = insn.offset();
384 func_set.op_lists.clear();
385 break;
386
387 // Decorations
388 case spv::OpDecorate: {
389 auto target_id = insn.word(1);
390 decorations[target_id].add(insn.word(2), insn.len() > 3u ? insn.word(3) : 0u);
391 decoration_inst.push_back(insn);
392 if (insn.word(2) == spv::DecorationBuiltIn) {
393 builtin_decoration_list.emplace_back(insn.offset(), static_cast<spv::BuiltIn>(insn.word(3)));
Nathaniel Cesariocf69bda2021-06-22 13:23:42 -0600394 } else if (insn.word(2) == spv::DecorationSpecId) {
395 spec_const_map[insn.word(3)] = target_id;
sfricke-samsung962cad92021-04-13 00:46:29 -0700396 }
397
398 } break;
399 case spv::OpGroupDecorate: {
400 auto const &src = decorations[insn.word(1)];
401 for (auto i = 2u; i < insn.len(); i++) decorations[insn.word(i)].merge(src);
402 } break;
403 case spv::OpMemberDecorate: {
404 member_decoration_inst.push_back(insn);
405 if (insn.word(3) == spv::DecorationBuiltIn) {
406 builtin_decoration_list.emplace_back(insn.offset(), static_cast<spv::BuiltIn>(insn.word(4)));
407 }
408 } break;
409
410 // Entry points ... add to the entrypoint table
411 case spv::OpEntryPoint: {
412 if (entry_point != nullptr) {
413 multiple_entry_points = true;
414 }
415
416 // Entry points do not have an id (the id is the function id) and thus need their own table
417 auto entrypoint_name = reinterpret_cast<char const *>(&insn.word(3));
418 auto execution_model = insn.word(1);
419 auto entrypoint_stage = ExecutionModelToShaderStageFlagBits(execution_model);
420 entry_points.emplace(entrypoint_name,
421 EntryPoint{insn.offset(), static_cast<VkShaderStageFlagBits>(entrypoint_stage)});
422
423 auto range = entry_points.equal_range(entrypoint_name);
424 for (auto it = range.first; it != range.second; ++it) {
425 if (it->second.offset == insn.offset()) {
426 entry_point = &(it->second);
427 break;
428 }
429 }
430 assert(entry_point != nullptr);
431 break;
432 }
433 case spv::OpFunctionEnd: {
434 assert(entry_point != nullptr);
435 func_set.length = insn.offset() - func_set.offset;
436 entry_point->function_set_list.emplace_back(func_set);
437 break;
438 }
439
440 // Copy operations
441 case spv::OpCopyLogical:
442 case spv::OpCopyObject: {
443 def_index[insn.word(2)] = insn.offset();
444 break;
445 }
446
447 // Execution Mode
448 case spv::OpExecutionMode: {
449 execution_mode_inst[insn.word(1)].push_back(insn);
450 } break;
451
Lionel Landwerlin5f2065a2021-07-23 11:51:28 +0300452 case spv::OpLoad: {
453 def_index[insn.word(2)] = insn.offset();
454 } break;
455
sfricke-samsung962cad92021-04-13 00:46:29 -0700456 default:
sfricke-samsung58b84352021-07-31 21:41:04 -0700457 if (AtomicOperation(insn.opcode()) == true) {
458 // All atomics have a pointer referenced
459 spirv_inst_iter access;
460 if (insn.opcode() == spv::OpAtomicStore) {
461 access = get_def(insn.word(1));
462 } else {
463 access = get_def(insn.word(3));
464 def_index[insn.word(2)] = insn.offset();
465 }
466
467 atomic_instruction atomic;
468
469 auto pointer = get_def(access.word(1));
470 // spirv-val should catch if not pointer
471 assert(pointer.opcode() == spv::OpTypePointer);
472 atomic.storage_class = pointer.word(2);
473
474 auto data_type = get_def(pointer.word(3));
475 atomic.type = data_type.opcode();
476
477 // TODO - Should have a proper GetBitWidth like spirv-val does
478 assert(data_type.opcode() == spv::OpTypeFloat || data_type.opcode() == spv::OpTypeInt);
479 atomic.bit_width = data_type.word(2);
480
481 atomic_inst[insn.offset()] = atomic;
482 }
sfricke-samsung962cad92021-04-13 00:46:29 -0700483 // We don't care about any other defs for now.
484 break;
485 }
486 }
487}
488
489std::vector<uint32_t> SHADER_MODULE_STATE::PreprocessShaderBinary(uint32_t *src_binary, size_t binary_size, spv_target_env env) {
490 std::vector<uint32_t> src(src_binary, src_binary + binary_size / sizeof(uint32_t));
491
492 // Check if there are any group decoration instructions, and flatten them if found.
493 bool has_group_decoration = false;
494 bool done = false;
495
496 // Walk through the first part of the SPIR-V module, looking for group decoration and specialization constant instructions.
497 // Skip the header (5 words).
498 auto itr = spirv_inst_iter(src.begin(), src.begin() + 5);
499 auto itrend = spirv_inst_iter(src.begin(), src.end());
500 while (itr != itrend && !done) {
501 spv::Op opcode = (spv::Op)itr.opcode();
502 switch (opcode) {
503 case spv::OpDecorationGroup:
504 case spv::OpGroupDecorate:
505 case spv::OpGroupMemberDecorate:
506 has_group_decoration = true;
507 break;
508 case spv::OpSpecConstantTrue:
509 case spv::OpSpecConstantFalse:
510 case spv::OpSpecConstant:
511 case spv::OpSpecConstantComposite:
512 case spv::OpSpecConstantOp:
513 has_specialization_constants = true;
514 break;
515 case spv::OpFunction:
516 // An OpFunction indicates there are no more decorations
517 done = true;
518 break;
519 default:
520 break;
521 }
522 itr++;
523 }
524
525 if (has_group_decoration) {
526 spvtools::Optimizer optimizer(env);
527 optimizer.RegisterPass(spvtools::CreateFlattenDecorationPass());
528 std::vector<uint32_t> optimized_binary;
529 // Run optimizer to flatten decorations only, set skip_validation so as to not re-run validator
530 auto result =
531 optimizer.Run(src_binary, binary_size / sizeof(uint32_t), &optimized_binary, spvtools::ValidatorOptions(), true);
532 if (result) {
533 return optimized_binary;
534 }
535 }
536 // Return the original module.
537 return src;
538}
539
sfricke-samsung58b84352021-07-31 21:41:04 -0700540char const *StorageClassName(unsigned sc) {
sfricke-samsung962cad92021-04-13 00:46:29 -0700541 switch (sc) {
542 case spv::StorageClassInput:
543 return "input";
544 case spv::StorageClassOutput:
545 return "output";
546 case spv::StorageClassUniformConstant:
547 return "const uniform";
548 case spv::StorageClassUniform:
549 return "uniform";
550 case spv::StorageClassWorkgroup:
551 return "workgroup local";
552 case spv::StorageClassCrossWorkgroup:
553 return "workgroup global";
554 case spv::StorageClassPrivate:
555 return "private global";
556 case spv::StorageClassFunction:
557 return "function";
558 case spv::StorageClassGeneric:
559 return "generic";
560 case spv::StorageClassAtomicCounter:
561 return "atomic counter";
562 case spv::StorageClassImage:
563 return "image";
564 case spv::StorageClassPushConstant:
565 return "push constant";
566 case spv::StorageClassStorageBuffer:
567 return "storage buffer";
568 default:
569 return "unknown";
570 }
571}
572
573void SHADER_MODULE_STATE::DescribeTypeInner(std::ostringstream &ss, unsigned type) const {
574 auto insn = get_def(type);
575 assert(insn != end());
576
577 switch (insn.opcode()) {
578 case spv::OpTypeBool:
579 ss << "bool";
580 break;
581 case spv::OpTypeInt:
582 ss << (insn.word(3) ? 's' : 'u') << "int" << insn.word(2);
583 break;
584 case spv::OpTypeFloat:
585 ss << "float" << insn.word(2);
586 break;
587 case spv::OpTypeVector:
588 ss << "vec" << insn.word(3) << " of ";
589 DescribeTypeInner(ss, insn.word(2));
590 break;
591 case spv::OpTypeMatrix:
592 ss << "mat" << insn.word(3) << " of ";
593 DescribeTypeInner(ss, insn.word(2));
594 break;
595 case spv::OpTypeArray:
596 ss << "arr[" << GetConstantValueById(insn.word(3)) << "] of ";
597 DescribeTypeInner(ss, insn.word(2));
598 break;
599 case spv::OpTypeRuntimeArray:
600 ss << "runtime arr[] of ";
601 DescribeTypeInner(ss, insn.word(2));
602 break;
603 case spv::OpTypePointer:
604 ss << "ptr to " << StorageClassName(insn.word(2)) << " ";
605 DescribeTypeInner(ss, insn.word(3));
606 break;
607 case spv::OpTypeStruct: {
608 ss << "struct of (";
609 for (unsigned i = 2; i < insn.len(); i++) {
610 DescribeTypeInner(ss, insn.word(i));
611 if (i == insn.len() - 1) {
612 ss << ")";
613 } else {
614 ss << ", ";
615 }
616 }
617 break;
618 }
619 case spv::OpTypeSampler:
620 ss << "sampler";
621 break;
622 case spv::OpTypeSampledImage:
623 ss << "sampler+";
624 DescribeTypeInner(ss, insn.word(2));
625 break;
626 case spv::OpTypeImage:
627 ss << "image(dim=" << insn.word(3) << ", sampled=" << insn.word(7) << ")";
628 break;
629 case spv::OpTypeAccelerationStructureNV:
630 ss << "accelerationStruture";
631 break;
632 default:
633 ss << "oddtype";
634 break;
635 }
636}
637
638std::string SHADER_MODULE_STATE::DescribeType(unsigned type) const {
639 std::ostringstream ss;
640 DescribeTypeInner(ss, type);
641 return ss.str();
642}
643
644const SHADER_MODULE_STATE::EntryPoint *SHADER_MODULE_STATE::FindEntrypointStruct(char const *name,
645 VkShaderStageFlagBits stageBits) const {
646 auto range = entry_points.equal_range(name);
647 for (auto it = range.first; it != range.second; ++it) {
648 if (it->second.stage == stageBits) {
649 return &(it->second);
650 }
651 }
652 return nullptr;
653}
654
655spirv_inst_iter SHADER_MODULE_STATE::FindEntrypoint(char const *name, VkShaderStageFlagBits stageBits) const {
656 auto range = entry_points.equal_range(name);
657 for (auto it = range.first; it != range.second; ++it) {
658 if (it->second.stage == stageBits) {
659 return at(it->second.offset);
660 }
661 }
662 return end();
663}
664
665// Because the following is legal, need the entry point
666// OpEntryPoint GLCompute %main "name_a"
667// OpEntryPoint GLCompute %main "name_b"
668bool SHADER_MODULE_STATE::FindLocalSize(const spirv_inst_iter &entrypoint, uint32_t &local_size_x, uint32_t &local_size_y,
669 uint32_t &local_size_z) const {
670 auto entrypoint_id = entrypoint.word(2);
671 auto it = execution_mode_inst.find(entrypoint_id);
672 if (it != execution_mode_inst.end()) {
673 for (auto insn : it->second) {
674 // Future Note: For now, Vulkan doesn't have a valid mode that can makes use of OpExecutionModeId
675 // In the future if something like LocalSizeId is supported, the <id> will need to be checked also
676 assert(insn.opcode() == spv::OpExecutionMode);
677 if (insn.word(2) == spv::ExecutionModeLocalSize) {
678 local_size_x = insn.word(3);
679 local_size_y = insn.word(4);
680 local_size_z = insn.word(5);
681 return true;
682 }
683 }
684 }
685 return false;
686}
687
688// If the instruction at id is a constant or copy of a constant, returns a valid iterator pointing to that instruction.
689// Otherwise, returns src->end().
690spirv_inst_iter SHADER_MODULE_STATE::GetConstantDef(unsigned id) const {
691 auto value = get_def(id);
692
693 // If id is a copy, see where it was copied from
694 if ((end() != value) && ((value.opcode() == spv::OpCopyObject) || (value.opcode() == spv::OpCopyLogical))) {
695 id = value.word(3);
696 value = get_def(id);
697 }
698
699 if ((end() != value) && (value.opcode() == spv::OpConstant)) {
700 return value;
701 }
702 return end();
703}
704
705// Either returns the constant value described by the instruction at id, or 1
706uint32_t SHADER_MODULE_STATE::GetConstantValueById(unsigned id) const {
707 auto value = GetConstantDef(id);
708
709 if (end() == value) {
710 // TODO: Either ensure that the specialization transform is already performed on a module we're
711 // considering here, OR -- specialize on the fly now.
712 return 1;
713 }
714 return GetConstantValue(value);
715}
716
717// Returns an int32_t corresponding to the spv::Dim of the given resource, when positive, and corresponding to an unknown type, when
718// negative.
719int32_t SHADER_MODULE_STATE::GetShaderResourceDimensionality(const interface_var &resource) const {
720 auto type = get_def(resource.type_id);
721 while (true) {
722 switch (type.opcode()) {
723 case spv::OpTypeSampledImage:
724 type = get_def(type.word(2));
725 break;
726 case spv::OpTypePointer:
727 type = get_def(type.word(3));
728 break;
729 case spv::OpTypeImage:
730 return type.word(3);
731 default:
732 return -1;
733 }
734 }
735}
736
737unsigned SHADER_MODULE_STATE::GetLocationsConsumedByType(unsigned type, bool strip_array_level) const {
738 auto insn = get_def(type);
739 assert(insn != end());
740
741 switch (insn.opcode()) {
742 case spv::OpTypePointer:
743 // See through the ptr -- this is only ever at the toplevel for graphics shaders we're never actually passing
744 // pointers around.
745 return GetLocationsConsumedByType(insn.word(3), strip_array_level);
746 case spv::OpTypeArray:
747 if (strip_array_level) {
748 return GetLocationsConsumedByType(insn.word(2), false);
749 } else {
750 return GetConstantValueById(insn.word(3)) * GetLocationsConsumedByType(insn.word(2), false);
751 }
752 case spv::OpTypeMatrix:
753 // Num locations is the dimension * element size
754 return insn.word(3) * GetLocationsConsumedByType(insn.word(2), false);
755 case spv::OpTypeVector: {
756 auto scalar_type = get_def(insn.word(2));
757 auto bit_width =
758 (scalar_type.opcode() == spv::OpTypeInt || scalar_type.opcode() == spv::OpTypeFloat) ? scalar_type.word(2) : 32;
759
760 // Locations are 128-bit wide; 3- and 4-component vectors of 64 bit types require two.
761 return (bit_width * insn.word(3) + 127) / 128;
762 }
763 default:
764 // Everything else is just 1.
765 return 1;
766
767 // TODO: extend to handle 64bit scalar types, whose vectors may need multiple locations.
768 }
769}
770
771unsigned SHADER_MODULE_STATE::GetComponentsConsumedByType(unsigned type, bool strip_array_level) const {
772 auto insn = get_def(type);
773 assert(insn != end());
774
775 switch (insn.opcode()) {
776 case spv::OpTypePointer:
777 // See through the ptr -- this is only ever at the toplevel for graphics shaders we're never actually passing
778 // pointers around.
779 return GetComponentsConsumedByType(insn.word(3), strip_array_level);
780 case spv::OpTypeStruct: {
781 uint32_t sum = 0;
782 for (uint32_t i = 2; i < insn.len(); i++) { // i=2 to skip word(0) and word(1)=ID of struct
783 sum += GetComponentsConsumedByType(insn.word(i), false);
784 }
785 return sum;
786 }
787 case spv::OpTypeArray:
788 if (strip_array_level) {
789 return GetComponentsConsumedByType(insn.word(2), false);
790 } else {
791 return GetConstantValueById(insn.word(3)) * GetComponentsConsumedByType(insn.word(2), false);
792 }
793 case spv::OpTypeMatrix:
794 // Num locations is the dimension * element size
795 return insn.word(3) * GetComponentsConsumedByType(insn.word(2), false);
796 case spv::OpTypeVector: {
797 auto scalar_type = get_def(insn.word(2));
798 auto bit_width =
799 (scalar_type.opcode() == spv::OpTypeInt || scalar_type.opcode() == spv::OpTypeFloat) ? scalar_type.word(2) : 32;
800 // One component is 32-bit
801 return (bit_width * insn.word(3) + 31) / 32;
802 }
803 case spv::OpTypeFloat: {
804 auto bit_width = insn.word(2);
805 return (bit_width + 31) / 32;
806 }
807 case spv::OpTypeInt: {
808 auto bit_width = insn.word(2);
809 return (bit_width + 31) / 32;
810 }
811 case spv::OpConstant:
812 return GetComponentsConsumedByType(insn.word(1), false);
813 default:
814 return 0;
815 }
816}
817
818// characterizes a SPIR-V type appearing in an interface to a FF stage, for comparison to a VkFormat's characterization above.
819// also used for input attachments, as we statically know their format.
820unsigned SHADER_MODULE_STATE::GetFundamentalType(unsigned type) const {
821 auto insn = get_def(type);
822 assert(insn != end());
823
824 switch (insn.opcode()) {
825 case spv::OpTypeInt:
826 return insn.word(3) ? FORMAT_TYPE_SINT : FORMAT_TYPE_UINT;
827 case spv::OpTypeFloat:
828 return FORMAT_TYPE_FLOAT;
829 case spv::OpTypeVector:
830 case spv::OpTypeMatrix:
831 case spv::OpTypeArray:
832 case spv::OpTypeRuntimeArray:
833 case spv::OpTypeImage:
834 return GetFundamentalType(insn.word(2));
835 case spv::OpTypePointer:
836 return GetFundamentalType(insn.word(3));
837
838 default:
839 return 0;
840 }
841}
842
843spirv_inst_iter SHADER_MODULE_STATE::GetStructType(spirv_inst_iter def, bool is_array_of_verts) const {
844 while (true) {
845 if (def.opcode() == spv::OpTypePointer) {
846 def = get_def(def.word(3));
847 } else if (def.opcode() == spv::OpTypeArray && is_array_of_verts) {
848 def = get_def(def.word(2));
849 is_array_of_verts = false;
850 } else if (def.opcode() == spv::OpTypeStruct) {
851 return def;
852 } else {
853 return end();
854 }
855 }
856}
857
858void SHADER_MODULE_STATE::DefineStructMember(const spirv_inst_iter &it, const std::vector<uint32_t> &memberDecorate_offsets,
859 shader_struct_member &data) const {
860 const auto struct_it = GetStructType(it, false);
861 assert(struct_it != end());
862 data.size = 0;
863
864 shader_struct_member data1;
865 uint32_t i = 2;
866 uint32_t local_offset = 0;
867 std::vector<uint32_t> offsets;
868 offsets.resize(struct_it.len() - i);
869
870 // The members of struct in SPRIV_R aren't always sort, so we need to know their order.
871 for (const auto offset : memberDecorate_offsets) {
872 const auto member_decorate = at(offset);
873 if (member_decorate.word(1) != struct_it.word(1)) {
874 continue;
875 }
876
877 offsets[member_decorate.word(2)] = member_decorate.word(4);
878 }
879
880 for (const auto offset : offsets) {
881 local_offset = offset;
882 data1 = {};
883 data1.root = data.root;
884 data1.offset = local_offset;
885 auto def_member = get_def(struct_it.word(i));
886
887 // Array could be multi-dimensional
888 while (def_member.opcode() == spv::OpTypeArray) {
889 const auto len_id = def_member.word(3);
890 const auto def_len = get_def(len_id);
891 data1.array_length_hierarchy.emplace_back(def_len.word(3)); // array length
892 def_member = get_def(def_member.word(2));
893 }
894
895 if (def_member.opcode() == spv::OpTypeStruct) {
896 DefineStructMember(def_member, memberDecorate_offsets, data1);
897 } else if (def_member.opcode() == spv::OpTypePointer) {
898 if (def_member.word(2) == spv::StorageClassPhysicalStorageBuffer) {
899 // If it's a pointer with PhysicalStorageBuffer class, this member is essentially a uint64_t containing an address
900 // that "points to something."
901 data1.size = 8;
902 } else {
903 // If it's OpTypePointer. it means the member is a buffer, the type will be TypePointer, and then struct
904 DefineStructMember(def_member, memberDecorate_offsets, data1);
905 }
906 } else {
907 if (def_member.opcode() == spv::OpTypeMatrix) {
908 data1.array_length_hierarchy.emplace_back(def_member.word(3)); // matrix's columns. matrix's row is vector.
909 def_member = get_def(def_member.word(2));
910 }
911
912 if (def_member.opcode() == spv::OpTypeVector) {
913 data1.array_length_hierarchy.emplace_back(def_member.word(3)); // vector length
914 def_member = get_def(def_member.word(2));
915 }
916
917 // Get scalar type size. The value in SPRV-R is bit. It needs to translate to byte.
918 data1.size = (def_member.word(2) / 8);
919 }
920 const auto array_length_hierarchy_szie = data1.array_length_hierarchy.size();
921 if (array_length_hierarchy_szie > 0) {
922 data1.array_block_size.resize(array_length_hierarchy_szie, 1);
923
924 for (int i2 = static_cast<int>(array_length_hierarchy_szie - 1); i2 > 0; --i2) {
925 data1.array_block_size[i2 - 1] = data1.array_length_hierarchy[i2] * data1.array_block_size[i2];
926 }
927 }
928 data.struct_members.emplace_back(data1);
929 ++i;
930 }
931 uint32_t total_array_length = 1;
932 for (const auto length : data1.array_length_hierarchy) {
933 total_array_length *= length;
934 }
935 data.size = local_offset + data1.size * total_array_length;
936}
937
938static uint32_t UpdateOffset(uint32_t offset, const std::vector<uint32_t> &array_indices, const shader_struct_member &data) {
939 int array_indices_size = static_cast<int>(array_indices.size());
940 if (array_indices_size) {
941 uint32_t array_index = 0;
942 uint32_t i = 0;
943 for (const auto index : array_indices) {
944 array_index += (data.array_block_size[i] * index);
945 ++i;
946 }
947 offset += (array_index * data.size);
948 }
949 return offset;
950}
951
952static void SetUsedBytes(uint32_t offset, const std::vector<uint32_t> &array_indices, const shader_struct_member &data) {
953 int array_indices_size = static_cast<int>(array_indices.size());
954 uint32_t block_memory_size = data.size;
955 for (uint32_t i = static_cast<int>(array_indices_size); i < data.array_length_hierarchy.size(); ++i) {
956 block_memory_size *= data.array_length_hierarchy[i];
957 }
958
959 offset = UpdateOffset(offset, array_indices, data);
960
961 uint32_t end = offset + block_memory_size;
962 auto used_bytes = data.GetUsedbytes();
963 if (used_bytes->size() < end) {
964 used_bytes->resize(end, 0);
965 }
966 std::memset(used_bytes->data() + offset, true, static_cast<std::size_t>(block_memory_size));
967}
968
969void SHADER_MODULE_STATE::RunUsedArray(uint32_t offset, std::vector<uint32_t> array_indices, uint32_t access_chain_word_index,
970 spirv_inst_iter &access_chain_it, const shader_struct_member &data) const {
971 if (access_chain_word_index < access_chain_it.len()) {
972 if (data.array_length_hierarchy.size() > array_indices.size()) {
973 auto def_it = get_def(access_chain_it.word(access_chain_word_index));
974 ++access_chain_word_index;
975
976 if (def_it != end() && def_it.opcode() == spv::OpConstant) {
977 array_indices.emplace_back(def_it.word(3));
978 RunUsedArray(offset, array_indices, access_chain_word_index, access_chain_it, data);
979 } else {
980 // If it is a variable, set the all array is used.
981 if (access_chain_word_index < access_chain_it.len()) {
982 uint32_t array_length = data.array_length_hierarchy[array_indices.size()];
983 for (uint32_t i = 0; i < array_length; ++i) {
984 auto array_indices2 = array_indices;
985 array_indices2.emplace_back(i);
986 RunUsedArray(offset, array_indices2, access_chain_word_index, access_chain_it, data);
987 }
988 } else {
989 SetUsedBytes(offset, array_indices, data);
990 }
991 }
992 } else {
993 offset = UpdateOffset(offset, array_indices, data);
994 RunUsedStruct(offset, access_chain_word_index, access_chain_it, data);
995 }
996 } else {
997 SetUsedBytes(offset, array_indices, data);
998 }
999}
1000
1001void SHADER_MODULE_STATE::RunUsedStruct(uint32_t offset, uint32_t access_chain_word_index, spirv_inst_iter &access_chain_it,
1002 const shader_struct_member &data) const {
1003 std::vector<uint32_t> array_indices_emptry;
1004
1005 if (access_chain_word_index < access_chain_it.len()) {
1006 auto strcut_member_index = GetConstantValueById(access_chain_it.word(access_chain_word_index));
1007 ++access_chain_word_index;
1008
1009 auto data1 = data.struct_members[strcut_member_index];
1010 RunUsedArray(offset + data1.offset, array_indices_emptry, access_chain_word_index, access_chain_it, data1);
1011 }
1012}
1013
1014void SHADER_MODULE_STATE::SetUsedStructMember(const uint32_t variable_id, const std::vector<function_set> &function_set_list,
1015 const shader_struct_member &data) const {
1016 for (const auto &func_set : function_set_list) {
1017 auto range = func_set.op_lists.equal_range(spv::OpAccessChain);
1018 for (auto it = range.first; it != range.second; ++it) {
1019 auto access_chain = at(it->second);
1020 if (access_chain.word(3) == variable_id) {
1021 RunUsedStruct(0, 4, access_chain, data);
1022 }
1023 }
1024 }
1025}
1026
1027void SHADER_MODULE_STATE::SetPushConstantUsedInShader() {
1028 for (auto &entrypoint : entry_points) {
1029 auto range = entrypoint.second.decorate_list.equal_range(spv::OpVariable);
1030 for (auto it = range.first; it != range.second; ++it) {
1031 const auto def_insn = at(it->second);
1032
1033 if (def_insn.word(3) == spv::StorageClassPushConstant) {
1034 spirv_inst_iter type = get_def(def_insn.word(1));
1035 const auto range2 = entrypoint.second.decorate_list.equal_range(spv::OpMemberDecorate);
1036 std::vector<uint32_t> offsets;
1037
1038 for (auto it2 = range2.first; it2 != range2.second; ++it2) {
1039 auto member_decorate = at(it2->second);
1040 if (member_decorate.len() == 5 && member_decorate.word(3) == spv::DecorationOffset) {
1041 offsets.emplace_back(member_decorate.offset());
1042 }
1043 }
1044 entrypoint.second.push_constant_used_in_shader.root = &entrypoint.second.push_constant_used_in_shader;
1045 DefineStructMember(type, offsets, entrypoint.second.push_constant_used_in_shader);
1046 SetUsedStructMember(def_insn.word(2), entrypoint.second.function_set_list,
1047 entrypoint.second.push_constant_used_in_shader);
1048 }
1049 }
1050 }
1051}
1052
1053uint32_t SHADER_MODULE_STATE::DescriptorTypeToReqs(uint32_t type_id) const {
1054 auto type = get_def(type_id);
1055
1056 while (true) {
1057 switch (type.opcode()) {
1058 case spv::OpTypeArray:
1059 case spv::OpTypeRuntimeArray:
1060 case spv::OpTypeSampledImage:
1061 type = get_def(type.word(2));
1062 break;
1063 case spv::OpTypePointer:
1064 type = get_def(type.word(3));
1065 break;
1066 case spv::OpTypeImage: {
1067 auto dim = type.word(3);
1068 auto arrayed = type.word(5);
1069 auto msaa = type.word(6);
1070
1071 uint32_t bits = 0;
1072 switch (GetFundamentalType(type.word(2))) {
1073 case FORMAT_TYPE_FLOAT:
1074 bits = DESCRIPTOR_REQ_COMPONENT_TYPE_FLOAT;
1075 break;
1076 case FORMAT_TYPE_UINT:
1077 bits = DESCRIPTOR_REQ_COMPONENT_TYPE_UINT;
1078 break;
1079 case FORMAT_TYPE_SINT:
1080 bits = DESCRIPTOR_REQ_COMPONENT_TYPE_SINT;
1081 break;
1082 default:
1083 break;
1084 }
1085
1086 switch (dim) {
1087 case spv::Dim1D:
1088 bits |= arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_1D_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_1D;
1089 return bits;
1090 case spv::Dim2D:
1091 bits |= msaa ? DESCRIPTOR_REQ_MULTI_SAMPLE : DESCRIPTOR_REQ_SINGLE_SAMPLE;
1092 bits |= arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_2D_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_2D;
1093 return bits;
1094 case spv::Dim3D:
1095 bits |= DESCRIPTOR_REQ_VIEW_TYPE_3D;
1096 return bits;
1097 case spv::DimCube:
1098 bits |= arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_CUBE_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_CUBE;
1099 return bits;
1100 case spv::DimSubpassData:
1101 bits |= msaa ? DESCRIPTOR_REQ_MULTI_SAMPLE : DESCRIPTOR_REQ_SINGLE_SAMPLE;
1102 return bits;
1103 default: // buffer, etc.
1104 return bits;
1105 }
1106 }
1107 default:
1108 return 0;
1109 }
1110 }
1111}
1112
1113// For some built-in analysis we need to know if the variable decorated with as the built-in was actually written to.
1114// This function examines instructions in the static call tree for a write to this variable.
1115bool SHADER_MODULE_STATE::IsBuiltInWritten(spirv_inst_iter builtin_instr, spirv_inst_iter entrypoint) const {
1116 auto type = builtin_instr.opcode();
1117 uint32_t target_id = builtin_instr.word(1);
1118 bool init_complete = false;
1119
1120 if (type == spv::OpMemberDecorate) {
1121 // Built-in is part of a structure -- examine instructions up to first function body to get initial IDs
1122 auto insn = entrypoint;
1123 while (!init_complete && (insn.opcode() != spv::OpFunction)) {
1124 switch (insn.opcode()) {
1125 case spv::OpTypePointer:
Nathaniel Cesario58fc2282021-08-18 12:20:40 -06001126 if (insn.word(2) == spv::StorageClassOutput) {
1127 const auto type_id = insn.word(3);
1128 if (type_id == target_id) {
1129 target_id = insn.word(1);
1130 } else {
1131 // If the output is an array, check if the element type is what we're looking for
1132 const auto type_insn = get_def(type_id);
1133 if ((type_insn.opcode() == spv::OpTypeArray) && (type_insn.word(2) == target_id)) {
1134 target_id = insn.word(1);
1135 }
1136 }
sfricke-samsung962cad92021-04-13 00:46:29 -07001137 }
1138 break;
1139 case spv::OpVariable:
1140 if (insn.word(1) == target_id) {
1141 target_id = insn.word(2);
1142 init_complete = true;
1143 }
1144 break;
1145 }
1146 insn++;
1147 }
1148 }
1149
1150 if (!init_complete && (type == spv::OpMemberDecorate)) return false;
1151
1152 bool found_write = false;
1153 layer_data::unordered_set<uint32_t> worklist;
1154 worklist.insert(entrypoint.word(2));
1155
1156 // Follow instructions in call graph looking for writes to target
1157 while (!worklist.empty() && !found_write) {
1158 auto id_iter = worklist.begin();
1159 auto id = *id_iter;
1160 worklist.erase(id_iter);
1161
1162 auto insn = get_def(id);
1163 if (insn == end()) {
1164 continue;
1165 }
1166
1167 if (insn.opcode() == spv::OpFunction) {
1168 // Scan body of function looking for other function calls or items in our ID chain
Nathaniel Cesario58fc2282021-08-18 12:20:40 -06001169 while (++insn, (insn.opcode() != spv::OpFunctionEnd) && !found_write) {
sfricke-samsung962cad92021-04-13 00:46:29 -07001170 switch (insn.opcode()) {
1171 case spv::OpAccessChain:
1172 if (insn.word(3) == target_id) {
1173 if (type == spv::OpMemberDecorate) {
Nathaniel Cesario58fc2282021-08-18 12:20:40 -06001174 // The last member offset in the chain should match the decorator offset
1175 auto value = GetConstantValueById(insn.word(insn.len() - 1));
sfricke-samsung962cad92021-04-13 00:46:29 -07001176 if (value == builtin_instr.word(2)) {
1177 target_id = insn.word(2);
1178 }
1179 } else {
1180 target_id = insn.word(2);
1181 }
1182 }
1183 break;
1184 case spv::OpStore:
1185 if (insn.word(1) == target_id) {
1186 found_write = true;
1187 }
1188 break;
1189 case spv::OpFunctionCall:
1190 worklist.insert(insn.word(3));
1191 break;
1192 }
1193 }
1194 }
1195 }
1196 return found_write;
1197}
1198
1199// Used by the collection functions to help aid in state tracking
1200struct shader_module_used_operators {
1201 bool updated;
1202 std::vector<unsigned> imagwrite_members;
1203 std::vector<unsigned> atomic_members;
1204 std::vector<unsigned> store_members;
1205 std::vector<unsigned> atomic_store_members;
1206 std::vector<unsigned> sampler_implicitLod_dref_proj_members; // sampler Load id
1207 std::vector<unsigned> sampler_bias_offset_members; // sampler Load id
1208 std::vector<std::pair<unsigned, unsigned>> sampledImage_members; // <image,sampler> Load id
1209 layer_data::unordered_map<unsigned, unsigned> load_members;
1210 layer_data::unordered_map<unsigned, std::pair<unsigned, unsigned>> accesschain_members;
1211 layer_data::unordered_map<unsigned, unsigned> image_texel_pointer_members;
1212
1213 shader_module_used_operators() : updated(false) {}
1214
1215 bool CheckImageOperandsBiasOffset(uint32_t type) {
1216 return type & (spv::ImageOperandsBiasMask | spv::ImageOperandsConstOffsetMask | spv::ImageOperandsOffsetMask |
1217 spv::ImageOperandsConstOffsetsMask)
1218 ? true
1219 : false;
1220 }
1221
1222 void update(SHADER_MODULE_STATE const *module) {
1223 if (updated) return;
1224 updated = true;
1225
1226 for (auto insn : *module) {
1227 switch (insn.opcode()) {
1228 case spv::OpImageSampleImplicitLod:
1229 case spv::OpImageSampleProjImplicitLod:
1230 case spv::OpImageSampleProjExplicitLod:
1231 case spv::OpImageSparseSampleImplicitLod:
1232 case spv::OpImageSparseSampleProjImplicitLod:
1233 case spv::OpImageSparseSampleProjExplicitLod: {
1234 sampler_implicitLod_dref_proj_members.emplace_back(insn.word(3)); // Load id
1235 // ImageOperands in index: 5
1236 if (insn.len() > 5 && CheckImageOperandsBiasOffset(insn.word(5))) {
1237 sampler_bias_offset_members.emplace_back(insn.word(3));
1238 }
1239 break;
1240 }
1241 case spv::OpImageSampleDrefImplicitLod:
1242 case spv::OpImageSampleDrefExplicitLod:
1243 case spv::OpImageSampleProjDrefImplicitLod:
1244 case spv::OpImageSampleProjDrefExplicitLod:
1245 case spv::OpImageSparseSampleDrefImplicitLod:
1246 case spv::OpImageSparseSampleDrefExplicitLod:
1247 case spv::OpImageSparseSampleProjDrefImplicitLod:
1248 case spv::OpImageSparseSampleProjDrefExplicitLod: {
1249 sampler_implicitLod_dref_proj_members.emplace_back(insn.word(3)); // Load id
1250 // ImageOperands in index: 6
1251 if (insn.len() > 6 && CheckImageOperandsBiasOffset(insn.word(6))) {
1252 sampler_bias_offset_members.emplace_back(insn.word(3));
1253 }
1254 break;
1255 }
1256 case spv::OpImageSampleExplicitLod:
1257 case spv::OpImageSparseSampleExplicitLod: {
1258 // ImageOperands in index: 5
1259 if (insn.len() > 5 && CheckImageOperandsBiasOffset(insn.word(5))) {
1260 sampler_bias_offset_members.emplace_back(insn.word(3));
1261 }
1262 break;
1263 }
1264 case spv::OpStore: {
1265 store_members.emplace_back(insn.word(1)); // object id or AccessChain id
1266 break;
1267 }
1268 case spv::OpImageWrite: {
1269 imagwrite_members.emplace_back(insn.word(1)); // Load id
1270 break;
1271 }
1272 case spv::OpSampledImage: {
1273 // 3: image load id, 4: sampler load id
1274 sampledImage_members.emplace_back(std::pair<unsigned, unsigned>(insn.word(3), insn.word(4)));
1275 break;
1276 }
1277 case spv::OpLoad: {
1278 // 2: Load id, 3: object id or AccessChain id
1279 load_members.emplace(insn.word(2), insn.word(3));
1280 break;
1281 }
1282 case spv::OpAccessChain: {
1283 if (insn.len() == 4) {
1284 // If it is for struct, the length is only 4.
1285 // 2: AccessChain id, 3: object id
1286 accesschain_members.emplace(insn.word(2), std::pair<unsigned, unsigned>(insn.word(3), 0));
1287 } else {
1288 // 2: AccessChain id, 3: object id, 4: object id of array index
1289 accesschain_members.emplace(insn.word(2), std::pair<unsigned, unsigned>(insn.word(3), insn.word(4)));
1290 }
1291 break;
1292 }
1293 case spv::OpImageTexelPointer: {
1294 // 2: ImageTexelPointer id, 3: object id
1295 image_texel_pointer_members.emplace(insn.word(2), insn.word(3));
1296 break;
1297 }
1298 default: {
1299 if (AtomicOperation(insn.opcode())) {
1300 if (insn.opcode() == spv::OpAtomicStore) {
1301 atomic_store_members.emplace_back(insn.word(1)); // ImageTexelPointer id
1302 } else {
1303 atomic_members.emplace_back(insn.word(3)); // ImageTexelPointer id
1304 }
1305 }
1306 break;
1307 }
1308 }
1309 }
1310 }
1311};
1312
1313static bool CheckObjectIDFromOpLoad(uint32_t object_id, const std::vector<unsigned> &operator_members,
1314 const layer_data::unordered_map<unsigned, unsigned> &load_members,
1315 const layer_data::unordered_map<unsigned, std::pair<unsigned, unsigned>> &accesschain_members) {
1316 for (auto load_id : operator_members) {
1317 if (object_id == load_id) return true;
1318 auto load_it = load_members.find(load_id);
1319 if (load_it == load_members.end()) {
1320 continue;
1321 }
1322 if (load_it->second == object_id) {
1323 return true;
1324 }
1325
1326 auto accesschain_it = accesschain_members.find(load_it->second);
1327 if (accesschain_it == accesschain_members.end()) {
1328 continue;
1329 }
1330 if (accesschain_it->second.first == object_id) {
1331 return true;
1332 }
1333 }
1334 return false;
1335}
1336
1337// Takes a OpVariable and looks at the the descriptor type it uses. This will find things such as if the variable is writable, image
1338// atomic operation, matching images to samplers, etc
1339void SHADER_MODULE_STATE::IsSpecificDescriptorType(const spirv_inst_iter &id_it, bool is_storage_buffer, bool is_check_writable,
1340 interface_var &out_interface_var,
1341 shader_module_used_operators &used_operators) const {
1342 uint32_t type_id = id_it.word(1);
1343 unsigned int id = id_it.word(2);
1344
1345 auto type = get_def(type_id);
1346
1347 // Strip off any array or ptrs. Where we remove array levels, adjust the descriptor count for each dimension.
1348 while (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypePointer || type.opcode() == spv::OpTypeRuntimeArray ||
1349 type.opcode() == spv::OpTypeSampledImage) {
1350 if (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypeRuntimeArray ||
1351 type.opcode() == spv::OpTypeSampledImage) {
1352 type = get_def(type.word(2)); // Element type
1353 } else {
1354 type = get_def(type.word(3)); // Pointer type
1355 }
1356 }
1357 switch (type.opcode()) {
1358 case spv::OpTypeImage: {
1359 auto dim = type.word(3);
1360 if (dim != spv::DimSubpassData) {
1361 used_operators.update(this);
1362
1363 if (CheckObjectIDFromOpLoad(id, used_operators.imagwrite_members, used_operators.load_members,
1364 used_operators.accesschain_members)) {
1365 out_interface_var.is_writable = true;
1366 }
1367 if (CheckObjectIDFromOpLoad(id, used_operators.sampler_implicitLod_dref_proj_members, used_operators.load_members,
1368 used_operators.accesschain_members)) {
1369 out_interface_var.is_sampler_implicitLod_dref_proj = true;
1370 }
1371 if (CheckObjectIDFromOpLoad(id, used_operators.sampler_bias_offset_members, used_operators.load_members,
1372 used_operators.accesschain_members)) {
1373 out_interface_var.is_sampler_bias_offset = true;
1374 }
1375 if (CheckObjectIDFromOpLoad(id, used_operators.atomic_members, used_operators.image_texel_pointer_members,
1376 used_operators.accesschain_members) ||
1377 CheckObjectIDFromOpLoad(id, used_operators.atomic_store_members, used_operators.image_texel_pointer_members,
1378 used_operators.accesschain_members)) {
1379 out_interface_var.is_atomic_operation = true;
1380 }
1381
1382 for (auto &itp_id : used_operators.sampledImage_members) {
1383 // Find if image id match.
1384 uint32_t image_index = 0;
1385 auto load_it = used_operators.load_members.find(itp_id.first);
1386 if (load_it == used_operators.load_members.end()) {
1387 continue;
1388 } else {
1389 if (load_it->second != id) {
1390 auto accesschain_it = used_operators.accesschain_members.find(load_it->second);
1391 if (accesschain_it == used_operators.accesschain_members.end()) {
1392 continue;
1393 } else {
1394 if (accesschain_it->second.first != id) {
1395 continue;
1396 }
1397
1398 const auto const_itr = GetConstantDef(accesschain_it->second.second);
1399 if (const_itr == end()) {
1400 // access chain index not a constant, skip.
1401 break;
1402 }
1403 image_index = GetConstantValue(const_itr);
1404 }
1405 }
1406 }
1407 // Find sampler's set binding.
1408 load_it = used_operators.load_members.find(itp_id.second);
1409 if (load_it == used_operators.load_members.end()) {
1410 continue;
1411 } else {
1412 uint32_t sampler_id = load_it->second;
1413 uint32_t sampler_index = 0;
1414 auto accesschain_it = used_operators.accesschain_members.find(load_it->second);
1415
1416 if (accesschain_it != used_operators.accesschain_members.end()) {
1417 const auto const_itr = GetConstantDef(accesschain_it->second.second);
1418 if (const_itr == end()) {
1419 // access chain index representing sampler index is not a constant, skip.
1420 break;
1421 }
1422 sampler_id = const_itr.offset();
1423 sampler_index = GetConstantValue(const_itr);
1424 }
1425 auto sampler_dec = get_decorations(sampler_id);
1426 if (image_index >= out_interface_var.samplers_used_by_image.size()) {
1427 out_interface_var.samplers_used_by_image.resize(image_index + 1);
1428 }
1429 out_interface_var.samplers_used_by_image[image_index].emplace(
Jeremy Gebben7fc88a22021-08-25 13:30:45 -06001430 SamplerUsedByImage{DescriptorSlot{sampler_dec.descriptor_set, sampler_dec.binding}, sampler_index});
sfricke-samsung962cad92021-04-13 00:46:29 -07001431 }
1432 }
1433 }
1434 return;
1435 }
1436
1437 case spv::OpTypeStruct: {
1438 layer_data::unordered_set<unsigned> nonwritable_members;
1439 if (get_decorations(type.word(1)).flags & decoration_set::buffer_block_bit) is_storage_buffer = true;
1440 for (auto insn : member_decoration_inst) {
1441 if (insn.word(1) == type.word(1) && insn.word(3) == spv::DecorationNonWritable) {
1442 nonwritable_members.insert(insn.word(2));
1443 }
1444 }
1445
1446 // A buffer is writable if it's either flavor of storage buffer, and has any member not decorated
1447 // as nonwritable.
1448 if (is_storage_buffer && nonwritable_members.size() != type.len() - 2) {
1449 used_operators.update(this);
1450
1451 for (auto oid : used_operators.store_members) {
1452 if (id == oid) {
1453 out_interface_var.is_writable = true;
1454 return;
1455 }
1456 auto accesschain_it = used_operators.accesschain_members.find(oid);
1457 if (accesschain_it == used_operators.accesschain_members.end()) {
1458 continue;
1459 }
1460 if (accesschain_it->second.first == id) {
1461 out_interface_var.is_writable = true;
1462 return;
1463 }
1464 }
1465 if (CheckObjectIDFromOpLoad(id, used_operators.atomic_store_members, used_operators.image_texel_pointer_members,
1466 used_operators.accesschain_members)) {
1467 out_interface_var.is_writable = true;
1468 return;
1469 }
1470 }
1471 }
1472 }
1473}
1474
Jeremy Gebben7fc88a22021-08-25 13:30:45 -06001475std::vector<std::pair<DescriptorSlot, interface_var>> SHADER_MODULE_STATE::CollectInterfaceByDescriptorSlot(
Jeremy Gebben84b838b2021-08-23 08:41:39 -06001476 layer_data::unordered_set<uint32_t> const &accessible_ids) const {
Jeremy Gebben7fc88a22021-08-25 13:30:45 -06001477 std::vector<std::pair<DescriptorSlot, interface_var>> out;
sfricke-samsung962cad92021-04-13 00:46:29 -07001478 shader_module_used_operators operators;
1479
1480 for (auto id : accessible_ids) {
1481 auto insn = get_def(id);
1482 assert(insn != end());
1483
1484 if (insn.opcode() == spv::OpVariable &&
1485 (insn.word(3) == spv::StorageClassUniform || insn.word(3) == spv::StorageClassUniformConstant ||
1486 insn.word(3) == spv::StorageClassStorageBuffer)) {
1487 auto d = get_decorations(insn.word(2));
1488 unsigned set = d.descriptor_set;
1489 unsigned binding = d.binding;
1490
1491 interface_var v = {};
1492 v.id = insn.word(2);
1493 v.type_id = insn.word(1);
1494
1495 IsSpecificDescriptorType(insn, insn.word(3) == spv::StorageClassStorageBuffer,
1496 !(d.flags & decoration_set::nonwritable_bit), v, operators);
Jeremy Gebben84b838b2021-08-23 08:41:39 -06001497 out.emplace_back(DescriptorSlot{set, binding}, v);
sfricke-samsung962cad92021-04-13 00:46:29 -07001498 }
1499 }
1500
1501 return out;
1502}
1503
1504layer_data::unordered_set<uint32_t> SHADER_MODULE_STATE::CollectWritableOutputLocationinFS(
Jeremy Gebben84b838b2021-08-23 08:41:39 -06001505 const spirv_inst_iter &entrypoint) const {
sfricke-samsung962cad92021-04-13 00:46:29 -07001506 layer_data::unordered_set<uint32_t> location_list;
sfricke-samsung962cad92021-04-13 00:46:29 -07001507 const auto outputs = CollectInterfaceByLocation(entrypoint, spv::StorageClassOutput, false);
1508 layer_data::unordered_set<unsigned> store_members;
1509 layer_data::unordered_map<unsigned, unsigned> accesschain_members;
1510
1511 for (auto insn : *this) {
1512 switch (insn.opcode()) {
1513 case spv::OpStore:
1514 case spv::OpAtomicStore: {
1515 store_members.insert(insn.word(1)); // object id or AccessChain id
1516 break;
1517 }
1518 case spv::OpAccessChain: {
1519 // 2: AccessChain id, 3: object id
1520 if (insn.word(3)) accesschain_members.emplace(insn.word(2), insn.word(3));
1521 break;
1522 }
1523 default:
1524 break;
1525 }
1526 }
1527 if (store_members.empty()) {
1528 return location_list;
1529 }
1530 for (auto output : outputs) {
1531 auto store_it = store_members.find(output.second.id);
1532 if (store_it != store_members.end()) {
1533 location_list.insert(output.first.first);
1534 store_members.erase(store_it);
1535 continue;
1536 }
1537 store_it = store_members.begin();
1538 while (store_it != store_members.end()) {
1539 auto accesschain_it = accesschain_members.find(*store_it);
1540 if (accesschain_it == accesschain_members.end()) {
1541 ++store_it;
1542 continue;
1543 }
1544 if (accesschain_it->second == output.second.id) {
1545 location_list.insert(output.first.first);
1546 store_members.erase(store_it);
1547 accesschain_members.erase(accesschain_it);
1548 break;
1549 }
1550 ++store_it;
1551 }
1552 }
1553 return location_list;
1554}
1555
1556bool SHADER_MODULE_STATE::CollectInterfaceBlockMembers(std::map<location_t, interface_var> *out, bool is_array_of_verts,
ziga-lunarg9e94e112021-09-27 00:21:10 +02001557 uint32_t id, uint32_t type_id, bool is_patch,
1558 uint32_t /*first_location*/) const {
sfricke-samsung962cad92021-04-13 00:46:29 -07001559 // Walk down the type_id presented, trying to determine whether it's actually an interface block.
1560 auto type = GetStructType(get_def(type_id), is_array_of_verts && !is_patch);
1561 if (type == end() || !(get_decorations(type.word(1)).flags & decoration_set::block_bit)) {
1562 // This isn't an interface block.
1563 return false;
1564 }
1565
1566 layer_data::unordered_map<unsigned, unsigned> member_components;
1567 layer_data::unordered_map<unsigned, unsigned> member_relaxed_precision;
1568 layer_data::unordered_map<unsigned, unsigned> member_patch;
1569
1570 // Walk all the OpMemberDecorate for type's result id -- first pass, collect components.
1571 for (auto insn : member_decoration_inst) {
1572 if (insn.word(1) == type.word(1)) {
1573 unsigned member_index = insn.word(2);
1574
1575 if (insn.word(3) == spv::DecorationComponent) {
1576 unsigned component = insn.word(4);
1577 member_components[member_index] = component;
1578 }
1579
1580 if (insn.word(3) == spv::DecorationRelaxedPrecision) {
1581 member_relaxed_precision[member_index] = 1;
1582 }
1583
1584 if (insn.word(3) == spv::DecorationPatch) {
1585 member_patch[member_index] = 1;
1586 }
1587 }
1588 }
1589
1590 // TODO: correctly handle location assignment from outside
1591
1592 // Second pass -- produce the output, from Location decorations
1593 for (auto insn : member_decoration_inst) {
1594 if (insn.word(1) == type.word(1)) {
1595 unsigned member_index = insn.word(2);
1596 unsigned member_type_id = type.word(2 + member_index);
1597
1598 if (insn.word(3) == spv::DecorationLocation) {
1599 unsigned location = insn.word(4);
1600 unsigned num_locations = GetLocationsConsumedByType(member_type_id, false);
1601 auto component_it = member_components.find(member_index);
1602 unsigned component = component_it == member_components.end() ? 0 : component_it->second;
1603 bool is_relaxed_precision = member_relaxed_precision.find(member_index) != member_relaxed_precision.end();
1604 bool member_is_patch = is_patch || member_patch.count(member_index) > 0;
1605
1606 for (unsigned int offset = 0; offset < num_locations; offset++) {
1607 interface_var v = {};
1608 v.id = id;
1609 // TODO: member index in interface_var too?
1610 v.type_id = member_type_id;
1611 v.offset = offset;
1612 v.is_patch = member_is_patch;
1613 v.is_block_member = true;
1614 v.is_relaxed_precision = is_relaxed_precision;
1615 (*out)[std::make_pair(location + offset, component)] = v;
1616 }
1617 }
1618 }
1619 }
1620
1621 return true;
1622}
1623
1624std::map<location_t, interface_var> SHADER_MODULE_STATE::CollectInterfaceByLocation(spirv_inst_iter entrypoint,
1625 spv::StorageClass sinterface,
1626 bool is_array_of_verts) const {
1627 // TODO: handle index=1 dual source outputs from FS -- two vars will have the same location, and we DON'T want to clobber.
1628
1629 std::map<location_t, interface_var> out;
1630
1631 for (uint32_t iid : FindEntrypointInterfaces(entrypoint)) {
1632 auto insn = get_def(iid);
1633 assert(insn != end());
1634 assert(insn.opcode() == spv::OpVariable);
1635
ziga-lunarg9e94e112021-09-27 00:21:10 +02001636 const auto d = get_decorations(iid);
1637 bool passthrough = sinterface == spv::StorageClassOutput && insn.word(3) == spv::StorageClassInput &&
1638 (d.flags & decoration_set::passthrough_bit) != 0;
1639 if (insn.word(3) == static_cast<uint32_t>(sinterface) || passthrough) {
sfricke-samsung962cad92021-04-13 00:46:29 -07001640 unsigned id = insn.word(2);
1641 unsigned type = insn.word(1);
1642
ziga-lunarg9e94e112021-09-27 00:21:10 +02001643 auto location = d.location;
sfricke-samsung962cad92021-04-13 00:46:29 -07001644 int builtin = d.builtin;
1645 unsigned component = d.component;
1646 bool is_patch = (d.flags & decoration_set::patch_bit) != 0;
1647 bool is_relaxed_precision = (d.flags & decoration_set::relaxed_precision_bit) != 0;
ziga-lunarg9e94e112021-09-27 00:21:10 +02001648 bool is_per_vertex = (d.flags & decoration_set::per_vertex_bit) != 0;
sfricke-samsung962cad92021-04-13 00:46:29 -07001649
1650 if (builtin != -1) {
1651 continue;
ziga-lunarg9e94e112021-09-27 00:21:10 +02001652 } else if (!CollectInterfaceBlockMembers(&out, is_array_of_verts, id, type, is_patch, location) ||
1653 location != decoration_set::kInvalidValue) {
sfricke-samsung962cad92021-04-13 00:46:29 -07001654 // A user-defined interface variable, with a location. Where a variable occupied multiple locations, emit
1655 // one result for each.
ziga-lunarg9e94e112021-09-27 00:21:10 +02001656 unsigned num_locations = GetLocationsConsumedByType(type, (is_array_of_verts && !is_patch) || is_per_vertex);
sfricke-samsung962cad92021-04-13 00:46:29 -07001657 for (unsigned int offset = 0; offset < num_locations; offset++) {
1658 interface_var v = {};
1659 v.id = id;
1660 v.type_id = type;
1661 v.offset = offset;
1662 v.is_patch = is_patch;
1663 v.is_relaxed_precision = is_relaxed_precision;
1664 out[std::make_pair(location + offset, component)] = v;
1665 }
1666 }
1667 }
1668 }
1669
1670 return out;
1671}
1672
1673std::vector<uint32_t> SHADER_MODULE_STATE::CollectBuiltinBlockMembers(spirv_inst_iter entrypoint, uint32_t storageClass) const {
sfricke-samsung962cad92021-04-13 00:46:29 -07001674 // Find all interface variables belonging to the entrypoint and matching the storage class
sfricke-samsung0df5ee72021-07-24 23:27:16 -07001675 std::vector<uint32_t> variables;
sfricke-samsung962cad92021-04-13 00:46:29 -07001676 for (uint32_t id : FindEntrypointInterfaces(entrypoint)) {
1677 auto def = get_def(id);
1678 assert(def != end());
1679 assert(def.opcode() == spv::OpVariable);
1680
1681 if (def.word(3) == storageClass) variables.push_back(def.word(1));
1682 }
1683
1684 // Find all members belonging to the builtin block selected
1685 std::vector<uint32_t> builtin_block_members;
1686 for (auto &var : variables) {
1687 auto def = get_def(get_def(var).word(3));
1688
1689 // It could be an array of IO blocks. The element type should be the struct defining the block contents
1690 if (def.opcode() == spv::OpTypeArray) def = get_def(def.word(2));
1691
1692 // Now find all members belonging to the struct defining the IO block
1693 if (def.opcode() == spv::OpTypeStruct) {
sfricke-samsung0df5ee72021-07-24 23:27:16 -07001694 for (auto set : builtin_decoration_list) {
1695 auto insn = at(set.offset);
1696 if ((insn.opcode() == spv::OpMemberDecorate) && (def.word(1) == insn.word(1))) {
1697 // Start with undefined builtin for each struct member.
1698 // But only when confirmed the struct is the built-in inteface block (can only be one per shader)
1699 if (builtin_block_members.size() == 0) {
1700 builtin_block_members.resize(def.len() - 2, spv::BuiltInMax);
sfricke-samsung962cad92021-04-13 00:46:29 -07001701 }
sfricke-samsung0df5ee72021-07-24 23:27:16 -07001702 auto struct_index = insn.word(2);
1703 assert(struct_index < builtin_block_members.size());
1704 builtin_block_members[struct_index] = insn.word(4);
sfricke-samsung962cad92021-04-13 00:46:29 -07001705 }
1706 }
1707 }
1708 }
1709
1710 return builtin_block_members;
1711}
1712
1713std::vector<std::pair<uint32_t, interface_var>> SHADER_MODULE_STATE::CollectInterfaceByInputAttachmentIndex(
1714 layer_data::unordered_set<uint32_t> const &accessible_ids) const {
1715 std::vector<std::pair<uint32_t, interface_var>> out;
1716
1717 for (auto insn : decoration_inst) {
1718 if (insn.word(2) == spv::DecorationInputAttachmentIndex) {
1719 auto attachment_index = insn.word(3);
1720 auto id = insn.word(1);
1721
1722 if (accessible_ids.count(id)) {
1723 auto def = get_def(id);
1724 assert(def != end());
1725 if (def.opcode() == spv::OpVariable && def.word(3) == spv::StorageClassUniformConstant) {
1726 auto num_locations = GetLocationsConsumedByType(def.word(1), false);
1727 for (unsigned int offset = 0; offset < num_locations; offset++) {
1728 interface_var v = {};
1729 v.id = id;
1730 v.type_id = def.word(1);
1731 v.offset = offset;
1732 out.emplace_back(attachment_index + offset, v);
1733 }
1734 }
1735 }
1736 }
1737 }
1738
1739 return out;
1740}
1741
Lionel Landwerlin5f2065a2021-07-23 11:51:28 +03001742spirv_inst_iter SHADER_MODULE_STATE::GetImageFormatInst(uint32_t id) const
Lionel Landwerlin892d6c32021-05-05 12:56:19 +03001743{
1744 do {
Lionel Landwerlin892d6c32021-05-05 12:56:19 +03001745 auto def = get_def(id);
Lionel Landwerlin5f2065a2021-07-23 11:51:28 +03001746 if (def == end())
1747 return def;
1748
Lionel Landwerlin892d6c32021-05-05 12:56:19 +03001749 switch (def.opcode()) {
1750 case spv::OpLoad:
Lionel Landwerlin892d6c32021-05-05 12:56:19 +03001751 case spv::OpAccessChain:
1752 case spv::OpCompositeConstruct:
1753 case spv::OpVariable: {
1754 id = def.word(1);
1755 break;
1756 }
1757
1758 case spv::OpTypeArray:
1759 case spv::OpTypeRuntimeArray:
1760 id = def.word(2);
1761 break;
1762
1763 case spv::OpTypePointer:
1764 id = def.word(3);
1765 break;
1766
1767 case spv::OpTypeImage:
1768 return def;
1769
1770 default:
1771 return end();
1772 }
1773 } while (true);
1774}
1775
ziga-lunarg8346fe82021-08-22 17:30:50 +02001776uint32_t SHADER_MODULE_STATE::GetNumComponentsInBaseType(const spirv_inst_iter &iter) const {
1777 const uint32_t opcode = iter.opcode();
1778 if (opcode == spv::OpTypeFloat || opcode == spv::OpTypeInt) {
1779 return 1;
1780 } else if (opcode == spv::OpTypeVector) {
1781 const uint32_t component_count = iter.word(3);
1782 return component_count;
1783 } else if (opcode == spv::OpTypeMatrix) {
1784 const auto column_type = get_def(iter.word(2));
1785 const uint32_t vector_length = GetNumComponentsInBaseType(column_type);
1786 const uint32_t column_count = iter.word(3);
1787 return vector_length * column_count;
1788 } else if (opcode == spv::OpTypeArray) {
1789 const auto element_type = get_def(iter.word(2));
1790 const uint32_t element_length = GetNumComponentsInBaseType(element_type);
1791 return element_length;
1792 } else if (opcode == spv::OpTypeStruct) {
1793 uint32_t total_size = 0;
1794 for (uint32_t i = 2; i < iter.len(); ++i) {
1795 total_size += GetNumComponentsInBaseType(get_def(iter.word(i)));
1796 }
1797 return total_size;
1798 } else if (opcode == spv::OpTypePointer) {
1799 const auto type = get_def(iter.word(3));
1800 return GetNumComponentsInBaseType(type);
1801 }
1802 return 0;
1803}
1804
ziga-lunarg2818f492021-08-12 14:30:51 +02001805std::array<uint32_t, 3> SHADER_MODULE_STATE::GetWorkgroupSize(
1806 VkPipelineShaderStageCreateInfo const *pStage, const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) const {
1807 std::array<uint32_t, 3> work_group_size = {1, 1, 1};
1808
1809 uint32_t work_group_size_id = std::numeric_limits<uint32_t>::max();
1810
1811 for (const auto &builtin : builtin_decoration_list) {
1812 if (builtin.builtin == spv::BuiltInWorkgroupSize) {
1813 work_group_size_id = at(builtin.offset).word(1);
1814 break;
1815 }
1816 }
1817 for (auto insn : *this) {
1818 uint32_t opcode = insn.opcode();
1819 if (opcode == spv::OpSpecConstantComposite) { // WorkGroupSize must be a composite
1820 uint32_t result_id = insn.word(2);
1821 if (result_id == work_group_size_id) {
1822 uint32_t result_type_id = insn.word(1);
1823 auto result_type = get_def(result_type_id);
1824 if (result_type.opcode() == spv::OpTypeVector) {
1825 uint32_t component_count = result_type.word(3);
1826 for (uint32_t i = 0; i < component_count; ++i) {
1827 auto constituent = get_def(insn.word(3 + i));
1828 for (const auto &sc : spec_const_map) {
1829 if (sc.second == constituent.word(2)) {
1830 const auto iter = id_value_map.find(sc.first);
1831 if (iter != id_value_map.cend()) {
1832 work_group_size[i] = *iter->second.begin();
1833 }
1834 break;
1835 }
1836 }
1837 }
1838 }
1839 }
1840 }
1841 }
1842
1843 return work_group_size;
1844}
1845
ziga-lunarga26b3602021-08-08 15:53:00 +02001846uint32_t SHADER_MODULE_STATE::GetTypeBitsSize(const spirv_inst_iter &iter) const {
1847 const uint32_t opcode = iter.opcode();
1848 if (opcode == spv::OpTypeFloat || opcode == spv::OpTypeInt) {
1849 return iter.word(2);
1850 } else if (opcode == spv::OpTypeVector) {
1851 const auto component_type = get_def(iter.word(2));
1852 uint32_t scalar_width = GetTypeBitsSize(component_type);
1853 uint32_t component_count = iter.word(3);
1854 return scalar_width * component_count;
1855 } else if (opcode == spv::OpTypeMatrix) {
1856 const auto column_type = get_def(iter.word(2));
1857 uint32_t vector_width = GetTypeBitsSize(column_type);
1858 uint32_t column_count = iter.word(3);
1859 return vector_width * column_count;
1860 } else if (opcode == spv::OpTypeArray) {
1861 const auto element_type = get_def(iter.word(2));
1862 uint32_t element_width = GetTypeBitsSize(element_type);
1863 const auto length_type = get_def(iter.word(3));
1864 uint32_t length = GetConstantValue(length_type);
1865 return element_width * length;
1866 } else if (opcode == spv::OpTypeStruct) {
1867 uint32_t total_size = 0;
1868 for (uint32_t i = 2; i < iter.len(); ++i) {
1869 total_size += GetTypeBitsSize(get_def(iter.word(i)));
1870 }
1871 return total_size;
ziga-lunarg8346fe82021-08-22 17:30:50 +02001872 } else if (opcode == spv::OpTypePointer) {
1873 const auto type = get_def(iter.word(3));
1874 return GetTypeBitsSize(type);
ziga-lunarga26b3602021-08-08 15:53:00 +02001875 }
1876 return 0;
1877}
1878
1879uint32_t SHADER_MODULE_STATE::GetTypeBytesSize(const spirv_inst_iter &iter) const { return GetTypeBitsSize(iter) / 8; }
1880
ziga-lunarg19fc6ae2021-09-09 00:05:19 +02001881// Returns the base type (float, int or unsigned int) or struct (can have multiple different base types inside)
ziga-lunarg8346fe82021-08-22 17:30:50 +02001882uint32_t SHADER_MODULE_STATE::GetBaseType(const spirv_inst_iter &iter) const {
1883 const uint32_t opcode = iter.opcode();
1884 if (opcode == spv::OpTypeFloat || opcode == spv::OpTypeInt || opcode == spv::OpTypeStruct) {
1885 return iter.word(1);
1886 } else if (opcode == spv::OpTypeVector) {
1887 const auto& component_type = get_def(iter.word(2));
1888 return GetBaseType(component_type);
1889 } else if (opcode == spv::OpTypeMatrix) {
1890 const auto& column_type = get_def(iter.word(2));
1891 return GetBaseType(column_type);
1892 } else if (opcode == spv::OpTypeArray) {
1893 const auto& element_type = get_def(iter.word(2));
1894 return GetBaseType(element_type);
1895 } else if (opcode == spv::OpTypePointer) {
1896 const auto& type = get_def(iter.word(3));
1897 return GetBaseType(type);
1898 }
1899 return 0;
1900}
1901
ziga-lunarga26b3602021-08-08 15:53:00 +02001902uint32_t SHADER_MODULE_STATE::CalcComputeSharedMemory(VkShaderStageFlagBits stage,
1903 const spirv_inst_iter &insn) const {
1904 if (stage == VK_SHADER_STAGE_COMPUTE_BIT && insn.opcode() == spv::OpVariable) {
1905 uint32_t storage_class = insn.word(3);
1906 if (storage_class == spv::StorageClassWorkgroup) { // StorageClass Workgroup is shared memory
1907 uint32_t result_type_id = insn.word(1);
1908 auto result_type = get_def(result_type_id);
1909 auto type = get_def(result_type.word(3));
1910 return GetTypeBytesSize(type);
1911 }
1912 }
1913
1914 return 0;
1915}
1916
ziga-lunarga12c75a2021-09-16 16:36:16 +02001917uint32_t SHADER_MODULE_STATE::ImageOperandsCount(uint32_t i) const {
1918 uint32_t count = 0;
1919 switch (i) {
1920 case spv::ImageOperandsMaskNone:
1921 case spv::ImageOperandsMakeTexelAvailableMask: // ImageOperandsMakeTexelAvailableKHRMask
1922 case spv::ImageOperandsMakeTexelVisibleMask: // ImageOperandsMakeTexelVisibleKHRMask
1923 case spv::ImageOperandsNonPrivateTexelMask: // ImageOperandsNonPrivateTexelKHRMask
1924 case spv::ImageOperandsVolatileTexelMask: // ImageOperandsVolatileTexelKHRMask
1925 case spv::ImageOperandsSignExtendMask:
1926 case spv::ImageOperandsZeroExtendMask:
1927 count = 0;
1928 break;
1929 case spv::ImageOperandsBiasMask:
1930 case spv::ImageOperandsLodMask:
1931 case spv::ImageOperandsConstOffsetMask:
1932 case spv::ImageOperandsOffsetMask:
1933 case spv::ImageOperandsConstOffsetsMask:
1934 case spv::ImageOperandsSampleMask:
1935 case spv::ImageOperandsMinLodMask:
1936 count = 1;
1937 break;
1938 case spv::ImageOperandsGradMask:
1939 count = 2;
1940 break;
1941 default:
1942 break;
1943 }
1944 return count;
1945}
1946
sfricke-samsung962cad92021-04-13 00:46:29 -07001947// Assumes itr points to an OpConstant instruction
1948uint32_t GetConstantValue(const spirv_inst_iter &itr) { return itr.word(3); }
1949
1950std::vector<uint32_t> FindEntrypointInterfaces(const spirv_inst_iter &entrypoint) {
1951 assert(entrypoint.opcode() == spv::OpEntryPoint);
1952
1953 std::vector<uint32_t> interfaces;
1954 // Find the end of the entrypoint's name string. additional zero bytes follow the actual null terminator, to fill out the
1955 // rest of the word - so we only need to look at the last byte in the word to determine which word contains the terminator.
1956 uint32_t word = 3;
1957 while (entrypoint.word(word) & 0xff000000u) {
1958 ++word;
1959 }
1960 ++word;
1961
1962 for (; word < entrypoint.len(); word++) interfaces.push_back(entrypoint.word(word));
1963
1964 return interfaces;
1965}
1966
1967bool AtomicOperation(uint32_t opcode) {
1968 switch (opcode) {
1969 case spv::OpAtomicLoad:
1970 case spv::OpAtomicStore:
1971 case spv::OpAtomicExchange:
1972 case spv::OpAtomicCompareExchange:
1973 case spv::OpAtomicCompareExchangeWeak:
1974 case spv::OpAtomicIIncrement:
1975 case spv::OpAtomicIDecrement:
1976 case spv::OpAtomicIAdd:
1977 case spv::OpAtomicISub:
1978 case spv::OpAtomicSMin:
1979 case spv::OpAtomicUMin:
1980 case spv::OpAtomicSMax:
1981 case spv::OpAtomicUMax:
1982 case spv::OpAtomicAnd:
1983 case spv::OpAtomicOr:
1984 case spv::OpAtomicXor:
1985 case spv::OpAtomicFAddEXT:
sfricke-samsungf5042b12021-08-05 01:09:40 -07001986 case spv::OpAtomicFMinEXT:
1987 case spv::OpAtomicFMaxEXT:
sfricke-samsung962cad92021-04-13 00:46:29 -07001988 return true;
1989 default:
1990 return false;
1991 }
1992 return false;
1993}
1994
1995// Only includes valid group operations used in Vulkan (for now thats only subgroup ops) and any non supported operation will be
1996// covered with VUID 01090
1997bool GroupOperation(uint32_t opcode) {
1998 switch (opcode) {
1999 case spv::OpGroupNonUniformElect:
2000 case spv::OpGroupNonUniformAll:
2001 case spv::OpGroupNonUniformAny:
2002 case spv::OpGroupNonUniformAllEqual:
2003 case spv::OpGroupNonUniformBroadcast:
2004 case spv::OpGroupNonUniformBroadcastFirst:
2005 case spv::OpGroupNonUniformBallot:
2006 case spv::OpGroupNonUniformInverseBallot:
2007 case spv::OpGroupNonUniformBallotBitExtract:
2008 case spv::OpGroupNonUniformBallotBitCount:
2009 case spv::OpGroupNonUniformBallotFindLSB:
2010 case spv::OpGroupNonUniformBallotFindMSB:
2011 case spv::OpGroupNonUniformShuffle:
2012 case spv::OpGroupNonUniformShuffleXor:
2013 case spv::OpGroupNonUniformShuffleUp:
2014 case spv::OpGroupNonUniformShuffleDown:
2015 case spv::OpGroupNonUniformIAdd:
2016 case spv::OpGroupNonUniformFAdd:
2017 case spv::OpGroupNonUniformIMul:
2018 case spv::OpGroupNonUniformFMul:
2019 case spv::OpGroupNonUniformSMin:
2020 case spv::OpGroupNonUniformUMin:
2021 case spv::OpGroupNonUniformFMin:
2022 case spv::OpGroupNonUniformSMax:
2023 case spv::OpGroupNonUniformUMax:
2024 case spv::OpGroupNonUniformFMax:
2025 case spv::OpGroupNonUniformBitwiseAnd:
2026 case spv::OpGroupNonUniformBitwiseOr:
2027 case spv::OpGroupNonUniformBitwiseXor:
2028 case spv::OpGroupNonUniformLogicalAnd:
2029 case spv::OpGroupNonUniformLogicalOr:
2030 case spv::OpGroupNonUniformLogicalXor:
2031 case spv::OpGroupNonUniformQuadBroadcast:
2032 case spv::OpGroupNonUniformQuadSwap:
2033 case spv::OpGroupNonUniformPartitionNV:
2034 return true;
2035 default:
2036 return false;
2037 }
2038 return false;
Jeremy Gebben5d970742021-05-31 16:04:14 -06002039}