Skip to content

Commit

Permalink
[vulkan] refactored bindless descriptor updates for maximum simplicit…
Browse files Browse the repository at this point in the history
…y (and in preparation for upcoming validation error fixes)
  • Loading branch information
PanosK92 committed Dec 26, 2024
1 parent e3bfaaf commit bb213ca
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 97 deletions.
17 changes: 9 additions & 8 deletions runtime/RHI/RHI_Definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,12 @@ namespace Spartan
}
}

enum class RHI_Device_Resource
enum class RHI_Device_Bindless_Resource
{
sampler_comparison,
sampler_regular,
textures_material
SamplersComparison,
SamplersRegular,
MaterialTextures,
Max
};

static uint64_t rhi_hash_combine(uint64_t seed, uint64_t x)
Expand Down Expand Up @@ -419,10 +420,10 @@ namespace Spartan

// shader register slot shifts (required to produce spirv from hlsl)
// 000-099 is push constant buffer range
const uint32_t rhi_shader_shift_register_u = 100;
const uint32_t rhi_shader_shift_register_b = 200;
const uint32_t rhi_shader_shift_register_t = 300;
const uint32_t rhi_shader_shift_register_s = 400;
const uint32_t rhi_shader_register_shift_u = 100;
const uint32_t rhi_shader_register_shift_b = 200;
const uint32_t rhi_shader_register_shift_t = 300;
const uint32_t rhi_shader_register_shift_s = 400;
const Color rhi_color_dont_care = Color(std::numeric_limits<float>::max(), 0.0f, 0.0f, 0.0f);
const Color rhi_color_load = Color(std::numeric_limits<float>::infinity(), 0.0f, 0.0f, 0.0f);
const float rhi_depth_dont_care = std::numeric_limits<float>::max();
Expand Down
8 changes: 4 additions & 4 deletions runtime/RHI/RHI_DescriptorSetLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ namespace Spartan
{
for (RHI_Descriptor& descriptor : m_descriptors)
{
if (descriptor.slot == slot + rhi_shader_shift_register_b)
if (descriptor.slot == slot + rhi_shader_register_shift_b)
{
descriptor.data = static_cast<void*>(constant_buffer); // needed for vkUpdateDescriptorSets()
descriptor.range = constant_buffer->GetStride(); // needed for vkUpdateDescriptorSets()
Expand All @@ -70,7 +70,7 @@ namespace Spartan
{
for (RHI_Descriptor& descriptor : m_descriptors)
{
if (descriptor.slot == slot + rhi_shader_shift_register_u)
if (descriptor.slot == slot + rhi_shader_register_shift_u)
{
descriptor.data = static_cast<void*>(buffer);
descriptor.range = buffer->GetStride();
Expand All @@ -85,7 +85,7 @@ namespace Spartan
{
for (RHI_Descriptor& descriptor : m_descriptors)
{
if (descriptor.slot == slot + rhi_shader_shift_register_s)
if (descriptor.slot == slot + rhi_shader_register_shift_s)
{
descriptor.data = static_cast<void*>(sampler);

Expand All @@ -104,7 +104,7 @@ namespace Spartan
for (RHI_Descriptor& descriptor : m_descriptors)
{
bool is_storage = layout == RHI_Image_Layout::General;
uint32_t shift = is_storage ? rhi_shader_shift_register_u : rhi_shader_shift_register_t;
uint32_t shift = is_storage ? rhi_shader_register_shift_u : rhi_shader_register_shift_t;

if (descriptor.slot == (slot + shift))
{
Expand Down
4 changes: 2 additions & 2 deletions runtime/RHI/RHI_Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ namespace Spartan
static void CreateDescriptorPool();
static void AllocateDescriptorSet(void*& resource, RHI_DescriptorSetLayout* descriptor_set_layout, const std::vector<RHI_Descriptor>& descriptors);
static std::unordered_map<uint64_t, RHI_DescriptorSet>& GetDescriptorSets();
static void* GetDescriptorSet(const RHI_Device_Resource resource_type);
static void* GetDescriptorSetLayout(const RHI_Device_Resource resource_type);
static void* GetDescriptorSet(const RHI_Device_Bindless_Resource resource_type);
static void* GetDescriptorSetLayout(const RHI_Device_Bindless_Resource resource_type);
static void UpdateBindlessResources(const std::array<std::shared_ptr<RHI_Sampler>, static_cast<uint32_t>(Renderer_Sampler::Max)>* samplers, std::array<RHI_Texture*, rhi_max_array_size>* textures);

// pipelines
Expand Down
6 changes: 3 additions & 3 deletions runtime/RHI/Vulkan/Vulkan_CommandList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,9 @@ namespace Spartan
{
array<void*, 3> resources =
{
RHI_Device::GetDescriptorSet(RHI_Device_Resource::textures_material),
RHI_Device::GetDescriptorSet(RHI_Device_Resource::sampler_comparison),
RHI_Device::GetDescriptorSet(RHI_Device_Resource::sampler_regular)
RHI_Device::GetDescriptorSet(RHI_Device_Bindless_Resource::MaterialTextures),
RHI_Device::GetDescriptorSet(RHI_Device_Bindless_Resource::SamplersComparison),
RHI_Device::GetDescriptorSet(RHI_Device_Bindless_Resource::SamplersRegular)
};

VkPipelineBindPoint bind_point = pso.IsCompute() ? VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_COMPUTE : VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS;
Expand Down
135 changes: 62 additions & 73 deletions runtime/RHI/Vulkan/Vulkan_Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,10 @@ namespace Spartan

// legit but they spam every frame
{
// buffer update
if (p_callback_data->messageIdNumber == 0x376bc9df)
return VK_FALSE;

// present related, they happen without the renderer doing anything, imgui presenting is enough
if (p_callback_data->messageIdNumber == 0xe17ab4ae || p_callback_data->messageIdNumber == 0x42f2f4ed)
return VK_FALSE;
Expand Down Expand Up @@ -897,14 +901,14 @@ namespace Spartan

namespace bindless
{
array<VkDescriptorSet, 3> sets;
array<VkDescriptorSetLayout, 3> layouts;
array<VkDescriptorSet, static_cast<uint32_t>(RHI_Device_Bindless_Resource::Max)> sets;
array<VkDescriptorSetLayout, static_cast<uint32_t>(RHI_Device_Bindless_Resource::Max)> layouts;

void create_layout(const RHI_Device_Resource resource_type, const uint32_t binding, const uint32_t resource_count, const string& debug_name)
void create_layout(const RHI_Device_Bindless_Resource resource_type, const uint32_t binding, const uint32_t resource_count, const string& debug_name)
{
VkDescriptorSetLayoutBinding layout_binding = {};
layout_binding.binding = binding;
layout_binding.descriptorType = resource_type == RHI_Device_Resource::textures_material ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE : VK_DESCRIPTOR_TYPE_SAMPLER;
layout_binding.descriptorType = resource_type == RHI_Device_Bindless_Resource::MaterialTextures ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE : VK_DESCRIPTOR_TYPE_SAMPLER;
layout_binding.descriptorCount = rhi_max_array_size;
layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
layout_binding.pImmutableSamplers = nullptr;
Expand All @@ -928,7 +932,7 @@ namespace Spartan
RHI_Device::SetResourceName(static_cast<void*>(*layout), RHI_Resource_Type::DescriptorSetLayout, debug_name);
}

void create_set(const RHI_Device_Resource resource_type, const uint32_t resource_count, const string& debug_name)
void create_set(const RHI_Device_Bindless_Resource resource_type, const uint32_t resource_count, const string& debug_name)
{
// allocate descriptor set with actual descriptor count
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT real_descriptor_count_info = {};
Expand All @@ -950,84 +954,69 @@ namespace Spartan
RHI_Device::SetResourceName(static_cast<void*>(*descriptor_set), RHI_Resource_Type::DescriptorSet, debug_name);
}

void update_samplers(const vector<shared_ptr<RHI_Sampler>>& samplers, const uint32_t binding_slot, const RHI_Device_Resource resource_type)
void update(const void* data, const uint32_t count, const uint32_t slot, const RHI_Device_Bindless_Resource type, const string& name)
{
uint32_t sampler_count = static_cast<uint32_t>(samplers.size());
uint32_t binding = rhi_shader_shift_register_s + binding_slot;

// create layout and set (if needed)
if (layouts[static_cast<uint32_t>(resource_type)] == nullptr)
{
string debug_name = resource_type == RHI_Device_Resource::sampler_comparison ? "samplers_comparison" : "samplers_regular";

create_layout(resource_type, binding, sampler_count, debug_name);
create_set(resource_type, sampler_count, debug_name);
// deduce binding from slot (HLSL register style)
uint32_t binding = 0;
if (type == RHI_Device_Bindless_Resource::MaterialTextures)
{
binding = rhi_shader_register_shift_t + slot;
}

// update
else
{
vector<VkDescriptorImageInfo> image_infos(sampler_count);
for (uint32_t i = 0; i < sampler_count; i++)
{
image_infos[i].sampler = static_cast<VkSampler>(samplers[i]->GetRhiResource());
image_infos[i].imageView = nullptr;
image_infos[i].imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
}

VkWriteDescriptorSet descriptor_write = {};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.dstSet = sets[static_cast<uint32_t>(resource_type)];
descriptor_write.dstBinding = binding;
descriptor_write.dstArrayElement = 0; // starting element in that array
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
descriptor_write.descriptorCount = sampler_count;
descriptor_write.pImageInfo = image_infos.data();

vkUpdateDescriptorSets(RHI_Context::device, 1, &descriptor_write, 0, nullptr);
binding = rhi_shader_register_shift_s + slot;
}
}

void update_textures(const array<RHI_Texture*, rhi_max_array_size>* textures, const uint32_t binding_slot)
{
uint32_t texture_count = static_cast<uint32_t>(textures->size());
uint32_t binding = rhi_shader_shift_register_t + binding_slot;

// create layout and set (if needed)
if (layouts[static_cast<uint32_t>(RHI_Device_Resource::textures_material)] == nullptr)
// create layout and and layout set (if needed)
if (layouts[static_cast<uint32_t>(type)] == nullptr)
{
string debug_name = "textures_material";

create_layout(RHI_Device_Resource::textures_material, binding, texture_count, debug_name);
create_set(RHI_Device_Resource::textures_material, texture_count, debug_name);
create_layout(type, binding, count, name);
create_set(type, count, name);
}

// update
{
vector<VkDescriptorImageInfo> image_infos(texture_count);
for (uint32_t i = 0; i < texture_count; ++i)
vector<VkDescriptorImageInfo> image_infos(count);
if (type == RHI_Device_Bindless_Resource::MaterialTextures)
{
RHI_Texture* texture = (*textures)[i];
if (!texture)
continue;

// get texture, if unable to do so, fallback to a checkerboard texture, so we can spot it by eye
void* srv_default = Renderer::GetStandardTexture(Renderer_StandardTexture::Checkerboard)->GetRhiSrv();
void* resource = texture ? texture->GetRhiSrv() : srv_default;
const auto* textures = static_cast<const array<RHI_Texture*, rhi_max_array_size>*>(data);

image_infos[i].sampler = nullptr;
image_infos[i].imageView = static_cast<VkImageView>(resource);
image_infos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
for (uint32_t i = 0; i < count; ++i)
{
RHI_Texture* texture = (*textures)[i];
if (!texture)
continue;

// get texture, fallback to checkerboard texture if null
void* srv_default = Renderer::GetStandardTexture(Renderer_StandardTexture::Checkerboard)->GetRhiSrv();
void* resource = texture ? texture->GetRhiSrv() : srv_default;

image_infos[i].sampler = nullptr;
image_infos[i].imageView = static_cast<VkImageView>(resource);
image_infos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
}
else if (type == RHI_Device_Bindless_Resource::SamplersRegular || type == RHI_Device_Bindless_Resource::SamplersComparison)
{
const auto* samplers = static_cast<const shared_ptr<RHI_Sampler>*>(data);

for (uint32_t i = 0; i < count; ++i)
{
image_infos[i].sampler = static_cast<VkSampler>(samplers[i]->GetRhiResource());
image_infos[i].imageView = nullptr;
image_infos[i].imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
}
}

VkWriteDescriptorSet descriptor_write = {};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.dstSet = sets[static_cast<uint32_t>(RHI_Device_Resource::textures_material)];
descriptor_write.dstSet = sets[static_cast<uint32_t>(type)];
descriptor_write.dstBinding = binding;
descriptor_write.dstArrayElement = 0; // starting element in the array
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
descriptor_write.descriptorCount = texture_count;
descriptor_write.descriptorType = type == RHI_Device_Bindless_Resource::MaterialTextures ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE : VK_DESCRIPTOR_TYPE_SAMPLER;
descriptor_write.descriptorCount = count;
descriptor_write.pImageInfo = image_infos.data();

vkUpdateDescriptorSets(RHI_Context::device, 1, &descriptor_write, 0, nullptr);
}
}
Expand Down Expand Up @@ -1731,12 +1720,12 @@ namespace Spartan
Profiler::m_descriptor_set_count++;
}

void* RHI_Device::GetDescriptorSet(const RHI_Device_Resource resource_type)
void* RHI_Device::GetDescriptorSet(const RHI_Device_Bindless_Resource resource_type)
{
return static_cast<void*>(descriptors::bindless::sets[static_cast<uint32_t>(resource_type)]);
}

void* RHI_Device::GetDescriptorSetLayout(const RHI_Device_Resource resource_type)
void* RHI_Device::GetDescriptorSetLayout(const RHI_Device_Bindless_Resource resource_type)
{
return static_cast<void*>(descriptors::bindless::layouts[static_cast<uint32_t>(resource_type)]);
}
Expand Down Expand Up @@ -1775,10 +1764,10 @@ namespace Spartan
{
vector<shared_ptr<RHI_Sampler>> data =
{
(*samplers)[0],
(*samplers)[0], // comparison
};

descriptors::bindless::update_samplers(data, 0, RHI_Device_Resource::sampler_comparison);
descriptors::bindless::update(&data[0], static_cast<uint32_t>(data.size()), 0, RHI_Device_Bindless_Resource::SamplersComparison, "samplers_comparison");
}

// regular
Expand All @@ -1795,7 +1784,7 @@ namespace Spartan
(*samplers)[8] // anisotropic_wrap
};

descriptors::bindless::update_samplers(data, 1, RHI_Device_Resource::sampler_regular);
descriptors::bindless::update(&data[0], static_cast<uint32_t>(data.size()), 1, RHI_Device_Bindless_Resource::SamplersRegular, "samplers_regular");
}
}

Expand All @@ -1805,20 +1794,20 @@ namespace Spartan

// by the time vkCmdBindDescriptorSets runs, we can't have a null descriptor set, so make sure there is something there
{
bool first_run = descriptors::bindless::sets[static_cast<uint32_t>(RHI_Device_Resource::textures_material)] == nullptr;
bool first_run = descriptors::bindless::sets[static_cast<uint32_t>(RHI_Device_Bindless_Resource::MaterialTextures)] == nullptr;
bool no_textures = textures == nullptr;

if (first_run && no_textures)
{
array<RHI_Texture*, rhi_max_array_size> array_dummy;
array_dummy.fill(nullptr);
descriptors::bindless::update_textures(&array_dummy, binding_slot);
descriptors::bindless::update(&array_dummy[0], static_cast<uint32_t>(array_dummy.size()), binding_slot, RHI_Device_Bindless_Resource::MaterialTextures, "material_textures");
}
}

if (textures)
{
descriptors::bindless::update_textures(textures, binding_slot);
descriptors::bindless::update(&textures[0], rhi_max_array_size, binding_slot, RHI_Device_Bindless_Resource::MaterialTextures, "material_textures");
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions runtime/RHI/Vulkan/Vulkan_Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ namespace Spartan
array<void*, 4> layouts =
{
descriptor_set_layout->GetRhiResource(),
RHI_Device::GetDescriptorSetLayout(RHI_Device_Resource::textures_material),
RHI_Device::GetDescriptorSetLayout(RHI_Device_Resource::sampler_comparison),
RHI_Device::GetDescriptorSetLayout(RHI_Device_Resource::sampler_regular)
RHI_Device::GetDescriptorSetLayout(RHI_Device_Bindless_Resource::MaterialTextures),
RHI_Device::GetDescriptorSetLayout(RHI_Device_Bindless_Resource::SamplersComparison),
RHI_Device::GetDescriptorSetLayout(RHI_Device_Bindless_Resource::SamplersRegular)
};

// validate descriptor set layouts
Expand Down
Loading

0 comments on commit bb213ca

Please sign in to comment.