1 /* Copyright (c) 2015-2021 The Khronos Group Inc.
2  * Copyright (c) 2015-2021 Valve Corporation
3  * Copyright (c) 2015-2021 LunarG, Inc.
4  * Copyright (C) 2015-2021 Google Inc.
5  * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * Author: Chris Forbes <chrisf@ijw.co.nz>
20  * Author: Dave Houlton <daveh@lunarg.com>
21  * Author: Tobias Hector <tobias.hector@amd.com>
22  */
23 
24 #include "shader_validation.h"
25 
26 #include <cassert>
27 #include <cinttypes>
28 #include <cmath>
29 #include <sstream>
30 #include <string>
31 #include <vector>
32 
33 #include <spirv/unified1/spirv.hpp>
34 #include "vk_enum_string_helper.h"
35 #include "vk_layer_data.h"
36 #include "vk_layer_utils.h"
37 #include "chassis.h"
38 #include "core_validation.h"
39 #include "spirv_grammar_helper.h"
40 
41 #include "xxhash.h"
42 
43 static shader_stage_attributes shader_stage_attribs[] = {
44     {"vertex shader", false, false, VK_SHADER_STAGE_VERTEX_BIT},
45     {"tessellation control shader", true, true, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT},
46     {"tessellation evaluation shader", true, false, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT},
47     {"geometry shader", true, false, VK_SHADER_STAGE_GEOMETRY_BIT},
48     {"fragment shader", false, false, VK_SHADER_STAGE_FRAGMENT_BIT},
49 };
50 
GetBaseTypeIter(SHADER_MODULE_STATE const * src,uint32_t type)51 static const spirv_inst_iter GetBaseTypeIter(SHADER_MODULE_STATE const *src, uint32_t type) {
52     const auto &insn = src->get_def(type);
53     const uint32_t base_insn_id = src->GetBaseType(insn);
54     return src->get_def(base_insn_id);
55 }
56 
BaseTypesMatch(SHADER_MODULE_STATE const * a,SHADER_MODULE_STATE const * b,const spirv_inst_iter & a_base_insn,const spirv_inst_iter & b_base_insn)57 static bool BaseTypesMatch(SHADER_MODULE_STATE const *a, SHADER_MODULE_STATE const *b, const spirv_inst_iter &a_base_insn,
58                            const spirv_inst_iter &b_base_insn) {
59     const uint32_t a_opcode = a_base_insn.opcode();
60     const uint32_t b_opcode = b_base_insn.opcode();
61     if (a_opcode == b_opcode) {
62         if (a_opcode == spv::OpTypeInt) {
63             // Match width and signedness
64             return a_base_insn.word(2) == b_base_insn.word(2) && a_base_insn.word(3) == b_base_insn.word(3);
65         } else if (a_opcode == spv::OpTypeFloat) {
66             // Match width
67             return a_base_insn.word(2) == b_base_insn.word(2);
68         } else if (a_opcode == spv::OpTypeStruct) {
69             // Match on all element types
70             if (a_base_insn.len() != b_base_insn.len()) {
71                 return false;  // Structs cannot match if member counts differ
72             }
73 
74             for (uint32_t i = 2; i < a_base_insn.len(); i++) {
75                 const auto &c_base_insn = GetBaseTypeIter(a, a_base_insn.word(i));
76                 const auto &d_base_insn = GetBaseTypeIter(b, b_base_insn.word(i));
77                 if (!BaseTypesMatch(a, b, c_base_insn, d_base_insn)) {
78                     return false;
79                 }
80             }
81 
82             return true;
83         }
84     }
85     return false;
86 }
87 
TypesMatch(SHADER_MODULE_STATE const * a,SHADER_MODULE_STATE const * b,uint32_t a_type,uint32_t b_type)88 static bool TypesMatch(SHADER_MODULE_STATE const *a, SHADER_MODULE_STATE const *b, uint32_t a_type, uint32_t b_type) {
89     const auto &a_base_insn = GetBaseTypeIter(a, a_type);
90     const auto &b_base_insn = GetBaseTypeIter(b, b_type);
91 
92     return BaseTypesMatch(a, b, a_base_insn, b_base_insn);
93 }
94 
GetLocationsConsumedByFormat(VkFormat format)95 static unsigned GetLocationsConsumedByFormat(VkFormat format) {
96     switch (format) {
97         case VK_FORMAT_R64G64B64A64_SFLOAT:
98         case VK_FORMAT_R64G64B64A64_SINT:
99         case VK_FORMAT_R64G64B64A64_UINT:
100         case VK_FORMAT_R64G64B64_SFLOAT:
101         case VK_FORMAT_R64G64B64_SINT:
102         case VK_FORMAT_R64G64B64_UINT:
103             return 2;
104         default:
105             return 1;
106     }
107 }
108 
GetFormatType(VkFormat fmt)109 static unsigned GetFormatType(VkFormat fmt) {
110     if (FormatIsSINT(fmt)) return FORMAT_TYPE_SINT;
111     if (FormatIsUINT(fmt)) return FORMAT_TYPE_UINT;
112     // Formats such as VK_FORMAT_D16_UNORM_S8_UINT are both
113     if (FormatIsDepthAndStencil(fmt)) return FORMAT_TYPE_FLOAT | FORMAT_TYPE_UINT;
114     if (fmt == VK_FORMAT_UNDEFINED) return 0;
115     // everything else -- UNORM/SNORM/FLOAT/USCALED/SSCALED is all float in the shader.
116     return FORMAT_TYPE_FLOAT;
117 }
118 
GetShaderStageId(VkShaderStageFlagBits stage)119 static uint32_t GetShaderStageId(VkShaderStageFlagBits stage) {
120     uint32_t bit_pos = uint32_t(u_ffs(stage));
121     return bit_pos - 1;
122 }
123 
ValidateViConsistency(VkPipelineVertexInputStateCreateInfo const * vi) const124 bool CoreChecks::ValidateViConsistency(VkPipelineVertexInputStateCreateInfo const *vi) const {
125     // Walk the binding descriptions, which describe the step rate and stride of each vertex buffer.  Each binding should
126     // be specified only once.
127     layer_data::unordered_map<uint32_t, VkVertexInputBindingDescription const *> bindings;
128     bool skip = false;
129 
130     for (unsigned i = 0; i < vi->vertexBindingDescriptionCount; i++) {
131         auto desc = &vi->pVertexBindingDescriptions[i];
132         auto &binding = bindings[desc->binding];
133         if (binding) {
134             // TODO: "VUID-VkGraphicsPipelineCreateInfo-pStages-00742" perhaps?
135             skip |= LogError(device, kVUID_Core_Shader_InconsistentVi, "Duplicate vertex input binding descriptions for binding %d",
136                              desc->binding);
137         } else {
138             binding = desc;
139         }
140     }
141 
142     return skip;
143 }
144 
ValidateViAgainstVsInputs(VkPipelineVertexInputStateCreateInfo const * vi,SHADER_MODULE_STATE const * vs,spirv_inst_iter entrypoint) const145 bool CoreChecks::ValidateViAgainstVsInputs(VkPipelineVertexInputStateCreateInfo const *vi, SHADER_MODULE_STATE const *vs,
146                                            spirv_inst_iter entrypoint) const {
147     bool skip = false;
148 
149     const auto inputs = vs->CollectInterfaceByLocation(entrypoint, spv::StorageClassInput, false);
150 
151     // Build index by location
152     std::map<uint32_t, const VkVertexInputAttributeDescription *> attribs;
153     if (vi) {
154         for (uint32_t i = 0; i < vi->vertexAttributeDescriptionCount; ++i) {
155             const auto num_locations = GetLocationsConsumedByFormat(vi->pVertexAttributeDescriptions[i].format);
156             for (uint32_t j = 0; j < num_locations; ++j) {
157                 attribs[vi->pVertexAttributeDescriptions[i].location + j] = &vi->pVertexAttributeDescriptions[i];
158             }
159         }
160     }
161 
162     struct AttribInputPair {
163         const VkVertexInputAttributeDescription *attrib = nullptr;
164         const interface_var *input = nullptr;
165     };
166     std::map<uint32_t, AttribInputPair> location_map;
167     for (const auto &attrib_it : attribs) location_map[attrib_it.first].attrib = attrib_it.second;
168     for (const auto &input_it : inputs) location_map[input_it.first.first].input = &input_it.second;
169 
170     for (const auto &location_it : location_map) {
171         const auto location = location_it.first;
172         const auto attrib = location_it.second.attrib;
173         const auto input = location_it.second.input;
174 
175         if (attrib && !input) {
176             skip |= LogPerformanceWarning(vs->vk_shader_module(), kVUID_Core_Shader_OutputNotConsumed,
177                                           "Vertex attribute at location %" PRIu32 " not consumed by vertex shader", location);
178         } else if (!attrib && input) {
179             skip |= LogError(vs->vk_shader_module(), kVUID_Core_Shader_InputNotProduced,
180                              "Vertex shader consumes input at location %" PRIu32 " but not provided", location);
181         } else if (attrib && input) {
182             const auto attrib_type = GetFormatType(attrib->format);
183             const auto input_type = vs->GetFundamentalType(input->type_id);
184 
185             // Type checking
186             if (!(attrib_type & input_type)) {
187                 skip |= LogError(vs->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
188                                  "Attribute type of `%s` at location %" PRIu32 " does not match vertex shader input type of `%s`",
189                                  string_VkFormat(attrib->format), location, vs->DescribeType(input->type_id).c_str());
190             }
191         } else {            // !attrib && !input
192             assert(false);  // at least one exists in the map
193         }
194     }
195 
196     return skip;
197 }
198 
ValidateFsOutputsAgainstDynamicRenderingRenderPass(SHADER_MODULE_STATE const * fs,spirv_inst_iter entrypoint,PIPELINE_STATE const * pipeline) const199 bool CoreChecks::ValidateFsOutputsAgainstDynamicRenderingRenderPass(SHADER_MODULE_STATE const* fs, spirv_inst_iter entrypoint,
200                                                                     PIPELINE_STATE const* pipeline) const {
201     bool skip = false;
202 
203     struct Attachment {
204         const interface_var* output = nullptr;
205     };
206     std::map<uint32_t, Attachment> location_map;
207 
208     // TODO: dual source blend index (spv::DecIndex, zero if not provided)
209     const auto outputs = fs->CollectInterfaceByLocation(entrypoint, spv::StorageClassOutput, false);
210     for (const auto& output_it : outputs) {
211         auto const location = output_it.first.first;
212         location_map[location].output = &output_it.second;
213     }
214 
215     const bool alpha_to_coverage_enabled = pipeline->create_info.graphics.pMultisampleState != NULL &&
216         pipeline->create_info.graphics.pMultisampleState->alphaToCoverageEnable == VK_TRUE;
217 
218     for (uint32_t location = 0; location < pipeline->rp_state->dynamic_rendering_pipeline_create_info.colorAttachmentCount; ++location) {
219          const auto output = location_map[location].output;
220 
221         if (!output && pipeline->attachments[location].colorWriteMask != 0) {
222             skip |= LogWarning(fs->vk_shader_module(), kVUID_Core_Shader_InputNotProduced,
223                 "Attachment %" PRIu32
224                 " not written by fragment shader; undefined values will be written to attachment",
225                 location);
226         } else if (output) {
227             auto format = pipeline->rp_state->dynamic_rendering_pipeline_create_info.pColorAttachmentFormats[location];
228             const auto attachment_type = GetFormatType(format);
229             const auto output_type = fs->GetFundamentalType(output->type_id);
230 
231             // Type checking
232             if (!(output_type & attachment_type)) {
233                 skip |=
234                     LogWarning(fs->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
235                         "Attachment %" PRIu32
236                         " of type `%s` does not match fragment shader output type of `%s`; resulting values are undefined",
237                         location, string_VkFormat(format), fs->DescribeType(output->type_id).c_str());
238             }
239         }
240     }
241 
242     const auto output_zero = location_map.count(0) ? location_map[0].output : nullptr;
243     bool location_zero_has_alpha = output_zero && fs->get_def(output_zero->type_id) != fs->end() &&
244         fs->GetComponentsConsumedByType(output_zero->type_id, false) == 4;
245     if (alpha_to_coverage_enabled && !location_zero_has_alpha) {
246         skip |= LogError(fs->vk_shader_module(), kVUID_Core_Shader_NoAlphaAtLocation0WithAlphaToCoverage,
247             "fragment shader doesn't declare alpha output at location 0 even though alpha to coverage is enabled.");
248     }
249 
250     return skip;
251 
252 }
253 
ValidateFsOutputsAgainstRenderPass(SHADER_MODULE_STATE const * fs,spirv_inst_iter entrypoint,PIPELINE_STATE const * pipeline,uint32_t subpass_index) const254 bool CoreChecks::ValidateFsOutputsAgainstRenderPass(SHADER_MODULE_STATE const *fs, spirv_inst_iter entrypoint,
255                                                     PIPELINE_STATE const *pipeline, uint32_t subpass_index) const {
256     bool skip = false;
257 
258     struct Attachment {
259         const VkAttachmentReference2 *reference = nullptr;
260         const VkAttachmentDescription2 *attachment = nullptr;
261         const interface_var *output = nullptr;
262     };
263     std::map<uint32_t, Attachment> location_map;
264 
265     if (pipeline->rp_state && !pipeline->rp_state->use_dynamic_rendering) {
266         const auto rpci = pipeline->rp_state->createInfo.ptr();
267         const auto subpass = rpci->pSubpasses[subpass_index];
268         for (uint32_t i = 0; i < subpass.colorAttachmentCount; ++i) {
269             auto const &reference = subpass.pColorAttachments[i];
270             location_map[i].reference = &reference;
271             if (reference.attachment != VK_ATTACHMENT_UNUSED &&
272                 rpci->pAttachments[reference.attachment].format != VK_FORMAT_UNDEFINED) {
273                 location_map[i].attachment = &rpci->pAttachments[reference.attachment];
274             }
275         }
276     }
277 
278     // TODO: dual source blend index (spv::DecIndex, zero if not provided)
279 
280     const auto outputs = fs->CollectInterfaceByLocation(entrypoint, spv::StorageClassOutput, false);
281     for (const auto &output_it : outputs) {
282         auto const location = output_it.first.first;
283         location_map[location].output = &output_it.second;
284     }
285 
286     const bool alpha_to_coverage_enabled = pipeline->create_info.graphics.pMultisampleState != NULL &&
287                                            pipeline->create_info.graphics.pMultisampleState->alphaToCoverageEnable == VK_TRUE;
288 
289     for (const auto &location_it : location_map) {
290         const auto reference = location_it.second.reference;
291         if (reference != nullptr && reference->attachment == VK_ATTACHMENT_UNUSED) {
292             continue;
293         }
294 
295         const auto location = location_it.first;
296         const auto attachment = location_it.second.attachment;
297         const auto output = location_it.second.output;
298         if (attachment && !output) {
299             if (pipeline->attachments[location].colorWriteMask != 0) {
300                 skip |= LogWarning(fs->vk_shader_module(), kVUID_Core_Shader_InputNotProduced,
301                                    "Attachment %" PRIu32
302                                    " not written by fragment shader; undefined values will be written to attachment",
303                                    location);
304             }
305         } else if (!attachment && output) {
306             if (!(alpha_to_coverage_enabled && location == 0)) {
307                 skip |= LogWarning(fs->vk_shader_module(), kVUID_Core_Shader_OutputNotConsumed,
308                                    "fragment shader writes to output location %" PRIu32 " with no matching attachment", location);
309             }
310         } else if (attachment && output) {
311             const auto attachment_type = GetFormatType(attachment->format);
312             const auto output_type = fs->GetFundamentalType(output->type_id);
313 
314             // Type checking
315             if (!(output_type & attachment_type)) {
316                 skip |=
317                     LogWarning(fs->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
318                                "Attachment %" PRIu32
319                                " of type `%s` does not match fragment shader output type of `%s`; resulting values are undefined",
320                                location, string_VkFormat(attachment->format), fs->DescribeType(output->type_id).c_str());
321             }
322         } else {            // !attachment && !output
323             assert(false);  // at least one exists in the map
324         }
325     }
326 
327     const auto output_zero = location_map.count(0) ? location_map[0].output : nullptr;
328     bool location_zero_has_alpha = output_zero && fs->get_def(output_zero->type_id) != fs->end() &&
329                                    fs->GetComponentsConsumedByType(output_zero->type_id, false) == 4;
330     if (alpha_to_coverage_enabled && !location_zero_has_alpha) {
331         skip |= LogError(fs->vk_shader_module(), kVUID_Core_Shader_NoAlphaAtLocation0WithAlphaToCoverage,
332                          "fragment shader doesn't declare alpha output at location 0 even though alpha to coverage is enabled.");
333     }
334 
335     return skip;
336 }
337 
ValidatePushConstantSetUpdate(const std::vector<uint8_t> & push_constant_data_update,const shader_struct_member & push_constant_used_in_shader,uint32_t & out_issue_index) const338 PushConstantByteState CoreChecks::ValidatePushConstantSetUpdate(const std::vector<uint8_t> &push_constant_data_update,
339                                                                 const shader_struct_member &push_constant_used_in_shader,
340                                                                 uint32_t &out_issue_index) const {
341     const auto *used_bytes = push_constant_used_in_shader.GetUsedbytes();
342     const auto used_bytes_size = used_bytes->size();
343     if (used_bytes_size == 0) return PC_Byte_Updated;
344 
345     const auto push_constant_data_update_size = push_constant_data_update.size();
346     const auto *data = push_constant_data_update.data();
347     if ((*data == PC_Byte_Updated) && std::memcmp(data, data + 1, push_constant_data_update_size - 1) == 0) {
348         if (used_bytes_size <= push_constant_data_update_size) {
349             return PC_Byte_Updated;
350         }
351         const auto used_bytes_size1 = used_bytes_size - push_constant_data_update_size;
352 
353         const auto *used_bytes_data1 = used_bytes->data() + push_constant_data_update_size;
354         if ((*used_bytes_data1 == 0) && std::memcmp(used_bytes_data1, used_bytes_data1 + 1, used_bytes_size1 - 1) == 0) {
355             return PC_Byte_Updated;
356         }
357     }
358 
359     uint32_t i = 0;
360     for (const auto used : *used_bytes) {
361         if (used) {
362             if (i >= push_constant_data_update.size() || push_constant_data_update[i] == PC_Byte_Not_Set) {
363                 out_issue_index = i;
364                 return PC_Byte_Not_Set;
365             } else if (push_constant_data_update[i] == PC_Byte_Not_Updated) {
366                 out_issue_index = i;
367                 return PC_Byte_Not_Updated;
368             }
369         }
370         ++i;
371     }
372     return PC_Byte_Updated;
373 }
374 
ValidatePushConstantUsage(const PIPELINE_STATE & pipeline,SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const std::string & vuid) const375 bool CoreChecks::ValidatePushConstantUsage(const PIPELINE_STATE &pipeline, SHADER_MODULE_STATE const *src,
376                                            VkPipelineShaderStageCreateInfo const *pStage, const std::string &vuid) const {
377     bool skip = false;
378     // Temp workaround to prevent false positive errors
379     // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2450
380     if (src->HasMultipleEntryPoints()) {
381         return skip;
382     }
383 
384     // Validate directly off the offsets. this isn't quite correct for arrays and matrices, but is a good first step.
385     const auto *entrypoint = src->FindEntrypointStruct(pStage->pName, pStage->stage);
386     if (!entrypoint || !entrypoint->push_constant_used_in_shader.IsUsed()) {
387         return skip;
388     }
389     std::vector<VkPushConstantRange> const *push_constant_ranges = pipeline.pipeline_layout->push_constant_ranges.get();
390 
391     bool found_stage = false;
392     for (auto const &range : *push_constant_ranges) {
393         if (range.stageFlags & pStage->stage) {
394             found_stage = true;
395             std::string location_desc;
396             std::vector<uint8_t> push_constant_bytes_set;
397             if (range.offset > 0) {
398                 push_constant_bytes_set.resize(range.offset, PC_Byte_Not_Set);
399             }
400             push_constant_bytes_set.resize(range.offset + range.size, PC_Byte_Updated);
401             uint32_t issue_index = 0;
402             const auto ret =
403                 ValidatePushConstantSetUpdate(push_constant_bytes_set, entrypoint->push_constant_used_in_shader, issue_index);
404 
405             if (ret == PC_Byte_Not_Set) {
406                 const auto loc_descr = entrypoint->push_constant_used_in_shader.GetLocationDesc(issue_index);
407                 LogObjectList objlist(src->vk_shader_module());
408                 objlist.add(pipeline.pipeline_layout->layout());
409                 skip |= LogError(objlist, vuid, "Push constant buffer:%s in %s is out of range in %s.", loc_descr.c_str(),
410                                  string_VkShaderStageFlags(pStage->stage).c_str(),
411                                  report_data->FormatHandle(pipeline.pipeline_layout->layout()).c_str());
412                 break;
413             }
414         }
415     }
416 
417     if (!found_stage) {
418         LogObjectList objlist(src->vk_shader_module());
419         objlist.add(pipeline.pipeline_layout->layout());
420         skip |= LogError(objlist, vuid, "Push constant is used in %s of %s. But %s doesn't set %s.",
421                          string_VkShaderStageFlags(pStage->stage).c_str(), report_data->FormatHandle(src->vk_shader_module()).c_str(),
422                          report_data->FormatHandle(pipeline.pipeline_layout->layout()).c_str(),
423                          string_VkShaderStageFlags(pStage->stage).c_str());
424     }
425     return skip;
426 }
427 
ValidateBuiltinLimits(SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint) const428 bool CoreChecks::ValidateBuiltinLimits(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint) const {
429     bool skip = false;
430 
431     // Currently all builtin tested are only found in fragment shaders
432     if (entrypoint.word(1) != spv::ExecutionModelFragment) {
433         return skip;
434     }
435 
436     // Find all builtin from just the interface variables
437     for (uint32_t id : FindEntrypointInterfaces(entrypoint)) {
438         auto insn = src->get_def(id);
439         assert(insn.opcode() == spv::OpVariable);
440         const decoration_set decorations = src->get_decorations(insn.word(2));
441 
442         // Currently don't need to search in structs
443         if (((decorations.flags & decoration_set::builtin_bit) != 0) && (decorations.builtin == spv::BuiltInSampleMask)) {
444             auto type_pointer = src->get_def(insn.word(1));
445             assert(type_pointer.opcode() == spv::OpTypePointer);
446 
447             auto type = src->get_def(type_pointer.word(3));
448             if (type.opcode() == spv::OpTypeArray) {
449                 uint32_t length = static_cast<uint32_t>(src->GetConstantValueById(type.word(3)));
450                 // Handles both the input and output sampleMask
451                 if (length > phys_dev_props.limits.maxSampleMaskWords) {
452                     skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-maxSampleMaskWords-00711",
453                                      "vkCreateGraphicsPipelines(): The BuiltIns SampleMask array sizes is %u which exceeds "
454                                      "maxSampleMaskWords of %u in %s.",
455                                      length, phys_dev_props.limits.maxSampleMaskWords,
456                                      report_data->FormatHandle(src->vk_shader_module()).c_str());
457                 }
458                 break;
459             }
460         }
461     }
462 
463     return skip;
464 }
465 
466 // Validate that data for each specialization entry is fully contained within the buffer.
ValidateSpecializations(VkPipelineShaderStageCreateInfo const * info) const467 bool CoreChecks::ValidateSpecializations(VkPipelineShaderStageCreateInfo const *info) const {
468     bool skip = false;
469 
470     VkSpecializationInfo const *spec = info->pSpecializationInfo;
471 
472     if (spec) {
473         for (auto i = 0u; i < spec->mapEntryCount; i++) {
474             if (spec->pMapEntries[i].offset >= spec->dataSize) {
475                 skip |= LogError(device, "VUID-VkSpecializationInfo-offset-00773",
476                                  "Specialization entry %u (for constant id %u) references memory outside provided specialization "
477                                  "data (bytes %u.." PRINTF_SIZE_T_SPECIFIER "; " PRINTF_SIZE_T_SPECIFIER " bytes provided).",
478                                  i, spec->pMapEntries[i].constantID, spec->pMapEntries[i].offset,
479                                  spec->pMapEntries[i].offset + spec->dataSize - 1, spec->dataSize);
480 
481                 continue;
482             }
483             if (spec->pMapEntries[i].offset + spec->pMapEntries[i].size > spec->dataSize) {
484                 skip |= LogError(device, "VUID-VkSpecializationInfo-pMapEntries-00774",
485                                  "Specialization entry %u (for constant id %u) references memory outside provided specialization "
486                                  "data (bytes %u.." PRINTF_SIZE_T_SPECIFIER "; " PRINTF_SIZE_T_SPECIFIER " bytes provided).",
487                                  i, spec->pMapEntries[i].constantID, spec->pMapEntries[i].offset,
488                                  spec->pMapEntries[i].offset + spec->pMapEntries[i].size - 1, spec->dataSize);
489             }
490             for (uint32_t j = i + 1; j < spec->mapEntryCount; ++j) {
491                 if (spec->pMapEntries[i].constantID == spec->pMapEntries[j].constantID) {
492                     skip |= LogError(device, "VUID-VkSpecializationInfo-constantID-04911",
493                                      "Specialization entry %" PRIu32 " and %" PRIu32 " have the same constantID (%" PRIu32 ").", i,
494                                      j, spec->pMapEntries[i].constantID);
495                 }
496             }
497         }
498     }
499 
500     return skip;
501 }
502 
503 // TODO (jbolz): Can this return a const reference?
TypeToDescriptorTypeSet(SHADER_MODULE_STATE const * module,uint32_t type_id,unsigned & descriptor_count,bool is_khr)504 static std::set<uint32_t> TypeToDescriptorTypeSet(SHADER_MODULE_STATE const *module, uint32_t type_id, unsigned &descriptor_count,
505                                                   bool is_khr) {
506     auto type = module->get_def(type_id);
507     bool is_storage_buffer = false;
508     descriptor_count = 1;
509     std::set<uint32_t> ret;
510 
511     // Strip off any array or ptrs. Where we remove array levels, adjust the  descriptor count for each dimension.
512     while (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypePointer || type.opcode() == spv::OpTypeRuntimeArray) {
513         if (type.opcode() == spv::OpTypeRuntimeArray) {
514             descriptor_count = 0;
515             type = module->get_def(type.word(2));
516         } else if (type.opcode() == spv::OpTypeArray) {
517             descriptor_count *= module->GetConstantValueById(type.word(3));
518             type = module->get_def(type.word(2));
519         } else {
520             if (type.word(2) == spv::StorageClassStorageBuffer) {
521                 is_storage_buffer = true;
522             }
523             type = module->get_def(type.word(3));
524         }
525     }
526 
527     switch (type.opcode()) {
528         case spv::OpTypeStruct: {
529             for (const auto insn : module->GetDecorationInstructions()) {
530                 if (insn.word(1) == type.word(1)) {
531                     if (insn.word(2) == spv::DecorationBlock) {
532                         if (is_storage_buffer) {
533                             ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
534                             ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
535                             return ret;
536                         } else {
537                             ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
538                             ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
539                             ret.insert(VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT);
540                             return ret;
541                         }
542                     } else if (insn.word(2) == spv::DecorationBufferBlock) {
543                         ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
544                         ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
545                         return ret;
546                     }
547                 }
548             }
549 
550             // Invalid
551             return ret;
552         }
553 
554         case spv::OpTypeSampler:
555             ret.insert(VK_DESCRIPTOR_TYPE_SAMPLER);
556             ret.insert(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
557             return ret;
558 
559         case spv::OpTypeSampledImage: {
560             // Slight relaxation for some GLSL historical madness: samplerBuffer doesn't really have a sampler, and a texel
561             // buffer descriptor doesn't really provide one. Allow this slight mismatch.
562             auto image_type = module->get_def(type.word(2));
563             auto dim = image_type.word(3);
564             auto sampled = image_type.word(7);
565             if (dim == spv::DimBuffer && sampled == 1) {
566                 ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
567                 return ret;
568             }
569         }
570             ret.insert(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
571             return ret;
572 
573         case spv::OpTypeImage: {
574             // Many descriptor types backing image types-- depends on dimension and whether the image will be used with a sampler.
575             // SPIRV for Vulkan requires that sampled be 1 or 2 -- leaving the decision to runtime is unacceptable.
576             auto dim = type.word(3);
577             auto sampled = type.word(7);
578 
579             if (dim == spv::DimSubpassData) {
580                 ret.insert(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
581                 return ret;
582             } else if (dim == spv::DimBuffer) {
583                 if (sampled == 1) {
584                     ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
585                     return ret;
586                 } else {
587                     ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
588                     return ret;
589                 }
590             } else if (sampled == 1) {
591                 ret.insert(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
592                 ret.insert(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
593                 return ret;
594             } else {
595                 ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
596                 return ret;
597             }
598         }
599         case spv::OpTypeAccelerationStructureNV:
600             is_khr ? ret.insert(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR)
601                    : ret.insert(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV);
602             return ret;
603 
604             // We shouldn't really see any other junk types -- but if we do, they're a mismatch.
605         default:
606             return ret;  // Matches nothing
607     }
608 }
609 
string_descriptorTypes(const std::set<uint32_t> & descriptor_types)610 static std::string string_descriptorTypes(const std::set<uint32_t> &descriptor_types) {
611     std::stringstream ss;
612     for (auto it = descriptor_types.begin(); it != descriptor_types.end(); ++it) {
613         if (ss.tellp()) ss << ", ";
614         ss << string_VkDescriptorType(VkDescriptorType(*it));
615     }
616     return ss.str();
617 }
618 
RequirePropertyFlag(VkBool32 check,char const * flag,char const * structure,const char * vuid) const619 bool CoreChecks::RequirePropertyFlag(VkBool32 check, char const *flag, char const *structure, const char *vuid) const {
620     if (!check) {
621         if (LogError(device, vuid, "Shader requires flag %s set in %s but it is not set on the device", flag, structure)) {
622             return true;
623         }
624     }
625 
626     return false;
627 }
628 
RequireFeature(VkBool32 feature,char const * feature_name,const char * vuid) const629 bool CoreChecks::RequireFeature(VkBool32 feature, char const *feature_name, const char *vuid) const {
630     if (!feature) {
631         if (LogError(device, vuid, "Shader requires %s but is not enabled on the device", feature_name)) {
632             return true;
633         }
634     }
635 
636     return false;
637 }
638 
ValidateShaderStageWritableOrAtomicDescriptor(VkShaderStageFlagBits stage,bool has_writable_descriptor,bool has_atomic_descriptor) const639 bool CoreChecks::ValidateShaderStageWritableOrAtomicDescriptor(VkShaderStageFlagBits stage, bool has_writable_descriptor,
640                                                                bool has_atomic_descriptor) const {
641     bool skip = false;
642 
643     if (has_writable_descriptor || has_atomic_descriptor) {
644         switch (stage) {
645             case VK_SHADER_STAGE_COMPUTE_BIT:
646             case VK_SHADER_STAGE_RAYGEN_BIT_NV:
647             case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
648             case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
649             case VK_SHADER_STAGE_MISS_BIT_NV:
650             case VK_SHADER_STAGE_INTERSECTION_BIT_NV:
651             case VK_SHADER_STAGE_CALLABLE_BIT_NV:
652             case VK_SHADER_STAGE_TASK_BIT_NV:
653             case VK_SHADER_STAGE_MESH_BIT_NV:
654                 /* No feature requirements for writes and atomics from compute
655                  * raytracing, or mesh stages */
656                 break;
657             case VK_SHADER_STAGE_FRAGMENT_BIT:
658                 skip |= RequireFeature(enabled_features.core.fragmentStoresAndAtomics, "fragmentStoresAndAtomics",
659                                        "VUID-RuntimeSpirv-NonWritable-06340");
660                 break;
661             default:
662                 skip |= RequireFeature(enabled_features.core.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics",
663                                        "VUID-RuntimeSpirv-NonWritable-06341");
664                 break;
665         }
666     }
667 
668     return skip;
669 }
670 
ValidateShaderStageGroupNonUniform(SHADER_MODULE_STATE const * module,VkShaderStageFlagBits stage,spirv_inst_iter & insn) const671 bool CoreChecks::ValidateShaderStageGroupNonUniform(SHADER_MODULE_STATE const *module, VkShaderStageFlagBits stage,
672                                                     spirv_inst_iter &insn) const {
673     bool skip = false;
674 
675     // Check anything using a group operation (which currently is only OpGroupNonUnifrom* operations)
676     if (GroupOperation(insn.opcode()) == true) {
677         // Check the quad operations.
678         if ((insn.opcode() == spv::OpGroupNonUniformQuadBroadcast) || (insn.opcode() == spv::OpGroupNonUniformQuadSwap)) {
679             if ((stage != VK_SHADER_STAGE_FRAGMENT_BIT) && (stage != VK_SHADER_STAGE_COMPUTE_BIT)) {
680                 skip |=
681                     RequireFeature(phys_dev_props_core11.subgroupQuadOperationsInAllStages,
682                                    "VkPhysicalDeviceSubgroupProperties::quadOperationsInAllStages", "VUID-RuntimeSpirv-None-06342");
683             }
684         }
685 
686         uint32_t scope_type = spv::ScopeMax;
687         if (insn.opcode() == spv::OpGroupNonUniformPartitionNV) {
688             // OpGroupNonUniformPartitionNV always assumed subgroup as missing operand
689             scope_type = spv::ScopeSubgroup;
690         } else {
691             // "All <id> used for Scope <id> must be of an OpConstant"
692             auto scope_id = module->get_def(insn.word(3));
693             scope_type = scope_id.word(3);
694         }
695 
696         if (scope_type == spv::ScopeSubgroup) {
697             // "Group operations with subgroup scope" must have stage support
698             const VkSubgroupFeatureFlags supported_stages = phys_dev_props_core11.subgroupSupportedStages;
699             skip |= RequirePropertyFlag(supported_stages & stage, string_VkShaderStageFlagBits(stage),
700                                         "VkPhysicalDeviceSubgroupProperties::supportedStages", "VUID-RuntimeSpirv-None-06343");
701         }
702 
703         if (!enabled_features.core12.shaderSubgroupExtendedTypes) {
704             auto type = module->get_def(insn.word(1));
705 
706             if (type.opcode() == spv::OpTypeVector) {
707                 // Get the element type
708                 type = module->get_def(type.word(2));
709             }
710 
711             if (type.opcode() != spv::OpTypeBool) {
712                 // Both OpTypeInt and OpTypeFloat the width is in the 2nd word.
713                 const uint32_t width = type.word(2);
714 
715                 if ((type.opcode() == spv::OpTypeFloat && width == 16) ||
716                     (type.opcode() == spv::OpTypeInt && (width == 8 || width == 16 || width == 64))) {
717                     skip |= RequireFeature(enabled_features.core12.shaderSubgroupExtendedTypes,
718                                            "VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures::shaderSubgroupExtendedTypes",
719                                            "VUID-RuntimeSpirv-None-06275");
720                 }
721             }
722         }
723     }
724 
725     return skip;
726 }
727 
ValidateMemoryScope(SHADER_MODULE_STATE const * src,const spirv_inst_iter & insn) const728 bool CoreChecks::ValidateMemoryScope(SHADER_MODULE_STATE const *src, const spirv_inst_iter &insn) const {
729     bool skip = false;
730 
731     const auto &entry = MemoryScopeParamPosition(insn.opcode());
732     if (entry > 0) {
733         const uint32_t scope_id = insn.word(entry);
734         if (enabled_features.core12.vulkanMemoryModel && !enabled_features.core12.vulkanMemoryModelDeviceScope) {
735             const auto &iter = src->GetConstantDef(scope_id);
736             if (iter != src->end()) {
737                 if (GetConstantValue(iter) == spv::Scope::ScopeDevice) {
738                     skip |= LogError(device, "VUID-RuntimeSpirv-vulkanMemoryModel-06265",
739                                      "VkPhysicalDeviceVulkan12Features::vulkanMemoryModel is enabled and "
740                                      "VkPhysicalDeviceVulkan12Features::vulkanMemoryModelDeviceScope is disabled, but Device "
741                                      "memory scope is used.");
742                 }
743             }
744         } else if (!enabled_features.core12.vulkanMemoryModel) {
745             const auto &iter = src->GetConstantDef(scope_id);
746             if (iter != src->end()) {
747                 if (GetConstantValue(iter) == spv::Scope::ScopeQueueFamily) {
748                     skip |= LogError(device, "VUID-RuntimeSpirv-vulkanMemoryModel-06266",
749                                      "VkPhysicalDeviceVulkan12Features::vulkanMemoryModel is not enabled, but QueueFamily "
750                                      "memory scope is used.");
751                 }
752             }
753         }
754     }
755 
756     return skip;
757 }
758 
ValidateWorkgroupSize(SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const std::unordered_map<uint32_t,std::vector<uint32_t>> & id_value_map) const759 bool CoreChecks::ValidateWorkgroupSize(SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
760                                        const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) const {
761     bool skip = false;
762 
763     std::array<uint32_t, 3> work_group_size = src->GetWorkgroupSize(pStage, id_value_map);
764 
765     for (uint32_t i = 0; i < 3; ++i) {
766         if (work_group_size[i] > phys_dev_props.limits.maxComputeWorkGroupSize[i]) {
767             const char member = 'x' + static_cast<int8_t>(i);
768             skip |= LogError(device, kVUID_Core_Shader_MaxComputeWorkGroupSize,
769                              "Specialization constant is being used to specialize WorkGroupSize.%c, but value (%" PRIu32
770                              ") is greater than VkPhysicalDeviceLimits::maxComputeWorkGroupSize[%" PRIu32 "] = %" PRIu32 ".",
771                              member, work_group_size[i], i, phys_dev_props.limits.maxComputeWorkGroupSize[i]);
772         }
773     }
774     return skip;
775 }
776 
ValidateShaderStageInputOutputLimits(SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const PIPELINE_STATE * pipeline,spirv_inst_iter entrypoint) const777 bool CoreChecks::ValidateShaderStageInputOutputLimits(SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
778                                                       const PIPELINE_STATE *pipeline, spirv_inst_iter entrypoint) const {
779     if (pStage->stage == VK_SHADER_STAGE_COMPUTE_BIT || pStage->stage == VK_SHADER_STAGE_ALL_GRAPHICS ||
780         pStage->stage == VK_SHADER_STAGE_ALL) {
781         return false;
782     }
783 
784     bool skip = false;
785     auto const &limits = phys_dev_props.limits;
786 
787     std::set<uint32_t> patch_i_ds;
788     struct Variable {
789         uint32_t baseTypePtrID;
790         uint32_t ID;
791         uint32_t storageClass;
792     };
793     std::vector<Variable> variables;
794 
795     uint32_t num_vertices = 0;
796     bool is_iso_lines = false;
797     bool is_point_mode = false;
798 
799     auto entrypoint_variables = FindEntrypointInterfaces(entrypoint);
800 
801     for (auto insn : *src) {
802         switch (insn.opcode()) {
803             // Find all Patch decorations
804             case spv::OpDecorate:
805                 switch (insn.word(2)) {
806                     case spv::DecorationPatch: {
807                         patch_i_ds.insert(insn.word(1));
808                         break;
809                     }
810                     default:
811                         break;
812                 }
813                 break;
814             // Find all input and output variables
815             case spv::OpVariable: {
816                 Variable var = {};
817                 var.storageClass = insn.word(3);
818                 if ((var.storageClass == spv::StorageClassInput || var.storageClass == spv::StorageClassOutput) &&
819                     // Only include variables in the entrypoint's interface
820                     find(entrypoint_variables.begin(), entrypoint_variables.end(), insn.word(2)) != entrypoint_variables.end()) {
821                     var.baseTypePtrID = insn.word(1);
822                     var.ID = insn.word(2);
823                     variables.push_back(var);
824                 }
825                 break;
826             }
827             case spv::OpExecutionMode:
828                 if (insn.word(1) == entrypoint.word(2)) {
829                     switch (insn.word(2)) {
830                         default:
831                             break;
832                         case spv::ExecutionModeOutputVertices:
833                             num_vertices = insn.word(3);
834                             break;
835                         case spv::ExecutionModeIsolines:
836                             is_iso_lines = true;
837                             break;
838                         case spv::ExecutionModePointMode:
839                             is_point_mode = true;
840                             break;
841                     }
842                 }
843                 break;
844             default:
845                 break;
846         }
847     }
848 
849     bool strip_output_array_level =
850         (pStage->stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || pStage->stage == VK_SHADER_STAGE_MESH_BIT_NV);
851     bool strip_input_array_level =
852         (pStage->stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ||
853          pStage->stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT || pStage->stage == VK_SHADER_STAGE_GEOMETRY_BIT);
854 
855     uint32_t num_comp_in = 0, num_comp_out = 0;
856     int max_comp_in = 0, max_comp_out = 0;
857 
858     auto inputs = src->CollectInterfaceByLocation(entrypoint, spv::StorageClassInput, strip_input_array_level);
859     auto outputs = src->CollectInterfaceByLocation(entrypoint, spv::StorageClassOutput, strip_output_array_level);
860 
861     // Find max component location used for input variables.
862     for (auto &var : inputs) {
863         int location = var.first.first;
864         int component = var.first.second;
865         interface_var &iv = var.second;
866 
867         // Only need to look at the first location, since we use the type's whole size
868         if (iv.offset != 0) {
869             continue;
870         }
871 
872         if (iv.is_patch) {
873             continue;
874         }
875 
876         int num_components = src->GetComponentsConsumedByType(iv.type_id, strip_input_array_level);
877         max_comp_in = std::max(max_comp_in, location * 4 + component + num_components);
878     }
879 
880     // Find max component location used for output variables.
881     for (auto &var : outputs) {
882         int location = var.first.first;
883         int component = var.first.second;
884         interface_var &iv = var.second;
885 
886         // Only need to look at the first location, since we use the type's whole size
887         if (iv.offset != 0) {
888             continue;
889         }
890 
891         if (iv.is_patch) {
892             continue;
893         }
894 
895         int num_components = src->GetComponentsConsumedByType(iv.type_id, strip_output_array_level);
896         max_comp_out = std::max(max_comp_out, location * 4 + component + num_components);
897     }
898 
899     // XXX TODO: Would be nice to rewrite this to use CollectInterfaceByLocation (or something similar),
900     // but that doesn't include builtins.
901     // When rewritten, using the CreatePipelineExceedVertexMaxComponentsWithBuiltins test it would be nice to also let the user know
902     // how many components were from builtins as it might not be obvious
903     for (auto &var : variables) {
904         // Check if the variable is a patch. Patches can also be members of blocks,
905         // but if they are then the top-level arrayness has already been stripped
906         // by the time GetComponentsConsumedByType gets to it.
907         bool is_patch = patch_i_ds.find(var.ID) != patch_i_ds.end();
908 
909         if (var.storageClass == spv::StorageClassInput) {
910             num_comp_in += src->GetComponentsConsumedByType(var.baseTypePtrID, strip_input_array_level && !is_patch);
911         } else {  // var.storageClass == spv::StorageClassOutput
912             num_comp_out += src->GetComponentsConsumedByType(var.baseTypePtrID, strip_output_array_level && !is_patch);
913         }
914     }
915 
916     switch (pStage->stage) {
917         case VK_SHADER_STAGE_VERTEX_BIT:
918             if (num_comp_out > limits.maxVertexOutputComponents) {
919                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
920                                  "Invalid Pipeline CreateInfo State: Vertex shader exceeds "
921                                  "VkPhysicalDeviceLimits::maxVertexOutputComponents of %u "
922                                  "components by %u components",
923                                  limits.maxVertexOutputComponents, num_comp_out - limits.maxVertexOutputComponents);
924             }
925             if (max_comp_out > static_cast<int>(limits.maxVertexOutputComponents)) {
926                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
927                                  "Invalid Pipeline CreateInfo State: Vertex shader output variable uses location that "
928                                  "exceeds component limit VkPhysicalDeviceLimits::maxVertexOutputComponents (%u)",
929                                  limits.maxVertexOutputComponents);
930             }
931             break;
932 
933         case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
934             if (num_comp_in > limits.maxTessellationControlPerVertexInputComponents) {
935                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
936                                  "Invalid Pipeline CreateInfo State: Tessellation control shader exceeds "
937                                  "VkPhysicalDeviceLimits::maxTessellationControlPerVertexInputComponents of %u "
938                                  "components by %u components",
939                                  limits.maxTessellationControlPerVertexInputComponents,
940                                  num_comp_in - limits.maxTessellationControlPerVertexInputComponents);
941             }
942             if (max_comp_in > static_cast<int>(limits.maxTessellationControlPerVertexInputComponents)) {
943                 skip |=
944                     LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
945                              "Invalid Pipeline CreateInfo State: Tessellation control shader input variable uses location that "
946                              "exceeds component limit VkPhysicalDeviceLimits::maxTessellationControlPerVertexInputComponents (%u)",
947                              limits.maxTessellationControlPerVertexInputComponents);
948             }
949             if (num_comp_out > limits.maxTessellationControlPerVertexOutputComponents) {
950                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
951                                  "Invalid Pipeline CreateInfo State: Tessellation control shader exceeds "
952                                  "VkPhysicalDeviceLimits::maxTessellationControlPerVertexOutputComponents of %u "
953                                  "components by %u components",
954                                  limits.maxTessellationControlPerVertexOutputComponents,
955                                  num_comp_out - limits.maxTessellationControlPerVertexOutputComponents);
956             }
957             if (max_comp_out > static_cast<int>(limits.maxTessellationControlPerVertexOutputComponents)) {
958                 skip |=
959                     LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
960                              "Invalid Pipeline CreateInfo State: Tessellation control shader output variable uses location that "
961                              "exceeds component limit VkPhysicalDeviceLimits::maxTessellationControlPerVertexOutputComponents (%u)",
962                              limits.maxTessellationControlPerVertexOutputComponents);
963             }
964             break;
965 
966         case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
967             if (num_comp_in > limits.maxTessellationEvaluationInputComponents) {
968                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
969                                  "Invalid Pipeline CreateInfo State: Tessellation evaluation shader exceeds "
970                                  "VkPhysicalDeviceLimits::maxTessellationEvaluationInputComponents of %u "
971                                  "components by %u components",
972                                  limits.maxTessellationEvaluationInputComponents,
973                                  num_comp_in - limits.maxTessellationEvaluationInputComponents);
974             }
975             if (max_comp_in > static_cast<int>(limits.maxTessellationEvaluationInputComponents)) {
976                 skip |=
977                     LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
978                              "Invalid Pipeline CreateInfo State: Tessellation evaluation shader input variable uses location that "
979                              "exceeds component limit VkPhysicalDeviceLimits::maxTessellationEvaluationInputComponents (%u)",
980                              limits.maxTessellationEvaluationInputComponents);
981             }
982             if (num_comp_out > limits.maxTessellationEvaluationOutputComponents) {
983                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
984                                  "Invalid Pipeline CreateInfo State: Tessellation evaluation shader exceeds "
985                                  "VkPhysicalDeviceLimits::maxTessellationEvaluationOutputComponents of %u "
986                                  "components by %u components",
987                                  limits.maxTessellationEvaluationOutputComponents,
988                                  num_comp_out - limits.maxTessellationEvaluationOutputComponents);
989             }
990             if (max_comp_out > static_cast<int>(limits.maxTessellationEvaluationOutputComponents)) {
991                 skip |=
992                     LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
993                              "Invalid Pipeline CreateInfo State: Tessellation evaluation shader output variable uses location that "
994                              "exceeds component limit VkPhysicalDeviceLimits::maxTessellationEvaluationOutputComponents (%u)",
995                              limits.maxTessellationEvaluationOutputComponents);
996             }
997             // Portability validation
998             if (IsExtEnabled(device_extensions.vk_khr_portability_subset)) {
999                 if (is_iso_lines && (VK_FALSE == enabled_features.portability_subset_features.tessellationIsolines)) {
1000                     skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-tessellationShader-06326",
1001                                      "Invalid Pipeline CreateInfo state (portability error): Tessellation evaluation shader"
1002                                      " is using abstract patch type IsoLines, but this is not supported on this platform");
1003                 }
1004                 if (is_point_mode && (VK_FALSE == enabled_features.portability_subset_features.tessellationPointMode)) {
1005                     skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-tessellationShader-06327",
1006                                      "Invalid Pipeline CreateInfo state (portability error): Tessellation evaluation shader"
1007                                      " is using abstract patch type PointMode, but this is not supported on this platform");
1008                 }
1009             }
1010             break;
1011 
1012         case VK_SHADER_STAGE_GEOMETRY_BIT:
1013             if (num_comp_in > limits.maxGeometryInputComponents) {
1014                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1015                                  "Invalid Pipeline CreateInfo State: Geometry shader exceeds "
1016                                  "VkPhysicalDeviceLimits::maxGeometryInputComponents of %u "
1017                                  "components by %u components",
1018                                  limits.maxGeometryInputComponents, num_comp_in - limits.maxGeometryInputComponents);
1019             }
1020             if (max_comp_in > static_cast<int>(limits.maxGeometryInputComponents)) {
1021                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1022                                  "Invalid Pipeline CreateInfo State: Geometry shader input variable uses location that "
1023                                  "exceeds component limit VkPhysicalDeviceLimits::maxGeometryInputComponents (%u)",
1024                                  limits.maxGeometryInputComponents);
1025             }
1026             if (num_comp_out > limits.maxGeometryOutputComponents) {
1027                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1028                                  "Invalid Pipeline CreateInfo State: Geometry shader exceeds "
1029                                  "VkPhysicalDeviceLimits::maxGeometryOutputComponents of %u "
1030                                  "components by %u components",
1031                                  limits.maxGeometryOutputComponents, num_comp_out - limits.maxGeometryOutputComponents);
1032             }
1033             if (max_comp_out > static_cast<int>(limits.maxGeometryOutputComponents)) {
1034                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1035                                  "Invalid Pipeline CreateInfo State: Geometry shader output variable uses location that "
1036                                  "exceeds component limit VkPhysicalDeviceLimits::maxGeometryOutputComponents (%u)",
1037                                  limits.maxGeometryOutputComponents);
1038             }
1039             if (num_comp_out * num_vertices > limits.maxGeometryTotalOutputComponents) {
1040                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1041                                  "Invalid Pipeline CreateInfo State: Geometry shader exceeds "
1042                                  "VkPhysicalDeviceLimits::maxGeometryTotalOutputComponents of %u "
1043                                  "components by %u components",
1044                                  limits.maxGeometryTotalOutputComponents,
1045                                  num_comp_out * num_vertices - limits.maxGeometryTotalOutputComponents);
1046             }
1047             break;
1048 
1049         case VK_SHADER_STAGE_FRAGMENT_BIT:
1050             if (num_comp_in > limits.maxFragmentInputComponents) {
1051                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1052                                  "Invalid Pipeline CreateInfo State: Fragment shader exceeds "
1053                                  "VkPhysicalDeviceLimits::maxFragmentInputComponents of %u "
1054                                  "components by %u components",
1055                                  limits.maxFragmentInputComponents, num_comp_in - limits.maxFragmentInputComponents);
1056             }
1057             if (max_comp_in > static_cast<int>(limits.maxFragmentInputComponents)) {
1058                 skip |= LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-Location-06272",
1059                                  "Invalid Pipeline CreateInfo State: Fragment shader input variable uses location that "
1060                                  "exceeds component limit VkPhysicalDeviceLimits::maxFragmentInputComponents (%u)",
1061                                  limits.maxFragmentInputComponents);
1062             }
1063             break;
1064 
1065         case VK_SHADER_STAGE_RAYGEN_BIT_NV:
1066         case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
1067         case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
1068         case VK_SHADER_STAGE_MISS_BIT_NV:
1069         case VK_SHADER_STAGE_INTERSECTION_BIT_NV:
1070         case VK_SHADER_STAGE_CALLABLE_BIT_NV:
1071         case VK_SHADER_STAGE_TASK_BIT_NV:
1072         case VK_SHADER_STAGE_MESH_BIT_NV:
1073             break;
1074 
1075         default:
1076             assert(false);  // This should never happen
1077     }
1078     return skip;
1079 }
1080 
ValidateShaderStorageImageFormats(SHADER_MODULE_STATE const * src) const1081 bool CoreChecks::ValidateShaderStorageImageFormats(SHADER_MODULE_STATE const *src) const {
1082     bool skip = false;
1083 
1084     // Got through all ImageRead/Write instructions
1085     for (auto insn : *src) {
1086         switch (insn.opcode()) {
1087             case spv::OpImageSparseRead:
1088             case spv::OpImageRead: {
1089                 spirv_inst_iter type_def = src->GetImageFormatInst(insn.word(3));
1090                 if (type_def != src->end()) {
1091                     const auto dim = type_def.word(3);
1092                     // If the Image Dim operand is not SubpassData, the Image Format must not be Unknown, unless the
1093                     // StorageImageReadWithoutFormat Capability was declared.
1094                     if (dim != spv::DimSubpassData && type_def.word(8) == spv::ImageFormatUnknown) {
1095                         skip |= RequireFeature(enabled_features.core.shaderStorageImageReadWithoutFormat,
1096                                                "shaderStorageImageReadWithoutFormat",
1097                                                kVUID_Features_shaderStorageImageReadWithoutFormat);
1098                     }
1099                 }
1100                 break;
1101             }
1102             case spv::OpImageWrite: {
1103                 spirv_inst_iter type_def = src->GetImageFormatInst(insn.word(1));
1104                 if (type_def != src->end()) {
1105                     if (type_def.word(8) == spv::ImageFormatUnknown) {
1106                         skip |= RequireFeature(enabled_features.core.shaderStorageImageWriteWithoutFormat,
1107                                                "shaderStorageImageWriteWithoutFormat",
1108                                                kVUID_Features_shaderStorageImageWriteWithoutFormat);
1109                     }
1110                 }
1111                 break;
1112             }
1113         }
1114     }
1115 
1116     // Go through all variables for images and check decorations
1117     for (auto insn : *src) {
1118         if (insn.opcode() != spv::OpVariable)
1119             continue;
1120 
1121         uint32_t var = insn.word(2);
1122         spirv_inst_iter type_def = src->GetImageFormatInst(insn.word(1));
1123         if (type_def == src->end())
1124             continue;
1125         // Only check if the Image Dim operand is not SubpassData
1126         const auto dim = type_def.word(3);
1127         if (dim == spv::DimSubpassData) continue;
1128         // Only check storage images
1129         if (type_def.word(7) != 2) continue;
1130         if (type_def.word(8) != spv::ImageFormatUnknown) continue;
1131 
1132         decoration_set img_decorations = src->get_decorations(var);
1133 
1134         if (!enabled_features.core.shaderStorageImageReadWithoutFormat &&
1135             !(img_decorations.flags & decoration_set::nonreadable_bit)) {
1136             skip |= LogError(device, "VUID-RuntimeSpirv-OpTypeImage-06270",
1137                              "shaderStorageImageReadWithoutFormat not supported but variable %" PRIu32
1138                              " "
1139                              " without format not marked a NonReadable",
1140                              var);
1141         }
1142 
1143         if (!enabled_features.core.shaderStorageImageWriteWithoutFormat &&
1144             !(img_decorations.flags & decoration_set::nonwritable_bit)) {
1145             skip |= LogError(device, "VUID-RuntimeSpirv-OpTypeImage-06269",
1146                              "shaderStorageImageWriteWithoutFormat not supported but variable %" PRIu32
1147                              " "
1148                              "without format not marked a NonWritable",
1149                              var);
1150         }
1151     }
1152 
1153     return skip;
1154 }
1155 
ValidateShaderStageMaxResources(VkShaderStageFlagBits stage,const PIPELINE_STATE * pipeline) const1156 bool CoreChecks::ValidateShaderStageMaxResources(VkShaderStageFlagBits stage, const PIPELINE_STATE *pipeline) const {
1157     bool skip = false;
1158     uint32_t total_resources = 0;
1159 
1160     // Only currently testing for graphics and compute pipelines
1161     // TODO: Add check and support for Ray Tracing pipeline VUID 03428
1162     if ((stage & (VK_SHADER_STAGE_ALL_GRAPHICS | VK_SHADER_STAGE_COMPUTE_BIT)) == 0) {
1163         return false;
1164     }
1165 
1166     if (stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
1167         if (pipeline->rp_state->use_dynamic_rendering) {
1168             total_resources += pipeline->rp_state->dynamic_rendering_pipeline_create_info.colorAttachmentCount;
1169         } else {
1170             // "For the fragment shader stage the framebuffer color attachments also count against this limit"
1171             total_resources +=
1172                 pipeline->rp_state->createInfo.pSubpasses[pipeline->create_info.graphics.subpass].colorAttachmentCount;
1173         }
1174     }
1175 
1176     // TODO: This reuses a lot of GetDescriptorCountMaxPerStage but currently would need to make it agnostic in a way to handle
1177     // input from CreatePipeline and CreatePipelineLayout level
1178     for (auto set_layout : pipeline->pipeline_layout->set_layouts) {
1179         if ((set_layout->GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT) != 0) {
1180             continue;
1181         }
1182 
1183         for (uint32_t binding_idx = 0; binding_idx < set_layout->GetBindingCount(); binding_idx++) {
1184             const VkDescriptorSetLayoutBinding *binding = set_layout->GetDescriptorSetLayoutBindingPtrFromIndex(binding_idx);
1185             // Bindings with a descriptorCount of 0 are "reserved" and should be skipped
1186             if (((stage & binding->stageFlags) != 0) && (binding->descriptorCount > 0)) {
1187                 // Check only descriptor types listed in maxPerStageResources description in spec
1188                 switch (binding->descriptorType) {
1189                     case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
1190                     case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
1191                     case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
1192                     case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
1193                     case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
1194                     case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
1195                     case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
1196                     case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
1197                     case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
1198                     case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
1199                         total_resources += binding->descriptorCount;
1200                         break;
1201                     default:
1202                         break;
1203                 }
1204             }
1205         }
1206     }
1207 
1208     if (total_resources > phys_dev_props.limits.maxPerStageResources) {
1209         const char *vuid = (stage == VK_SHADER_STAGE_COMPUTE_BIT) ? "VUID-VkComputePipelineCreateInfo-layout-01687"
1210                                                                   : "VUID-VkGraphicsPipelineCreateInfo-layout-01688";
1211         skip |= LogError(pipeline->pipeline(), vuid,
1212                          "Invalid Pipeline CreateInfo State: Shader Stage %s exceeds component limit "
1213                          "VkPhysicalDeviceLimits::maxPerStageResources (%u)",
1214                          string_VkShaderStageFlagBits(stage), phys_dev_props.limits.maxPerStageResources);
1215     }
1216 
1217     return skip;
1218 }
1219 
1220 // copy the specialization constant value into buf, if it is present
GetSpecConstantValue(VkPipelineShaderStageCreateInfo const * pStage,uint32_t spec_id,void * buf)1221 void GetSpecConstantValue(VkPipelineShaderStageCreateInfo const *pStage, uint32_t spec_id, void *buf) {
1222     VkSpecializationInfo const *spec = pStage->pSpecializationInfo;
1223 
1224     if (spec && spec_id < spec->mapEntryCount) {
1225         memcpy(buf, (uint8_t *)spec->pData + spec->pMapEntries[spec_id].offset, spec->pMapEntries[spec_id].size);
1226     }
1227 }
1228 
1229 // Fill in value with the constant or specialization constant value, if available.
1230 // Returns true if the value has been accurately filled out.
GetIntConstantValue(spirv_inst_iter insn,SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const layer_data::unordered_map<uint32_t,uint32_t> & id_to_spec_id,uint32_t * value)1231 static bool GetIntConstantValue(spirv_inst_iter insn, SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
1232                                 const layer_data::unordered_map<uint32_t, uint32_t> &id_to_spec_id, uint32_t *value) {
1233     auto type_id = src->get_def(insn.word(1));
1234     if (type_id.opcode() != spv::OpTypeInt || type_id.word(2) != 32) {
1235         return false;
1236     }
1237     switch (insn.opcode()) {
1238         case spv::OpSpecConstant:
1239             *value = insn.word(3);
1240             GetSpecConstantValue(pStage, id_to_spec_id.at(insn.word(2)), value);
1241             return true;
1242         case spv::OpConstant:
1243             *value = insn.word(3);
1244             return true;
1245         default:
1246             return false;
1247     }
1248 }
1249 
1250 // Map SPIR-V type to VK_COMPONENT_TYPE enum
GetComponentType(spirv_inst_iter insn,SHADER_MODULE_STATE const * src)1251 VkComponentTypeNV GetComponentType(spirv_inst_iter insn, SHADER_MODULE_STATE const *src) {
1252     switch (insn.opcode()) {
1253         case spv::OpTypeInt:
1254             switch (insn.word(2)) {
1255                 case 8:
1256                     return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT8_NV : VK_COMPONENT_TYPE_UINT8_NV;
1257                 case 16:
1258                     return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT16_NV : VK_COMPONENT_TYPE_UINT16_NV;
1259                 case 32:
1260                     return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT32_NV : VK_COMPONENT_TYPE_UINT32_NV;
1261                 case 64:
1262                     return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT64_NV : VK_COMPONENT_TYPE_UINT64_NV;
1263                 default:
1264                     return VK_COMPONENT_TYPE_MAX_ENUM_NV;
1265             }
1266         case spv::OpTypeFloat:
1267             switch (insn.word(2)) {
1268                 case 16:
1269                     return VK_COMPONENT_TYPE_FLOAT16_NV;
1270                 case 32:
1271                     return VK_COMPONENT_TYPE_FLOAT32_NV;
1272                 case 64:
1273                     return VK_COMPONENT_TYPE_FLOAT64_NV;
1274                 default:
1275                     return VK_COMPONENT_TYPE_MAX_ENUM_NV;
1276             }
1277         default:
1278             return VK_COMPONENT_TYPE_MAX_ENUM_NV;
1279     }
1280 }
1281 
1282 // Validate SPV_NV_cooperative_matrix behavior that can't be statically validated
1283 // in SPIRV-Tools (e.g. due to specialization constant usage).
ValidateCooperativeMatrix(SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const PIPELINE_STATE * pipeline) const1284 bool CoreChecks::ValidateCooperativeMatrix(SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
1285                                            const PIPELINE_STATE *pipeline) const {
1286     bool skip = false;
1287 
1288     // Map SPIR-V result ID to specialization constant id (SpecId decoration value)
1289     layer_data::unordered_map<uint32_t, uint32_t> id_to_spec_id;
1290     // Map SPIR-V result ID to the ID of its type.
1291     layer_data::unordered_map<uint32_t, uint32_t> id_to_type_id;
1292 
1293     struct CoopMatType {
1294         uint32_t scope, rows, cols;
1295         VkComponentTypeNV component_type;
1296         bool all_constant;
1297 
1298         CoopMatType() : scope(0), rows(0), cols(0), component_type(VK_COMPONENT_TYPE_MAX_ENUM_NV), all_constant(false) {}
1299 
1300         void Init(uint32_t id, SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
1301                   const layer_data::unordered_map<uint32_t, uint32_t> &id_to_spec_id) {
1302             spirv_inst_iter insn = src->get_def(id);
1303             uint32_t component_type_id = insn.word(2);
1304             uint32_t scope_id = insn.word(3);
1305             uint32_t rows_id = insn.word(4);
1306             uint32_t cols_id = insn.word(5);
1307             auto component_type_iter = src->get_def(component_type_id);
1308             auto scope_iter = src->get_def(scope_id);
1309             auto rows_iter = src->get_def(rows_id);
1310             auto cols_iter = src->get_def(cols_id);
1311 
1312             all_constant = true;
1313             if (!GetIntConstantValue(scope_iter, src, pStage, id_to_spec_id, &scope)) {
1314                 all_constant = false;
1315             }
1316             if (!GetIntConstantValue(rows_iter, src, pStage, id_to_spec_id, &rows)) {
1317                 all_constant = false;
1318             }
1319             if (!GetIntConstantValue(cols_iter, src, pStage, id_to_spec_id, &cols)) {
1320                 all_constant = false;
1321             }
1322             component_type = GetComponentType(component_type_iter, src);
1323         }
1324     };
1325 
1326     bool seen_coopmat_capability = false;
1327 
1328     for (auto insn : *src) {
1329         // Whitelist instructions whose result can be a cooperative matrix type, and
1330         // keep track of their types. It would be nice if SPIRV-Headers generated code
1331         // to identify which instructions have a result type and result id. Lacking that,
1332         // this whitelist is based on the set of instructions that
1333         // SPV_NV_cooperative_matrix says can be used with cooperative matrix types.
1334         switch (insn.opcode()) {
1335             case spv::OpLoad:
1336             case spv::OpCooperativeMatrixLoadNV:
1337             case spv::OpCooperativeMatrixMulAddNV:
1338             case spv::OpSNegate:
1339             case spv::OpFNegate:
1340             case spv::OpIAdd:
1341             case spv::OpFAdd:
1342             case spv::OpISub:
1343             case spv::OpFSub:
1344             case spv::OpFDiv:
1345             case spv::OpSDiv:
1346             case spv::OpUDiv:
1347             case spv::OpMatrixTimesScalar:
1348             case spv::OpConstantComposite:
1349             case spv::OpCompositeConstruct:
1350             case spv::OpConvertFToU:
1351             case spv::OpConvertFToS:
1352             case spv::OpConvertSToF:
1353             case spv::OpConvertUToF:
1354             case spv::OpUConvert:
1355             case spv::OpSConvert:
1356             case spv::OpFConvert:
1357                 id_to_type_id[insn.word(2)] = insn.word(1);
1358                 break;
1359             default:
1360                 break;
1361         }
1362 
1363         switch (insn.opcode()) {
1364             case spv::OpDecorate:
1365                 if (insn.word(2) == spv::DecorationSpecId) {
1366                     id_to_spec_id[insn.word(1)] = insn.word(3);
1367                 }
1368                 break;
1369             case spv::OpCapability:
1370                 if (insn.word(1) == spv::CapabilityCooperativeMatrixNV) {
1371                     seen_coopmat_capability = true;
1372 
1373                     if (!(pStage->stage & phys_dev_ext_props.cooperative_matrix_props.cooperativeMatrixSupportedStages)) {
1374                         skip |= LogError(
1375                             pipeline->pipeline(), "VUID-RuntimeSpirv-OpTypeCooperativeMatrixNV-06322",
1376                             "OpTypeCooperativeMatrixNV used in shader stage not in cooperativeMatrixSupportedStages (= %u)",
1377                             phys_dev_ext_props.cooperative_matrix_props.cooperativeMatrixSupportedStages);
1378                     }
1379                 }
1380                 break;
1381             case spv::OpMemoryModel:
1382                 // If the capability isn't enabled, don't bother with the rest of this function.
1383                 // OpMemoryModel is the first required instruction after all OpCapability instructions.
1384                 if (!seen_coopmat_capability) {
1385                     return skip;
1386                 }
1387                 break;
1388             case spv::OpTypeCooperativeMatrixNV: {
1389                 CoopMatType m;
1390                 m.Init(insn.word(1), src, pStage, id_to_spec_id);
1391 
1392                 if (m.all_constant) {
1393                     // Validate that the type parameters are all supported for one of the
1394                     // operands of a cooperative matrix property.
1395                     bool valid = false;
1396                     for (unsigned i = 0; i < cooperative_matrix_properties.size(); ++i) {
1397                         if (cooperative_matrix_properties[i].AType == m.component_type &&
1398                             cooperative_matrix_properties[i].MSize == m.rows && cooperative_matrix_properties[i].KSize == m.cols &&
1399                             cooperative_matrix_properties[i].scope == m.scope) {
1400                             valid = true;
1401                             break;
1402                         }
1403                         if (cooperative_matrix_properties[i].BType == m.component_type &&
1404                             cooperative_matrix_properties[i].KSize == m.rows && cooperative_matrix_properties[i].NSize == m.cols &&
1405                             cooperative_matrix_properties[i].scope == m.scope) {
1406                             valid = true;
1407                             break;
1408                         }
1409                         if (cooperative_matrix_properties[i].CType == m.component_type &&
1410                             cooperative_matrix_properties[i].MSize == m.rows && cooperative_matrix_properties[i].NSize == m.cols &&
1411                             cooperative_matrix_properties[i].scope == m.scope) {
1412                             valid = true;
1413                             break;
1414                         }
1415                         if (cooperative_matrix_properties[i].DType == m.component_type &&
1416                             cooperative_matrix_properties[i].MSize == m.rows && cooperative_matrix_properties[i].NSize == m.cols &&
1417                             cooperative_matrix_properties[i].scope == m.scope) {
1418                             valid = true;
1419                             break;
1420                         }
1421                     }
1422                     if (!valid) {
1423                         skip |= LogError(pipeline->pipeline(), kVUID_Core_Shader_CooperativeMatrixType,
1424                                          "OpTypeCooperativeMatrixNV (result id = %u) operands don't match a supported matrix type",
1425                                          insn.word(1));
1426                     }
1427                 }
1428                 break;
1429             }
1430             case spv::OpCooperativeMatrixMulAddNV: {
1431                 CoopMatType a, b, c, d;
1432                 if (id_to_type_id.find(insn.word(2)) == id_to_type_id.end() ||
1433                     id_to_type_id.find(insn.word(3)) == id_to_type_id.end() ||
1434                     id_to_type_id.find(insn.word(4)) == id_to_type_id.end() ||
1435                     id_to_type_id.find(insn.word(5)) == id_to_type_id.end()) {
1436                     // Couldn't find type of matrix
1437                     assert(false);
1438                     break;
1439                 }
1440                 d.Init(id_to_type_id[insn.word(2)], src, pStage, id_to_spec_id);
1441                 a.Init(id_to_type_id[insn.word(3)], src, pStage, id_to_spec_id);
1442                 b.Init(id_to_type_id[insn.word(4)], src, pStage, id_to_spec_id);
1443                 c.Init(id_to_type_id[insn.word(5)], src, pStage, id_to_spec_id);
1444 
1445                 if (a.all_constant && b.all_constant && c.all_constant && d.all_constant) {
1446                     // Validate that the type parameters are all supported for the same
1447                     // cooperative matrix property.
1448                     bool valid = false;
1449                     for (unsigned i = 0; i < cooperative_matrix_properties.size(); ++i) {
1450                         if (cooperative_matrix_properties[i].AType == a.component_type &&
1451                             cooperative_matrix_properties[i].MSize == a.rows && cooperative_matrix_properties[i].KSize == a.cols &&
1452                             cooperative_matrix_properties[i].scope == a.scope &&
1453 
1454                             cooperative_matrix_properties[i].BType == b.component_type &&
1455                             cooperative_matrix_properties[i].KSize == b.rows && cooperative_matrix_properties[i].NSize == b.cols &&
1456                             cooperative_matrix_properties[i].scope == b.scope &&
1457 
1458                             cooperative_matrix_properties[i].CType == c.component_type &&
1459                             cooperative_matrix_properties[i].MSize == c.rows && cooperative_matrix_properties[i].NSize == c.cols &&
1460                             cooperative_matrix_properties[i].scope == c.scope &&
1461 
1462                             cooperative_matrix_properties[i].DType == d.component_type &&
1463                             cooperative_matrix_properties[i].MSize == d.rows && cooperative_matrix_properties[i].NSize == d.cols &&
1464                             cooperative_matrix_properties[i].scope == d.scope) {
1465                             valid = true;
1466                             break;
1467                         }
1468                     }
1469                     if (!valid) {
1470                         skip |= LogError(pipeline->pipeline(), kVUID_Core_Shader_CooperativeMatrixMulAdd,
1471                                          "OpCooperativeMatrixMulAddNV (result id = %u) operands don't match a supported matrix "
1472                                          "VkCooperativeMatrixPropertiesNV",
1473                                          insn.word(2));
1474                     }
1475                 }
1476                 break;
1477             }
1478             default:
1479                 break;
1480         }
1481     }
1482 
1483     return skip;
1484 }
1485 
ValidateShaderResolveQCOM(SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const PIPELINE_STATE * pipeline) const1486 bool CoreChecks::ValidateShaderResolveQCOM(SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
1487                                            const PIPELINE_STATE *pipeline) const {
1488     bool skip = false;
1489 
1490     // If the pipeline's subpass description contains flag VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM,
1491     // then the fragment shader must not enable the SPIRV SampleRateShading capability.
1492     if (pStage->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
1493         for (auto insn : *src) {
1494             switch (insn.opcode()) {
1495                 case spv::OpCapability:
1496                     if (insn.word(1) == spv::CapabilitySampleRateShading) {
1497                         auto subpass_flags =
1498                             (pipeline->rp_state == nullptr)
1499                                 ? 0
1500                                 : pipeline->rp_state->createInfo.pSubpasses[pipeline->create_info.graphics.subpass].flags;
1501                         if ((subpass_flags & VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM) != 0) {
1502                             skip |=
1503                                 LogError(pipeline->pipeline(), "VUID-RuntimeSpirv-SampleRateShading-06378",
1504                                          "Invalid Pipeline CreateInfo State: fragment shader enables SampleRateShading capability "
1505                                          "and the subpass flags includes VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM.");
1506                         }
1507                     }
1508                     break;
1509                 default:
1510                     break;
1511             }
1512         }
1513     }
1514 
1515     return skip;
1516 }
1517 
ValidateShaderSubgroupSizeControl(VkPipelineShaderStageCreateInfo const * pStage) const1518 bool CoreChecks::ValidateShaderSubgroupSizeControl(VkPipelineShaderStageCreateInfo const *pStage) const {
1519     bool skip = false;
1520 
1521     if ((pStage->flags & VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) != 0 &&
1522         !enabled_features.subgroup_size_control_features.subgroupSizeControl) {
1523         skip |= LogError(
1524             device, "VUID-VkPipelineShaderStageCreateInfo-flags-02784",
1525             "VkPipelineShaderStageCreateInfo flags contain VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT, "
1526             "but the VkPhysicalDeviceSubgroupSizeControlFeaturesEXT::subgroupSizeControl feature is not enabled.");
1527     }
1528 
1529     if ((pStage->flags & VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT) != 0 &&
1530         !enabled_features.subgroup_size_control_features.computeFullSubgroups) {
1531         skip |= LogError(
1532             device, "VUID-VkPipelineShaderStageCreateInfo-flags-02785",
1533             "VkPipelineShaderStageCreateInfo flags contain VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT, but the "
1534             "VkPhysicalDeviceSubgroupSizeControlFeaturesEXT::computeFullSubgroups feature is not enabled");
1535     }
1536 
1537     return skip;
1538 }
1539 
ValidateAtomicsTypes(SHADER_MODULE_STATE const * src) const1540 bool CoreChecks::ValidateAtomicsTypes(SHADER_MODULE_STATE const *src) const {
1541     bool skip = false;
1542 
1543     // "If sparseImageInt64Atomics is enabled, shaderImageInt64Atomics must be enabled"
1544     const bool valid_image_64_int = enabled_features.shader_image_atomic_int64_features.shaderImageInt64Atomics == VK_TRUE;
1545 
1546     const VkPhysicalDeviceShaderAtomicFloatFeaturesEXT &float_features = enabled_features.shader_atomic_float_features;
1547     const VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT &float2_features = enabled_features.shader_atomic_float2_features;
1548 
1549     const bool valid_storage_buffer_float = (
1550         (float_features.shaderBufferFloat32Atomics == VK_TRUE) ||
1551         (float_features.shaderBufferFloat32AtomicAdd == VK_TRUE) ||
1552         (float_features.shaderBufferFloat64Atomics == VK_TRUE) ||
1553         (float_features.shaderBufferFloat64AtomicAdd == VK_TRUE) ||
1554         (float2_features.shaderBufferFloat16Atomics == VK_TRUE) ||
1555         (float2_features.shaderBufferFloat16AtomicAdd == VK_TRUE) ||
1556         (float2_features.shaderBufferFloat16AtomicMinMax == VK_TRUE) ||
1557         (float2_features.shaderBufferFloat32AtomicMinMax == VK_TRUE) ||
1558         (float2_features.shaderBufferFloat64AtomicMinMax == VK_TRUE));
1559 
1560     const bool valid_workgroup_float = (
1561         (float_features.shaderSharedFloat32Atomics == VK_TRUE) ||
1562         (float_features.shaderSharedFloat32AtomicAdd == VK_TRUE) ||
1563         (float_features.shaderSharedFloat64Atomics == VK_TRUE) ||
1564         (float_features.shaderSharedFloat64AtomicAdd == VK_TRUE) ||
1565         (float2_features.shaderSharedFloat16Atomics == VK_TRUE) ||
1566         (float2_features.shaderSharedFloat16AtomicAdd == VK_TRUE) ||
1567         (float2_features.shaderSharedFloat16AtomicMinMax == VK_TRUE) ||
1568         (float2_features.shaderSharedFloat32AtomicMinMax == VK_TRUE) ||
1569         (float2_features.shaderSharedFloat64AtomicMinMax == VK_TRUE));
1570 
1571     const bool valid_image_float = (
1572         (float_features.shaderImageFloat32Atomics == VK_TRUE) ||
1573         (float_features.shaderImageFloat32AtomicAdd == VK_TRUE) ||
1574         (float2_features.shaderImageFloat32AtomicMinMax == VK_TRUE));
1575 
1576     const bool valid_16_float = (
1577         (float2_features.shaderBufferFloat16Atomics == VK_TRUE) ||
1578         (float2_features.shaderBufferFloat16AtomicAdd == VK_TRUE) ||
1579         (float2_features.shaderBufferFloat16AtomicMinMax == VK_TRUE) ||
1580         (float2_features.shaderSharedFloat16Atomics == VK_TRUE) ||
1581         (float2_features.shaderSharedFloat16AtomicAdd == VK_TRUE) ||
1582         (float2_features.shaderSharedFloat16AtomicMinMax == VK_TRUE));
1583 
1584     const bool valid_32_float = (
1585         (float_features.shaderBufferFloat32Atomics == VK_TRUE) ||
1586         (float_features.shaderBufferFloat32AtomicAdd == VK_TRUE) ||
1587         (float_features.shaderSharedFloat32Atomics == VK_TRUE) ||
1588         (float_features.shaderSharedFloat32AtomicAdd == VK_TRUE) ||
1589         (float_features.shaderImageFloat32Atomics == VK_TRUE) ||
1590         (float_features.shaderImageFloat32AtomicAdd == VK_TRUE) ||
1591         (float2_features.shaderBufferFloat32AtomicMinMax == VK_TRUE) ||
1592         (float2_features.shaderSharedFloat32AtomicMinMax == VK_TRUE) ||
1593         (float2_features.shaderImageFloat32AtomicMinMax == VK_TRUE));
1594 
1595     const bool valid_64_float = (
1596         (float_features.shaderBufferFloat64Atomics == VK_TRUE) ||
1597         (float_features.shaderBufferFloat64AtomicAdd == VK_TRUE) ||
1598         (float_features.shaderSharedFloat64Atomics == VK_TRUE) ||
1599         (float_features.shaderSharedFloat64AtomicAdd == VK_TRUE) ||
1600         (float2_features.shaderBufferFloat64AtomicMinMax == VK_TRUE) ||
1601         (float2_features.shaderSharedFloat64AtomicMinMax == VK_TRUE));
1602     // clang-format on
1603 
1604     for (const auto &atomic_inst : src->GetAtomicInstructions()) {
1605         const atomic_instruction &atomic = atomic_inst.second;
1606         const uint32_t opcode = src->at(atomic_inst.first).opcode();
1607 
1608         if ((atomic.bit_width == 64) && (atomic.type == spv::OpTypeInt)) {
1609             // Validate 64-bit image atomics
1610             if (((atomic.storage_class == spv::StorageClassStorageBuffer) || (atomic.storage_class == spv::StorageClassUniform)) &&
1611                 (enabled_features.core12.shaderBufferInt64Atomics == VK_FALSE)) {
1612                 skip |= LogError(device, "VUID-RuntimeSpirv-None-06278",
1613                                  "%s: Can't use 64-bit int atomics operations (%s) with %s storage class without "
1614                                  "shaderBufferInt64Atomics enabled.",
1615                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode),
1616                                  StorageClassName(atomic.storage_class));
1617             } else if ((atomic.storage_class == spv::StorageClassWorkgroup) &&
1618                        (enabled_features.core12.shaderSharedInt64Atomics == VK_FALSE)) {
1619                 skip |= LogError(device, "VUID-RuntimeSpirv-None-06279",
1620                                  "%s: Can't use 64-bit int atomics operations (%s) with Workgroup storage class without "
1621                                  "shaderSharedInt64Atomics enabled.",
1622                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1623             } else if ((atomic.storage_class == spv::StorageClassImage) && (valid_image_64_int == false)) {
1624                 skip |= LogError(device, "VUID-RuntimeSpirv-None-06288",
1625                                  "%s: Can't use 64-bit int atomics operations (%s) with Image storage class without "
1626                                  "shaderImageInt64Atomics enabled.",
1627                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1628             }
1629         } else if (atomic.type == spv::OpTypeFloat) {
1630             // Validate Floats
1631             if (atomic.storage_class == spv::StorageClassStorageBuffer) {
1632                 if (valid_storage_buffer_float == false) {
1633                     const char *vuid = IsExtEnabled(device_extensions.vk_ext_shader_atomic_float2) ? "VUID-RuntimeSpirv-None-06284"
1634                                                                                                    : "VUID-RuntimeSpirv-None-06280";
1635                     skip |= LogError(device, vuid,
1636                                      "%s: Can't use float atomics operations (%s) with StorageBuffer storage class without "
1637                                      "shaderBufferFloat32Atomics or shaderBufferFloat32AtomicAdd or shaderBufferFloat64Atomics or "
1638                                      "shaderBufferFloat64AtomicAdd or shaderBufferFloat16Atomics or shaderBufferFloat16AtomicAdd "
1639                                      "or shaderBufferFloat16AtomicMinMax or shaderBufferFloat32AtomicMinMax or "
1640                                      "shaderBufferFloat64AtomicMinMax enabled.",
1641                                      report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1642                 } else if (opcode == spv::OpAtomicFAddEXT) {
1643                     if ((atomic.bit_width == 16) && (float2_features.shaderBufferFloat16AtomicAdd == VK_FALSE)) {
1644                         skip |= LogError(device, kVUID_Core_Shader_AtomicFeature,
1645                                          "%s: Can't use 16-bit float atomics for add operations (OpAtomicFAddEXT) with "
1646                                          "StorageBuffer storage class without shaderBufferFloat16AtomicAdd enabled.",
1647                                          report_data->FormatHandle(src->vk_shader_module()).c_str());
1648                     } else if ((atomic.bit_width == 32) && (float_features.shaderBufferFloat32AtomicAdd == VK_FALSE)) {
1649                         skip |= LogError(device, kVUID_Core_Shader_AtomicFeature,
1650                                          "%s: Can't use 32-bit float atomics for add operations (OpAtomicFAddEXT) with "
1651                                          "StorageBuffer storage class without shaderBufferFloat32AtomicAdd enabled.",
1652                                          report_data->FormatHandle(src->vk_shader_module()).c_str());
1653                     } else if ((atomic.bit_width == 64) && (float_features.shaderBufferFloat64AtomicAdd == VK_FALSE)) {
1654                         skip |= LogError(device, kVUID_Core_Shader_AtomicFeature,
1655                                          "%s: Can't use 64-bit float atomics for add operations (OpAtomicFAddEXT) with "
1656                                          "StorageBuffer storage class without shaderBufferFloat64AtomicAdd enabled.",
1657                                          report_data->FormatHandle(src->vk_shader_module()).c_str());
1658                     }
1659                 } else if (opcode == spv::OpAtomicFMinEXT || opcode == spv::OpAtomicFMaxEXT) {
1660                     if ((atomic.bit_width == 16) && (float2_features.shaderBufferFloat16AtomicMinMax == VK_FALSE)) {
1661                         skip |= LogError(
1662                             device, kVUID_Core_Shader_AtomicFeature,
1663                             "%s: Can't use 16-bit float atomics for min/max operations (OpAtomicFMinEXT or OpAtomicFMaxEXT) with "
1664                             "StorageBuffer storage class without shaderBufferFloat16AtomicMinMax enabled.",
1665                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1666                     } else if ((atomic.bit_width == 32) && (float2_features.shaderBufferFloat32AtomicMinMax == VK_FALSE)) {
1667                         skip |= LogError(
1668                             device, kVUID_Core_Shader_AtomicFeature,
1669                             "%s: Can't use 32-bit float atomics for min/max operations (OpAtomicFMinEXT or OpAtomicFMaxEXT) with "
1670                             "StorageBuffer storage class without shaderBufferFloat32AtomicMinMax enabled.",
1671                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1672                     } else if ((atomic.bit_width == 64) && (float2_features.shaderBufferFloat64AtomicMinMax == VK_FALSE)) {
1673                         skip |= LogError(
1674                             device, kVUID_Core_Shader_AtomicFeature,
1675                             "%s: Can't use 64-bit float atomics for min/max operations (OpAtomicFMinEXT or OpAtomicFMaxEXT) with "
1676                             "StorageBuffer storage class without shaderBufferFloat64AtomicMinMax enabled.",
1677                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1678                     }
1679                 } else {
1680                     // Assume is valid load/store/exchange (rest of supported atomic operations) or else spirv-val will catch
1681                     if ((atomic.bit_width == 16) && (float2_features.shaderBufferFloat16Atomics == VK_FALSE)) {
1682                         skip |= LogError(
1683                             device, kVUID_Core_Shader_AtomicFeature,
1684                             "%s: Can't use 16-bit float atomics for load/store/exhange operations (OpAtomicLoad, OpAtomicStore, "
1685                             "OpAtomicExchange) with StorageBuffer storage class without shaderBufferFloat16Atomics enabled.",
1686                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1687                     } else if ((atomic.bit_width == 32) && (float_features.shaderBufferFloat32Atomics == VK_FALSE)) {
1688                         skip |= LogError(
1689                             device, kVUID_Core_Shader_AtomicFeature,
1690                             "%s: Can't use 32-bit float atomics for load/store/exhange operations (OpAtomicLoad, OpAtomicStore, "
1691                             "OpAtomicExchange) with StorageBuffer storage class without shaderBufferFloat32Atomics enabled.",
1692                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1693                     } else if ((atomic.bit_width == 64) && (float_features.shaderBufferFloat64Atomics == VK_FALSE)) {
1694                         skip |= LogError(
1695                             device, kVUID_Core_Shader_AtomicFeature,
1696                             "%s: Can't use 64-bit float atomics for load/store/exhange operations (OpAtomicLoad, OpAtomicStore, "
1697                             "OpAtomicExchange) with StorageBuffer storage class without shaderBufferFloat64Atomics enabled.",
1698                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1699                     }
1700                 }
1701             } else if (atomic.storage_class == spv::StorageClassWorkgroup) {
1702                 if (valid_workgroup_float == false) {
1703                     const char *vuid = IsExtEnabled(device_extensions.vk_ext_shader_atomic_float2) ? "VUID-RuntimeSpirv-None-06285"
1704                                                                                                    : "VUID-RuntimeSpirv-None-06281";
1705                     skip |=
1706                         LogError(device, vuid,
1707                                  "%s: Can't use float atomics operations (%s) with Workgroup storage class without "
1708                                  "shaderSharedFloat32Atomics or "
1709                                  "shaderSharedFloat32AtomicAdd or shaderSharedFloat64Atomics or shaderSharedFloat64AtomicAdd or "
1710                                  "shaderSharedFloat16Atomics or shaderSharedFloat16AtomicAdd or shaderSharedFloat16AtomicMinMax or "
1711                                  "shaderSharedFloat32AtomicMinMax or shaderSharedFloat64AtomicMinMax enabled.",
1712                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1713                 } else if (opcode == spv::OpAtomicFAddEXT) {
1714                     if ((atomic.bit_width == 16) && (float2_features.shaderSharedFloat16AtomicAdd == VK_FALSE)) {
1715                         skip |= LogError(device, kVUID_Core_Shader_AtomicFeature,
1716                                          "%s: Can't use 16-bit float atomics for add operations (OpAtomicFAddEXT) with Workgroup "
1717                                          "storage class without shaderSharedFloat16AtomicAdd enabled.",
1718                                          report_data->FormatHandle(src->vk_shader_module()).c_str());
1719                     } else if ((atomic.bit_width == 32) && (float_features.shaderSharedFloat32AtomicAdd == VK_FALSE)) {
1720                         skip |= LogError(device, kVUID_Core_Shader_AtomicFeature,
1721                                          "%s: Can't use 32-bit float atomics for add operations (OpAtomicFAddEXT) with Workgroup "
1722                                          "storage class without shaderSharedFloat32AtomicAdd enabled.",
1723                                          report_data->FormatHandle(src->vk_shader_module()).c_str());
1724                     } else if ((atomic.bit_width == 64) && (float_features.shaderSharedFloat64AtomicAdd == VK_FALSE)) {
1725                         skip |= LogError(device, kVUID_Core_Shader_AtomicFeature,
1726                                          "%s: Can't use 64-bit float atomics for add operations (OpAtomicFAddEXT) with Workgroup "
1727                                          "storage class without shaderSharedFloat64AtomicAdd enabled.",
1728                                          report_data->FormatHandle(src->vk_shader_module()).c_str());
1729                     }
1730                 } else if (opcode == spv::OpAtomicFMinEXT || opcode == spv::OpAtomicFMaxEXT) {
1731                     if ((atomic.bit_width == 16) && (float2_features.shaderSharedFloat16AtomicMinMax == VK_FALSE)) {
1732                         skip |= LogError(
1733                             device, kVUID_Core_Shader_AtomicFeature,
1734                             "%s: Can't use 16-bit float atomics for min/max operations (OpAtomicFMinEXT or OpAtomicFMaxEXT) with "
1735                             "Workgroup storage class without shaderSharedFloat16AtomicMinMax enabled.",
1736                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1737                     } else if ((atomic.bit_width == 32) && (float2_features.shaderSharedFloat32AtomicMinMax == VK_FALSE)) {
1738                         skip |= LogError(
1739                             device, kVUID_Core_Shader_AtomicFeature,
1740                             "%s: Can't use 32-bit float atomics for min/max operations (OpAtomicFMinEXT or OpAtomicFMaxEXT) with "
1741                             "Workgroup storage class without shaderSharedFloat32AtomicMinMax enabled.",
1742                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1743                     } else if ((atomic.bit_width == 64) && (float2_features.shaderSharedFloat64AtomicMinMax == VK_FALSE)) {
1744                         skip |= LogError(
1745                             device, kVUID_Core_Shader_AtomicFeature,
1746                             "%s: Can't use 64-bit float atomics for min/max operations (OpAtomicFMinEXT or OpAtomicFMaxEXT) with "
1747                             "Workgroup storage class without shaderSharedFloat64AtomicMinMax enabled.",
1748                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1749                     }
1750                 } else {
1751                     // Assume is valid load/store/exchange (rest of supported atomic operations) or else spirv-val will catch
1752                     if ((atomic.bit_width == 16) && (float2_features.shaderSharedFloat16Atomics == VK_FALSE)) {
1753                         skip |= LogError(
1754                             device, kVUID_Core_Shader_AtomicFeature,
1755                             "%s: Can't use 16-bit float atomics for load/store/exhange operations (OpAtomicLoad, OpAtomicStore, "
1756                             "OpAtomicExchange) with Workgroup storage class without shaderSharedFloat16Atomics enabled.",
1757                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1758                     } else if ((atomic.bit_width == 32) && (float_features.shaderSharedFloat32Atomics == VK_FALSE)) {
1759                         skip |= LogError(
1760                             device, kVUID_Core_Shader_AtomicFeature,
1761                             "%s: Can't use 32-bit float atomics for load/store/exhange operations (OpAtomicLoad, OpAtomicStore, "
1762                             "OpAtomicExchange) with Workgroup storage class without shaderSharedFloat32Atomics enabled.",
1763                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1764                     } else if ((atomic.bit_width == 64) && (float_features.shaderSharedFloat64Atomics == VK_FALSE)) {
1765                         skip |= LogError(
1766                             device, kVUID_Core_Shader_AtomicFeature,
1767                             "%s: Can't use 64-bit float atomics for load/store/exhange operations (OpAtomicLoad, OpAtomicStore, "
1768                             "OpAtomicExchange) with Workgroup storage class without shaderSharedFloat64Atomics enabled.",
1769                             report_data->FormatHandle(src->vk_shader_module()).c_str());
1770                     }
1771                 }
1772             } else if ((atomic.storage_class == spv::StorageClassImage) && (valid_image_float == false)) {
1773                 const char *vuid = IsExtEnabled(device_extensions.vk_ext_shader_atomic_float2) ? "VUID-RuntimeSpirv-None-06286"
1774                                                                                                : "VUID-RuntimeSpirv-None-06282";
1775                 skip |= LogError(
1776                     device, vuid,
1777                     "%s: Can't use float atomics operations (%s) with Image storage class without shaderImageFloat32Atomics or "
1778                     "shaderImageFloat32AtomicAdd or shaderImageFloat32AtomicMinMax enabled.",
1779                     report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1780             } else if ((atomic.bit_width == 16) && (valid_16_float == false)) {
1781                 skip |= LogError(device, "VUID-RuntimeSpirv-None-06337",
1782                                  "%s: Can't use 16-bit float atomics operations (%s) without shaderBufferFloat16Atomics, "
1783                                  "shaderBufferFloat16AtomicAdd, shaderBufferFloat16AtomicMinMax, shaderSharedFloat16Atomics, "
1784                                  "shaderSharedFloat16AtomicAdd or shaderSharedFloat16AtomicMinMax enabled.",
1785                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1786             } else if ((atomic.bit_width == 32) && (valid_32_float == false)) {
1787                 const char *vuid = IsExtEnabled(device_extensions.vk_ext_shader_atomic_float2) ? "VUID-RuntimeSpirv-None-06338"
1788                                                                                                : "VUID-RuntimeSpirv-None-06335";
1789                 skip |= LogError(device, vuid,
1790                                  "%s: Can't use 32-bit float atomics operations (%s) without shaderBufferFloat32AtomicMinMax, "
1791                                  "shaderSharedFloat32AtomicMinMax, shaderImageFloat32AtomicMinMax, sparseImageFloat32AtomicMinMax, "
1792                                  "shaderBufferFloat32Atomics, shaderBufferFloat32AtomicAdd, shaderSharedFloat32Atomics, "
1793                                  "shaderSharedFloat32AtomicAdd, shaderImageFloat32Atomics, shaderImageFloat32AtomicAdd, "
1794                                  "sparseImageFloat32Atomics or sparseImageFloat32AtomicAdd enabled.",
1795                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1796             } else if ((atomic.bit_width == 64) && (valid_64_float == false)) {
1797                 const char *vuid = IsExtEnabled(device_extensions.vk_ext_shader_atomic_float2) ? "VUID-RuntimeSpirv-None-06339"
1798                                                                                                : "VUID-RuntimeSpirv-None-06336";
1799                 skip |= LogError(device, vuid,
1800                                  "%s: Can't use 64-bit float atomics operations (%s) without shaderBufferFloat64AtomicMinMax, "
1801                                  "shaderSharedFloat64AtomicMinMax, shaderBufferFloat64Atomics, shaderBufferFloat64AtomicAdd, "
1802                                  "shaderSharedFloat64Atomics or shaderSharedFloat64AtomicAdd enabled.",
1803                                  report_data->FormatHandle(src->vk_shader_module()).c_str(), string_SpvOpcode(opcode));
1804             }
1805         }
1806     }
1807     return skip;
1808 }
1809 
ValidateExecutionModes(SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint,VkShaderStageFlagBits stage,const PIPELINE_STATE * pipeline) const1810 bool CoreChecks::ValidateExecutionModes(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint, VkShaderStageFlagBits stage,
1811                                         const PIPELINE_STATE *pipeline) const {
1812     auto entrypoint_id = entrypoint.word(2);
1813 
1814     // The first denorm execution mode encountered, along with its bit width.
1815     // Used to check if SeparateDenormSettings is respected.
1816     std::pair<spv::ExecutionMode, uint32_t> first_denorm_execution_mode = std::make_pair(spv::ExecutionModeMax, 0);
1817 
1818     // The first rounding mode encountered, along with its bit width.
1819     // Used to check if SeparateRoundingModeSettings is respected.
1820     std::pair<spv::ExecutionMode, uint32_t> first_rounding_mode = std::make_pair(spv::ExecutionModeMax, 0);
1821 
1822     bool skip = false;
1823 
1824     uint32_t vertices_out = 0;
1825     uint32_t invocations = 0;
1826 
1827     const auto &execution_mode_inst = src->GetExecutionModeInstructions();
1828     auto it = execution_mode_inst.find(entrypoint_id);
1829     if (it != execution_mode_inst.end()) {
1830         for (auto insn : it->second) {
1831             auto mode = insn.word(2);
1832             switch (mode) {
1833                 case spv::ExecutionModeSignedZeroInfNanPreserve: {
1834                     auto bit_width = insn.word(3);
1835                     if (bit_width == 16 && !phys_dev_props_core12.shaderSignedZeroInfNanPreserveFloat16) {
1836                         skip |= LogError(
1837                             device, "VUID-RuntimeSpirv-shaderSignedZeroInfNanPreserveFloat16-06293",
1838                             "Shader requires SignedZeroInfNanPreserve for bit width 16 but it is not enabled on the device");
1839                     } else if (bit_width == 32 && !phys_dev_props_core12.shaderSignedZeroInfNanPreserveFloat32) {
1840                         skip |= LogError(
1841                             device, "VUID-RuntimeSpirv-shaderSignedZeroInfNanPreserveFloat32-06294",
1842                             "Shader requires SignedZeroInfNanPreserve for bit width 32 but it is not enabled on the device");
1843                     } else if (bit_width == 64 && !phys_dev_props_core12.shaderSignedZeroInfNanPreserveFloat64) {
1844                         skip |= LogError(
1845                             device, "VUID-RuntimeSpirv-shaderSignedZeroInfNanPreserveFloat64-06295",
1846                             "Shader requires SignedZeroInfNanPreserve for bit width 64 but it is not enabled on the device");
1847                     }
1848                     break;
1849                 }
1850 
1851                 case spv::ExecutionModeDenormPreserve: {
1852                     auto bit_width = insn.word(3);
1853                     if (bit_width == 16 && !phys_dev_props_core12.shaderDenormPreserveFloat16) {
1854                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderDenormPreserveFloat16-06296",
1855                                          "Shader requires DenormPreserve for bit width 16 but it is not enabled on the device");
1856                     } else if (bit_width == 32 && !phys_dev_props_core12.shaderDenormPreserveFloat32) {
1857                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderDenormPreserveFloat32-06297",
1858                                          "Shader requires DenormPreserve for bit width 32 but it is not enabled on the device");
1859                     } else if (bit_width == 64 && !phys_dev_props_core12.shaderDenormPreserveFloat64) {
1860                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderDenormPreserveFloat64-06298",
1861                                          "Shader requires DenormPreserve for bit width 64 but it is not enabled on the device");
1862                     }
1863 
1864                     if (first_denorm_execution_mode.first == spv::ExecutionModeMax) {
1865                         // Register the first denorm execution mode found
1866                         first_denorm_execution_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
1867                     } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width) {
1868                         switch (phys_dev_props_core12.denormBehaviorIndependence) {
1869                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY:
1870                                 if (first_rounding_mode.second != 32 && bit_width != 32) {
1871                                     skip |= LogError(device, "VUID-RuntimeSpirv-denormBehaviorIndependence-06289",
1872                                                      "Shader uses different denorm execution modes for 16 and 64-bit but "
1873                                                      "denormBehaviorIndependence is "
1874                                                      "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY on the device");
1875                                 }
1876                                 break;
1877 
1878                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL:
1879                                 break;
1880 
1881                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE:
1882                                 skip |= LogError(device, "VUID-RuntimeSpirv-denormBehaviorIndependence-06290",
1883                                                  "Shader uses different denorm execution modes for different bit widths but "
1884                                                  "denormBehaviorIndependence is "
1885                                                  "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE on the device");
1886                                 break;
1887 
1888                             default:
1889                                 break;
1890                         }
1891                     }
1892                     break;
1893                 }
1894 
1895                 case spv::ExecutionModeDenormFlushToZero: {
1896                     auto bit_width = insn.word(3);
1897                     if (bit_width == 16 && !phys_dev_props_core12.shaderDenormFlushToZeroFloat16) {
1898                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderDenormFlushToZeroFloat16-06299",
1899                                          "Shader requires DenormFlushToZero for bit width 16 but it is not enabled on the device");
1900                     } else if (bit_width == 32 && !phys_dev_props_core12.shaderDenormFlushToZeroFloat32) {
1901                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderDenormFlushToZeroFloat32-06300",
1902                                          "Shader requires DenormFlushToZero for bit width 32 but it is not enabled on the device");
1903                     } else if (bit_width == 64 && !phys_dev_props_core12.shaderDenormFlushToZeroFloat64) {
1904                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderDenormFlushToZeroFloat64-06301",
1905                                          "Shader requires DenormFlushToZero for bit width 64 but it is not enabled on the device");
1906                     }
1907 
1908                     if (first_denorm_execution_mode.first == spv::ExecutionModeMax) {
1909                         // Register the first denorm execution mode found
1910                         first_denorm_execution_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
1911                     } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width) {
1912                         switch (phys_dev_props_core12.denormBehaviorIndependence) {
1913                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY:
1914                                 if (first_rounding_mode.second != 32 && bit_width != 32) {
1915                                     skip |= LogError(device, "VUID-RuntimeSpirv-denormBehaviorIndependence-06289",
1916                                                      "Shader uses different denorm execution modes for 16 and 64-bit but "
1917                                                      "denormBehaviorIndependence is "
1918                                                      "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY on the device");
1919                                 }
1920                                 break;
1921 
1922                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL:
1923                                 break;
1924 
1925                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE:
1926                                 skip |= LogError(device, "VUID-RuntimeSpirv-denormBehaviorIndependence-06290",
1927                                                  "Shader uses different denorm execution modes for different bit widths but "
1928                                                  "denormBehaviorIndependence is "
1929                                                  "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE on the device");
1930                                 break;
1931 
1932                             default:
1933                                 break;
1934                         }
1935                     }
1936                     break;
1937                 }
1938 
1939                 case spv::ExecutionModeRoundingModeRTE: {
1940                     auto bit_width = insn.word(3);
1941                     if (bit_width == 16 && !phys_dev_props_core12.shaderRoundingModeRTEFloat16) {
1942                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderRoundingModeRTEFloat16-06302",
1943                                          "Shader requires RoundingModeRTE for bit width 16 but it is not enabled on the device");
1944                     } else if (bit_width == 32 && !phys_dev_props_core12.shaderRoundingModeRTEFloat32) {
1945                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderRoundingModeRTEFloat32-06303",
1946                                          "Shader requires RoundingModeRTE for bit width 32 but it is not enabled on the device");
1947                     } else if (bit_width == 64 && !phys_dev_props_core12.shaderRoundingModeRTEFloat64) {
1948                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderRoundingModeRTEFloat64-06304",
1949                                          "Shader requires RoundingModeRTE for bit width 64 but it is not enabled on the device");
1950                     }
1951 
1952                     if (first_rounding_mode.first == spv::ExecutionModeMax) {
1953                         // Register the first rounding mode found
1954                         first_rounding_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
1955                     } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width) {
1956                         switch (phys_dev_props_core12.roundingModeIndependence) {
1957                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY:
1958                                 if (first_rounding_mode.second != 32 && bit_width != 32) {
1959                                     skip |= LogError(device, "VUID-RuntimeSpirv-roundingModeIndependence-06291",
1960                                                      "Shader uses different rounding modes for 16 and 64-bit but "
1961                                                      "roundingModeIndependence is "
1962                                                      "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY on the device");
1963                                 }
1964                                 break;
1965 
1966                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL:
1967                                 break;
1968 
1969                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE:
1970                                 skip |= LogError(device, "VUID-RuntimeSpirv-roundingModeIndependence-06292",
1971                                                  "Shader uses different rounding modes for different bit widths but "
1972                                                  "roundingModeIndependence is "
1973                                                  "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE on the device");
1974                                 break;
1975 
1976                             default:
1977                                 break;
1978                         }
1979                     }
1980                     break;
1981                 }
1982 
1983                 case spv::ExecutionModeRoundingModeRTZ: {
1984                     auto bit_width = insn.word(3);
1985                     if (bit_width == 16 && !phys_dev_props_core12.shaderRoundingModeRTZFloat16) {
1986                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderRoundingModeRTZFloat16-06305",
1987                                          "Shader requires RoundingModeRTZ for bit width 16 but it is not enabled on the device");
1988                     } else if (bit_width == 32 && !phys_dev_props_core12.shaderRoundingModeRTZFloat32) {
1989                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderRoundingModeRTZFloat32-06306",
1990                                          "Shader requires RoundingModeRTZ for bit width 32 but it is not enabled on the device");
1991                     } else if (bit_width == 64 && !phys_dev_props_core12.shaderRoundingModeRTZFloat64) {
1992                         skip |= LogError(device, "VUID-RuntimeSpirv-shaderRoundingModeRTZFloat64-06307",
1993                                          "Shader requires RoundingModeRTZ for bit width 64 but it is not enabled on the device");
1994                     }
1995 
1996                     if (first_rounding_mode.first == spv::ExecutionModeMax) {
1997                         // Register the first rounding mode found
1998                         first_rounding_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
1999                     } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width) {
2000                         switch (phys_dev_props_core12.roundingModeIndependence) {
2001                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY:
2002                                 if (first_rounding_mode.second != 32 && bit_width != 32) {
2003                                     skip |= LogError(device, "VUID-RuntimeSpirv-roundingModeIndependence-06291",
2004                                                      "Shader uses different rounding modes for 16 and 64-bit but "
2005                                                      "roundingModeIndependence is "
2006                                                      "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY on the device");
2007                                 }
2008                                 break;
2009 
2010                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL:
2011                                 break;
2012 
2013                             case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE:
2014                                 skip |= LogError(device, "VUID-RuntimeSpirv-roundingModeIndependence-06292",
2015                                                  "Shader uses different rounding modes for different bit widths but "
2016                                                  "roundingModeIndependence is "
2017                                                  "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE on the device");
2018                                 break;
2019 
2020                             default:
2021                                 break;
2022                         }
2023                     }
2024                     break;
2025                 }
2026 
2027                 case spv::ExecutionModeOutputVertices: {
2028                     vertices_out = insn.word(3);
2029                     break;
2030                 }
2031 
2032                 case spv::ExecutionModeInvocations: {
2033                     invocations = insn.word(3);
2034                     break;
2035                 }
2036 
2037                 case spv::ExecutionModeLocalSizeId: {
2038                     if (!enabled_features.maintenance4_features.maintenance4) {
2039                         skip |= LogError(device, "VUID-RuntimeSpirv-LocalSizeId-06434",
2040                                          "LocalSizeId execution mode used but maintenance4 feature not enabled");
2041                     }
2042                     break;
2043                 }
2044 
2045                 case spv::ExecutionModeEarlyFragmentTests: {
2046                     if ((stage == VK_SHADER_STAGE_FRAGMENT_BIT) &&
2047                         (pipeline && pipeline->create_info.graphics.pDepthStencilState &&
2048                          (pipeline->create_info.graphics.pDepthStencilState->flags &
2049                           (VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM |
2050                            VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM)) != 0)) {
2051                         skip |= LogError(
2052                             device, " VUID-VkGraphicsPipelineCreateInfo-pStages-06466",
2053                             "The fragment shader enables early fragment tests, but VkPipelineDepthStencilStateCreateInfo::flags == "
2054                             "%s",
2055                             string_VkPipelineDepthStencilStateCreateFlags(pipeline->create_info.graphics.pDepthStencilState->flags)
2056                                 .c_str());
2057                     }
2058                     break;
2059                 }
2060             }
2061         }
2062     }
2063 
2064     if (entrypoint.word(1) == spv::ExecutionModelGeometry) {
2065         if (vertices_out == 0 || vertices_out > phys_dev_props.limits.maxGeometryOutputVertices) {
2066             skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-stage-00714",
2067                              "Geometry shader entry point must have an OpExecutionMode instruction that "
2068                              "specifies a maximum output vertex count that is greater than 0 and less "
2069                              "than or equal to maxGeometryOutputVertices. "
2070                              "OutputVertices=%d, maxGeometryOutputVertices=%d",
2071                              vertices_out, phys_dev_props.limits.maxGeometryOutputVertices);
2072         }
2073 
2074         if (invocations == 0 || invocations > phys_dev_props.limits.maxGeometryShaderInvocations) {
2075             skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-stage-00715",
2076                              "Geometry shader entry point must have an OpExecutionMode instruction that "
2077                              "specifies an invocation count that is greater than 0 and less "
2078                              "than or equal to maxGeometryShaderInvocations. "
2079                              "Invocations=%d, maxGeometryShaderInvocations=%d",
2080                              invocations, phys_dev_props.limits.maxGeometryShaderInvocations);
2081         }
2082     }
2083     return skip;
2084 }
2085 
2086 // For given pipelineLayout verify that the set_layout_node at slot.first
2087 //  has the requested binding at slot.second and return ptr to that binding
GetDescriptorBinding(PIPELINE_LAYOUT_STATE const * pipelineLayout,DescriptorSlot slot)2088 static VkDescriptorSetLayoutBinding const *GetDescriptorBinding(PIPELINE_LAYOUT_STATE const *pipelineLayout,
2089                                                                 DescriptorSlot slot) {
2090     if (!pipelineLayout) return nullptr;
2091 
2092     if (slot.set >= pipelineLayout->set_layouts.size()) return nullptr;
2093 
2094     return pipelineLayout->set_layouts[slot.set]->GetDescriptorSetLayoutBindingPtrFromBinding(slot.binding);
2095 }
2096 
2097 // If PointList topology is specified in the pipeline, verify that a shader geometry stage writes PointSize
2098 //    o If there is only a vertex shader : gl_PointSize must be written when using points
2099 //    o If there is a geometry or tessellation shader:
2100 //        - If shaderTessellationAndGeometryPointSize feature is enabled:
2101 //            * gl_PointSize must be written in the final geometry stage
2102 //        - If shaderTessellationAndGeometryPointSize feature is disabled:
2103 //            * gl_PointSize must NOT be written and a default of 1.0 is assumed
ValidatePointListShaderState(const PIPELINE_STATE * pipeline,SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint,VkShaderStageFlagBits stage) const2104 bool CoreChecks::ValidatePointListShaderState(const PIPELINE_STATE *pipeline, SHADER_MODULE_STATE const *src,
2105                                               spirv_inst_iter entrypoint, VkShaderStageFlagBits stage) const {
2106     if (pipeline->topology_at_rasterizer != VK_PRIMITIVE_TOPOLOGY_POINT_LIST) {
2107         return false;
2108     }
2109 
2110     bool pointsize_written = false;
2111     bool skip = false;
2112 
2113     // Search for PointSize built-in decorations
2114     for (const auto &set : src->GetBuiltinDecorationList()) {
2115         auto insn = src->at(set.offset);
2116         if (set.builtin == spv::BuiltInPointSize) {
2117             pointsize_written = src->IsBuiltInWritten(insn, entrypoint);
2118             if (pointsize_written) {
2119                 break;
2120             }
2121         }
2122     }
2123 
2124     if ((stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT || stage == VK_SHADER_STAGE_GEOMETRY_BIT) &&
2125         !enabled_features.core.shaderTessellationAndGeometryPointSize) {
2126         if (pointsize_written) {
2127             skip |= LogError(pipeline->pipeline(), kVUID_Core_Shader_PointSizeBuiltInOverSpecified,
2128                              "Pipeline topology is set to POINT_LIST and geometry or tessellation shaders write PointSize which "
2129                              "is prohibited when the shaderTessellationAndGeometryPointSize feature is not enabled.");
2130         }
2131     } else if (!pointsize_written) {
2132         skip |=
2133             LogError(pipeline->pipeline(), kVUID_Core_Shader_MissingPointSizeBuiltIn,
2134                      "Pipeline topology is set to POINT_LIST, but PointSize is not written to in the shader corresponding to %s.",
2135                      string_VkShaderStageFlagBits(stage));
2136     }
2137     return skip;
2138 }
2139 
ValidatePrimitiveRateShaderState(const PIPELINE_STATE * pipeline,SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint,VkShaderStageFlagBits stage) const2140 bool CoreChecks::ValidatePrimitiveRateShaderState(const PIPELINE_STATE *pipeline, SHADER_MODULE_STATE const *src,
2141                                                   spirv_inst_iter entrypoint, VkShaderStageFlagBits stage) const {
2142     bool primitiverate_written = false;
2143     bool viewportindex_written = false;
2144     bool viewportmask_written = false;
2145     bool skip = false;
2146 
2147     // Check if the primitive shading rate is written
2148     for (const auto &set : src->GetBuiltinDecorationList()) {
2149         auto insn = src->at(set.offset);
2150         if (set.builtin == spv::BuiltInPrimitiveShadingRateKHR) {
2151             primitiverate_written = src->IsBuiltInWritten(insn, entrypoint);
2152         } else if (set.builtin == spv::BuiltInViewportIndex) {
2153             viewportindex_written = src->IsBuiltInWritten(insn, entrypoint);
2154         } else if (set.builtin == spv::BuiltInViewportMaskNV) {
2155             viewportmask_written = src->IsBuiltInWritten(insn, entrypoint);
2156         }
2157         if (primitiverate_written && viewportindex_written && viewportmask_written) {
2158             break;
2159         }
2160     }
2161 
2162     if (!phys_dev_ext_props.fragment_shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports &&
2163         (pipeline->GetPipelineType() == VK_PIPELINE_BIND_POINT_GRAPHICS) && pipeline->create_info.graphics.pViewportState) {
2164         if (!IsDynamic(pipeline, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT) &&
2165             pipeline->create_info.graphics.pViewportState->viewportCount > 1 && primitiverate_written) {
2166             skip |= LogError(pipeline->pipeline(),
2167                              "VUID-VkGraphicsPipelineCreateInfo-primitiveFragmentShadingRateWithMultipleViewports-04503",
2168                              "vkCreateGraphicsPipelines: %s shader statically writes to PrimitiveShadingRateKHR built-in, but "
2169                              "multiple viewports "
2170                              "are used and the primitiveFragmentShadingRateWithMultipleViewports limit is not supported.",
2171                              string_VkShaderStageFlagBits(stage));
2172         }
2173 
2174         if (primitiverate_written && viewportindex_written) {
2175             skip |= LogError(pipeline->pipeline(),
2176                              "VUID-VkGraphicsPipelineCreateInfo-primitiveFragmentShadingRateWithMultipleViewports-04504",
2177                              "vkCreateGraphicsPipelines: %s shader statically writes to both PrimitiveShadingRateKHR and "
2178                              "ViewportIndex built-ins,"
2179                              "but the primitiveFragmentShadingRateWithMultipleViewports limit is not supported.",
2180                              string_VkShaderStageFlagBits(stage));
2181         }
2182 
2183         if (primitiverate_written && viewportmask_written) {
2184             skip |= LogError(pipeline->pipeline(),
2185                              "VUID-VkGraphicsPipelineCreateInfo-primitiveFragmentShadingRateWithMultipleViewports-04505",
2186                              "vkCreateGraphicsPipelines: %s shader statically writes to both PrimitiveShadingRateKHR and "
2187                              "ViewportMaskNV built-ins,"
2188                              "but the primitiveFragmentShadingRateWithMultipleViewports limit is not supported.",
2189                              string_VkShaderStageFlagBits(stage));
2190         }
2191     }
2192     return skip;
2193 }
2194 
ValidateDecorations(SHADER_MODULE_STATE const * module) const2195 bool CoreChecks::ValidateDecorations(SHADER_MODULE_STATE const* module) const {
2196     bool skip = false;
2197 
2198     std::vector<spirv_inst_iter> xfb_streams;
2199     std::vector<spirv_inst_iter> xfb_buffers;
2200     std::vector<spirv_inst_iter> xfb_offsets;
2201 
2202     for (const auto &op_decorate : module->GetDecorationInstructions()) {
2203         uint32_t decoration = op_decorate.word(2);
2204         if (decoration == spv::DecorationXfbStride) {
2205             uint32_t stride = op_decorate.word(3);
2206             if (stride > phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackBufferDataStride) {
2207                 skip |= LogError(
2208                     device, "VUID-RuntimeSpirv-XfbStride-06313",
2209                     "vkCreateGraphicsPipelines(): shader uses transform feedback with xfb_stride (%" PRIu32
2210                     ") greater than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackBufferDataStride (%" PRIu32
2211                     ").",
2212                     stride, phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackBufferDataStride);
2213             }
2214         }
2215         if (decoration == spv::DecorationStream) {
2216             xfb_streams.push_back(op_decorate);
2217             uint32_t stream = op_decorate.word(3);
2218             if (stream >= phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams) {
2219                 skip |= LogError(
2220                     device, "VUID-RuntimeSpirv-Stream-06312",
2221                     "vkCreateGraphicsPipelines(): shader uses transform feedback with stream (%" PRIu32
2222                     ") not less than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackStreams (%" PRIu32 ").",
2223                     stream, phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams);
2224             }
2225         }
2226         if (decoration == spv::DecorationXfbBuffer) {
2227             xfb_buffers.push_back(op_decorate);
2228         }
2229         if (decoration == spv::DecorationOffset) {
2230             xfb_offsets.push_back(op_decorate);
2231         }
2232     }
2233 
2234     // XfbBuffer, buffer data size
2235     std::vector<std::pair<uint32_t, uint32_t>> buffer_data_sizes;
2236     for (const auto &op_decorate : xfb_offsets) {
2237         for (const auto xfb_buffer : xfb_buffers) {
2238             if (xfb_buffer.word(1) == op_decorate.word(1)) {
2239                 const auto offset = op_decorate.word(3);
2240                 const auto def = module->get_def(xfb_buffer.word(1));
2241                 const auto size = module->GetTypeBytesSize(def);
2242                 const uint32_t buffer_data_size = offset + size;
2243                 if (buffer_data_size > phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackBufferDataSize) {
2244                     skip |= LogError(
2245                         device, "VUID-RuntimeSpirv-Offset-06308",
2246                         "vkCreateGraphicsPipelines(): shader uses transform feedback with xfb_offset (%" PRIu32
2247                         ") + size of variable (%" PRIu32 ") greater than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackBufferDataSize "
2248                         "(%" PRIu32 ").",
2249                         offset, size, phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackBufferDataSize);
2250                 }
2251 
2252                 bool found = false;
2253                 for (auto &bds : buffer_data_sizes) {
2254                     if (bds.first == xfb_buffer.word(1)) {
2255                         bds.second = std::max(bds.second, buffer_data_size);
2256                         found = true;
2257                         break;
2258                     }
2259                 }
2260                 if (!found) {
2261                     buffer_data_sizes.emplace_back(xfb_buffer.word(1), buffer_data_size);
2262                 }
2263 
2264                 break;
2265             }
2266         }
2267     }
2268 
2269     std::unordered_map<uint32_t, uint32_t> stream_data_size;
2270     for (const auto &xfb_stream : xfb_streams) {
2271         for (const auto& bds : buffer_data_sizes) {
2272             if (xfb_stream.word(1) == bds.first) {
2273                 uint32_t stream = xfb_stream.word(3);
2274                 const auto itr = stream_data_size.find(stream);
2275                 if (itr != stream_data_size.end()) {
2276                     itr->second += bds.second;
2277                 } else {
2278                     stream_data_size.insert({stream, bds.second});
2279                 }
2280             }
2281         }
2282     }
2283 
2284     for (const auto& stream : stream_data_size) {
2285         if (stream.second > phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreamDataSize) {
2286             skip |= LogError(device, "VUID-RuntimeSpirv-XfbBuffer-06309",
2287                              "vkCreateGraphicsPipelines(): shader uses transform feedback with stream (%" PRIu32
2288                              ") having the sum of buffer data sizes (%" PRIu32
2289                              ") not less than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackBufferDataSize "
2290                              "(%" PRIu32 ").",
2291                              stream.first, stream.second,
2292                              phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackBufferDataSize);
2293         }
2294     }
2295 
2296     return skip;
2297 }
2298 
ValidateTransformFeedback(SHADER_MODULE_STATE const * src) const2299 bool CoreChecks::ValidateTransformFeedback(SHADER_MODULE_STATE const *src) const {
2300     bool skip = false;
2301 
2302     // Temp workaround to prevent false positive errors
2303     // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2450
2304     if (src->HasMultipleEntryPoints()) {
2305         return skip;
2306     }
2307 
2308     layer_data::unordered_set<uint32_t> emitted_streams;
2309     bool output_points = false;
2310     for (const auto& insn : *src) {
2311         const uint32_t opcode = insn.opcode();
2312         if (opcode == spv::OpEmitStreamVertex) {
2313             emitted_streams.emplace(static_cast<uint32_t>(src->GetConstantValueById(insn.word(1))));
2314         }
2315         if (opcode == spv::OpEmitStreamVertex || opcode == spv::OpEndStreamPrimitive) {
2316             uint32_t stream = static_cast<uint32_t>(src->GetConstantValueById(insn.word(1)));
2317             if (stream >= phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams) {
2318                 skip |= LogError(
2319                     device, "VUID-RuntimeSpirv-OpEmitStreamVertex-06310",
2320                     "vkCreateGraphicsPipelines(): shader uses transform feedback stream (%s) with index %" PRIu32
2321                     ", which is not less than VkPhysicalDeviceTransformFeedbackPropertiesEXT::maxTransformFeedbackStreams (%" PRIu32
2322                     ").",
2323                     string_SpvOpcode(opcode), stream, phys_dev_ext_props.transform_feedback_props.maxTransformFeedbackStreams);
2324             }
2325         }
2326         if (opcode == spv::OpExecutionMode && insn.word(2) == spv::ExecutionModeOutputPoints) {
2327             output_points = true;
2328         }
2329     }
2330 
2331     const uint32_t emitted_streams_size = static_cast<uint32_t>(emitted_streams.size());
2332     if (emitted_streams_size > 1 && !output_points &&
2333         phys_dev_ext_props.transform_feedback_props.transformFeedbackStreamsLinesTriangles == VK_FALSE) {
2334         skip |= LogError(
2335             device, "VUID-RuntimeSpirv-transformFeedbackStreamsLinesTriangles-06311",
2336             "vkCreateGraphicsPipelines(): shader emits to %" PRIu32 " vertex streams and VkPhysicalDeviceTransformFeedbackPropertiesEXT::transformFeedbackStreamsLinesTriangles is VK_FALSE, but execution mode is not OutputPoints.",
2337                      emitted_streams_size);
2338     }
2339 
2340     return skip;
2341 }
2342 
2343 // Checks for both TexelOffset and TexelGatherOffset limits
ValidateTexelOffsetLimits(SHADER_MODULE_STATE const * src,spirv_inst_iter & insn) const2344 bool CoreChecks::ValidateTexelOffsetLimits(SHADER_MODULE_STATE const *src, spirv_inst_iter &insn) const {
2345     bool skip = false;
2346 
2347     const uint32_t opcode = insn.opcode();
2348     if (ImageGatherOperation(opcode) || ImageSampleOperation(opcode) || ImageFetchOperation(opcode)) {
2349         uint32_t image_operand_position = ImageOperandsParamPosition(opcode);
2350         // Image operands can be optional
2351         if (image_operand_position != 0 && insn.len() > image_operand_position) {
2352             auto image_operand = insn.word(image_operand_position);
2353             // Bits we are validating (sample/fetch only check ConstOffset)
2354             uint32_t offset_bits =
2355                 ImageGatherOperation(opcode)
2356                     ? (spv::ImageOperandsOffsetMask | spv::ImageOperandsConstOffsetMask | spv::ImageOperandsConstOffsetsMask)
2357                     : (spv::ImageOperandsConstOffsetMask);
2358             if (image_operand & (offset_bits)) {
2359                 // Operand values follow
2360                 uint32_t index = image_operand_position + 1;
2361                 // Each bit has it's own operand, starts with the smallest set bit and loop to the highest bit among
2362                 // ImageOperandsOffsetMask, ImageOperandsConstOffsetMask and ImageOperandsConstOffsetsMask
2363                 for (uint32_t i = 1; i < spv::ImageOperandsConstOffsetsMask; i <<= 1) {
2364                     if (image_operand & i) {  // If the bit is set, consume operand
2365                         if (insn.len() > index && (i & offset_bits)) {
2366                             uint32_t constant_id = insn.word(index);
2367                             const auto &constant = src->get_def(constant_id);
2368                             const bool is_dynamic_offset = constant == src->end();
2369                             if (!is_dynamic_offset && constant.opcode() == spv::OpConstantComposite) {
2370                                 for (uint32_t j = 3; j < constant.len(); ++j) {
2371                                     uint32_t comp_id = constant.word(j);
2372                                     const auto &comp = src->get_def(comp_id);
2373                                     const auto &comp_type = src->get_def(comp.word(1));
2374                                     // Get operand value
2375                                     const uint32_t offset = comp.word(3);
2376                                     // spec requires minTexelGatherOffset/minTexelOffset to be -8 or less so never can compare if
2377                                     // unsigned spec requires maxTexelGatherOffset/maxTexelOffset to be 7 or greater so never can
2378                                     // compare if signed is less then zero
2379                                     const int32_t signed_offset = static_cast<int32_t>(offset);
2380                                     const bool use_signed = (comp_type.opcode() == spv::OpTypeInt && comp_type.word(3) != 0);
2381 
2382                                     // There are 2 sets of VU being covered where the only main difference is the opcode
2383                                     if (ImageGatherOperation(opcode)) {
2384                                         // min/maxTexelGatherOffset
2385                                         if (use_signed && (signed_offset < phys_dev_props.limits.minTexelGatherOffset)) {
2386                                             skip |=
2387                                                 LogError(device, "VUID-RuntimeSpirv-OpImage-06376",
2388                                                          "vkCreateShaderModule(): Shader uses %s with offset (%" PRIi32
2389                                                          ") less than VkPhysicalDeviceLimits::minTexelGatherOffset (%" PRIi32 ").",
2390                                                          string_SpvOpcode(opcode), signed_offset,
2391                                                          phys_dev_props.limits.minTexelGatherOffset);
2392                                         } else if ((offset > phys_dev_props.limits.maxTexelGatherOffset) &&
2393                                                    (!use_signed || (use_signed && signed_offset > 0))) {
2394                                             skip |= LogError(
2395                                                 device, "VUID-RuntimeSpirv-OpImage-06377",
2396                                                 "vkCreateShaderModule(): Shader uses %s with offset (%" PRIu32
2397                                                 ") greater than VkPhysicalDeviceLimits::maxTexelGatherOffset (%" PRIu32 ").",
2398                                                 string_SpvOpcode(opcode), offset, phys_dev_props.limits.maxTexelGatherOffset);
2399                                         }
2400                                     } else {
2401                                         // min/maxTexelOffset
2402                                         if (use_signed && (signed_offset < phys_dev_props.limits.minTexelOffset)) {
2403                                             skip |= LogError(device, "VUID-RuntimeSpirv-OpImageSample-06435",
2404                                                              "vkCreateShaderModule(): Shader uses %s with offset (%" PRIi32
2405                                                              ") less than VkPhysicalDeviceLimits::minTexelOffset (%" PRIi32 ").",
2406                                                              string_SpvOpcode(opcode), signed_offset,
2407                                                              phys_dev_props.limits.minTexelOffset);
2408                                         } else if ((offset > phys_dev_props.limits.maxTexelOffset) &&
2409                                                    (!use_signed || (use_signed && signed_offset > 0))) {
2410                                             skip |=
2411                                                 LogError(device, "VUID-RuntimeSpirv-OpImageSample-06436",
2412                                                          "vkCreateShaderModule(): Shader uses %s with offset (%" PRIu32
2413                                                          ") greater than VkPhysicalDeviceLimits::maxTexelOffset (%" PRIu32 ").",
2414                                                          string_SpvOpcode(opcode), offset, phys_dev_props.limits.maxTexelOffset);
2415                                         }
2416                                     }
2417                                 }
2418                             }
2419                         }
2420                         index += ImageOperandsParamCount(i);
2421                     }
2422                 }
2423             }
2424         }
2425     }
2426 
2427     return skip;
2428 }
2429 
ValidateShaderClock(SHADER_MODULE_STATE const * module,spirv_inst_iter & insn) const2430 bool CoreChecks::ValidateShaderClock(SHADER_MODULE_STATE const *module, spirv_inst_iter &insn) const {
2431     bool skip = false;
2432 
2433     switch (insn.opcode()) {
2434         case spv::OpReadClockKHR: {
2435             auto scope_id = module->get_def(insn.word(3));
2436             auto scope_type = scope_id.word(3);
2437             // if scope isn't Subgroup or Device, spirv-val will catch
2438             if ((scope_type == spv::ScopeSubgroup) && (enabled_features.shader_clock_features.shaderSubgroupClock == VK_FALSE)) {
2439                 skip |= LogError(device, "VUID-RuntimeSpirv-shaderSubgroupClock-06267",
2440                                  "%s: OpReadClockKHR is used with a Subgroup scope but shaderSubgroupClock was not enabled.",
2441                                  report_data->FormatHandle(module->vk_shader_module()).c_str());
2442             } else if ((scope_type == spv::ScopeDevice) && (enabled_features.shader_clock_features.shaderDeviceClock == VK_FALSE)) {
2443                 skip |= LogError(device, "VUID-RuntimeSpirv-shaderDeviceClock-06268",
2444                                  "%s: OpReadClockKHR is used with a Device scope but shaderDeviceClock was not enabled.",
2445                                  report_data->FormatHandle(module->vk_shader_module()).c_str());
2446             }
2447             break;
2448         }
2449     }
2450     return skip;
2451 }
2452 
ValidatePipelineShaderStage(const PIPELINE_STATE * pipeline,const PipelineStageState & stage_state,bool check_point_size) const2453 bool CoreChecks::ValidatePipelineShaderStage(const PIPELINE_STATE *pipeline, const PipelineStageState &stage_state,
2454                                              bool check_point_size) const {
2455     bool skip = false;
2456     const auto *pStage = stage_state.create_info;
2457     const auto *module = stage_state.module.get();
2458     const auto &entrypoint = stage_state.entrypoint;
2459     // Check the module
2460     if (!module->has_valid_spirv) {
2461         skip |= LogError(
2462             device, "VUID-VkPipelineShaderStageCreateInfo-module-parameter", "%s does not contain valid spirv for stage %s.",
2463             report_data->FormatHandle(module->vk_shader_module()).c_str(), string_VkShaderStageFlagBits(stage_state.stage_flag));
2464     }
2465 
2466     // If specialization-constant values are given and specialization-constant instructions are present in the shader, the
2467     // specializations should be applied and validated.
2468     if (pStage->pSpecializationInfo != nullptr && pStage->pSpecializationInfo->mapEntryCount > 0 &&
2469         pStage->pSpecializationInfo->pMapEntries != nullptr && module->HasSpecConstants()) {
2470         // Gather the specialization-constant values.
2471         auto const &specialization_info = pStage->pSpecializationInfo;
2472         auto const &specialization_data = reinterpret_cast<uint8_t const *>(specialization_info->pData);
2473         std::unordered_map<uint32_t, std::vector<uint32_t>> id_value_map;  // note: this must be std:: to work with spvtools
2474         id_value_map.reserve(specialization_info->mapEntryCount);
2475         for (auto i = 0u; i < specialization_info->mapEntryCount; ++i) {
2476             auto const &map_entry = specialization_info->pMapEntries[i];
2477             const auto itr = module->GetSpecConstMap().find(map_entry.constantID);
2478             // "If a constantID value is not a specialization constant ID used in the shader, that map entry does not affect the
2479             // behavior of the pipeline."
2480             if (itr != module->GetSpecConstMap().cend()) {
2481                 // Make sure map_entry.size matches the spec constant's size
2482                 uint32_t spec_const_size = decoration_set::kInvalidValue;
2483                 const auto def_ins = module->get_def(itr->second);
2484                 const auto type_ins = module->get_def(def_ins.word(1));
2485                 // Specialization constants can only be of type bool, scalar integer, or scalar floating point
2486                 switch (type_ins.opcode()) {
2487                     case spv::OpTypeBool:
2488                         // "If the specialization constant is of type boolean, size must be the byte size of VkBool32"
2489                         spec_const_size = sizeof(VkBool32);
2490                         break;
2491                     case spv::OpTypeInt:
2492                     case spv::OpTypeFloat:
2493                         spec_const_size = type_ins.word(2) / 8;
2494                         break;
2495                     default:
2496                         // spirv-val should catch if SpecId is not used on a OpSpecConstantTrue/OpSpecConstantFalse/OpSpecConstant
2497                         // and OpSpecConstant is validated to be a OpTypeInt or OpTypeFloat
2498                         break;
2499                 }
2500 
2501                 if (map_entry.size != spec_const_size) {
2502                     skip |=
2503                         LogError(device, "VUID-VkSpecializationMapEntry-constantID-00776",
2504                                  "Specialization constant (ID = %" PRIu32 ", entry = %" PRIu32
2505                                  ") has invalid size %zu in shader module %s. Expected size is %" PRIu32 " from shader definition.",
2506                                  map_entry.constantID, i, map_entry.size,
2507                                  report_data->FormatHandle(module->vk_shader_module()).c_str(), spec_const_size);
2508                 }
2509             }
2510 
2511             if ((map_entry.offset + map_entry.size) <= specialization_info->dataSize) {
2512                 // Allocate enough room for ceil(map_entry.size / 4) to store entries
2513                 std::vector<uint32_t> entry_data((map_entry.size + 4 - 1) / 4, 0);
2514                 uint8_t *out_p = reinterpret_cast<uint8_t *>(entry_data.data());
2515                 const uint8_t *const start_in_p = specialization_data + map_entry.offset;
2516                 const uint8_t *const end_in_p = start_in_p + map_entry.size;
2517 
2518                 std::copy(start_in_p, end_in_p, out_p);
2519                 id_value_map.emplace(map_entry.constantID, std::move(entry_data));
2520             }
2521         }
2522 
2523         // both spirv-opt and spirv-val will use the same flags
2524         spvtools::ValidatorOptions options;
2525         AdjustValidatorOptions(device_extensions, enabled_features, options);
2526 
2527         // Apply the specialization-constant values and revalidate the shader module.
2528         spv_target_env spirv_environment = PickSpirvEnv(api_version, IsExtEnabled(device_extensions.vk_khr_spirv_1_4));
2529         spvtools::Optimizer optimizer(spirv_environment);
2530         spvtools::MessageConsumer consumer = [&skip, &module, &stage_state, this](spv_message_level_t level, const char *source,
2531                                                                                   const spv_position_t &position,
2532                                                                                   const char *message) {
2533             skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-module-parameter",
2534                              "%s does not contain valid spirv for stage %s. %s",
2535                              report_data->FormatHandle(module->vk_shader_module()).c_str(),
2536                              string_VkShaderStageFlagBits(stage_state.stage_flag), message);
2537         };
2538         optimizer.SetMessageConsumer(consumer);
2539         optimizer.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass(id_value_map));
2540         optimizer.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass());
2541         std::vector<uint32_t> specialized_spirv;
2542         auto const optimized = optimizer.Run(module->words.data(), module->words.size(), &specialized_spirv, options, false);
2543         assert(optimized == true);
2544 
2545         if (optimized) {
2546             spv_context ctx = spvContextCreate(spirv_environment);
2547             spv_const_binary_t binary{specialized_spirv.data(), specialized_spirv.size()};
2548             spv_diagnostic diag = nullptr;
2549             auto const spv_valid = spvValidateWithOptions(ctx, options, &binary, &diag);
2550             if (spv_valid != SPV_SUCCESS) {
2551                 skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-module-04145",
2552                                  "After specialization was applied, %s does not contain valid spirv for stage %s.",
2553                                  report_data->FormatHandle(module->vk_shader_module()).c_str(),
2554                                  string_VkShaderStageFlagBits(stage_state.stage_flag));
2555             }
2556 
2557             spvDiagnosticDestroy(diag);
2558             spvContextDestroy(ctx);
2559         }
2560 
2561         skip |= ValidateWorkgroupSize(module, pStage, id_value_map);
2562     }
2563 
2564     // Check the entrypoint
2565     if (entrypoint == module->end()) {
2566         skip |= LogError(device, "VUID-VkPipelineShaderStageCreateInfo-pName-00707", "No entrypoint found named `%s` for stage %s.",
2567                          pStage->pName, string_VkShaderStageFlagBits(stage_state.stage_flag));
2568     }
2569     if (skip) return true;  // no point continuing beyond here, any analysis is just going to be garbage.
2570 
2571     // Mark accessible ids
2572     auto &accessible_ids = stage_state.accessible_ids;
2573 
2574     // Validate descriptor set layout against what the entrypoint actually uses
2575 
2576     // The following tries to limit the number of passes through the shader module. The validation passes in here are "stateless"
2577     // and mainly only checking the instruction in detail for a single operation
2578     uint32_t total_shared_size = 0;
2579     for (auto insn : *module) {
2580         skip |= ValidateTexelOffsetLimits(module, insn);
2581         skip |= ValidateShaderCapabilitiesAndExtensions(module, insn);
2582         skip |= ValidateShaderClock(module, insn);
2583         skip |= ValidateShaderStageGroupNonUniform(module, pStage->stage, insn);
2584         skip |= ValidateMemoryScope(module, insn);
2585         total_shared_size += module->CalcComputeSharedMemory(pStage->stage, insn);
2586     }
2587 
2588     if (total_shared_size > phys_dev_props.limits.maxComputeSharedMemorySize) {
2589         skip |= LogError(device, kVUID_Core_Shader_MaxComputeSharedMemorySize,
2590                          "Shader uses %" PRIu32 " bytes of shared memory, more than allowed by physicalDeviceLimits::maxComputeSharedMemorySize (%" PRIu32 ")",
2591                          total_shared_size, phys_dev_props.limits.maxComputeSharedMemorySize);
2592     }
2593 
2594     skip |= ValidateTransformFeedback(module);
2595     skip |= ValidateShaderStageWritableOrAtomicDescriptor(pStage->stage, stage_state.has_writable_descriptor,
2596                                                           stage_state.has_atomic_descriptor);
2597     skip |= ValidateShaderStageInputOutputLimits(module, pStage, pipeline, entrypoint);
2598     skip |= ValidateShaderStorageImageFormats(module);
2599     skip |= ValidateShaderStageMaxResources(pStage->stage, pipeline);
2600     skip |= ValidateAtomicsTypes(module);
2601     skip |= ValidateExecutionModes(module, entrypoint, pStage->stage, pipeline);
2602     skip |= ValidateSpecializations(pStage);
2603     skip |= ValidateDecorations(module);
2604     if (check_point_size && !pipeline->create_info.graphics.pRasterizationState->rasterizerDiscardEnable) {
2605         skip |= ValidatePointListShaderState(pipeline, module, entrypoint, pStage->stage);
2606     }
2607     skip |= ValidateBuiltinLimits(module, entrypoint);
2608     if (enabled_features.cooperative_matrix_features.cooperativeMatrix) {
2609         skip |= ValidateCooperativeMatrix(module, pStage, pipeline);
2610     }
2611     if (enabled_features.fragment_shading_rate_features.primitiveFragmentShadingRate) {
2612         skip |= ValidatePrimitiveRateShaderState(pipeline, module, entrypoint, pStage->stage);
2613     }
2614     if (IsExtEnabled(device_extensions.vk_qcom_render_pass_shader_resolve)) {
2615         skip |= ValidateShaderResolveQCOM(module, pStage, pipeline);
2616     }
2617     if (IsExtEnabled(device_extensions.vk_ext_subgroup_size_control)) {
2618         skip |= ValidateShaderSubgroupSizeControl(pStage);
2619     }
2620 
2621     // "layout must be consistent with the layout of the * shader"
2622     // 'consistent' -> #descriptorsets-pipelinelayout-consistency
2623     std::string vuid_layout_mismatch;
2624     switch (pipeline->create_info.graphics.sType) {
2625         case VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO:
2626             vuid_layout_mismatch = "VUID-VkGraphicsPipelineCreateInfo-layout-00756";
2627             break;
2628         case VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO:
2629             vuid_layout_mismatch = "VUID-VkComputePipelineCreateInfo-layout-00703";
2630             break;
2631         case VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR:
2632             vuid_layout_mismatch = "VUID-VkRayTracingPipelineCreateInfoKHR-layout-03427";
2633             break;
2634         case VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV:
2635             vuid_layout_mismatch = "VUID-VkRayTracingPipelineCreateInfoNV-layout-03427";
2636             break;
2637         default:
2638             assert(false);
2639             break;
2640     }
2641 
2642     // Validate Push Constants use
2643     skip |= ValidatePushConstantUsage(*pipeline, module, pStage, vuid_layout_mismatch);
2644 
2645     // Validate descriptor use
2646     for (auto use : stage_state.descriptor_uses) {
2647         // Verify given pipelineLayout has requested setLayout with requested binding
2648         const auto &binding = GetDescriptorBinding(pipeline->pipeline_layout.get(), use.first);
2649         unsigned required_descriptor_count;
2650         bool is_khr = binding && binding->descriptorType == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
2651         std::set<uint32_t> descriptor_types =
2652             TypeToDescriptorTypeSet(module, use.second.type_id, required_descriptor_count, is_khr);
2653 
2654         if (!binding) {
2655             skip |= LogError(device, vuid_layout_mismatch,
2656                              "Shader uses descriptor slot %u.%u (expected `%s`) but not declared in pipeline layout",
2657                              use.first.set, use.first.binding, string_descriptorTypes(descriptor_types).c_str());
2658         } else if (~binding->stageFlags & pStage->stage) {
2659             skip |= LogError(device, vuid_layout_mismatch,
2660                              "Shader uses descriptor slot %u.%u but descriptor not accessible from stage %s", use.first.set,
2661                              use.first.binding, string_VkShaderStageFlagBits(pStage->stage));
2662         } else if ((binding->descriptorType != VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) &&
2663                    (descriptor_types.find(binding->descriptorType) == descriptor_types.end())) {
2664             skip |= LogError(device, vuid_layout_mismatch,
2665                              "Type mismatch on descriptor slot %u.%u (expected `%s`) but descriptor of type %s", use.first.set,
2666                              use.first.binding, string_descriptorTypes(descriptor_types).c_str(),
2667                              string_VkDescriptorType(binding->descriptorType));
2668         } else if (binding->descriptorCount < required_descriptor_count) {
2669             skip |= LogError(device, vuid_layout_mismatch,
2670                              "Shader expects at least %u descriptors for binding %u.%u but only %u provided",
2671                              required_descriptor_count, use.first.set, use.first.binding, binding->descriptorCount);
2672         }
2673     }
2674 
2675     // Validate use of input attachments against subpass structure
2676     if (pStage->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
2677         auto input_attachment_uses = module->CollectInterfaceByInputAttachmentIndex(accessible_ids);
2678 
2679         if (!pipeline->rp_state->use_dynamic_rendering) {
2680             auto rpci = pipeline->rp_state->createInfo.ptr();
2681             auto subpass = pipeline->create_info.graphics.subpass;
2682             for (auto use : input_attachment_uses) {
2683                 auto input_attachments = rpci->pSubpasses[subpass].pInputAttachments;
2684                 auto index = (input_attachments && use.first < rpci->pSubpasses[subpass].inputAttachmentCount)
2685                     ? input_attachments[use.first].attachment
2686                     : VK_ATTACHMENT_UNUSED;
2687 
2688                 if (index == VK_ATTACHMENT_UNUSED) {
2689                     skip |= LogError(device, kVUID_Core_Shader_MissingInputAttachment,
2690                         "Shader consumes input attachment index %d but not provided in subpass", use.first);
2691                 }
2692                 else if (!(GetFormatType(rpci->pAttachments[index].format) & module->GetFundamentalType(use.second.type_id))) {
2693                     skip |=
2694                         LogError(device, kVUID_Core_Shader_InputAttachmentTypeMismatch,
2695                             "Subpass input attachment %u format of %s does not match type used in shader `%s`", use.first,
2696                             string_VkFormat(rpci->pAttachments[index].format), module->DescribeType(use.second.type_id).c_str());
2697                 }
2698             }
2699         }
2700     }
2701     if (pStage->stage == VK_SHADER_STAGE_COMPUTE_BIT) {
2702         skip |= ValidateComputeWorkGroupSizes(module, entrypoint, stage_state);
2703     }
2704 
2705     return skip;
2706 }
2707 
ValidateInterfaceBetweenStages(SHADER_MODULE_STATE const * producer,spirv_inst_iter producer_entrypoint,shader_stage_attributes const * producer_stage,SHADER_MODULE_STATE const * consumer,spirv_inst_iter consumer_entrypoint,shader_stage_attributes const * consumer_stage) const2708 bool CoreChecks::ValidateInterfaceBetweenStages(SHADER_MODULE_STATE const *producer, spirv_inst_iter producer_entrypoint,
2709                                                 shader_stage_attributes const *producer_stage, SHADER_MODULE_STATE const *consumer,
2710                                                 spirv_inst_iter consumer_entrypoint,
2711                                                 shader_stage_attributes const *consumer_stage) const {
2712     bool skip = false;
2713 
2714     auto outputs =
2715         producer->CollectInterfaceByLocation(producer_entrypoint, spv::StorageClassOutput, producer_stage->arrayed_output);
2716     auto inputs = consumer->CollectInterfaceByLocation(consumer_entrypoint, spv::StorageClassInput, consumer_stage->arrayed_input);
2717 
2718     auto a_it = outputs.begin();
2719     auto b_it = inputs.begin();
2720 
2721     uint32_t a_component = 0;
2722     uint32_t b_component = 0;
2723 
2724     // Maps sorted by key (location); walk them together to find mismatches
2725     while ((outputs.size() > 0 && a_it != outputs.end()) || (inputs.size() && b_it != inputs.end())) {
2726         bool a_at_end = outputs.size() == 0 || a_it == outputs.end();
2727         bool b_at_end = inputs.size() == 0 || b_it == inputs.end();
2728         auto a_first = a_at_end ? std::make_pair(0u, 0u) : a_it->first;
2729         auto b_first = b_at_end ? std::make_pair(0u, 0u) : b_it->first;
2730 
2731         a_first.second += a_component;
2732         b_first.second += b_component;
2733 
2734         const auto a_length = a_at_end ? 0 : producer->GetNumComponentsInBaseType(producer->get_def(a_it->second.type_id));
2735         const auto b_length = b_at_end ? 0 : consumer->GetNumComponentsInBaseType(consumer->get_def(b_it->second.type_id));
2736         assert(a_at_end || a_component < a_length);
2737         assert(b_at_end || b_component < b_length);
2738 
2739         if (b_at_end || ((!a_at_end) && (a_first < b_first))) {
2740             skip |= LogPerformanceWarning(producer->vk_shader_module(), kVUID_Core_Shader_OutputNotConsumed,
2741                                           "%s writes to output location %" PRIu32 ".%" PRIu32 " which is not consumed by %s",
2742                                           producer_stage->name, a_first.first, a_first.second, consumer_stage->name);
2743             if ((b_first.first > a_first.first) || b_at_end || (a_component + 1 == a_length)) {
2744                 a_it++;
2745                 a_component = 0;
2746             } else {
2747                 a_component++;
2748             }
2749         } else if (a_at_end || a_first > b_first) {
2750             skip |= LogError(consumer->vk_shader_module(), kVUID_Core_Shader_InputNotProduced,
2751                              "%s consumes input location %" PRIu32 ".%" PRIu32 " which is not written by %s", consumer_stage->name,
2752                              b_first.first, b_first.second, producer_stage->name);
2753             if ((a_first.first > b_first.first) || a_at_end || (b_component + 1 == b_length)) {
2754                 b_it++;
2755                 b_component = 0;
2756             } else {
2757                 b_component++;
2758             }
2759         } else {
2760             // subtleties of arrayed interfaces:
2761             // - if is_patch, then the member is not arrayed, even though the interface may be.
2762             // - if is_block_member, then the extra array level of an arrayed interface is not
2763             //   expressed in the member type -- it's expressed in the block type.
2764             if (!TypesMatch(producer, consumer, a_it->second.type_id, b_it->second.type_id)) {
2765                 skip |= LogError(producer->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
2766                                  "Type mismatch on location %" PRIu32 ".%" PRIu32 ": '%s' vs '%s'", a_first.first, a_first.second,
2767                                  producer->DescribeType(a_it->second.type_id).c_str(),
2768                                  consumer->DescribeType(b_it->second.type_id).c_str());
2769                 a_it++;
2770                 b_it++;
2771                 continue;
2772             }
2773             if (a_it->second.is_patch != b_it->second.is_patch) {
2774                 skip |= LogError(producer->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
2775                                  "Decoration mismatch on location %u.%u: is per-%s in %s stage but per-%s in %s stage",
2776                                  a_first.first, a_first.second, a_it->second.is_patch ? "patch" : "vertex", producer_stage->name,
2777                                  b_it->second.is_patch ? "patch" : "vertex", consumer_stage->name);
2778             }
2779             if (a_it->second.is_relaxed_precision != b_it->second.is_relaxed_precision) {
2780                 skip |= LogError(producer->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
2781                                  "Decoration mismatch on location %" PRIu32 ".%" PRIu32 ": %s and %s stages differ in precision",
2782                                  a_first.first, a_first.second, producer_stage->name, consumer_stage->name);
2783             }
2784             uint32_t a_remaining = a_length - a_component;
2785             uint32_t b_remaining = b_length - b_component;
2786             if (a_remaining == b_remaining) {  // Sizes match so we can advance both a_it and b_it
2787                 a_it++;
2788                 b_it++;
2789                 a_component = 0;
2790                 b_component = 0;
2791             } else if (a_remaining > b_remaining) {  // a has more components remaining
2792                 a_component += b_remaining;
2793                 b_component = 0;
2794                 b_it++;
2795             } else if (b_remaining > a_remaining) {  // b has more components remaining
2796                 b_component += a_remaining;
2797                 a_component = 0;
2798                 a_it++;
2799             }
2800         }
2801     }
2802 
2803     if (consumer_stage->stage != VK_SHADER_STAGE_FRAGMENT_BIT) {
2804         auto builtins_producer = producer->CollectBuiltinBlockMembers(producer_entrypoint, spv::StorageClassOutput);
2805         auto builtins_consumer = consumer->CollectBuiltinBlockMembers(consumer_entrypoint, spv::StorageClassInput);
2806 
2807         if (!builtins_producer.empty() && !builtins_consumer.empty()) {
2808             if (builtins_producer.size() != builtins_consumer.size()) {
2809                 skip |= LogError(producer->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
2810                                  "Number of elements inside builtin block differ between stages (%s %d vs %s %d).",
2811                                  producer_stage->name, static_cast<int>(builtins_producer.size()), consumer_stage->name,
2812                                  static_cast<int>(builtins_consumer.size()));
2813             } else {
2814                 auto it_producer = builtins_producer.begin();
2815                 auto it_consumer = builtins_consumer.begin();
2816                 while (it_producer != builtins_producer.end() && it_consumer != builtins_consumer.end()) {
2817                     if (*it_producer != *it_consumer) {
2818                         skip |= LogError(producer->vk_shader_module(), kVUID_Core_Shader_InterfaceTypeMismatch,
2819                                          "Builtin variable inside block doesn't match between %s and %s.", producer_stage->name,
2820                                          consumer_stage->name);
2821                         break;
2822                     }
2823                     it_producer++;
2824                     it_consumer++;
2825                 }
2826             }
2827         }
2828     }
2829 
2830     return skip;
2831 }
2832 
DetermineFinalGeomStage(const PIPELINE_STATE * pipeline,const VkGraphicsPipelineCreateInfo * pCreateInfo)2833 static inline uint32_t DetermineFinalGeomStage(const PIPELINE_STATE *pipeline, const VkGraphicsPipelineCreateInfo *pCreateInfo) {
2834     uint32_t stage_mask = 0;
2835     if (pipeline->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_POINT_LIST) {
2836         for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
2837             stage_mask |= pCreateInfo->pStages[i].stage;
2838         }
2839         // Determine which shader in which PointSize should be written (the final geometry stage)
2840         if (stage_mask & VK_SHADER_STAGE_MESH_BIT_NV) {
2841             stage_mask = VK_SHADER_STAGE_MESH_BIT_NV;
2842         } else if (stage_mask & VK_SHADER_STAGE_GEOMETRY_BIT) {
2843             stage_mask = VK_SHADER_STAGE_GEOMETRY_BIT;
2844         } else if (stage_mask & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
2845             stage_mask = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
2846         } else if (stage_mask & VK_SHADER_STAGE_VERTEX_BIT) {
2847             stage_mask = VK_SHADER_STAGE_VERTEX_BIT;
2848         }
2849     }
2850     return stage_mask;
2851 }
2852 
2853 // Validate that the shaders used by the given pipeline and store the active_slots
2854 //  that are actually used by the pipeline into pPipeline->active_slots
ValidateGraphicsPipelineShaderState(const PIPELINE_STATE * pipeline) const2855 bool CoreChecks::ValidateGraphicsPipelineShaderState(const PIPELINE_STATE *pipeline) const {
2856     const auto create_info = pipeline->create_info.graphics.ptr();
2857 
2858     bool skip = false;
2859 
2860     uint32_t pointlist_stage_mask = DetermineFinalGeomStage(pipeline, create_info);
2861 
2862     const PipelineStageState *vertex_stage = nullptr, *fragment_stage = nullptr;
2863     for (auto &stage : pipeline->stage_state) {
2864         skip |= ValidatePipelineShaderStage(pipeline, stage, (pointlist_stage_mask == stage.stage_flag));
2865         if (stage.stage_flag == VK_SHADER_STAGE_VERTEX_BIT) {
2866             vertex_stage = &stage;
2867         }
2868         if (stage.stage_flag == VK_SHADER_STAGE_FRAGMENT_BIT) {
2869             fragment_stage = &stage;
2870         }
2871     }
2872 
2873     // if the shader stages are no good individually, cross-stage validation is pointless.
2874     if (skip) return true;
2875 
2876     auto vi = create_info->pVertexInputState;
2877 
2878     if (vi) {
2879         skip |= ValidateViConsistency(vi);
2880     }
2881 
2882     if (vertex_stage && vertex_stage->module->has_valid_spirv && !IsDynamic(pipeline, VK_DYNAMIC_STATE_VERTEX_INPUT_EXT)) {
2883         skip |= ValidateViAgainstVsInputs(vi, vertex_stage->module.get(), vertex_stage->entrypoint);
2884     }
2885 
2886     for (size_t i = 1; i < pipeline->stage_state.size(); i++) {
2887         const auto &producer = pipeline->stage_state[i - 1];
2888         const auto &consumer = pipeline->stage_state[i];
2889         assert(producer.module);
2890         if (&producer == fragment_stage) {
2891             break;
2892         }
2893         if (consumer.module) {
2894             if (consumer.module->has_valid_spirv && producer.module->has_valid_spirv) {
2895                 auto producer_id = GetShaderStageId(producer.stage_flag);
2896                 auto consumer_id = GetShaderStageId(consumer.stage_flag);
2897                 skip |=
2898                     ValidateInterfaceBetweenStages(producer.module.get(), producer.entrypoint, &shader_stage_attribs[producer_id],
2899                                                    consumer.module.get(), consumer.entrypoint, &shader_stage_attribs[consumer_id]);
2900             }
2901         }
2902     }
2903 
2904     if (fragment_stage && fragment_stage->module->has_valid_spirv) {
2905         if (pipeline->rp_state->use_dynamic_rendering) {
2906             skip |= ValidateFsOutputsAgainstDynamicRenderingRenderPass(fragment_stage->module.get(), fragment_stage->entrypoint, pipeline);
2907         } else {
2908             skip |= ValidateFsOutputsAgainstRenderPass(fragment_stage->module.get(), fragment_stage->entrypoint, pipeline,
2909                                                        create_info->subpass);
2910         }
2911     }
2912 
2913     return skip;
2914 }
2915 
ValidateGraphicsPipelineShaderDynamicState(const PIPELINE_STATE * pipeline,const CMD_BUFFER_STATE * pCB,const char * caller,const DrawDispatchVuid & vuid) const2916 bool CoreChecks::ValidateGraphicsPipelineShaderDynamicState(const PIPELINE_STATE *pipeline, const CMD_BUFFER_STATE *pCB,
2917                                                             const char *caller, const DrawDispatchVuid &vuid) const {
2918     bool skip = false;
2919 
2920     for (auto &stage : pipeline->stage_state) {
2921         if (stage.stage_flag == VK_SHADER_STAGE_VERTEX_BIT || stage.stage_flag == VK_SHADER_STAGE_GEOMETRY_BIT ||
2922             stage.stage_flag == VK_SHADER_STAGE_MESH_BIT_NV) {
2923             if (!phys_dev_ext_props.fragment_shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports &&
2924                 IsDynamic(pipeline, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT) && pCB->viewportWithCountCount != 1) {
2925                 if (stage.wrote_primitive_shading_rate) {
2926                     skip |=
2927                         LogError(pipeline->pipeline(), vuid.viewport_count_primitive_shading_rate,
2928                                  "%s: %s shader of currently bound pipeline statically writes to PrimitiveShadingRateKHR built-in"
2929                                  "but multiple viewports are set by the last call to vkCmdSetViewportWithCountEXT,"
2930                                  "and the primitiveFragmentShadingRateWithMultipleViewports limit is not supported.",
2931                                  caller, string_VkShaderStageFlagBits(stage.stage_flag));
2932                 }
2933             }
2934         }
2935     }
2936 
2937     return skip;
2938 }
2939 
ValidateComputePipelineShaderState(PIPELINE_STATE * pipeline) const2940 bool CoreChecks::ValidateComputePipelineShaderState(PIPELINE_STATE *pipeline) const {
2941     return ValidatePipelineShaderStage(pipeline, pipeline->stage_state[0], false);
2942 }
2943 
CalcShaderStageCount(const PIPELINE_STATE * pipeline,VkShaderStageFlagBits stageBit) const2944 uint32_t CoreChecks::CalcShaderStageCount(const PIPELINE_STATE *pipeline, VkShaderStageFlagBits stageBit) const {
2945     uint32_t total = 0;
2946     const auto &create_info = pipeline->create_info.raytracing;
2947     const auto *stages = create_info.ptr()->pStages;
2948     for (uint32_t stage_index = 0; stage_index < create_info.stageCount; stage_index++) {
2949         if (stages[stage_index].stage == stageBit) {
2950             total++;
2951         }
2952     }
2953 
2954     if (create_info.pLibraryInfo) {
2955         for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
2956             const auto library_pipeline = Get<PIPELINE_STATE>(create_info.pLibraryInfo->pLibraries[i]);
2957             total += CalcShaderStageCount(library_pipeline.get(), stageBit);
2958         }
2959     }
2960 
2961     return total;
2962 }
2963 
GroupHasValidIndex(const PIPELINE_STATE * pipeline,uint32_t group,uint32_t stage) const2964 bool CoreChecks::GroupHasValidIndex(const PIPELINE_STATE *pipeline, uint32_t group, uint32_t stage) const {
2965     if (group == VK_SHADER_UNUSED_NV) {
2966         return true;
2967     }
2968 
2969     const auto &create_info = pipeline->create_info.raytracing;
2970     const auto *stages = create_info.ptr()->pStages;
2971 
2972     if (group < create_info.stageCount) {
2973         return (stages[group].stage & stage) != 0;
2974     }
2975     group -= create_info.stageCount;
2976 
2977     // Search libraries
2978     if (create_info.pLibraryInfo) {
2979         for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
2980             auto library_pipeline = Get<PIPELINE_STATE>(create_info.pLibraryInfo->pLibraries[i]);
2981             const uint32_t stage_count = library_pipeline->create_info.raytracing.ptr()->stageCount;
2982             if (group < stage_count) {
2983                 return (library_pipeline->create_info.raytracing.ptr()->pStages[group].stage & stage) != 0;
2984             }
2985             group -= stage_count;
2986         }
2987     }
2988 
2989     // group index too large
2990     return false;
2991 }
2992 
ValidateRayTracingPipeline(PIPELINE_STATE * pipeline,VkPipelineCreateFlags flags,bool isKHR) const2993 bool CoreChecks::ValidateRayTracingPipeline(PIPELINE_STATE *pipeline, VkPipelineCreateFlags flags, bool isKHR) const {
2994     bool skip = false;
2995 
2996     const auto &create_info = pipeline->create_info.raytracing;
2997     if (isKHR) {
2998         if (create_info.maxPipelineRayRecursionDepth > phys_dev_ext_props.ray_tracing_propsKHR.maxRayRecursionDepth) {
2999             skip |=
3000                 LogError(device, "VUID-VkRayTracingPipelineCreateInfoKHR-maxPipelineRayRecursionDepth-03589",
3001                          "vkCreateRayTracingPipelinesKHR: maxPipelineRayRecursionDepth (%d ) must be less than or equal to "
3002                          "VkPhysicalDeviceRayTracingPipelinePropertiesKHR::maxRayRecursionDepth %d",
3003                          create_info.maxPipelineRayRecursionDepth, phys_dev_ext_props.ray_tracing_propsKHR.maxRayRecursionDepth);
3004         }
3005         if (create_info.pLibraryInfo) {
3006             for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
3007                 const auto library_pipelinestate = Get<PIPELINE_STATE>(create_info.pLibraryInfo->pLibraries[i]);
3008                 const auto &library_create_info = library_pipelinestate->create_info.raytracing;
3009                 if (library_create_info.maxPipelineRayRecursionDepth != create_info.maxPipelineRayRecursionDepth) {
3010                     skip |= LogError(
3011                         device, "VUID-VkRayTracingPipelineCreateInfoKHR-pLibraries-03591",
3012                         "vkCreateRayTracingPipelinesKHR: Each element  (%d) of the pLibraries member of libraries must have been"
3013                         "created with the value of maxPipelineRayRecursionDepth (%d) equal to that in this pipeline (%d) .",
3014                         i, library_create_info.maxPipelineRayRecursionDepth, create_info.maxPipelineRayRecursionDepth);
3015                 }
3016                 if (library_create_info.pLibraryInfo && (library_create_info.pLibraryInterface->maxPipelineRayHitAttributeSize !=
3017                                                              create_info.pLibraryInterface->maxPipelineRayHitAttributeSize ||
3018                                                          library_create_info.pLibraryInterface->maxPipelineRayPayloadSize !=
3019                                                              create_info.pLibraryInterface->maxPipelineRayPayloadSize)) {
3020                     skip |= LogError(device, "VUID-VkRayTracingPipelineCreateInfoKHR-pLibraryInfo-03593",
3021                                      "vkCreateRayTracingPipelinesKHR: If pLibraryInfo is not NULL, each element of its pLibraries "
3022                                      "member must have been created with values of the maxPipelineRayPayloadSize and "
3023                                      "maxPipelineRayHitAttributeSize members of pLibraryInterface equal to those in this pipeline");
3024                 }
3025                 if ((flags & VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR) &&
3026                     !(library_create_info.flags & VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR)) {
3027                     skip |= LogError(device, "VUID-VkRayTracingPipelineCreateInfoKHR-flags-03594",
3028                                      "vkCreateRayTracingPipelinesKHR: If flags includes "
3029                                      "VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR, each element of "
3030                                      "the pLibraries member of libraries must have been created with the "
3031                                      "VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR bit set");
3032                 }
3033             }
3034         }
3035     } else {
3036         if (create_info.maxRecursionDepth > phys_dev_ext_props.ray_tracing_propsNV.maxRecursionDepth) {
3037             skip |= LogError(device, "VUID-VkRayTracingPipelineCreateInfoNV-maxRecursionDepth-03457",
3038                              "vkCreateRayTracingPipelinesNV: maxRecursionDepth (%d) must be less than or equal to "
3039                              "VkPhysicalDeviceRayTracingPropertiesNV::maxRecursionDepth (%d)",
3040                              create_info.maxRecursionDepth, phys_dev_ext_props.ray_tracing_propsNV.maxRecursionDepth);
3041         }
3042     }
3043     const auto *groups = create_info.ptr()->pGroups;
3044 
3045     for (uint32_t stage_index = 0; stage_index < create_info.stageCount; stage_index++) {
3046         skip |= ValidatePipelineShaderStage(pipeline, pipeline->stage_state[stage_index], false);
3047     }
3048 
3049     if ((create_info.flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) == 0) {
3050         const uint32_t raygen_stages_count = CalcShaderStageCount(pipeline, VK_SHADER_STAGE_RAYGEN_BIT_KHR);
3051         if (raygen_stages_count == 0) {
3052             skip |= LogError(
3053                 device,
3054                 isKHR ? "VUID-VkRayTracingPipelineCreateInfoKHR-stage-03425" : "VUID-VkRayTracingPipelineCreateInfoNV-stage-06232",
3055                 " : The stage member of at least one element of pStages must be VK_SHADER_STAGE_RAYGEN_BIT_KHR.");
3056         }
3057     }
3058 
3059     for (uint32_t group_index = 0; group_index < create_info.groupCount; group_index++) {
3060         const auto &group = groups[group_index];
3061 
3062         if (group.type == VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV) {
3063             if (!GroupHasValidIndex(
3064                     pipeline, group.generalShader,
3065                     VK_SHADER_STAGE_RAYGEN_BIT_NV | VK_SHADER_STAGE_MISS_BIT_NV | VK_SHADER_STAGE_CALLABLE_BIT_NV)) {
3066                 skip |= LogError(device,
3067                                  isKHR ? "VUID-VkRayTracingShaderGroupCreateInfoKHR-type-03474"
3068                                        : "VUID-VkRayTracingShaderGroupCreateInfoNV-type-02413",
3069                                  ": pGroups[%d]", group_index);
3070             }
3071             if (group.anyHitShader != VK_SHADER_UNUSED_NV || group.closestHitShader != VK_SHADER_UNUSED_NV ||
3072                 group.intersectionShader != VK_SHADER_UNUSED_NV) {
3073                 skip |= LogError(device,
3074                                  isKHR ? "VUID-VkRayTracingShaderGroupCreateInfoKHR-type-03475"
3075                                        : "VUID-VkRayTracingShaderGroupCreateInfoNV-type-02414",
3076                                  ": pGroups[%d]", group_index);
3077             }
3078         } else if (group.type == VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV) {
3079             if (!GroupHasValidIndex(pipeline, group.intersectionShader, VK_SHADER_STAGE_INTERSECTION_BIT_NV)) {
3080                 skip |= LogError(device,
3081                                  isKHR ? "VUID-VkRayTracingShaderGroupCreateInfoKHR-type-03476"
3082                                        : "VUID-VkRayTracingShaderGroupCreateInfoNV-type-02415",
3083                                  ": pGroups[%d]", group_index);
3084             }
3085         } else if (group.type == VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV) {
3086             if (group.intersectionShader != VK_SHADER_UNUSED_NV) {
3087                 skip |= LogError(device,
3088                                  isKHR ? "VUID-VkRayTracingShaderGroupCreateInfoKHR-type-03477"
3089                                        : "VUID-VkRayTracingShaderGroupCreateInfoNV-type-02416",
3090                                  ": pGroups[%d]", group_index);
3091             }
3092         }
3093 
3094         if (group.type == VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV ||
3095             group.type == VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV) {
3096             if (!GroupHasValidIndex(pipeline, group.anyHitShader, VK_SHADER_STAGE_ANY_HIT_BIT_NV)) {
3097                 skip |= LogError(device,
3098                                  isKHR ? "VUID-VkRayTracingShaderGroupCreateInfoKHR-anyHitShader-03479"
3099                                        : "VUID-VkRayTracingShaderGroupCreateInfoNV-anyHitShader-02418",
3100                                  ": pGroups[%d]", group_index);
3101             }
3102             if (!GroupHasValidIndex(pipeline, group.closestHitShader, VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV)) {
3103                 skip |= LogError(device,
3104                                  isKHR ? "VUID-VkRayTracingShaderGroupCreateInfoKHR-closestHitShader-03478"
3105                                        : "VUID-VkRayTracingShaderGroupCreateInfoNV-closestHitShader-02417",
3106                                  ": pGroups[%d]", group_index);
3107             }
3108         }
3109     }
3110     return skip;
3111 }
3112 
MakeShaderHash(VkShaderModuleCreateInfo const * smci)3113 uint32_t ValidationCache::MakeShaderHash(VkShaderModuleCreateInfo const *smci) { return XXH32(smci->pCode, smci->codeSize, 0); }
3114 
GetValidationCacheInfo(VkShaderModuleCreateInfo const * pCreateInfo)3115 static ValidationCache *GetValidationCacheInfo(VkShaderModuleCreateInfo const *pCreateInfo) {
3116     const auto validation_cache_ci = LvlFindInChain<VkShaderModuleValidationCacheCreateInfoEXT>(pCreateInfo->pNext);
3117     if (validation_cache_ci) {
3118         return CastFromHandle<ValidationCache *>(validation_cache_ci->validationCache);
3119     }
3120     return nullptr;
3121 }
3122 
PreCallValidateCreateShaderModule(VkDevice device,const VkShaderModuleCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkShaderModule * pShaderModule) const3123 bool CoreChecks::PreCallValidateCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo *pCreateInfo,
3124                                                    const VkAllocationCallbacks *pAllocator, VkShaderModule *pShaderModule) const {
3125     bool skip = false;
3126     spv_result_t spv_valid = SPV_SUCCESS;
3127 
3128     if (disabled[shader_validation]) {
3129         return false;
3130     }
3131 
3132     auto have_glsl_shader = IsExtEnabled(device_extensions.vk_nv_glsl_shader);
3133 
3134     if (!have_glsl_shader && (pCreateInfo->codeSize % 4)) {
3135         skip |= LogError(device, "VUID-VkShaderModuleCreateInfo-pCode-01376",
3136                          "SPIR-V module not valid: Codesize must be a multiple of 4 but is " PRINTF_SIZE_T_SPECIFIER ".",
3137                          pCreateInfo->codeSize);
3138     } else {
3139         auto cache = GetValidationCacheInfo(pCreateInfo);
3140         uint32_t hash = 0;
3141         // If app isn't using a shader validation cache, use the default one from CoreChecks
3142         if (!cache) cache = CastFromHandle<ValidationCache *>(core_validation_cache);
3143         if (cache) {
3144             hash = ValidationCache::MakeShaderHash(pCreateInfo);
3145             if (cache->Contains(hash)) return false;
3146         }
3147 
3148         // Use SPIRV-Tools validator to try and catch any issues with the module itself. If specialization constants are present,
3149         // the default values will be used during validation.
3150         spv_target_env spirv_environment = PickSpirvEnv(api_version, IsExtEnabled(device_extensions.vk_khr_spirv_1_4));
3151         spv_context ctx = spvContextCreate(spirv_environment);
3152         spv_const_binary_t binary{pCreateInfo->pCode, pCreateInfo->codeSize / sizeof(uint32_t)};
3153         spv_diagnostic diag = nullptr;
3154         spvtools::ValidatorOptions options;
3155         AdjustValidatorOptions(device_extensions, enabled_features, options);
3156         spv_valid = spvValidateWithOptions(ctx, options, &binary, &diag);
3157         if (spv_valid != SPV_SUCCESS) {
3158             if (!have_glsl_shader || (pCreateInfo->pCode[0] == spv::MagicNumber)) {
3159                 if (spv_valid == SPV_WARNING) {
3160                     skip |= LogWarning(device, kVUID_Core_Shader_InconsistentSpirv, "SPIR-V module not valid: %s",
3161                                        diag && diag->error ? diag->error : "(no error text)");
3162                 } else {
3163                     skip |= LogError(device, kVUID_Core_Shader_InconsistentSpirv, "SPIR-V module not valid: %s",
3164                                      diag && diag->error ? diag->error : "(no error text)");
3165                 }
3166             }
3167         } else {
3168             if (cache) {
3169                 cache->Insert(hash);
3170             }
3171         }
3172 
3173         spvDiagnosticDestroy(diag);
3174         spvContextDestroy(ctx);
3175     }
3176 
3177     return skip;
3178 }
3179 
ValidateComputeWorkGroupSizes(const SHADER_MODULE_STATE * shader,const spirv_inst_iter & entrypoint,const PipelineStageState & stage_state) const3180 bool CoreChecks::ValidateComputeWorkGroupSizes(const SHADER_MODULE_STATE *shader, const spirv_inst_iter &entrypoint,
3181                                                const PipelineStageState &stage_state) const {
3182     bool skip = false;
3183     uint32_t local_size_x = 0;
3184     uint32_t local_size_y = 0;
3185     uint32_t local_size_z = 0;
3186     if (shader->FindLocalSize(entrypoint, local_size_x, local_size_y, local_size_z)) {
3187         if (local_size_x > phys_dev_props.limits.maxComputeWorkGroupSize[0]) {
3188             skip |= LogError(shader->vk_shader_module(), "VUID-RuntimeSpirv-x-06429",
3189                              "%s local_size_x (%" PRIu32 ") exceeds device limit maxComputeWorkGroupSize[0] (%" PRIu32 ").",
3190                              report_data->FormatHandle(shader->vk_shader_module()).c_str(), local_size_x,
3191                              phys_dev_props.limits.maxComputeWorkGroupSize[0]);
3192         }
3193         if (local_size_y > phys_dev_props.limits.maxComputeWorkGroupSize[1]) {
3194             skip |= LogError(shader->vk_shader_module(), "VUID-RuntimeSpirv-y-06430",
3195                              "%s local_size_y (%" PRIu32 ") exceeds device limit maxComputeWorkGroupSize[1] (%" PRIu32 ").",
3196                              report_data->FormatHandle(shader->vk_shader_module()).c_str(), local_size_x,
3197                              phys_dev_props.limits.maxComputeWorkGroupSize[1]);
3198         }
3199         if (local_size_z > phys_dev_props.limits.maxComputeWorkGroupSize[2]) {
3200             skip |= LogError(shader->vk_shader_module(), "VUID-RuntimeSpirv-z-06431",
3201                              "%s local_size_z (%" PRIu32 ") exceeds device limit maxComputeWorkGroupSize[2] (%" PRIu32 ").",
3202                              report_data->FormatHandle(shader->vk_shader_module()).c_str(), local_size_x,
3203                              phys_dev_props.limits.maxComputeWorkGroupSize[2]);
3204         }
3205 
3206         uint32_t limit = phys_dev_props.limits.maxComputeWorkGroupInvocations;
3207         uint64_t invocations = local_size_x * local_size_y;
3208         // Prevent overflow.
3209         bool fail = false;
3210         if (invocations > UINT32_MAX || invocations > limit) {
3211             fail = true;
3212         }
3213         if (!fail) {
3214             invocations *= local_size_z;
3215             if (invocations > UINT32_MAX || invocations > limit) {
3216                 fail = true;
3217             }
3218         }
3219         if (fail) {
3220             skip |= LogError(shader->vk_shader_module(), "VUID-RuntimeSpirv-x-06432",
3221                              "%s local_size (%" PRIu32 ", %" PRIu32 ", %" PRIu32
3222                              ") exceeds device limit maxComputeWorkGroupInvocations (%" PRIu32 ").",
3223                              report_data->FormatHandle(shader->vk_shader_module()).c_str(), local_size_x, local_size_y,
3224                              local_size_z, limit);
3225         }
3226 
3227         const auto subgroup_flags = VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT |
3228                                     VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT;
3229         if ((stage_state.create_info->flags & subgroup_flags) == subgroup_flags) {
3230             if (SafeModulo(local_size_x, phys_dev_ext_props.subgroup_size_control_props.maxSubgroupSize) != 0) {
3231                 skip |= LogError(
3232                     shader->vk_shader_module(), "VUID-VkPipelineShaderStageCreateInfo-flags-02758",
3233                     "%s flags contain VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT and "
3234                     "VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT bits, but local workgroup size in the X "
3235                     "dimension (%" PRIu32
3236                     ") is not a multiple of VkPhysicalDeviceSubgroupSizeControlPropertiesEXT::maxSubgroupSize (%" PRIu32 ").",
3237                     report_data->FormatHandle(shader->vk_shader_module()).c_str(), local_size_x,
3238                     phys_dev_ext_props.subgroup_size_control_props.maxSubgroupSize);
3239             }
3240         } else if ((stage_state.create_info->flags & VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT) &&
3241             (stage_state.create_info->flags & VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) == 0) {
3242             const auto *required_subgroup_size_features =
3243                 LvlFindInChain<VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT>(stage_state.create_info->pNext);
3244             if (!required_subgroup_size_features) {
3245                 if (SafeModulo(local_size_x, phys_dev_props_core11.subgroupSize) != 0) {
3246                     skip |= LogError(
3247                         shader->vk_shader_module(), "VUID-VkPipelineShaderStageCreateInfo-flags-02759",
3248                         "%s flags contain VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT bit, and not the"
3249                         "VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT bit, but local workgroup size in the "
3250                         "X dimension (%" PRIu32 ") is not a multiple of VkPhysicalDeviceVulkan11Properties::subgroupSize (%" PRIu32
3251                         ").",
3252                         report_data->FormatHandle(shader->vk_shader_module()).c_str(), local_size_x,
3253                         phys_dev_props_core11.subgroupSize);
3254                 }
3255             }
3256         }
3257     }
3258     return skip;
3259 }
3260 
PickSpirvEnv(uint32_t api_version,bool spirv_1_4)3261 spv_target_env PickSpirvEnv(uint32_t api_version, bool spirv_1_4) {
3262     if (api_version >= VK_API_VERSION_1_2) {
3263         return SPV_ENV_VULKAN_1_2;
3264     } else if (api_version >= VK_API_VERSION_1_1) {
3265         if (spirv_1_4) {
3266             return SPV_ENV_VULKAN_1_1_SPIRV_1_4;
3267         } else {
3268             return SPV_ENV_VULKAN_1_1;
3269         }
3270     }
3271     return SPV_ENV_VULKAN_1_0;
3272 }
3273 
3274 // Some Vulkan extensions/features are just all done in spirv-val behind optional settings
AdjustValidatorOptions(const DeviceExtensions & device_extensions,const DeviceFeatures & enabled_features,spvtools::ValidatorOptions & options)3275 void AdjustValidatorOptions(const DeviceExtensions &device_extensions, const DeviceFeatures &enabled_features,
3276                             spvtools::ValidatorOptions &options) {
3277     // VK_KHR_relaxed_block_layout never had a feature bit so just enabling the extension allows relaxed layout
3278     // Was promotoed in Vulkan 1.1 so anyone using Vulkan 1.1 also gets this for free
3279     if (IsExtEnabled(device_extensions.vk_khr_relaxed_block_layout)) {
3280         // --relax-block-layout
3281         options.SetRelaxBlockLayout(true);
3282     }
3283 
3284     // The rest of the settings are controlled from a feature bit, which are set correctly in the state tracking. Regardless of
3285     // Vulkan version used, the feature bit is needed (also described in the spec).
3286 
3287     if (enabled_features.core12.uniformBufferStandardLayout == VK_TRUE) {
3288         // --uniform-buffer-standard-layout
3289         options.SetUniformBufferStandardLayout(true);
3290     }
3291     if (enabled_features.core12.scalarBlockLayout == VK_TRUE) {
3292         // --scalar-block-layout
3293         options.SetScalarBlockLayout(true);
3294     }
3295     if (enabled_features.workgroup_memory_explicit_layout_features.workgroupMemoryExplicitLayoutScalarBlockLayout) {
3296         // --workgroup-scalar-block-layout
3297         options.SetWorkgroupScalarBlockLayout(true);
3298     }
3299     if (enabled_features.maintenance4_features.maintenance4) {
3300         // --allow-localsizeid
3301         options.SetAllowLocalSizeId(true);
3302     }
3303 }
3304