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