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