blob: dfe04796d955a78cdc3b8fb17ba2510c7e2349b4 [file]
/*
* Copyright (c) 2026 The Khronos Group Inc.
* Copyright (c) 2026 Valve Corporation
* Copyright (c) 2026 LunarG, 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.
*/
#include "descriptor_heap_object.h"
#include <vulkan/vulkan_core.h>
#include "containers/container_utils.h"
#include "../framework/layer_validation_tests.h"
#include "../framework/ray_tracing_objects.h"
#include "utils/math_utils.h"
namespace vkt {
static bool IsAddressRange(VkDescriptorType type) {
return IsValueIn(type, {VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV,
VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER});
}
static bool IsTexelBuffer(VkDescriptorType type) {
return IsValueIn(type, {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER});
}
static bool IsImage(VkDescriptorType type) {
return IsValueIn(
type, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM,
VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT});
}
void DescriptorHeap::AddDescriptorHeapRequirements(VkLayerTest& test) {
test.AddRequiredExtensions(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
test.AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
test.AddRequiredFeature(vkt::Feature::descriptorHeap);
}
void DescriptorHeap::AddUntypedDescriptorHeapRequirements(VkLayerTest &test) {
test.AddRequiredExtensions(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
test.AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
test.AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
test.AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
test.AddRequiredFeature(vkt::Feature::descriptorHeap);
}
DescriptorHeap::DescriptorHeap(VkLayerTest& test) : test_(&test) {
heap_props.pNext = &tensor_heap_props;
test_->GetPhysicalDeviceProperties2(heap_props);
}
void DescriptorHeap::CreateResourceHeap(VkDeviceSize app_size, bool reserved_range_in_front) {
resource_reserved_range_in_front_ = reserved_range_in_front;
const VkDeviceSize heap_size = AlignResource(app_size + heap_props.minResourceHeapReservedRange);
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_2_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper();
allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
resource_heap_.Init(*test_->DeviceObj(), vkt::Buffer::CreateInfo(heap_size, 0, {}, &buffer_usage),
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info);
resource_heap_data_ = static_cast<uint8_t*>(resource_heap_.Memory().Map());
if (reserved_range_in_front) {
heap_offset_ = AlignResource(heap_props.minResourceHeapReservedRange);
}
}
void DescriptorHeap::CreateSamplerHeap(VkDeviceSize app_size, bool reserved_range_in_front, bool use_embedded_samplers) {
embedded_samplers = use_embedded_samplers;
sampler_reserved_range_in_front_ = reserved_range_in_front;
const VkDeviceSize reserved_range =
(embedded_samplers ? heap_props.minSamplerHeapReservedRangeWithEmbedded : heap_props.minSamplerHeapReservedRange);
const VkDeviceSize heap_size = AlignSampler(app_size + reserved_range);
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_2_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper();
allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
sampler_heap_.Init(*test_->DeviceObj(), vkt::Buffer::CreateInfo(heap_size, 0, {}, &buffer_usage),
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info);
sampler_heap_data_ = static_cast<uint8_t*>(sampler_heap_.Memory().Map());
if (reserved_range_in_front) {
heap_offset_ = AlignSampler(reserved_range);
}
}
VkDeviceSize DescriptorHeap::WriteBufferDescriptor(const vkt::Buffer& buffer, VkDescriptorType desc_type) {
const VkDeviceAddressRangeKHR addr_range = buffer.AddressRange();
return WriteBufferDescriptor(addr_range, desc_type);
}
VkDeviceSize DescriptorHeap::WriteBufferDescriptor(VkDeviceAddressRangeKHR addr_range, VkDescriptorType desc_type) {
heap_offset_ = Align(heap_offset_, heap_props.bufferDescriptorAlignment);
const VkDeviceSize write_offset = WriteBufferDescriptorAtOffset(addr_range, desc_type, heap_offset_);
heap_offset_ += heap_props.bufferDescriptorSize;
assert(heap_offset_ <= resource_heap_.CreateInfo().size);
return write_offset;
}
VkDeviceSize DescriptorHeap::WriteBufferDescriptorAtOffset(VkDeviceAddressRangeKHR addr_range, VkDescriptorType desc_type,
VkDeviceSize heap_offset) {
assert(IsValueIn(desc_type, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER}));
return WriteDescriptorAtOffset(addr_range, desc_type, heap_offset);
}
VkDeviceSize DescriptorHeap::WriteImageDescriptorAtOffset(const vkt::Image& image, VkDeviceSize heap_offset,
VkDescriptorType desc_type, VkImageLayout layout) {
VkImageViewCreateInfo view_info = image.BasicViewCreatInfo();
VkImageDescriptorInfoEXT image_info = vku::InitStructHelper();
image_info.pView = &view_info;
image_info.layout = layout;
return WriteDescriptorAtOffset(&image_info, desc_type, heap_offset);
}
VkDeviceSize DescriptorHeap::WriteImageDescriptor(const vkt::Image& image, VkDescriptorType desc_type, VkImageLayout layout) {
heap_offset_ = Align(heap_offset_, heap_props.imageDescriptorAlignment);
VkImageViewCreateInfo view_info = image.BasicViewCreatInfo();
VkImageDescriptorInfoEXT image_info = vku::InitStructHelper();
image_info.pView = &view_info;
image_info.layout = layout;
const VkDeviceSize write_offset = WriteDescriptorAtOffset(&image_info, desc_type, heap_offset_);
heap_offset_ += heap_props.imageDescriptorSize;
assert(heap_offset_ <= resource_heap_.CreateInfo().size);
return write_offset;
}
VkDeviceSize DescriptorHeap::WriteAccelerationStructureDescriptor(vkt::as::AccelerationStructureKHR& as) {
const VkDeviceAddress as_addr = as.GetAccelerationStructureDeviceAddress();
return WriteAccelerationStructureDescriptor(as_addr);
}
VkDeviceSize DescriptorHeap::WriteAccelerationStructureDescriptor(VkDeviceAddress as_addr) {
constexpr VkDescriptorType as_desc_type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
heap_offset_ = Align(heap_offset_, heap_props.bufferDescriptorAlignment);
// Per the spec, size can be 0
const VkDeviceAddressRangeEXT as_addr_range = {as_addr, 0};
const VkDeviceSize write_offset = WriteDescriptorAtOffset(as_addr_range, as_desc_type, heap_offset_);
heap_offset_ += heap_props.bufferDescriptorSize;
assert(heap_offset_ <= resource_heap_.CreateInfo().size);
return write_offset;
}
VkDeviceSize DescriptorHeap::WriteSamplerDescriptor(VkSamplerCreateInfo* sampler_create_info) {
VkSamplerCreateInfo safe_create_info = SafeSaneSamplerCreateInfo();
sampler_heap_offset_ = Align(sampler_heap_offset_, heap_props.samplerHeapAlignment);
const VkDeviceSize write_offset = sampler_heap_offset_;
VkHostAddressRangeEXT sampler_host_data{};
sampler_host_data.address = sampler_heap_data_ + write_offset;
sampler_host_data.size = static_cast<size_t>(heap_props.samplerDescriptorSize);
vk::WriteSamplerDescriptorsEXT(*test_->DeviceObj(), 1u, sampler_create_info ? sampler_create_info : &safe_create_info,
&sampler_host_data);
sampler_heap_offset_ += heap_props.samplerDescriptorSize;
assert(sampler_heap_offset_ <= sampler_heap_.CreateInfo().size);
return write_offset;
}
VkDeviceSize DescriptorHeap::WriteNullDescriptorAtOffset(VkDescriptorType desc_type, VkDeviceSize heap_offset) {
assert(resource_heap_.handle() != VK_NULL_HANDLE);
VkResourceDescriptorInfoEXT desc_info = vku::InitStructHelper();
desc_info.type = desc_type;
if (IsAddressRange(desc_type)) {
desc_info.data.pAddressRange = nullptr;
} else if (IsTexelBuffer(desc_type)) {
desc_info.data.pTexelBuffer = nullptr;
} else if (IsImage(desc_type)) {
desc_info.data.pImage = nullptr;
} else {
desc_info.data.pTensorARM = nullptr;
}
VkHostAddressRangeEXT desc_host_data{};
desc_host_data.address = resource_heap_data_ + heap_offset;
desc_host_data.size = vk::GetPhysicalDeviceDescriptorSizeEXT(test_->DeviceObj()->Physical(), desc_type);
vk::WriteResourceDescriptorsEXT(*test_->DeviceObj(), 1, &desc_info, &desc_host_data);
return heap_offset;
}
VkDeviceSize DescriptorHeap::AlignResource(VkDeviceSize offset) {
VkDeviceSize aligned_offset = Align(offset, heap_props.bufferDescriptorAlignment);
aligned_offset = Align(aligned_offset, heap_props.imageDescriptorAlignment);
return aligned_offset;
}
VkDeviceSize DescriptorHeap::AlignSampler(VkDeviceSize offset) { return Align(offset, heap_props.samplerDescriptorAlignment); }
VkDeviceSize DescriptorHeap::GetResourceHeapReservedRangeOffset() const {
if (resource_reserved_range_in_front_) {
return 0;
} else {
return resource_heap_.CreateInfo().size - heap_props.minResourceHeapReservedRange;
}
}
VkDeviceSize DescriptorHeap::GetSamplerHeapReservedRangeOffset() const {
const VkDeviceSize min_reserved_range =
embedded_samplers ? heap_props.minSamplerHeapReservedRangeWithEmbedded : heap_props.minSamplerHeapReservedRange;
if (resource_reserved_range_in_front_) {
return 0;
} else {
return sampler_heap_.CreateInfo().size - min_reserved_range;
}
}
void DescriptorHeap::BindResourceHeap(vkt::CommandBuffer& cmd_buffer) {
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange = resource_heap_.AddressRange();
bind_resource_info.reservedRangeOffset = GetResourceHeapReservedRangeOffset();
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(cmd_buffer, &bind_resource_info);
}
void DescriptorHeap::BindSamplerHeap(vkt::CommandBuffer &cmd_buffer) {
const VkDeviceSize min_reserved_range =
embedded_samplers ? heap_props.minSamplerHeapReservedRangeWithEmbedded : heap_props.minSamplerHeapReservedRange;
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange = sampler_heap_.AddressRange();
bind_resource_info.reservedRangeOffset = GetSamplerHeapReservedRangeOffset();
bind_resource_info.reservedRangeSize = min_reserved_range;
vk::CmdBindSamplerHeapEXT(cmd_buffer, &bind_resource_info);
}
// Buffer variation
VkDeviceSize DescriptorHeap::WriteDescriptorAtOffset(VkDeviceAddressRangeKHR addr_range, VkDescriptorType desc_type,
VkDeviceSize heap_offset) {
assert(resource_heap_.handle() != VK_NULL_HANDLE);
VkResourceDescriptorInfoEXT desc_info = vku::InitStructHelper();
desc_info.type = desc_type;
desc_info.data.pAddressRange = &addr_range;
const VkDeviceSize desc_size = heap_props.bufferDescriptorSize;
VkHostAddressRangeEXT desc_host_data{};
desc_host_data.address = resource_heap_data_ + heap_offset;
desc_host_data.size = desc_size;
vk::WriteResourceDescriptorsEXT(*test_->DeviceObj(), 1, &desc_info, &desc_host_data);
return heap_offset;
}
// Image variation
VkDeviceSize DescriptorHeap::WriteDescriptorAtOffset(const VkImageDescriptorInfoEXT* image_info, VkDescriptorType desc_type,
VkDeviceSize heap_offset) {
assert(resource_heap_.handle() != VK_NULL_HANDLE);
VkResourceDescriptorInfoEXT desc_info = vku::InitStructHelper();
desc_info.type = desc_type;
desc_info.data.pImage = image_info;
const VkDeviceSize desc_size = heap_props.imageDescriptorSize;
VkHostAddressRangeEXT desc_host_data{};
desc_host_data.address = resource_heap_data_ + heap_offset;
desc_host_data.size = desc_size;
vk::WriteResourceDescriptorsEXT(*test_->DeviceObj(), 1, &desc_info, &desc_host_data);
return heap_offset;
}
} // namespace vkt