blob: 653b0f9c70e623b90eac230b9c3646959f94194e [file] [log] [blame]
/* Copyright (c) 2021-2023 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* The Shader Module file is in charge of all things around creating and parsing an internal representation of a shader module
*/
#pragma once
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <unordered_map>
#include <vector>
#include "state_tracker/shader_instruction.h"
#include "state_tracker/base_node.h"
#include "state_tracker/sampler_state.h"
#include <spirv/unified1/spirv.hpp>
#include "spirv-tools/optimizer.hpp"
class PIPELINE_STATE;
struct EntryPoint;
static constexpr uint32_t kInvalidSpirvValue = std::numeric_limits<uint32_t>::max();
// This is the common info for both OpDecorate and OpMemberDecorate
// Used to keep track of all decorations applied to any instruction
struct DecorationBase {
enum FlagBit {
patch_bit = 1 << 0,
block_bit = 1 << 1,
buffer_block_bit = 1 << 2,
nonwritable_bit = 1 << 3,
nonreadable_bit = 1 << 4,
per_vertex_bit = 1 << 5,
passthrough_bit = 1 << 6,
aliased_bit = 1 << 7,
input_attachment_bit = 1 << 8,
per_task_nv = 1 << 9,
per_primitive_ext = 1 << 10,
};
// bits to know if things have been set or not by a Decoration
uint32_t flags = 0;
// When being used as an User-defined Variable (input, output, rtx)
uint32_t location = kInvalidSpirvValue;
// Component is optional and spec says it is 0 if not defined
uint32_t component = 0;
uint32_t offset = 0;
// A given object can only have a single BuiltIn OpDecoration
uint32_t builtin = kInvalidSpirvValue;
void Add(uint32_t decoration, uint32_t value);
bool Has(FlagBit flag_bit) const { return (flags & flag_bit) != 0; }
};
// subset only for OpDecorate
// Can't have nested structs with OpMemberDecorate, this class prevents accidently creating a 2nd level of member decorations,
struct DecorationSet : public DecorationBase {
// For descriptors
uint32_t set = 0;
uint32_t binding = 0;
// Value of InputAttachmentIndex the variable starts
uint32_t input_attachment_index_start = kInvalidSpirvValue;
// <index into struct, DecorationBase>
vvl::unordered_map<uint32_t, DecorationBase> member_decorations;
void Add(uint32_t decoration, uint32_t value);
bool HasBuiltIn() const;
bool HasInMember(FlagBit flag_bit) const;
bool AllMemberHave(FlagBit flag_bit) const;
};
// Tracking of OpExecutionMode / OpExecutionModeId values
struct ExecutionModeSet {
enum FlagBit {
output_points_bit = 1 << 0,
point_mode_bit = 1 << 1,
post_depth_coverage_bit = 1 << 2,
local_size_bit = 1 << 3,
local_size_id_bit = 1 << 4,
iso_lines_bit = 1 << 5,
xfb_bit = 1 << 6,
early_fragment_test_bit = 1 << 7,
subgroup_uniform_control_flow_bit = 1 << 8,
signed_zero_inf_nan_preserve_width_16 = 1 << 9,
signed_zero_inf_nan_preserve_width_32 = 1 << 10,
signed_zero_inf_nan_preserve_width_64 = 1 << 11,
denorm_preserve_width_16 = 1 << 12,
denorm_preserve_width_32 = 1 << 13,
denorm_preserve_width_64 = 1 << 14,
denorm_flush_to_zero_width_16 = 1 << 15,
denorm_flush_to_zero_width_32 = 1 << 16,
denorm_flush_to_zero_width_64 = 1 << 17,
rounding_mode_rte_width_16 = 1 << 18,
rounding_mode_rte_width_32 = 1 << 19,
rounding_mode_rte_width_64 = 1 << 20,
rounding_mode_rtz_width_16 = 1 << 21,
rounding_mode_rtz_width_32 = 1 << 22,
rounding_mode_rtz_width_64 = 1 << 23,
};
// bits to know if things have been set or not by a Decoration
uint32_t flags = 0;
VkPrimitiveTopology input_primitive_topology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
VkPrimitiveTopology primitive_topology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
// SPIR-V spec says only LocalSize or LocalSizeId can be used, so can share
uint32_t local_size_x = kInvalidSpirvValue;
uint32_t local_size_y = kInvalidSpirvValue;
uint32_t local_size_z = kInvalidSpirvValue;
uint32_t output_vertices = 0;
uint32_t output_primitives = 0;
uint32_t invocations = 0;
uint32_t tessellation_subdivision = 0;
uint32_t tessellation_orientation = 0;
uint32_t tessellation_spacing = 0;
void Add(const Instruction &insn);
bool Has(FlagBit flag_bit) const { return (flags & flag_bit) != 0; }
};
// Contains all the details for a OpTypeStruct
struct TypeStructInfo {
const uint32_t id;
const uint32_t length; // number of elements
const DecorationSet &decorations;
// data about each member in struct
struct Member {
uint32_t id;
const Instruction *insn;
const DecorationBase *decorations;
std::shared_ptr<const TypeStructInfo> type_struct_info; // for nested structs
};
std::vector<Member> members;
TypeStructInfo(const SPIRV_MODULE_STATE &module_state, const Instruction &struct_insn);
};
// Represents the OpImage* instructions and how it maps to the variable
// This is created in the SPIRV_MODULE_STATE but then used with VariableBase objects
struct ImageAccess {
const Instruction &image_insn; // OpImage*
const Instruction *variable_image_insn = nullptr;
// If there is a OpSampledImage there will also be a sampler variable
const Instruction *variable_sampler_insn = nullptr;
bool no_function_jump = true; // TODO 5614 - Handle function jumps
bool is_dref = false;
bool is_sampler_implicitLod_dref_proj = false;
bool is_sampler_sampled = false;
bool is_sampler_bias_offset = false;
bool is_written_to = false;
bool is_read_from = false;
uint32_t image_access_chain_index = kInvalidSpirvValue; // OpAccessChain's Index 0
uint32_t sampler_access_chain_index = kInvalidSpirvValue; // OpAccessChain's Index 0
uint32_t texel_component_count = kInvalidSpirvValue;
ImageAccess(const SPIRV_MODULE_STATE &module_state, const Instruction &image_insn);
};
// <Image OpVariable Result ID, [ImageAccess, ImageAccess, etc] > - used for faster lookup
// Many ImageAccess can point to a single Image Variable
using ImageAccessMap = vvl::unordered_map<uint32_t, std::vector<std::shared_ptr<const ImageAccess>>>;
// A slot is a <Location, Component> mapping
struct InterfaceSlot {
// A Location is made up of 4 Components
// Example: Location 2, Component 1
// L0 : [ C0, C1, C2, C3 ]
// L1 : [ C0, C1, C2, C3 ]
// L2 : [ C0, C1, C2, C3 ]
// ^
// index == 9 == (Location * 4) + Component
const uint32_t slot = 0; // default
// Information about the variable type
// Easier to find this information once then re-look each time (mainly for structs)
const uint32_t type = 0; // Opcode of OpType*
const uint32_t bit_width = 0;
uint32_t Location() const { return slot / 4; }
uint32_t Component() const { return slot % 4; }
std::string Describe() const;
// Having a single uint32_t slot allows a 64-bit Vec3 to pass in (Loc 0, Comp 5) and have it automatically mean (Loc 1, Comp 1)
InterfaceSlot(uint32_t location, uint32_t component, uint32_t type, uint32_t bit_width)
: slot(GetSlotValue(location, component)), type(type), bit_width(bit_width) {}
InterfaceSlot(uint32_t slot, uint32_t type, uint32_t bit_width) : slot(slot), type(type), bit_width(bit_width) {}
bool operator<(const InterfaceSlot &rhs) const { return slot < rhs.slot; }
bool operator==(const InterfaceSlot &rhs) const { return slot == rhs.slot; }
struct Hash {
std::size_t operator()(const InterfaceSlot &object) const { return object.slot; }
};
uint32_t GetSlotValue(uint32_t location, uint32_t component) { return (location * 4) + component; }
};
// Represents the Image formats that can map to a SPIR-V format
enum NumericType {
NumericTypeUnknown = 0, // In case image is not used
NumericTypeFloat = 1, // UNORM, SNORM, FLOAT, USCALED, SSCALED, SRGB -- anything we consider float in the shader
NumericTypeSint = 2,
NumericTypeUint = 4,
};
uint32_t GetFormatType(VkFormat format);
char const *string_NumericType(uint32_t type);
// Common info needed for all OpVariable
struct VariableBase {
const uint32_t id;
const uint32_t type_id;
const spv::StorageClass storage_class;
const DecorationSet &decorations;
std::shared_ptr<const TypeStructInfo> type_struct_info; // null if no struct type
// If variable is accessed from mulitple entrypoint, create a seperate VariableBase object as info about it being access will be
// different
const VkShaderStageFlagBits stage;
VariableBase(const SPIRV_MODULE_STATE &module_state, const Instruction &insn, VkShaderStageFlagBits stage);
};
// These are Input/Output OpVariable that go in-between stages
// (also for example the input to a Vertex and output of the Fragment).
// These are always ints/floats (not images or samplers).
// Besides the input vertex binding, all of these are fully known at pipeline creation time
//
// These include both BuiltIns and User Defined, while there are difference in member variables, the variables are needed for the
// common logic so its easier using the same object in the end
struct StageInteraceVariable : public VariableBase {
// Only will be true in BuiltIns
const bool is_patch;
const bool is_per_vertex; // VK_KHR_fragment_shader_barycentric
const bool is_per_task_nv; // VK_NV_mesh_shader
const bool is_array_interface;
uint32_t array_size = 1; // flatten size of all dimensions; 1 if no array
const Instruction &base_type;
const bool is_builtin;
bool nested_struct;
bool physical_storage_buffer;
const std::vector<InterfaceSlot> interface_slots; // Only for User Defined variables
const std::vector<uint32_t> builtin_block;
uint32_t total_builtin_components = 0;
StageInteraceVariable(const SPIRV_MODULE_STATE &module_state, const Instruction &insn, VkShaderStageFlagBits stage);
protected:
static bool IsPerTaskNV(const StageInteraceVariable &variable);
static bool IsArrayInterface(const StageInteraceVariable &variable);
static const Instruction &FindBaseType(StageInteraceVariable &variable, const SPIRV_MODULE_STATE &module_state);
static bool IsBuiltin(const StageInteraceVariable &variable, const SPIRV_MODULE_STATE &module_state);
static std::vector<InterfaceSlot> GetInterfaceSlots(StageInteraceVariable &variable, const SPIRV_MODULE_STATE &module_state);
static std::vector<uint32_t> GetBuiltinBlock(const StageInteraceVariable &variable, const SPIRV_MODULE_STATE &module_state);
static uint32_t GetBuiltinComponents(const StageInteraceVariable &variable, const SPIRV_MODULE_STATE &module_state);
};
// vkspec.html#interfaces-resources describes 'Shader Resource Interface'
// These are the OpVariable attached to descriptors.
// The slots are known at Pipeline creation time, but the type images/sampler/etc is
// not known until the descriptors are bound.
// The main purpose of this struct is to track what operations are statically done so
// at draw/submit time we can cross reference with the last bound descriptor.
struct ResourceInterfaceVariable : public VariableBase {
// If the type is a OpTypeArray save the length
uint32_t array_length;
bool runtime_array; // OpTypeRuntimeArray - can't validate length until run time
bool is_sampled_image; // OpTypeSampledImage
// List of samplers that sample a given image. The index of array is index of image.
std::vector<vvl::unordered_set<SamplerUsedByImage>> samplers_used_by_image;
// For storage images - list of Texel component length the OpImageWrite
std::vector<uint32_t> write_without_formats_component_count_list;
// A variable can have an array of indexes, need to track which are written to
// can't use bitset because number of indexes isn't known until runtime
// This array will match the OpTypeArray and not consider the InputAttachmentIndex
std::vector<bool> input_attachment_index_read;
// Type once array/pointer are stripped
// most likly will be OpTypeImage, OpTypeStruct, OpTypeSampler, or OpTypeAccelerationStructureKHR
const Instruction &base_type;
const NumericType image_format_type;
bool IsImage() const { return image_format_type != NumericTypeUnknown; }
const spv::Dim image_dim;
const bool is_image_array;
const bool is_multisampled;
// Sampled Type width of the OpTypeImage the variable points to, 0 if doesn't use the image
uint32_t image_sampled_type_width = 0;
bool is_read_from{false}; // has operation to reads from the variable
bool is_written_to{false}; // has operation to writes to the variable
// Type of resource type (vkspec.html#interfaces-resources-storage-class-correspondence)
bool is_storage_image{false};
bool is_storage_texel_buffer{false};
const bool is_storage_buffer;
bool is_input_attachment{false};
bool is_atomic_operation{false};
bool is_sampler_sampled{false};
bool is_sampler_implicitLod_dref_proj{false};
bool is_sampler_bias_offset{false};
bool is_read_without_format{false}; // For storage images
bool is_write_without_format{false}; // For storage images
bool is_dref{false};
ResourceInterfaceVariable(const SPIRV_MODULE_STATE &module_state, const EntryPoint &entrypoint, const Instruction &insn,
const ImageAccessMap &image_access_map);
protected:
static const Instruction &FindBaseType(ResourceInterfaceVariable &variable, const SPIRV_MODULE_STATE &module_state);
static NumericType FindImageFormatType(const SPIRV_MODULE_STATE &module_state, const Instruction &base_type);
static bool IsStorageBuffer(const ResourceInterfaceVariable &variable);
};
// Used to help detect if different variable is being used
inline bool operator==(const ResourceInterfaceVariable &a, const ResourceInterfaceVariable &b) noexcept { return a.id == b.id; }
inline bool operator<(const ResourceInterfaceVariable &a, const ResourceInterfaceVariable &b) noexcept { return a.id < b.id; }
// vkspec.html#interfaces-resources-pushconst
// Push constants need to be statically used in shader
// Push constants are always OpTypeStruct and Block decorated
struct PushConstantVariable : public VariableBase {
// This info could be found/saved in TypeStructInfo, but since Push Constants are the only ones using it right now, no point to
// do it for every struct
uint32_t offset; // where first member is
uint32_t size; // total size of block
PushConstantVariable(const SPIRV_MODULE_STATE &module_state, const Instruction &insn, VkShaderStageFlagBits stage);
};
// Represents a single Entrypoint into a Shader Module
struct EntryPoint {
// "A module must not have two OpEntryPoint instructions with the same Execution Model and the same Name string."
// There is no single unique item for a single entry point
const Instruction &entrypoint_insn; // OpEntryPoint instruction
// For things like MeshNV vs MeshEXT, we need the execution_model
const spv::ExecutionModel execution_model;
const VkShaderStageFlagBits stage;
const uint32_t id;
const std::string name;
const ExecutionModeSet &execution_mode;
// Values found while gather the Accessible Ids
bool emit_vertex_geometry;
// All ids that can be accessed from the entry point
const vvl::unordered_set<uint32_t> accessible_ids;
// only one Push Constant block is allowed per entry point
std::shared_ptr<const PushConstantVariable> push_constant_variable;
const std::vector<ResourceInterfaceVariable> resource_interface_variables;
const std::vector<StageInteraceVariable> stage_interface_variables;
// Easier to lookup without having to check for the is_builtin bool
// "Built-in interface variables" - vkspec.html#interfaces-iointerfaces-builtin
std::vector<const StageInteraceVariable *> built_in_variables;
// "User-defined Variable Interface" - vkspec.html#interfaces-iointerfaces-user
std::vector<const StageInteraceVariable *> user_defined_interface_variables;
// Lookup map from Interface slot to the variable in that spot
// spirv-val guarantees no overlap so 2 variables won't have same slot
std::unordered_map<InterfaceSlot, const StageInteraceVariable *, InterfaceSlot::Hash> input_interface_slots;
std::unordered_map<InterfaceSlot, const StageInteraceVariable *, InterfaceSlot::Hash> output_interface_slots;
// Uesd for limit check
const StageInteraceVariable *max_input_slot_variable = nullptr;
const StageInteraceVariable *max_output_slot_variable = nullptr;
const InterfaceSlot *max_input_slot = nullptr;
const InterfaceSlot *max_output_slot = nullptr;
uint32_t builtin_input_components = 0;
uint32_t builtin_output_components = 0;
// Mark if a BuiltIn is written to
bool written_builtin_point_size{false};
bool written_builtin_primitive_shading_rate_khr{false};
bool written_builtin_viewport_index{false};
bool written_builtin_viewport_mask_nv{false};
bool has_passthrough{false};
bool has_alpha_to_coverage_variable{false}; // only for Fragment shaders
EntryPoint(const SPIRV_MODULE_STATE &module_state, const Instruction &entrypoint_insn, const ImageAccessMap &image_access_map);
protected:
static vvl::unordered_set<uint32_t> GetAccessibleIds(const SPIRV_MODULE_STATE &module_state, EntryPoint &entrypoint);
static std::vector<StageInteraceVariable> GetStageInterfaceVariables(const SPIRV_MODULE_STATE &module_state,
const EntryPoint &entrypoint);
static std::vector<ResourceInterfaceVariable> GetResourceInterfaceVariables(const SPIRV_MODULE_STATE &module_state,
EntryPoint &entrypoint,
const ImageAccessMap &image_access_map);
};
// Represents a SPIR-V Module
// This holds the SPIR-V source and parse it
struct SPIRV_MODULE_STATE {
// Static/const data extracted from a SPIRV module at initialization time
// The goal of this struct is to move everything that is ready only into here
struct StaticData {
StaticData() = default;
StaticData(const SPIRV_MODULE_STATE &module_state);
StaticData &operator=(StaticData &&) = default;
StaticData(StaticData &&) = default;
// List of all instructions in the order they appear in the binary
std::vector<Instruction> instructions;
// Instructions that can be referenced by Ids
// A mapping of <id> to the first word of its def. this is useful because walking type
// trees, constant expressions, etc requires jumping all over the instruction stream.
vvl::unordered_map<uint32_t, const Instruction *> definitions;
vvl::unordered_map<uint32_t, DecorationSet> decorations;
DecorationSet empty_decoration; // all zero values, allows use to return a reference and not a copy each time
// Execution Modes are tied to a Function <id>, multiple EntryPoints can point to the same Funciton <id>
// Keep a mapping so each EntryPoint can grab a reference to it
vvl::unordered_map<uint32_t, ExecutionModeSet> execution_modes;
ExecutionModeSet empty_execution_mode; // all zero values, allows use to return a reference and not a copy each time
// <Specialization constant ID -> target ID> mapping
vvl::unordered_map<uint32_t, uint32_t> spec_const_map;
// <target ID - > Specialization constant ID> mapping
// TODO - Remove having a second copy for the map in reverse
vvl::unordered_map<uint32_t, uint32_t> id_to_spec_id;
// Find all decoration instructions to prevent relooping module later - many checks need this info
std::vector<const Instruction *> decoration_inst;
std::vector<const Instruction *> member_decoration_inst;
// Find all variable instructions to prevent relookping module later
std::vector<const Instruction *> variable_inst;
// both OpDecorate and OpMemberDecorate builtin instructions
std::vector<const Instruction *> builtin_decoration_inst;
// OpEmitStreamVertex/OpEndStreamPrimitive - only allowed in Geometry shader
std::vector<const Instruction *> transform_feedback_stream_inst;
// OpString - used to find debug information
std::vector<const Instruction *> debug_string_inst;
// For shader tile image - OpDepthAttachmentReadEXT/OpStencilAttachmentReadEXT/OpColorAttachmentReadEXT
bool has_shader_tile_image_depth_read{false};
bool has_shader_tile_image_stencil_read{false};
bool has_shader_tile_image_color_read{false};
// BuiltIn we just care about existing or not, don't have to be written to
bool has_builtin_layer{false};
bool has_builtin_fully_covered{false};
bool has_builtin_workgroup_size{false};
uint32_t builtin_workgroup_size_id = 0;
std::vector<const Instruction *> atomic_inst;
std::vector<const Instruction *> group_inst;
std::vector<const Instruction *> read_clock_inst;
std::vector<const Instruction *> cooperative_matrix_inst;
std::vector<spv::Capability> capability_list;
bool has_specialization_constants{false};
bool has_invocation_repack_instruction{false};
// EntryPoint has pointer references inside it that need to be preserved
std::vector<std::shared_ptr<EntryPoint>> entry_points;
std::vector<std::shared_ptr<TypeStructInfo>> type_structs; // All OpTypeStruct objects
// <OpTypeStruct ID, info> - used for faster lookup as there can many structs
vvl::unordered_map<uint32_t, std::shared_ptr<const TypeStructInfo>> type_struct_map;
bool has_group_decoration{false};
// Tracks accesses (load, store, atomic) to the instruction calling them
// Example: the OpLoad does the "access" but need to know if a OpImageRead uses that OpLoad later
vvl::unordered_map<const Instruction *, uint32_t> image_write_load_id_map; // <OpImageWrite, load id>
std::vector<uint32_t> atomic_pointer_ids;
std::vector<uint32_t> store_pointer_ids;
std::vector<uint32_t> atomic_store_pointer_ids;
vvl::unordered_map<uint32_t, uint32_t> load_members; // <result id, pointer>
vvl::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> accesschain_members; // <result id, <base,index[0]>>
vvl::unordered_map<uint32_t, uint32_t> image_texel_pointer_members; // <result id, image>
};
// This is the SPIR-V module data content
const std::vector<uint32_t> words_;
const StaticData static_data_;
// Hold a handle so error message can know where the SPIR-V was from (VkShaderModule or VkShaderEXT)
VulkanTypedHandle handle_; // Will be updated once its known its valid SPIR-V
VulkanTypedHandle handle() const { return handle_; } // matches normal convention to get handle
// Used for when modifying the SPIR-V (spirv-opt, GPU-AV instrumentation, etc) and need reparse it for VVL validaiton
SPIRV_MODULE_STATE(vvl::span<const uint32_t> code) : words_(code.begin(), code.end()), static_data_(*this) {}
SPIRV_MODULE_STATE(size_t codeSize, const uint32_t *pCode)
: words_(pCode, pCode + codeSize / sizeof(uint32_t)), static_data_(*this) {}
const Instruction *FindDef(uint32_t id) const {
auto it = static_data_.definitions.find(id);
if (it == static_data_.definitions.end()) return nullptr;
return it->second;
}
const std::vector<Instruction> &GetInstructions() const { return static_data_.instructions; }
const std::vector<const Instruction *> FindVariableAccesses(uint32_t variable_id, const std::vector<uint32_t> &access_ids,
bool atomic) const;
const DecorationSet &GetDecorationSet(uint32_t id) const {
// return the actual decorations for this id, or a default empty set.
const auto it = static_data_.decorations.find(id);
return (it != static_data_.decorations.end()) ? it->second : static_data_.empty_decoration;
}
const ExecutionModeSet &GetExecutionModeSet(uint32_t function_id) const {
// return the actual execution modes for this id, or a default empty set.
const auto it = static_data_.execution_modes.find(function_id);
return (it != static_data_.execution_modes.end()) ? it->second : static_data_.empty_execution_mode;
}
std::shared_ptr<const TypeStructInfo> GetTypeStructInfo(uint32_t struct_id) const {
// return the actual execution modes for this id, or a default empty set.
const auto it = static_data_.type_struct_map.find(struct_id);
return (it != static_data_.type_struct_map.end()) ? it->second : nullptr;
}
// Overload to walk down and find the OpTypeStruct
std::shared_ptr<const TypeStructInfo> GetTypeStructInfo(const Instruction *insn) const {
while (true) {
if (insn->Opcode() == spv::OpVariable) {
insn = FindDef(insn->Word(1));
} else if (insn->Opcode() == spv::OpTypePointer) {
insn = FindDef(insn->Word(3));
} else if (insn->IsArray()) {
insn = FindDef(insn->Word(2));
} else if (insn->Opcode() == spv::OpTypeStruct) {
return GetTypeStructInfo(insn->Word(1));
} else {
return nullptr;
}
}
}
// Used to get human readable strings for error messages
void DescribeTypeInner(std::ostringstream &ss, uint32_t type, uint32_t indent) const;
std::string DescribeType(uint32_t type) const;
std::optional<VkPrimitiveTopology> GetTopology(const EntryPoint &entrypoint) const;
std::shared_ptr<const EntryPoint> FindEntrypoint(char const *name, VkShaderStageFlagBits stageBits) const;
bool FindLocalSize(const EntryPoint &entrypoint, uint32_t &local_size_x, uint32_t &local_size_y, uint32_t &local_size_z) const;
uint32_t CalculateWorkgroupSharedMemory() const;
const Instruction *GetConstantDef(uint32_t id) const;
uint32_t GetConstantValueById(uint32_t id) const;
uint32_t GetLocationsConsumedByType(uint32_t type) const;
uint32_t GetComponentsConsumedByType(uint32_t type) const;
NumericType GetNumericType(uint32_t type) const;
bool IsBuiltInWritten(const Instruction *builtin_insn, const EntryPoint &entrypoint) const;
// Instruction helpers that need the knowledge of the whole SPIR-V module
uint32_t GetNumComponentsInBaseType(const Instruction *insn) const;
uint32_t GetTypeBitsSize(const Instruction *insn) const;
uint32_t GetTypeBytesSize(const Instruction *insn) const;
uint32_t GetBaseType(const Instruction *insn) const;
const Instruction *GetBaseTypeInstruction(uint32_t type) const;
uint32_t GetTypeId(uint32_t id) const;
uint32_t GetTexelComponentCount(const Instruction &insn) const;
uint32_t GetFlattenArraySize(const Instruction &insn) const;
bool HasCapability(spv::Capability find_capability) const {
return std::any_of(static_data_.capability_list.begin(), static_data_.capability_list.end(),
[find_capability](const spv::Capability &capability) { return capability == find_capability; });
}
};
// Represents a VkShaderModule handle
struct SHADER_MODULE_STATE : public BASE_NODE {
SHADER_MODULE_STATE(VkShaderModule shader_module, std::shared_ptr<SPIRV_MODULE_STATE> &spirv_module, uint32_t unique_shader_id)
: BASE_NODE(shader_module, kVulkanObjectTypeShaderModule), spirv(spirv_module), gpu_validation_shader_id(unique_shader_id) {
spirv->handle_ = handle_;
}
// For when we need to create a module with no SPIR-V backing it
SHADER_MODULE_STATE(uint32_t unique_shader_id)
: BASE_NODE(static_cast<VkShaderModule>(VK_NULL_HANDLE), kVulkanObjectTypeShaderModule),
gpu_validation_shader_id(unique_shader_id) {}
// If null, means this is a empty object and no shader backing it
// TODO - This (and SHADER_OBJECT_STATE) could be unique, but need handle multiple ValidationObjects
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/6265/files
std::shared_ptr<SPIRV_MODULE_STATE> spirv;
// Used as way to match instrumented GPU-AV shader to a VkShaderModule handle
uint32_t gpu_validation_shader_id = 0;
};