1*b7893ccfSSadaf Ebrahimi /* Copyright (c) 2015-2019 The Khronos Group Inc.
2*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2019 Valve Corporation
3*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2019 LunarG, Inc.
4*b7893ccfSSadaf Ebrahimi * Copyright (C) 2015-2019 Google Inc.
5*b7893ccfSSadaf Ebrahimi *
6*b7893ccfSSadaf Ebrahimi * Licensed under the Apache License, Version 2.0 (the "License");
7*b7893ccfSSadaf Ebrahimi * you may not use this file except in compliance with the License.
8*b7893ccfSSadaf Ebrahimi * You may obtain a copy of the License at
9*b7893ccfSSadaf Ebrahimi *
10*b7893ccfSSadaf Ebrahimi * http://www.apache.org/licenses/LICENSE-2.0
11*b7893ccfSSadaf Ebrahimi *
12*b7893ccfSSadaf Ebrahimi * Unless required by applicable law or agreed to in writing, software
13*b7893ccfSSadaf Ebrahimi * distributed under the License is distributed on an "AS IS" BASIS,
14*b7893ccfSSadaf Ebrahimi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*b7893ccfSSadaf Ebrahimi * See the License for the specific language governing permissions and
16*b7893ccfSSadaf Ebrahimi * limitations under the License.
17*b7893ccfSSadaf Ebrahimi *
18*b7893ccfSSadaf Ebrahimi * Author: Chris Forbes <[email protected]>
19*b7893ccfSSadaf Ebrahimi * Author: Dave Houlton <[email protected]>
20*b7893ccfSSadaf Ebrahimi */
21*b7893ccfSSadaf Ebrahimi
22*b7893ccfSSadaf Ebrahimi #include <cinttypes>
23*b7893ccfSSadaf Ebrahimi #include <cassert>
24*b7893ccfSSadaf Ebrahimi #include <chrono>
25*b7893ccfSSadaf Ebrahimi #include <vector>
26*b7893ccfSSadaf Ebrahimi #include <unordered_map>
27*b7893ccfSSadaf Ebrahimi #include <string>
28*b7893ccfSSadaf Ebrahimi #include <sstream>
29*b7893ccfSSadaf Ebrahimi #include <SPIRV/spirv.hpp>
30*b7893ccfSSadaf Ebrahimi #include "vk_loader_platform.h"
31*b7893ccfSSadaf Ebrahimi #include "vk_enum_string_helper.h"
32*b7893ccfSSadaf Ebrahimi #include "vk_layer_data.h"
33*b7893ccfSSadaf Ebrahimi #include "vk_layer_extension_utils.h"
34*b7893ccfSSadaf Ebrahimi #include "vk_layer_utils.h"
35*b7893ccfSSadaf Ebrahimi #include "chassis.h"
36*b7893ccfSSadaf Ebrahimi #include "core_validation.h"
37*b7893ccfSSadaf Ebrahimi #include "shader_validation.h"
38*b7893ccfSSadaf Ebrahimi #include "spirv-tools/libspirv.h"
39*b7893ccfSSadaf Ebrahimi #include "xxhash.h"
40*b7893ccfSSadaf Ebrahimi
add(uint32_t decoration,uint32_t value)41*b7893ccfSSadaf Ebrahimi void decoration_set::add(uint32_t decoration, uint32_t value) {
42*b7893ccfSSadaf Ebrahimi switch (decoration) {
43*b7893ccfSSadaf Ebrahimi case spv::DecorationLocation:
44*b7893ccfSSadaf Ebrahimi flags |= location_bit;
45*b7893ccfSSadaf Ebrahimi location = value;
46*b7893ccfSSadaf Ebrahimi break;
47*b7893ccfSSadaf Ebrahimi case spv::DecorationPatch:
48*b7893ccfSSadaf Ebrahimi flags |= patch_bit;
49*b7893ccfSSadaf Ebrahimi break;
50*b7893ccfSSadaf Ebrahimi case spv::DecorationRelaxedPrecision:
51*b7893ccfSSadaf Ebrahimi flags |= relaxed_precision_bit;
52*b7893ccfSSadaf Ebrahimi break;
53*b7893ccfSSadaf Ebrahimi case spv::DecorationBlock:
54*b7893ccfSSadaf Ebrahimi flags |= block_bit;
55*b7893ccfSSadaf Ebrahimi break;
56*b7893ccfSSadaf Ebrahimi case spv::DecorationBufferBlock:
57*b7893ccfSSadaf Ebrahimi flags |= buffer_block_bit;
58*b7893ccfSSadaf Ebrahimi break;
59*b7893ccfSSadaf Ebrahimi case spv::DecorationComponent:
60*b7893ccfSSadaf Ebrahimi flags |= component_bit;
61*b7893ccfSSadaf Ebrahimi component = value;
62*b7893ccfSSadaf Ebrahimi break;
63*b7893ccfSSadaf Ebrahimi case spv::DecorationInputAttachmentIndex:
64*b7893ccfSSadaf Ebrahimi flags |= input_attachment_index_bit;
65*b7893ccfSSadaf Ebrahimi input_attachment_index = value;
66*b7893ccfSSadaf Ebrahimi break;
67*b7893ccfSSadaf Ebrahimi case spv::DecorationDescriptorSet:
68*b7893ccfSSadaf Ebrahimi flags |= descriptor_set_bit;
69*b7893ccfSSadaf Ebrahimi descriptor_set = value;
70*b7893ccfSSadaf Ebrahimi break;
71*b7893ccfSSadaf Ebrahimi case spv::DecorationBinding:
72*b7893ccfSSadaf Ebrahimi flags |= binding_bit;
73*b7893ccfSSadaf Ebrahimi binding = value;
74*b7893ccfSSadaf Ebrahimi break;
75*b7893ccfSSadaf Ebrahimi case spv::DecorationNonWritable:
76*b7893ccfSSadaf Ebrahimi flags |= nonwritable_bit;
77*b7893ccfSSadaf Ebrahimi break;
78*b7893ccfSSadaf Ebrahimi case spv::DecorationBuiltIn:
79*b7893ccfSSadaf Ebrahimi flags |= builtin_bit;
80*b7893ccfSSadaf Ebrahimi builtin = value;
81*b7893ccfSSadaf Ebrahimi break;
82*b7893ccfSSadaf Ebrahimi }
83*b7893ccfSSadaf Ebrahimi }
84*b7893ccfSSadaf Ebrahimi
85*b7893ccfSSadaf Ebrahimi enum FORMAT_TYPE {
86*b7893ccfSSadaf Ebrahimi FORMAT_TYPE_FLOAT = 1, // UNORM, SNORM, FLOAT, USCALED, SSCALED, SRGB -- anything we consider float in the shader
87*b7893ccfSSadaf Ebrahimi FORMAT_TYPE_SINT = 2,
88*b7893ccfSSadaf Ebrahimi FORMAT_TYPE_UINT = 4,
89*b7893ccfSSadaf Ebrahimi };
90*b7893ccfSSadaf Ebrahimi
91*b7893ccfSSadaf Ebrahimi typedef std::pair<unsigned, unsigned> location_t;
92*b7893ccfSSadaf Ebrahimi
93*b7893ccfSSadaf Ebrahimi struct shader_stage_attributes {
94*b7893ccfSSadaf Ebrahimi char const *const name;
95*b7893ccfSSadaf Ebrahimi bool arrayed_input;
96*b7893ccfSSadaf Ebrahimi bool arrayed_output;
97*b7893ccfSSadaf Ebrahimi VkShaderStageFlags stage;
98*b7893ccfSSadaf Ebrahimi };
99*b7893ccfSSadaf Ebrahimi
100*b7893ccfSSadaf Ebrahimi static shader_stage_attributes shader_stage_attribs[] = {
101*b7893ccfSSadaf Ebrahimi {"vertex shader", false, false, VK_SHADER_STAGE_VERTEX_BIT},
102*b7893ccfSSadaf Ebrahimi {"tessellation control shader", true, true, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT},
103*b7893ccfSSadaf Ebrahimi {"tessellation evaluation shader", true, false, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT},
104*b7893ccfSSadaf Ebrahimi {"geometry shader", true, false, VK_SHADER_STAGE_GEOMETRY_BIT},
105*b7893ccfSSadaf Ebrahimi {"fragment shader", false, false, VK_SHADER_STAGE_FRAGMENT_BIT},
106*b7893ccfSSadaf Ebrahimi };
107*b7893ccfSSadaf Ebrahimi
108*b7893ccfSSadaf Ebrahimi unsigned ExecutionModelToShaderStageFlagBits(unsigned mode);
109*b7893ccfSSadaf Ebrahimi
110*b7893ccfSSadaf Ebrahimi // SPIRV utility functions
BuildDefIndex()111*b7893ccfSSadaf Ebrahimi void SHADER_MODULE_STATE::BuildDefIndex() {
112*b7893ccfSSadaf Ebrahimi for (auto insn : *this) {
113*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
114*b7893ccfSSadaf Ebrahimi // Types
115*b7893ccfSSadaf Ebrahimi case spv::OpTypeVoid:
116*b7893ccfSSadaf Ebrahimi case spv::OpTypeBool:
117*b7893ccfSSadaf Ebrahimi case spv::OpTypeInt:
118*b7893ccfSSadaf Ebrahimi case spv::OpTypeFloat:
119*b7893ccfSSadaf Ebrahimi case spv::OpTypeVector:
120*b7893ccfSSadaf Ebrahimi case spv::OpTypeMatrix:
121*b7893ccfSSadaf Ebrahimi case spv::OpTypeImage:
122*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampler:
123*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampledImage:
124*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
125*b7893ccfSSadaf Ebrahimi case spv::OpTypeRuntimeArray:
126*b7893ccfSSadaf Ebrahimi case spv::OpTypeStruct:
127*b7893ccfSSadaf Ebrahimi case spv::OpTypeOpaque:
128*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
129*b7893ccfSSadaf Ebrahimi case spv::OpTypeFunction:
130*b7893ccfSSadaf Ebrahimi case spv::OpTypeEvent:
131*b7893ccfSSadaf Ebrahimi case spv::OpTypeDeviceEvent:
132*b7893ccfSSadaf Ebrahimi case spv::OpTypeReserveId:
133*b7893ccfSSadaf Ebrahimi case spv::OpTypeQueue:
134*b7893ccfSSadaf Ebrahimi case spv::OpTypePipe:
135*b7893ccfSSadaf Ebrahimi case spv::OpTypeAccelerationStructureNV:
136*b7893ccfSSadaf Ebrahimi case spv::OpTypeCooperativeMatrixNV:
137*b7893ccfSSadaf Ebrahimi def_index[insn.word(1)] = insn.offset();
138*b7893ccfSSadaf Ebrahimi break;
139*b7893ccfSSadaf Ebrahimi
140*b7893ccfSSadaf Ebrahimi // Fixed constants
141*b7893ccfSSadaf Ebrahimi case spv::OpConstantTrue:
142*b7893ccfSSadaf Ebrahimi case spv::OpConstantFalse:
143*b7893ccfSSadaf Ebrahimi case spv::OpConstant:
144*b7893ccfSSadaf Ebrahimi case spv::OpConstantComposite:
145*b7893ccfSSadaf Ebrahimi case spv::OpConstantSampler:
146*b7893ccfSSadaf Ebrahimi case spv::OpConstantNull:
147*b7893ccfSSadaf Ebrahimi def_index[insn.word(2)] = insn.offset();
148*b7893ccfSSadaf Ebrahimi break;
149*b7893ccfSSadaf Ebrahimi
150*b7893ccfSSadaf Ebrahimi // Specialization constants
151*b7893ccfSSadaf Ebrahimi case spv::OpSpecConstantTrue:
152*b7893ccfSSadaf Ebrahimi case spv::OpSpecConstantFalse:
153*b7893ccfSSadaf Ebrahimi case spv::OpSpecConstant:
154*b7893ccfSSadaf Ebrahimi case spv::OpSpecConstantComposite:
155*b7893ccfSSadaf Ebrahimi case spv::OpSpecConstantOp:
156*b7893ccfSSadaf Ebrahimi def_index[insn.word(2)] = insn.offset();
157*b7893ccfSSadaf Ebrahimi break;
158*b7893ccfSSadaf Ebrahimi
159*b7893ccfSSadaf Ebrahimi // Variables
160*b7893ccfSSadaf Ebrahimi case spv::OpVariable:
161*b7893ccfSSadaf Ebrahimi def_index[insn.word(2)] = insn.offset();
162*b7893ccfSSadaf Ebrahimi break;
163*b7893ccfSSadaf Ebrahimi
164*b7893ccfSSadaf Ebrahimi // Functions
165*b7893ccfSSadaf Ebrahimi case spv::OpFunction:
166*b7893ccfSSadaf Ebrahimi def_index[insn.word(2)] = insn.offset();
167*b7893ccfSSadaf Ebrahimi break;
168*b7893ccfSSadaf Ebrahimi
169*b7893ccfSSadaf Ebrahimi // Decorations
170*b7893ccfSSadaf Ebrahimi case spv::OpDecorate: {
171*b7893ccfSSadaf Ebrahimi auto targetId = insn.word(1);
172*b7893ccfSSadaf Ebrahimi decorations[targetId].add(insn.word(2), insn.len() > 3u ? insn.word(3) : 0u);
173*b7893ccfSSadaf Ebrahimi } break;
174*b7893ccfSSadaf Ebrahimi case spv::OpGroupDecorate: {
175*b7893ccfSSadaf Ebrahimi auto const &src = decorations[insn.word(1)];
176*b7893ccfSSadaf Ebrahimi for (auto i = 2u; i < insn.len(); i++) decorations[insn.word(i)].merge(src);
177*b7893ccfSSadaf Ebrahimi } break;
178*b7893ccfSSadaf Ebrahimi
179*b7893ccfSSadaf Ebrahimi // Entry points ... add to the entrypoint table
180*b7893ccfSSadaf Ebrahimi case spv::OpEntryPoint: {
181*b7893ccfSSadaf Ebrahimi // Entry points do not have an id (the id is the function id) and thus need their own table
182*b7893ccfSSadaf Ebrahimi auto entrypoint_name = (char const *)&insn.word(3);
183*b7893ccfSSadaf Ebrahimi auto execution_model = insn.word(1);
184*b7893ccfSSadaf Ebrahimi auto entrypoint_stage = ExecutionModelToShaderStageFlagBits(execution_model);
185*b7893ccfSSadaf Ebrahimi entry_points.emplace(entrypoint_name, EntryPoint{insn.offset(), entrypoint_stage});
186*b7893ccfSSadaf Ebrahimi break;
187*b7893ccfSSadaf Ebrahimi }
188*b7893ccfSSadaf Ebrahimi
189*b7893ccfSSadaf Ebrahimi default:
190*b7893ccfSSadaf Ebrahimi // We don't care about any other defs for now.
191*b7893ccfSSadaf Ebrahimi break;
192*b7893ccfSSadaf Ebrahimi }
193*b7893ccfSSadaf Ebrahimi }
194*b7893ccfSSadaf Ebrahimi }
195*b7893ccfSSadaf Ebrahimi
ExecutionModelToShaderStageFlagBits(unsigned mode)196*b7893ccfSSadaf Ebrahimi unsigned ExecutionModelToShaderStageFlagBits(unsigned mode) {
197*b7893ccfSSadaf Ebrahimi switch (mode) {
198*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelVertex:
199*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_VERTEX_BIT;
200*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelTessellationControl:
201*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
202*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelTessellationEvaluation:
203*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
204*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelGeometry:
205*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_GEOMETRY_BIT;
206*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelFragment:
207*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_FRAGMENT_BIT;
208*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelGLCompute:
209*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_COMPUTE_BIT;
210*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelRayGenerationNV:
211*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_RAYGEN_BIT_NV;
212*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelAnyHitNV:
213*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_ANY_HIT_BIT_NV;
214*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelClosestHitNV:
215*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV;
216*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelMissNV:
217*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_MISS_BIT_NV;
218*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelIntersectionNV:
219*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_INTERSECTION_BIT_NV;
220*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelCallableNV:
221*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_CALLABLE_BIT_NV;
222*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelTaskNV:
223*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_TASK_BIT_NV;
224*b7893ccfSSadaf Ebrahimi case spv::ExecutionModelMeshNV:
225*b7893ccfSSadaf Ebrahimi return VK_SHADER_STAGE_MESH_BIT_NV;
226*b7893ccfSSadaf Ebrahimi default:
227*b7893ccfSSadaf Ebrahimi return 0;
228*b7893ccfSSadaf Ebrahimi }
229*b7893ccfSSadaf Ebrahimi }
230*b7893ccfSSadaf Ebrahimi
FindEntrypoint(SHADER_MODULE_STATE const * src,char const * name,VkShaderStageFlagBits stageBits)231*b7893ccfSSadaf Ebrahimi static spirv_inst_iter FindEntrypoint(SHADER_MODULE_STATE const *src, char const *name, VkShaderStageFlagBits stageBits) {
232*b7893ccfSSadaf Ebrahimi auto range = src->entry_points.equal_range(name);
233*b7893ccfSSadaf Ebrahimi for (auto it = range.first; it != range.second; ++it) {
234*b7893ccfSSadaf Ebrahimi if (it->second.stage == stageBits) {
235*b7893ccfSSadaf Ebrahimi return src->at(it->second.offset);
236*b7893ccfSSadaf Ebrahimi }
237*b7893ccfSSadaf Ebrahimi }
238*b7893ccfSSadaf Ebrahimi return src->end();
239*b7893ccfSSadaf Ebrahimi }
240*b7893ccfSSadaf Ebrahimi
StorageClassName(unsigned sc)241*b7893ccfSSadaf Ebrahimi static char const *StorageClassName(unsigned sc) {
242*b7893ccfSSadaf Ebrahimi switch (sc) {
243*b7893ccfSSadaf Ebrahimi case spv::StorageClassInput:
244*b7893ccfSSadaf Ebrahimi return "input";
245*b7893ccfSSadaf Ebrahimi case spv::StorageClassOutput:
246*b7893ccfSSadaf Ebrahimi return "output";
247*b7893ccfSSadaf Ebrahimi case spv::StorageClassUniformConstant:
248*b7893ccfSSadaf Ebrahimi return "const uniform";
249*b7893ccfSSadaf Ebrahimi case spv::StorageClassUniform:
250*b7893ccfSSadaf Ebrahimi return "uniform";
251*b7893ccfSSadaf Ebrahimi case spv::StorageClassWorkgroup:
252*b7893ccfSSadaf Ebrahimi return "workgroup local";
253*b7893ccfSSadaf Ebrahimi case spv::StorageClassCrossWorkgroup:
254*b7893ccfSSadaf Ebrahimi return "workgroup global";
255*b7893ccfSSadaf Ebrahimi case spv::StorageClassPrivate:
256*b7893ccfSSadaf Ebrahimi return "private global";
257*b7893ccfSSadaf Ebrahimi case spv::StorageClassFunction:
258*b7893ccfSSadaf Ebrahimi return "function";
259*b7893ccfSSadaf Ebrahimi case spv::StorageClassGeneric:
260*b7893ccfSSadaf Ebrahimi return "generic";
261*b7893ccfSSadaf Ebrahimi case spv::StorageClassAtomicCounter:
262*b7893ccfSSadaf Ebrahimi return "atomic counter";
263*b7893ccfSSadaf Ebrahimi case spv::StorageClassImage:
264*b7893ccfSSadaf Ebrahimi return "image";
265*b7893ccfSSadaf Ebrahimi case spv::StorageClassPushConstant:
266*b7893ccfSSadaf Ebrahimi return "push constant";
267*b7893ccfSSadaf Ebrahimi case spv::StorageClassStorageBuffer:
268*b7893ccfSSadaf Ebrahimi return "storage buffer";
269*b7893ccfSSadaf Ebrahimi default:
270*b7893ccfSSadaf Ebrahimi return "unknown";
271*b7893ccfSSadaf Ebrahimi }
272*b7893ccfSSadaf Ebrahimi }
273*b7893ccfSSadaf Ebrahimi
274*b7893ccfSSadaf Ebrahimi // Get the value of an integral constant
GetConstantValue(SHADER_MODULE_STATE const * src,unsigned id)275*b7893ccfSSadaf Ebrahimi unsigned GetConstantValue(SHADER_MODULE_STATE const *src, unsigned id) {
276*b7893ccfSSadaf Ebrahimi auto value = src->get_def(id);
277*b7893ccfSSadaf Ebrahimi assert(value != src->end());
278*b7893ccfSSadaf Ebrahimi
279*b7893ccfSSadaf Ebrahimi if (value.opcode() != spv::OpConstant) {
280*b7893ccfSSadaf Ebrahimi // TODO: Either ensure that the specialization transform is already performed on a module we're
281*b7893ccfSSadaf Ebrahimi // considering here, OR -- specialize on the fly now.
282*b7893ccfSSadaf Ebrahimi return 1;
283*b7893ccfSSadaf Ebrahimi }
284*b7893ccfSSadaf Ebrahimi
285*b7893ccfSSadaf Ebrahimi return value.word(3);
286*b7893ccfSSadaf Ebrahimi }
287*b7893ccfSSadaf Ebrahimi
DescribeTypeInner(std::ostringstream & ss,SHADER_MODULE_STATE const * src,unsigned type)288*b7893ccfSSadaf Ebrahimi static void DescribeTypeInner(std::ostringstream &ss, SHADER_MODULE_STATE const *src, unsigned type) {
289*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(type);
290*b7893ccfSSadaf Ebrahimi assert(insn != src->end());
291*b7893ccfSSadaf Ebrahimi
292*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
293*b7893ccfSSadaf Ebrahimi case spv::OpTypeBool:
294*b7893ccfSSadaf Ebrahimi ss << "bool";
295*b7893ccfSSadaf Ebrahimi break;
296*b7893ccfSSadaf Ebrahimi case spv::OpTypeInt:
297*b7893ccfSSadaf Ebrahimi ss << (insn.word(3) ? 's' : 'u') << "int" << insn.word(2);
298*b7893ccfSSadaf Ebrahimi break;
299*b7893ccfSSadaf Ebrahimi case spv::OpTypeFloat:
300*b7893ccfSSadaf Ebrahimi ss << "float" << insn.word(2);
301*b7893ccfSSadaf Ebrahimi break;
302*b7893ccfSSadaf Ebrahimi case spv::OpTypeVector:
303*b7893ccfSSadaf Ebrahimi ss << "vec" << insn.word(3) << " of ";
304*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(2));
305*b7893ccfSSadaf Ebrahimi break;
306*b7893ccfSSadaf Ebrahimi case spv::OpTypeMatrix:
307*b7893ccfSSadaf Ebrahimi ss << "mat" << insn.word(3) << " of ";
308*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(2));
309*b7893ccfSSadaf Ebrahimi break;
310*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
311*b7893ccfSSadaf Ebrahimi ss << "arr[" << GetConstantValue(src, insn.word(3)) << "] of ";
312*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(2));
313*b7893ccfSSadaf Ebrahimi break;
314*b7893ccfSSadaf Ebrahimi case spv::OpTypeRuntimeArray:
315*b7893ccfSSadaf Ebrahimi ss << "runtime arr[] of ";
316*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(2));
317*b7893ccfSSadaf Ebrahimi break;
318*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
319*b7893ccfSSadaf Ebrahimi ss << "ptr to " << StorageClassName(insn.word(2)) << " ";
320*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(3));
321*b7893ccfSSadaf Ebrahimi break;
322*b7893ccfSSadaf Ebrahimi case spv::OpTypeStruct: {
323*b7893ccfSSadaf Ebrahimi ss << "struct of (";
324*b7893ccfSSadaf Ebrahimi for (unsigned i = 2; i < insn.len(); i++) {
325*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(i));
326*b7893ccfSSadaf Ebrahimi if (i == insn.len() - 1) {
327*b7893ccfSSadaf Ebrahimi ss << ")";
328*b7893ccfSSadaf Ebrahimi } else {
329*b7893ccfSSadaf Ebrahimi ss << ", ";
330*b7893ccfSSadaf Ebrahimi }
331*b7893ccfSSadaf Ebrahimi }
332*b7893ccfSSadaf Ebrahimi break;
333*b7893ccfSSadaf Ebrahimi }
334*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampler:
335*b7893ccfSSadaf Ebrahimi ss << "sampler";
336*b7893ccfSSadaf Ebrahimi break;
337*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampledImage:
338*b7893ccfSSadaf Ebrahimi ss << "sampler+";
339*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, insn.word(2));
340*b7893ccfSSadaf Ebrahimi break;
341*b7893ccfSSadaf Ebrahimi case spv::OpTypeImage:
342*b7893ccfSSadaf Ebrahimi ss << "image(dim=" << insn.word(3) << ", sampled=" << insn.word(7) << ")";
343*b7893ccfSSadaf Ebrahimi break;
344*b7893ccfSSadaf Ebrahimi case spv::OpTypeAccelerationStructureNV:
345*b7893ccfSSadaf Ebrahimi ss << "accelerationStruture";
346*b7893ccfSSadaf Ebrahimi break;
347*b7893ccfSSadaf Ebrahimi default:
348*b7893ccfSSadaf Ebrahimi ss << "oddtype";
349*b7893ccfSSadaf Ebrahimi break;
350*b7893ccfSSadaf Ebrahimi }
351*b7893ccfSSadaf Ebrahimi }
352*b7893ccfSSadaf Ebrahimi
DescribeType(SHADER_MODULE_STATE const * src,unsigned type)353*b7893ccfSSadaf Ebrahimi static std::string DescribeType(SHADER_MODULE_STATE const *src, unsigned type) {
354*b7893ccfSSadaf Ebrahimi std::ostringstream ss;
355*b7893ccfSSadaf Ebrahimi DescribeTypeInner(ss, src, type);
356*b7893ccfSSadaf Ebrahimi return ss.str();
357*b7893ccfSSadaf Ebrahimi }
358*b7893ccfSSadaf Ebrahimi
IsNarrowNumericType(spirv_inst_iter type)359*b7893ccfSSadaf Ebrahimi static bool IsNarrowNumericType(spirv_inst_iter type) {
360*b7893ccfSSadaf Ebrahimi if (type.opcode() != spv::OpTypeInt && type.opcode() != spv::OpTypeFloat) return false;
361*b7893ccfSSadaf Ebrahimi return type.word(2) < 64;
362*b7893ccfSSadaf Ebrahimi }
363*b7893ccfSSadaf Ebrahimi
TypesMatch(SHADER_MODULE_STATE const * a,SHADER_MODULE_STATE const * b,unsigned a_type,unsigned b_type,bool a_arrayed,bool b_arrayed,bool relaxed)364*b7893ccfSSadaf Ebrahimi static bool TypesMatch(SHADER_MODULE_STATE const *a, SHADER_MODULE_STATE const *b, unsigned a_type, unsigned b_type, bool a_arrayed,
365*b7893ccfSSadaf Ebrahimi bool b_arrayed, bool relaxed) {
366*b7893ccfSSadaf Ebrahimi // Walk two type trees together, and complain about differences
367*b7893ccfSSadaf Ebrahimi auto a_insn = a->get_def(a_type);
368*b7893ccfSSadaf Ebrahimi auto b_insn = b->get_def(b_type);
369*b7893ccfSSadaf Ebrahimi assert(a_insn != a->end());
370*b7893ccfSSadaf Ebrahimi assert(b_insn != b->end());
371*b7893ccfSSadaf Ebrahimi
372*b7893ccfSSadaf Ebrahimi // Ignore runtime-sized arrays-- they cannot appear in these interfaces.
373*b7893ccfSSadaf Ebrahimi
374*b7893ccfSSadaf Ebrahimi if (a_arrayed && a_insn.opcode() == spv::OpTypeArray) {
375*b7893ccfSSadaf Ebrahimi return TypesMatch(a, b, a_insn.word(2), b_type, false, b_arrayed, relaxed);
376*b7893ccfSSadaf Ebrahimi }
377*b7893ccfSSadaf Ebrahimi
378*b7893ccfSSadaf Ebrahimi if (b_arrayed && b_insn.opcode() == spv::OpTypeArray) {
379*b7893ccfSSadaf Ebrahimi // We probably just found the extra level of arrayness in b_type: compare the type inside it to a_type
380*b7893ccfSSadaf Ebrahimi return TypesMatch(a, b, a_type, b_insn.word(2), a_arrayed, false, relaxed);
381*b7893ccfSSadaf Ebrahimi }
382*b7893ccfSSadaf Ebrahimi
383*b7893ccfSSadaf Ebrahimi if (a_insn.opcode() == spv::OpTypeVector && relaxed && IsNarrowNumericType(b_insn)) {
384*b7893ccfSSadaf Ebrahimi return TypesMatch(a, b, a_insn.word(2), b_type, a_arrayed, b_arrayed, false);
385*b7893ccfSSadaf Ebrahimi }
386*b7893ccfSSadaf Ebrahimi
387*b7893ccfSSadaf Ebrahimi if (a_insn.opcode() != b_insn.opcode()) {
388*b7893ccfSSadaf Ebrahimi return false;
389*b7893ccfSSadaf Ebrahimi }
390*b7893ccfSSadaf Ebrahimi
391*b7893ccfSSadaf Ebrahimi if (a_insn.opcode() == spv::OpTypePointer) {
392*b7893ccfSSadaf Ebrahimi // Match on pointee type. storage class is expected to differ
393*b7893ccfSSadaf Ebrahimi return TypesMatch(a, b, a_insn.word(3), b_insn.word(3), a_arrayed, b_arrayed, relaxed);
394*b7893ccfSSadaf Ebrahimi }
395*b7893ccfSSadaf Ebrahimi
396*b7893ccfSSadaf Ebrahimi if (a_arrayed || b_arrayed) {
397*b7893ccfSSadaf Ebrahimi // If we havent resolved array-of-verts by here, we're not going to.
398*b7893ccfSSadaf Ebrahimi return false;
399*b7893ccfSSadaf Ebrahimi }
400*b7893ccfSSadaf Ebrahimi
401*b7893ccfSSadaf Ebrahimi switch (a_insn.opcode()) {
402*b7893ccfSSadaf Ebrahimi case spv::OpTypeBool:
403*b7893ccfSSadaf Ebrahimi return true;
404*b7893ccfSSadaf Ebrahimi case spv::OpTypeInt:
405*b7893ccfSSadaf Ebrahimi // Match on width, signedness
406*b7893ccfSSadaf Ebrahimi return a_insn.word(2) == b_insn.word(2) && a_insn.word(3) == b_insn.word(3);
407*b7893ccfSSadaf Ebrahimi case spv::OpTypeFloat:
408*b7893ccfSSadaf Ebrahimi // Match on width
409*b7893ccfSSadaf Ebrahimi return a_insn.word(2) == b_insn.word(2);
410*b7893ccfSSadaf Ebrahimi case spv::OpTypeVector:
411*b7893ccfSSadaf Ebrahimi // Match on element type, count.
412*b7893ccfSSadaf Ebrahimi if (!TypesMatch(a, b, a_insn.word(2), b_insn.word(2), a_arrayed, b_arrayed, false)) return false;
413*b7893ccfSSadaf Ebrahimi if (relaxed && IsNarrowNumericType(a->get_def(a_insn.word(2)))) {
414*b7893ccfSSadaf Ebrahimi return a_insn.word(3) >= b_insn.word(3);
415*b7893ccfSSadaf Ebrahimi } else {
416*b7893ccfSSadaf Ebrahimi return a_insn.word(3) == b_insn.word(3);
417*b7893ccfSSadaf Ebrahimi }
418*b7893ccfSSadaf Ebrahimi case spv::OpTypeMatrix:
419*b7893ccfSSadaf Ebrahimi // Match on element type, count.
420*b7893ccfSSadaf Ebrahimi return TypesMatch(a, b, a_insn.word(2), b_insn.word(2), a_arrayed, b_arrayed, false) &&
421*b7893ccfSSadaf Ebrahimi a_insn.word(3) == b_insn.word(3);
422*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
423*b7893ccfSSadaf Ebrahimi // Match on element type, count. these all have the same layout. we don't get here if b_arrayed. This differs from
424*b7893ccfSSadaf Ebrahimi // vector & matrix types in that the array size is the id of a constant instruction, * not a literal within OpTypeArray
425*b7893ccfSSadaf Ebrahimi return TypesMatch(a, b, a_insn.word(2), b_insn.word(2), a_arrayed, b_arrayed, false) &&
426*b7893ccfSSadaf Ebrahimi GetConstantValue(a, a_insn.word(3)) == GetConstantValue(b, b_insn.word(3));
427*b7893ccfSSadaf Ebrahimi case spv::OpTypeStruct:
428*b7893ccfSSadaf Ebrahimi // Match on all element types
429*b7893ccfSSadaf Ebrahimi {
430*b7893ccfSSadaf Ebrahimi if (a_insn.len() != b_insn.len()) {
431*b7893ccfSSadaf Ebrahimi return false; // Structs cannot match if member counts differ
432*b7893ccfSSadaf Ebrahimi }
433*b7893ccfSSadaf Ebrahimi
434*b7893ccfSSadaf Ebrahimi for (unsigned i = 2; i < a_insn.len(); i++) {
435*b7893ccfSSadaf Ebrahimi if (!TypesMatch(a, b, a_insn.word(i), b_insn.word(i), a_arrayed, b_arrayed, false)) {
436*b7893ccfSSadaf Ebrahimi return false;
437*b7893ccfSSadaf Ebrahimi }
438*b7893ccfSSadaf Ebrahimi }
439*b7893ccfSSadaf Ebrahimi
440*b7893ccfSSadaf Ebrahimi return true;
441*b7893ccfSSadaf Ebrahimi }
442*b7893ccfSSadaf Ebrahimi default:
443*b7893ccfSSadaf Ebrahimi // Remaining types are CLisms, or may not appear in the interfaces we are interested in. Just claim no match.
444*b7893ccfSSadaf Ebrahimi return false;
445*b7893ccfSSadaf Ebrahimi }
446*b7893ccfSSadaf Ebrahimi }
447*b7893ccfSSadaf Ebrahimi
ValueOrDefault(std::unordered_map<unsigned,unsigned> const & map,unsigned id,unsigned def)448*b7893ccfSSadaf Ebrahimi static unsigned ValueOrDefault(std::unordered_map<unsigned, unsigned> const &map, unsigned id, unsigned def) {
449*b7893ccfSSadaf Ebrahimi auto it = map.find(id);
450*b7893ccfSSadaf Ebrahimi if (it == map.end())
451*b7893ccfSSadaf Ebrahimi return def;
452*b7893ccfSSadaf Ebrahimi else
453*b7893ccfSSadaf Ebrahimi return it->second;
454*b7893ccfSSadaf Ebrahimi }
455*b7893ccfSSadaf Ebrahimi
GetLocationsConsumedByType(SHADER_MODULE_STATE const * src,unsigned type,bool strip_array_level)456*b7893ccfSSadaf Ebrahimi static unsigned GetLocationsConsumedByType(SHADER_MODULE_STATE const *src, unsigned type, bool strip_array_level) {
457*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(type);
458*b7893ccfSSadaf Ebrahimi assert(insn != src->end());
459*b7893ccfSSadaf Ebrahimi
460*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
461*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
462*b7893ccfSSadaf Ebrahimi // See through the ptr -- this is only ever at the toplevel for graphics shaders we're never actually passing
463*b7893ccfSSadaf Ebrahimi // pointers around.
464*b7893ccfSSadaf Ebrahimi return GetLocationsConsumedByType(src, insn.word(3), strip_array_level);
465*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
466*b7893ccfSSadaf Ebrahimi if (strip_array_level) {
467*b7893ccfSSadaf Ebrahimi return GetLocationsConsumedByType(src, insn.word(2), false);
468*b7893ccfSSadaf Ebrahimi } else {
469*b7893ccfSSadaf Ebrahimi return GetConstantValue(src, insn.word(3)) * GetLocationsConsumedByType(src, insn.word(2), false);
470*b7893ccfSSadaf Ebrahimi }
471*b7893ccfSSadaf Ebrahimi case spv::OpTypeMatrix:
472*b7893ccfSSadaf Ebrahimi // Num locations is the dimension * element size
473*b7893ccfSSadaf Ebrahimi return insn.word(3) * GetLocationsConsumedByType(src, insn.word(2), false);
474*b7893ccfSSadaf Ebrahimi case spv::OpTypeVector: {
475*b7893ccfSSadaf Ebrahimi auto scalar_type = src->get_def(insn.word(2));
476*b7893ccfSSadaf Ebrahimi auto bit_width =
477*b7893ccfSSadaf Ebrahimi (scalar_type.opcode() == spv::OpTypeInt || scalar_type.opcode() == spv::OpTypeFloat) ? scalar_type.word(2) : 32;
478*b7893ccfSSadaf Ebrahimi
479*b7893ccfSSadaf Ebrahimi // Locations are 128-bit wide; 3- and 4-component vectors of 64 bit types require two.
480*b7893ccfSSadaf Ebrahimi return (bit_width * insn.word(3) + 127) / 128;
481*b7893ccfSSadaf Ebrahimi }
482*b7893ccfSSadaf Ebrahimi default:
483*b7893ccfSSadaf Ebrahimi // Everything else is just 1.
484*b7893ccfSSadaf Ebrahimi return 1;
485*b7893ccfSSadaf Ebrahimi
486*b7893ccfSSadaf Ebrahimi // TODO: extend to handle 64bit scalar types, whose vectors may need multiple locations.
487*b7893ccfSSadaf Ebrahimi }
488*b7893ccfSSadaf Ebrahimi }
489*b7893ccfSSadaf Ebrahimi
GetComponentsConsumedByType(SHADER_MODULE_STATE const * src,unsigned type,bool strip_array_level)490*b7893ccfSSadaf Ebrahimi static unsigned GetComponentsConsumedByType(SHADER_MODULE_STATE const *src, unsigned type, bool strip_array_level) {
491*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(type);
492*b7893ccfSSadaf Ebrahimi assert(insn != src->end());
493*b7893ccfSSadaf Ebrahimi
494*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
495*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
496*b7893ccfSSadaf Ebrahimi // See through the ptr -- this is only ever at the toplevel for graphics shaders we're never actually passing
497*b7893ccfSSadaf Ebrahimi // pointers around.
498*b7893ccfSSadaf Ebrahimi return GetComponentsConsumedByType(src, insn.word(3), strip_array_level);
499*b7893ccfSSadaf Ebrahimi case spv::OpTypeStruct: {
500*b7893ccfSSadaf Ebrahimi uint32_t sum = 0;
501*b7893ccfSSadaf Ebrahimi for (uint32_t i = 2; i < insn.len(); i++) { // i=2 to skip word(0) and word(1)=ID of struct
502*b7893ccfSSadaf Ebrahimi sum += GetComponentsConsumedByType(src, insn.word(i), false);
503*b7893ccfSSadaf Ebrahimi }
504*b7893ccfSSadaf Ebrahimi return sum;
505*b7893ccfSSadaf Ebrahimi }
506*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
507*b7893ccfSSadaf Ebrahimi if (strip_array_level) {
508*b7893ccfSSadaf Ebrahimi return GetComponentsConsumedByType(src, insn.word(2), false);
509*b7893ccfSSadaf Ebrahimi } else {
510*b7893ccfSSadaf Ebrahimi return GetConstantValue(src, insn.word(3)) * GetComponentsConsumedByType(src, insn.word(2), false);
511*b7893ccfSSadaf Ebrahimi }
512*b7893ccfSSadaf Ebrahimi case spv::OpTypeMatrix:
513*b7893ccfSSadaf Ebrahimi // Num locations is the dimension * element size
514*b7893ccfSSadaf Ebrahimi return insn.word(3) * GetComponentsConsumedByType(src, insn.word(2), false);
515*b7893ccfSSadaf Ebrahimi case spv::OpTypeVector: {
516*b7893ccfSSadaf Ebrahimi auto scalar_type = src->get_def(insn.word(2));
517*b7893ccfSSadaf Ebrahimi auto bit_width =
518*b7893ccfSSadaf Ebrahimi (scalar_type.opcode() == spv::OpTypeInt || scalar_type.opcode() == spv::OpTypeFloat) ? scalar_type.word(2) : 32;
519*b7893ccfSSadaf Ebrahimi // One component is 32-bit
520*b7893ccfSSadaf Ebrahimi return (bit_width * insn.word(3) + 31) / 32;
521*b7893ccfSSadaf Ebrahimi }
522*b7893ccfSSadaf Ebrahimi case spv::OpTypeFloat: {
523*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(2);
524*b7893ccfSSadaf Ebrahimi return (bit_width + 31) / 32;
525*b7893ccfSSadaf Ebrahimi }
526*b7893ccfSSadaf Ebrahimi case spv::OpTypeInt: {
527*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(2);
528*b7893ccfSSadaf Ebrahimi return (bit_width + 31) / 32;
529*b7893ccfSSadaf Ebrahimi }
530*b7893ccfSSadaf Ebrahimi case spv::OpConstant:
531*b7893ccfSSadaf Ebrahimi return GetComponentsConsumedByType(src, insn.word(1), false);
532*b7893ccfSSadaf Ebrahimi default:
533*b7893ccfSSadaf Ebrahimi return 0;
534*b7893ccfSSadaf Ebrahimi }
535*b7893ccfSSadaf Ebrahimi }
536*b7893ccfSSadaf Ebrahimi
GetLocationsConsumedByFormat(VkFormat format)537*b7893ccfSSadaf Ebrahimi static unsigned GetLocationsConsumedByFormat(VkFormat format) {
538*b7893ccfSSadaf Ebrahimi switch (format) {
539*b7893ccfSSadaf Ebrahimi case VK_FORMAT_R64G64B64A64_SFLOAT:
540*b7893ccfSSadaf Ebrahimi case VK_FORMAT_R64G64B64A64_SINT:
541*b7893ccfSSadaf Ebrahimi case VK_FORMAT_R64G64B64A64_UINT:
542*b7893ccfSSadaf Ebrahimi case VK_FORMAT_R64G64B64_SFLOAT:
543*b7893ccfSSadaf Ebrahimi case VK_FORMAT_R64G64B64_SINT:
544*b7893ccfSSadaf Ebrahimi case VK_FORMAT_R64G64B64_UINT:
545*b7893ccfSSadaf Ebrahimi return 2;
546*b7893ccfSSadaf Ebrahimi default:
547*b7893ccfSSadaf Ebrahimi return 1;
548*b7893ccfSSadaf Ebrahimi }
549*b7893ccfSSadaf Ebrahimi }
550*b7893ccfSSadaf Ebrahimi
GetFormatType(VkFormat fmt)551*b7893ccfSSadaf Ebrahimi static unsigned GetFormatType(VkFormat fmt) {
552*b7893ccfSSadaf Ebrahimi if (FormatIsSInt(fmt)) return FORMAT_TYPE_SINT;
553*b7893ccfSSadaf Ebrahimi if (FormatIsUInt(fmt)) return FORMAT_TYPE_UINT;
554*b7893ccfSSadaf Ebrahimi if (FormatIsDepthAndStencil(fmt)) return FORMAT_TYPE_FLOAT | FORMAT_TYPE_UINT;
555*b7893ccfSSadaf Ebrahimi if (fmt == VK_FORMAT_UNDEFINED) return 0;
556*b7893ccfSSadaf Ebrahimi // everything else -- UNORM/SNORM/FLOAT/USCALED/SSCALED is all float in the shader.
557*b7893ccfSSadaf Ebrahimi return FORMAT_TYPE_FLOAT;
558*b7893ccfSSadaf Ebrahimi }
559*b7893ccfSSadaf Ebrahimi
560*b7893ccfSSadaf Ebrahimi // characterizes a SPIR-V type appearing in an interface to a FF stage, for comparison to a VkFormat's characterization above.
561*b7893ccfSSadaf Ebrahimi // also used for input attachments, as we statically know their format.
GetFundamentalType(SHADER_MODULE_STATE const * src,unsigned type)562*b7893ccfSSadaf Ebrahimi static unsigned GetFundamentalType(SHADER_MODULE_STATE const *src, unsigned type) {
563*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(type);
564*b7893ccfSSadaf Ebrahimi assert(insn != src->end());
565*b7893ccfSSadaf Ebrahimi
566*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
567*b7893ccfSSadaf Ebrahimi case spv::OpTypeInt:
568*b7893ccfSSadaf Ebrahimi return insn.word(3) ? FORMAT_TYPE_SINT : FORMAT_TYPE_UINT;
569*b7893ccfSSadaf Ebrahimi case spv::OpTypeFloat:
570*b7893ccfSSadaf Ebrahimi return FORMAT_TYPE_FLOAT;
571*b7893ccfSSadaf Ebrahimi case spv::OpTypeVector:
572*b7893ccfSSadaf Ebrahimi case spv::OpTypeMatrix:
573*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
574*b7893ccfSSadaf Ebrahimi case spv::OpTypeRuntimeArray:
575*b7893ccfSSadaf Ebrahimi case spv::OpTypeImage:
576*b7893ccfSSadaf Ebrahimi return GetFundamentalType(src, insn.word(2));
577*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
578*b7893ccfSSadaf Ebrahimi return GetFundamentalType(src, insn.word(3));
579*b7893ccfSSadaf Ebrahimi
580*b7893ccfSSadaf Ebrahimi default:
581*b7893ccfSSadaf Ebrahimi return 0;
582*b7893ccfSSadaf Ebrahimi }
583*b7893ccfSSadaf Ebrahimi }
584*b7893ccfSSadaf Ebrahimi
GetShaderStageId(VkShaderStageFlagBits stage)585*b7893ccfSSadaf Ebrahimi static uint32_t GetShaderStageId(VkShaderStageFlagBits stage) {
586*b7893ccfSSadaf Ebrahimi uint32_t bit_pos = uint32_t(u_ffs(stage));
587*b7893ccfSSadaf Ebrahimi return bit_pos - 1;
588*b7893ccfSSadaf Ebrahimi }
589*b7893ccfSSadaf Ebrahimi
GetStructType(SHADER_MODULE_STATE const * src,spirv_inst_iter def,bool is_array_of_verts)590*b7893ccfSSadaf Ebrahimi static spirv_inst_iter GetStructType(SHADER_MODULE_STATE const *src, spirv_inst_iter def, bool is_array_of_verts) {
591*b7893ccfSSadaf Ebrahimi while (true) {
592*b7893ccfSSadaf Ebrahimi if (def.opcode() == spv::OpTypePointer) {
593*b7893ccfSSadaf Ebrahimi def = src->get_def(def.word(3));
594*b7893ccfSSadaf Ebrahimi } else if (def.opcode() == spv::OpTypeArray && is_array_of_verts) {
595*b7893ccfSSadaf Ebrahimi def = src->get_def(def.word(2));
596*b7893ccfSSadaf Ebrahimi is_array_of_verts = false;
597*b7893ccfSSadaf Ebrahimi } else if (def.opcode() == spv::OpTypeStruct) {
598*b7893ccfSSadaf Ebrahimi return def;
599*b7893ccfSSadaf Ebrahimi } else {
600*b7893ccfSSadaf Ebrahimi return src->end();
601*b7893ccfSSadaf Ebrahimi }
602*b7893ccfSSadaf Ebrahimi }
603*b7893ccfSSadaf Ebrahimi }
604*b7893ccfSSadaf Ebrahimi
CollectInterfaceBlockMembers(SHADER_MODULE_STATE const * src,std::map<location_t,interface_var> * out,bool is_array_of_verts,uint32_t id,uint32_t type_id,bool is_patch,int)605*b7893ccfSSadaf Ebrahimi static bool CollectInterfaceBlockMembers(SHADER_MODULE_STATE const *src, std::map<location_t, interface_var> *out,
606*b7893ccfSSadaf Ebrahimi bool is_array_of_verts, uint32_t id, uint32_t type_id, bool is_patch,
607*b7893ccfSSadaf Ebrahimi int /*first_location*/) {
608*b7893ccfSSadaf Ebrahimi // Walk down the type_id presented, trying to determine whether it's actually an interface block.
609*b7893ccfSSadaf Ebrahimi auto type = GetStructType(src, src->get_def(type_id), is_array_of_verts && !is_patch);
610*b7893ccfSSadaf Ebrahimi if (type == src->end() || !(src->get_decorations(type.word(1)).flags & decoration_set::block_bit)) {
611*b7893ccfSSadaf Ebrahimi // This isn't an interface block.
612*b7893ccfSSadaf Ebrahimi return false;
613*b7893ccfSSadaf Ebrahimi }
614*b7893ccfSSadaf Ebrahimi
615*b7893ccfSSadaf Ebrahimi std::unordered_map<unsigned, unsigned> member_components;
616*b7893ccfSSadaf Ebrahimi std::unordered_map<unsigned, unsigned> member_relaxed_precision;
617*b7893ccfSSadaf Ebrahimi std::unordered_map<unsigned, unsigned> member_patch;
618*b7893ccfSSadaf Ebrahimi
619*b7893ccfSSadaf Ebrahimi // Walk all the OpMemberDecorate for type's result id -- first pass, collect components.
620*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
621*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1)) {
622*b7893ccfSSadaf Ebrahimi unsigned member_index = insn.word(2);
623*b7893ccfSSadaf Ebrahimi
624*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationComponent) {
625*b7893ccfSSadaf Ebrahimi unsigned component = insn.word(4);
626*b7893ccfSSadaf Ebrahimi member_components[member_index] = component;
627*b7893ccfSSadaf Ebrahimi }
628*b7893ccfSSadaf Ebrahimi
629*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationRelaxedPrecision) {
630*b7893ccfSSadaf Ebrahimi member_relaxed_precision[member_index] = 1;
631*b7893ccfSSadaf Ebrahimi }
632*b7893ccfSSadaf Ebrahimi
633*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationPatch) {
634*b7893ccfSSadaf Ebrahimi member_patch[member_index] = 1;
635*b7893ccfSSadaf Ebrahimi }
636*b7893ccfSSadaf Ebrahimi }
637*b7893ccfSSadaf Ebrahimi }
638*b7893ccfSSadaf Ebrahimi
639*b7893ccfSSadaf Ebrahimi // TODO: correctly handle location assignment from outside
640*b7893ccfSSadaf Ebrahimi
641*b7893ccfSSadaf Ebrahimi // Second pass -- produce the output, from Location decorations
642*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
643*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1)) {
644*b7893ccfSSadaf Ebrahimi unsigned member_index = insn.word(2);
645*b7893ccfSSadaf Ebrahimi unsigned member_type_id = type.word(2 + member_index);
646*b7893ccfSSadaf Ebrahimi
647*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationLocation) {
648*b7893ccfSSadaf Ebrahimi unsigned location = insn.word(4);
649*b7893ccfSSadaf Ebrahimi unsigned num_locations = GetLocationsConsumedByType(src, member_type_id, false);
650*b7893ccfSSadaf Ebrahimi auto component_it = member_components.find(member_index);
651*b7893ccfSSadaf Ebrahimi unsigned component = component_it == member_components.end() ? 0 : component_it->second;
652*b7893ccfSSadaf Ebrahimi bool is_relaxed_precision = member_relaxed_precision.find(member_index) != member_relaxed_precision.end();
653*b7893ccfSSadaf Ebrahimi bool member_is_patch = is_patch || member_patch.count(member_index) > 0;
654*b7893ccfSSadaf Ebrahimi
655*b7893ccfSSadaf Ebrahimi for (unsigned int offset = 0; offset < num_locations; offset++) {
656*b7893ccfSSadaf Ebrahimi interface_var v = {};
657*b7893ccfSSadaf Ebrahimi v.id = id;
658*b7893ccfSSadaf Ebrahimi // TODO: member index in interface_var too?
659*b7893ccfSSadaf Ebrahimi v.type_id = member_type_id;
660*b7893ccfSSadaf Ebrahimi v.offset = offset;
661*b7893ccfSSadaf Ebrahimi v.is_patch = member_is_patch;
662*b7893ccfSSadaf Ebrahimi v.is_block_member = true;
663*b7893ccfSSadaf Ebrahimi v.is_relaxed_precision = is_relaxed_precision;
664*b7893ccfSSadaf Ebrahimi (*out)[std::make_pair(location + offset, component)] = v;
665*b7893ccfSSadaf Ebrahimi }
666*b7893ccfSSadaf Ebrahimi }
667*b7893ccfSSadaf Ebrahimi }
668*b7893ccfSSadaf Ebrahimi }
669*b7893ccfSSadaf Ebrahimi
670*b7893ccfSSadaf Ebrahimi return true;
671*b7893ccfSSadaf Ebrahimi }
672*b7893ccfSSadaf Ebrahimi
FindEntrypointInterfaces(spirv_inst_iter entrypoint)673*b7893ccfSSadaf Ebrahimi static std::vector<uint32_t> FindEntrypointInterfaces(spirv_inst_iter entrypoint) {
674*b7893ccfSSadaf Ebrahimi assert(entrypoint.opcode() == spv::OpEntryPoint);
675*b7893ccfSSadaf Ebrahimi
676*b7893ccfSSadaf Ebrahimi std::vector<uint32_t> interfaces;
677*b7893ccfSSadaf Ebrahimi // Find the end of the entrypoint's name string. additional zero bytes follow the actual null terminator, to fill out the
678*b7893ccfSSadaf Ebrahimi // rest of the word - so we only need to look at the last byte in the word to determine which word contains the terminator.
679*b7893ccfSSadaf Ebrahimi uint32_t word = 3;
680*b7893ccfSSadaf Ebrahimi while (entrypoint.word(word) & 0xff000000u) {
681*b7893ccfSSadaf Ebrahimi ++word;
682*b7893ccfSSadaf Ebrahimi }
683*b7893ccfSSadaf Ebrahimi ++word;
684*b7893ccfSSadaf Ebrahimi
685*b7893ccfSSadaf Ebrahimi for (; word < entrypoint.len(); word++) interfaces.push_back(entrypoint.word(word));
686*b7893ccfSSadaf Ebrahimi
687*b7893ccfSSadaf Ebrahimi return interfaces;
688*b7893ccfSSadaf Ebrahimi }
689*b7893ccfSSadaf Ebrahimi
CollectInterfaceByLocation(SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint,spv::StorageClass sinterface,bool is_array_of_verts)690*b7893ccfSSadaf Ebrahimi static std::map<location_t, interface_var> CollectInterfaceByLocation(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint,
691*b7893ccfSSadaf Ebrahimi spv::StorageClass sinterface, bool is_array_of_verts) {
692*b7893ccfSSadaf Ebrahimi // TODO: handle index=1 dual source outputs from FS -- two vars will have the same location, and we DON'T want to clobber.
693*b7893ccfSSadaf Ebrahimi
694*b7893ccfSSadaf Ebrahimi std::map<location_t, interface_var> out;
695*b7893ccfSSadaf Ebrahimi
696*b7893ccfSSadaf Ebrahimi for (uint32_t iid : FindEntrypointInterfaces(entrypoint)) {
697*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(iid);
698*b7893ccfSSadaf Ebrahimi assert(insn != src->end());
699*b7893ccfSSadaf Ebrahimi assert(insn.opcode() == spv::OpVariable);
700*b7893ccfSSadaf Ebrahimi
701*b7893ccfSSadaf Ebrahimi if (insn.word(3) == static_cast<uint32_t>(sinterface)) {
702*b7893ccfSSadaf Ebrahimi auto d = src->get_decorations(iid);
703*b7893ccfSSadaf Ebrahimi unsigned id = insn.word(2);
704*b7893ccfSSadaf Ebrahimi unsigned type = insn.word(1);
705*b7893ccfSSadaf Ebrahimi
706*b7893ccfSSadaf Ebrahimi int location = d.location;
707*b7893ccfSSadaf Ebrahimi int builtin = d.builtin;
708*b7893ccfSSadaf Ebrahimi unsigned component = d.component;
709*b7893ccfSSadaf Ebrahimi bool is_patch = (d.flags & decoration_set::patch_bit) != 0;
710*b7893ccfSSadaf Ebrahimi bool is_relaxed_precision = (d.flags & decoration_set::relaxed_precision_bit) != 0;
711*b7893ccfSSadaf Ebrahimi
712*b7893ccfSSadaf Ebrahimi if (builtin != -1)
713*b7893ccfSSadaf Ebrahimi continue;
714*b7893ccfSSadaf Ebrahimi else if (!CollectInterfaceBlockMembers(src, &out, is_array_of_verts, id, type, is_patch, location)) {
715*b7893ccfSSadaf Ebrahimi // A user-defined interface variable, with a location. Where a variable occupied multiple locations, emit
716*b7893ccfSSadaf Ebrahimi // one result for each.
717*b7893ccfSSadaf Ebrahimi unsigned num_locations = GetLocationsConsumedByType(src, type, is_array_of_verts && !is_patch);
718*b7893ccfSSadaf Ebrahimi for (unsigned int offset = 0; offset < num_locations; offset++) {
719*b7893ccfSSadaf Ebrahimi interface_var v = {};
720*b7893ccfSSadaf Ebrahimi v.id = id;
721*b7893ccfSSadaf Ebrahimi v.type_id = type;
722*b7893ccfSSadaf Ebrahimi v.offset = offset;
723*b7893ccfSSadaf Ebrahimi v.is_patch = is_patch;
724*b7893ccfSSadaf Ebrahimi v.is_relaxed_precision = is_relaxed_precision;
725*b7893ccfSSadaf Ebrahimi out[std::make_pair(location + offset, component)] = v;
726*b7893ccfSSadaf Ebrahimi }
727*b7893ccfSSadaf Ebrahimi }
728*b7893ccfSSadaf Ebrahimi }
729*b7893ccfSSadaf Ebrahimi }
730*b7893ccfSSadaf Ebrahimi
731*b7893ccfSSadaf Ebrahimi return out;
732*b7893ccfSSadaf Ebrahimi }
733*b7893ccfSSadaf Ebrahimi
CollectBuiltinBlockMembers(SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint,uint32_t storageClass)734*b7893ccfSSadaf Ebrahimi static std::vector<uint32_t> CollectBuiltinBlockMembers(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint,
735*b7893ccfSSadaf Ebrahimi uint32_t storageClass) {
736*b7893ccfSSadaf Ebrahimi std::vector<uint32_t> variables;
737*b7893ccfSSadaf Ebrahimi std::vector<uint32_t> builtinStructMembers;
738*b7893ccfSSadaf Ebrahimi std::vector<uint32_t> builtinDecorations;
739*b7893ccfSSadaf Ebrahimi
740*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
741*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
742*b7893ccfSSadaf Ebrahimi // Find all built-in member decorations
743*b7893ccfSSadaf Ebrahimi case spv::OpMemberDecorate:
744*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationBuiltIn) {
745*b7893ccfSSadaf Ebrahimi builtinStructMembers.push_back(insn.word(1));
746*b7893ccfSSadaf Ebrahimi }
747*b7893ccfSSadaf Ebrahimi break;
748*b7893ccfSSadaf Ebrahimi // Find all built-in decorations
749*b7893ccfSSadaf Ebrahimi case spv::OpDecorate:
750*b7893ccfSSadaf Ebrahimi switch (insn.word(2)) {
751*b7893ccfSSadaf Ebrahimi case spv::DecorationBlock: {
752*b7893ccfSSadaf Ebrahimi uint32_t blockID = insn.word(1);
753*b7893ccfSSadaf Ebrahimi for (auto builtInBlockID : builtinStructMembers) {
754*b7893ccfSSadaf Ebrahimi // Check if one of the members of the block are built-in -> the block is built-in
755*b7893ccfSSadaf Ebrahimi if (blockID == builtInBlockID) {
756*b7893ccfSSadaf Ebrahimi builtinDecorations.push_back(blockID);
757*b7893ccfSSadaf Ebrahimi break;
758*b7893ccfSSadaf Ebrahimi }
759*b7893ccfSSadaf Ebrahimi }
760*b7893ccfSSadaf Ebrahimi break;
761*b7893ccfSSadaf Ebrahimi }
762*b7893ccfSSadaf Ebrahimi case spv::DecorationBuiltIn:
763*b7893ccfSSadaf Ebrahimi builtinDecorations.push_back(insn.word(1));
764*b7893ccfSSadaf Ebrahimi break;
765*b7893ccfSSadaf Ebrahimi default:
766*b7893ccfSSadaf Ebrahimi break;
767*b7893ccfSSadaf Ebrahimi }
768*b7893ccfSSadaf Ebrahimi break;
769*b7893ccfSSadaf Ebrahimi default:
770*b7893ccfSSadaf Ebrahimi break;
771*b7893ccfSSadaf Ebrahimi }
772*b7893ccfSSadaf Ebrahimi }
773*b7893ccfSSadaf Ebrahimi
774*b7893ccfSSadaf Ebrahimi // Find all interface variables belonging to the entrypoint and matching the storage class
775*b7893ccfSSadaf Ebrahimi for (uint32_t id : FindEntrypointInterfaces(entrypoint)) {
776*b7893ccfSSadaf Ebrahimi auto def = src->get_def(id);
777*b7893ccfSSadaf Ebrahimi assert(def != src->end());
778*b7893ccfSSadaf Ebrahimi assert(def.opcode() == spv::OpVariable);
779*b7893ccfSSadaf Ebrahimi
780*b7893ccfSSadaf Ebrahimi if (def.word(3) == storageClass) variables.push_back(def.word(1));
781*b7893ccfSSadaf Ebrahimi }
782*b7893ccfSSadaf Ebrahimi
783*b7893ccfSSadaf Ebrahimi // Find all members belonging to the builtin block selected
784*b7893ccfSSadaf Ebrahimi std::vector<uint32_t> builtinBlockMembers;
785*b7893ccfSSadaf Ebrahimi for (auto &var : variables) {
786*b7893ccfSSadaf Ebrahimi auto def = src->get_def(src->get_def(var).word(3));
787*b7893ccfSSadaf Ebrahimi
788*b7893ccfSSadaf Ebrahimi // It could be an array of IO blocks. The element type should be the struct defining the block contents
789*b7893ccfSSadaf Ebrahimi if (def.opcode() == spv::OpTypeArray) def = src->get_def(def.word(2));
790*b7893ccfSSadaf Ebrahimi
791*b7893ccfSSadaf Ebrahimi // Now find all members belonging to the struct defining the IO block
792*b7893ccfSSadaf Ebrahimi if (def.opcode() == spv::OpTypeStruct) {
793*b7893ccfSSadaf Ebrahimi for (auto builtInID : builtinDecorations) {
794*b7893ccfSSadaf Ebrahimi if (builtInID == def.word(1)) {
795*b7893ccfSSadaf Ebrahimi for (int i = 2; i < (int)def.len(); i++)
796*b7893ccfSSadaf Ebrahimi builtinBlockMembers.push_back(spv::BuiltInMax); // Start with undefined builtin for each struct member.
797*b7893ccfSSadaf Ebrahimi // These shouldn't be left after replacing.
798*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
799*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == builtInID &&
800*b7893ccfSSadaf Ebrahimi insn.word(3) == spv::DecorationBuiltIn) {
801*b7893ccfSSadaf Ebrahimi auto structIndex = insn.word(2);
802*b7893ccfSSadaf Ebrahimi assert(structIndex < builtinBlockMembers.size());
803*b7893ccfSSadaf Ebrahimi builtinBlockMembers[structIndex] = insn.word(4);
804*b7893ccfSSadaf Ebrahimi }
805*b7893ccfSSadaf Ebrahimi }
806*b7893ccfSSadaf Ebrahimi }
807*b7893ccfSSadaf Ebrahimi }
808*b7893ccfSSadaf Ebrahimi }
809*b7893ccfSSadaf Ebrahimi }
810*b7893ccfSSadaf Ebrahimi
811*b7893ccfSSadaf Ebrahimi return builtinBlockMembers;
812*b7893ccfSSadaf Ebrahimi }
813*b7893ccfSSadaf Ebrahimi
CollectInterfaceByInputAttachmentIndex(SHADER_MODULE_STATE const * src,std::unordered_set<uint32_t> const & accessible_ids)814*b7893ccfSSadaf Ebrahimi static std::vector<std::pair<uint32_t, interface_var>> CollectInterfaceByInputAttachmentIndex(
815*b7893ccfSSadaf Ebrahimi SHADER_MODULE_STATE const *src, std::unordered_set<uint32_t> const &accessible_ids) {
816*b7893ccfSSadaf Ebrahimi std::vector<std::pair<uint32_t, interface_var>> out;
817*b7893ccfSSadaf Ebrahimi
818*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
819*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpDecorate) {
820*b7893ccfSSadaf Ebrahimi if (insn.word(2) == spv::DecorationInputAttachmentIndex) {
821*b7893ccfSSadaf Ebrahimi auto attachment_index = insn.word(3);
822*b7893ccfSSadaf Ebrahimi auto id = insn.word(1);
823*b7893ccfSSadaf Ebrahimi
824*b7893ccfSSadaf Ebrahimi if (accessible_ids.count(id)) {
825*b7893ccfSSadaf Ebrahimi auto def = src->get_def(id);
826*b7893ccfSSadaf Ebrahimi assert(def != src->end());
827*b7893ccfSSadaf Ebrahimi
828*b7893ccfSSadaf Ebrahimi if (def.opcode() == spv::OpVariable && insn.word(3) == spv::StorageClassUniformConstant) {
829*b7893ccfSSadaf Ebrahimi auto num_locations = GetLocationsConsumedByType(src, def.word(1), false);
830*b7893ccfSSadaf Ebrahimi for (unsigned int offset = 0; offset < num_locations; offset++) {
831*b7893ccfSSadaf Ebrahimi interface_var v = {};
832*b7893ccfSSadaf Ebrahimi v.id = id;
833*b7893ccfSSadaf Ebrahimi v.type_id = def.word(1);
834*b7893ccfSSadaf Ebrahimi v.offset = offset;
835*b7893ccfSSadaf Ebrahimi out.emplace_back(attachment_index + offset, v);
836*b7893ccfSSadaf Ebrahimi }
837*b7893ccfSSadaf Ebrahimi }
838*b7893ccfSSadaf Ebrahimi }
839*b7893ccfSSadaf Ebrahimi }
840*b7893ccfSSadaf Ebrahimi }
841*b7893ccfSSadaf Ebrahimi }
842*b7893ccfSSadaf Ebrahimi
843*b7893ccfSSadaf Ebrahimi return out;
844*b7893ccfSSadaf Ebrahimi }
845*b7893ccfSSadaf Ebrahimi
IsWritableDescriptorType(SHADER_MODULE_STATE const * module,uint32_t type_id,bool is_storage_buffer)846*b7893ccfSSadaf Ebrahimi static bool IsWritableDescriptorType(SHADER_MODULE_STATE const *module, uint32_t type_id, bool is_storage_buffer) {
847*b7893ccfSSadaf Ebrahimi auto type = module->get_def(type_id);
848*b7893ccfSSadaf Ebrahimi
849*b7893ccfSSadaf Ebrahimi // Strip off any array or ptrs. Where we remove array levels, adjust the descriptor count for each dimension.
850*b7893ccfSSadaf Ebrahimi while (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypePointer || type.opcode() == spv::OpTypeRuntimeArray) {
851*b7893ccfSSadaf Ebrahimi if (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypeRuntimeArray) {
852*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(2)); // Element type
853*b7893ccfSSadaf Ebrahimi } else {
854*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(3)); // Pointee type
855*b7893ccfSSadaf Ebrahimi }
856*b7893ccfSSadaf Ebrahimi }
857*b7893ccfSSadaf Ebrahimi
858*b7893ccfSSadaf Ebrahimi switch (type.opcode()) {
859*b7893ccfSSadaf Ebrahimi case spv::OpTypeImage: {
860*b7893ccfSSadaf Ebrahimi auto dim = type.word(3);
861*b7893ccfSSadaf Ebrahimi auto sampled = type.word(7);
862*b7893ccfSSadaf Ebrahimi return sampled == 2 && dim != spv::DimSubpassData;
863*b7893ccfSSadaf Ebrahimi }
864*b7893ccfSSadaf Ebrahimi
865*b7893ccfSSadaf Ebrahimi case spv::OpTypeStruct: {
866*b7893ccfSSadaf Ebrahimi std::unordered_set<unsigned> nonwritable_members;
867*b7893ccfSSadaf Ebrahimi if (module->get_decorations(type.word(1)).flags & decoration_set::buffer_block_bit) is_storage_buffer = true;
868*b7893ccfSSadaf Ebrahimi for (auto insn : *module) {
869*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1) &&
870*b7893ccfSSadaf Ebrahimi insn.word(3) == spv::DecorationNonWritable) {
871*b7893ccfSSadaf Ebrahimi nonwritable_members.insert(insn.word(2));
872*b7893ccfSSadaf Ebrahimi }
873*b7893ccfSSadaf Ebrahimi }
874*b7893ccfSSadaf Ebrahimi
875*b7893ccfSSadaf Ebrahimi // A buffer is writable if it's either flavor of storage buffer, and has any member not decorated
876*b7893ccfSSadaf Ebrahimi // as nonwritable.
877*b7893ccfSSadaf Ebrahimi return is_storage_buffer && nonwritable_members.size() != type.len() - 2;
878*b7893ccfSSadaf Ebrahimi }
879*b7893ccfSSadaf Ebrahimi }
880*b7893ccfSSadaf Ebrahimi
881*b7893ccfSSadaf Ebrahimi return false;
882*b7893ccfSSadaf Ebrahimi }
883*b7893ccfSSadaf Ebrahimi
CollectInterfaceByDescriptorSlot(debug_report_data const * report_data,SHADER_MODULE_STATE const * src,std::unordered_set<uint32_t> const & accessible_ids,bool * has_writable_descriptor)884*b7893ccfSSadaf Ebrahimi static std::vector<std::pair<descriptor_slot_t, interface_var>> CollectInterfaceByDescriptorSlot(
885*b7893ccfSSadaf Ebrahimi debug_report_data const *report_data, SHADER_MODULE_STATE const *src, std::unordered_set<uint32_t> const &accessible_ids,
886*b7893ccfSSadaf Ebrahimi bool *has_writable_descriptor) {
887*b7893ccfSSadaf Ebrahimi std::vector<std::pair<descriptor_slot_t, interface_var>> out;
888*b7893ccfSSadaf Ebrahimi
889*b7893ccfSSadaf Ebrahimi for (auto id : accessible_ids) {
890*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(id);
891*b7893ccfSSadaf Ebrahimi assert(insn != src->end());
892*b7893ccfSSadaf Ebrahimi
893*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpVariable &&
894*b7893ccfSSadaf Ebrahimi (insn.word(3) == spv::StorageClassUniform || insn.word(3) == spv::StorageClassUniformConstant ||
895*b7893ccfSSadaf Ebrahimi insn.word(3) == spv::StorageClassStorageBuffer)) {
896*b7893ccfSSadaf Ebrahimi auto d = src->get_decorations(insn.word(2));
897*b7893ccfSSadaf Ebrahimi unsigned set = d.descriptor_set;
898*b7893ccfSSadaf Ebrahimi unsigned binding = d.binding;
899*b7893ccfSSadaf Ebrahimi
900*b7893ccfSSadaf Ebrahimi interface_var v = {};
901*b7893ccfSSadaf Ebrahimi v.id = insn.word(2);
902*b7893ccfSSadaf Ebrahimi v.type_id = insn.word(1);
903*b7893ccfSSadaf Ebrahimi out.emplace_back(std::make_pair(set, binding), v);
904*b7893ccfSSadaf Ebrahimi
905*b7893ccfSSadaf Ebrahimi if (!(d.flags & decoration_set::nonwritable_bit) &&
906*b7893ccfSSadaf Ebrahimi IsWritableDescriptorType(src, insn.word(1), insn.word(3) == spv::StorageClassStorageBuffer)) {
907*b7893ccfSSadaf Ebrahimi *has_writable_descriptor = true;
908*b7893ccfSSadaf Ebrahimi }
909*b7893ccfSSadaf Ebrahimi }
910*b7893ccfSSadaf Ebrahimi }
911*b7893ccfSSadaf Ebrahimi
912*b7893ccfSSadaf Ebrahimi return out;
913*b7893ccfSSadaf Ebrahimi }
914*b7893ccfSSadaf Ebrahimi
ValidateViConsistency(debug_report_data const * report_data,VkPipelineVertexInputStateCreateInfo const * vi)915*b7893ccfSSadaf Ebrahimi static bool ValidateViConsistency(debug_report_data const *report_data, VkPipelineVertexInputStateCreateInfo const *vi) {
916*b7893ccfSSadaf Ebrahimi // Walk the binding descriptions, which describe the step rate and stride of each vertex buffer. Each binding should
917*b7893ccfSSadaf Ebrahimi // be specified only once.
918*b7893ccfSSadaf Ebrahimi std::unordered_map<uint32_t, VkVertexInputBindingDescription const *> bindings;
919*b7893ccfSSadaf Ebrahimi bool skip = false;
920*b7893ccfSSadaf Ebrahimi
921*b7893ccfSSadaf Ebrahimi for (unsigned i = 0; i < vi->vertexBindingDescriptionCount; i++) {
922*b7893ccfSSadaf Ebrahimi auto desc = &vi->pVertexBindingDescriptions[i];
923*b7893ccfSSadaf Ebrahimi auto &binding = bindings[desc->binding];
924*b7893ccfSSadaf Ebrahimi if (binding) {
925*b7893ccfSSadaf Ebrahimi // TODO: "VUID-VkGraphicsPipelineCreateInfo-pStages-00742" perhaps?
926*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
927*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_InconsistentVi, "Duplicate vertex input binding descriptions for binding %d",
928*b7893ccfSSadaf Ebrahimi desc->binding);
929*b7893ccfSSadaf Ebrahimi } else {
930*b7893ccfSSadaf Ebrahimi binding = desc;
931*b7893ccfSSadaf Ebrahimi }
932*b7893ccfSSadaf Ebrahimi }
933*b7893ccfSSadaf Ebrahimi
934*b7893ccfSSadaf Ebrahimi return skip;
935*b7893ccfSSadaf Ebrahimi }
936*b7893ccfSSadaf Ebrahimi
ValidateViAgainstVsInputs(debug_report_data const * report_data,VkPipelineVertexInputStateCreateInfo const * vi,SHADER_MODULE_STATE const * vs,spirv_inst_iter entrypoint)937*b7893ccfSSadaf Ebrahimi static bool ValidateViAgainstVsInputs(debug_report_data const *report_data, VkPipelineVertexInputStateCreateInfo const *vi,
938*b7893ccfSSadaf Ebrahimi SHADER_MODULE_STATE const *vs, spirv_inst_iter entrypoint) {
939*b7893ccfSSadaf Ebrahimi bool skip = false;
940*b7893ccfSSadaf Ebrahimi
941*b7893ccfSSadaf Ebrahimi auto inputs = CollectInterfaceByLocation(vs, entrypoint, spv::StorageClassInput, false);
942*b7893ccfSSadaf Ebrahimi
943*b7893ccfSSadaf Ebrahimi // Build index by location
944*b7893ccfSSadaf Ebrahimi std::map<uint32_t, VkVertexInputAttributeDescription const *> attribs;
945*b7893ccfSSadaf Ebrahimi if (vi) {
946*b7893ccfSSadaf Ebrahimi for (unsigned i = 0; i < vi->vertexAttributeDescriptionCount; i++) {
947*b7893ccfSSadaf Ebrahimi auto num_locations = GetLocationsConsumedByFormat(vi->pVertexAttributeDescriptions[i].format);
948*b7893ccfSSadaf Ebrahimi for (auto j = 0u; j < num_locations; j++) {
949*b7893ccfSSadaf Ebrahimi attribs[vi->pVertexAttributeDescriptions[i].location + j] = &vi->pVertexAttributeDescriptions[i];
950*b7893ccfSSadaf Ebrahimi }
951*b7893ccfSSadaf Ebrahimi }
952*b7893ccfSSadaf Ebrahimi }
953*b7893ccfSSadaf Ebrahimi
954*b7893ccfSSadaf Ebrahimi auto it_a = attribs.begin();
955*b7893ccfSSadaf Ebrahimi auto it_b = inputs.begin();
956*b7893ccfSSadaf Ebrahimi bool used = false;
957*b7893ccfSSadaf Ebrahimi
958*b7893ccfSSadaf Ebrahimi while ((attribs.size() > 0 && it_a != attribs.end()) || (inputs.size() > 0 && it_b != inputs.end())) {
959*b7893ccfSSadaf Ebrahimi bool a_at_end = attribs.size() == 0 || it_a == attribs.end();
960*b7893ccfSSadaf Ebrahimi bool b_at_end = inputs.size() == 0 || it_b == inputs.end();
961*b7893ccfSSadaf Ebrahimi auto a_first = a_at_end ? 0 : it_a->first;
962*b7893ccfSSadaf Ebrahimi auto b_first = b_at_end ? 0 : it_b->first.first;
963*b7893ccfSSadaf Ebrahimi
964*b7893ccfSSadaf Ebrahimi if (!a_at_end && (b_at_end || a_first < b_first)) {
965*b7893ccfSSadaf Ebrahimi if (!used &&
966*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
967*b7893ccfSSadaf Ebrahimi HandleToUint64(vs->vk_shader_module), kVUID_Core_Shader_OutputNotConsumed,
968*b7893ccfSSadaf Ebrahimi "Vertex attribute at location %d not consumed by vertex shader", a_first)) {
969*b7893ccfSSadaf Ebrahimi skip = true;
970*b7893ccfSSadaf Ebrahimi }
971*b7893ccfSSadaf Ebrahimi used = false;
972*b7893ccfSSadaf Ebrahimi it_a++;
973*b7893ccfSSadaf Ebrahimi } else if (!b_at_end && (a_at_end || b_first < a_first)) {
974*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
975*b7893ccfSSadaf Ebrahimi HandleToUint64(vs->vk_shader_module), kVUID_Core_Shader_InputNotProduced,
976*b7893ccfSSadaf Ebrahimi "Vertex shader consumes input at location %d but not provided", b_first);
977*b7893ccfSSadaf Ebrahimi it_b++;
978*b7893ccfSSadaf Ebrahimi } else {
979*b7893ccfSSadaf Ebrahimi unsigned attrib_type = GetFormatType(it_a->second->format);
980*b7893ccfSSadaf Ebrahimi unsigned input_type = GetFundamentalType(vs, it_b->second.type_id);
981*b7893ccfSSadaf Ebrahimi
982*b7893ccfSSadaf Ebrahimi // Type checking
983*b7893ccfSSadaf Ebrahimi if (!(attrib_type & input_type)) {
984*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
985*b7893ccfSSadaf Ebrahimi HandleToUint64(vs->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
986*b7893ccfSSadaf Ebrahimi "Attribute type of `%s` at location %d does not match vertex shader input type of `%s`",
987*b7893ccfSSadaf Ebrahimi string_VkFormat(it_a->second->format), a_first, DescribeType(vs, it_b->second.type_id).c_str());
988*b7893ccfSSadaf Ebrahimi }
989*b7893ccfSSadaf Ebrahimi
990*b7893ccfSSadaf Ebrahimi // OK!
991*b7893ccfSSadaf Ebrahimi used = true;
992*b7893ccfSSadaf Ebrahimi it_b++;
993*b7893ccfSSadaf Ebrahimi }
994*b7893ccfSSadaf Ebrahimi }
995*b7893ccfSSadaf Ebrahimi
996*b7893ccfSSadaf Ebrahimi return skip;
997*b7893ccfSSadaf Ebrahimi }
998*b7893ccfSSadaf Ebrahimi
ValidateFsOutputsAgainstRenderPass(debug_report_data const * report_data,SHADER_MODULE_STATE const * fs,spirv_inst_iter entrypoint,PIPELINE_STATE const * pipeline,uint32_t subpass_index)999*b7893ccfSSadaf Ebrahimi static bool ValidateFsOutputsAgainstRenderPass(debug_report_data const *report_data, SHADER_MODULE_STATE const *fs,
1000*b7893ccfSSadaf Ebrahimi spirv_inst_iter entrypoint, PIPELINE_STATE const *pipeline, uint32_t subpass_index) {
1001*b7893ccfSSadaf Ebrahimi auto rpci = pipeline->rp_state->createInfo.ptr();
1002*b7893ccfSSadaf Ebrahimi
1003*b7893ccfSSadaf Ebrahimi std::map<uint32_t, VkFormat> color_attachments;
1004*b7893ccfSSadaf Ebrahimi auto subpass = rpci->pSubpasses[subpass_index];
1005*b7893ccfSSadaf Ebrahimi for (auto i = 0u; i < subpass.colorAttachmentCount; ++i) {
1006*b7893ccfSSadaf Ebrahimi uint32_t attachment = subpass.pColorAttachments[i].attachment;
1007*b7893ccfSSadaf Ebrahimi if (attachment == VK_ATTACHMENT_UNUSED) continue;
1008*b7893ccfSSadaf Ebrahimi if (rpci->pAttachments[attachment].format != VK_FORMAT_UNDEFINED) {
1009*b7893ccfSSadaf Ebrahimi color_attachments[i] = rpci->pAttachments[attachment].format;
1010*b7893ccfSSadaf Ebrahimi }
1011*b7893ccfSSadaf Ebrahimi }
1012*b7893ccfSSadaf Ebrahimi
1013*b7893ccfSSadaf Ebrahimi bool skip = false;
1014*b7893ccfSSadaf Ebrahimi
1015*b7893ccfSSadaf Ebrahimi // TODO: dual source blend index (spv::DecIndex, zero if not provided)
1016*b7893ccfSSadaf Ebrahimi
1017*b7893ccfSSadaf Ebrahimi auto outputs = CollectInterfaceByLocation(fs, entrypoint, spv::StorageClassOutput, false);
1018*b7893ccfSSadaf Ebrahimi
1019*b7893ccfSSadaf Ebrahimi auto it_a = outputs.begin();
1020*b7893ccfSSadaf Ebrahimi auto it_b = color_attachments.begin();
1021*b7893ccfSSadaf Ebrahimi bool used = false;
1022*b7893ccfSSadaf Ebrahimi bool alphaToCoverageEnabled = pipeline->graphicsPipelineCI.pMultisampleState != NULL &&
1023*b7893ccfSSadaf Ebrahimi pipeline->graphicsPipelineCI.pMultisampleState->alphaToCoverageEnable == VK_TRUE;
1024*b7893ccfSSadaf Ebrahimi bool locationZeroHasAlpha = false;
1025*b7893ccfSSadaf Ebrahimi
1026*b7893ccfSSadaf Ebrahimi // Walk attachment list and outputs together
1027*b7893ccfSSadaf Ebrahimi
1028*b7893ccfSSadaf Ebrahimi while ((outputs.size() > 0 && it_a != outputs.end()) || (color_attachments.size() > 0 && it_b != color_attachments.end())) {
1029*b7893ccfSSadaf Ebrahimi bool a_at_end = outputs.size() == 0 || it_a == outputs.end();
1030*b7893ccfSSadaf Ebrahimi bool b_at_end = color_attachments.size() == 0 || it_b == color_attachments.end();
1031*b7893ccfSSadaf Ebrahimi
1032*b7893ccfSSadaf Ebrahimi if (!a_at_end && it_a->first.first == 0 && fs->get_def(it_a->second.type_id) != fs->end() &&
1033*b7893ccfSSadaf Ebrahimi GetComponentsConsumedByType(fs, it_a->second.type_id, false) == 4)
1034*b7893ccfSSadaf Ebrahimi locationZeroHasAlpha = true;
1035*b7893ccfSSadaf Ebrahimi
1036*b7893ccfSSadaf Ebrahimi if (!a_at_end && (b_at_end || it_a->first.first < it_b->first)) {
1037*b7893ccfSSadaf Ebrahimi if (!alphaToCoverageEnabled || it_a->first.first != 0) {
1038*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
1039*b7893ccfSSadaf Ebrahimi HandleToUint64(fs->vk_shader_module), kVUID_Core_Shader_OutputNotConsumed,
1040*b7893ccfSSadaf Ebrahimi "fragment shader writes to output location %d with no matching attachment", it_a->first.first);
1041*b7893ccfSSadaf Ebrahimi }
1042*b7893ccfSSadaf Ebrahimi it_a++;
1043*b7893ccfSSadaf Ebrahimi } else if (!b_at_end && (a_at_end || it_a->first.first > it_b->first)) {
1044*b7893ccfSSadaf Ebrahimi // Only complain if there are unmasked channels for this attachment. If the writemask is 0, it's acceptable for the
1045*b7893ccfSSadaf Ebrahimi // shader to not produce a matching output.
1046*b7893ccfSSadaf Ebrahimi if (!used) {
1047*b7893ccfSSadaf Ebrahimi if (pipeline->attachments[it_b->first].colorWriteMask != 0) {
1048*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
1049*b7893ccfSSadaf Ebrahimi HandleToUint64(fs->vk_shader_module), kVUID_Core_Shader_InputNotProduced,
1050*b7893ccfSSadaf Ebrahimi "Attachment %d not written by fragment shader; undefined values will be written to attachment",
1051*b7893ccfSSadaf Ebrahimi it_b->first);
1052*b7893ccfSSadaf Ebrahimi }
1053*b7893ccfSSadaf Ebrahimi }
1054*b7893ccfSSadaf Ebrahimi used = false;
1055*b7893ccfSSadaf Ebrahimi it_b++;
1056*b7893ccfSSadaf Ebrahimi } else {
1057*b7893ccfSSadaf Ebrahimi unsigned output_type = GetFundamentalType(fs, it_a->second.type_id);
1058*b7893ccfSSadaf Ebrahimi unsigned att_type = GetFormatType(it_b->second);
1059*b7893ccfSSadaf Ebrahimi
1060*b7893ccfSSadaf Ebrahimi // Type checking
1061*b7893ccfSSadaf Ebrahimi if (!(output_type & att_type)) {
1062*b7893ccfSSadaf Ebrahimi skip |= log_msg(
1063*b7893ccfSSadaf Ebrahimi report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
1064*b7893ccfSSadaf Ebrahimi HandleToUint64(fs->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
1065*b7893ccfSSadaf Ebrahimi "Attachment %d of type `%s` does not match fragment shader output type of `%s`; resulting values are undefined",
1066*b7893ccfSSadaf Ebrahimi it_b->first, string_VkFormat(it_b->second), DescribeType(fs, it_a->second.type_id).c_str());
1067*b7893ccfSSadaf Ebrahimi }
1068*b7893ccfSSadaf Ebrahimi
1069*b7893ccfSSadaf Ebrahimi // OK!
1070*b7893ccfSSadaf Ebrahimi it_a++;
1071*b7893ccfSSadaf Ebrahimi used = true;
1072*b7893ccfSSadaf Ebrahimi }
1073*b7893ccfSSadaf Ebrahimi }
1074*b7893ccfSSadaf Ebrahimi
1075*b7893ccfSSadaf Ebrahimi if (alphaToCoverageEnabled && !locationZeroHasAlpha) {
1076*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
1077*b7893ccfSSadaf Ebrahimi HandleToUint64(fs->vk_shader_module), kVUID_Core_Shader_NoAlphaAtLocation0WithAlphaToCoverage,
1078*b7893ccfSSadaf Ebrahimi "fragment shader doesn't declare alpha output at location 0 even though alpha to coverage is enabled.");
1079*b7893ccfSSadaf Ebrahimi }
1080*b7893ccfSSadaf Ebrahimi
1081*b7893ccfSSadaf Ebrahimi return skip;
1082*b7893ccfSSadaf Ebrahimi }
1083*b7893ccfSSadaf Ebrahimi
1084*b7893ccfSSadaf Ebrahimi // For PointSize analysis we need to know if the variable decorated with the PointSize built-in was actually written to.
1085*b7893ccfSSadaf Ebrahimi // This function examines instructions in the static call tree for a write to this variable.
IsPointSizeWritten(SHADER_MODULE_STATE const * src,spirv_inst_iter builtin_instr,spirv_inst_iter entrypoint)1086*b7893ccfSSadaf Ebrahimi static bool IsPointSizeWritten(SHADER_MODULE_STATE const *src, spirv_inst_iter builtin_instr, spirv_inst_iter entrypoint) {
1087*b7893ccfSSadaf Ebrahimi auto type = builtin_instr.opcode();
1088*b7893ccfSSadaf Ebrahimi uint32_t target_id = builtin_instr.word(1);
1089*b7893ccfSSadaf Ebrahimi bool init_complete = false;
1090*b7893ccfSSadaf Ebrahimi
1091*b7893ccfSSadaf Ebrahimi if (type == spv::OpMemberDecorate) {
1092*b7893ccfSSadaf Ebrahimi // Built-in is part of a structure -- examine instructions up to first function body to get initial IDs
1093*b7893ccfSSadaf Ebrahimi auto insn = entrypoint;
1094*b7893ccfSSadaf Ebrahimi while (!init_complete && (insn.opcode() != spv::OpFunction)) {
1095*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
1096*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
1097*b7893ccfSSadaf Ebrahimi if ((insn.word(3) == target_id) && (insn.word(2) == spv::StorageClassOutput)) {
1098*b7893ccfSSadaf Ebrahimi target_id = insn.word(1);
1099*b7893ccfSSadaf Ebrahimi }
1100*b7893ccfSSadaf Ebrahimi break;
1101*b7893ccfSSadaf Ebrahimi case spv::OpVariable:
1102*b7893ccfSSadaf Ebrahimi if (insn.word(1) == target_id) {
1103*b7893ccfSSadaf Ebrahimi target_id = insn.word(2);
1104*b7893ccfSSadaf Ebrahimi init_complete = true;
1105*b7893ccfSSadaf Ebrahimi }
1106*b7893ccfSSadaf Ebrahimi break;
1107*b7893ccfSSadaf Ebrahimi }
1108*b7893ccfSSadaf Ebrahimi insn++;
1109*b7893ccfSSadaf Ebrahimi }
1110*b7893ccfSSadaf Ebrahimi }
1111*b7893ccfSSadaf Ebrahimi
1112*b7893ccfSSadaf Ebrahimi if (!init_complete && (type == spv::OpMemberDecorate)) return false;
1113*b7893ccfSSadaf Ebrahimi
1114*b7893ccfSSadaf Ebrahimi bool found_write = false;
1115*b7893ccfSSadaf Ebrahimi std::unordered_set<uint32_t> worklist;
1116*b7893ccfSSadaf Ebrahimi worklist.insert(entrypoint.word(2));
1117*b7893ccfSSadaf Ebrahimi
1118*b7893ccfSSadaf Ebrahimi // Follow instructions in call graph looking for writes to target
1119*b7893ccfSSadaf Ebrahimi while (!worklist.empty() && !found_write) {
1120*b7893ccfSSadaf Ebrahimi auto id_iter = worklist.begin();
1121*b7893ccfSSadaf Ebrahimi auto id = *id_iter;
1122*b7893ccfSSadaf Ebrahimi worklist.erase(id_iter);
1123*b7893ccfSSadaf Ebrahimi
1124*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(id);
1125*b7893ccfSSadaf Ebrahimi if (insn == src->end()) {
1126*b7893ccfSSadaf Ebrahimi continue;
1127*b7893ccfSSadaf Ebrahimi }
1128*b7893ccfSSadaf Ebrahimi
1129*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpFunction) {
1130*b7893ccfSSadaf Ebrahimi // Scan body of function looking for other function calls or items in our ID chain
1131*b7893ccfSSadaf Ebrahimi while (++insn, insn.opcode() != spv::OpFunctionEnd) {
1132*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
1133*b7893ccfSSadaf Ebrahimi case spv::OpAccessChain:
1134*b7893ccfSSadaf Ebrahimi if (insn.word(3) == target_id) {
1135*b7893ccfSSadaf Ebrahimi if (type == spv::OpMemberDecorate) {
1136*b7893ccfSSadaf Ebrahimi auto value = GetConstantValue(src, insn.word(4));
1137*b7893ccfSSadaf Ebrahimi if (value == builtin_instr.word(2)) {
1138*b7893ccfSSadaf Ebrahimi target_id = insn.word(2);
1139*b7893ccfSSadaf Ebrahimi }
1140*b7893ccfSSadaf Ebrahimi } else {
1141*b7893ccfSSadaf Ebrahimi target_id = insn.word(2);
1142*b7893ccfSSadaf Ebrahimi }
1143*b7893ccfSSadaf Ebrahimi }
1144*b7893ccfSSadaf Ebrahimi break;
1145*b7893ccfSSadaf Ebrahimi case spv::OpStore:
1146*b7893ccfSSadaf Ebrahimi if (insn.word(1) == target_id) {
1147*b7893ccfSSadaf Ebrahimi found_write = true;
1148*b7893ccfSSadaf Ebrahimi }
1149*b7893ccfSSadaf Ebrahimi break;
1150*b7893ccfSSadaf Ebrahimi case spv::OpFunctionCall:
1151*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(3));
1152*b7893ccfSSadaf Ebrahimi break;
1153*b7893ccfSSadaf Ebrahimi }
1154*b7893ccfSSadaf Ebrahimi }
1155*b7893ccfSSadaf Ebrahimi }
1156*b7893ccfSSadaf Ebrahimi }
1157*b7893ccfSSadaf Ebrahimi return found_write;
1158*b7893ccfSSadaf Ebrahimi }
1159*b7893ccfSSadaf Ebrahimi
1160*b7893ccfSSadaf Ebrahimi // For some analyses, we need to know about all ids referenced by the static call tree of a particular entrypoint. This is
1161*b7893ccfSSadaf Ebrahimi // important for identifying the set of shader resources actually used by an entrypoint, for example.
1162*b7893ccfSSadaf Ebrahimi // Note: we only explore parts of the image which might actually contain ids we care about for the above analyses.
1163*b7893ccfSSadaf Ebrahimi // - NOT the shader input/output interfaces.
1164*b7893ccfSSadaf Ebrahimi //
1165*b7893ccfSSadaf Ebrahimi // TODO: The set of interesting opcodes here was determined by eyeballing the SPIRV spec. It might be worth
1166*b7893ccfSSadaf Ebrahimi // converting parts of this to be generated from the machine-readable spec instead.
MarkAccessibleIds(SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint)1167*b7893ccfSSadaf Ebrahimi static std::unordered_set<uint32_t> MarkAccessibleIds(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint) {
1168*b7893ccfSSadaf Ebrahimi std::unordered_set<uint32_t> ids;
1169*b7893ccfSSadaf Ebrahimi std::unordered_set<uint32_t> worklist;
1170*b7893ccfSSadaf Ebrahimi worklist.insert(entrypoint.word(2));
1171*b7893ccfSSadaf Ebrahimi
1172*b7893ccfSSadaf Ebrahimi while (!worklist.empty()) {
1173*b7893ccfSSadaf Ebrahimi auto id_iter = worklist.begin();
1174*b7893ccfSSadaf Ebrahimi auto id = *id_iter;
1175*b7893ccfSSadaf Ebrahimi worklist.erase(id_iter);
1176*b7893ccfSSadaf Ebrahimi
1177*b7893ccfSSadaf Ebrahimi auto insn = src->get_def(id);
1178*b7893ccfSSadaf Ebrahimi if (insn == src->end()) {
1179*b7893ccfSSadaf Ebrahimi // ID is something we didn't collect in BuildDefIndex. that's OK -- we'll stumble across all kinds of things here
1180*b7893ccfSSadaf Ebrahimi // that we may not care about.
1181*b7893ccfSSadaf Ebrahimi continue;
1182*b7893ccfSSadaf Ebrahimi }
1183*b7893ccfSSadaf Ebrahimi
1184*b7893ccfSSadaf Ebrahimi // Try to add to the output set
1185*b7893ccfSSadaf Ebrahimi if (!ids.insert(id).second) {
1186*b7893ccfSSadaf Ebrahimi continue; // If we already saw this id, we don't want to walk it again.
1187*b7893ccfSSadaf Ebrahimi }
1188*b7893ccfSSadaf Ebrahimi
1189*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
1190*b7893ccfSSadaf Ebrahimi case spv::OpFunction:
1191*b7893ccfSSadaf Ebrahimi // Scan whole body of the function, enlisting anything interesting
1192*b7893ccfSSadaf Ebrahimi while (++insn, insn.opcode() != spv::OpFunctionEnd) {
1193*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
1194*b7893ccfSSadaf Ebrahimi case spv::OpLoad:
1195*b7893ccfSSadaf Ebrahimi case spv::OpAtomicLoad:
1196*b7893ccfSSadaf Ebrahimi case spv::OpAtomicExchange:
1197*b7893ccfSSadaf Ebrahimi case spv::OpAtomicCompareExchange:
1198*b7893ccfSSadaf Ebrahimi case spv::OpAtomicCompareExchangeWeak:
1199*b7893ccfSSadaf Ebrahimi case spv::OpAtomicIIncrement:
1200*b7893ccfSSadaf Ebrahimi case spv::OpAtomicIDecrement:
1201*b7893ccfSSadaf Ebrahimi case spv::OpAtomicIAdd:
1202*b7893ccfSSadaf Ebrahimi case spv::OpAtomicISub:
1203*b7893ccfSSadaf Ebrahimi case spv::OpAtomicSMin:
1204*b7893ccfSSadaf Ebrahimi case spv::OpAtomicUMin:
1205*b7893ccfSSadaf Ebrahimi case spv::OpAtomicSMax:
1206*b7893ccfSSadaf Ebrahimi case spv::OpAtomicUMax:
1207*b7893ccfSSadaf Ebrahimi case spv::OpAtomicAnd:
1208*b7893ccfSSadaf Ebrahimi case spv::OpAtomicOr:
1209*b7893ccfSSadaf Ebrahimi case spv::OpAtomicXor:
1210*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(3)); // ptr
1211*b7893ccfSSadaf Ebrahimi break;
1212*b7893ccfSSadaf Ebrahimi case spv::OpStore:
1213*b7893ccfSSadaf Ebrahimi case spv::OpAtomicStore:
1214*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(1)); // ptr
1215*b7893ccfSSadaf Ebrahimi break;
1216*b7893ccfSSadaf Ebrahimi case spv::OpAccessChain:
1217*b7893ccfSSadaf Ebrahimi case spv::OpInBoundsAccessChain:
1218*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(3)); // base ptr
1219*b7893ccfSSadaf Ebrahimi break;
1220*b7893ccfSSadaf Ebrahimi case spv::OpSampledImage:
1221*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleImplicitLod:
1222*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleExplicitLod:
1223*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleDrefImplicitLod:
1224*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleDrefExplicitLod:
1225*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleProjImplicitLod:
1226*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleProjExplicitLod:
1227*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleProjDrefImplicitLod:
1228*b7893ccfSSadaf Ebrahimi case spv::OpImageSampleProjDrefExplicitLod:
1229*b7893ccfSSadaf Ebrahimi case spv::OpImageFetch:
1230*b7893ccfSSadaf Ebrahimi case spv::OpImageGather:
1231*b7893ccfSSadaf Ebrahimi case spv::OpImageDrefGather:
1232*b7893ccfSSadaf Ebrahimi case spv::OpImageRead:
1233*b7893ccfSSadaf Ebrahimi case spv::OpImage:
1234*b7893ccfSSadaf Ebrahimi case spv::OpImageQueryFormat:
1235*b7893ccfSSadaf Ebrahimi case spv::OpImageQueryOrder:
1236*b7893ccfSSadaf Ebrahimi case spv::OpImageQuerySizeLod:
1237*b7893ccfSSadaf Ebrahimi case spv::OpImageQuerySize:
1238*b7893ccfSSadaf Ebrahimi case spv::OpImageQueryLod:
1239*b7893ccfSSadaf Ebrahimi case spv::OpImageQueryLevels:
1240*b7893ccfSSadaf Ebrahimi case spv::OpImageQuerySamples:
1241*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleImplicitLod:
1242*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleExplicitLod:
1243*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleDrefImplicitLod:
1244*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleDrefExplicitLod:
1245*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleProjImplicitLod:
1246*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleProjExplicitLod:
1247*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleProjDrefImplicitLod:
1248*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseSampleProjDrefExplicitLod:
1249*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseFetch:
1250*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseGather:
1251*b7893ccfSSadaf Ebrahimi case spv::OpImageSparseDrefGather:
1252*b7893ccfSSadaf Ebrahimi case spv::OpImageTexelPointer:
1253*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(3)); // Image or sampled image
1254*b7893ccfSSadaf Ebrahimi break;
1255*b7893ccfSSadaf Ebrahimi case spv::OpImageWrite:
1256*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(1)); // Image -- different operand order to above
1257*b7893ccfSSadaf Ebrahimi break;
1258*b7893ccfSSadaf Ebrahimi case spv::OpFunctionCall:
1259*b7893ccfSSadaf Ebrahimi for (uint32_t i = 3; i < insn.len(); i++) {
1260*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(i)); // fn itself, and all args
1261*b7893ccfSSadaf Ebrahimi }
1262*b7893ccfSSadaf Ebrahimi break;
1263*b7893ccfSSadaf Ebrahimi
1264*b7893ccfSSadaf Ebrahimi case spv::OpExtInst:
1265*b7893ccfSSadaf Ebrahimi for (uint32_t i = 5; i < insn.len(); i++) {
1266*b7893ccfSSadaf Ebrahimi worklist.insert(insn.word(i)); // Operands to ext inst
1267*b7893ccfSSadaf Ebrahimi }
1268*b7893ccfSSadaf Ebrahimi break;
1269*b7893ccfSSadaf Ebrahimi }
1270*b7893ccfSSadaf Ebrahimi }
1271*b7893ccfSSadaf Ebrahimi break;
1272*b7893ccfSSadaf Ebrahimi }
1273*b7893ccfSSadaf Ebrahimi }
1274*b7893ccfSSadaf Ebrahimi
1275*b7893ccfSSadaf Ebrahimi return ids;
1276*b7893ccfSSadaf Ebrahimi }
1277*b7893ccfSSadaf Ebrahimi
ValidatePushConstantBlockAgainstPipeline(debug_report_data const * report_data,std::vector<VkPushConstantRange> const * push_constant_ranges,SHADER_MODULE_STATE const * src,spirv_inst_iter type,VkShaderStageFlagBits stage)1278*b7893ccfSSadaf Ebrahimi static bool ValidatePushConstantBlockAgainstPipeline(debug_report_data const *report_data,
1279*b7893ccfSSadaf Ebrahimi std::vector<VkPushConstantRange> const *push_constant_ranges,
1280*b7893ccfSSadaf Ebrahimi SHADER_MODULE_STATE const *src, spirv_inst_iter type,
1281*b7893ccfSSadaf Ebrahimi VkShaderStageFlagBits stage) {
1282*b7893ccfSSadaf Ebrahimi bool skip = false;
1283*b7893ccfSSadaf Ebrahimi
1284*b7893ccfSSadaf Ebrahimi // Strip off ptrs etc
1285*b7893ccfSSadaf Ebrahimi type = GetStructType(src, type, false);
1286*b7893ccfSSadaf Ebrahimi assert(type != src->end());
1287*b7893ccfSSadaf Ebrahimi
1288*b7893ccfSSadaf Ebrahimi // Validate directly off the offsets. this isn't quite correct for arrays and matrices, but is a good first step.
1289*b7893ccfSSadaf Ebrahimi // TODO: arrays, matrices, weird sizes
1290*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
1291*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1)) {
1292*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationOffset) {
1293*b7893ccfSSadaf Ebrahimi unsigned offset = insn.word(4);
1294*b7893ccfSSadaf Ebrahimi auto size = 4; // Bytes; TODO: calculate this based on the type
1295*b7893ccfSSadaf Ebrahimi
1296*b7893ccfSSadaf Ebrahimi bool found_range = false;
1297*b7893ccfSSadaf Ebrahimi for (auto const &range : *push_constant_ranges) {
1298*b7893ccfSSadaf Ebrahimi if (range.offset <= offset && range.offset + range.size >= offset + size) {
1299*b7893ccfSSadaf Ebrahimi found_range = true;
1300*b7893ccfSSadaf Ebrahimi
1301*b7893ccfSSadaf Ebrahimi if ((range.stageFlags & stage) == 0) {
1302*b7893ccfSSadaf Ebrahimi skip |=
1303*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
1304*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_PushConstantNotAccessibleFromStage,
1305*b7893ccfSSadaf Ebrahimi "Push constant range covering variable starting at offset %u not accessible from stage %s",
1306*b7893ccfSSadaf Ebrahimi offset, string_VkShaderStageFlagBits(stage));
1307*b7893ccfSSadaf Ebrahimi }
1308*b7893ccfSSadaf Ebrahimi
1309*b7893ccfSSadaf Ebrahimi break;
1310*b7893ccfSSadaf Ebrahimi }
1311*b7893ccfSSadaf Ebrahimi }
1312*b7893ccfSSadaf Ebrahimi
1313*b7893ccfSSadaf Ebrahimi if (!found_range) {
1314*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
1315*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_PushConstantOutOfRange,
1316*b7893ccfSSadaf Ebrahimi "Push constant range covering variable starting at offset %u not declared in layout", offset);
1317*b7893ccfSSadaf Ebrahimi }
1318*b7893ccfSSadaf Ebrahimi }
1319*b7893ccfSSadaf Ebrahimi }
1320*b7893ccfSSadaf Ebrahimi }
1321*b7893ccfSSadaf Ebrahimi
1322*b7893ccfSSadaf Ebrahimi return skip;
1323*b7893ccfSSadaf Ebrahimi }
1324*b7893ccfSSadaf Ebrahimi
ValidatePushConstantUsage(debug_report_data const * report_data,std::vector<VkPushConstantRange> const * push_constant_ranges,SHADER_MODULE_STATE const * src,std::unordered_set<uint32_t> accessible_ids,VkShaderStageFlagBits stage)1325*b7893ccfSSadaf Ebrahimi static bool ValidatePushConstantUsage(debug_report_data const *report_data,
1326*b7893ccfSSadaf Ebrahimi std::vector<VkPushConstantRange> const *push_constant_ranges, SHADER_MODULE_STATE const *src,
1327*b7893ccfSSadaf Ebrahimi std::unordered_set<uint32_t> accessible_ids, VkShaderStageFlagBits stage) {
1328*b7893ccfSSadaf Ebrahimi bool skip = false;
1329*b7893ccfSSadaf Ebrahimi
1330*b7893ccfSSadaf Ebrahimi for (auto id : accessible_ids) {
1331*b7893ccfSSadaf Ebrahimi auto def_insn = src->get_def(id);
1332*b7893ccfSSadaf Ebrahimi if (def_insn.opcode() == spv::OpVariable && def_insn.word(3) == spv::StorageClassPushConstant) {
1333*b7893ccfSSadaf Ebrahimi skip |= ValidatePushConstantBlockAgainstPipeline(report_data, push_constant_ranges, src, src->get_def(def_insn.word(1)),
1334*b7893ccfSSadaf Ebrahimi stage);
1335*b7893ccfSSadaf Ebrahimi }
1336*b7893ccfSSadaf Ebrahimi }
1337*b7893ccfSSadaf Ebrahimi
1338*b7893ccfSSadaf Ebrahimi return skip;
1339*b7893ccfSSadaf Ebrahimi }
1340*b7893ccfSSadaf Ebrahimi
1341*b7893ccfSSadaf Ebrahimi // Validate that data for each specialization entry is fully contained within the buffer.
ValidateSpecializationOffsets(debug_report_data const * report_data,VkPipelineShaderStageCreateInfo const * info)1342*b7893ccfSSadaf Ebrahimi static bool ValidateSpecializationOffsets(debug_report_data const *report_data, VkPipelineShaderStageCreateInfo const *info) {
1343*b7893ccfSSadaf Ebrahimi bool skip = false;
1344*b7893ccfSSadaf Ebrahimi
1345*b7893ccfSSadaf Ebrahimi VkSpecializationInfo const *spec = info->pSpecializationInfo;
1346*b7893ccfSSadaf Ebrahimi
1347*b7893ccfSSadaf Ebrahimi if (spec) {
1348*b7893ccfSSadaf Ebrahimi for (auto i = 0u; i < spec->mapEntryCount; i++) {
1349*b7893ccfSSadaf Ebrahimi // TODO: This is a good place for "VUID-VkSpecializationInfo-offset-00773".
1350*b7893ccfSSadaf Ebrahimi if (spec->pMapEntries[i].offset + spec->pMapEntries[i].size > spec->dataSize) {
1351*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0,
1352*b7893ccfSSadaf Ebrahimi "VUID-VkSpecializationInfo-pMapEntries-00774",
1353*b7893ccfSSadaf Ebrahimi "Specialization entry %u (for constant id %u) references memory outside provided specialization "
1354*b7893ccfSSadaf Ebrahimi "data (bytes %u.." PRINTF_SIZE_T_SPECIFIER "; " PRINTF_SIZE_T_SPECIFIER " bytes provided)..",
1355*b7893ccfSSadaf Ebrahimi i, spec->pMapEntries[i].constantID, spec->pMapEntries[i].offset,
1356*b7893ccfSSadaf Ebrahimi spec->pMapEntries[i].offset + spec->pMapEntries[i].size - 1, spec->dataSize);
1357*b7893ccfSSadaf Ebrahimi }
1358*b7893ccfSSadaf Ebrahimi }
1359*b7893ccfSSadaf Ebrahimi }
1360*b7893ccfSSadaf Ebrahimi
1361*b7893ccfSSadaf Ebrahimi return skip;
1362*b7893ccfSSadaf Ebrahimi }
1363*b7893ccfSSadaf Ebrahimi
1364*b7893ccfSSadaf Ebrahimi // TODO (jbolz): Can this return a const reference?
TypeToDescriptorTypeSet(SHADER_MODULE_STATE const * module,uint32_t type_id,unsigned & descriptor_count)1365*b7893ccfSSadaf Ebrahimi static std::set<uint32_t> TypeToDescriptorTypeSet(SHADER_MODULE_STATE const *module, uint32_t type_id, unsigned &descriptor_count) {
1366*b7893ccfSSadaf Ebrahimi auto type = module->get_def(type_id);
1367*b7893ccfSSadaf Ebrahimi bool is_storage_buffer = false;
1368*b7893ccfSSadaf Ebrahimi descriptor_count = 1;
1369*b7893ccfSSadaf Ebrahimi std::set<uint32_t> ret;
1370*b7893ccfSSadaf Ebrahimi
1371*b7893ccfSSadaf Ebrahimi // Strip off any array or ptrs. Where we remove array levels, adjust the descriptor count for each dimension.
1372*b7893ccfSSadaf Ebrahimi while (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypePointer || type.opcode() == spv::OpTypeRuntimeArray) {
1373*b7893ccfSSadaf Ebrahimi if (type.opcode() == spv::OpTypeRuntimeArray) {
1374*b7893ccfSSadaf Ebrahimi descriptor_count = 0;
1375*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(2));
1376*b7893ccfSSadaf Ebrahimi } else if (type.opcode() == spv::OpTypeArray) {
1377*b7893ccfSSadaf Ebrahimi descriptor_count *= GetConstantValue(module, type.word(3));
1378*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(2));
1379*b7893ccfSSadaf Ebrahimi } else {
1380*b7893ccfSSadaf Ebrahimi if (type.word(2) == spv::StorageClassStorageBuffer) {
1381*b7893ccfSSadaf Ebrahimi is_storage_buffer = true;
1382*b7893ccfSSadaf Ebrahimi }
1383*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(3));
1384*b7893ccfSSadaf Ebrahimi }
1385*b7893ccfSSadaf Ebrahimi }
1386*b7893ccfSSadaf Ebrahimi
1387*b7893ccfSSadaf Ebrahimi switch (type.opcode()) {
1388*b7893ccfSSadaf Ebrahimi case spv::OpTypeStruct: {
1389*b7893ccfSSadaf Ebrahimi for (auto insn : *module) {
1390*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpDecorate && insn.word(1) == type.word(1)) {
1391*b7893ccfSSadaf Ebrahimi if (insn.word(2) == spv::DecorationBlock) {
1392*b7893ccfSSadaf Ebrahimi if (is_storage_buffer) {
1393*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
1394*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
1395*b7893ccfSSadaf Ebrahimi return ret;
1396*b7893ccfSSadaf Ebrahimi } else {
1397*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
1398*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
1399*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT);
1400*b7893ccfSSadaf Ebrahimi return ret;
1401*b7893ccfSSadaf Ebrahimi }
1402*b7893ccfSSadaf Ebrahimi } else if (insn.word(2) == spv::DecorationBufferBlock) {
1403*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
1404*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
1405*b7893ccfSSadaf Ebrahimi return ret;
1406*b7893ccfSSadaf Ebrahimi }
1407*b7893ccfSSadaf Ebrahimi }
1408*b7893ccfSSadaf Ebrahimi }
1409*b7893ccfSSadaf Ebrahimi
1410*b7893ccfSSadaf Ebrahimi // Invalid
1411*b7893ccfSSadaf Ebrahimi return ret;
1412*b7893ccfSSadaf Ebrahimi }
1413*b7893ccfSSadaf Ebrahimi
1414*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampler:
1415*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_SAMPLER);
1416*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
1417*b7893ccfSSadaf Ebrahimi return ret;
1418*b7893ccfSSadaf Ebrahimi
1419*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampledImage: {
1420*b7893ccfSSadaf Ebrahimi // Slight relaxation for some GLSL historical madness: samplerBuffer doesn't really have a sampler, and a texel
1421*b7893ccfSSadaf Ebrahimi // buffer descriptor doesn't really provide one. Allow this slight mismatch.
1422*b7893ccfSSadaf Ebrahimi auto image_type = module->get_def(type.word(2));
1423*b7893ccfSSadaf Ebrahimi auto dim = image_type.word(3);
1424*b7893ccfSSadaf Ebrahimi auto sampled = image_type.word(7);
1425*b7893ccfSSadaf Ebrahimi if (dim == spv::DimBuffer && sampled == 1) {
1426*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
1427*b7893ccfSSadaf Ebrahimi return ret;
1428*b7893ccfSSadaf Ebrahimi }
1429*b7893ccfSSadaf Ebrahimi }
1430*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
1431*b7893ccfSSadaf Ebrahimi return ret;
1432*b7893ccfSSadaf Ebrahimi
1433*b7893ccfSSadaf Ebrahimi case spv::OpTypeImage: {
1434*b7893ccfSSadaf Ebrahimi // Many descriptor types backing image types-- depends on dimension and whether the image will be used with a sampler.
1435*b7893ccfSSadaf Ebrahimi // SPIRV for Vulkan requires that sampled be 1 or 2 -- leaving the decision to runtime is unacceptable.
1436*b7893ccfSSadaf Ebrahimi auto dim = type.word(3);
1437*b7893ccfSSadaf Ebrahimi auto sampled = type.word(7);
1438*b7893ccfSSadaf Ebrahimi
1439*b7893ccfSSadaf Ebrahimi if (dim == spv::DimSubpassData) {
1440*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
1441*b7893ccfSSadaf Ebrahimi return ret;
1442*b7893ccfSSadaf Ebrahimi } else if (dim == spv::DimBuffer) {
1443*b7893ccfSSadaf Ebrahimi if (sampled == 1) {
1444*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
1445*b7893ccfSSadaf Ebrahimi return ret;
1446*b7893ccfSSadaf Ebrahimi } else {
1447*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
1448*b7893ccfSSadaf Ebrahimi return ret;
1449*b7893ccfSSadaf Ebrahimi }
1450*b7893ccfSSadaf Ebrahimi } else if (sampled == 1) {
1451*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
1452*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
1453*b7893ccfSSadaf Ebrahimi return ret;
1454*b7893ccfSSadaf Ebrahimi } else {
1455*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
1456*b7893ccfSSadaf Ebrahimi return ret;
1457*b7893ccfSSadaf Ebrahimi }
1458*b7893ccfSSadaf Ebrahimi }
1459*b7893ccfSSadaf Ebrahimi case spv::OpTypeAccelerationStructureNV:
1460*b7893ccfSSadaf Ebrahimi ret.insert(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV);
1461*b7893ccfSSadaf Ebrahimi return ret;
1462*b7893ccfSSadaf Ebrahimi
1463*b7893ccfSSadaf Ebrahimi // We shouldn't really see any other junk types -- but if we do, they're a mismatch.
1464*b7893ccfSSadaf Ebrahimi default:
1465*b7893ccfSSadaf Ebrahimi return ret; // Matches nothing
1466*b7893ccfSSadaf Ebrahimi }
1467*b7893ccfSSadaf Ebrahimi }
1468*b7893ccfSSadaf Ebrahimi
string_descriptorTypes(const std::set<uint32_t> & descriptor_types)1469*b7893ccfSSadaf Ebrahimi static std::string string_descriptorTypes(const std::set<uint32_t> &descriptor_types) {
1470*b7893ccfSSadaf Ebrahimi std::stringstream ss;
1471*b7893ccfSSadaf Ebrahimi for (auto it = descriptor_types.begin(); it != descriptor_types.end(); ++it) {
1472*b7893ccfSSadaf Ebrahimi if (ss.tellp()) ss << ", ";
1473*b7893ccfSSadaf Ebrahimi ss << string_VkDescriptorType(VkDescriptorType(*it));
1474*b7893ccfSSadaf Ebrahimi }
1475*b7893ccfSSadaf Ebrahimi return ss.str();
1476*b7893ccfSSadaf Ebrahimi }
1477*b7893ccfSSadaf Ebrahimi
RequirePropertyFlag(debug_report_data const * report_data,VkBool32 check,char const * flag,char const * structure)1478*b7893ccfSSadaf Ebrahimi static bool RequirePropertyFlag(debug_report_data const *report_data, VkBool32 check, char const *flag, char const *structure) {
1479*b7893ccfSSadaf Ebrahimi if (!check) {
1480*b7893ccfSSadaf Ebrahimi if (log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
1481*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_ExceedDeviceLimit, "Shader requires flag %s set in %s but it is not set on the device", flag,
1482*b7893ccfSSadaf Ebrahimi structure)) {
1483*b7893ccfSSadaf Ebrahimi return true;
1484*b7893ccfSSadaf Ebrahimi }
1485*b7893ccfSSadaf Ebrahimi }
1486*b7893ccfSSadaf Ebrahimi
1487*b7893ccfSSadaf Ebrahimi return false;
1488*b7893ccfSSadaf Ebrahimi }
1489*b7893ccfSSadaf Ebrahimi
RequireFeature(debug_report_data const * report_data,VkBool32 feature,char const * feature_name)1490*b7893ccfSSadaf Ebrahimi static bool RequireFeature(debug_report_data const *report_data, VkBool32 feature, char const *feature_name) {
1491*b7893ccfSSadaf Ebrahimi if (!feature) {
1492*b7893ccfSSadaf Ebrahimi if (log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
1493*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled, "Shader requires %s but is not enabled on the device", feature_name)) {
1494*b7893ccfSSadaf Ebrahimi return true;
1495*b7893ccfSSadaf Ebrahimi }
1496*b7893ccfSSadaf Ebrahimi }
1497*b7893ccfSSadaf Ebrahimi
1498*b7893ccfSSadaf Ebrahimi return false;
1499*b7893ccfSSadaf Ebrahimi }
1500*b7893ccfSSadaf Ebrahimi
RequireExtension(debug_report_data const * report_data,bool extension,char const * extension_name)1501*b7893ccfSSadaf Ebrahimi static bool RequireExtension(debug_report_data const *report_data, bool extension, char const *extension_name) {
1502*b7893ccfSSadaf Ebrahimi if (!extension) {
1503*b7893ccfSSadaf Ebrahimi if (log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
1504*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled, "Shader requires extension %s but is not enabled on the device",
1505*b7893ccfSSadaf Ebrahimi extension_name)) {
1506*b7893ccfSSadaf Ebrahimi return true;
1507*b7893ccfSSadaf Ebrahimi }
1508*b7893ccfSSadaf Ebrahimi }
1509*b7893ccfSSadaf Ebrahimi
1510*b7893ccfSSadaf Ebrahimi return false;
1511*b7893ccfSSadaf Ebrahimi }
1512*b7893ccfSSadaf Ebrahimi
ValidateShaderCapabilities(SHADER_MODULE_STATE const * src,VkShaderStageFlagBits stage) const1513*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateShaderCapabilities(SHADER_MODULE_STATE const *src, VkShaderStageFlagBits stage) const {
1514*b7893ccfSSadaf Ebrahimi bool skip = false;
1515*b7893ccfSSadaf Ebrahimi
1516*b7893ccfSSadaf Ebrahimi struct FeaturePointer {
1517*b7893ccfSSadaf Ebrahimi // Callable object to test if this feature is enabled in the given aggregate feature struct
1518*b7893ccfSSadaf Ebrahimi const std::function<VkBool32(const DeviceFeatures &)> IsEnabled;
1519*b7893ccfSSadaf Ebrahimi
1520*b7893ccfSSadaf Ebrahimi // Test if feature pointer is populated
1521*b7893ccfSSadaf Ebrahimi explicit operator bool() const { return static_cast<bool>(IsEnabled); }
1522*b7893ccfSSadaf Ebrahimi
1523*b7893ccfSSadaf Ebrahimi // Default and nullptr constructor to create an empty FeaturePointer
1524*b7893ccfSSadaf Ebrahimi FeaturePointer() : IsEnabled(nullptr) {}
1525*b7893ccfSSadaf Ebrahimi FeaturePointer(std::nullptr_t ptr) : IsEnabled(nullptr) {}
1526*b7893ccfSSadaf Ebrahimi
1527*b7893ccfSSadaf Ebrahimi // Constructors to populate FeaturePointer based on given pointer to member
1528*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceFeatures::*ptr)
1529*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.core.*ptr; }) {}
1530*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceDescriptorIndexingFeaturesEXT::*ptr)
1531*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.descriptor_indexing.*ptr; }) {}
1532*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDevice8BitStorageFeaturesKHR::*ptr)
1533*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.eight_bit_storage.*ptr; }) {}
1534*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceTransformFeedbackFeaturesEXT::*ptr)
1535*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.transform_feedback_features.*ptr; }) {}
1536*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceFloat16Int8FeaturesKHR::*ptr)
1537*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.float16_int8.*ptr; }) {}
1538*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceScalarBlockLayoutFeaturesEXT::*ptr)
1539*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.scalar_block_layout_features.*ptr; }) {}
1540*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceCooperativeMatrixFeaturesNV::*ptr)
1541*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.cooperative_matrix_features.*ptr; }) {}
1542*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceFloatControlsPropertiesKHR::*ptr)
1543*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.float_controls.*ptr; }) {}
1544*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR::*ptr)
1545*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.uniform_buffer_standard_layout.*ptr; }) {}
1546*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceComputeShaderDerivativesFeaturesNV::*ptr)
1547*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.compute_shader_derivatives_features.*ptr; }) {}
1548*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV::*ptr)
1549*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.fragment_shader_barycentric_features.*ptr; }) {}
1550*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceShaderImageFootprintFeaturesNV::*ptr)
1551*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.shader_image_footprint_features.*ptr; }) {}
1552*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::*ptr)
1553*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.fragment_shader_interlock_features.*ptr; }) {}
1554*b7893ccfSSadaf Ebrahimi FeaturePointer(VkBool32 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::*ptr)
1555*b7893ccfSSadaf Ebrahimi : IsEnabled([=](const DeviceFeatures &features) { return features.demote_to_helper_invocation_features.*ptr; }) {}
1556*b7893ccfSSadaf Ebrahimi };
1557*b7893ccfSSadaf Ebrahimi
1558*b7893ccfSSadaf Ebrahimi struct CapabilityInfo {
1559*b7893ccfSSadaf Ebrahimi char const *name;
1560*b7893ccfSSadaf Ebrahimi FeaturePointer feature;
1561*b7893ccfSSadaf Ebrahimi bool DeviceExtensions::*extension;
1562*b7893ccfSSadaf Ebrahimi };
1563*b7893ccfSSadaf Ebrahimi
1564*b7893ccfSSadaf Ebrahimi // clang-format off
1565*b7893ccfSSadaf Ebrahimi static const std::unordered_multimap<uint32_t, CapabilityInfo> capabilities = {
1566*b7893ccfSSadaf Ebrahimi // Capabilities always supported by a Vulkan 1.0 implementation -- no
1567*b7893ccfSSadaf Ebrahimi // feature bits.
1568*b7893ccfSSadaf Ebrahimi {spv::CapabilityMatrix, {nullptr}},
1569*b7893ccfSSadaf Ebrahimi {spv::CapabilityShader, {nullptr}},
1570*b7893ccfSSadaf Ebrahimi {spv::CapabilityInputAttachment, {nullptr}},
1571*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampled1D, {nullptr}},
1572*b7893ccfSSadaf Ebrahimi {spv::CapabilityImage1D, {nullptr}},
1573*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampledBuffer, {nullptr}},
1574*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageImageExtendedFormats, {nullptr}},
1575*b7893ccfSSadaf Ebrahimi {spv::CapabilityImageQuery, {nullptr}},
1576*b7893ccfSSadaf Ebrahimi {spv::CapabilityDerivativeControl, {nullptr}},
1577*b7893ccfSSadaf Ebrahimi
1578*b7893ccfSSadaf Ebrahimi // Capabilities that are optionally supported, but require a feature to
1579*b7893ccfSSadaf Ebrahimi // be enabled on the device
1580*b7893ccfSSadaf Ebrahimi {spv::CapabilityGeometry, {"VkPhysicalDeviceFeatures::geometryShader", &VkPhysicalDeviceFeatures::geometryShader}},
1581*b7893ccfSSadaf Ebrahimi {spv::CapabilityTessellation, {"VkPhysicalDeviceFeatures::tessellationShader", &VkPhysicalDeviceFeatures::tessellationShader}},
1582*b7893ccfSSadaf Ebrahimi {spv::CapabilityFloat64, {"VkPhysicalDeviceFeatures::shaderFloat64", &VkPhysicalDeviceFeatures::shaderFloat64}},
1583*b7893ccfSSadaf Ebrahimi {spv::CapabilityInt64, {"VkPhysicalDeviceFeatures::shaderInt64", &VkPhysicalDeviceFeatures::shaderInt64}},
1584*b7893ccfSSadaf Ebrahimi {spv::CapabilityTessellationPointSize, {"VkPhysicalDeviceFeatures::shaderTessellationAndGeometryPointSize", &VkPhysicalDeviceFeatures::shaderTessellationAndGeometryPointSize}},
1585*b7893ccfSSadaf Ebrahimi {spv::CapabilityGeometryPointSize, {"VkPhysicalDeviceFeatures::shaderTessellationAndGeometryPointSize", &VkPhysicalDeviceFeatures::shaderTessellationAndGeometryPointSize}},
1586*b7893ccfSSadaf Ebrahimi {spv::CapabilityImageGatherExtended, {"VkPhysicalDeviceFeatures::shaderImageGatherExtended", &VkPhysicalDeviceFeatures::shaderImageGatherExtended}},
1587*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageImageMultisample, {"VkPhysicalDeviceFeatures::shaderStorageImageMultisample", &VkPhysicalDeviceFeatures::shaderStorageImageMultisample}},
1588*b7893ccfSSadaf Ebrahimi {spv::CapabilityUniformBufferArrayDynamicIndexing, {"VkPhysicalDeviceFeatures::shaderUniformBufferArrayDynamicIndexing", &VkPhysicalDeviceFeatures::shaderUniformBufferArrayDynamicIndexing}},
1589*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampledImageArrayDynamicIndexing, {"VkPhysicalDeviceFeatures::shaderSampledImageArrayDynamicIndexing", &VkPhysicalDeviceFeatures::shaderSampledImageArrayDynamicIndexing}},
1590*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageBufferArrayDynamicIndexing, {"VkPhysicalDeviceFeatures::shaderStorageBufferArrayDynamicIndexing", &VkPhysicalDeviceFeatures::shaderStorageBufferArrayDynamicIndexing}},
1591*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageImageArrayDynamicIndexing, {"VkPhysicalDeviceFeatures::shaderStorageImageArrayDynamicIndexing", &VkPhysicalDeviceFeatures::shaderStorageBufferArrayDynamicIndexing}},
1592*b7893ccfSSadaf Ebrahimi {spv::CapabilityClipDistance, {"VkPhysicalDeviceFeatures::shaderClipDistance", &VkPhysicalDeviceFeatures::shaderClipDistance}},
1593*b7893ccfSSadaf Ebrahimi {spv::CapabilityCullDistance, {"VkPhysicalDeviceFeatures::shaderCullDistance", &VkPhysicalDeviceFeatures::shaderCullDistance}},
1594*b7893ccfSSadaf Ebrahimi {spv::CapabilityImageCubeArray, {"VkPhysicalDeviceFeatures::imageCubeArray", &VkPhysicalDeviceFeatures::imageCubeArray}},
1595*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampleRateShading, {"VkPhysicalDeviceFeatures::sampleRateShading", &VkPhysicalDeviceFeatures::sampleRateShading}},
1596*b7893ccfSSadaf Ebrahimi {spv::CapabilitySparseResidency, {"VkPhysicalDeviceFeatures::shaderResourceResidency", &VkPhysicalDeviceFeatures::shaderResourceResidency}},
1597*b7893ccfSSadaf Ebrahimi {spv::CapabilityMinLod, {"VkPhysicalDeviceFeatures::shaderResourceMinLod", &VkPhysicalDeviceFeatures::shaderResourceMinLod}},
1598*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampledCubeArray, {"VkPhysicalDeviceFeatures::imageCubeArray", &VkPhysicalDeviceFeatures::imageCubeArray}},
1599*b7893ccfSSadaf Ebrahimi {spv::CapabilityImageMSArray, {"VkPhysicalDeviceFeatures::shaderStorageImageMultisample", &VkPhysicalDeviceFeatures::shaderStorageImageMultisample}},
1600*b7893ccfSSadaf Ebrahimi {spv::CapabilityInterpolationFunction, {"VkPhysicalDeviceFeatures::sampleRateShading", &VkPhysicalDeviceFeatures::sampleRateShading}},
1601*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageImageReadWithoutFormat, {"VkPhysicalDeviceFeatures::shaderStorageImageReadWithoutFormat", &VkPhysicalDeviceFeatures::shaderStorageImageReadWithoutFormat}},
1602*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageImageWriteWithoutFormat, {"VkPhysicalDeviceFeatures::shaderStorageImageWriteWithoutFormat", &VkPhysicalDeviceFeatures::shaderStorageImageWriteWithoutFormat}},
1603*b7893ccfSSadaf Ebrahimi {spv::CapabilityMultiViewport, {"VkPhysicalDeviceFeatures::multiViewport", &VkPhysicalDeviceFeatures::multiViewport}},
1604*b7893ccfSSadaf Ebrahimi
1605*b7893ccfSSadaf Ebrahimi {spv::CapabilityShaderNonUniformEXT, {VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_ext_descriptor_indexing}},
1606*b7893ccfSSadaf Ebrahimi {spv::CapabilityRuntimeDescriptorArrayEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::runtimeDescriptorArray", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::runtimeDescriptorArray}},
1607*b7893ccfSSadaf Ebrahimi {spv::CapabilityInputAttachmentArrayDynamicIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderInputAttachmentArrayDynamicIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderInputAttachmentArrayDynamicIndexing}},
1608*b7893ccfSSadaf Ebrahimi {spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderUniformTexelBufferArrayDynamicIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderUniformTexelBufferArrayDynamicIndexing}},
1609*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageTexelBufferArrayDynamicIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageTexelBufferArrayDynamicIndexing}},
1610*b7893ccfSSadaf Ebrahimi {spv::CapabilityUniformBufferArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderUniformBufferArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderUniformBufferArrayNonUniformIndexing}},
1611*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampledImageArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderSampledImageArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderSampledImageArrayNonUniformIndexing}},
1612*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageBufferArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageBufferArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageBufferArrayNonUniformIndexing}},
1613*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageImageArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageImageArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageImageArrayNonUniformIndexing}},
1614*b7893ccfSSadaf Ebrahimi {spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderInputAttachmentArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderInputAttachmentArrayNonUniformIndexing}},
1615*b7893ccfSSadaf Ebrahimi {spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderUniformTexelBufferArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderUniformTexelBufferArrayNonUniformIndexing}},
1616*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT, {"VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageTexelBufferArrayNonUniformIndexing", &VkPhysicalDeviceDescriptorIndexingFeaturesEXT::shaderStorageTexelBufferArrayNonUniformIndexing}},
1617*b7893ccfSSadaf Ebrahimi
1618*b7893ccfSSadaf Ebrahimi // Capabilities that require an extension
1619*b7893ccfSSadaf Ebrahimi {spv::CapabilityDrawParameters, {VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_khr_shader_draw_parameters}},
1620*b7893ccfSSadaf Ebrahimi {spv::CapabilityGeometryShaderPassthroughNV, {VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_nv_geometry_shader_passthrough}},
1621*b7893ccfSSadaf Ebrahimi {spv::CapabilitySampleMaskOverrideCoverageNV, {VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_nv_sample_mask_override_coverage}},
1622*b7893ccfSSadaf Ebrahimi {spv::CapabilityShaderViewportIndexLayerEXT, {VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_ext_shader_viewport_index_layer}},
1623*b7893ccfSSadaf Ebrahimi {spv::CapabilityShaderViewportIndexLayerNV, {VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_nv_viewport_array2}},
1624*b7893ccfSSadaf Ebrahimi {spv::CapabilityShaderViewportMaskNV, {VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_nv_viewport_array2}},
1625*b7893ccfSSadaf Ebrahimi {spv::CapabilitySubgroupBallotKHR, {VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_ext_shader_subgroup_ballot }},
1626*b7893ccfSSadaf Ebrahimi {spv::CapabilitySubgroupVoteKHR, {VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_ext_shader_subgroup_vote }},
1627*b7893ccfSSadaf Ebrahimi {spv::CapabilityGroupNonUniformPartitionedNV, {VK_NV_SHADER_SUBGROUP_PARTITIONED_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_nv_shader_subgroup_partitioned}},
1628*b7893ccfSSadaf Ebrahimi {spv::CapabilityInt64Atomics, {VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, nullptr, &DeviceExtensions::vk_khr_shader_atomic_int64 }},
1629*b7893ccfSSadaf Ebrahimi
1630*b7893ccfSSadaf Ebrahimi {spv::CapabilityComputeDerivativeGroupQuadsNV, {"VkPhysicalDeviceComputeShaderDerivativesFeaturesNV::computeDerivativeGroupQuads", &VkPhysicalDeviceComputeShaderDerivativesFeaturesNV::computeDerivativeGroupQuads, &DeviceExtensions::vk_nv_compute_shader_derivatives}},
1631*b7893ccfSSadaf Ebrahimi {spv::CapabilityComputeDerivativeGroupLinearNV, {"VkPhysicalDeviceComputeShaderDerivativesFeaturesNV::computeDerivativeGroupLinear", &VkPhysicalDeviceComputeShaderDerivativesFeaturesNV::computeDerivativeGroupLinear, &DeviceExtensions::vk_nv_compute_shader_derivatives}},
1632*b7893ccfSSadaf Ebrahimi {spv::CapabilityFragmentBarycentricNV, {"VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV::fragmentShaderBarycentric", &VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV::fragmentShaderBarycentric, &DeviceExtensions::vk_nv_fragment_shader_barycentric}},
1633*b7893ccfSSadaf Ebrahimi
1634*b7893ccfSSadaf Ebrahimi {spv::CapabilityStorageBuffer8BitAccess, {"VkPhysicalDevice8BitStorageFeaturesKHR::storageBuffer8BitAccess", &VkPhysicalDevice8BitStorageFeaturesKHR::storageBuffer8BitAccess, &DeviceExtensions::vk_khr_8bit_storage}},
1635*b7893ccfSSadaf Ebrahimi {spv::CapabilityUniformAndStorageBuffer8BitAccess, {"VkPhysicalDevice8BitStorageFeaturesKHR::uniformAndStorageBuffer8BitAccess", &VkPhysicalDevice8BitStorageFeaturesKHR::uniformAndStorageBuffer8BitAccess, &DeviceExtensions::vk_khr_8bit_storage}},
1636*b7893ccfSSadaf Ebrahimi {spv::CapabilityStoragePushConstant8, {"VkPhysicalDevice8BitStorageFeaturesKHR::storagePushConstant8", &VkPhysicalDevice8BitStorageFeaturesKHR::storagePushConstant8, &DeviceExtensions::vk_khr_8bit_storage}},
1637*b7893ccfSSadaf Ebrahimi
1638*b7893ccfSSadaf Ebrahimi {spv::CapabilityTransformFeedback, { "VkPhysicalDeviceTransformFeedbackFeaturesEXT::transformFeedback", &VkPhysicalDeviceTransformFeedbackFeaturesEXT::transformFeedback, &DeviceExtensions::vk_ext_transform_feedback}},
1639*b7893ccfSSadaf Ebrahimi {spv::CapabilityGeometryStreams, { "VkPhysicalDeviceTransformFeedbackFeaturesEXT::geometryStreams", &VkPhysicalDeviceTransformFeedbackFeaturesEXT::geometryStreams, &DeviceExtensions::vk_ext_transform_feedback}},
1640*b7893ccfSSadaf Ebrahimi
1641*b7893ccfSSadaf Ebrahimi {spv::CapabilityFloat16, {"VkPhysicalDeviceFloat16Int8FeaturesKHR::shaderFloat16", &VkPhysicalDeviceFloat16Int8FeaturesKHR::shaderFloat16, &DeviceExtensions::vk_khr_shader_float16_int8}},
1642*b7893ccfSSadaf Ebrahimi {spv::CapabilityInt8, {"VkPhysicalDeviceFloat16Int8FeaturesKHR::shaderInt8", &VkPhysicalDeviceFloat16Int8FeaturesKHR::shaderInt8, &DeviceExtensions::vk_khr_shader_float16_int8}},
1643*b7893ccfSSadaf Ebrahimi
1644*b7893ccfSSadaf Ebrahimi {spv::CapabilityImageFootprintNV, {"VkPhysicalDeviceShaderImageFootprintFeaturesNV::imageFootprint", &VkPhysicalDeviceShaderImageFootprintFeaturesNV::imageFootprint, &DeviceExtensions::vk_nv_shader_image_footprint}},
1645*b7893ccfSSadaf Ebrahimi
1646*b7893ccfSSadaf Ebrahimi {spv::CapabilityCooperativeMatrixNV, {"VkPhysicalDeviceCooperativeMatrixFeaturesNV::cooperativeMatrix", &VkPhysicalDeviceCooperativeMatrixFeaturesNV::cooperativeMatrix, &DeviceExtensions::vk_nv_cooperative_matrix}},
1647*b7893ccfSSadaf Ebrahimi
1648*b7893ccfSSadaf Ebrahimi {spv::CapabilitySignedZeroInfNanPreserve, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderSignedZeroInfNanPreserveFloat16", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderSignedZeroInfNanPreserveFloat16, &DeviceExtensions::vk_khr_shader_float_controls}},
1649*b7893ccfSSadaf Ebrahimi {spv::CapabilitySignedZeroInfNanPreserve, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderSignedZeroInfNanPreserveFloat32", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderSignedZeroInfNanPreserveFloat32, &DeviceExtensions::vk_khr_shader_float_controls}},
1650*b7893ccfSSadaf Ebrahimi {spv::CapabilitySignedZeroInfNanPreserve, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderSignedZeroInfNanPreserveFloat64", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderSignedZeroInfNanPreserveFloat64, &DeviceExtensions::vk_khr_shader_float_controls}},
1651*b7893ccfSSadaf Ebrahimi {spv::CapabilityDenormPreserve, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormPreserveFloat16", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormPreserveFloat16, &DeviceExtensions::vk_khr_shader_float_controls}},
1652*b7893ccfSSadaf Ebrahimi {spv::CapabilityDenormPreserve, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormPreserveFloat32", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormPreserveFloat32, &DeviceExtensions::vk_khr_shader_float_controls}},
1653*b7893ccfSSadaf Ebrahimi {spv::CapabilityDenormPreserve, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormPreserveFloat64", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormPreserveFloat64, &DeviceExtensions::vk_khr_shader_float_controls}},
1654*b7893ccfSSadaf Ebrahimi {spv::CapabilityDenormFlushToZero, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormFlushToZeroFloat16", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormFlushToZeroFloat16, &DeviceExtensions::vk_khr_shader_float_controls}},
1655*b7893ccfSSadaf Ebrahimi {spv::CapabilityDenormFlushToZero, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormFlushToZeroFloat32", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormFlushToZeroFloat32, &DeviceExtensions::vk_khr_shader_float_controls}},
1656*b7893ccfSSadaf Ebrahimi {spv::CapabilityDenormFlushToZero, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormFlushToZeroFloat64", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderDenormFlushToZeroFloat64, &DeviceExtensions::vk_khr_shader_float_controls}},
1657*b7893ccfSSadaf Ebrahimi {spv::CapabilityRoundingModeRTE, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTEFloat16", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTEFloat16, &DeviceExtensions::vk_khr_shader_float_controls}},
1658*b7893ccfSSadaf Ebrahimi {spv::CapabilityRoundingModeRTE, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTEFloat32", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTEFloat32, &DeviceExtensions::vk_khr_shader_float_controls}},
1659*b7893ccfSSadaf Ebrahimi {spv::CapabilityRoundingModeRTE, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTEFloat64", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTEFloat64, &DeviceExtensions::vk_khr_shader_float_controls}},
1660*b7893ccfSSadaf Ebrahimi {spv::CapabilityRoundingModeRTZ, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTZFloat16", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTZFloat16, &DeviceExtensions::vk_khr_shader_float_controls}},
1661*b7893ccfSSadaf Ebrahimi {spv::CapabilityRoundingModeRTZ, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTZFloat32", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTZFloat32, &DeviceExtensions::vk_khr_shader_float_controls}},
1662*b7893ccfSSadaf Ebrahimi {spv::CapabilityRoundingModeRTZ, {"VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTZFloat64", &VkPhysicalDeviceFloatControlsPropertiesKHR::shaderRoundingModeRTZFloat64, &DeviceExtensions::vk_khr_shader_float_controls}},
1663*b7893ccfSSadaf Ebrahimi
1664*b7893ccfSSadaf Ebrahimi {spv::CapabilityFragmentShaderSampleInterlockEXT, {"VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderSampleInterlock", &VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderSampleInterlock, &DeviceExtensions::vk_ext_fragment_shader_interlock}},
1665*b7893ccfSSadaf Ebrahimi {spv::CapabilityFragmentShaderPixelInterlockEXT, {"VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderPixelInterlock", &VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderPixelInterlock, &DeviceExtensions::vk_ext_fragment_shader_interlock}},
1666*b7893ccfSSadaf Ebrahimi {spv::CapabilityFragmentShaderShadingRateInterlockEXT, {"VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderShadingRateInterlock", &VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderShadingRateInterlock, &DeviceExtensions::vk_ext_fragment_shader_interlock}},
1667*b7893ccfSSadaf Ebrahimi {spv::CapabilityDemoteToHelperInvocationEXT, {"VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::shaderDemoteToHelperInvocation", &VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::shaderDemoteToHelperInvocation, &DeviceExtensions::vk_ext_shader_demote_to_helper_invocation}},
1668*b7893ccfSSadaf Ebrahimi };
1669*b7893ccfSSadaf Ebrahimi // clang-format on
1670*b7893ccfSSadaf Ebrahimi
1671*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
1672*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpCapability) {
1673*b7893ccfSSadaf Ebrahimi size_t n = capabilities.count(insn.word(1));
1674*b7893ccfSSadaf Ebrahimi if (1 == n) { // key occurs exactly once
1675*b7893ccfSSadaf Ebrahimi auto it = capabilities.find(insn.word(1));
1676*b7893ccfSSadaf Ebrahimi if (it != capabilities.end()) {
1677*b7893ccfSSadaf Ebrahimi if (it->second.feature) {
1678*b7893ccfSSadaf Ebrahimi skip |= RequireFeature(report_data, it->second.feature.IsEnabled(enabled_features), it->second.name);
1679*b7893ccfSSadaf Ebrahimi }
1680*b7893ccfSSadaf Ebrahimi if (it->second.extension) {
1681*b7893ccfSSadaf Ebrahimi skip |= RequireExtension(report_data, device_extensions.*(it->second.extension), it->second.name);
1682*b7893ccfSSadaf Ebrahimi }
1683*b7893ccfSSadaf Ebrahimi }
1684*b7893ccfSSadaf Ebrahimi } else if (1 < n) { // key occurs multiple times, at least one must be enabled
1685*b7893ccfSSadaf Ebrahimi bool needs_feature = false, has_feature = false;
1686*b7893ccfSSadaf Ebrahimi bool needs_ext = false, has_ext = false;
1687*b7893ccfSSadaf Ebrahimi std::string feature_names = "(one of) [ ";
1688*b7893ccfSSadaf Ebrahimi std::string extension_names = feature_names;
1689*b7893ccfSSadaf Ebrahimi auto caps = capabilities.equal_range(insn.word(1));
1690*b7893ccfSSadaf Ebrahimi for (auto it = caps.first; it != caps.second; ++it) {
1691*b7893ccfSSadaf Ebrahimi if (it->second.feature) {
1692*b7893ccfSSadaf Ebrahimi needs_feature = true;
1693*b7893ccfSSadaf Ebrahimi has_feature = has_feature || it->second.feature.IsEnabled(enabled_features);
1694*b7893ccfSSadaf Ebrahimi feature_names += it->second.name;
1695*b7893ccfSSadaf Ebrahimi feature_names += " ";
1696*b7893ccfSSadaf Ebrahimi }
1697*b7893ccfSSadaf Ebrahimi if (it->second.extension) {
1698*b7893ccfSSadaf Ebrahimi needs_ext = true;
1699*b7893ccfSSadaf Ebrahimi has_ext = has_ext || device_extensions.*(it->second.extension);
1700*b7893ccfSSadaf Ebrahimi extension_names += it->second.name;
1701*b7893ccfSSadaf Ebrahimi extension_names += " ";
1702*b7893ccfSSadaf Ebrahimi }
1703*b7893ccfSSadaf Ebrahimi }
1704*b7893ccfSSadaf Ebrahimi if (needs_feature) {
1705*b7893ccfSSadaf Ebrahimi feature_names += "]";
1706*b7893ccfSSadaf Ebrahimi skip |= RequireFeature(report_data, has_feature, feature_names.c_str());
1707*b7893ccfSSadaf Ebrahimi }
1708*b7893ccfSSadaf Ebrahimi if (needs_ext) {
1709*b7893ccfSSadaf Ebrahimi extension_names += "]";
1710*b7893ccfSSadaf Ebrahimi skip |= RequireExtension(report_data, has_ext, extension_names.c_str());
1711*b7893ccfSSadaf Ebrahimi }
1712*b7893ccfSSadaf Ebrahimi } else { // Do group non-uniform checks
1713*b7893ccfSSadaf Ebrahimi const VkSubgroupFeatureFlags supportedOperations = phys_dev_ext_props.subgroup_props.supportedOperations;
1714*b7893ccfSSadaf Ebrahimi const VkSubgroupFeatureFlags supportedStages = phys_dev_ext_props.subgroup_props.supportedStages;
1715*b7893ccfSSadaf Ebrahimi
1716*b7893ccfSSadaf Ebrahimi switch (insn.word(1)) {
1717*b7893ccfSSadaf Ebrahimi default:
1718*b7893ccfSSadaf Ebrahimi break;
1719*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniform:
1720*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformVote:
1721*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformArithmetic:
1722*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformBallot:
1723*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformShuffle:
1724*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformShuffleRelative:
1725*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformClustered:
1726*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformQuad:
1727*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformPartitionedNV:
1728*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedStages & stage, string_VkShaderStageFlagBits(stage),
1729*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedStages");
1730*b7893ccfSSadaf Ebrahimi break;
1731*b7893ccfSSadaf Ebrahimi }
1732*b7893ccfSSadaf Ebrahimi
1733*b7893ccfSSadaf Ebrahimi switch (insn.word(1)) {
1734*b7893ccfSSadaf Ebrahimi default:
1735*b7893ccfSSadaf Ebrahimi break;
1736*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniform:
1737*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT,
1738*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_BASIC_BIT",
1739*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1740*b7893ccfSSadaf Ebrahimi break;
1741*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformVote:
1742*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT,
1743*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_VOTE_BIT",
1744*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1745*b7893ccfSSadaf Ebrahimi break;
1746*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformArithmetic:
1747*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT,
1748*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_ARITHMETIC_BIT",
1749*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1750*b7893ccfSSadaf Ebrahimi break;
1751*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformBallot:
1752*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT,
1753*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_BALLOT_BIT",
1754*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1755*b7893ccfSSadaf Ebrahimi break;
1756*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformShuffle:
1757*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT,
1758*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_SHUFFLE_BIT",
1759*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1760*b7893ccfSSadaf Ebrahimi break;
1761*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformShuffleRelative:
1762*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT,
1763*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT",
1764*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1765*b7893ccfSSadaf Ebrahimi break;
1766*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformClustered:
1767*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT,
1768*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_CLUSTERED_BIT",
1769*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1770*b7893ccfSSadaf Ebrahimi break;
1771*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformQuad:
1772*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT,
1773*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_QUAD_BIT",
1774*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1775*b7893ccfSSadaf Ebrahimi break;
1776*b7893ccfSSadaf Ebrahimi case spv::CapabilityGroupNonUniformPartitionedNV:
1777*b7893ccfSSadaf Ebrahimi RequirePropertyFlag(report_data, supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV,
1778*b7893ccfSSadaf Ebrahimi "VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV",
1779*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::supportedOperations");
1780*b7893ccfSSadaf Ebrahimi break;
1781*b7893ccfSSadaf Ebrahimi }
1782*b7893ccfSSadaf Ebrahimi }
1783*b7893ccfSSadaf Ebrahimi }
1784*b7893ccfSSadaf Ebrahimi }
1785*b7893ccfSSadaf Ebrahimi
1786*b7893ccfSSadaf Ebrahimi return skip;
1787*b7893ccfSSadaf Ebrahimi }
1788*b7893ccfSSadaf Ebrahimi
ValidateShaderStageWritableDescriptor(VkShaderStageFlagBits stage,bool has_writable_descriptor) const1789*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateShaderStageWritableDescriptor(VkShaderStageFlagBits stage, bool has_writable_descriptor) const {
1790*b7893ccfSSadaf Ebrahimi bool skip = false;
1791*b7893ccfSSadaf Ebrahimi
1792*b7893ccfSSadaf Ebrahimi if (has_writable_descriptor) {
1793*b7893ccfSSadaf Ebrahimi switch (stage) {
1794*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_COMPUTE_BIT:
1795*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_RAYGEN_BIT_NV:
1796*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
1797*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
1798*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_MISS_BIT_NV:
1799*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_INTERSECTION_BIT_NV:
1800*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_CALLABLE_BIT_NV:
1801*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_TASK_BIT_NV:
1802*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_MESH_BIT_NV:
1803*b7893ccfSSadaf Ebrahimi /* No feature requirements for writes and atomics from compute
1804*b7893ccfSSadaf Ebrahimi * raytracing, or mesh stages */
1805*b7893ccfSSadaf Ebrahimi break;
1806*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_FRAGMENT_BIT:
1807*b7893ccfSSadaf Ebrahimi skip |= RequireFeature(report_data, enabled_features.core.fragmentStoresAndAtomics, "fragmentStoresAndAtomics");
1808*b7893ccfSSadaf Ebrahimi break;
1809*b7893ccfSSadaf Ebrahimi default:
1810*b7893ccfSSadaf Ebrahimi skip |= RequireFeature(report_data, enabled_features.core.vertexPipelineStoresAndAtomics,
1811*b7893ccfSSadaf Ebrahimi "vertexPipelineStoresAndAtomics");
1812*b7893ccfSSadaf Ebrahimi break;
1813*b7893ccfSSadaf Ebrahimi }
1814*b7893ccfSSadaf Ebrahimi }
1815*b7893ccfSSadaf Ebrahimi
1816*b7893ccfSSadaf Ebrahimi return skip;
1817*b7893ccfSSadaf Ebrahimi }
1818*b7893ccfSSadaf Ebrahimi
ValidateShaderStageGroupNonUniform(SHADER_MODULE_STATE const * module,VkShaderStageFlagBits stage,std::unordered_set<uint32_t> const & accessible_ids) const1819*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateShaderStageGroupNonUniform(SHADER_MODULE_STATE const *module, VkShaderStageFlagBits stage,
1820*b7893ccfSSadaf Ebrahimi std::unordered_set<uint32_t> const &accessible_ids) const {
1821*b7893ccfSSadaf Ebrahimi bool skip = false;
1822*b7893ccfSSadaf Ebrahimi
1823*b7893ccfSSadaf Ebrahimi auto const subgroup_props = phys_dev_ext_props.subgroup_props;
1824*b7893ccfSSadaf Ebrahimi
1825*b7893ccfSSadaf Ebrahimi for (uint32_t id : accessible_ids) {
1826*b7893ccfSSadaf Ebrahimi auto inst = module->get_def(id);
1827*b7893ccfSSadaf Ebrahimi
1828*b7893ccfSSadaf Ebrahimi // Check the quad operations.
1829*b7893ccfSSadaf Ebrahimi switch (inst.opcode()) {
1830*b7893ccfSSadaf Ebrahimi default:
1831*b7893ccfSSadaf Ebrahimi break;
1832*b7893ccfSSadaf Ebrahimi case spv::OpGroupNonUniformQuadBroadcast:
1833*b7893ccfSSadaf Ebrahimi case spv::OpGroupNonUniformQuadSwap:
1834*b7893ccfSSadaf Ebrahimi if ((stage != VK_SHADER_STAGE_FRAGMENT_BIT) && (stage != VK_SHADER_STAGE_COMPUTE_BIT)) {
1835*b7893ccfSSadaf Ebrahimi skip |= RequireFeature(report_data, subgroup_props.quadOperationsInAllStages,
1836*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceSubgroupProperties::quadOperationsInAllStages");
1837*b7893ccfSSadaf Ebrahimi }
1838*b7893ccfSSadaf Ebrahimi break;
1839*b7893ccfSSadaf Ebrahimi }
1840*b7893ccfSSadaf Ebrahimi }
1841*b7893ccfSSadaf Ebrahimi
1842*b7893ccfSSadaf Ebrahimi return skip;
1843*b7893ccfSSadaf Ebrahimi }
1844*b7893ccfSSadaf Ebrahimi
ValidateShaderStageInputOutputLimits(SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const PIPELINE_STATE * pipeline,spirv_inst_iter entrypoint) const1845*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateShaderStageInputOutputLimits(SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
1846*b7893ccfSSadaf Ebrahimi const PIPELINE_STATE *pipeline, spirv_inst_iter entrypoint) const {
1847*b7893ccfSSadaf Ebrahimi if (pStage->stage == VK_SHADER_STAGE_COMPUTE_BIT || pStage->stage == VK_SHADER_STAGE_ALL_GRAPHICS ||
1848*b7893ccfSSadaf Ebrahimi pStage->stage == VK_SHADER_STAGE_ALL) {
1849*b7893ccfSSadaf Ebrahimi return false;
1850*b7893ccfSSadaf Ebrahimi }
1851*b7893ccfSSadaf Ebrahimi
1852*b7893ccfSSadaf Ebrahimi bool skip = false;
1853*b7893ccfSSadaf Ebrahimi auto const &limits = phys_dev_props.limits;
1854*b7893ccfSSadaf Ebrahimi
1855*b7893ccfSSadaf Ebrahimi std::set<uint32_t> patchIDs;
1856*b7893ccfSSadaf Ebrahimi struct Variable {
1857*b7893ccfSSadaf Ebrahimi uint32_t baseTypePtrID;
1858*b7893ccfSSadaf Ebrahimi uint32_t ID;
1859*b7893ccfSSadaf Ebrahimi uint32_t storageClass;
1860*b7893ccfSSadaf Ebrahimi };
1861*b7893ccfSSadaf Ebrahimi std::vector<Variable> variables;
1862*b7893ccfSSadaf Ebrahimi
1863*b7893ccfSSadaf Ebrahimi uint32_t numVertices = 0;
1864*b7893ccfSSadaf Ebrahimi
1865*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
1866*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
1867*b7893ccfSSadaf Ebrahimi // Find all Patch decorations
1868*b7893ccfSSadaf Ebrahimi case spv::OpDecorate:
1869*b7893ccfSSadaf Ebrahimi switch (insn.word(2)) {
1870*b7893ccfSSadaf Ebrahimi case spv::DecorationPatch: {
1871*b7893ccfSSadaf Ebrahimi patchIDs.insert(insn.word(1));
1872*b7893ccfSSadaf Ebrahimi break;
1873*b7893ccfSSadaf Ebrahimi }
1874*b7893ccfSSadaf Ebrahimi default:
1875*b7893ccfSSadaf Ebrahimi break;
1876*b7893ccfSSadaf Ebrahimi }
1877*b7893ccfSSadaf Ebrahimi break;
1878*b7893ccfSSadaf Ebrahimi // Find all input and output variables
1879*b7893ccfSSadaf Ebrahimi case spv::OpVariable: {
1880*b7893ccfSSadaf Ebrahimi Variable var = {};
1881*b7893ccfSSadaf Ebrahimi var.storageClass = insn.word(3);
1882*b7893ccfSSadaf Ebrahimi if (var.storageClass == spv::StorageClassInput || var.storageClass == spv::StorageClassOutput) {
1883*b7893ccfSSadaf Ebrahimi var.baseTypePtrID = insn.word(1);
1884*b7893ccfSSadaf Ebrahimi var.ID = insn.word(2);
1885*b7893ccfSSadaf Ebrahimi variables.push_back(var);
1886*b7893ccfSSadaf Ebrahimi }
1887*b7893ccfSSadaf Ebrahimi break;
1888*b7893ccfSSadaf Ebrahimi }
1889*b7893ccfSSadaf Ebrahimi case spv::OpExecutionMode:
1890*b7893ccfSSadaf Ebrahimi if (insn.word(1) == entrypoint.word(2)) {
1891*b7893ccfSSadaf Ebrahimi switch (insn.word(2)) {
1892*b7893ccfSSadaf Ebrahimi default:
1893*b7893ccfSSadaf Ebrahimi break;
1894*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeOutputVertices:
1895*b7893ccfSSadaf Ebrahimi numVertices = insn.word(3);
1896*b7893ccfSSadaf Ebrahimi break;
1897*b7893ccfSSadaf Ebrahimi }
1898*b7893ccfSSadaf Ebrahimi }
1899*b7893ccfSSadaf Ebrahimi break;
1900*b7893ccfSSadaf Ebrahimi default:
1901*b7893ccfSSadaf Ebrahimi break;
1902*b7893ccfSSadaf Ebrahimi }
1903*b7893ccfSSadaf Ebrahimi }
1904*b7893ccfSSadaf Ebrahimi
1905*b7893ccfSSadaf Ebrahimi bool strip_output_array_level =
1906*b7893ccfSSadaf Ebrahimi (pStage->stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || pStage->stage == VK_SHADER_STAGE_MESH_BIT_NV);
1907*b7893ccfSSadaf Ebrahimi bool strip_input_array_level =
1908*b7893ccfSSadaf Ebrahimi (pStage->stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ||
1909*b7893ccfSSadaf Ebrahimi pStage->stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT || pStage->stage == VK_SHADER_STAGE_GEOMETRY_BIT);
1910*b7893ccfSSadaf Ebrahimi
1911*b7893ccfSSadaf Ebrahimi uint32_t numCompIn = 0, numCompOut = 0;
1912*b7893ccfSSadaf Ebrahimi for (auto &var : variables) {
1913*b7893ccfSSadaf Ebrahimi // Check if the variable is a patch. Patches can also be members of blocks,
1914*b7893ccfSSadaf Ebrahimi // but if they are then the top-level arrayness has already been stripped
1915*b7893ccfSSadaf Ebrahimi // by the time GetComponentsConsumedByType gets to it.
1916*b7893ccfSSadaf Ebrahimi bool isPatch = patchIDs.find(var.ID) != patchIDs.end();
1917*b7893ccfSSadaf Ebrahimi
1918*b7893ccfSSadaf Ebrahimi if (var.storageClass == spv::StorageClassInput) {
1919*b7893ccfSSadaf Ebrahimi numCompIn += GetComponentsConsumedByType(src, var.baseTypePtrID, strip_input_array_level && !isPatch);
1920*b7893ccfSSadaf Ebrahimi } else { // var.storageClass == spv::StorageClassOutput
1921*b7893ccfSSadaf Ebrahimi numCompOut += GetComponentsConsumedByType(src, var.baseTypePtrID, strip_output_array_level && !isPatch);
1922*b7893ccfSSadaf Ebrahimi }
1923*b7893ccfSSadaf Ebrahimi }
1924*b7893ccfSSadaf Ebrahimi
1925*b7893ccfSSadaf Ebrahimi switch (pStage->stage) {
1926*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_VERTEX_BIT:
1927*b7893ccfSSadaf Ebrahimi if (numCompOut > limits.maxVertexOutputComponents) {
1928*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1929*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1930*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Vertex shader exceeds "
1931*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxVertexOutputComponents of %u "
1932*b7893ccfSSadaf Ebrahimi "components by %u components",
1933*b7893ccfSSadaf Ebrahimi limits.maxVertexOutputComponents, numCompOut - limits.maxVertexOutputComponents);
1934*b7893ccfSSadaf Ebrahimi }
1935*b7893ccfSSadaf Ebrahimi break;
1936*b7893ccfSSadaf Ebrahimi
1937*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
1938*b7893ccfSSadaf Ebrahimi if (numCompIn > limits.maxTessellationControlPerVertexInputComponents) {
1939*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1940*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1941*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Tessellation control shader exceeds "
1942*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxTessellationControlPerVertexInputComponents of %u "
1943*b7893ccfSSadaf Ebrahimi "components by %u components",
1944*b7893ccfSSadaf Ebrahimi limits.maxTessellationControlPerVertexInputComponents,
1945*b7893ccfSSadaf Ebrahimi numCompIn - limits.maxTessellationControlPerVertexInputComponents);
1946*b7893ccfSSadaf Ebrahimi }
1947*b7893ccfSSadaf Ebrahimi if (numCompOut > limits.maxTessellationControlPerVertexOutputComponents) {
1948*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1949*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1950*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Tessellation control shader exceeds "
1951*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxTessellationControlPerVertexOutputComponents of %u "
1952*b7893ccfSSadaf Ebrahimi "components by %u components",
1953*b7893ccfSSadaf Ebrahimi limits.maxTessellationControlPerVertexOutputComponents,
1954*b7893ccfSSadaf Ebrahimi numCompOut - limits.maxTessellationControlPerVertexOutputComponents);
1955*b7893ccfSSadaf Ebrahimi }
1956*b7893ccfSSadaf Ebrahimi break;
1957*b7893ccfSSadaf Ebrahimi
1958*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
1959*b7893ccfSSadaf Ebrahimi if (numCompIn > limits.maxTessellationEvaluationInputComponents) {
1960*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1961*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1962*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Tessellation evaluation shader exceeds "
1963*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxTessellationEvaluationInputComponents of %u "
1964*b7893ccfSSadaf Ebrahimi "components by %u components",
1965*b7893ccfSSadaf Ebrahimi limits.maxTessellationEvaluationInputComponents,
1966*b7893ccfSSadaf Ebrahimi numCompIn - limits.maxTessellationEvaluationInputComponents);
1967*b7893ccfSSadaf Ebrahimi }
1968*b7893ccfSSadaf Ebrahimi if (numCompOut > limits.maxTessellationEvaluationOutputComponents) {
1969*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1970*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1971*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Tessellation evaluation shader exceeds "
1972*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxTessellationEvaluationOutputComponents of %u "
1973*b7893ccfSSadaf Ebrahimi "components by %u components",
1974*b7893ccfSSadaf Ebrahimi limits.maxTessellationEvaluationOutputComponents,
1975*b7893ccfSSadaf Ebrahimi numCompOut - limits.maxTessellationEvaluationOutputComponents);
1976*b7893ccfSSadaf Ebrahimi }
1977*b7893ccfSSadaf Ebrahimi break;
1978*b7893ccfSSadaf Ebrahimi
1979*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_GEOMETRY_BIT:
1980*b7893ccfSSadaf Ebrahimi if (numCompIn > limits.maxGeometryInputComponents) {
1981*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1982*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1983*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Geometry shader exceeds "
1984*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxGeometryInputComponents of %u "
1985*b7893ccfSSadaf Ebrahimi "components by %u components",
1986*b7893ccfSSadaf Ebrahimi limits.maxGeometryInputComponents, numCompIn - limits.maxGeometryInputComponents);
1987*b7893ccfSSadaf Ebrahimi }
1988*b7893ccfSSadaf Ebrahimi if (numCompOut > limits.maxGeometryOutputComponents) {
1989*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1990*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1991*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Geometry shader exceeds "
1992*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxGeometryOutputComponents of %u "
1993*b7893ccfSSadaf Ebrahimi "components by %u components",
1994*b7893ccfSSadaf Ebrahimi limits.maxGeometryOutputComponents, numCompOut - limits.maxGeometryOutputComponents);
1995*b7893ccfSSadaf Ebrahimi }
1996*b7893ccfSSadaf Ebrahimi if (numCompOut * numVertices > limits.maxGeometryTotalOutputComponents) {
1997*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
1998*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
1999*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Geometry shader exceeds "
2000*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxGeometryTotalOutputComponents of %u "
2001*b7893ccfSSadaf Ebrahimi "components by %u components",
2002*b7893ccfSSadaf Ebrahimi limits.maxGeometryTotalOutputComponents,
2003*b7893ccfSSadaf Ebrahimi numCompOut * numVertices - limits.maxGeometryTotalOutputComponents);
2004*b7893ccfSSadaf Ebrahimi }
2005*b7893ccfSSadaf Ebrahimi break;
2006*b7893ccfSSadaf Ebrahimi
2007*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_FRAGMENT_BIT:
2008*b7893ccfSSadaf Ebrahimi if (numCompIn > limits.maxFragmentInputComponents) {
2009*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
2010*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_ExceedDeviceLimit,
2011*b7893ccfSSadaf Ebrahimi "Invalid Pipeline CreateInfo State: Fragment shader exceeds "
2012*b7893ccfSSadaf Ebrahimi "VkPhysicalDeviceLimits::maxFragmentInputComponents of %u "
2013*b7893ccfSSadaf Ebrahimi "components by %u components",
2014*b7893ccfSSadaf Ebrahimi limits.maxFragmentInputComponents, numCompIn - limits.maxFragmentInputComponents);
2015*b7893ccfSSadaf Ebrahimi }
2016*b7893ccfSSadaf Ebrahimi break;
2017*b7893ccfSSadaf Ebrahimi
2018*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_RAYGEN_BIT_NV:
2019*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
2020*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
2021*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_MISS_BIT_NV:
2022*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_INTERSECTION_BIT_NV:
2023*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_CALLABLE_BIT_NV:
2024*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_TASK_BIT_NV:
2025*b7893ccfSSadaf Ebrahimi case VK_SHADER_STAGE_MESH_BIT_NV:
2026*b7893ccfSSadaf Ebrahimi break;
2027*b7893ccfSSadaf Ebrahimi
2028*b7893ccfSSadaf Ebrahimi default:
2029*b7893ccfSSadaf Ebrahimi assert(false); // This should never happen
2030*b7893ccfSSadaf Ebrahimi }
2031*b7893ccfSSadaf Ebrahimi return skip;
2032*b7893ccfSSadaf Ebrahimi }
2033*b7893ccfSSadaf Ebrahimi
2034*b7893ccfSSadaf Ebrahimi // copy the specialization constant value into buf, if it is present
GetSpecConstantValue(VkPipelineShaderStageCreateInfo const * pStage,uint32_t spec_id,void * buf)2035*b7893ccfSSadaf Ebrahimi void GetSpecConstantValue(VkPipelineShaderStageCreateInfo const *pStage, uint32_t spec_id, void *buf) {
2036*b7893ccfSSadaf Ebrahimi VkSpecializationInfo const *spec = pStage->pSpecializationInfo;
2037*b7893ccfSSadaf Ebrahimi
2038*b7893ccfSSadaf Ebrahimi if (spec && spec_id < spec->mapEntryCount) {
2039*b7893ccfSSadaf Ebrahimi memcpy(buf, (uint8_t *)spec->pData + spec->pMapEntries[spec_id].offset, spec->pMapEntries[spec_id].size);
2040*b7893ccfSSadaf Ebrahimi }
2041*b7893ccfSSadaf Ebrahimi }
2042*b7893ccfSSadaf Ebrahimi
2043*b7893ccfSSadaf Ebrahimi // Fill in value with the constant or specialization constant value, if available.
2044*b7893ccfSSadaf Ebrahimi // Returns true if the value has been accurately filled out.
GetIntConstantValue(spirv_inst_iter insn,SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const std::unordered_map<uint32_t,uint32_t> & id_to_spec_id,uint32_t * value)2045*b7893ccfSSadaf Ebrahimi static bool GetIntConstantValue(spirv_inst_iter insn, SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
2046*b7893ccfSSadaf Ebrahimi const std::unordered_map<uint32_t, uint32_t> &id_to_spec_id, uint32_t *value) {
2047*b7893ccfSSadaf Ebrahimi auto type_id = src->get_def(insn.word(1));
2048*b7893ccfSSadaf Ebrahimi if (type_id.opcode() != spv::OpTypeInt || type_id.word(2) != 32) {
2049*b7893ccfSSadaf Ebrahimi return false;
2050*b7893ccfSSadaf Ebrahimi }
2051*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
2052*b7893ccfSSadaf Ebrahimi case spv::OpSpecConstant:
2053*b7893ccfSSadaf Ebrahimi *value = insn.word(3);
2054*b7893ccfSSadaf Ebrahimi GetSpecConstantValue(pStage, id_to_spec_id.at(insn.word(2)), value);
2055*b7893ccfSSadaf Ebrahimi return true;
2056*b7893ccfSSadaf Ebrahimi case spv::OpConstant:
2057*b7893ccfSSadaf Ebrahimi *value = insn.word(3);
2058*b7893ccfSSadaf Ebrahimi return true;
2059*b7893ccfSSadaf Ebrahimi default:
2060*b7893ccfSSadaf Ebrahimi return false;
2061*b7893ccfSSadaf Ebrahimi }
2062*b7893ccfSSadaf Ebrahimi }
2063*b7893ccfSSadaf Ebrahimi
2064*b7893ccfSSadaf Ebrahimi // Map SPIR-V type to VK_COMPONENT_TYPE enum
GetComponentType(spirv_inst_iter insn,SHADER_MODULE_STATE const * src)2065*b7893ccfSSadaf Ebrahimi VkComponentTypeNV GetComponentType(spirv_inst_iter insn, SHADER_MODULE_STATE const *src) {
2066*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
2067*b7893ccfSSadaf Ebrahimi case spv::OpTypeInt:
2068*b7893ccfSSadaf Ebrahimi switch (insn.word(2)) {
2069*b7893ccfSSadaf Ebrahimi case 8:
2070*b7893ccfSSadaf Ebrahimi return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT8_NV : VK_COMPONENT_TYPE_UINT8_NV;
2071*b7893ccfSSadaf Ebrahimi case 16:
2072*b7893ccfSSadaf Ebrahimi return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT16_NV : VK_COMPONENT_TYPE_UINT16_NV;
2073*b7893ccfSSadaf Ebrahimi case 32:
2074*b7893ccfSSadaf Ebrahimi return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT32_NV : VK_COMPONENT_TYPE_UINT32_NV;
2075*b7893ccfSSadaf Ebrahimi case 64:
2076*b7893ccfSSadaf Ebrahimi return insn.word(3) != 0 ? VK_COMPONENT_TYPE_SINT64_NV : VK_COMPONENT_TYPE_UINT64_NV;
2077*b7893ccfSSadaf Ebrahimi default:
2078*b7893ccfSSadaf Ebrahimi return VK_COMPONENT_TYPE_MAX_ENUM_NV;
2079*b7893ccfSSadaf Ebrahimi }
2080*b7893ccfSSadaf Ebrahimi case spv::OpTypeFloat:
2081*b7893ccfSSadaf Ebrahimi switch (insn.word(2)) {
2082*b7893ccfSSadaf Ebrahimi case 16:
2083*b7893ccfSSadaf Ebrahimi return VK_COMPONENT_TYPE_FLOAT16_NV;
2084*b7893ccfSSadaf Ebrahimi case 32:
2085*b7893ccfSSadaf Ebrahimi return VK_COMPONENT_TYPE_FLOAT32_NV;
2086*b7893ccfSSadaf Ebrahimi case 64:
2087*b7893ccfSSadaf Ebrahimi return VK_COMPONENT_TYPE_FLOAT64_NV;
2088*b7893ccfSSadaf Ebrahimi default:
2089*b7893ccfSSadaf Ebrahimi return VK_COMPONENT_TYPE_MAX_ENUM_NV;
2090*b7893ccfSSadaf Ebrahimi }
2091*b7893ccfSSadaf Ebrahimi default:
2092*b7893ccfSSadaf Ebrahimi return VK_COMPONENT_TYPE_MAX_ENUM_NV;
2093*b7893ccfSSadaf Ebrahimi }
2094*b7893ccfSSadaf Ebrahimi }
2095*b7893ccfSSadaf Ebrahimi
2096*b7893ccfSSadaf Ebrahimi // Validate SPV_NV_cooperative_matrix behavior that can't be statically validated
2097*b7893ccfSSadaf Ebrahimi // in SPIRV-Tools (e.g. due to specialization constant usage).
ValidateCooperativeMatrix(SHADER_MODULE_STATE const * src,VkPipelineShaderStageCreateInfo const * pStage,const PIPELINE_STATE * pipeline) const2098*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateCooperativeMatrix(SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
2099*b7893ccfSSadaf Ebrahimi const PIPELINE_STATE *pipeline) const {
2100*b7893ccfSSadaf Ebrahimi bool skip = false;
2101*b7893ccfSSadaf Ebrahimi
2102*b7893ccfSSadaf Ebrahimi // Map SPIR-V result ID to specialization constant id (SpecId decoration value)
2103*b7893ccfSSadaf Ebrahimi std::unordered_map<uint32_t, uint32_t> id_to_spec_id;
2104*b7893ccfSSadaf Ebrahimi // Map SPIR-V result ID to the ID of its type.
2105*b7893ccfSSadaf Ebrahimi std::unordered_map<uint32_t, uint32_t> id_to_type_id;
2106*b7893ccfSSadaf Ebrahimi
2107*b7893ccfSSadaf Ebrahimi struct CoopMatType {
2108*b7893ccfSSadaf Ebrahimi uint32_t scope, rows, cols;
2109*b7893ccfSSadaf Ebrahimi VkComponentTypeNV component_type;
2110*b7893ccfSSadaf Ebrahimi bool all_constant;
2111*b7893ccfSSadaf Ebrahimi
2112*b7893ccfSSadaf Ebrahimi CoopMatType() : scope(0), rows(0), cols(0), component_type(VK_COMPONENT_TYPE_MAX_ENUM_NV), all_constant(false) {}
2113*b7893ccfSSadaf Ebrahimi
2114*b7893ccfSSadaf Ebrahimi void Init(uint32_t id, SHADER_MODULE_STATE const *src, VkPipelineShaderStageCreateInfo const *pStage,
2115*b7893ccfSSadaf Ebrahimi const std::unordered_map<uint32_t, uint32_t> &id_to_spec_id) {
2116*b7893ccfSSadaf Ebrahimi spirv_inst_iter insn = src->get_def(id);
2117*b7893ccfSSadaf Ebrahimi uint32_t component_type_id = insn.word(2);
2118*b7893ccfSSadaf Ebrahimi uint32_t scope_id = insn.word(3);
2119*b7893ccfSSadaf Ebrahimi uint32_t rows_id = insn.word(4);
2120*b7893ccfSSadaf Ebrahimi uint32_t cols_id = insn.word(5);
2121*b7893ccfSSadaf Ebrahimi auto component_type_iter = src->get_def(component_type_id);
2122*b7893ccfSSadaf Ebrahimi auto scope_iter = src->get_def(scope_id);
2123*b7893ccfSSadaf Ebrahimi auto rows_iter = src->get_def(rows_id);
2124*b7893ccfSSadaf Ebrahimi auto cols_iter = src->get_def(cols_id);
2125*b7893ccfSSadaf Ebrahimi
2126*b7893ccfSSadaf Ebrahimi all_constant = true;
2127*b7893ccfSSadaf Ebrahimi if (!GetIntConstantValue(scope_iter, src, pStage, id_to_spec_id, &scope)) {
2128*b7893ccfSSadaf Ebrahimi all_constant = false;
2129*b7893ccfSSadaf Ebrahimi }
2130*b7893ccfSSadaf Ebrahimi if (!GetIntConstantValue(rows_iter, src, pStage, id_to_spec_id, &rows)) {
2131*b7893ccfSSadaf Ebrahimi all_constant = false;
2132*b7893ccfSSadaf Ebrahimi }
2133*b7893ccfSSadaf Ebrahimi if (!GetIntConstantValue(cols_iter, src, pStage, id_to_spec_id, &cols)) {
2134*b7893ccfSSadaf Ebrahimi all_constant = false;
2135*b7893ccfSSadaf Ebrahimi }
2136*b7893ccfSSadaf Ebrahimi component_type = GetComponentType(component_type_iter, src);
2137*b7893ccfSSadaf Ebrahimi }
2138*b7893ccfSSadaf Ebrahimi };
2139*b7893ccfSSadaf Ebrahimi
2140*b7893ccfSSadaf Ebrahimi bool seen_coopmat_capability = false;
2141*b7893ccfSSadaf Ebrahimi
2142*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
2143*b7893ccfSSadaf Ebrahimi // Whitelist instructions whose result can be a cooperative matrix type, and
2144*b7893ccfSSadaf Ebrahimi // keep track of their types. It would be nice if SPIRV-Headers generated code
2145*b7893ccfSSadaf Ebrahimi // to identify which instructions have a result type and result id. Lacking that,
2146*b7893ccfSSadaf Ebrahimi // this whitelist is based on the set of instructions that
2147*b7893ccfSSadaf Ebrahimi // SPV_NV_cooperative_matrix says can be used with cooperative matrix types.
2148*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
2149*b7893ccfSSadaf Ebrahimi case spv::OpLoad:
2150*b7893ccfSSadaf Ebrahimi case spv::OpCooperativeMatrixLoadNV:
2151*b7893ccfSSadaf Ebrahimi case spv::OpCooperativeMatrixMulAddNV:
2152*b7893ccfSSadaf Ebrahimi case spv::OpSNegate:
2153*b7893ccfSSadaf Ebrahimi case spv::OpFNegate:
2154*b7893ccfSSadaf Ebrahimi case spv::OpIAdd:
2155*b7893ccfSSadaf Ebrahimi case spv::OpFAdd:
2156*b7893ccfSSadaf Ebrahimi case spv::OpISub:
2157*b7893ccfSSadaf Ebrahimi case spv::OpFSub:
2158*b7893ccfSSadaf Ebrahimi case spv::OpFDiv:
2159*b7893ccfSSadaf Ebrahimi case spv::OpSDiv:
2160*b7893ccfSSadaf Ebrahimi case spv::OpUDiv:
2161*b7893ccfSSadaf Ebrahimi case spv::OpMatrixTimesScalar:
2162*b7893ccfSSadaf Ebrahimi case spv::OpConstantComposite:
2163*b7893ccfSSadaf Ebrahimi case spv::OpCompositeConstruct:
2164*b7893ccfSSadaf Ebrahimi case spv::OpConvertFToU:
2165*b7893ccfSSadaf Ebrahimi case spv::OpConvertFToS:
2166*b7893ccfSSadaf Ebrahimi case spv::OpConvertSToF:
2167*b7893ccfSSadaf Ebrahimi case spv::OpConvertUToF:
2168*b7893ccfSSadaf Ebrahimi case spv::OpUConvert:
2169*b7893ccfSSadaf Ebrahimi case spv::OpSConvert:
2170*b7893ccfSSadaf Ebrahimi case spv::OpFConvert:
2171*b7893ccfSSadaf Ebrahimi id_to_type_id[insn.word(2)] = insn.word(1);
2172*b7893ccfSSadaf Ebrahimi break;
2173*b7893ccfSSadaf Ebrahimi default:
2174*b7893ccfSSadaf Ebrahimi break;
2175*b7893ccfSSadaf Ebrahimi }
2176*b7893ccfSSadaf Ebrahimi
2177*b7893ccfSSadaf Ebrahimi switch (insn.opcode()) {
2178*b7893ccfSSadaf Ebrahimi case spv::OpDecorate:
2179*b7893ccfSSadaf Ebrahimi if (insn.word(2) == spv::DecorationSpecId) {
2180*b7893ccfSSadaf Ebrahimi id_to_spec_id[insn.word(1)] = insn.word(3);
2181*b7893ccfSSadaf Ebrahimi }
2182*b7893ccfSSadaf Ebrahimi break;
2183*b7893ccfSSadaf Ebrahimi case spv::OpCapability:
2184*b7893ccfSSadaf Ebrahimi if (insn.word(1) == spv::CapabilityCooperativeMatrixNV) {
2185*b7893ccfSSadaf Ebrahimi seen_coopmat_capability = true;
2186*b7893ccfSSadaf Ebrahimi
2187*b7893ccfSSadaf Ebrahimi if (!(pStage->stage & phys_dev_ext_props.cooperative_matrix_props.cooperativeMatrixSupportedStages)) {
2188*b7893ccfSSadaf Ebrahimi skip |=
2189*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
2190*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_CooperativeMatrixSupportedStages,
2191*b7893ccfSSadaf Ebrahimi "OpTypeCooperativeMatrixNV used in shader stage not in cooperativeMatrixSupportedStages (= %u)",
2192*b7893ccfSSadaf Ebrahimi phys_dev_ext_props.cooperative_matrix_props.cooperativeMatrixSupportedStages);
2193*b7893ccfSSadaf Ebrahimi }
2194*b7893ccfSSadaf Ebrahimi }
2195*b7893ccfSSadaf Ebrahimi break;
2196*b7893ccfSSadaf Ebrahimi case spv::OpMemoryModel:
2197*b7893ccfSSadaf Ebrahimi // If the capability isn't enabled, don't bother with the rest of this function.
2198*b7893ccfSSadaf Ebrahimi // OpMemoryModel is the first required instruction after all OpCapability instructions.
2199*b7893ccfSSadaf Ebrahimi if (!seen_coopmat_capability) {
2200*b7893ccfSSadaf Ebrahimi return skip;
2201*b7893ccfSSadaf Ebrahimi }
2202*b7893ccfSSadaf Ebrahimi break;
2203*b7893ccfSSadaf Ebrahimi case spv::OpTypeCooperativeMatrixNV: {
2204*b7893ccfSSadaf Ebrahimi CoopMatType M;
2205*b7893ccfSSadaf Ebrahimi M.Init(insn.word(1), src, pStage, id_to_spec_id);
2206*b7893ccfSSadaf Ebrahimi
2207*b7893ccfSSadaf Ebrahimi if (M.all_constant) {
2208*b7893ccfSSadaf Ebrahimi // Validate that the type parameters are all supported for one of the
2209*b7893ccfSSadaf Ebrahimi // operands of a cooperative matrix property.
2210*b7893ccfSSadaf Ebrahimi bool valid = false;
2211*b7893ccfSSadaf Ebrahimi for (unsigned i = 0; i < cooperative_matrix_properties.size(); ++i) {
2212*b7893ccfSSadaf Ebrahimi if (cooperative_matrix_properties[i].AType == M.component_type &&
2213*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].MSize == M.rows && cooperative_matrix_properties[i].KSize == M.cols &&
2214*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == M.scope) {
2215*b7893ccfSSadaf Ebrahimi valid = true;
2216*b7893ccfSSadaf Ebrahimi break;
2217*b7893ccfSSadaf Ebrahimi }
2218*b7893ccfSSadaf Ebrahimi if (cooperative_matrix_properties[i].BType == M.component_type &&
2219*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].KSize == M.rows && cooperative_matrix_properties[i].NSize == M.cols &&
2220*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == M.scope) {
2221*b7893ccfSSadaf Ebrahimi valid = true;
2222*b7893ccfSSadaf Ebrahimi break;
2223*b7893ccfSSadaf Ebrahimi }
2224*b7893ccfSSadaf Ebrahimi if (cooperative_matrix_properties[i].CType == M.component_type &&
2225*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].MSize == M.rows && cooperative_matrix_properties[i].NSize == M.cols &&
2226*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == M.scope) {
2227*b7893ccfSSadaf Ebrahimi valid = true;
2228*b7893ccfSSadaf Ebrahimi break;
2229*b7893ccfSSadaf Ebrahimi }
2230*b7893ccfSSadaf Ebrahimi if (cooperative_matrix_properties[i].DType == M.component_type &&
2231*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].MSize == M.rows && cooperative_matrix_properties[i].NSize == M.cols &&
2232*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == M.scope) {
2233*b7893ccfSSadaf Ebrahimi valid = true;
2234*b7893ccfSSadaf Ebrahimi break;
2235*b7893ccfSSadaf Ebrahimi }
2236*b7893ccfSSadaf Ebrahimi }
2237*b7893ccfSSadaf Ebrahimi if (!valid) {
2238*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
2239*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_CooperativeMatrixType,
2240*b7893ccfSSadaf Ebrahimi "OpTypeCooperativeMatrixNV (result id = %u) operands don't match a supported matrix type",
2241*b7893ccfSSadaf Ebrahimi insn.word(1));
2242*b7893ccfSSadaf Ebrahimi }
2243*b7893ccfSSadaf Ebrahimi }
2244*b7893ccfSSadaf Ebrahimi break;
2245*b7893ccfSSadaf Ebrahimi }
2246*b7893ccfSSadaf Ebrahimi case spv::OpCooperativeMatrixMulAddNV: {
2247*b7893ccfSSadaf Ebrahimi CoopMatType A, B, C, D;
2248*b7893ccfSSadaf Ebrahimi if (id_to_type_id.find(insn.word(2)) == id_to_type_id.end() ||
2249*b7893ccfSSadaf Ebrahimi id_to_type_id.find(insn.word(3)) == id_to_type_id.end() ||
2250*b7893ccfSSadaf Ebrahimi id_to_type_id.find(insn.word(4)) == id_to_type_id.end() ||
2251*b7893ccfSSadaf Ebrahimi id_to_type_id.find(insn.word(5)) == id_to_type_id.end()) {
2252*b7893ccfSSadaf Ebrahimi // Couldn't find type of matrix
2253*b7893ccfSSadaf Ebrahimi assert(false);
2254*b7893ccfSSadaf Ebrahimi break;
2255*b7893ccfSSadaf Ebrahimi }
2256*b7893ccfSSadaf Ebrahimi D.Init(id_to_type_id[insn.word(2)], src, pStage, id_to_spec_id);
2257*b7893ccfSSadaf Ebrahimi A.Init(id_to_type_id[insn.word(3)], src, pStage, id_to_spec_id);
2258*b7893ccfSSadaf Ebrahimi B.Init(id_to_type_id[insn.word(4)], src, pStage, id_to_spec_id);
2259*b7893ccfSSadaf Ebrahimi C.Init(id_to_type_id[insn.word(5)], src, pStage, id_to_spec_id);
2260*b7893ccfSSadaf Ebrahimi
2261*b7893ccfSSadaf Ebrahimi if (A.all_constant && B.all_constant && C.all_constant && D.all_constant) {
2262*b7893ccfSSadaf Ebrahimi // Validate that the type parameters are all supported for the same
2263*b7893ccfSSadaf Ebrahimi // cooperative matrix property.
2264*b7893ccfSSadaf Ebrahimi bool valid = false;
2265*b7893ccfSSadaf Ebrahimi for (unsigned i = 0; i < cooperative_matrix_properties.size(); ++i) {
2266*b7893ccfSSadaf Ebrahimi if (cooperative_matrix_properties[i].AType == A.component_type &&
2267*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].MSize == A.rows && cooperative_matrix_properties[i].KSize == A.cols &&
2268*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == A.scope &&
2269*b7893ccfSSadaf Ebrahimi
2270*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].BType == B.component_type &&
2271*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].KSize == B.rows && cooperative_matrix_properties[i].NSize == B.cols &&
2272*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == B.scope &&
2273*b7893ccfSSadaf Ebrahimi
2274*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].CType == C.component_type &&
2275*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].MSize == C.rows && cooperative_matrix_properties[i].NSize == C.cols &&
2276*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == C.scope &&
2277*b7893ccfSSadaf Ebrahimi
2278*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].DType == D.component_type &&
2279*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].MSize == D.rows && cooperative_matrix_properties[i].NSize == D.cols &&
2280*b7893ccfSSadaf Ebrahimi cooperative_matrix_properties[i].scope == D.scope) {
2281*b7893ccfSSadaf Ebrahimi valid = true;
2282*b7893ccfSSadaf Ebrahimi break;
2283*b7893ccfSSadaf Ebrahimi }
2284*b7893ccfSSadaf Ebrahimi }
2285*b7893ccfSSadaf Ebrahimi if (!valid) {
2286*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
2287*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_CooperativeMatrixMulAdd,
2288*b7893ccfSSadaf Ebrahimi "OpCooperativeMatrixMulAddNV (result id = %u) operands don't match a supported matrix "
2289*b7893ccfSSadaf Ebrahimi "VkCooperativeMatrixPropertiesNV",
2290*b7893ccfSSadaf Ebrahimi insn.word(2));
2291*b7893ccfSSadaf Ebrahimi }
2292*b7893ccfSSadaf Ebrahimi }
2293*b7893ccfSSadaf Ebrahimi break;
2294*b7893ccfSSadaf Ebrahimi }
2295*b7893ccfSSadaf Ebrahimi default:
2296*b7893ccfSSadaf Ebrahimi break;
2297*b7893ccfSSadaf Ebrahimi }
2298*b7893ccfSSadaf Ebrahimi }
2299*b7893ccfSSadaf Ebrahimi
2300*b7893ccfSSadaf Ebrahimi return skip;
2301*b7893ccfSSadaf Ebrahimi }
2302*b7893ccfSSadaf Ebrahimi
ValidateExecutionModes(SHADER_MODULE_STATE const * src,spirv_inst_iter entrypoint) const2303*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateExecutionModes(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint) const {
2304*b7893ccfSSadaf Ebrahimi auto entrypoint_id = entrypoint.word(2);
2305*b7893ccfSSadaf Ebrahimi
2306*b7893ccfSSadaf Ebrahimi // The first denorm execution mode encountered, along with its bit width.
2307*b7893ccfSSadaf Ebrahimi // Used to check if SeparateDenormSettings is respected.
2308*b7893ccfSSadaf Ebrahimi std::pair<spv::ExecutionMode, uint32_t> first_denorm_execution_mode = std::make_pair(spv::ExecutionModeMax, 0);
2309*b7893ccfSSadaf Ebrahimi
2310*b7893ccfSSadaf Ebrahimi // The first rounding mode encountered, along with its bit width.
2311*b7893ccfSSadaf Ebrahimi // Used to check if SeparateRoundingModeSettings is respected.
2312*b7893ccfSSadaf Ebrahimi std::pair<spv::ExecutionMode, uint32_t> first_rounding_mode = std::make_pair(spv::ExecutionModeMax, 0);
2313*b7893ccfSSadaf Ebrahimi
2314*b7893ccfSSadaf Ebrahimi bool skip = false;
2315*b7893ccfSSadaf Ebrahimi
2316*b7893ccfSSadaf Ebrahimi uint32_t verticesOut = 0;
2317*b7893ccfSSadaf Ebrahimi uint32_t invocations = 0;
2318*b7893ccfSSadaf Ebrahimi
2319*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
2320*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpExecutionMode && insn.word(1) == entrypoint_id) {
2321*b7893ccfSSadaf Ebrahimi auto mode = insn.word(2);
2322*b7893ccfSSadaf Ebrahimi switch (mode) {
2323*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeSignedZeroInfNanPreserve: {
2324*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(3);
2325*b7893ccfSSadaf Ebrahimi if ((bit_width == 16 && !enabled_features.float_controls.shaderSignedZeroInfNanPreserveFloat16) ||
2326*b7893ccfSSadaf Ebrahimi (bit_width == 32 && !enabled_features.float_controls.shaderSignedZeroInfNanPreserveFloat32) ||
2327*b7893ccfSSadaf Ebrahimi (bit_width == 64 && !enabled_features.float_controls.shaderSignedZeroInfNanPreserveFloat64)) {
2328*b7893ccfSSadaf Ebrahimi skip |=
2329*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2330*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled,
2331*b7893ccfSSadaf Ebrahimi "Shader requires SignedZeroInfNanPreserve for bit width %d but it is not enabled on the device",
2332*b7893ccfSSadaf Ebrahimi bit_width);
2333*b7893ccfSSadaf Ebrahimi }
2334*b7893ccfSSadaf Ebrahimi break;
2335*b7893ccfSSadaf Ebrahimi }
2336*b7893ccfSSadaf Ebrahimi
2337*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeDenormPreserve: {
2338*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(3);
2339*b7893ccfSSadaf Ebrahimi if ((bit_width == 16 && !enabled_features.float_controls.shaderDenormPreserveFloat16) ||
2340*b7893ccfSSadaf Ebrahimi (bit_width == 32 && !enabled_features.float_controls.shaderDenormPreserveFloat32) ||
2341*b7893ccfSSadaf Ebrahimi (bit_width == 64 && !enabled_features.float_controls.shaderDenormPreserveFloat64)) {
2342*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2343*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled,
2344*b7893ccfSSadaf Ebrahimi "Shader requires DenormPreserve for bit width %d but it is not enabled on the device",
2345*b7893ccfSSadaf Ebrahimi bit_width);
2346*b7893ccfSSadaf Ebrahimi }
2347*b7893ccfSSadaf Ebrahimi
2348*b7893ccfSSadaf Ebrahimi if (first_denorm_execution_mode.first == spv::ExecutionModeMax) {
2349*b7893ccfSSadaf Ebrahimi // Register the first denorm execution mode found
2350*b7893ccfSSadaf Ebrahimi first_denorm_execution_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
2351*b7893ccfSSadaf Ebrahimi } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width) {
2352*b7893ccfSSadaf Ebrahimi switch (enabled_features.float_controls.denormBehaviorIndependence) {
2353*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
2354*b7893ccfSSadaf Ebrahimi if (first_rounding_mode.second != 32 && bit_width != 32) {
2355*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
2356*b7893ccfSSadaf Ebrahimi VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
2357*b7893ccfSSadaf Ebrahimi "Shader uses different denorm execution modes for 16 and 64-bit but "
2358*b7893ccfSSadaf Ebrahimi "denormBehaviorIndependence is "
2359*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
2360*b7893ccfSSadaf Ebrahimi }
2361*b7893ccfSSadaf Ebrahimi break;
2362*b7893ccfSSadaf Ebrahimi
2363*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
2364*b7893ccfSSadaf Ebrahimi break;
2365*b7893ccfSSadaf Ebrahimi
2366*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
2367*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
2368*b7893ccfSSadaf Ebrahimi 0, kVUID_Core_Shader_FeatureNotEnabled,
2369*b7893ccfSSadaf Ebrahimi "Shader uses different denorm execution modes for different bit widths but "
2370*b7893ccfSSadaf Ebrahimi "denormBehaviorIndependence is "
2371*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
2372*b7893ccfSSadaf Ebrahimi break;
2373*b7893ccfSSadaf Ebrahimi
2374*b7893ccfSSadaf Ebrahimi default:
2375*b7893ccfSSadaf Ebrahimi break;
2376*b7893ccfSSadaf Ebrahimi }
2377*b7893ccfSSadaf Ebrahimi }
2378*b7893ccfSSadaf Ebrahimi break;
2379*b7893ccfSSadaf Ebrahimi }
2380*b7893ccfSSadaf Ebrahimi
2381*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeDenormFlushToZero: {
2382*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(3);
2383*b7893ccfSSadaf Ebrahimi if ((bit_width == 16 && !enabled_features.float_controls.shaderDenormFlushToZeroFloat16) ||
2384*b7893ccfSSadaf Ebrahimi (bit_width == 32 && !enabled_features.float_controls.shaderDenormFlushToZeroFloat32) ||
2385*b7893ccfSSadaf Ebrahimi (bit_width == 64 && !enabled_features.float_controls.shaderDenormFlushToZeroFloat64)) {
2386*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2387*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled,
2388*b7893ccfSSadaf Ebrahimi "Shader requires DenormFlushToZero for bit width %d but it is not enabled on the device",
2389*b7893ccfSSadaf Ebrahimi bit_width);
2390*b7893ccfSSadaf Ebrahimi }
2391*b7893ccfSSadaf Ebrahimi
2392*b7893ccfSSadaf Ebrahimi if (first_denorm_execution_mode.first == spv::ExecutionModeMax) {
2393*b7893ccfSSadaf Ebrahimi // Register the first denorm execution mode found
2394*b7893ccfSSadaf Ebrahimi first_denorm_execution_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
2395*b7893ccfSSadaf Ebrahimi } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width) {
2396*b7893ccfSSadaf Ebrahimi switch (enabled_features.float_controls.denormBehaviorIndependence) {
2397*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
2398*b7893ccfSSadaf Ebrahimi if (first_rounding_mode.second != 32 && bit_width != 32) {
2399*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
2400*b7893ccfSSadaf Ebrahimi VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
2401*b7893ccfSSadaf Ebrahimi "Shader uses different denorm execution modes for 16 and 64-bit but "
2402*b7893ccfSSadaf Ebrahimi "denormBehaviorIndependence is "
2403*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
2404*b7893ccfSSadaf Ebrahimi }
2405*b7893ccfSSadaf Ebrahimi break;
2406*b7893ccfSSadaf Ebrahimi
2407*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
2408*b7893ccfSSadaf Ebrahimi break;
2409*b7893ccfSSadaf Ebrahimi
2410*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
2411*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
2412*b7893ccfSSadaf Ebrahimi 0, kVUID_Core_Shader_FeatureNotEnabled,
2413*b7893ccfSSadaf Ebrahimi "Shader uses different denorm execution modes for different bit widths but "
2414*b7893ccfSSadaf Ebrahimi "denormBehaviorIndependence is "
2415*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
2416*b7893ccfSSadaf Ebrahimi break;
2417*b7893ccfSSadaf Ebrahimi
2418*b7893ccfSSadaf Ebrahimi default:
2419*b7893ccfSSadaf Ebrahimi break;
2420*b7893ccfSSadaf Ebrahimi }
2421*b7893ccfSSadaf Ebrahimi }
2422*b7893ccfSSadaf Ebrahimi break;
2423*b7893ccfSSadaf Ebrahimi }
2424*b7893ccfSSadaf Ebrahimi
2425*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeRoundingModeRTE: {
2426*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(3);
2427*b7893ccfSSadaf Ebrahimi if ((bit_width == 16 && !enabled_features.float_controls.shaderRoundingModeRTEFloat16) ||
2428*b7893ccfSSadaf Ebrahimi (bit_width == 32 && !enabled_features.float_controls.shaderRoundingModeRTEFloat32) ||
2429*b7893ccfSSadaf Ebrahimi (bit_width == 64 && !enabled_features.float_controls.shaderRoundingModeRTEFloat64)) {
2430*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2431*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled,
2432*b7893ccfSSadaf Ebrahimi "Shader requires RoundingModeRTE for bit width %d but it is not enabled on the device",
2433*b7893ccfSSadaf Ebrahimi bit_width);
2434*b7893ccfSSadaf Ebrahimi }
2435*b7893ccfSSadaf Ebrahimi
2436*b7893ccfSSadaf Ebrahimi if (first_rounding_mode.first == spv::ExecutionModeMax) {
2437*b7893ccfSSadaf Ebrahimi // Register the first rounding mode found
2438*b7893ccfSSadaf Ebrahimi first_rounding_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
2439*b7893ccfSSadaf Ebrahimi } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width) {
2440*b7893ccfSSadaf Ebrahimi switch (enabled_features.float_controls.roundingModeIndependence) {
2441*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
2442*b7893ccfSSadaf Ebrahimi if (first_rounding_mode.second != 32 && bit_width != 32) {
2443*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
2444*b7893ccfSSadaf Ebrahimi VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
2445*b7893ccfSSadaf Ebrahimi "Shader uses different rounding modes for 16 and 64-bit but "
2446*b7893ccfSSadaf Ebrahimi "roundingModeIndependence is "
2447*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
2448*b7893ccfSSadaf Ebrahimi }
2449*b7893ccfSSadaf Ebrahimi break;
2450*b7893ccfSSadaf Ebrahimi
2451*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
2452*b7893ccfSSadaf Ebrahimi break;
2453*b7893ccfSSadaf Ebrahimi
2454*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
2455*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
2456*b7893ccfSSadaf Ebrahimi 0, kVUID_Core_Shader_FeatureNotEnabled,
2457*b7893ccfSSadaf Ebrahimi "Shader uses different rounding modes for different bit widths but "
2458*b7893ccfSSadaf Ebrahimi "roundingModeIndependence is "
2459*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
2460*b7893ccfSSadaf Ebrahimi break;
2461*b7893ccfSSadaf Ebrahimi
2462*b7893ccfSSadaf Ebrahimi default:
2463*b7893ccfSSadaf Ebrahimi break;
2464*b7893ccfSSadaf Ebrahimi }
2465*b7893ccfSSadaf Ebrahimi }
2466*b7893ccfSSadaf Ebrahimi break;
2467*b7893ccfSSadaf Ebrahimi }
2468*b7893ccfSSadaf Ebrahimi
2469*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeRoundingModeRTZ: {
2470*b7893ccfSSadaf Ebrahimi auto bit_width = insn.word(3);
2471*b7893ccfSSadaf Ebrahimi if ((bit_width == 16 && !enabled_features.float_controls.shaderRoundingModeRTZFloat16) ||
2472*b7893ccfSSadaf Ebrahimi (bit_width == 32 && !enabled_features.float_controls.shaderRoundingModeRTZFloat32) ||
2473*b7893ccfSSadaf Ebrahimi (bit_width == 64 && !enabled_features.float_controls.shaderRoundingModeRTZFloat64)) {
2474*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2475*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_FeatureNotEnabled,
2476*b7893ccfSSadaf Ebrahimi "Shader requires RoundingModeRTZ for bit width %d but it is not enabled on the device",
2477*b7893ccfSSadaf Ebrahimi bit_width);
2478*b7893ccfSSadaf Ebrahimi }
2479*b7893ccfSSadaf Ebrahimi
2480*b7893ccfSSadaf Ebrahimi if (first_rounding_mode.first == spv::ExecutionModeMax) {
2481*b7893ccfSSadaf Ebrahimi // Register the first rounding mode found
2482*b7893ccfSSadaf Ebrahimi first_rounding_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
2483*b7893ccfSSadaf Ebrahimi } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width) {
2484*b7893ccfSSadaf Ebrahimi switch (enabled_features.float_controls.roundingModeIndependence) {
2485*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
2486*b7893ccfSSadaf Ebrahimi if (first_rounding_mode.second != 32 && bit_width != 32) {
2487*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
2488*b7893ccfSSadaf Ebrahimi VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
2489*b7893ccfSSadaf Ebrahimi "Shader uses different rounding modes for 16 and 64-bit but "
2490*b7893ccfSSadaf Ebrahimi "roundingModeIndependence is "
2491*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
2492*b7893ccfSSadaf Ebrahimi }
2493*b7893ccfSSadaf Ebrahimi break;
2494*b7893ccfSSadaf Ebrahimi
2495*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
2496*b7893ccfSSadaf Ebrahimi break;
2497*b7893ccfSSadaf Ebrahimi
2498*b7893ccfSSadaf Ebrahimi case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
2499*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
2500*b7893ccfSSadaf Ebrahimi 0, kVUID_Core_Shader_FeatureNotEnabled,
2501*b7893ccfSSadaf Ebrahimi "Shader uses different rounding modes for different bit widths but "
2502*b7893ccfSSadaf Ebrahimi "roundingModeIndependence is "
2503*b7893ccfSSadaf Ebrahimi "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
2504*b7893ccfSSadaf Ebrahimi break;
2505*b7893ccfSSadaf Ebrahimi
2506*b7893ccfSSadaf Ebrahimi default:
2507*b7893ccfSSadaf Ebrahimi break;
2508*b7893ccfSSadaf Ebrahimi }
2509*b7893ccfSSadaf Ebrahimi }
2510*b7893ccfSSadaf Ebrahimi break;
2511*b7893ccfSSadaf Ebrahimi }
2512*b7893ccfSSadaf Ebrahimi
2513*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeOutputVertices: {
2514*b7893ccfSSadaf Ebrahimi verticesOut = insn.word(3);
2515*b7893ccfSSadaf Ebrahimi break;
2516*b7893ccfSSadaf Ebrahimi }
2517*b7893ccfSSadaf Ebrahimi
2518*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeInvocations: {
2519*b7893ccfSSadaf Ebrahimi invocations = insn.word(3);
2520*b7893ccfSSadaf Ebrahimi break;
2521*b7893ccfSSadaf Ebrahimi }
2522*b7893ccfSSadaf Ebrahimi }
2523*b7893ccfSSadaf Ebrahimi }
2524*b7893ccfSSadaf Ebrahimi }
2525*b7893ccfSSadaf Ebrahimi
2526*b7893ccfSSadaf Ebrahimi if (entrypoint.word(1) == spv::ExecutionModelGeometry) {
2527*b7893ccfSSadaf Ebrahimi if (verticesOut == 0 || verticesOut > phys_dev_props.limits.maxGeometryOutputVertices) {
2528*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2529*b7893ccfSSadaf Ebrahimi "VUID-VkPipelineShaderStageCreateInfo-stage-00714",
2530*b7893ccfSSadaf Ebrahimi "Geometry shader entry point must have an OpExecutionMode instruction that "
2531*b7893ccfSSadaf Ebrahimi "specifies a maximum output vertex count that is greater than 0 and less "
2532*b7893ccfSSadaf Ebrahimi "than or equal to maxGeometryOutputVertices. "
2533*b7893ccfSSadaf Ebrahimi "OutputVertices=%d, maxGeometryOutputVertices=%d",
2534*b7893ccfSSadaf Ebrahimi verticesOut, phys_dev_props.limits.maxGeometryOutputVertices);
2535*b7893ccfSSadaf Ebrahimi }
2536*b7893ccfSSadaf Ebrahimi
2537*b7893ccfSSadaf Ebrahimi if (invocations == 0 || invocations > phys_dev_props.limits.maxGeometryShaderInvocations) {
2538*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2539*b7893ccfSSadaf Ebrahimi "VUID-VkPipelineShaderStageCreateInfo-stage-00715",
2540*b7893ccfSSadaf Ebrahimi "Geometry shader entry point must have an OpExecutionMode instruction that "
2541*b7893ccfSSadaf Ebrahimi "specifies an invocation count that is greater than 0 and less "
2542*b7893ccfSSadaf Ebrahimi "than or equal to maxGeometryShaderInvocations. "
2543*b7893ccfSSadaf Ebrahimi "Invocations=%d, maxGeometryShaderInvocations=%d",
2544*b7893ccfSSadaf Ebrahimi invocations, phys_dev_props.limits.maxGeometryShaderInvocations);
2545*b7893ccfSSadaf Ebrahimi }
2546*b7893ccfSSadaf Ebrahimi }
2547*b7893ccfSSadaf Ebrahimi return skip;
2548*b7893ccfSSadaf Ebrahimi }
2549*b7893ccfSSadaf Ebrahimi
DescriptorTypeToReqs(SHADER_MODULE_STATE const * module,uint32_t type_id)2550*b7893ccfSSadaf Ebrahimi static uint32_t DescriptorTypeToReqs(SHADER_MODULE_STATE const *module, uint32_t type_id) {
2551*b7893ccfSSadaf Ebrahimi auto type = module->get_def(type_id);
2552*b7893ccfSSadaf Ebrahimi
2553*b7893ccfSSadaf Ebrahimi while (true) {
2554*b7893ccfSSadaf Ebrahimi switch (type.opcode()) {
2555*b7893ccfSSadaf Ebrahimi case spv::OpTypeArray:
2556*b7893ccfSSadaf Ebrahimi case spv::OpTypeRuntimeArray:
2557*b7893ccfSSadaf Ebrahimi case spv::OpTypeSampledImage:
2558*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(2));
2559*b7893ccfSSadaf Ebrahimi break;
2560*b7893ccfSSadaf Ebrahimi case spv::OpTypePointer:
2561*b7893ccfSSadaf Ebrahimi type = module->get_def(type.word(3));
2562*b7893ccfSSadaf Ebrahimi break;
2563*b7893ccfSSadaf Ebrahimi case spv::OpTypeImage: {
2564*b7893ccfSSadaf Ebrahimi auto dim = type.word(3);
2565*b7893ccfSSadaf Ebrahimi auto arrayed = type.word(5);
2566*b7893ccfSSadaf Ebrahimi auto msaa = type.word(6);
2567*b7893ccfSSadaf Ebrahimi
2568*b7893ccfSSadaf Ebrahimi uint32_t bits = 0;
2569*b7893ccfSSadaf Ebrahimi switch (GetFundamentalType(module, type.word(2))) {
2570*b7893ccfSSadaf Ebrahimi case FORMAT_TYPE_FLOAT:
2571*b7893ccfSSadaf Ebrahimi bits = DESCRIPTOR_REQ_COMPONENT_TYPE_FLOAT;
2572*b7893ccfSSadaf Ebrahimi break;
2573*b7893ccfSSadaf Ebrahimi case FORMAT_TYPE_UINT:
2574*b7893ccfSSadaf Ebrahimi bits = DESCRIPTOR_REQ_COMPONENT_TYPE_UINT;
2575*b7893ccfSSadaf Ebrahimi break;
2576*b7893ccfSSadaf Ebrahimi case FORMAT_TYPE_SINT:
2577*b7893ccfSSadaf Ebrahimi bits = DESCRIPTOR_REQ_COMPONENT_TYPE_SINT;
2578*b7893ccfSSadaf Ebrahimi break;
2579*b7893ccfSSadaf Ebrahimi default:
2580*b7893ccfSSadaf Ebrahimi break;
2581*b7893ccfSSadaf Ebrahimi }
2582*b7893ccfSSadaf Ebrahimi
2583*b7893ccfSSadaf Ebrahimi switch (dim) {
2584*b7893ccfSSadaf Ebrahimi case spv::Dim1D:
2585*b7893ccfSSadaf Ebrahimi bits |= arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_1D_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_1D;
2586*b7893ccfSSadaf Ebrahimi return bits;
2587*b7893ccfSSadaf Ebrahimi case spv::Dim2D:
2588*b7893ccfSSadaf Ebrahimi bits |= msaa ? DESCRIPTOR_REQ_MULTI_SAMPLE : DESCRIPTOR_REQ_SINGLE_SAMPLE;
2589*b7893ccfSSadaf Ebrahimi bits |= arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_2D_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_2D;
2590*b7893ccfSSadaf Ebrahimi return bits;
2591*b7893ccfSSadaf Ebrahimi case spv::Dim3D:
2592*b7893ccfSSadaf Ebrahimi bits |= DESCRIPTOR_REQ_VIEW_TYPE_3D;
2593*b7893ccfSSadaf Ebrahimi return bits;
2594*b7893ccfSSadaf Ebrahimi case spv::DimCube:
2595*b7893ccfSSadaf Ebrahimi bits |= arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_CUBE_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_CUBE;
2596*b7893ccfSSadaf Ebrahimi return bits;
2597*b7893ccfSSadaf Ebrahimi case spv::DimSubpassData:
2598*b7893ccfSSadaf Ebrahimi bits |= msaa ? DESCRIPTOR_REQ_MULTI_SAMPLE : DESCRIPTOR_REQ_SINGLE_SAMPLE;
2599*b7893ccfSSadaf Ebrahimi return bits;
2600*b7893ccfSSadaf Ebrahimi default: // buffer, etc.
2601*b7893ccfSSadaf Ebrahimi return bits;
2602*b7893ccfSSadaf Ebrahimi }
2603*b7893ccfSSadaf Ebrahimi }
2604*b7893ccfSSadaf Ebrahimi default:
2605*b7893ccfSSadaf Ebrahimi return 0;
2606*b7893ccfSSadaf Ebrahimi }
2607*b7893ccfSSadaf Ebrahimi }
2608*b7893ccfSSadaf Ebrahimi }
2609*b7893ccfSSadaf Ebrahimi
2610*b7893ccfSSadaf Ebrahimi // For given pipelineLayout verify that the set_layout_node at slot.first
2611*b7893ccfSSadaf Ebrahimi // has the requested binding at slot.second and return ptr to that binding
GetDescriptorBinding(PIPELINE_LAYOUT_STATE const * pipelineLayout,descriptor_slot_t slot)2612*b7893ccfSSadaf Ebrahimi static VkDescriptorSetLayoutBinding const *GetDescriptorBinding(PIPELINE_LAYOUT_STATE const *pipelineLayout,
2613*b7893ccfSSadaf Ebrahimi descriptor_slot_t slot) {
2614*b7893ccfSSadaf Ebrahimi if (!pipelineLayout) return nullptr;
2615*b7893ccfSSadaf Ebrahimi
2616*b7893ccfSSadaf Ebrahimi if (slot.first >= pipelineLayout->set_layouts.size()) return nullptr;
2617*b7893ccfSSadaf Ebrahimi
2618*b7893ccfSSadaf Ebrahimi return pipelineLayout->set_layouts[slot.first]->GetDescriptorSetLayoutBindingPtrFromBinding(slot.second);
2619*b7893ccfSSadaf Ebrahimi }
2620*b7893ccfSSadaf Ebrahimi
FindLocalSize(SHADER_MODULE_STATE const * src,uint32_t & local_size_x,uint32_t & local_size_y,uint32_t & local_size_z)2621*b7893ccfSSadaf Ebrahimi static bool FindLocalSize(SHADER_MODULE_STATE const *src, uint32_t &local_size_x, uint32_t &local_size_y, uint32_t &local_size_z) {
2622*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
2623*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpEntryPoint) {
2624*b7893ccfSSadaf Ebrahimi auto executionModel = insn.word(1);
2625*b7893ccfSSadaf Ebrahimi auto entrypointStageBits = ExecutionModelToShaderStageFlagBits(executionModel);
2626*b7893ccfSSadaf Ebrahimi if (entrypointStageBits == VK_SHADER_STAGE_COMPUTE_BIT) {
2627*b7893ccfSSadaf Ebrahimi auto entrypoint_id = insn.word(2);
2628*b7893ccfSSadaf Ebrahimi for (auto insn1 : *src) {
2629*b7893ccfSSadaf Ebrahimi if (insn1.opcode() == spv::OpExecutionMode && insn1.word(1) == entrypoint_id &&
2630*b7893ccfSSadaf Ebrahimi insn1.word(2) == spv::ExecutionModeLocalSize) {
2631*b7893ccfSSadaf Ebrahimi local_size_x = insn1.word(3);
2632*b7893ccfSSadaf Ebrahimi local_size_y = insn1.word(4);
2633*b7893ccfSSadaf Ebrahimi local_size_z = insn1.word(5);
2634*b7893ccfSSadaf Ebrahimi return true;
2635*b7893ccfSSadaf Ebrahimi }
2636*b7893ccfSSadaf Ebrahimi }
2637*b7893ccfSSadaf Ebrahimi }
2638*b7893ccfSSadaf Ebrahimi }
2639*b7893ccfSSadaf Ebrahimi }
2640*b7893ccfSSadaf Ebrahimi return false;
2641*b7893ccfSSadaf Ebrahimi }
2642*b7893ccfSSadaf Ebrahimi
ProcessExecutionModes(SHADER_MODULE_STATE const * src,const spirv_inst_iter & entrypoint,PIPELINE_STATE * pipeline)2643*b7893ccfSSadaf Ebrahimi static void ProcessExecutionModes(SHADER_MODULE_STATE const *src, const spirv_inst_iter &entrypoint, PIPELINE_STATE *pipeline) {
2644*b7893ccfSSadaf Ebrahimi auto entrypoint_id = entrypoint.word(2);
2645*b7893ccfSSadaf Ebrahimi bool is_point_mode = false;
2646*b7893ccfSSadaf Ebrahimi
2647*b7893ccfSSadaf Ebrahimi for (auto insn : *src) {
2648*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpExecutionMode && insn.word(1) == entrypoint_id) {
2649*b7893ccfSSadaf Ebrahimi switch (insn.word(2)) {
2650*b7893ccfSSadaf Ebrahimi case spv::ExecutionModePointMode:
2651*b7893ccfSSadaf Ebrahimi // In tessellation shaders, PointMode is separate and trumps the tessellation topology.
2652*b7893ccfSSadaf Ebrahimi is_point_mode = true;
2653*b7893ccfSSadaf Ebrahimi break;
2654*b7893ccfSSadaf Ebrahimi
2655*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeOutputPoints:
2656*b7893ccfSSadaf Ebrahimi pipeline->topology_at_rasterizer = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
2657*b7893ccfSSadaf Ebrahimi break;
2658*b7893ccfSSadaf Ebrahimi
2659*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeIsolines:
2660*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeOutputLineStrip:
2661*b7893ccfSSadaf Ebrahimi pipeline->topology_at_rasterizer = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
2662*b7893ccfSSadaf Ebrahimi break;
2663*b7893ccfSSadaf Ebrahimi
2664*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeTriangles:
2665*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeQuads:
2666*b7893ccfSSadaf Ebrahimi case spv::ExecutionModeOutputTriangleStrip:
2667*b7893ccfSSadaf Ebrahimi pipeline->topology_at_rasterizer = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
2668*b7893ccfSSadaf Ebrahimi break;
2669*b7893ccfSSadaf Ebrahimi }
2670*b7893ccfSSadaf Ebrahimi }
2671*b7893ccfSSadaf Ebrahimi }
2672*b7893ccfSSadaf Ebrahimi
2673*b7893ccfSSadaf Ebrahimi if (is_point_mode) pipeline->topology_at_rasterizer = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
2674*b7893ccfSSadaf Ebrahimi }
2675*b7893ccfSSadaf Ebrahimi
2676*b7893ccfSSadaf Ebrahimi // If PointList topology is specified in the pipeline, verify that a shader geometry stage writes PointSize
2677*b7893ccfSSadaf Ebrahimi // o If there is only a vertex shader : gl_PointSize must be written when using points
2678*b7893ccfSSadaf Ebrahimi // o If there is a geometry or tessellation shader:
2679*b7893ccfSSadaf Ebrahimi // - If shaderTessellationAndGeometryPointSize feature is enabled:
2680*b7893ccfSSadaf Ebrahimi // * gl_PointSize must be written in the final geometry stage
2681*b7893ccfSSadaf Ebrahimi // - If shaderTessellationAndGeometryPointSize feature is disabled:
2682*b7893ccfSSadaf Ebrahimi // * 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) const2683*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidatePointListShaderState(const PIPELINE_STATE *pipeline, SHADER_MODULE_STATE const *src,
2684*b7893ccfSSadaf Ebrahimi spirv_inst_iter entrypoint, VkShaderStageFlagBits stage) const {
2685*b7893ccfSSadaf Ebrahimi if (pipeline->topology_at_rasterizer != VK_PRIMITIVE_TOPOLOGY_POINT_LIST) {
2686*b7893ccfSSadaf Ebrahimi return false;
2687*b7893ccfSSadaf Ebrahimi }
2688*b7893ccfSSadaf Ebrahimi
2689*b7893ccfSSadaf Ebrahimi bool pointsize_written = false;
2690*b7893ccfSSadaf Ebrahimi bool skip = false;
2691*b7893ccfSSadaf Ebrahimi
2692*b7893ccfSSadaf Ebrahimi // Search for PointSize built-in decorations
2693*b7893ccfSSadaf Ebrahimi std::vector<uint32_t> pointsize_builtin_offsets;
2694*b7893ccfSSadaf Ebrahimi spirv_inst_iter insn = entrypoint;
2695*b7893ccfSSadaf Ebrahimi while (!pointsize_written && (insn.opcode() != spv::OpFunction)) {
2696*b7893ccfSSadaf Ebrahimi if (insn.opcode() == spv::OpMemberDecorate) {
2697*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::DecorationBuiltIn) {
2698*b7893ccfSSadaf Ebrahimi if (insn.word(4) == spv::BuiltInPointSize) {
2699*b7893ccfSSadaf Ebrahimi pointsize_written = IsPointSizeWritten(src, insn, entrypoint);
2700*b7893ccfSSadaf Ebrahimi }
2701*b7893ccfSSadaf Ebrahimi }
2702*b7893ccfSSadaf Ebrahimi } else if (insn.opcode() == spv::OpDecorate) {
2703*b7893ccfSSadaf Ebrahimi if (insn.word(2) == spv::DecorationBuiltIn) {
2704*b7893ccfSSadaf Ebrahimi if (insn.word(3) == spv::BuiltInPointSize) {
2705*b7893ccfSSadaf Ebrahimi pointsize_written = IsPointSizeWritten(src, insn, entrypoint);
2706*b7893ccfSSadaf Ebrahimi }
2707*b7893ccfSSadaf Ebrahimi }
2708*b7893ccfSSadaf Ebrahimi }
2709*b7893ccfSSadaf Ebrahimi
2710*b7893ccfSSadaf Ebrahimi insn++;
2711*b7893ccfSSadaf Ebrahimi }
2712*b7893ccfSSadaf Ebrahimi
2713*b7893ccfSSadaf Ebrahimi if ((stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT || stage == VK_SHADER_STAGE_GEOMETRY_BIT) &&
2714*b7893ccfSSadaf Ebrahimi !enabled_features.core.shaderTessellationAndGeometryPointSize) {
2715*b7893ccfSSadaf Ebrahimi if (pointsize_written) {
2716*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
2717*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_PointSizeBuiltInOverSpecified,
2718*b7893ccfSSadaf Ebrahimi "Pipeline topology is set to POINT_LIST and geometry or tessellation shaders write PointSize which "
2719*b7893ccfSSadaf Ebrahimi "is prohibited when the shaderTessellationAndGeometryPointSize feature is not enabled.");
2720*b7893ccfSSadaf Ebrahimi }
2721*b7893ccfSSadaf Ebrahimi } else if (!pointsize_written) {
2722*b7893ccfSSadaf Ebrahimi skip |=
2723*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
2724*b7893ccfSSadaf Ebrahimi HandleToUint64(pipeline->pipeline), kVUID_Core_Shader_MissingPointSizeBuiltIn,
2725*b7893ccfSSadaf Ebrahimi "Pipeline topology is set to POINT_LIST, but PointSize is not written to in the shader corresponding to %s.",
2726*b7893ccfSSadaf Ebrahimi string_VkShaderStageFlagBits(stage));
2727*b7893ccfSSadaf Ebrahimi }
2728*b7893ccfSSadaf Ebrahimi return skip;
2729*b7893ccfSSadaf Ebrahimi }
RecordPipelineShaderStage(VkPipelineShaderStageCreateInfo const * pStage,PIPELINE_STATE * pipeline,PIPELINE_STATE::StageState * stage_state)2730*b7893ccfSSadaf Ebrahimi void ValidationStateTracker::RecordPipelineShaderStage(VkPipelineShaderStageCreateInfo const *pStage, PIPELINE_STATE *pipeline,
2731*b7893ccfSSadaf Ebrahimi PIPELINE_STATE::StageState *stage_state) {
2732*b7893ccfSSadaf Ebrahimi // Validation shouldn't rely on anything in stage state being valid if the spirv isn't
2733*b7893ccfSSadaf Ebrahimi auto module = GetShaderModuleState(pStage->module);
2734*b7893ccfSSadaf Ebrahimi if (!module->has_valid_spirv) return;
2735*b7893ccfSSadaf Ebrahimi
2736*b7893ccfSSadaf Ebrahimi // Validation shouldn't rely on anything in stage state being valid if the entrypoint isn't present
2737*b7893ccfSSadaf Ebrahimi auto entrypoint = FindEntrypoint(module, pStage->pName, pStage->stage);
2738*b7893ccfSSadaf Ebrahimi if (entrypoint == module->end()) return;
2739*b7893ccfSSadaf Ebrahimi
2740*b7893ccfSSadaf Ebrahimi // Mark accessible ids
2741*b7893ccfSSadaf Ebrahimi stage_state->accessible_ids = MarkAccessibleIds(module, entrypoint);
2742*b7893ccfSSadaf Ebrahimi ProcessExecutionModes(module, entrypoint, pipeline);
2743*b7893ccfSSadaf Ebrahimi
2744*b7893ccfSSadaf Ebrahimi stage_state->descriptor_uses =
2745*b7893ccfSSadaf Ebrahimi CollectInterfaceByDescriptorSlot(report_data, module, stage_state->accessible_ids, &stage_state->has_writable_descriptor);
2746*b7893ccfSSadaf Ebrahimi // Capture descriptor uses for the pipeline
2747*b7893ccfSSadaf Ebrahimi for (auto use : stage_state->descriptor_uses) {
2748*b7893ccfSSadaf Ebrahimi // While validating shaders capture which slots are used by the pipeline
2749*b7893ccfSSadaf Ebrahimi auto &reqs = pipeline->active_slots[use.first.first][use.first.second];
2750*b7893ccfSSadaf Ebrahimi reqs = descriptor_req(reqs | DescriptorTypeToReqs(module, use.second.type_id));
2751*b7893ccfSSadaf Ebrahimi }
2752*b7893ccfSSadaf Ebrahimi }
2753*b7893ccfSSadaf Ebrahimi
ValidatePipelineShaderStage(VkPipelineShaderStageCreateInfo const * pStage,const PIPELINE_STATE * pipeline,const PIPELINE_STATE::StageState & stage_state,const SHADER_MODULE_STATE * module,const spirv_inst_iter & entrypoint,bool check_point_size) const2754*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidatePipelineShaderStage(VkPipelineShaderStageCreateInfo const *pStage, const PIPELINE_STATE *pipeline,
2755*b7893ccfSSadaf Ebrahimi const PIPELINE_STATE::StageState &stage_state, const SHADER_MODULE_STATE *module,
2756*b7893ccfSSadaf Ebrahimi const spirv_inst_iter &entrypoint, bool check_point_size) const {
2757*b7893ccfSSadaf Ebrahimi bool skip = false;
2758*b7893ccfSSadaf Ebrahimi
2759*b7893ccfSSadaf Ebrahimi // Check the module
2760*b7893ccfSSadaf Ebrahimi if (!module->has_valid_spirv) {
2761*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2762*b7893ccfSSadaf Ebrahimi "VUID-VkPipelineShaderStageCreateInfo-module-parameter", "%s does not contain valid spirv for stage %s.",
2763*b7893ccfSSadaf Ebrahimi report_data->FormatHandle(module->vk_shader_module).c_str(), string_VkShaderStageFlagBits(pStage->stage));
2764*b7893ccfSSadaf Ebrahimi }
2765*b7893ccfSSadaf Ebrahimi
2766*b7893ccfSSadaf Ebrahimi // Check the entrypoint
2767*b7893ccfSSadaf Ebrahimi if (entrypoint == module->end()) {
2768*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2769*b7893ccfSSadaf Ebrahimi "VUID-VkPipelineShaderStageCreateInfo-pName-00707", "No entrypoint found named `%s` for stage %s..",
2770*b7893ccfSSadaf Ebrahimi pStage->pName, string_VkShaderStageFlagBits(pStage->stage));
2771*b7893ccfSSadaf Ebrahimi }
2772*b7893ccfSSadaf Ebrahimi if (skip) return true; // no point continuing beyond here, any analysis is just going to be garbage.
2773*b7893ccfSSadaf Ebrahimi
2774*b7893ccfSSadaf Ebrahimi // Mark accessible ids
2775*b7893ccfSSadaf Ebrahimi auto &accessible_ids = stage_state.accessible_ids;
2776*b7893ccfSSadaf Ebrahimi
2777*b7893ccfSSadaf Ebrahimi // Validate descriptor set layout against what the entrypoint actually uses
2778*b7893ccfSSadaf Ebrahimi bool has_writable_descriptor = stage_state.has_writable_descriptor;
2779*b7893ccfSSadaf Ebrahimi auto &descriptor_uses = stage_state.descriptor_uses;
2780*b7893ccfSSadaf Ebrahimi
2781*b7893ccfSSadaf Ebrahimi // Validate shader capabilities against enabled device features
2782*b7893ccfSSadaf Ebrahimi skip |= ValidateShaderCapabilities(module, pStage->stage);
2783*b7893ccfSSadaf Ebrahimi skip |= ValidateShaderStageWritableDescriptor(pStage->stage, has_writable_descriptor);
2784*b7893ccfSSadaf Ebrahimi skip |= ValidateShaderStageInputOutputLimits(module, pStage, pipeline, entrypoint);
2785*b7893ccfSSadaf Ebrahimi skip |= ValidateShaderStageGroupNonUniform(module, pStage->stage, accessible_ids);
2786*b7893ccfSSadaf Ebrahimi skip |= ValidateExecutionModes(module, entrypoint);
2787*b7893ccfSSadaf Ebrahimi skip |= ValidateSpecializationOffsets(report_data, pStage);
2788*b7893ccfSSadaf Ebrahimi skip |= ValidatePushConstantUsage(report_data, pipeline->pipeline_layout.push_constant_ranges.get(), module, accessible_ids,
2789*b7893ccfSSadaf Ebrahimi pStage->stage);
2790*b7893ccfSSadaf Ebrahimi if (check_point_size && !pipeline->graphicsPipelineCI.pRasterizationState->rasterizerDiscardEnable) {
2791*b7893ccfSSadaf Ebrahimi skip |= ValidatePointListShaderState(pipeline, module, entrypoint, pStage->stage);
2792*b7893ccfSSadaf Ebrahimi }
2793*b7893ccfSSadaf Ebrahimi skip |= ValidateCooperativeMatrix(module, pStage, pipeline);
2794*b7893ccfSSadaf Ebrahimi
2795*b7893ccfSSadaf Ebrahimi // Validate descriptor use
2796*b7893ccfSSadaf Ebrahimi for (auto use : descriptor_uses) {
2797*b7893ccfSSadaf Ebrahimi // Verify given pipelineLayout has requested setLayout with requested binding
2798*b7893ccfSSadaf Ebrahimi const auto &binding = GetDescriptorBinding(&pipeline->pipeline_layout, use.first);
2799*b7893ccfSSadaf Ebrahimi unsigned required_descriptor_count;
2800*b7893ccfSSadaf Ebrahimi std::set<uint32_t> descriptor_types = TypeToDescriptorTypeSet(module, use.second.type_id, required_descriptor_count);
2801*b7893ccfSSadaf Ebrahimi
2802*b7893ccfSSadaf Ebrahimi if (!binding) {
2803*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2804*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_MissingDescriptor,
2805*b7893ccfSSadaf Ebrahimi "Shader uses descriptor slot %u.%u (expected `%s`) but not declared in pipeline layout",
2806*b7893ccfSSadaf Ebrahimi use.first.first, use.first.second, string_descriptorTypes(descriptor_types).c_str());
2807*b7893ccfSSadaf Ebrahimi } else if (~binding->stageFlags & pStage->stage) {
2808*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0,
2809*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_DescriptorNotAccessibleFromStage,
2810*b7893ccfSSadaf Ebrahimi "Shader uses descriptor slot %u.%u but descriptor not accessible from stage %s", use.first.first,
2811*b7893ccfSSadaf Ebrahimi use.first.second, string_VkShaderStageFlagBits(pStage->stage));
2812*b7893ccfSSadaf Ebrahimi } else if (descriptor_types.find(binding->descriptorType) == descriptor_types.end()) {
2813*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2814*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_DescriptorTypeMismatch,
2815*b7893ccfSSadaf Ebrahimi "Type mismatch on descriptor slot %u.%u (expected `%s`) but descriptor of type %s", use.first.first,
2816*b7893ccfSSadaf Ebrahimi use.first.second, string_descriptorTypes(descriptor_types).c_str(),
2817*b7893ccfSSadaf Ebrahimi string_VkDescriptorType(binding->descriptorType));
2818*b7893ccfSSadaf Ebrahimi } else if (binding->descriptorCount < required_descriptor_count) {
2819*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2820*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_DescriptorTypeMismatch,
2821*b7893ccfSSadaf Ebrahimi "Shader expects at least %u descriptors for binding %u.%u but only %u provided",
2822*b7893ccfSSadaf Ebrahimi required_descriptor_count, use.first.first, use.first.second, binding->descriptorCount);
2823*b7893ccfSSadaf Ebrahimi }
2824*b7893ccfSSadaf Ebrahimi }
2825*b7893ccfSSadaf Ebrahimi
2826*b7893ccfSSadaf Ebrahimi // Validate use of input attachments against subpass structure
2827*b7893ccfSSadaf Ebrahimi if (pStage->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
2828*b7893ccfSSadaf Ebrahimi auto input_attachment_uses = CollectInterfaceByInputAttachmentIndex(module, accessible_ids);
2829*b7893ccfSSadaf Ebrahimi
2830*b7893ccfSSadaf Ebrahimi auto rpci = pipeline->rp_state->createInfo.ptr();
2831*b7893ccfSSadaf Ebrahimi auto subpass = pipeline->graphicsPipelineCI.subpass;
2832*b7893ccfSSadaf Ebrahimi
2833*b7893ccfSSadaf Ebrahimi for (auto use : input_attachment_uses) {
2834*b7893ccfSSadaf Ebrahimi auto input_attachments = rpci->pSubpasses[subpass].pInputAttachments;
2835*b7893ccfSSadaf Ebrahimi auto index = (input_attachments && use.first < rpci->pSubpasses[subpass].inputAttachmentCount)
2836*b7893ccfSSadaf Ebrahimi ? input_attachments[use.first].attachment
2837*b7893ccfSSadaf Ebrahimi : VK_ATTACHMENT_UNUSED;
2838*b7893ccfSSadaf Ebrahimi
2839*b7893ccfSSadaf Ebrahimi if (index == VK_ATTACHMENT_UNUSED) {
2840*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2841*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_MissingInputAttachment,
2842*b7893ccfSSadaf Ebrahimi "Shader consumes input attachment index %d but not provided in subpass", use.first);
2843*b7893ccfSSadaf Ebrahimi } else if (!(GetFormatType(rpci->pAttachments[index].format) & GetFundamentalType(module, use.second.type_id))) {
2844*b7893ccfSSadaf Ebrahimi skip |=
2845*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
2846*b7893ccfSSadaf Ebrahimi kVUID_Core_Shader_InputAttachmentTypeMismatch,
2847*b7893ccfSSadaf Ebrahimi "Subpass input attachment %u format of %s does not match type used in shader `%s`", use.first,
2848*b7893ccfSSadaf Ebrahimi string_VkFormat(rpci->pAttachments[index].format), DescribeType(module, use.second.type_id).c_str());
2849*b7893ccfSSadaf Ebrahimi }
2850*b7893ccfSSadaf Ebrahimi }
2851*b7893ccfSSadaf Ebrahimi }
2852*b7893ccfSSadaf Ebrahimi if (pStage->stage == VK_SHADER_STAGE_COMPUTE_BIT) {
2853*b7893ccfSSadaf Ebrahimi skip |= ValidateComputeWorkGroupSizes(module);
2854*b7893ccfSSadaf Ebrahimi }
2855*b7893ccfSSadaf Ebrahimi return skip;
2856*b7893ccfSSadaf Ebrahimi }
2857*b7893ccfSSadaf Ebrahimi
ValidateInterfaceBetweenStages(debug_report_data const * report_data,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)2858*b7893ccfSSadaf Ebrahimi static bool ValidateInterfaceBetweenStages(debug_report_data const *report_data, SHADER_MODULE_STATE const *producer,
2859*b7893ccfSSadaf Ebrahimi spirv_inst_iter producer_entrypoint, shader_stage_attributes const *producer_stage,
2860*b7893ccfSSadaf Ebrahimi SHADER_MODULE_STATE const *consumer, spirv_inst_iter consumer_entrypoint,
2861*b7893ccfSSadaf Ebrahimi shader_stage_attributes const *consumer_stage) {
2862*b7893ccfSSadaf Ebrahimi bool skip = false;
2863*b7893ccfSSadaf Ebrahimi
2864*b7893ccfSSadaf Ebrahimi auto outputs =
2865*b7893ccfSSadaf Ebrahimi CollectInterfaceByLocation(producer, producer_entrypoint, spv::StorageClassOutput, producer_stage->arrayed_output);
2866*b7893ccfSSadaf Ebrahimi auto inputs = CollectInterfaceByLocation(consumer, consumer_entrypoint, spv::StorageClassInput, consumer_stage->arrayed_input);
2867*b7893ccfSSadaf Ebrahimi
2868*b7893ccfSSadaf Ebrahimi auto a_it = outputs.begin();
2869*b7893ccfSSadaf Ebrahimi auto b_it = inputs.begin();
2870*b7893ccfSSadaf Ebrahimi
2871*b7893ccfSSadaf Ebrahimi // Maps sorted by key (location); walk them together to find mismatches
2872*b7893ccfSSadaf Ebrahimi while ((outputs.size() > 0 && a_it != outputs.end()) || (inputs.size() && b_it != inputs.end())) {
2873*b7893ccfSSadaf Ebrahimi bool a_at_end = outputs.size() == 0 || a_it == outputs.end();
2874*b7893ccfSSadaf Ebrahimi bool b_at_end = inputs.size() == 0 || b_it == inputs.end();
2875*b7893ccfSSadaf Ebrahimi auto a_first = a_at_end ? std::make_pair(0u, 0u) : a_it->first;
2876*b7893ccfSSadaf Ebrahimi auto b_first = b_at_end ? std::make_pair(0u, 0u) : b_it->first;
2877*b7893ccfSSadaf Ebrahimi
2878*b7893ccfSSadaf Ebrahimi if (b_at_end || ((!a_at_end) && (a_first < b_first))) {
2879*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2880*b7893ccfSSadaf Ebrahimi HandleToUint64(producer->vk_shader_module), kVUID_Core_Shader_OutputNotConsumed,
2881*b7893ccfSSadaf Ebrahimi "%s writes to output location %u.%u which is not consumed by %s", producer_stage->name, a_first.first,
2882*b7893ccfSSadaf Ebrahimi a_first.second, consumer_stage->name);
2883*b7893ccfSSadaf Ebrahimi a_it++;
2884*b7893ccfSSadaf Ebrahimi } else if (a_at_end || a_first > b_first) {
2885*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2886*b7893ccfSSadaf Ebrahimi HandleToUint64(consumer->vk_shader_module), kVUID_Core_Shader_InputNotProduced,
2887*b7893ccfSSadaf Ebrahimi "%s consumes input location %u.%u which is not written by %s", consumer_stage->name, b_first.first,
2888*b7893ccfSSadaf Ebrahimi b_first.second, producer_stage->name);
2889*b7893ccfSSadaf Ebrahimi b_it++;
2890*b7893ccfSSadaf Ebrahimi } else {
2891*b7893ccfSSadaf Ebrahimi // subtleties of arrayed interfaces:
2892*b7893ccfSSadaf Ebrahimi // - if is_patch, then the member is not arrayed, even though the interface may be.
2893*b7893ccfSSadaf Ebrahimi // - if is_block_member, then the extra array level of an arrayed interface is not
2894*b7893ccfSSadaf Ebrahimi // expressed in the member type -- it's expressed in the block type.
2895*b7893ccfSSadaf Ebrahimi if (!TypesMatch(producer, consumer, a_it->second.type_id, b_it->second.type_id,
2896*b7893ccfSSadaf Ebrahimi producer_stage->arrayed_output && !a_it->second.is_patch && !a_it->second.is_block_member,
2897*b7893ccfSSadaf Ebrahimi consumer_stage->arrayed_input && !b_it->second.is_patch && !b_it->second.is_block_member, true)) {
2898*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2899*b7893ccfSSadaf Ebrahimi HandleToUint64(producer->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
2900*b7893ccfSSadaf Ebrahimi "Type mismatch on location %u.%u: '%s' vs '%s'", a_first.first, a_first.second,
2901*b7893ccfSSadaf Ebrahimi DescribeType(producer, a_it->second.type_id).c_str(),
2902*b7893ccfSSadaf Ebrahimi DescribeType(consumer, b_it->second.type_id).c_str());
2903*b7893ccfSSadaf Ebrahimi }
2904*b7893ccfSSadaf Ebrahimi if (a_it->second.is_patch != b_it->second.is_patch) {
2905*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2906*b7893ccfSSadaf Ebrahimi HandleToUint64(producer->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
2907*b7893ccfSSadaf Ebrahimi "Decoration mismatch on location %u.%u: is per-%s in %s stage but per-%s in %s stage",
2908*b7893ccfSSadaf Ebrahimi a_first.first, a_first.second, a_it->second.is_patch ? "patch" : "vertex", producer_stage->name,
2909*b7893ccfSSadaf Ebrahimi b_it->second.is_patch ? "patch" : "vertex", consumer_stage->name);
2910*b7893ccfSSadaf Ebrahimi }
2911*b7893ccfSSadaf Ebrahimi if (a_it->second.is_relaxed_precision != b_it->second.is_relaxed_precision) {
2912*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2913*b7893ccfSSadaf Ebrahimi HandleToUint64(producer->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
2914*b7893ccfSSadaf Ebrahimi "Decoration mismatch on location %u.%u: %s and %s stages differ in precision", a_first.first,
2915*b7893ccfSSadaf Ebrahimi a_first.second, producer_stage->name, consumer_stage->name);
2916*b7893ccfSSadaf Ebrahimi }
2917*b7893ccfSSadaf Ebrahimi a_it++;
2918*b7893ccfSSadaf Ebrahimi b_it++;
2919*b7893ccfSSadaf Ebrahimi }
2920*b7893ccfSSadaf Ebrahimi }
2921*b7893ccfSSadaf Ebrahimi
2922*b7893ccfSSadaf Ebrahimi if (consumer_stage->stage != VK_SHADER_STAGE_FRAGMENT_BIT) {
2923*b7893ccfSSadaf Ebrahimi auto builtins_producer = CollectBuiltinBlockMembers(producer, producer_entrypoint, spv::StorageClassOutput);
2924*b7893ccfSSadaf Ebrahimi auto builtins_consumer = CollectBuiltinBlockMembers(consumer, consumer_entrypoint, spv::StorageClassInput);
2925*b7893ccfSSadaf Ebrahimi
2926*b7893ccfSSadaf Ebrahimi if (!builtins_producer.empty() && !builtins_consumer.empty()) {
2927*b7893ccfSSadaf Ebrahimi if (builtins_producer.size() != builtins_consumer.size()) {
2928*b7893ccfSSadaf Ebrahimi skip |=
2929*b7893ccfSSadaf Ebrahimi log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2930*b7893ccfSSadaf Ebrahimi HandleToUint64(producer->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
2931*b7893ccfSSadaf Ebrahimi "Number of elements inside builtin block differ between stages (%s %d vs %s %d).", producer_stage->name,
2932*b7893ccfSSadaf Ebrahimi (int)builtins_producer.size(), consumer_stage->name, (int)builtins_consumer.size());
2933*b7893ccfSSadaf Ebrahimi } else {
2934*b7893ccfSSadaf Ebrahimi auto it_producer = builtins_producer.begin();
2935*b7893ccfSSadaf Ebrahimi auto it_consumer = builtins_consumer.begin();
2936*b7893ccfSSadaf Ebrahimi while (it_producer != builtins_producer.end() && it_consumer != builtins_consumer.end()) {
2937*b7893ccfSSadaf Ebrahimi if (*it_producer != *it_consumer) {
2938*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
2939*b7893ccfSSadaf Ebrahimi HandleToUint64(producer->vk_shader_module), kVUID_Core_Shader_InterfaceTypeMismatch,
2940*b7893ccfSSadaf Ebrahimi "Builtin variable inside block doesn't match between %s and %s.", producer_stage->name,
2941*b7893ccfSSadaf Ebrahimi consumer_stage->name);
2942*b7893ccfSSadaf Ebrahimi break;
2943*b7893ccfSSadaf Ebrahimi }
2944*b7893ccfSSadaf Ebrahimi it_producer++;
2945*b7893ccfSSadaf Ebrahimi it_consumer++;
2946*b7893ccfSSadaf Ebrahimi }
2947*b7893ccfSSadaf Ebrahimi }
2948*b7893ccfSSadaf Ebrahimi }
2949*b7893ccfSSadaf Ebrahimi }
2950*b7893ccfSSadaf Ebrahimi
2951*b7893ccfSSadaf Ebrahimi return skip;
2952*b7893ccfSSadaf Ebrahimi }
2953*b7893ccfSSadaf Ebrahimi
DetermineFinalGeomStage(const PIPELINE_STATE * pipeline,const VkGraphicsPipelineCreateInfo * pCreateInfo)2954*b7893ccfSSadaf Ebrahimi static inline uint32_t DetermineFinalGeomStage(const PIPELINE_STATE *pipeline, const VkGraphicsPipelineCreateInfo *pCreateInfo) {
2955*b7893ccfSSadaf Ebrahimi uint32_t stage_mask = 0;
2956*b7893ccfSSadaf Ebrahimi if (pipeline->topology_at_rasterizer == VK_PRIMITIVE_TOPOLOGY_POINT_LIST) {
2957*b7893ccfSSadaf Ebrahimi for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
2958*b7893ccfSSadaf Ebrahimi stage_mask |= pCreateInfo->pStages[i].stage;
2959*b7893ccfSSadaf Ebrahimi }
2960*b7893ccfSSadaf Ebrahimi // Determine which shader in which PointSize should be written (the final geometry stage)
2961*b7893ccfSSadaf Ebrahimi if (stage_mask & VK_SHADER_STAGE_MESH_BIT_NV) {
2962*b7893ccfSSadaf Ebrahimi stage_mask = VK_SHADER_STAGE_MESH_BIT_NV;
2963*b7893ccfSSadaf Ebrahimi } else if (stage_mask & VK_SHADER_STAGE_GEOMETRY_BIT) {
2964*b7893ccfSSadaf Ebrahimi stage_mask = VK_SHADER_STAGE_GEOMETRY_BIT;
2965*b7893ccfSSadaf Ebrahimi } else if (stage_mask & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
2966*b7893ccfSSadaf Ebrahimi stage_mask = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
2967*b7893ccfSSadaf Ebrahimi } else if (stage_mask & VK_SHADER_STAGE_VERTEX_BIT) {
2968*b7893ccfSSadaf Ebrahimi stage_mask = VK_SHADER_STAGE_VERTEX_BIT;
2969*b7893ccfSSadaf Ebrahimi }
2970*b7893ccfSSadaf Ebrahimi }
2971*b7893ccfSSadaf Ebrahimi return stage_mask;
2972*b7893ccfSSadaf Ebrahimi }
2973*b7893ccfSSadaf Ebrahimi
2974*b7893ccfSSadaf Ebrahimi // Validate that the shaders used by the given pipeline and store the active_slots
2975*b7893ccfSSadaf Ebrahimi // that are actually used by the pipeline into pPipeline->active_slots
ValidateGraphicsPipelineShaderState(const PIPELINE_STATE * pipeline) const2976*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateGraphicsPipelineShaderState(const PIPELINE_STATE *pipeline) const {
2977*b7893ccfSSadaf Ebrahimi auto pCreateInfo = pipeline->graphicsPipelineCI.ptr();
2978*b7893ccfSSadaf Ebrahimi int vertex_stage = GetShaderStageId(VK_SHADER_STAGE_VERTEX_BIT);
2979*b7893ccfSSadaf Ebrahimi int fragment_stage = GetShaderStageId(VK_SHADER_STAGE_FRAGMENT_BIT);
2980*b7893ccfSSadaf Ebrahimi
2981*b7893ccfSSadaf Ebrahimi const SHADER_MODULE_STATE *shaders[32];
2982*b7893ccfSSadaf Ebrahimi memset(shaders, 0, sizeof(shaders));
2983*b7893ccfSSadaf Ebrahimi spirv_inst_iter entrypoints[32];
2984*b7893ccfSSadaf Ebrahimi memset(entrypoints, 0, sizeof(entrypoints));
2985*b7893ccfSSadaf Ebrahimi bool skip = false;
2986*b7893ccfSSadaf Ebrahimi
2987*b7893ccfSSadaf Ebrahimi uint32_t pointlist_stage_mask = DetermineFinalGeomStage(pipeline, pCreateInfo);
2988*b7893ccfSSadaf Ebrahimi
2989*b7893ccfSSadaf Ebrahimi for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
2990*b7893ccfSSadaf Ebrahimi auto pStage = &pCreateInfo->pStages[i];
2991*b7893ccfSSadaf Ebrahimi auto stage_id = GetShaderStageId(pStage->stage);
2992*b7893ccfSSadaf Ebrahimi shaders[stage_id] = GetShaderModuleState(pStage->module);
2993*b7893ccfSSadaf Ebrahimi entrypoints[stage_id] = FindEntrypoint(shaders[stage_id], pStage->pName, pStage->stage);
2994*b7893ccfSSadaf Ebrahimi skip |= ValidatePipelineShaderStage(pStage, pipeline, pipeline->stage_state[i], shaders[stage_id], entrypoints[stage_id],
2995*b7893ccfSSadaf Ebrahimi
2996*b7893ccfSSadaf Ebrahimi (pointlist_stage_mask == pStage->stage));
2997*b7893ccfSSadaf Ebrahimi }
2998*b7893ccfSSadaf Ebrahimi
2999*b7893ccfSSadaf Ebrahimi // if the shader stages are no good individually, cross-stage validation is pointless.
3000*b7893ccfSSadaf Ebrahimi if (skip) return true;
3001*b7893ccfSSadaf Ebrahimi
3002*b7893ccfSSadaf Ebrahimi auto vi = pCreateInfo->pVertexInputState;
3003*b7893ccfSSadaf Ebrahimi
3004*b7893ccfSSadaf Ebrahimi if (vi) {
3005*b7893ccfSSadaf Ebrahimi skip |= ValidateViConsistency(report_data, vi);
3006*b7893ccfSSadaf Ebrahimi }
3007*b7893ccfSSadaf Ebrahimi
3008*b7893ccfSSadaf Ebrahimi if (shaders[vertex_stage] && shaders[vertex_stage]->has_valid_spirv) {
3009*b7893ccfSSadaf Ebrahimi skip |= ValidateViAgainstVsInputs(report_data, vi, shaders[vertex_stage], entrypoints[vertex_stage]);
3010*b7893ccfSSadaf Ebrahimi }
3011*b7893ccfSSadaf Ebrahimi
3012*b7893ccfSSadaf Ebrahimi int producer = GetShaderStageId(VK_SHADER_STAGE_VERTEX_BIT);
3013*b7893ccfSSadaf Ebrahimi int consumer = GetShaderStageId(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
3014*b7893ccfSSadaf Ebrahimi
3015*b7893ccfSSadaf Ebrahimi while (!shaders[producer] && producer != fragment_stage) {
3016*b7893ccfSSadaf Ebrahimi producer++;
3017*b7893ccfSSadaf Ebrahimi consumer++;
3018*b7893ccfSSadaf Ebrahimi }
3019*b7893ccfSSadaf Ebrahimi
3020*b7893ccfSSadaf Ebrahimi for (; producer != fragment_stage && consumer <= fragment_stage; consumer++) {
3021*b7893ccfSSadaf Ebrahimi assert(shaders[producer]);
3022*b7893ccfSSadaf Ebrahimi if (shaders[consumer]) {
3023*b7893ccfSSadaf Ebrahimi if (shaders[consumer]->has_valid_spirv && shaders[producer]->has_valid_spirv) {
3024*b7893ccfSSadaf Ebrahimi skip |= ValidateInterfaceBetweenStages(report_data, shaders[producer], entrypoints[producer],
3025*b7893ccfSSadaf Ebrahimi &shader_stage_attribs[producer], shaders[consumer], entrypoints[consumer],
3026*b7893ccfSSadaf Ebrahimi &shader_stage_attribs[consumer]);
3027*b7893ccfSSadaf Ebrahimi }
3028*b7893ccfSSadaf Ebrahimi
3029*b7893ccfSSadaf Ebrahimi producer = consumer;
3030*b7893ccfSSadaf Ebrahimi }
3031*b7893ccfSSadaf Ebrahimi }
3032*b7893ccfSSadaf Ebrahimi
3033*b7893ccfSSadaf Ebrahimi if (shaders[fragment_stage] && shaders[fragment_stage]->has_valid_spirv) {
3034*b7893ccfSSadaf Ebrahimi skip |= ValidateFsOutputsAgainstRenderPass(report_data, shaders[fragment_stage], entrypoints[fragment_stage], pipeline,
3035*b7893ccfSSadaf Ebrahimi pCreateInfo->subpass);
3036*b7893ccfSSadaf Ebrahimi }
3037*b7893ccfSSadaf Ebrahimi
3038*b7893ccfSSadaf Ebrahimi return skip;
3039*b7893ccfSSadaf Ebrahimi }
3040*b7893ccfSSadaf Ebrahimi
ValidateComputePipeline(PIPELINE_STATE * pipeline) const3041*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateComputePipeline(PIPELINE_STATE *pipeline) const {
3042*b7893ccfSSadaf Ebrahimi const auto &stage = *pipeline->computePipelineCI.stage.ptr();
3043*b7893ccfSSadaf Ebrahimi
3044*b7893ccfSSadaf Ebrahimi const SHADER_MODULE_STATE *module = GetShaderModuleState(stage.module);
3045*b7893ccfSSadaf Ebrahimi const spirv_inst_iter entrypoint = FindEntrypoint(module, stage.pName, stage.stage);
3046*b7893ccfSSadaf Ebrahimi
3047*b7893ccfSSadaf Ebrahimi return ValidatePipelineShaderStage(&stage, pipeline, pipeline->stage_state[0], module, entrypoint, false);
3048*b7893ccfSSadaf Ebrahimi }
3049*b7893ccfSSadaf Ebrahimi
ValidateRayTracingPipelineNV(PIPELINE_STATE * pipeline) const3050*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateRayTracingPipelineNV(PIPELINE_STATE *pipeline) const {
3051*b7893ccfSSadaf Ebrahimi bool skip = false;
3052*b7893ccfSSadaf Ebrahimi for (uint32_t stage_index = 0; stage_index < pipeline->raytracingPipelineCI.stageCount; stage_index++) {
3053*b7893ccfSSadaf Ebrahimi const auto &stage = pipeline->raytracingPipelineCI.ptr()->pStages[stage_index];
3054*b7893ccfSSadaf Ebrahimi
3055*b7893ccfSSadaf Ebrahimi const SHADER_MODULE_STATE *module = GetShaderModuleState(stage.module);
3056*b7893ccfSSadaf Ebrahimi const spirv_inst_iter entrypoint = FindEntrypoint(module, stage.pName, stage.stage);
3057*b7893ccfSSadaf Ebrahimi
3058*b7893ccfSSadaf Ebrahimi skip |= ValidatePipelineShaderStage(&stage, pipeline, pipeline->stage_state[stage_index], module, entrypoint, false);
3059*b7893ccfSSadaf Ebrahimi }
3060*b7893ccfSSadaf Ebrahimi return skip;
3061*b7893ccfSSadaf Ebrahimi }
3062*b7893ccfSSadaf Ebrahimi
MakeShaderHash(VkShaderModuleCreateInfo const * smci)3063*b7893ccfSSadaf Ebrahimi uint32_t ValidationCache::MakeShaderHash(VkShaderModuleCreateInfo const *smci) { return XXH32(smci->pCode, smci->codeSize, 0); }
3064*b7893ccfSSadaf Ebrahimi
GetValidationCacheInfo(VkShaderModuleCreateInfo const * pCreateInfo)3065*b7893ccfSSadaf Ebrahimi static ValidationCache *GetValidationCacheInfo(VkShaderModuleCreateInfo const *pCreateInfo) {
3066*b7893ccfSSadaf Ebrahimi const auto validation_cache_ci = lvl_find_in_chain<VkShaderModuleValidationCacheCreateInfoEXT>(pCreateInfo->pNext);
3067*b7893ccfSSadaf Ebrahimi if (validation_cache_ci) {
3068*b7893ccfSSadaf Ebrahimi return CastFromHandle<ValidationCache *>(validation_cache_ci->validationCache);
3069*b7893ccfSSadaf Ebrahimi }
3070*b7893ccfSSadaf Ebrahimi return nullptr;
3071*b7893ccfSSadaf Ebrahimi }
3072*b7893ccfSSadaf Ebrahimi
PreCallValidateCreateShaderModule(VkDevice device,const VkShaderModuleCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkShaderModule * pShaderModule)3073*b7893ccfSSadaf Ebrahimi bool CoreChecks::PreCallValidateCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo *pCreateInfo,
3074*b7893ccfSSadaf Ebrahimi const VkAllocationCallbacks *pAllocator, VkShaderModule *pShaderModule) {
3075*b7893ccfSSadaf Ebrahimi bool skip = false;
3076*b7893ccfSSadaf Ebrahimi spv_result_t spv_valid = SPV_SUCCESS;
3077*b7893ccfSSadaf Ebrahimi
3078*b7893ccfSSadaf Ebrahimi if (disabled.shader_validation) {
3079*b7893ccfSSadaf Ebrahimi return false;
3080*b7893ccfSSadaf Ebrahimi }
3081*b7893ccfSSadaf Ebrahimi
3082*b7893ccfSSadaf Ebrahimi auto have_glsl_shader = device_extensions.vk_nv_glsl_shader;
3083*b7893ccfSSadaf Ebrahimi
3084*b7893ccfSSadaf Ebrahimi if (!have_glsl_shader && (pCreateInfo->codeSize % 4)) {
3085*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
3086*b7893ccfSSadaf Ebrahimi "VUID-VkShaderModuleCreateInfo-pCode-01376",
3087*b7893ccfSSadaf Ebrahimi "SPIR-V module not valid: Codesize must be a multiple of 4 but is " PRINTF_SIZE_T_SPECIFIER ".",
3088*b7893ccfSSadaf Ebrahimi pCreateInfo->codeSize);
3089*b7893ccfSSadaf Ebrahimi } else {
3090*b7893ccfSSadaf Ebrahimi auto cache = GetValidationCacheInfo(pCreateInfo);
3091*b7893ccfSSadaf Ebrahimi uint32_t hash = 0;
3092*b7893ccfSSadaf Ebrahimi if (cache) {
3093*b7893ccfSSadaf Ebrahimi hash = ValidationCache::MakeShaderHash(pCreateInfo);
3094*b7893ccfSSadaf Ebrahimi if (cache->Contains(hash)) return false;
3095*b7893ccfSSadaf Ebrahimi }
3096*b7893ccfSSadaf Ebrahimi
3097*b7893ccfSSadaf Ebrahimi // Use SPIRV-Tools validator to try and catch any issues with the module itself
3098*b7893ccfSSadaf Ebrahimi spv_target_env spirv_environment = SPV_ENV_VULKAN_1_0;
3099*b7893ccfSSadaf Ebrahimi if (api_version >= VK_API_VERSION_1_1) {
3100*b7893ccfSSadaf Ebrahimi spirv_environment = SPV_ENV_VULKAN_1_1;
3101*b7893ccfSSadaf Ebrahimi }
3102*b7893ccfSSadaf Ebrahimi spv_context ctx = spvContextCreate(spirv_environment);
3103*b7893ccfSSadaf Ebrahimi spv_const_binary_t binary{pCreateInfo->pCode, pCreateInfo->codeSize / sizeof(uint32_t)};
3104*b7893ccfSSadaf Ebrahimi spv_diagnostic diag = nullptr;
3105*b7893ccfSSadaf Ebrahimi spv_validator_options options = spvValidatorOptionsCreate();
3106*b7893ccfSSadaf Ebrahimi if (device_extensions.vk_khr_relaxed_block_layout) {
3107*b7893ccfSSadaf Ebrahimi spvValidatorOptionsSetRelaxBlockLayout(options, true);
3108*b7893ccfSSadaf Ebrahimi }
3109*b7893ccfSSadaf Ebrahimi if (device_extensions.vk_khr_uniform_buffer_standard_layout &&
3110*b7893ccfSSadaf Ebrahimi enabled_features.uniform_buffer_standard_layout.uniformBufferStandardLayout == VK_TRUE) {
3111*b7893ccfSSadaf Ebrahimi spvValidatorOptionsSetUniformBufferStandardLayout(options, true);
3112*b7893ccfSSadaf Ebrahimi }
3113*b7893ccfSSadaf Ebrahimi if (device_extensions.vk_ext_scalar_block_layout &&
3114*b7893ccfSSadaf Ebrahimi enabled_features.scalar_block_layout_features.scalarBlockLayout == VK_TRUE) {
3115*b7893ccfSSadaf Ebrahimi spvValidatorOptionsSetScalarBlockLayout(options, true);
3116*b7893ccfSSadaf Ebrahimi }
3117*b7893ccfSSadaf Ebrahimi spv_valid = spvValidateWithOptions(ctx, options, &binary, &diag);
3118*b7893ccfSSadaf Ebrahimi if (spv_valid != SPV_SUCCESS) {
3119*b7893ccfSSadaf Ebrahimi if (!have_glsl_shader || (pCreateInfo->pCode[0] == spv::MagicNumber)) {
3120*b7893ccfSSadaf Ebrahimi skip |=
3121*b7893ccfSSadaf Ebrahimi log_msg(report_data, spv_valid == SPV_WARNING ? VK_DEBUG_REPORT_WARNING_BIT_EXT : VK_DEBUG_REPORT_ERROR_BIT_EXT,
3122*b7893ccfSSadaf Ebrahimi VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_InconsistentSpirv,
3123*b7893ccfSSadaf Ebrahimi "SPIR-V module not valid: %s", diag && diag->error ? diag->error : "(no error text)");
3124*b7893ccfSSadaf Ebrahimi }
3125*b7893ccfSSadaf Ebrahimi } else {
3126*b7893ccfSSadaf Ebrahimi if (cache) {
3127*b7893ccfSSadaf Ebrahimi cache->Insert(hash);
3128*b7893ccfSSadaf Ebrahimi }
3129*b7893ccfSSadaf Ebrahimi }
3130*b7893ccfSSadaf Ebrahimi
3131*b7893ccfSSadaf Ebrahimi spvValidatorOptionsDestroy(options);
3132*b7893ccfSSadaf Ebrahimi spvDiagnosticDestroy(diag);
3133*b7893ccfSSadaf Ebrahimi spvContextDestroy(ctx);
3134*b7893ccfSSadaf Ebrahimi }
3135*b7893ccfSSadaf Ebrahimi
3136*b7893ccfSSadaf Ebrahimi return skip;
3137*b7893ccfSSadaf Ebrahimi }
3138*b7893ccfSSadaf Ebrahimi
PreCallRecordCreateShaderModule(VkDevice device,const VkShaderModuleCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkShaderModule * pShaderModule,void * csm_state_data)3139*b7893ccfSSadaf Ebrahimi void CoreChecks::PreCallRecordCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo *pCreateInfo,
3140*b7893ccfSSadaf Ebrahimi const VkAllocationCallbacks *pAllocator, VkShaderModule *pShaderModule,
3141*b7893ccfSSadaf Ebrahimi void *csm_state_data) {
3142*b7893ccfSSadaf Ebrahimi create_shader_module_api_state *csm_state = reinterpret_cast<create_shader_module_api_state *>(csm_state_data);
3143*b7893ccfSSadaf Ebrahimi if (enabled.gpu_validation) {
3144*b7893ccfSSadaf Ebrahimi GpuPreCallCreateShaderModule(pCreateInfo, pAllocator, pShaderModule, &csm_state->unique_shader_id,
3145*b7893ccfSSadaf Ebrahimi &csm_state->instrumented_create_info, &csm_state->instrumented_pgm);
3146*b7893ccfSSadaf Ebrahimi }
3147*b7893ccfSSadaf Ebrahimi }
3148*b7893ccfSSadaf Ebrahimi
PostCallRecordCreateShaderModule(VkDevice device,const VkShaderModuleCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkShaderModule * pShaderModule,VkResult result,void * csm_state_data)3149*b7893ccfSSadaf Ebrahimi void ValidationStateTracker::PostCallRecordCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo *pCreateInfo,
3150*b7893ccfSSadaf Ebrahimi const VkAllocationCallbacks *pAllocator,
3151*b7893ccfSSadaf Ebrahimi VkShaderModule *pShaderModule, VkResult result,
3152*b7893ccfSSadaf Ebrahimi void *csm_state_data) {
3153*b7893ccfSSadaf Ebrahimi if (VK_SUCCESS != result) return;
3154*b7893ccfSSadaf Ebrahimi create_shader_module_api_state *csm_state = reinterpret_cast<create_shader_module_api_state *>(csm_state_data);
3155*b7893ccfSSadaf Ebrahimi
3156*b7893ccfSSadaf Ebrahimi spv_target_env spirv_environment = ((api_version >= VK_API_VERSION_1_1) ? SPV_ENV_VULKAN_1_1 : SPV_ENV_VULKAN_1_0);
3157*b7893ccfSSadaf Ebrahimi bool is_spirv = (pCreateInfo->pCode[0] == spv::MagicNumber);
3158*b7893ccfSSadaf Ebrahimi std::unique_ptr<SHADER_MODULE_STATE> new_shader_module(
3159*b7893ccfSSadaf Ebrahimi is_spirv ? new SHADER_MODULE_STATE(pCreateInfo, *pShaderModule, spirv_environment, csm_state->unique_shader_id)
3160*b7893ccfSSadaf Ebrahimi : new SHADER_MODULE_STATE());
3161*b7893ccfSSadaf Ebrahimi shaderModuleMap[*pShaderModule] = std::move(new_shader_module);
3162*b7893ccfSSadaf Ebrahimi }
3163*b7893ccfSSadaf Ebrahimi
ValidateComputeWorkGroupSizes(const SHADER_MODULE_STATE * shader) const3164*b7893ccfSSadaf Ebrahimi bool CoreChecks::ValidateComputeWorkGroupSizes(const SHADER_MODULE_STATE *shader) const {
3165*b7893ccfSSadaf Ebrahimi bool skip = false;
3166*b7893ccfSSadaf Ebrahimi uint32_t local_size_x = 0;
3167*b7893ccfSSadaf Ebrahimi uint32_t local_size_y = 0;
3168*b7893ccfSSadaf Ebrahimi uint32_t local_size_z = 0;
3169*b7893ccfSSadaf Ebrahimi if (FindLocalSize(shader, local_size_x, local_size_y, local_size_z)) {
3170*b7893ccfSSadaf Ebrahimi if (local_size_x > phys_dev_props.limits.maxComputeWorkGroupSize[0]) {
3171*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
3172*b7893ccfSSadaf Ebrahimi HandleToUint64(shader->vk_shader_module), "UNASSIGNED-features-limits-maxComputeWorkGroupSize",
3173*b7893ccfSSadaf Ebrahimi "%s local_size_x (%" PRIu32 ") exceeds device limit maxComputeWorkGroupSize[0] (%" PRIu32 ").",
3174*b7893ccfSSadaf Ebrahimi report_data->FormatHandle(shader->vk_shader_module).c_str(), local_size_x,
3175*b7893ccfSSadaf Ebrahimi phys_dev_props.limits.maxComputeWorkGroupSize[0]);
3176*b7893ccfSSadaf Ebrahimi }
3177*b7893ccfSSadaf Ebrahimi if (local_size_y > phys_dev_props.limits.maxComputeWorkGroupSize[1]) {
3178*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
3179*b7893ccfSSadaf Ebrahimi HandleToUint64(shader->vk_shader_module), "UNASSIGNED-features-limits-maxComputeWorkGroupSize",
3180*b7893ccfSSadaf Ebrahimi "%s local_size_y (%" PRIu32 ") exceeds device limit maxComputeWorkGroupSize[1] (%" PRIu32 ").",
3181*b7893ccfSSadaf Ebrahimi report_data->FormatHandle(shader->vk_shader_module).c_str(), local_size_x,
3182*b7893ccfSSadaf Ebrahimi phys_dev_props.limits.maxComputeWorkGroupSize[1]);
3183*b7893ccfSSadaf Ebrahimi }
3184*b7893ccfSSadaf Ebrahimi if (local_size_z > phys_dev_props.limits.maxComputeWorkGroupSize[2]) {
3185*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
3186*b7893ccfSSadaf Ebrahimi HandleToUint64(shader->vk_shader_module), "UNASSIGNED-features-limits-maxComputeWorkGroupSize",
3187*b7893ccfSSadaf Ebrahimi "%s local_size_z (%" PRIu32 ") exceeds device limit maxComputeWorkGroupSize[2] (%" PRIu32 ").",
3188*b7893ccfSSadaf Ebrahimi report_data->FormatHandle(shader->vk_shader_module).c_str(), local_size_x,
3189*b7893ccfSSadaf Ebrahimi phys_dev_props.limits.maxComputeWorkGroupSize[2]);
3190*b7893ccfSSadaf Ebrahimi }
3191*b7893ccfSSadaf Ebrahimi
3192*b7893ccfSSadaf Ebrahimi uint32_t limit = phys_dev_props.limits.maxComputeWorkGroupInvocations;
3193*b7893ccfSSadaf Ebrahimi uint64_t invocations = local_size_x * local_size_y;
3194*b7893ccfSSadaf Ebrahimi // Prevent overflow.
3195*b7893ccfSSadaf Ebrahimi bool fail = false;
3196*b7893ccfSSadaf Ebrahimi if (invocations > UINT32_MAX || invocations > limit) {
3197*b7893ccfSSadaf Ebrahimi fail = true;
3198*b7893ccfSSadaf Ebrahimi }
3199*b7893ccfSSadaf Ebrahimi if (!fail) {
3200*b7893ccfSSadaf Ebrahimi invocations *= local_size_z;
3201*b7893ccfSSadaf Ebrahimi if (invocations > UINT32_MAX || invocations > limit) {
3202*b7893ccfSSadaf Ebrahimi fail = true;
3203*b7893ccfSSadaf Ebrahimi }
3204*b7893ccfSSadaf Ebrahimi }
3205*b7893ccfSSadaf Ebrahimi if (fail) {
3206*b7893ccfSSadaf Ebrahimi skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT,
3207*b7893ccfSSadaf Ebrahimi HandleToUint64(shader->vk_shader_module), "UNASSIGNED-features-limits-maxComputeWorkGroupInvocations",
3208*b7893ccfSSadaf Ebrahimi "%s local_size (%" PRIu32 ", %" PRIu32 ", %" PRIu32
3209*b7893ccfSSadaf Ebrahimi ") exceeds device limit maxComputeWorkGroupInvocations (%" PRIu32 ").",
3210*b7893ccfSSadaf Ebrahimi report_data->FormatHandle(shader->vk_shader_module).c_str(), local_size_x, local_size_y, local_size_z,
3211*b7893ccfSSadaf Ebrahimi limit);
3212*b7893ccfSSadaf Ebrahimi }
3213*b7893ccfSSadaf Ebrahimi }
3214*b7893ccfSSadaf Ebrahimi return skip;
3215*b7893ccfSSadaf Ebrahimi }
3216