vulkan: added support for dynamic uniform buffers

This commit is contained in:
Turánszki János
2021-12-22 13:31:27 +01:00
parent 6cc5388e68
commit 9985b76c9d
3 changed files with 172 additions and 51 deletions
+165 -50
View File
@@ -758,6 +758,9 @@ namespace vulkan_internal
VkPushConstantRange pushconstants = {};
VkDeviceSize uniform_buffer_sizes[DESCRIPTORBINDER_CBV_COUNT] = {};
wi::vector<uint32_t> uniform_buffer_dynamic_slots;
size_t binding_hash = 0;
~Shader_Vulkan()
@@ -785,6 +788,9 @@ namespace vulkan_internal
VkPushConstantRange pushconstants = {};
VkDeviceSize uniform_buffer_sizes[DESCRIPTORBINDER_CBV_COUNT] = {};
wi::vector<uint32_t> uniform_buffer_dynamic_slots;
size_t binding_hash = 0;
VkGraphicsPipelineCreateInfo pipelineInfo = {};
@@ -1474,45 +1480,49 @@ using namespace vulkan_internal;
VkResult res;
// Create descriptor pool:
VkDescriptorPoolSize poolSizes[9] = {};
VkDescriptorPoolSize poolSizes[10] = {};
uint32_t count = 0;
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = DESCRIPTORBINDER_CBV_COUNT * poolSize;
count++;
poolSizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
poolSizes[1].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[1].descriptorCount = DESCRIPTORBINDER_CBV_COUNT * poolSize;
count++;
poolSizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
poolSizes[2].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
poolSizes[2].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
count++;
poolSizes[3].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
poolSizes[3].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
poolSizes[3].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
count++;
poolSizes[4].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
poolSizes[4].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
poolSizes[4].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
poolSizes[4].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
count++;
poolSizes[5].type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
poolSizes[5].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
poolSizes[5].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
count++;
poolSizes[6].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
poolSizes[6].type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
poolSizes[6].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
count++;
poolSizes[7].type = VK_DESCRIPTOR_TYPE_SAMPLER;
poolSizes[7].descriptorCount = DESCRIPTORBINDER_SAMPLER_COUNT * poolSize;
poolSizes[7].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
poolSizes[7].descriptorCount = DESCRIPTORBINDER_UAV_COUNT * poolSize;
count++;
poolSizes[8].type = VK_DESCRIPTOR_TYPE_SAMPLER;
poolSizes[8].descriptorCount = DESCRIPTORBINDER_SAMPLER_COUNT * poolSize;
count++;
if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
{
poolSizes[8].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
poolSizes[8].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
poolSizes[9].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
poolSizes[9].descriptorCount = DESCRIPTORBINDER_SRV_COUNT * poolSize;
count++;
}
@@ -1521,7 +1531,6 @@ using namespace vulkan_internal;
poolInfo.poolSizeCount = count;
poolInfo.pPoolSizes = poolSizes;
poolInfo.maxSets = poolSize;
//poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
res = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool);
assert(res == VK_SUCCESS);
@@ -1574,21 +1583,36 @@ using namespace vulkan_internal;
auto cs_internal = graphics ? nullptr : to_internal(device->active_cs[cmd]);
VkCommandBuffer commandBuffer = device->GetCommandList(cmd);
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
uint32_t uniform_buffer_dynamic_count = 0;
if (graphics)
{
pipelineLayout = pso_internal->pipelineLayout;
descriptorSetLayout = pso_internal->descriptorSetLayout;
descriptorSet = descriptorSet_graphics;
uniform_buffer_dynamic_count = (uint32_t)pso_internal->uniform_buffer_dynamic_slots.size();
for (size_t i = 0; i < pso_internal->uniform_buffer_dynamic_slots.size(); ++i)
{
uniform_buffer_dynamic_offsets[i] = (uint32_t)table.CBV_offset[pso_internal->uniform_buffer_dynamic_slots[i]];
}
}
else
{
pipelineLayout = cs_internal->pipelineLayout_cs;
descriptorSetLayout = cs_internal->descriptorSetLayout;
descriptorSet = descriptorSet_compute;
uniform_buffer_dynamic_count = (uint32_t)cs_internal->uniform_buffer_dynamic_slots.size();
for (size_t i = 0; i < cs_internal->uniform_buffer_dynamic_slots.size(); ++i)
{
uniform_buffer_dynamic_offsets[i] = (uint32_t)table.CBV_offset[cs_internal->uniform_buffer_dynamic_slots[i]];
}
}
if (dirty & DIRTY_DESCRIPTOR)
{
auto& binder_pool = device->GetFrameResources().binder_pools[cmd];
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
if (graphics)
{
pipelineLayout = pso_internal->pipelineLayout;
descriptorSetLayout = pso_internal->descriptorSetLayout;
}
else
{
pipelineLayout = cs_internal->pipelineLayout_cs;
descriptorSetLayout = cs_internal->descriptorSetLayout;
}
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
@@ -1596,7 +1620,6 @@ using namespace vulkan_internal;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &descriptorSetLayout;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
VkResult res = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet);
while (res == VK_ERROR_OUT_OF_POOL_MEMORY)
{
@@ -1798,7 +1821,52 @@ using namespace vulkan_internal;
auto internal_state = to_internal(&buffer);
bufferInfos.back().buffer = internal_state->resource;
bufferInfos.back().offset = offset;
bufferInfos.back().range = std::min(buffer.desc.size - offset, (uint64_t)device->properties2.properties.limits.maxUniformBufferRange);
if (graphics)
{
bufferInfos.back().range = pso_internal->uniform_buffer_sizes[original_binding];
}
else
{
bufferInfos.back().range = cs_internal->uniform_buffer_sizes[original_binding];
}
if (bufferInfos.back().range == 0ull)
{
bufferInfos.back().range = VK_WHOLE_SIZE;
}
}
}
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
{
bufferInfos.emplace_back();
write.pBufferInfo = &bufferInfos.back();
bufferInfos.back() = {};
const uint32_t original_binding = unrolled_binding - VULKAN_BINDING_SHIFT_B;
const GPUBuffer& buffer = table.CBV[original_binding];
if (!buffer.IsValid())
{
bufferInfos.back().buffer = device->nullBuffer;
bufferInfos.back().range = VK_WHOLE_SIZE;
}
else
{
auto internal_state = to_internal(&buffer);
bufferInfos.back().buffer = internal_state->resource;
if (graphics)
{
bufferInfos.back().range = pso_internal->uniform_buffer_sizes[original_binding];
}
else
{
bufferInfos.back().range = cs_internal->uniform_buffer_sizes[original_binding];
}
if (bufferInfos.back().range == 0ull)
{
bufferInfos.back().range = VK_WHOLE_SIZE;
}
}
}
break;
@@ -1937,28 +2005,38 @@ using namespace vulkan_internal;
0,
nullptr
);
}
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
if (!graphics)
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
if (!graphics)
{
bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
if (device->active_cs[cmd]->stage == ShaderStage::LIB)
{
bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
if (device->active_cs[cmd]->stage == ShaderStage::LIB)
{
bindPoint = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR;
}
bindPoint = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR;
}
}
vkCmdBindDescriptorSets(
commandBuffer,
bindPoint,
pipelineLayout,
0,
1,
&descriptorSet,
uniform_buffer_dynamic_count,
uniform_buffer_dynamic_offsets
);
vkCmdBindDescriptorSets(
commandBuffer,
bindPoint,
pipelineLayout,
0,
1,
&descriptorSet,
0,
nullptr
);
// Save last used descriptor set handles:
// This is needed to handle the case when descriptorSet is not allocated, but only dynamic offsets are updated
if (graphics)
{
descriptorSet_graphics = descriptorSet;
}
else
{
descriptorSet_compute = descriptorSet;
}
dirty = DIRTY_NONE;
@@ -3976,6 +4054,19 @@ using namespace vulkan_internal;
continue;
}
if (descriptor.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
{
// For now, always replace VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER with VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
// It would be quite messy to track which buffer is dynamic and which is not in the binding code, consider multiple pipeline bind points too
// But maybe the dynamic uniform buffer is not always best because it occupies more registers (like DX12 root descriptor)?
descriptor.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
for (uint32_t i = 0; i < descriptor.descriptorCount; ++i)
{
internal_state->uniform_buffer_sizes[descriptor.binding + i] = x->block.size;
internal_state->uniform_buffer_dynamic_slots.push_back(descriptor.binding + i);
}
}
switch (x->descriptor_type)
{
default:
@@ -4142,6 +4233,9 @@ using namespace vulkan_internal;
if (stage == ShaderStage::CS)
{
// sort because dynamic offsets array is tightly packed to match slot numbers:
std::sort(internal_state->uniform_buffer_dynamic_slots.begin(), internal_state->uniform_buffer_dynamic_slots.end());
VkComputePipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipelineInfo.layout = internal_state->pipelineLayout_cs;
@@ -4451,8 +4545,6 @@ using namespace vulkan_internal;
VkResult res = VK_SUCCESS;
{
// Descriptor set layout comes from reflection data when there is no root signature specified:
auto insert_shader = [&](const Shader* shader) {
if (shader == nullptr)
return;
@@ -4485,6 +4577,20 @@ using namespace vulkan_internal;
{
internal_state->layoutBindings.push_back(x);
internal_state->imageViewTypes.push_back(shader_internal->imageViewTypes[i]);
if (x.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || x.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
{
for (uint32_t k = 0; k < x.descriptorCount; ++k)
{
internal_state->uniform_buffer_sizes[x.binding + k] = shader_internal->uniform_buffer_sizes[x.binding + k];
}
}
if (x.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
{
for (uint32_t k = 0; k < x.descriptorCount; ++k)
{
internal_state->uniform_buffer_dynamic_slots.push_back(x.binding + k);
}
}
}
i++;
}
@@ -4505,6 +4611,9 @@ using namespace vulkan_internal;
insert_shader(pDesc->gs);
insert_shader(pDesc->ps);
// sort because dynamic offsets array is tightly packed to match slot numbers:
std::sort(internal_state->uniform_buffer_dynamic_slots.begin(), internal_state->uniform_buffer_dynamic_slots.end());
auto insert_shader_bindless = [&](const Shader* shader) {
if (shader == nullptr)
return;
@@ -6494,12 +6603,18 @@ using namespace vulkan_internal;
{
assert(slot < DESCRIPTORBINDER_CBV_COUNT);
auto& binder = binders[cmd];
if (binder.table.CBV[slot].internal_state != buffer->internal_state || binder.table.CBV_offset[slot] != offset)
if (binder.table.CBV[slot].internal_state != buffer->internal_state)
{
binder.table.CBV[slot] = *buffer;
binder.table.CBV_offset[slot] = offset;
binders[cmd].dirty |= DescriptorBinder::DIRTY_DESCRIPTOR;
}
if (binder.table.CBV_offset[slot] != offset)
{
binder.table.CBV_offset[slot] = offset;
binders[cmd].dirty |= DescriptorBinder::DIRTY_OFFSET;
}
}
void GraphicsDevice_Vulkan::BindVertexBuffers(const GPUBuffer *const* vertexBuffers, uint32_t slot, uint32_t count, const uint32_t* strides, const uint64_t* offsets, CommandList cmd)
{
+6
View File
@@ -180,10 +180,16 @@ namespace wi::graphics
wi::vector<VkBufferView> texelBufferViews;
wi::vector<VkWriteDescriptorSetAccelerationStructureKHR> accelerationStructureViews;
uint32_t uniform_buffer_dynamic_offsets[DESCRIPTORBINDER_CBV_COUNT] = {};
VkDescriptorSet descriptorSet_graphics = VK_NULL_HANDLE;
VkDescriptorSet descriptorSet_compute = VK_NULL_HANDLE;
enum DIRTY_FLAGS
{
DIRTY_NONE = 0,
DIRTY_DESCRIPTOR = 1 << 1,
DIRTY_OFFSET = 1 << 2,
DIRTY_ALL = ~0,
};
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 60;
// minor bug fixes, alterations, refactors, updates
const int revision = 5;
const int revision = 6;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);