1d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes/* Copyright (c) 2015-2017 The Khronos Group Inc.
2d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * Copyright (c) 2015-2017 Valve Corporation
3d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * Copyright (c) 2015-2017 LunarG, Inc.
4d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * Copyright (C) 2015-2017 Google Inc.
5d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes *
6d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * Licensed under the Apache License, Version 2.0 (the "License");
7d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * you may not use this file except in compliance with the License.
8d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * You may obtain a copy of the License at
9d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes *
10d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes *     http://www.apache.org/licenses/LICENSE-2.0
11d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes *
12d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * Unless required by applicable law or agreed to in writing, software
13d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * distributed under the License is distributed on an "AS IS" BASIS,
14d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * See the License for the specific language governing permissions and
16d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * limitations under the License.
17d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes *
18d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes * Author: Chris Forbes <chrisf@ijw.co.nz>
19d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes */
20d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
21d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <cinttypes>
22d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <cassert>
23d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <vector>
24d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <unordered_map>
25d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <string>
26d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <sstream>
27d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include <SPIRV/spirv.hpp>
28d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "vk_loader_platform.h"
29d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "vk_enum_string_helper.h"
30d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "vk_layer_table.h"
31d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "vk_layer_data.h"
32d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "vk_layer_extension_utils.h"
33d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "vk_layer_utils.h"
34d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "core_validation.h"
35d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "core_validation_types.h"
36d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes#include "shader_validation.h"
377aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes#include "spirv-tools/libspirv.h"
38fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes#include "xxhash.h"
39d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
40d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesenum FORMAT_TYPE {
41d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    FORMAT_TYPE_FLOAT = 1,  // UNORM, SNORM, FLOAT, USCALED, SSCALED, SRGB -- anything we consider float in the shader
42d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    FORMAT_TYPE_SINT = 2,
43d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    FORMAT_TYPE_UINT = 4,
44d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes};
45d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
46d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbestypedef std::pair<unsigned, unsigned> location_t;
47d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
48d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstruct interface_var {
49d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    uint32_t id;
50d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    uint32_t type_id;
51d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    uint32_t offset;
52d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool is_patch;
53d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool is_block_member;
54d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool is_relaxed_precision;
55d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // TODO: collect the name, too? Isn't required to be present.
56d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes};
57d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
58d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstruct shader_stage_attributes {
59d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    char const *const name;
60d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool arrayed_input;
61d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool arrayed_output;
62d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes};
63d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
64d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic shader_stage_attributes shader_stage_attribs[] = {
65d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    {"vertex shader", false, false},  {"tessellation control shader", true, true}, {"tessellation evaluation shader", true, false},
66d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    {"geometry shader", true, false}, {"fragment shader", false, false},
67d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes};
68d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
69d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// SPIRV utility functions
70d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesvoid shader_module::build_def_index() {
71d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *this) {
72d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        switch (insn.opcode()) {
73d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Types
74d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeVoid:
75d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeBool:
76d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeInt:
77d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeFloat:
78d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeVector:
79d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeMatrix:
80d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeImage:
81d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeSampler:
82d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeSampledImage:
83d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeArray:
84d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeRuntimeArray:
85d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeStruct:
86d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeOpaque:
87d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypePointer:
88d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeFunction:
89d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeEvent:
90d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeDeviceEvent:
91d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeReserveId:
92d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeQueue:
93d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypePipe:
94d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                def_index[insn.word(1)] = insn.offset();
95d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
96d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
97d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // Fixed constants
98d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpConstantTrue:
99d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpConstantFalse:
100d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpConstant:
101d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpConstantComposite:
102d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpConstantSampler:
103d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpConstantNull:
104d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                def_index[insn.word(2)] = insn.offset();
105d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
106d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
107d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // Specialization constants
108d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpSpecConstantTrue:
109d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpSpecConstantFalse:
110d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpSpecConstant:
111d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpSpecConstantComposite:
112d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpSpecConstantOp:
113d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                def_index[insn.word(2)] = insn.offset();
114d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
115d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
116d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // Variables
117d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpVariable:
118d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                def_index[insn.word(2)] = insn.offset();
119d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
120d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
121d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // Functions
122d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpFunction:
123d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                def_index[insn.word(2)] = insn.offset();
124d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
125d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
126d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            default:
127d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // We don't care about any other defs for now.
128d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
129d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
130d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
131d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
132d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
133d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic spirv_inst_iter find_entrypoint(shader_module const *src, char const *name, VkShaderStageFlagBits stageBits) {
134d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
135d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpEntryPoint) {
136d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto entrypointName = (char const *)&insn.word(3);
137d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto entrypointStageBits = 1u << insn.word(1);
138d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
139d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (!strcmp(entrypointName, name) && (entrypointStageBits & stageBits)) {
140d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return insn;
141d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
142d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
143d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
144d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
145d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return src->end();
146d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
147d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
148d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic char const *storage_class_name(unsigned sc) {
149d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (sc) {
150d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassInput:
151d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "input";
152d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassOutput:
153d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "output";
154d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassUniformConstant:
155d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "const uniform";
156d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassUniform:
157d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "uniform";
158d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassWorkgroup:
159d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "workgroup local";
160d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassCrossWorkgroup:
161d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "workgroup global";
162d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassPrivate:
163d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "private global";
164d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassFunction:
165d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "function";
166d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassGeneric:
167d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "generic";
168d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassAtomicCounter:
169d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "atomic counter";
170d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassImage:
171d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "image";
172d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::StorageClassPushConstant:
173d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "push constant";
174d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
175d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return "unknown";
176d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
177d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
178d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
179d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// Get the value of an integral constant
180d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesunsigned get_constant_value(shader_module const *src, unsigned id) {
181d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto value = src->get_def(id);
182d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(value != src->end());
183d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
184d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (value.opcode() != spv::OpConstant) {
185d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // TODO: Either ensure that the specialization transform is already performed on a module we're
186d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        //       considering here, OR -- specialize on the fly now.
187d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return 1;
188d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
189d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
190d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return value.word(3);
191d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
192d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
193d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic void describe_type_inner(std::ostringstream &ss, shader_module const *src, unsigned type) {
194d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto insn = src->get_def(type);
195d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(insn != src->end());
196d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
197d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (insn.opcode()) {
198d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeBool:
199d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "bool";
200d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
201d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeInt:
202d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << (insn.word(3) ? 's' : 'u') << "int" << insn.word(2);
203d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
204d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeFloat:
205d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "float" << insn.word(2);
206d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
207d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeVector:
208d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "vec" << insn.word(3) << " of ";
209d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            describe_type_inner(ss, src, insn.word(2));
210d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
211d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeMatrix:
212d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "mat" << insn.word(3) << " of ";
213d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            describe_type_inner(ss, src, insn.word(2));
214d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
215d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeArray:
216d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "arr[" << get_constant_value(src, insn.word(3)) << "] of ";
217d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            describe_type_inner(ss, src, insn.word(2));
218d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
219d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypePointer:
220d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "ptr to " << storage_class_name(insn.word(2)) << " ";
221d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            describe_type_inner(ss, src, insn.word(3));
222d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
223d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeStruct: {
224d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "struct of (";
225d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            for (unsigned i = 2; i < insn.len(); i++) {
226d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                describe_type_inner(ss, src, insn.word(i));
227d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                if (i == insn.len() - 1) {
228d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    ss << ")";
229d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                } else {
230d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    ss << ", ";
231d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
232d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
233d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
234d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
235d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeSampler:
236d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "sampler";
237d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
238d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeSampledImage:
239d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "sampler+";
240d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            describe_type_inner(ss, src, insn.word(2));
241d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
242d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeImage:
243d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "image(dim=" << insn.word(3) << ", sampled=" << insn.word(7) << ")";
244d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
245d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
246d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            ss << "oddtype";
247d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            break;
248d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
249d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
250d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
251d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic std::string describe_type(shader_module const *src, unsigned type) {
252d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::ostringstream ss;
253d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    describe_type_inner(ss, src, type);
254d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return ss.str();
255d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
256d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
257d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool is_narrow_numeric_type(spirv_inst_iter type) {
258d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (type.opcode() != spv::OpTypeInt && type.opcode() != spv::OpTypeFloat) return false;
259d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return type.word(2) < 64;
260d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
261d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
262d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool types_match(shader_module const *a, shader_module const *b, unsigned a_type, unsigned b_type, bool a_arrayed,
263d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        bool b_arrayed, bool relaxed) {
264d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Walk two type trees together, and complain about differences
265d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto a_insn = a->get_def(a_type);
266d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto b_insn = b->get_def(b_type);
267d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(a_insn != a->end());
268d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(b_insn != b->end());
269d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
270d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (a_arrayed && a_insn.opcode() == spv::OpTypeArray) {
271d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return types_match(a, b, a_insn.word(2), b_type, false, b_arrayed, relaxed);
272d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
273d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
274d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (b_arrayed && b_insn.opcode() == spv::OpTypeArray) {
275d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // We probably just found the extra level of arrayness in b_type: compare the type inside it to a_type
276d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return types_match(a, b, a_type, b_insn.word(2), a_arrayed, false, relaxed);
277d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
278d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
279d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (a_insn.opcode() == spv::OpTypeVector && relaxed && is_narrow_numeric_type(b_insn)) {
280d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return types_match(a, b, a_insn.word(2), b_type, a_arrayed, b_arrayed, false);
281d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
282d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
283d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (a_insn.opcode() != b_insn.opcode()) {
284d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return false;
285d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
286d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
287d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (a_insn.opcode() == spv::OpTypePointer) {
288d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // Match on pointee type. storage class is expected to differ
289d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return types_match(a, b, a_insn.word(3), b_insn.word(3), a_arrayed, b_arrayed, relaxed);
290d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
291d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
292d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (a_arrayed || b_arrayed) {
293d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // If we havent resolved array-of-verts by here, we're not going to.
294d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return false;
295d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
296d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
297d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (a_insn.opcode()) {
298d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeBool:
299d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return true;
300d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeInt:
301d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Match on width, signedness
302d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return a_insn.word(2) == b_insn.word(2) && a_insn.word(3) == b_insn.word(3);
303d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeFloat:
304d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Match on width
305d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return a_insn.word(2) == b_insn.word(2);
306d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeVector:
307d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Match on element type, count.
308d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (!types_match(a, b, a_insn.word(2), b_insn.word(2), a_arrayed, b_arrayed, false)) return false;
309d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (relaxed && is_narrow_numeric_type(a->get_def(a_insn.word(2)))) {
310d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return a_insn.word(3) >= b_insn.word(3);
311d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            } else {
312d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return a_insn.word(3) == b_insn.word(3);
313d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
314d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeMatrix:
315d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Match on element type, count.
316d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return types_match(a, b, a_insn.word(2), b_insn.word(2), a_arrayed, b_arrayed, false) &&
3175f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                   a_insn.word(3) == b_insn.word(3);
318d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeArray:
319d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Match on element type, count. these all have the same layout. we don't get here if b_arrayed. This differs from
320d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // vector & matrix types in that the array size is the id of a constant instruction, * not a literal within OpTypeArray
321d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return types_match(a, b, a_insn.word(2), b_insn.word(2), a_arrayed, b_arrayed, false) &&
3225f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                   get_constant_value(a, a_insn.word(3)) == get_constant_value(b, b_insn.word(3));
323d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeStruct:
324d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Match on all element types
3255f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton            {
3265f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                if (a_insn.len() != b_insn.len()) {
3275f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                    return false;  // Structs cannot match if member counts differ
3285f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                }
329d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
3305f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                for (unsigned i = 2; i < a_insn.len(); i++) {
3315f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                    if (!types_match(a, b, a_insn.word(i), b_insn.word(i), a_arrayed, b_arrayed, false)) {
3325f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                        return false;
3335f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                    }
334d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
335d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
3365f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                return true;
3375f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton            }
338d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
339d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Remaining types are CLisms, or may not appear in the interfaces we are interested in. Just claim no match.
340d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return false;
341d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
342d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
343d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
344d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic unsigned value_or_default(std::unordered_map<unsigned, unsigned> const &map, unsigned id, unsigned def) {
345d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto it = map.find(id);
346d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (it == map.end())
347d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return def;
348d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    else
349d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        return it->second;
350d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
351d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
352d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic unsigned get_locations_consumed_by_type(shader_module const *src, unsigned type, bool strip_array_level) {
353d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto insn = src->get_def(type);
354d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(insn != src->end());
355d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
356d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (insn.opcode()) {
357d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypePointer:
358d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // See through the ptr -- this is only ever at the toplevel for graphics shaders we're never actually passing
359d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // pointers around.
360d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return get_locations_consumed_by_type(src, insn.word(3), strip_array_level);
361d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeArray:
362d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (strip_array_level) {
363d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return get_locations_consumed_by_type(src, insn.word(2), false);
364d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            } else {
365d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return get_constant_value(src, insn.word(3)) * get_locations_consumed_by_type(src, insn.word(2), false);
366d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
367d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeMatrix:
368d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Num locations is the dimension * element size
369d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return insn.word(3) * get_locations_consumed_by_type(src, insn.word(2), false);
370d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeVector: {
371d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto scalar_type = src->get_def(insn.word(2));
372d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto bit_width =
373d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                (scalar_type.opcode() == spv::OpTypeInt || scalar_type.opcode() == spv::OpTypeFloat) ? scalar_type.word(2) : 32;
374d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
375d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Locations are 128-bit wide; 3- and 4-component vectors of 64 bit types require two.
376d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return (bit_width * insn.word(3) + 127) / 128;
377d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
378d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
379d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Everything else is just 1.
380d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return 1;
381d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
382d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // TODO: extend to handle 64bit scalar types, whose vectors may need multiple locations.
383d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
384d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
385d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
386d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic unsigned get_locations_consumed_by_format(VkFormat format) {
387d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (format) {
388d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case VK_FORMAT_R64G64B64A64_SFLOAT:
389d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case VK_FORMAT_R64G64B64A64_SINT:
390d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case VK_FORMAT_R64G64B64A64_UINT:
391d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case VK_FORMAT_R64G64B64_SFLOAT:
392d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case VK_FORMAT_R64G64B64_SINT:
393d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case VK_FORMAT_R64G64B64_UINT:
394d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return 2;
395d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
396d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return 1;
397d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
398d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
399d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
400d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic unsigned get_format_type(VkFormat fmt) {
4015f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    if (FormatIsSInt(fmt)) return FORMAT_TYPE_SINT;
4025f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    if (FormatIsUInt(fmt)) return FORMAT_TYPE_UINT;
4035f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    if (FormatIsDepthAndStencil(fmt)) return FORMAT_TYPE_FLOAT | FORMAT_TYPE_UINT;
4045f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    if (fmt == VK_FORMAT_UNDEFINED) return 0;
405d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // everything else -- UNORM/SNORM/FLOAT/USCALED/SSCALED is all float in the shader.
406d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return FORMAT_TYPE_FLOAT;
407d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
408d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
409d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// characterizes a SPIR-V type appearing in an interface to a FF stage, for comparison to a VkFormat's characterization above.
410d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic unsigned get_fundamental_type(shader_module const *src, unsigned type) {
411d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto insn = src->get_def(type);
412d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(insn != src->end());
413d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
414d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (insn.opcode()) {
415d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeInt:
416d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return insn.word(3) ? FORMAT_TYPE_SINT : FORMAT_TYPE_UINT;
417d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeFloat:
418d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return FORMAT_TYPE_FLOAT;
419d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeVector:
420d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return get_fundamental_type(src, insn.word(2));
421d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeMatrix:
422d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return get_fundamental_type(src, insn.word(2));
423d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeArray:
424d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return get_fundamental_type(src, insn.word(2));
425d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypePointer:
426d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return get_fundamental_type(src, insn.word(3));
427d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeImage:
428d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return get_fundamental_type(src, insn.word(2));
429d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
430d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
431d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return 0;
432d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
433d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
434d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
435d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic uint32_t get_shader_stage_id(VkShaderStageFlagBits stage) {
436d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    uint32_t bit_pos = uint32_t(u_ffs(stage));
437d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return bit_pos - 1;
438d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
439d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
440d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic spirv_inst_iter get_struct_type(shader_module const *src, spirv_inst_iter def, bool is_array_of_verts) {
441d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while (true) {
442d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (def.opcode() == spv::OpTypePointer) {
443d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            def = src->get_def(def.word(3));
444d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (def.opcode() == spv::OpTypeArray && is_array_of_verts) {
445d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            def = src->get_def(def.word(2));
446d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            is_array_of_verts = false;
447d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (def.opcode() == spv::OpTypeStruct) {
448d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return def;
449d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else {
450d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return src->end();
451d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
452d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
453d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
454d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
455e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbesstatic bool collect_interface_block_members(shader_module const *src, std::map<location_t, interface_var> *out,
456d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                            std::unordered_map<unsigned, unsigned> const &blocks, bool is_array_of_verts,
457e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes                                            uint32_t id, uint32_t type_id, bool is_patch, int /*first_location*/) {
458d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Walk down the type_id presented, trying to determine whether it's actually an interface block.
459d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto type = get_struct_type(src, src->get_def(type_id), is_array_of_verts && !is_patch);
460d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (type == src->end() || blocks.find(type.word(1)) == blocks.end()) {
461d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // This isn't an interface block.
462e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes        return false;
463d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
464d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
465d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> member_components;
466d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> member_relaxed_precision;
467e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes    std::unordered_map<unsigned, unsigned> member_patch;
468d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
469d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Walk all the OpMemberDecorate for type's result id -- first pass, collect components.
470d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
471d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1)) {
472d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned member_index = insn.word(2);
473d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
474d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(3) == spv::DecorationComponent) {
475d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                unsigned component = insn.word(4);
476d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                member_components[member_index] = component;
477d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
478d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
479d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(3) == spv::DecorationRelaxedPrecision) {
480d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                member_relaxed_precision[member_index] = 1;
481d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
482e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes
483e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes            if (insn.word(3) == spv::DecorationPatch) {
484e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes                member_patch[member_index] = 1;
485e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes            }
486d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
487d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
488d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
489e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes    // TODO: correctly handle location assignment from outside
490e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes
491d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Second pass -- produce the output, from Location decorations
492d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
493d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1)) {
494d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned member_index = insn.word(2);
495d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned member_type_id = type.word(2 + member_index);
496d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
497d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(3) == spv::DecorationLocation) {
498d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                unsigned location = insn.word(4);
499d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                unsigned num_locations = get_locations_consumed_by_type(src, member_type_id, false);
500d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto component_it = member_components.find(member_index);
501d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                unsigned component = component_it == member_components.end() ? 0 : component_it->second;
502d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                bool is_relaxed_precision = member_relaxed_precision.find(member_index) != member_relaxed_precision.end();
5035f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                bool member_is_patch = is_patch || member_patch.count(member_index) > 0;
504d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
505d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                for (unsigned int offset = 0; offset < num_locations; offset++) {
506d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    interface_var v = {};
507d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.id = id;
508d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    // TODO: member index in interface_var too?
509d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.type_id = member_type_id;
510d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.offset = offset;
511e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes                    v.is_patch = member_is_patch;
512d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.is_block_member = true;
513d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.is_relaxed_precision = is_relaxed_precision;
514d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    (*out)[std::make_pair(location + offset, component)] = v;
515d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
516d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
517d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
518d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
519e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes
520e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes    return true;
521d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
522d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
523d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic std::map<location_t, interface_var> collect_interface_by_location(shader_module const *src, spirv_inst_iter entrypoint,
524d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                                         spv::StorageClass sinterface, bool is_array_of_verts) {
525d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_locations;
526d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_builtins;
527d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_components;
528d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> blocks;
529d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_patch;
530d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_relaxed_precision;
531d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
532d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
533d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // We consider two interface models: SSO rendezvous-by-location, and builtins. Complain about anything that
534d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // fits neither model.
535d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpDecorate) {
536d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationLocation) {
537d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_locations[insn.word(1)] = insn.word(3);
538d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
539d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
540d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationBuiltIn) {
541d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_builtins[insn.word(1)] = insn.word(3);
542d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
543d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
544d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationComponent) {
545d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_components[insn.word(1)] = insn.word(3);
546d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
547d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
548d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationBlock) {
549d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                blocks[insn.word(1)] = 1;
550d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
551d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
552d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationPatch) {
553d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_patch[insn.word(1)] = 1;
554d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
555d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
556d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationRelaxedPrecision) {
557d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_relaxed_precision[insn.word(1)] = 1;
558d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
559d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
560d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
561d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
562d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // TODO: handle grouped decorations
563d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // TODO: handle index=1 dual source outputs from FS -- two vars will have the same location, and we DON'T want to clobber.
564d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
565d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Find the end of the entrypoint's name string. additional zero bytes follow the actual null terminator, to fill out the
566d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // rest of the word - so we only need to look at the last byte in the word to determine which word contains the terminator.
567d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    uint32_t word = 3;
568d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while (entrypoint.word(word) & 0xff000000u) {
569d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        ++word;
570d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
571d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    ++word;
572d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
573d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::map<location_t, interface_var> out;
574d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
575d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (; word < entrypoint.len(); word++) {
576d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto insn = src->get_def(entrypoint.word(word));
577d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        assert(insn != src->end());
578d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        assert(insn.opcode() == spv::OpVariable);
579d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
580d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.word(3) == static_cast<uint32_t>(sinterface)) {
581d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned id = insn.word(2);
582d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned type = insn.word(1);
583d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
584bb6e1ecb1215cdd9aa496b3f9e2c5521711760f6Jamie Madill            int location = value_or_default(var_locations, id, static_cast<unsigned>(-1));
585bb6e1ecb1215cdd9aa496b3f9e2c5521711760f6Jamie Madill            int builtin = value_or_default(var_builtins, id, static_cast<unsigned>(-1));
586d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned component = value_or_default(var_components, id, 0);  // Unspecified is OK, is 0
587d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            bool is_patch = var_patch.find(id) != var_patch.end();
588d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            bool is_relaxed_precision = var_relaxed_precision.find(id) != var_relaxed_precision.end();
589d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
5905f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton            if (builtin != -1)
5915f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                continue;
592e0143ed99bb745a09b27fc7cb535ae7dcc91c09cChris Forbes            else if (!collect_interface_block_members(src, &out, blocks, is_array_of_verts, id, type, is_patch, location)) {
593d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // A user-defined interface variable, with a location. Where a variable occupied multiple locations, emit
594d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // one result for each.
595d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                unsigned num_locations = get_locations_consumed_by_type(src, type, is_array_of_verts && !is_patch);
596d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                for (unsigned int offset = 0; offset < num_locations; offset++) {
597d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    interface_var v = {};
598d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.id = id;
599d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.type_id = type;
600d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.offset = offset;
601d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.is_patch = is_patch;
602d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    v.is_relaxed_precision = is_relaxed_precision;
603d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    out[std::make_pair(location + offset, component)] = v;
604d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
605d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
606d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
607d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
608d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
609d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return out;
610d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
611d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
612d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic std::vector<std::pair<uint32_t, interface_var>> collect_interface_by_input_attachment_index(
613d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    shader_module const *src, std::unordered_set<uint32_t> const &accessible_ids) {
614d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::vector<std::pair<uint32_t, interface_var>> out;
615d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
616d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
617d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpDecorate) {
618d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationInputAttachmentIndex) {
619d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto attachment_index = insn.word(3);
620d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto id = insn.word(1);
621d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
622d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                if (accessible_ids.count(id)) {
623d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    auto def = src->get_def(id);
624d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    assert(def != src->end());
625d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
626d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    if (def.opcode() == spv::OpVariable && insn.word(3) == spv::StorageClassUniformConstant) {
627d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        auto num_locations = get_locations_consumed_by_type(src, def.word(1), false);
628d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        for (unsigned int offset = 0; offset < num_locations; offset++) {
629d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            interface_var v = {};
630d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            v.id = id;
631d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            v.type_id = def.word(1);
632d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            v.offset = offset;
633d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            out.emplace_back(attachment_index + offset, v);
634d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        }
635d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    }
636d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
637d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
638d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
639d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
640d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
641d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return out;
642d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
643d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
644d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic std::vector<std::pair<descriptor_slot_t, interface_var>> collect_interface_by_descriptor_slot(
645d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    debug_report_data const *report_data, shader_module const *src, std::unordered_set<uint32_t> const &accessible_ids) {
646d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_sets;
647d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<unsigned, unsigned> var_bindings;
648d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
649d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
650d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // All variables in the Uniform or UniformConstant storage classes are required to be decorated with both
651d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // DecorationDescriptorSet and DecorationBinding.
652d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpDecorate) {
653d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationDescriptorSet) {
654d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_sets[insn.word(1)] = insn.word(3);
655d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
656d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
657d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(2) == spv::DecorationBinding) {
658d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                var_bindings[insn.word(1)] = insn.word(3);
659d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
660d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
661d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
662d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
663d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::vector<std::pair<descriptor_slot_t, interface_var>> out;
664d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
665d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto id : accessible_ids) {
666d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto insn = src->get_def(id);
667d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        assert(insn != src->end());
668d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
669d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpVariable &&
670d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            (insn.word(3) == spv::StorageClassUniform || insn.word(3) == spv::StorageClassUniformConstant)) {
671d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned set = value_or_default(var_sets, insn.word(2), 0);
672d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned binding = value_or_default(var_bindings, insn.word(2), 0);
673d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
674d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            interface_var v = {};
675d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            v.id = insn.word(2);
676d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            v.type_id = insn.word(1);
677d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            out.emplace_back(std::make_pair(set, binding), v);
678d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
679d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
680d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
681d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return out;
682d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
683d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
684d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_vi_consistency(debug_report_data const *report_data, VkPipelineVertexInputStateCreateInfo const *vi) {
685d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Walk the binding descriptions, which describe the step rate and stride of each vertex buffer.  Each binding should
686d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // be specified only once.
687d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_map<uint32_t, VkVertexInputBindingDescription const *> bindings;
688d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
689d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
690d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (unsigned i = 0; i < vi->vertexBindingDescriptionCount; i++) {
691d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto desc = &vi->pVertexBindingDescriptions[i];
692d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto &binding = bindings[desc->binding];
693d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (binding) {
694d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // TODO: VALIDATION_ERROR_096005cc perhaps?
695d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
696d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_INCONSISTENT_VI, "SC", "Duplicate vertex input binding descriptions for binding %d",
697d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            desc->binding);
698d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else {
699d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            binding = desc;
700d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
701d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
702d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
703d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
704d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
705d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
706d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_vi_against_vs_inputs(debug_report_data const *report_data, VkPipelineVertexInputStateCreateInfo const *vi,
707d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                          shader_module const *vs, spirv_inst_iter entrypoint) {
708d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
709d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
710d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto inputs = collect_interface_by_location(vs, entrypoint, spv::StorageClassInput, false);
711d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
712d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Build index by location
713d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::map<uint32_t, VkVertexInputAttributeDescription const *> attribs;
714d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (vi) {
715d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        for (unsigned i = 0; i < vi->vertexAttributeDescriptionCount; i++) {
716d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto num_locations = get_locations_consumed_by_format(vi->pVertexAttributeDescriptions[i].format);
717d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            for (auto j = 0u; j < num_locations; j++) {
718d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                attribs[vi->pVertexAttributeDescriptions[i].location + j] = &vi->pVertexAttributeDescriptions[i];
719d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
720d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
721d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
722d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
723d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto it_a = attribs.begin();
724d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto it_b = inputs.begin();
725d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool used = false;
726d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
727d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while ((attribs.size() > 0 && it_a != attribs.end()) || (inputs.size() > 0 && it_b != inputs.end())) {
728d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool a_at_end = attribs.size() == 0 || it_a == attribs.end();
729d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool b_at_end = inputs.size() == 0 || it_b == inputs.end();
730d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto a_first = a_at_end ? 0 : it_a->first;
731d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto b_first = b_at_end ? 0 : it_b->first.first;
732d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (!a_at_end && (b_at_end || a_first < b_first)) {
733d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (!used && log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
734d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                 0, __LINE__, SHADER_CHECKER_OUTPUT_NOT_CONSUMED, "SC",
735d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                 "Vertex attribute at location %d not consumed by vertex shader", a_first)) {
736d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip = true;
737d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
738d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            used = false;
739d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_a++;
740d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (!b_at_end && (a_at_end || b_first < a_first)) {
741d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0, __LINE__,
742d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_INPUT_NOT_PRODUCED, "SC", "Vertex shader consumes input at location %d but not provided",
743d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            b_first);
744d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_b++;
745d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else {
746d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned attrib_type = get_format_type(it_a->second->format);
747d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned input_type = get_fundamental_type(vs, it_b->second.type_id);
748d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
749d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Type checking
750d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (!(attrib_type & input_type)) {
751d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
752d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                SHADER_CHECKER_INTERFACE_TYPE_MISMATCH, "SC",
753d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                "Attribute type of `%s` at location %d does not match vertex shader input type of `%s`",
754d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                string_VkFormat(it_a->second->format), a_first, describe_type(vs, it_b->second.type_id).c_str());
755d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
756d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
757d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // OK!
758d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            used = true;
759d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_b++;
760d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
761d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
762d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
763d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
764d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
765d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
766d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_fs_outputs_against_render_pass(debug_report_data const *report_data, shader_module const *fs,
767ac7791048680b52b3f3f751b350567b5092cd02eChris Forbes                                                    spirv_inst_iter entrypoint, PIPELINE_STATE const *pipeline,
768d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                    uint32_t subpass_index) {
769b50f981cf488d18192f3dea325b81f39300c1b3cPetr Kraus    auto rpci = pipeline->rp_state->createInfo.ptr();
7708534a4d376268080c7cf7443a095e3d7ca59af1bChris Forbes
771d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::map<uint32_t, VkFormat> color_attachments;
772d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto subpass = rpci->pSubpasses[subpass_index];
773d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto i = 0u; i < subpass.colorAttachmentCount; ++i) {
774d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        uint32_t attachment = subpass.pColorAttachments[i].attachment;
775d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (attachment == VK_ATTACHMENT_UNUSED) continue;
776d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (rpci->pAttachments[attachment].format != VK_FORMAT_UNDEFINED) {
777d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            color_attachments[i] = rpci->pAttachments[attachment].format;
778d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
779d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
780d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
781d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
782d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
783d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // TODO: dual source blend index (spv::DecIndex, zero if not provided)
784d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
785d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto outputs = collect_interface_by_location(fs, entrypoint, spv::StorageClassOutput, false);
786d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
787d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto it_a = outputs.begin();
788d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto it_b = color_attachments.begin();
789d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
790d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Walk attachment list and outputs together
791d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
792d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while ((outputs.size() > 0 && it_a != outputs.end()) || (color_attachments.size() > 0 && it_b != color_attachments.end())) {
793d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool a_at_end = outputs.size() == 0 || it_a == outputs.end();
794d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool b_at_end = color_attachments.size() == 0 || it_b == color_attachments.end();
795d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
796d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (!a_at_end && (b_at_end || it_a->first.first < it_b->first)) {
797d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
798d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_OUTPUT_NOT_CONSUMED, "SC",
799d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            "fragment shader writes to output location %d with no matching attachment", it_a->first.first);
800d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_a++;
801d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (!b_at_end && (a_at_end || it_a->first.first > it_b->first)) {
802841b2fe0cb1151b0eaf3c519f87162ce960e02c1Chris Forbes            // Only complain if there are unmasked channels for this attachment. If the writemask is 0, it's acceptable for the
803841b2fe0cb1151b0eaf3c519f87162ce960e02c1Chris Forbes            // shader to not produce a matching output.
804ac7791048680b52b3f3f751b350567b5092cd02eChris Forbes            if (pipeline->attachments[it_b->first].colorWriteMask != 0) {
805841b2fe0cb1151b0eaf3c519f87162ce960e02c1Chris Forbes                skip |=
806841b2fe0cb1151b0eaf3c519f87162ce960e02c1Chris Forbes                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
807d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_INPUT_NOT_PRODUCED, "SC", "Attachment %d not written by fragment shader", it_b->first);
808841b2fe0cb1151b0eaf3c519f87162ce960e02c1Chris Forbes            }
809d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_b++;
810d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else {
811d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned output_type = get_fundamental_type(fs, it_a->second.type_id);
812d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            unsigned att_type = get_format_type(it_b->second);
813d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
814d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Type checking
815d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (!(output_type & att_type)) {
816d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
817d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                SHADER_CHECKER_INTERFACE_TYPE_MISMATCH, "SC",
818d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                "Attachment %d of type `%s` does not match fragment shader output type of `%s`", it_b->first,
819d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                string_VkFormat(it_b->second), describe_type(fs, it_a->second.type_id).c_str());
820d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
821d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
822d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // OK!
823d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_a++;
824d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            it_b++;
825d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
826d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
827d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
828d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
829d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
830d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
831d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// For some analyses, we need to know about all ids referenced by the static call tree of a particular entrypoint. This is
832d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// important for identifying the set of shader resources actually used by an entrypoint, for example.
833d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// Note: we only explore parts of the image which might actually contain ids we care about for the above analyses.
834d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes//  - NOT the shader input/output interfaces.
835d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes//
836d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// TODO: The set of interesting opcodes here was determined by eyeballing the SPIRV spec. It might be worth
837d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// converting parts of this to be generated from the machine-readable spec instead.
838d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic std::unordered_set<uint32_t> mark_accessible_ids(shader_module const *src, spirv_inst_iter entrypoint) {
839d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_set<uint32_t> ids;
840d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    std::unordered_set<uint32_t> worklist;
841d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    worklist.insert(entrypoint.word(2));
842d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
843d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while (!worklist.empty()) {
844d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto id_iter = worklist.begin();
845d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto id = *id_iter;
846d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        worklist.erase(id_iter);
847d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
848d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto insn = src->get_def(id);
849d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn == src->end()) {
850d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // ID is something we didn't collect in build_def_index. that's OK -- we'll stumble across all kinds of things here
851d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // that we may not care about.
852d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            continue;
853d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
854d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
855d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // Try to add to the output set
856d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (!ids.insert(id).second) {
857d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            continue;  // If we already saw this id, we don't want to walk it again.
858d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
859d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
860d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        switch (insn.opcode()) {
861d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpFunction:
862d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // Scan whole body of the function, enlisting anything interesting
863d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                while (++insn, insn.opcode() != spv::OpFunctionEnd) {
864d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    switch (insn.opcode()) {
865d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpLoad:
866d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicLoad:
867d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicExchange:
868d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicCompareExchange:
869d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicCompareExchangeWeak:
870d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicIIncrement:
871d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicIDecrement:
872d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicIAdd:
873d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicISub:
874d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicSMin:
875d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicUMin:
876d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicSMax:
877d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicUMax:
878d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicAnd:
879d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicOr:
880d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicXor:
881d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            worklist.insert(insn.word(3));  // ptr
882d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
883d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpStore:
884d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAtomicStore:
885d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            worklist.insert(insn.word(1));  // ptr
886d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
887d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpAccessChain:
888d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpInBoundsAccessChain:
889d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            worklist.insert(insn.word(3));  // base ptr
890d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
891d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpSampledImage:
892d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleImplicitLod:
893d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleExplicitLod:
894d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleDrefImplicitLod:
895d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleDrefExplicitLod:
896d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleProjImplicitLod:
897d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleProjExplicitLod:
898d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleProjDrefImplicitLod:
899d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSampleProjDrefExplicitLod:
900d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageFetch:
901d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageGather:
902d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageDrefGather:
903d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageRead:
904d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImage:
905d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQueryFormat:
906d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQueryOrder:
907d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQuerySizeLod:
908d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQuerySize:
909d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQueryLod:
910d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQueryLevels:
911d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageQuerySamples:
912d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleImplicitLod:
913d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleExplicitLod:
914d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleDrefImplicitLod:
915d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleDrefExplicitLod:
916d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleProjImplicitLod:
917d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleProjExplicitLod:
918d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleProjDrefImplicitLod:
919d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseSampleProjDrefExplicitLod:
920d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseFetch:
921d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseGather:
922d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageSparseDrefGather:
923d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageTexelPointer:
924d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            worklist.insert(insn.word(3));  // Image or sampled image
925d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
926d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpImageWrite:
927d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            worklist.insert(insn.word(1));  // Image -- different operand order to above
928d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
929d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpFunctionCall:
930d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            for (uint32_t i = 3; i < insn.len(); i++) {
931d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                worklist.insert(insn.word(i));  // fn itself, and all args
932d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            }
933d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
934d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
935d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        case spv::OpExtInst:
936d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            for (uint32_t i = 5; i < insn.len(); i++) {
937d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                worklist.insert(insn.word(i));  // Operands to ext inst
938d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            }
939d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            break;
940d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    }
941d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
942d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
943d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
944d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
945d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
946d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return ids;
947d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
948d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
949d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_push_constant_block_against_pipeline(debug_report_data const *report_data,
950d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                          std::vector<VkPushConstantRange> const *push_constant_ranges,
951d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                          shader_module const *src, spirv_inst_iter type,
952d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                          VkShaderStageFlagBits stage) {
953d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
954d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
955d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Strip off ptrs etc
956d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    type = get_struct_type(src, type, false);
957d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    assert(type != src->end());
958d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
959d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Validate directly off the offsets. this isn't quite correct for arrays and matrices, but is a good first step.
960d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // TODO: arrays, matrices, weird sizes
961d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
962d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1)) {
963d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (insn.word(3) == spv::DecorationOffset) {
964d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                unsigned offset = insn.word(4);
965d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto size = 4;  // Bytes; TODO: calculate this based on the type
966d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
967d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                bool found_range = false;
968d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                for (auto const &range : *push_constant_ranges) {
969d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    if (range.offset <= offset && range.offset + range.size >= offset + size) {
970d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        found_range = true;
971d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
972d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        if ((range.stageFlags & stage) == 0) {
9735f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                            skip |=
9745f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
9755f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                        __LINE__, SHADER_CHECKER_PUSH_CONSTANT_NOT_ACCESSIBLE_FROM_STAGE, "SC",
9765f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                        "Push constant range covering variable starting at offset %u not accessible from stage %s",
9775f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                        offset, string_VkShaderStageFlagBits(stage));
978d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        }
979d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
980d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        break;
981d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    }
982d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
983d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
984d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                if (!found_range) {
985d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
986d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                    __LINE__, SHADER_CHECKER_PUSH_CONSTANT_OUT_OF_RANGE, "SC",
9875f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                    "Push constant range covering variable starting at offset %u not declared in layout", offset);
988d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
989d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
990d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
991d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
992d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
993d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
994d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
995d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
996d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_push_constant_usage(debug_report_data const *report_data,
997d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                         std::vector<VkPushConstantRange> const *push_constant_ranges, shader_module const *src,
998d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                         std::unordered_set<uint32_t> accessible_ids, VkShaderStageFlagBits stage) {
999d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
1000d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1001d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto id : accessible_ids) {
1002d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto def_insn = src->get_def(id);
1003d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (def_insn.opcode() == spv::OpVariable && def_insn.word(3) == spv::StorageClassPushConstant) {
1004d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= validate_push_constant_block_against_pipeline(report_data, push_constant_ranges, src,
1005d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                                  src->get_def(def_insn.word(1)), stage);
1006d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1007d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1008d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1009d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
1010d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1011d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1012d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// Validate that data for each specialization entry is fully contained within the buffer.
1013d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_specialization_offsets(debug_report_data const *report_data, VkPipelineShaderStageCreateInfo const *info) {
1014d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
1015d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1016d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    VkSpecializationInfo const *spec = info->pSpecializationInfo;
1017d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1018d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (spec) {
1019d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        for (auto i = 0u; i < spec->mapEntryCount; i++) {
1020d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // TODO: This is a good place for VALIDATION_ERROR_1360060a.
1021d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (spec->pMapEntries[i].offset + spec->pMapEntries[i].size > spec->dataSize) {
1022d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0, __LINE__,
1023d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                VALIDATION_ERROR_1360060c, "SC",
10245f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                "Specialization entry %u (for constant id %u) references memory outside provided specialization "
10255f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                "data (bytes %u.." PRINTF_SIZE_T_SPECIFIER "; " PRINTF_SIZE_T_SPECIFIER " bytes provided). %s.",
10265f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                i, spec->pMapEntries[i].constantID, spec->pMapEntries[i].offset,
10275f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                spec->pMapEntries[i].offset + spec->pMapEntries[i].size - 1, spec->dataSize,
10285f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                validation_error_map[VALIDATION_ERROR_1360060c]);
1029d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1030d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1031d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1032d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1033d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
1034d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1035d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1036d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool descriptor_type_match(shader_module const *module, uint32_t type_id, VkDescriptorType descriptor_type,
1037d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                  unsigned &descriptor_count) {
1038d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto type = module->get_def(type_id);
1039d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1040d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    descriptor_count = 1;
1041d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1042d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Strip off any array or ptrs. Where we remove array levels, adjust the  descriptor count for each dimension.
1043d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while (type.opcode() == spv::OpTypeArray || type.opcode() == spv::OpTypePointer) {
1044d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (type.opcode() == spv::OpTypeArray) {
1045d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            descriptor_count *= get_constant_value(module, type.word(3));
1046d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            type = module->get_def(type.word(2));
1047d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else {
1048d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            type = module->get_def(type.word(3));
1049d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1050d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1051d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1052d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    switch (type.opcode()) {
1053d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeStruct: {
1054d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            for (auto insn : *module) {
1055d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                if (insn.opcode() == spv::OpDecorate && insn.word(1) == type.word(1)) {
1056d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    if (insn.word(2) == spv::DecorationBlock) {
1057d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
10585f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                               descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
1059d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    } else if (insn.word(2) == spv::DecorationBufferBlock) {
1060d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
10615f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                               descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
1062d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    }
1063d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
1064d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1065d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1066d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Invalid
1067d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return false;
1068d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1069d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1070d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeSampler:
1071d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLER || descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1072d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1073d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeSampledImage:
1074d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) {
1075d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // Slight relaxation for some GLSL historical madness: samplerBuffer doesn't really have a sampler, and a texel
1076d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                // buffer descriptor doesn't really provide one. Allow this slight mismatch.
1077d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto image_type = module->get_def(type.word(2));
1078d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto dim = image_type.word(3);
1079d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto sampled = image_type.word(7);
1080d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return dim == spv::DimBuffer && sampled == 1;
1081d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1082d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1083d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1084d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        case spv::OpTypeImage: {
1085d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // Many descriptor types backing image types-- depends on dimension and whether the image will be used with a sampler.
1086d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // SPIRV for Vulkan requires that sampled be 1 or 2 -- leaving the decision to runtime is unacceptable.
1087d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto dim = type.word(3);
1088d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto sampled = type.word(7);
1089d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1090d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (dim == spv::DimSubpassData) {
1091d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return descriptor_type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
1092d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            } else if (dim == spv::DimBuffer) {
1093d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                if (sampled == 1) {
1094d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    return descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
1095d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                } else {
1096d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
1097d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
1098d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            } else if (sampled == 1) {
1099d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ||
11005f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                       descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1101d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            } else {
1102d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
1103d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1104d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1105d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1106d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // We shouldn't really see any other junk types -- but if we do, they're a mismatch.
1107d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        default:
1108d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return false;  // Mismatch
1109d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1110d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1111d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1112d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool require_feature(debug_report_data const *report_data, VkBool32 feature, char const *feature_name) {
1113d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (!feature) {
1114d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1115d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    SHADER_CHECKER_FEATURE_NOT_ENABLED, "SC",
11165f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                    "Shader requires VkPhysicalDeviceFeatures::%s but is not enabled on the device", feature_name)) {
1117d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return true;
1118d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1119d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1120d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1121d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return false;
1122d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1123d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1124d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool require_extension(debug_report_data const *report_data, bool extension, char const *extension_name) {
1125d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (!extension) {
1126d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
11275f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                    SHADER_CHECKER_FEATURE_NOT_ENABLED, "SC", "Shader requires extension %s but is not enabled on the device",
1128d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    extension_name)) {
1129d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return true;
1130d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1131d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1132d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1133d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return false;
1134d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1135d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1136d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_shader_capabilities(layer_data *dev_data, shader_module const *src) {
1137d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
1138d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1139d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto report_data = GetReportData(dev_data);
11405f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    auto const &enabledFeatures = GetEnabledFeatures(dev_data);
11415f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    auto const &extensions = GetEnabledExtensions(dev_data);
1142d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1143d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    struct CapabilityInfo {
1144d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        char const *name;
1145d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        VkBool32 const VkPhysicalDeviceFeatures::*feature;
1146d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool const DeviceExtensions::*extension;
1147d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    };
1148d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1149d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    using F = VkPhysicalDeviceFeatures;
1150d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    using E = DeviceExtensions;
1151d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1152d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // clang-format off
11530f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton    static const std::unordered_multimap<uint32_t, CapabilityInfo> capabilities = {
1154d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // Capabilities always supported by a Vulkan 1.0 implementation -- no
1155d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // feature bits.
1156d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityMatrix, {nullptr}},
1157d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityShader, {nullptr}},
1158d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityInputAttachment, {nullptr}},
1159d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySampled1D, {nullptr}},
1160d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityImage1D, {nullptr}},
1161d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySampledBuffer, {nullptr}},
1162d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityImageQuery, {nullptr}},
1163d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityDerivativeControl, {nullptr}},
1164d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1165d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // Capabilities that are optionally supported, but require a feature to
1166d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // be enabled on the device
1167d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityGeometry, {"geometryShader", &F::geometryShader}},
1168d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityTessellation, {"tessellationShader", &F::tessellationShader}},
1169d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityFloat64, {"shaderFloat64", &F::shaderFloat64}},
1170d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityInt64, {"shaderInt64", &F::shaderInt64}},
1171d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityTessellationPointSize, {"shaderTessellationAndGeometryPointSize", &F::shaderTessellationAndGeometryPointSize}},
1172d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityGeometryPointSize, {"shaderTessellationAndGeometryPointSize", &F::shaderTessellationAndGeometryPointSize}},
1173d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityImageGatherExtended, {"shaderImageGatherExtended", &F::shaderImageGatherExtended}},
1174d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityStorageImageMultisample, {"shaderStorageImageMultisample", &F::shaderStorageImageMultisample}},
1175d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityUniformBufferArrayDynamicIndexing, {"shaderUniformBufferArrayDynamicIndexing", &F::shaderUniformBufferArrayDynamicIndexing}},
1176d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySampledImageArrayDynamicIndexing, {"shaderSampledImageArrayDynamicIndexing", &F::shaderSampledImageArrayDynamicIndexing}},
1177d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityStorageBufferArrayDynamicIndexing, {"shaderStorageBufferArrayDynamicIndexing", &F::shaderStorageBufferArrayDynamicIndexing}},
1178d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityStorageImageArrayDynamicIndexing, {"shaderStorageImageArrayDynamicIndexing", &F::shaderStorageBufferArrayDynamicIndexing}},
1179d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityClipDistance, {"shaderClipDistance", &F::shaderClipDistance}},
1180d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityCullDistance, {"shaderCullDistance", &F::shaderCullDistance}},
1181d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityImageCubeArray, {"imageCubeArray", &F::imageCubeArray}},
1182d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySampleRateShading, {"sampleRateShading", &F::sampleRateShading}},
1183d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySparseResidency, {"shaderResourceResidency", &F::shaderResourceResidency}},
1184d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityMinLod, {"shaderResourceMinLod", &F::shaderResourceMinLod}},
1185d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySampledCubeArray, {"imageCubeArray", &F::imageCubeArray}},
1186d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityImageMSArray, {"shaderStorageImageMultisample", &F::shaderStorageImageMultisample}},
1187d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityStorageImageExtendedFormats, {"shaderStorageImageExtendedFormats", &F::shaderStorageImageExtendedFormats}},
1188d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityInterpolationFunction, {"sampleRateShading", &F::sampleRateShading}},
1189d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityStorageImageReadWithoutFormat, {"shaderStorageImageReadWithoutFormat", &F::shaderStorageImageReadWithoutFormat}},
1190d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityStorageImageWriteWithoutFormat, {"shaderStorageImageWriteWithoutFormat", &F::shaderStorageImageWriteWithoutFormat}},
1191d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityMultiViewport, {"multiViewport", &F::multiViewport}},
1192d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1193d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // Capabilities that require an extension
1194d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityDrawParameters, {VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, nullptr, &E::vk_khr_shader_draw_parameters}},
1195d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityGeometryShaderPassthroughNV, {VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME, nullptr, &E::vk_nv_geometry_shader_passthrough}},
1196d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySampleMaskOverrideCoverageNV, {VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME, nullptr, &E::vk_nv_sample_mask_override_coverage}},
11970f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton        {spv::CapabilityShaderViewportIndexLayerEXT, {VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, nullptr, &E::vk_ext_shader_viewport_index_layer}},
1198d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityShaderViewportIndexLayerNV, {VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, nullptr, &E::vk_nv_viewport_array2}},
1199d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilityShaderViewportMaskNV, {VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, nullptr, &E::vk_nv_viewport_array2}},
1200d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySubgroupBallotKHR, {VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, nullptr, &E::vk_ext_shader_subgroup_ballot }},
1201d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        {spv::CapabilitySubgroupVoteKHR, {VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, nullptr, &E::vk_ext_shader_subgroup_vote }},
1202d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    };
1203d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // clang-format on
1204d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1205d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto insn : *src) {
1206d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (insn.opcode() == spv::OpCapability) {
12070f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton            size_t n = capabilities.count(insn.word(1));
12080f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton            if (1 == n) {  // key occurs exactly once
12090f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                auto it = capabilities.find(insn.word(1));
12100f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                if (it != capabilities.end()) {
12110f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    if (it->second.feature) {
12120f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        skip |= require_feature(report_data, enabledFeatures->*(it->second.feature), it->second.name);
12130f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    }
12140f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    if (it->second.extension) {
12150f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        skip |= require_extension(report_data, extensions->*(it->second.extension), it->second.name);
12160f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    }
12170f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                }
12180f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton            } else if (1 < n) {  // key occurs multiple times, at least one must be enabled
12190f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                bool needs_feature = false, has_feature = false;
12200f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                bool needs_ext = false, has_ext = false;
12210f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                std::string feature_names = "(one of) [ ";
12220f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                std::string extension_names = feature_names;
12230f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                auto caps = capabilities.equal_range(insn.word(1));
12240f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                for (auto it = caps.first; it != caps.second; ++it) {
12250f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    if (it->second.feature) {
12260f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        needs_feature = true;
12270f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        has_feature = has_feature || enabledFeatures->*(it->second.feature);
12280f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        feature_names += it->second.name;
12290f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        feature_names += " ";
12300f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    }
12310f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    if (it->second.extension) {
12320f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        needs_ext = true;
12330f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        has_ext = has_ext || extensions->*(it->second.extension);
12340f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        extension_names += it->second.name;
12350f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                        extension_names += " ";
12360f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    }
12370f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                }
12380f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                if (needs_feature) {
12390f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    feature_names += "]";
12400f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    skip |= require_feature(report_data, has_feature, feature_names.c_str());
1241d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
12420f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                if (needs_ext) {
12430f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    extension_names += "]";
12440f1cc5c132beb2b18c13c500c7e59aad68ec0b07Dave Houlton                    skip |= require_extension(report_data, has_ext, extension_names.c_str());
1245d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
1246d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1247d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1248d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1249d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1250d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
1251d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1252d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1253d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic uint32_t descriptor_type_to_reqs(shader_module const *module, uint32_t type_id) {
1254d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto type = module->get_def(type_id);
1255d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1256d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while (true) {
1257d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        switch (type.opcode()) {
1258d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeArray:
1259d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeSampledImage:
1260d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                type = module->get_def(type.word(2));
1261d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
1262d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypePointer:
1263d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                type = module->get_def(type.word(3));
1264d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                break;
1265d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            case spv::OpTypeImage: {
1266d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto dim = type.word(3);
1267d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto arrayed = type.word(5);
1268d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                auto msaa = type.word(6);
1269d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1270d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                switch (dim) {
1271d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    case spv::Dim1D:
1272d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_1D_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_1D;
1273d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    case spv::Dim2D:
1274d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return (msaa ? DESCRIPTOR_REQ_MULTI_SAMPLE : DESCRIPTOR_REQ_SINGLE_SAMPLE) |
12755f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                               (arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_2D_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_2D);
1276d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    case spv::Dim3D:
1277d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return DESCRIPTOR_REQ_VIEW_TYPE_3D;
1278d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    case spv::DimCube:
1279d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return arrayed ? DESCRIPTOR_REQ_VIEW_TYPE_CUBE_ARRAY : DESCRIPTOR_REQ_VIEW_TYPE_CUBE;
1280d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    case spv::DimSubpassData:
1281d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return msaa ? DESCRIPTOR_REQ_MULTI_SAMPLE : DESCRIPTOR_REQ_SINGLE_SAMPLE;
1282d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    default:  // buffer, etc.
1283d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                        return 0;
1284d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                }
1285d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1286d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            default:
1287d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                return 0;
1288d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1289d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1290d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1291d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1292d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// For given pipelineLayout verify that the set_layout_node at slot.first
1293d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes//  has the requested binding at slot.second and return ptr to that binding
1294d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic VkDescriptorSetLayoutBinding const *get_descriptor_binding(PIPELINE_LAYOUT_NODE const *pipelineLayout,
1295d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                                  descriptor_slot_t slot) {
1296d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (!pipelineLayout) return nullptr;
1297d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1298d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (slot.first >= pipelineLayout->set_layouts.size()) return nullptr;
1299d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1300d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return pipelineLayout->set_layouts[slot.first]->GetDescriptorSetLayoutBindingPtrFromBinding(slot.second);
1301d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1302d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
13035f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houltonstatic bool validate_pipeline_shader_stage(layer_data *dev_data, VkPipelineShaderStageCreateInfo const *pStage,
13045f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                           PIPELINE_STATE *pipeline, shader_module const **out_module,
13055f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                           spirv_inst_iter *out_entrypoint) {
1306d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
1307d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto module = *out_module = GetShaderModuleState(dev_data, pStage->module);
1308d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto report_data = GetReportData(dev_data);
1309d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1310d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (!module->has_valid_spirv) return false;
1311d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1312d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Find the entrypoint
1313d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto entrypoint = *out_entrypoint = find_entrypoint(module, pStage->pName, pStage->stage);
1314d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (entrypoint == module->end()) {
1315d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1316d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    VALIDATION_ERROR_10600586, "SC", "No entrypoint found named `%s` for stage %s. %s.", pStage->pName,
1317d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    string_VkShaderStageFlagBits(pStage->stage), validation_error_map[VALIDATION_ERROR_10600586])) {
1318d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            return true;  // no point continuing beyond here, any analysis is just going to be garbage.
1319d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1320d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1321d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1322d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Validate shader capabilities against enabled device features
1323d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    skip |= validate_shader_capabilities(dev_data, module);
1324d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1325d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Mark accessible ids
1326d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto accessible_ids = mark_accessible_ids(module, entrypoint);
1327d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1328d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Validate descriptor set layout against what the entrypoint actually uses
1329d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto descriptor_uses = collect_interface_by_descriptor_slot(report_data, module, accessible_ids);
1330d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1331d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    skip |= validate_specialization_offsets(report_data, pStage);
13325f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton    skip |= validate_push_constant_usage(report_data, &pipeline->pipeline_layout.push_constant_ranges, module, accessible_ids,
13335f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                         pStage->stage);
1334d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1335d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Validate descriptor use
1336d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (auto use : descriptor_uses) {
1337d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // While validating shaders capture which slots are used by the pipeline
1338d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto &reqs = pipeline->active_slots[use.first.first][use.first.second];
1339d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        reqs = descriptor_req(reqs | descriptor_type_to_reqs(module, use.second.type_id));
1340d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1341d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        // Verify given pipelineLayout has requested setLayout with requested binding
13429ce5c15788e545ece9820a9d3de9075c980dd21aChris Forbes        const auto &binding = get_descriptor_binding(&pipeline->pipeline_layout, use.first);
1343d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        unsigned required_descriptor_count;
1344d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1345d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (!binding) {
1346d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1347d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_MISSING_DESCRIPTOR, "SC",
1348d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            "Shader uses descriptor slot %u.%u (used as type `%s`) but not declared in pipeline layout",
1349d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            use.first.first, use.first.second, describe_type(module, use.second.type_id).c_str());
1350d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (~binding->stageFlags & pStage->stage) {
1351d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0, __LINE__,
1352d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_DESCRIPTOR_NOT_ACCESSIBLE_FROM_STAGE, "SC",
13535f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                            "Shader uses descriptor slot %u.%u (used as type `%s`) but descriptor not accessible from stage %s",
1354d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            use.first.first, use.first.second, describe_type(module, use.second.type_id).c_str(),
1355d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            string_VkShaderStageFlagBits(pStage->stage));
1356d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (!descriptor_type_match(module, use.second.type_id, binding->descriptorType, required_descriptor_count)) {
1357d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1358d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_DESCRIPTOR_TYPE_MISMATCH, "SC",
13595f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                            "Type mismatch on descriptor slot %u.%u (used as type `%s`) but descriptor of type %s", use.first.first,
13605f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                            use.first.second, describe_type(module, use.second.type_id).c_str(),
1361d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            string_VkDescriptorType(binding->descriptorType));
1362d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (binding->descriptorCount < required_descriptor_count) {
1363d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1364d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_DESCRIPTOR_TYPE_MISMATCH, "SC",
1365d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            "Shader expects at least %u descriptors for binding %u.%u (used as type `%s`) but only %u provided",
1366d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            required_descriptor_count, use.first.first, use.first.second,
1367d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            describe_type(module, use.second.type_id).c_str(), binding->descriptorCount);
1368d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1369d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1370d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1371d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Validate use of input attachments against subpass structure
1372d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (pStage->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
1373d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto input_attachment_uses = collect_interface_by_input_attachment_index(module, accessible_ids);
1374d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1375b50f981cf488d18192f3dea325b81f39300c1b3cPetr Kraus        auto rpci = pipeline->rp_state->createInfo.ptr();
1376d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto subpass = pipeline->graphicsPipelineCI.subpass;
1377d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1378d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        for (auto use : input_attachment_uses) {
1379d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto input_attachments = rpci->pSubpasses[subpass].pInputAttachments;
1380d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            auto index = (input_attachments && use.first < rpci->pSubpasses[subpass].inputAttachmentCount)
13815f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                             ? input_attachments[use.first].attachment
13825f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                             : VK_ATTACHMENT_UNUSED;
1383d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1384d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (index == VK_ATTACHMENT_UNUSED) {
1385d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1386d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                SHADER_CHECKER_MISSING_INPUT_ATTACHMENT, "SC",
1387d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                "Shader consumes input attachment index %d but not provided in subpass", use.first);
1388d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            } else if (!(get_format_type(rpci->pAttachments[index].format) & get_fundamental_type(module, use.second.type_id))) {
1389d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |=
1390d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                    log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1391d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_INPUT_ATTACHMENT_TYPE_MISMATCH, "SC",
1392d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            "Subpass input attachment %u format of %s does not match type used in shader `%s`", use.first,
1393d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            string_VkFormat(rpci->pAttachments[index].format), describe_type(module, use.second.type_id).c_str());
1394d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1395d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1396d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1397d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1398d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
1399d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1400d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1401d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbesstatic bool validate_interface_between_stages(debug_report_data const *report_data, shader_module const *producer,
1402d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                              spirv_inst_iter producer_entrypoint, shader_stage_attributes const *producer_stage,
1403d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                              shader_module const *consumer, spirv_inst_iter consumer_entrypoint,
1404d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                              shader_stage_attributes const *consumer_stage) {
1405d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
1406d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1407d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto outputs =
1408d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        collect_interface_by_location(producer, producer_entrypoint, spv::StorageClassOutput, producer_stage->arrayed_output);
1409d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto inputs =
1410d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        collect_interface_by_location(consumer, consumer_entrypoint, spv::StorageClassInput, consumer_stage->arrayed_input);
1411d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1412d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto a_it = outputs.begin();
1413d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto b_it = inputs.begin();
1414d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1415d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // Maps sorted by key (location); walk them together to find mismatches
1416d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while ((outputs.size() > 0 && a_it != outputs.end()) || (inputs.size() && b_it != inputs.end())) {
1417d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool a_at_end = outputs.size() == 0 || a_it == outputs.end();
1418d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        bool b_at_end = inputs.size() == 0 || b_it == inputs.end();
1419d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto a_first = a_at_end ? std::make_pair(0u, 0u) : a_it->first;
1420d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto b_first = b_at_end ? std::make_pair(0u, 0u) : b_it->first;
1421d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1422d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (b_at_end || ((!a_at_end) && (a_first < b_first))) {
1423d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
1424d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            __LINE__, SHADER_CHECKER_OUTPUT_NOT_CONSUMED, "SC",
1425d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            "%s writes to output location %u.%u which is not consumed by %s", producer_stage->name, a_first.first,
1426d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            a_first.second, consumer_stage->name);
1427d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            a_it++;
1428d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else if (a_at_end || a_first > b_first) {
1429d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1430d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            SHADER_CHECKER_INPUT_NOT_PRODUCED, "SC", "%s consumes input location %u.%u which is not written by %s",
1431d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                            consumer_stage->name, b_first.first, b_first.second, producer_stage->name);
1432d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            b_it++;
1433d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        } else {
1434d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // subtleties of arrayed interfaces:
1435d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // - if is_patch, then the member is not arrayed, even though the interface may be.
1436d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            // - if is_block_member, then the extra array level of an arrayed interface is not
1437d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            //   expressed in the member type -- it's expressed in the block type.
1438d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (!types_match(producer, consumer, a_it->second.type_id, b_it->second.type_id,
1439d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                             producer_stage->arrayed_output && !a_it->second.is_patch && !a_it->second.is_block_member,
1440d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                             consumer_stage->arrayed_input && !b_it->second.is_patch && !b_it->second.is_block_member, true)) {
1441d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
1442d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                SHADER_CHECKER_INTERFACE_TYPE_MISMATCH, "SC", "Type mismatch on location %u.%u: '%s' vs '%s'",
1443d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                a_first.first, a_first.second, describe_type(producer, a_it->second.type_id).c_str(),
1444d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                describe_type(consumer, b_it->second.type_id).c_str());
1445d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1446d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (a_it->second.is_patch != b_it->second.is_patch) {
1447d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0, __LINE__,
1448d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                SHADER_CHECKER_INTERFACE_TYPE_MISMATCH, "SC",
14495f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                "Decoration mismatch on location %u.%u: is per-%s in %s stage but per-%s in %s stage",
1450d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                a_first.first, a_first.second, a_it->second.is_patch ? "patch" : "vertex", producer_stage->name,
1451d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                b_it->second.is_patch ? "patch" : "vertex", consumer_stage->name);
1452d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1453d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            if (a_it->second.is_relaxed_precision != b_it->second.is_relaxed_precision) {
1454d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0, __LINE__,
1455d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                SHADER_CHECKER_INTERFACE_TYPE_MISMATCH, "SC",
1456d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                "Decoration mismatch on location %u.%u: %s and %s stages differ in precision", a_first.first,
1457d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                a_first.second, producer_stage->name, consumer_stage->name);
1458d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            }
1459d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            a_it++;
1460d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            b_it++;
1461d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1462d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1463d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1464d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
1465d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1466d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1467d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes// Validate that the shaders used by the given pipeline and store the active_slots
1468d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes//  that are actually used by the pipeline into pPipeline->active_slots
1469ac7791048680b52b3f3f751b350567b5092cd02eChris Forbesbool validate_and_capture_pipeline_shader_state(layer_data *dev_data, PIPELINE_STATE *pipeline) {
1470ac7791048680b52b3f3f751b350567b5092cd02eChris Forbes    auto pCreateInfo = pipeline->graphicsPipelineCI.ptr();
1471d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    int vertex_stage = get_shader_stage_id(VK_SHADER_STAGE_VERTEX_BIT);
1472d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    int fragment_stage = get_shader_stage_id(VK_SHADER_STAGE_FRAGMENT_BIT);
1473d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto report_data = GetReportData(dev_data);
1474d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1475d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    shader_module const *shaders[5];
1476d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    memset(shaders, 0, sizeof(shaders));
1477d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    spirv_inst_iter entrypoints[5];
1478d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    memset(entrypoints, 0, sizeof(entrypoints));
1479d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    bool skip = false;
1480d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1481d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
1482d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto pStage = &pCreateInfo->pStages[i];
1483d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        auto stage_id = get_shader_stage_id(pStage->stage);
1484ac7791048680b52b3f3f751b350567b5092cd02eChris Forbes        skip |= validate_pipeline_shader_stage(dev_data, pStage, pipeline, &shaders[stage_id], &entrypoints[stage_id]);
1485d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1486d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1487d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    // if the shader stages are no good individually, cross-stage validation is pointless.
1488d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (skip) return true;
1489d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1490d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    auto vi = pCreateInfo->pVertexInputState;
1491d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1492d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (vi) {
1493d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        skip |= validate_vi_consistency(report_data, vi);
1494d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1495d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1496d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (shaders[vertex_stage] && shaders[vertex_stage]->has_valid_spirv) {
1497d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        skip |= validate_vi_against_vs_inputs(report_data, vi, shaders[vertex_stage], entrypoints[vertex_stage]);
1498d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1499d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1500d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    int producer = get_shader_stage_id(VK_SHADER_STAGE_VERTEX_BIT);
1501d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    int consumer = get_shader_stage_id(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
1502d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1503d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    while (!shaders[producer] && producer != fragment_stage) {
1504d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        producer++;
1505d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        consumer++;
1506d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1507d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1508d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    for (; producer != fragment_stage && consumer <= fragment_stage; consumer++) {
1509d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        assert(shaders[producer]);
1510d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        if (shaders[consumer] && shaders[consumer]->has_valid_spirv && shaders[producer]->has_valid_spirv) {
1511d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            skip |= validate_interface_between_stages(report_data, shaders[producer], entrypoints[producer],
1512d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                      &shader_stage_attribs[producer], shaders[consumer], entrypoints[consumer],
1513d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes                                                      &shader_stage_attribs[consumer]);
1514d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1515d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes            producer = consumer;
1516d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes        }
1517d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1518d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1519d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    if (shaders[fragment_stage] && shaders[fragment_stage]->has_valid_spirv) {
15205f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton        skip |= validate_fs_outputs_against_render_pass(report_data, shaders[fragment_stage], entrypoints[fragment_stage], pipeline,
15215f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                                                        pCreateInfo->subpass);
1522d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    }
1523d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1524d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    return skip;
1525d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
1526d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1527ac7791048680b52b3f3f751b350567b5092cd02eChris Forbesbool validate_compute_pipeline(layer_data *dev_data, PIPELINE_STATE *pipeline) {
1528ac7791048680b52b3f3f751b350567b5092cd02eChris Forbes    auto pCreateInfo = pipeline->computePipelineCI.ptr();
1529d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1530d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    shader_module const *module;
1531d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes    spirv_inst_iter entrypoint;
1532d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes
1533ac7791048680b52b3f3f751b350567b5092cd02eChris Forbes    return validate_pipeline_shader_stage(dev_data, &pCreateInfo->stage, pipeline, &module, &entrypoint);
1534d7aee1a79b51b1a46cb4f3b0667c24f9416fdd64Chris Forbes}
15357aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15365f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houltonuint32_t ValidationCache::MakeShaderHash(VkShaderModuleCreateInfo const *smci) { return XXH32(smci->pCode, smci->codeSize, 0); }
1537fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes
15385f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houltonstatic ValidationCache *GetValidationCacheInfo(VkShaderModuleCreateInfo const *pCreateInfo) {
1539fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes    while ((pCreateInfo = (VkShaderModuleCreateInfo const *)pCreateInfo->pNext) != nullptr) {
1540fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes        if (pCreateInfo->sType == VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT)
1541fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes            return (ValidationCache *)((VkShaderModuleValidationCacheCreateInfoEXT const *)pCreateInfo)->validationCache;
1542fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes    }
1543fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes
1544fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes    return nullptr;
1545fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes}
1546fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes
15477aec8a354c8de254cab09f6da7f24a794525d31eChris Forbesbool PreCallValidateCreateShaderModule(layer_data *dev_data, VkShaderModuleCreateInfo const *pCreateInfo, bool *spirv_valid) {
15487aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    bool skip = false;
15497aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    spv_result_t spv_valid = SPV_SUCCESS;
15507aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    auto report_data = GetReportData(dev_data);
15517aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15527aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    if (GetDisables(dev_data)->shader_validation) {
15537aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        return false;
15547aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    }
15557aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15567aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    auto have_glsl_shader = GetEnabledExtensions(dev_data)->vk_nv_glsl_shader;
15577aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15587aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    if (!have_glsl_shader && (pCreateInfo->codeSize % 4)) {
15595f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton        skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__,
15605f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                        VALIDATION_ERROR_12a00ac0, "SC",
15617aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes                        "SPIR-V module not valid: Codesize must be a multiple of 4 but is " PRINTF_SIZE_T_SPECIFIER ". %s",
15627aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes                        pCreateInfo->codeSize, validation_error_map[VALIDATION_ERROR_12a00ac0]);
15637aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    } else {
1564fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes        auto cache = GetValidationCacheInfo(pCreateInfo);
1565fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes        uint32_t hash = 0;
1566fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes        if (cache) {
1567fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes            hash = ValidationCache::MakeShaderHash(pCreateInfo);
15685f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton            if (cache->Contains(hash)) return false;
1569fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes        }
1570fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes
15717aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        // Use SPIRV-Tools validator to try and catch any issues with the module itself
15727aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        spv_context ctx = spvContextCreate(SPV_ENV_VULKAN_1_0);
15735f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton        spv_const_binary_t binary{pCreateInfo->pCode, pCreateInfo->codeSize / sizeof(uint32_t)};
15747aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        spv_diagnostic diag = nullptr;
15757aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15767aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        spv_valid = spvValidate(ctx, &binary, &diag);
15777aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        if (spv_valid != SPV_SUCCESS) {
15787aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes            if (!have_glsl_shader || (pCreateInfo->pCode[0] == spv::MagicNumber)) {
15795f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                skip |=
15805f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                    log_msg(report_data, spv_valid == SPV_WARNING ? VK_DEBUG_REPORT_WARNING_BIT_EXT : VK_DEBUG_REPORT_ERROR_BIT_EXT,
15815f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                            VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__, SHADER_CHECKER_INCONSISTENT_SPIRV, "SC",
15825f6b7bf3dab2c213b407e1dc16129c04e4405ff3Dave Houlton                            "SPIR-V module not valid: %s", diag && diag->error ? diag->error : "(no error text)");
15837aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes            }
1584fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes        } else {
1585fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes            if (cache) {
1586fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes                cache->Insert(hash);
1587fe772548f7a48b12772483e8faecc956fb76eb42Chris Forbes            }
15887aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        }
15897aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15907aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        spvDiagnosticDestroy(diag);
15917aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes        spvContextDestroy(ctx);
15927aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    }
15937aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes
15947aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    *spirv_valid = (spv_valid == SPV_SUCCESS);
15957aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes    return skip;
15967aec8a354c8de254cab09f6da7f24a794525d31eChris Forbes}
1597