diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index 0461beb77..7ac00627d 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -758,6 +758,9 @@ namespace vulkan_internal VkPushConstantRange pushconstants = {}; + VkDeviceSize uniform_buffer_sizes[DESCRIPTORBINDER_CBV_COUNT] = {}; + wi::vector 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 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) { diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.h b/WickedEngine/wiGraphicsDevice_Vulkan.h index adc0ee429..2d311593c 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.h +++ b/WickedEngine/wiGraphicsDevice_Vulkan.h @@ -180,10 +180,16 @@ namespace wi::graphics wi::vector texelBufferViews; wi::vector 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, }; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 0d0b68dd3..5a10be076 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -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);