blob: 5db6f57aff144e23298a2e715cac6d2438b2f7cd [file]
/* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Copyright (C) 2015-2026 Google Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* 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.
*/
#include "utils/descriptor_utils.h"
#include <vulkan/vulkan_core.h>
#include "containers/container_utils.h"
#include "generated/dispatch_functions.h"
#include "generated/spirv_grammar_helper.h"
#include "generated/vk_extension_helper.h"
#include "state_tracker/shader_module.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
uint32_t CountDescriptorHeapEmbeddedSamplers(const void* pNext) {
const VkShaderDescriptorSetAndBindingMappingInfoEXT* mapping_info =
vku::FindStructInPNextChain<VkShaderDescriptorSetAndBindingMappingInfoEXT>(pNext);
uint32_t count = 0;
if (mapping_info) {
for (uint32_t i = 0; i < mapping_info->mappingCount; ++i) {
const VkDescriptorSetAndBindingMappingEXT& mapping = mapping_info->pMappings[i];
if (GetEmbeddedSampler(mapping) != nullptr) {
count++;
}
}
}
return count;
}
size_t GetDescriptorBufferSize(const VkPhysicalDeviceDescriptorBufferPropertiesEXT& props, bool robust, VkDescriptorType type) {
switch (type) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
return props.samplerDescriptorSize;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
return props.combinedImageSamplerDescriptorSize;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
return props.sampledImageDescriptorSize;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
return props.storageImageDescriptorSize;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
return robust ? props.robustUniformTexelBufferDescriptorSize : props.uniformTexelBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
return robust ? props.robustStorageTexelBufferDescriptorSize : props.storageTexelBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
return robust ? props.robustUniformBufferDescriptorSize : props.uniformBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
return robust ? props.robustStorageBufferDescriptorSize : props.storageBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
return props.inputAttachmentDescriptorSize;
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
return props.accelerationStructureDescriptorSize;
default:
break;
}
return 0;
}
const char* DescribeDescriptorBufferSize(bool robust, VkDescriptorType type) {
switch (type) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
return "samplerDescriptorSize";
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
return "combinedImageSamplerDescriptorSize";
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
return "sampledImageDescriptorSize";
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
return "storageImageDescriptorSize";
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
return robust ? "robustUniformTexelBufferDescriptorSize" : "uniformTexelBufferDescriptorSize";
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
return robust ? "robustStorageTexelBufferDescriptorSize" : "storageTexelBufferDescriptorSize";
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
return robust ? "robustUniformBufferDescriptorSize" : "uniformBufferDescriptorSize";
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
return robust ? "robustStorageBufferDescriptorSize" : "storageBufferDescriptorSize";
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
return "inputAttachmentDescriptorSize";
case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
return "accelerationStructureDescriptorSize";
default:
break;
}
return "[Unknown]";
}
bool IsResourceVaribleInMapping(const VkDescriptorSetAndBindingMappingEXT& mapping,
const spirv::ResourceInterfaceVariable& resource_variable) {
const uint32_t descriptor_set = resource_variable.decorations.set;
const uint32_t descriptor_binding = resource_variable.decorations.binding;
const uint32_t last_binding = mapping.firstBinding + mapping.bindingCount;
return (mapping.descriptorSet == descriptor_set && descriptor_binding >= mapping.firstBinding &&
descriptor_binding < last_binding && ResourceTypeMatchesBinding(mapping.resourceMask, resource_variable));
}
bool ResourceTypeMatchesBinding(VkSpirvResourceTypeFlagsEXT resource_type,
const spirv::ResourceInterfaceVariable& resource_variable) {
if (resource_type == VK_SPIRV_RESOURCE_TYPE_ALL_EXT) {
return true;
}
const uint32_t opcode = resource_variable.base_type.Opcode();
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_SAMPLER_BIT_EXT) != 0 && opcode == spv::OpTypeSampler) {
return true;
}
if (opcode == spv::OpTypeImage) {
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_SAMPLED_IMAGE_BIT_EXT) != 0 && resource_variable.base_type.Word(7) == 1) {
return true;
}
// NonWritable must be on the OpVariable, not the OpTypeStruct
// https://gitlab.khronos.org/vulkan/vulkan/-/issues/4789
const bool nonwritable = resource_variable.decorations.Has(spirv::DecorationSet::nonwritable_bit);
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_ONLY_IMAGE_BIT_EXT) != 0 && resource_variable.base_type.Word(7) == 2 &&
nonwritable) {
return true;
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_WRITE_IMAGE_BIT_EXT) != 0 && resource_variable.base_type.Word(7) == 2 &&
!nonwritable) {
return true;
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_COMBINED_SAMPLED_IMAGE_BIT_EXT) != 0 &&
resource_variable.is_combined_image_sampler) {
return true;
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_UNIFORM_BUFFER_BIT_EXT) != 0 && resource_variable.is_uniform_buffer) {
return true;
}
if (resource_variable.is_storage_buffer) {
// NonWritable must be on the OpVariable, not the OpTypeStruct
// https://gitlab.khronos.org/vulkan/vulkan/-/issues/4789
const bool nonwritable = resource_variable.decorations.Has(spirv::DecorationSet::nonwritable_bit);
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_ONLY_STORAGE_BUFFER_BIT_EXT) != 0 && nonwritable) {
return true;
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_WRITE_STORAGE_BUFFER_BIT_EXT) != 0 && !nonwritable) {
return true;
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_ACCELERATION_STRUCTURE_BIT_EXT) != 0 &&
opcode == spv::OpTypeAccelerationStructureKHR) {
return true;
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_TENSOR_BIT_ARM) != 0 && opcode == spv::OpTypeTensorARM) {
return true;
}
return false;
}
// Only currently want to report the first mismatch
std::string DescribeResourceTypeMismatch(VkSpirvResourceTypeFlagsEXT resource_type,
const spirv::ResourceInterfaceVariable& resource_variable) {
const uint32_t opcode = resource_variable.base_type.Opcode();
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_SAMPLER_BIT_EXT) != 0 && opcode != spv::OpTypeSampler) {
return "base type is " + std::string(string_SpvOpcode(opcode)) + ", not OpTypeSampler";
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_SAMPLED_IMAGE_BIT_EXT) != 0) {
if (opcode != spv::OpTypeImage) {
return "base type is " + std::string(string_SpvOpcode(opcode)) + ", not OpTypeImage";
} else if (resource_variable.base_type.Word(7) != 1) {
return "OpTypeImage Sampled is " + std::to_string(resource_variable.base_type.Word(7)) + ", not 1";
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_ONLY_IMAGE_BIT_EXT) != 0) {
if (opcode != spv::OpTypeImage) {
return "base type is " + std::string(string_SpvOpcode(opcode)) + ", not OpTypeImage";
} else if (resource_variable.base_type.Word(7) != 2) {
return "OpTypeImage Sampled is " + std::to_string(resource_variable.base_type.Word(7)) + ", not 2";
} else if (!resource_variable.decorations.Has(spirv::DecorationSet::nonwritable_bit)) {
if (resource_variable.HasInMember(spirv::DecorationSet::nonwritable_bit)) {
return "is not decorated with NonWritable on the OpVariable, but is in the OpTypeStruct. This likely is hitting a "
"known DXC code generation bug (DirectXShaderCompiler/issues/8492). Simple work around is to just use "
"VK_SPIRV_RESOURCE_TYPE_ALL_EXT here as that will give the desired behavior.";
} else {
return "is not decorated with NonWritable";
}
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_WRITE_IMAGE_BIT_EXT) != 0) {
if (opcode != spv::OpTypeImage) {
return "base type is " + std::string(string_SpvOpcode(opcode)) + ", not OpTypeImage";
} else if (resource_variable.base_type.Word(7) != 2) {
return "OpTypeImage Sampled is " + std::to_string(resource_variable.base_type.Word(7)) + ", not 2";
} else if (resource_variable.decorations.Has(spirv::DecorationSet::nonwritable_bit)) {
return "is decorated with NonWritable";
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_COMBINED_SAMPLED_IMAGE_BIT_EXT) != 0 &&
!resource_variable.is_combined_image_sampler) {
return "is not OpTypeSampledImage";
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_UNIFORM_BUFFER_BIT_EXT) != 0 && !resource_variable.is_uniform_buffer) {
if (resource_variable.storage_class != spv::StorageClassUniform) {
return "is not Uniform StorageClass";
} else if (!resource_variable.type_struct_info) {
return "is not a OpTypeStruct";
} else if (!resource_variable.type_struct_info->decorations.Has(spirv::DecorationSet::block_bit)) {
return "is not decorated with Block";
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_ONLY_STORAGE_BUFFER_BIT_EXT) != 0) {
if (!resource_variable.decorations.Has(spirv::DecorationSet::nonwritable_bit)) {
if (resource_variable.HasInMember(spirv::DecorationSet::nonwritable_bit)) {
return "is not decorated with NonWritable on the OpVariable, but is in the OpTypeStruct. This likely is hitting a "
"known DXC code generation bug (DirectXShaderCompiler/issues/8492). Simple work around is to just use "
"VK_SPIRV_RESOURCE_TYPE_ALL_EXT here as that will give the desired behavior.";
} else {
return "is not decorated with NonWritable";
}
} else if (!resource_variable.type_struct_info) {
return "is not a OpTypeStruct";
} else if (!resource_variable.is_storage_buffer) {
return "is not a storage buffer"; // simplified
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_READ_WRITE_STORAGE_BUFFER_BIT_EXT) != 0) {
if (!resource_variable.decorations.Has(spirv::DecorationSet::nonwritable_bit)) {
return "is decorated with NonWritable";
} else if (!resource_variable.type_struct_info) {
return "is not a OpTypeStruct";
} else if (!resource_variable.is_storage_buffer) {
return "is not a storage buffer"; // simplified
}
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_ACCELERATION_STRUCTURE_BIT_EXT) != 0 &&
opcode == spv::OpTypeAccelerationStructureKHR) {
return "base type is " + std::string(string_SpvOpcode(opcode)) + ", not OpTypeAccelerationStructureKHR";
}
if ((resource_type & VK_SPIRV_RESOURCE_TYPE_TENSOR_BIT_ARM) != 0 && opcode == spv::OpTypeTensorARM) {
return "base type is " + std::string(string_SpvOpcode(opcode)) + ", not OpTypeTensorARM";
}
return "[UNKNOWN]";
}
void CachedDescriptorSize::Init(VkPhysicalDevice gpu, const DeviceExtensions& extensions) {
size_[0] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_SAMPLER);
size_[2] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
size_[3] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
size_[4] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
size_[5] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
size_[6] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
size_[7] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
size_[10] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
if (IsExtEnabled(extensions.vk_khr_acceleration_structure)) {
size_[1] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
}
if (IsExtEnabled(extensions.vk_nv_ray_tracing)) {
size_[8] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV);
}
if (IsExtEnabled(extensions.vk_nv_partitioned_acceleration_structure)) {
size_[9] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV);
}
if (IsExtEnabled(extensions.vk_arm_tensors)) {
size_[11] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_TENSOR_ARM);
}
if (IsExtEnabled(extensions.vk_qcom_image_processing)) {
size_[12] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM);
size_[13] = DispatchGetPhysicalDeviceDescriptorSizeEXT(gpu, VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM);
}
}
VkDeviceSize CachedDescriptorSize::GetSize(VkDescriptorType type) const {
if (type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) {
// takes VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER value at index 1
return size_[1];
} else if (type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV) {
// takes VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC value at index 8
return size_[8];
} else if (type == VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV) {
// takes VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC value at index 9
return size_[9];
} else if (type == VK_DESCRIPTOR_TYPE_TENSOR_ARM) {
return size_[11];
} else if (type == VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM) {
return size_[12];
} else if (type == VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM) {
return size_[13];
}
assert(IsValueIn(
type, {VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT}));
uint32_t index = (uint32_t)type;
return size_[index];
}