From 66d361bd2912fca07142e0cf13d10c48f8f48029 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 6 Jun 2025 10:20:36 +0200 Subject: [PATCH] Vulkan: Add support for descriptor buffers Descriptor sets/pools are known to be troublesome as it doesn't match how GPUs work, or how application want to work, adding more complexity than needed. This results is quite an overhead allocating and deallocating descriptor sets. This PR will use descriptor buffers when they are available. Most platforms support descriptor buffers. When not available descriptor pools/sets will be used. Although this is a feature I would like to land it in 4.5 due to the API changes. This makes it easier to fix issues when 4.5 is released. The feature can easily be disabled by setting the feature to false if it has to many problems. Pull Request: https://projects.blender.org/blender/blender/pulls/138266 --- intern/ghost/intern/GHOST_ContextVK.cc | 15 + .../render_graph/nodes/vk_pipeline_data.cc | 34 ++ .../render_graph/nodes/vk_pipeline_data.hh | 6 + .../tests/vk_render_graph_test_types.hh | 15 + .../render_graph/vk_command_buffer_wrapper.cc | 24 ++ .../render_graph/vk_command_buffer_wrapper.hh | 20 + .../render_graph/vk_render_graph_node.hh | 2 +- source/blender/gpu/vulkan/vk_backend.cc | 6 + source/blender/gpu/vulkan/vk_buffer.cc | 14 +- source/blender/gpu/vulkan/vk_buffer.hh | 8 + source/blender/gpu/vulkan/vk_context.cc | 9 +- .../blender/gpu/vulkan/vk_descriptor_set.cc | 374 ++++++++++++++---- .../blender/gpu/vulkan/vk_descriptor_set.hh | 163 ++++++-- .../gpu/vulkan/vk_descriptor_set_layouts.cc | 41 +- .../gpu/vulkan/vk_descriptor_set_layouts.hh | 44 +++ source/blender/gpu/vulkan/vk_device.cc | 24 +- source/blender/gpu/vulkan/vk_device.hh | 23 +- source/blender/gpu/vulkan/vk_index_buffer.hh | 4 + source/blender/gpu/vulkan/vk_pipeline_pool.cc | 8 + source/blender/gpu/vulkan/vk_state_manager.hh | 2 +- .../blender/gpu/vulkan/vk_storage_buffer.hh | 4 + source/blender/gpu/vulkan/vk_texture.hh | 2 +- .../blender/gpu/vulkan/vk_uniform_buffer.hh | 4 + source/blender/gpu/vulkan/vk_vertex_buffer.cc | 4 +- source/blender/gpu/vulkan/vk_vertex_buffer.hh | 11 + 25 files changed, 730 insertions(+), 131 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cc b/intern/ghost/intern/GHOST_ContextVK.cc index c9ad3bf173f..158de8f6c82 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cc +++ b/intern/ghost/intern/GHOST_ContextVK.cc @@ -306,6 +306,7 @@ class GHOST_DeviceVK { vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; vulkan_12_features.shaderOutputLayer = features_12.shaderOutputLayer; vulkan_12_features.shaderOutputViewportIndex = features_12.shaderOutputViewportIndex; + vulkan_12_features.bufferDeviceAddress = features_12.bufferDeviceAddress; vulkan_12_features.timelineSemaphore = VK_TRUE; feature_struct_ptr.push_back(&vulkan_12_features); @@ -365,6 +366,18 @@ class GHOST_DeviceVK { use_vk_ext_swapchain_maintenance_1 = true; } + /* Descriptor buffers */ + VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT, + nullptr, + VK_TRUE, + VK_FALSE, + VK_FALSE, + VK_FALSE}; + if (extension_requested(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME)) { + feature_struct_ptr.push_back(&descriptor_buffer); + } + /* Query and enable Fragment Shader Barycentrics. */ VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR fragment_shader_barycentric = {}; fragment_shader_barycentric.sType = @@ -1220,6 +1233,8 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() optional_device_extensions.push_back(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); optional_device_extensions.push_back(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME); optional_device_extensions.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + optional_device_extensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); + optional_device_extensions.push_back(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); VkInstance instance = VK_NULL_HANDLE; if (!vulkan_device.has_value()) { diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.cc b/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.cc index 0594e0276d2..76afa9d6733 100644 --- a/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.cc +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.cc @@ -55,6 +55,40 @@ void vk_pipeline_data_build_commands(VKCommandBufferInterface &command_buffer, nullptr); } + if (assign_if_different(r_bound_pipeline.descriptor_buffer_device_address, + pipeline_data.descriptor_buffer_device_address) && + r_bound_pipeline.descriptor_buffer_device_address != 0) + { + r_bound_pipeline.descriptor_buffer_offset = pipeline_data.descriptor_buffer_offset; + VkDescriptorBufferBindingInfoEXT descriptor_buffer_binding_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT, + nullptr, + r_bound_pipeline.descriptor_buffer_device_address, + VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | + VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT}; + command_buffer.bind_descriptor_buffers(1, &descriptor_buffer_binding_info); + + uint32_t buffer_index = 0; + command_buffer.set_descriptor_buffer_offsets(vk_pipeline_bind_point, + pipeline_data.vk_pipeline_layout, + 0, + 1, + &buffer_index, + &r_bound_pipeline.descriptor_buffer_offset); + } + else if (assign_if_different(r_bound_pipeline.descriptor_buffer_offset, + pipeline_data.descriptor_buffer_offset) && + r_bound_pipeline.descriptor_buffer_device_address != 0) + { + uint32_t buffer_index = 0; + command_buffer.set_descriptor_buffer_offsets(vk_pipeline_bind_point, + pipeline_data.vk_pipeline_layout, + 0, + 1, + &buffer_index, + &r_bound_pipeline.descriptor_buffer_offset); + } + if (pipeline_data.push_constants_size) { command_buffer.push_constants(pipeline_data.vk_pipeline_layout, vk_shader_stage_flags, diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.hh index 7273d4ec1af..052bcb65a5d 100644 --- a/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.hh +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_pipeline_data.hh @@ -24,6 +24,10 @@ struct VKPipelineData { VkPipeline vk_pipeline; VkPipelineLayout vk_pipeline_layout; VkDescriptorSet vk_descriptor_set; + /* VK_EXT_descriptor_buffer */ + VkDeviceAddress descriptor_buffer_device_address; + VkDeviceSize descriptor_buffer_offset; + uint32_t push_constants_size; const void *push_constants_data; }; @@ -65,6 +69,8 @@ struct VKViewportData { struct VKBoundPipeline { VkPipeline vk_pipeline; VkDescriptorSet vk_descriptor_set; + VkDeviceAddress descriptor_buffer_device_address; + VkDeviceSize descriptor_buffer_offset; }; struct VKIndexBufferBinding { diff --git a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh index ed277fb8754..dc660ab5fc6 100644 --- a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh +++ b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh @@ -476,6 +476,21 @@ class CommandBufferLog : public VKCommandBufferInterface { void begin_debug_utils_label(const VkDebugUtilsLabelEXT * /*vk_debug_utils_label*/) override {} void end_debug_utils_label() override {} + + /* VK_EXT_descriptor_buffer */ + void bind_descriptor_buffers( + uint32_t /*buffer_count*/, + const VkDescriptorBufferBindingInfoEXT * /*p_binding_infos*/) override + { + } + void set_descriptor_buffer_offsets(VkPipelineBindPoint /*pipeline_bind_point*/, + VkPipelineLayout /*layout*/, + uint32_t /*first_set*/, + uint32_t /*set_count*/, + const uint32_t * /*p_buffer_indices*/, + const VkDeviceSize * /*p_offsets*/) override + { + } }; class VKRenderGraphTest : public ::testing::Test { diff --git a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc index f40a96387d4..9c61045490d 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc +++ b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.cc @@ -326,4 +326,28 @@ void VKCommandBufferWrapper::end_debug_utils_label() } } +/* VK_EXT_descriptor_buffer */ +void VKCommandBufferWrapper::bind_descriptor_buffers( + uint32_t buffer_count, const VkDescriptorBufferBindingInfoEXT *p_binding_infos) +{ + const VKDevice &device = VKBackend::get().device; + device.functions.vkCmdBindDescriptorBuffers(vk_command_buffer_, buffer_count, p_binding_infos); +} +void VKCommandBufferWrapper::set_descriptor_buffer_offsets(VkPipelineBindPoint pipeline_bind_point, + VkPipelineLayout layout, + uint32_t first_set, + uint32_t set_count, + const uint32_t *p_buffer_indices, + const VkDeviceSize *p_offsets) +{ + const VKDevice &device = VKBackend::get().device; + device.functions.vkCmdSetDescriptorBufferOffsets(vk_command_buffer_, + pipeline_bind_point, + layout, + first_set, + set_count, + p_buffer_indices, + p_offsets); +} + } // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh index d4ab4f4a0ca..2f4e6c9f58e 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_command_buffer_wrapper.hh @@ -141,6 +141,16 @@ class VKCommandBufferInterface { /* VK_EXT_debug_utils */ virtual void begin_debug_utils_label(const VkDebugUtilsLabelEXT *vk_debug_utils_label) = 0; virtual void end_debug_utils_label() = 0; + + /* VK_EXT_descriptor_buffer */ + virtual void bind_descriptor_buffers( + uint32_t buffer_count, const VkDescriptorBufferBindingInfoEXT *p_binding_infos) = 0; + virtual void set_descriptor_buffer_offsets(VkPipelineBindPoint pipeline_bind_point, + VkPipelineLayout layout, + uint32_t first_set, + uint32_t set_count, + const uint32_t *p_buffer_indices, + const VkDeviceSize *p_offsets) = 0; }; class VKCommandBufferWrapper : public VKCommandBufferInterface { @@ -261,6 +271,16 @@ class VKCommandBufferWrapper : public VKCommandBufferInterface { void end_rendering() override; void begin_debug_utils_label(const VkDebugUtilsLabelEXT *vk_debug_utils_label) override; void end_debug_utils_label() override; + + /* VK_EXT_descriptor_buffer */ + void bind_descriptor_buffers(uint32_t buffer_count, + const VkDescriptorBufferBindingInfoEXT *p_binding_infos) override; + void set_descriptor_buffer_offsets(VkPipelineBindPoint pipeline_bind_point, + VkPipelineLayout layout, + uint32_t first_set, + uint32_t set_count, + const uint32_t *p_buffer_indices, + const VkDeviceSize *p_offsets) override; }; } // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh b/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh index 87cc5fe893f..f217cec7f66 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh @@ -327,7 +327,7 @@ struct VKRenderGraphNode { } }; -BLI_STATIC_ASSERT(sizeof(VKRenderGraphNode) <= 64, +BLI_STATIC_ASSERT(sizeof(VKRenderGraphNode) <= 96, "VKRenderGraphNode should be kept small. Consider moving data to the " "VKRenderGraphStorage class."); diff --git a/source/blender/gpu/vulkan/vk_backend.cc b/source/blender/gpu/vulkan/vk_backend.cc index c0c49da88db..ef8cf721ea7 100644 --- a/source/blender/gpu/vulkan/vk_backend.cc +++ b/source/blender/gpu/vulkan/vk_backend.cc @@ -137,6 +137,9 @@ static Vector missing_capabilities_get(VkPhysicalDevice vk_physic if (features_12.timelineSemaphore == VK_FALSE) { missing_capabilities.append("timeline semaphores"); } + if (features_12.bufferDeviceAddress == VK_FALSE) { + missing_capabilities.append("buffer device address"); + } /* Check device extensions. */ uint32_t vk_extension_count; @@ -385,6 +388,7 @@ void VKBackend::detect_workarounds(VKDevice &device) extensions.dynamic_rendering = false; extensions.dynamic_rendering_local_read = false; extensions.dynamic_rendering_unused_attachments = false; + extensions.descriptor_buffer = false; GCaps.render_pass_workaround = true; @@ -414,6 +418,8 @@ void VKBackend::detect_workarounds(VKDevice &device) #else extensions.external_memory = false; #endif + extensions.descriptor_buffer = device.supports_extension( + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); /* AMD GPUs don't support texture formats that use are aligned to 24 or 48 bits. */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) || diff --git a/source/blender/gpu/vulkan/vk_buffer.cc b/source/blender/gpu/vulkan/vk_buffer.cc index 450c9653350..4ed8c0c97f5 100644 --- a/source/blender/gpu/vulkan/vk_buffer.cc +++ b/source/blender/gpu/vulkan/vk_buffer.cc @@ -77,6 +77,11 @@ bool VKBuffer::create(size_t size_in_bytes, vma_create_info.pool = device.vma_pools.external_memory; } + const bool use_descriptor_buffer = device.extensions_get().descriptor_buffer; + if (use_descriptor_buffer) { + create_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + } + VkResult result = vmaCreateBuffer( allocator, &create_info, &vma_create_info, &vk_buffer_, &allocation_, nullptr); if (result != VK_SUCCESS) { @@ -88,11 +93,18 @@ bool VKBuffer::create(size_t size_in_bytes, device.resources.add_buffer(vk_buffer_); - vmaGetAllocationMemoryProperties(allocator, allocation_, &vk_memory_property_flags_); + if (use_descriptor_buffer) { + VkBufferDeviceAddressInfo vk_buffer_device_address_info = { + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, vk_buffer_}; + vk_device_address = vkGetBufferDeviceAddress(device.vk_handle(), + &vk_buffer_device_address_info); + } + vmaGetAllocationMemoryProperties(allocator, allocation_, &vk_memory_property_flags_); if (vk_memory_property_flags_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { return map(); } + return true; } diff --git a/source/blender/gpu/vulkan/vk_buffer.hh b/source/blender/gpu/vulkan/vk_buffer.hh index 97adf4dd83a..f53b460fb8e 100644 --- a/source/blender/gpu/vulkan/vk_buffer.hh +++ b/source/blender/gpu/vulkan/vk_buffer.hh @@ -33,8 +33,11 @@ class VKBuffer : public NonCopyable { /* Pointer to the virtually mapped memory. */ void *mapped_memory_ = nullptr; + VkDeviceAddress vk_device_address = 0; + public: VKBuffer() = default; + VKBuffer(VKBuffer &&other) = default; virtual ~VKBuffer(); /** Has this buffer been allocated? */ @@ -108,6 +111,11 @@ class VKBuffer : public NonCopyable { */ void *mapped_memory_get() const; + VkDeviceAddress device_address_get() const + { + return vk_device_address; + } + /** * Is this buffer mapped (visible on host) */ diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index 3ccbb15406c..ffbb4a3e0a9 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -157,9 +157,11 @@ TimelineValue VKContext::flush_render_graph(RenderGraphFlushFlags flags, framebuffer.rendering_end(*this); } } - descriptor_set_get().upload_descriptor_sets(); - descriptor_pools_get().discard(*this); VKDevice &device = VKBackend::get().device; + descriptor_set_get().upload_descriptor_sets(); + if (!device.extensions_get().descriptor_buffer) { + descriptor_pools_get().discard(*this); + } TimelineValue timeline = device.render_graph_submit( &render_graph_.value().get(), discard_pool, @@ -317,8 +319,7 @@ void VKContext::update_pipeline_data(VKShader &vk_shader, r_pipeline_data.vk_descriptor_set = VK_NULL_HANDLE; if (vk_shader.has_descriptor_set()) { VKDescriptorSetTracker &descriptor_set = descriptor_set_get(); - descriptor_set.update_descriptor_set(*this, access_info_); - r_pipeline_data.vk_descriptor_set = descriptor_set.vk_descriptor_set; + descriptor_set.update_descriptor_set(*this, access_info_, r_pipeline_data); } } diff --git a/source/blender/gpu/vulkan/vk_descriptor_set.cc b/source/blender/gpu/vulkan/vk_descriptor_set.cc index 286f17bd2ac..ba6c77fefac 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set.cc +++ b/source/blender/gpu/vulkan/vk_descriptor_set.cc @@ -17,61 +17,49 @@ namespace blender::gpu { -void VKDescriptorSetTracker::bind_buffer(VkDescriptorType vk_descriptor_type, - VkBuffer vk_buffer, - VkDeviceSize buffer_offset, - VkDeviceSize size_in_bytes, - VKDescriptorSet::Location location) +void VKDescriptorSetTracker::update_descriptor_set(VKContext &context, + render_graph::VKResourceAccessInfo &access_info, + render_graph::VKPipelineData &r_pipeline_data) { - vk_descriptor_buffer_infos_.append({vk_buffer, buffer_offset, size_in_bytes}); - vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - vk_descriptor_set, - location, - 0, - 1, - vk_descriptor_type, - nullptr, - nullptr, - nullptr}); + VKShader &shader = *unwrap(context.shader); + VKStateManager &state_manager = context.state_manager_get(); + + /* Can we reuse previous descriptor set. */ + if (!state_manager.is_dirty && + !assign_if_different(vk_descriptor_set_layout_, shader.vk_descriptor_set_layout_get()) && + shader.push_constants.layout_get().storage_type_get() != + VKPushConstants::StorageType::UNIFORM_BUFFER) + { + return; + } + state_manager.is_dirty = false; + + VKDevice &device = VKBackend::get().device; + VkDescriptorSetLayout vk_descriptor_set_layout = shader.vk_descriptor_set_layout_get(); + VKDescriptorSetUpdator *updator = &descriptor_sets; + if (device.extensions_get().descriptor_buffer) { + updator = &descriptor_buffers; + } + updator->allocate_new_descriptor_set( + device, context, shader, vk_descriptor_set_layout, r_pipeline_data); + updator->bind_shader_resources(device, state_manager, shader, access_info); } -void VKDescriptorSetTracker::bind_texel_buffer(VkBufferView vk_buffer_view, - const VKDescriptorSet::Location location) +void VKDescriptorSetTracker::upload_descriptor_sets() { - vk_buffer_views_.append(vk_buffer_view); - vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - vk_descriptor_set, - location, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, - nullptr, - nullptr, - nullptr}); + VKDevice &device = VKBackend::get().device; + VKDescriptorSetUpdator &updator = descriptor_sets; + if (device.extensions_get().descriptor_buffer) { + updator = descriptor_buffers; + } + updator.upload_descriptor_sets(); } -void VKDescriptorSetTracker::bind_image(VkDescriptorType vk_descriptor_type, - VkSampler vk_sampler, - VkImageView vk_image_view, - VkImageLayout vk_image_layout, - VKDescriptorSet::Location location) -{ - vk_descriptor_image_infos_.append({vk_sampler, vk_image_view, vk_image_layout}); - vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - vk_descriptor_set, - location, - 0, - 1, - vk_descriptor_type, - nullptr, - nullptr, - nullptr}); -} +/* -------------------------------------------------------------------- */ +/** \name VKDescriptorSetUpdator + * \{ */ -void VKDescriptorSetTracker::bind_image_resource(const VKStateManager &state_manager, +void VKDescriptorSetUpdator::bind_image_resource(const VKStateManager &state_manager, const VKResourceBinding &resource_binding, render_graph::VKResourceAccessInfo &access_info) { @@ -97,7 +85,7 @@ void VKDescriptorSetTracker::bind_image_resource(const VKStateManager &state_man layer_count}); } -void VKDescriptorSetTracker::bind_texture_resource(const VKDevice &device, +void VKDescriptorSetUpdator::bind_texture_resource(const VKDevice &device, const VKStateManager &state_manager, const VKResourceBinding &resource_binding, render_graph::VKResourceAccessInfo &access_info) @@ -105,11 +93,10 @@ void VKDescriptorSetTracker::bind_texture_resource(const VKDevice &device, const BindSpaceTextures::Elem &elem = state_manager.textures_.get(resource_binding.binding); switch (elem.resource_type) { case BindSpaceTextures::Type::VertexBuffer: { - VKVertexBuffer *vertex_buffer = static_cast(elem.resource); - vertex_buffer->ensure_updated(); - vertex_buffer->ensure_buffer_view(); - bind_texel_buffer(vertex_buffer->vk_buffer_view_get(), resource_binding.location); - access_info.buffers.append({vertex_buffer->vk_handle(), resource_binding.access_mask}); + VKVertexBuffer &vertex_buffer = *static_cast(elem.resource); + vertex_buffer.ensure_updated(); + bind_texel_buffer(vertex_buffer, resource_binding.location); + access_info.buffers.append({vertex_buffer.vk_handle(), resource_binding.access_mask}); break; } case BindSpaceTextures::Type::Texture: { @@ -118,11 +105,10 @@ void VKDescriptorSetTracker::bind_texture_resource(const VKDevice &device, /* Texture buffers are no textures, but wrap around vertex buffers and need to be * bound as texel buffers. */ /* TODO: Investigate if this can be improved in the API. */ - VKVertexBuffer *vertex_buffer = texture->source_buffer_; - vertex_buffer->ensure_updated(); - vertex_buffer->ensure_buffer_view(); - bind_texel_buffer(vertex_buffer->vk_buffer_view_get(), resource_binding.location); - access_info.buffers.append({vertex_buffer->vk_handle(), resource_binding.access_mask}); + VKVertexBuffer &vertex_buffer = *texture->source_buffer_; + vertex_buffer.ensure_updated(); + bind_texel_buffer(vertex_buffer, resource_binding.location); + access_info.buffers.append({vertex_buffer.vk_handle(), resource_binding.access_mask}); } else { const VKSampler &sampler = device.samplers().get(elem.sampler); @@ -146,7 +132,7 @@ void VKDescriptorSetTracker::bind_texture_resource(const VKDevice &device, } } -void VKDescriptorSetTracker::bind_input_attachment_resource( +void VKDescriptorSetUpdator::bind_input_attachment_resource( const VKDevice &device, const VKStateManager &state_manager, const VKResourceBinding &resource_binding, @@ -215,7 +201,7 @@ void VKDescriptorSetTracker::bind_input_attachment_resource( } } -void VKDescriptorSetTracker::bind_storage_buffer_resource( +void VKDescriptorSetUpdator::bind_storage_buffer_resource( const VKStateManager &state_manager, const VKResourceBinding &resource_binding, render_graph::VKResourceAccessInfo &access_info) @@ -224,12 +210,14 @@ void VKDescriptorSetTracker::bind_storage_buffer_resource( resource_binding.binding); VkBuffer vk_buffer = VK_NULL_HANDLE; VkDeviceSize vk_device_size = 0; + VkDeviceAddress vk_device_address = 0; switch (elem.resource_type) { case BindSpaceStorageBuffers::Type::IndexBuffer: { VKIndexBuffer *index_buffer = static_cast(elem.resource); index_buffer->ensure_updated(); vk_buffer = index_buffer->vk_handle(); vk_device_size = index_buffer->size_get(); + vk_device_address = index_buffer->device_address_get(); break; } case BindSpaceStorageBuffers::Type::VertexBuffer: { @@ -237,6 +225,7 @@ void VKDescriptorSetTracker::bind_storage_buffer_resource( vertex_buffer->ensure_updated(); vk_buffer = vertex_buffer->vk_handle(); vk_device_size = vertex_buffer->size_used_get(); + vk_device_address = vertex_buffer->device_address_get(); break; } case BindSpaceStorageBuffers::Type::UniformBuffer: { @@ -244,6 +233,7 @@ void VKDescriptorSetTracker::bind_storage_buffer_resource( uniform_buffer->ensure_updated(); vk_buffer = uniform_buffer->vk_handle(); vk_device_size = uniform_buffer->size_in_bytes(); + vk_device_address = uniform_buffer->device_address_get(); break; } case BindSpaceStorageBuffers::Type::StorageBuffer: { @@ -251,12 +241,14 @@ void VKDescriptorSetTracker::bind_storage_buffer_resource( storage_buffer->ensure_allocated(); vk_buffer = storage_buffer->vk_handle(); vk_device_size = storage_buffer->size_in_bytes(); + vk_device_address = storage_buffer->device_address_get(); break; } case BindSpaceStorageBuffers::Type::Buffer: { VKBuffer *buffer = static_cast(elem.resource); vk_buffer = buffer->vk_handle(); vk_device_size = buffer->size_in_bytes(); + vk_device_address = buffer->device_address_get(); break; } case BindSpaceStorageBuffers::Type::Unused: { @@ -266,6 +258,7 @@ void VKDescriptorSetTracker::bind_storage_buffer_resource( bind_buffer(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk_buffer, + vk_device_address, elem.offset, vk_device_size - elem.offset, resource_binding.location); @@ -274,7 +267,7 @@ void VKDescriptorSetTracker::bind_storage_buffer_resource( } } -void VKDescriptorSetTracker::bind_uniform_buffer_resource( +void VKDescriptorSetUpdator::bind_uniform_buffer_resource( const VKStateManager &state_manager, const VKResourceBinding &resource_binding, render_graph::VKResourceAccessInfo &access_info) @@ -283,13 +276,14 @@ void VKDescriptorSetTracker::bind_uniform_buffer_resource( uniform_buffer.ensure_updated(); bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniform_buffer.vk_handle(), + uniform_buffer.device_address_get(), 0, uniform_buffer.size_in_bytes(), resource_binding.location); access_info.buffers.append({uniform_buffer.vk_handle(), resource_binding.access_mask}); } -void VKDescriptorSetTracker::bind_push_constants(VKPushConstants &push_constants, +void VKDescriptorSetUpdator::bind_push_constants(VKPushConstants &push_constants, render_graph::VKResourceAccessInfo &access_info) { if (push_constants.layout_get().storage_type_get() != @@ -301,13 +295,14 @@ void VKDescriptorSetTracker::bind_push_constants(VKPushConstants &push_constants const VKUniformBuffer &uniform_buffer = *push_constants.uniform_buffer_get().get(); bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniform_buffer.vk_handle(), + uniform_buffer.device_address_get(), 0, uniform_buffer.size_in_bytes(), push_constants.layout_get().descriptor_set_location_get()); access_info.buffers.append({uniform_buffer.vk_handle(), VK_ACCESS_UNIFORM_READ_BIT}); } -void VKDescriptorSetTracker::bind_shader_resources(const VKDevice &device, +void VKDescriptorSetUpdator::bind_shader_resources(const VKDevice &device, const VKStateManager &state_manager, VKShader &shader, render_graph::VKResourceAccessInfo &access_info) @@ -345,32 +340,83 @@ void VKDescriptorSetTracker::bind_shader_resources(const VKDevice &device, bind_push_constants(shader.push_constants, access_info); } -void VKDescriptorSetTracker::update_descriptor_set(VKContext &context, - render_graph::VKResourceAccessInfo &access_info) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VKDescriptorSetPoolUpdator + * \{ */ + +void VKDescriptorSetPoolUpdator::allocate_new_descriptor_set( + VKDevice & /*device*/, + VKContext &context, + VKShader &shader, + VkDescriptorSetLayout vk_descriptor_set_layout, + render_graph::VKPipelineData &r_pipeline_data) { - VKShader &shader = *unwrap(context.shader); - VKStateManager &state_manager = context.state_manager_get(); - - /* Can we reuse previous descriptor set. */ - if (!state_manager.is_dirty && - !assign_if_different(vk_descriptor_set_layout_, shader.vk_descriptor_set_layout_get()) && - shader.push_constants.layout_get().storage_type_get() != - VKPushConstants::StorageType::UNIFORM_BUFFER) - { - return; - } - state_manager.is_dirty = false; - - /* Allocate a new descriptor set. */ - VkDescriptorSetLayout vk_descriptor_set_layout = shader.vk_descriptor_set_layout_get(); + /* Use descriptor pools/sets. */ vk_descriptor_set = context.descriptor_pools_get().allocate(vk_descriptor_set_layout); BLI_assert(vk_descriptor_set != VK_NULL_HANDLE); debug::object_label(vk_descriptor_set, shader.name_get()); - const VKDevice &device = VKBackend::get().device; - bind_shader_resources(device, state_manager, shader, access_info); + r_pipeline_data.vk_descriptor_set = vk_descriptor_set; } -void VKDescriptorSetTracker::upload_descriptor_sets() +void VKDescriptorSetPoolUpdator::bind_buffer(VkDescriptorType vk_descriptor_type, + VkBuffer vk_buffer, + VkDeviceAddress /*vk_device_address*/, + VkDeviceSize buffer_offset, + VkDeviceSize size_in_bytes, + VKDescriptorSet::Location location) +{ + vk_descriptor_buffer_infos_.append({vk_buffer, buffer_offset, size_in_bytes}); + vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + nullptr, + vk_descriptor_set, + location, + 0, + 1, + vk_descriptor_type, + nullptr, + nullptr, + nullptr}); +} + +void VKDescriptorSetPoolUpdator::bind_texel_buffer(VKVertexBuffer &vertex_buffer, + const VKDescriptorSet::Location location) +{ + vertex_buffer.ensure_buffer_view(); + vk_buffer_views_.append(vertex_buffer.vk_buffer_view_get()); + vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + nullptr, + vk_descriptor_set, + location, + 0, + 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + nullptr, + nullptr, + nullptr}); +} + +void VKDescriptorSetPoolUpdator::bind_image(VkDescriptorType vk_descriptor_type, + VkSampler vk_sampler, + VkImageView vk_image_view, + VkImageLayout vk_image_layout, + VKDescriptorSet::Location location) +{ + vk_descriptor_image_infos_.append({vk_sampler, vk_image_view, vk_image_layout}); + vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + nullptr, + vk_descriptor_set, + location, + 0, + 1, + vk_descriptor_type, + nullptr, + nullptr, + nullptr}); +} + +void VKDescriptorSetPoolUpdator::upload_descriptor_sets() { if (vk_write_descriptor_sets_.is_empty()) { return; @@ -464,4 +510,164 @@ void VKDescriptorSetTracker::upload_descriptor_sets() vk_write_descriptor_sets_.clear(); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VKDescriptorBufferUpdator + * \{ */ + +void VKDescriptorBufferUpdator::allocate_new_descriptor_set( + VKDevice &device, + VKContext & /*context*/, + VKShader & /*shader*/, + VkDescriptorSetLayout vk_descriptor_set_layout, + render_graph::VKPipelineData &r_pipeline_data) +{ + /* Use descriptor buffer. */ + descriptor_set_head = descriptor_set_tail; + layout = device.descriptor_set_layouts_get().descriptor_buffer_layout_get( + vk_descriptor_set_layout); + + /* Ensure if there is still place left in the current buffer. */ + if (buffers.is_empty() || layout.size > buffers.last().size_in_bytes() - descriptor_set_head) { + const VkDeviceSize default_buffer_size = 8 * 1024 * 1024; + buffers.append({}); + VKBuffer &buffer = buffers.last(); + buffer.create(default_buffer_size, + VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | + VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + 0, + VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); + debug::object_label(buffer.vk_handle(), "DescriptorBuffer"); + descriptor_buffer_data = static_cast(buffer.mapped_memory_get()); + descriptor_buffer_device_address = buffer.device_address_get(); + descriptor_buffer_offset = 0; + descriptor_set_head = 0; + descriptor_set_tail = 0; + } + + descriptor_set_tail = descriptor_set_head + layout.size; + + /* Update the current descriptor buffer and its offset to point to the active descriptor set. */ + descriptor_buffer_offset = descriptor_set_head; + + r_pipeline_data.descriptor_buffer_device_address = descriptor_buffer_device_address; + r_pipeline_data.descriptor_buffer_offset = descriptor_buffer_offset; +} + +void VKDescriptorBufferUpdator::bind_buffer(VkDescriptorType vk_descriptor_type, + VkBuffer /*vk_buffer*/, + VkDeviceAddress vk_device_address, + VkDeviceSize buffer_offset, + VkDeviceSize size_in_bytes, + VKDescriptorSet::Location location) +{ + BLI_assert(vk_device_address != 0); + VKDevice &device = VKBackend::get().device; + const VkPhysicalDeviceDescriptorBufferPropertiesEXT &vk_descriptor_buffer_properties = + device.physical_device_descriptor_buffer_properties_get(); + VkDescriptorAddressInfoEXT descriptor_address_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + nullptr, + vk_device_address + buffer_offset, + size_in_bytes}; + + VkDescriptorGetInfoEXT vk_descriptor_get_info{ + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, nullptr, vk_descriptor_type}; + VkDeviceSize descriptor_size = 0; + switch (vk_descriptor_type) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + vk_descriptor_get_info.data.pUniformBuffer = &descriptor_address_info; + descriptor_size = vk_descriptor_buffer_properties.uniformBufferDescriptorSize; + break; + + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + vk_descriptor_get_info.data.pStorageBuffer = &descriptor_address_info; + descriptor_size = vk_descriptor_buffer_properties.storageBufferDescriptorSize; + break; + + default: + BLI_assert_unreachable(); + } + + uint8_t *descriptor_ptr = get_descriptor_binding_ptr(location); + device.functions.vkGetDescriptor( + device.vk_handle(), &vk_descriptor_get_info, descriptor_size, descriptor_ptr); +} + +void VKDescriptorBufferUpdator::bind_texel_buffer(VKVertexBuffer &vertex_buffer, + const VKDescriptorSet::Location location) +{ + VkDeviceAddress vk_device_address = vertex_buffer.device_address_get(); + BLI_assert(vk_device_address != 0); + VKDevice &device = VKBackend::get().device; + const VkPhysicalDeviceDescriptorBufferPropertiesEXT &vk_descriptor_buffer_properties = + device.physical_device_descriptor_buffer_properties_get(); + VkDescriptorAddressInfoEXT descriptor_address_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT, + nullptr, + vk_device_address, + vertex_buffer.size_used_get(), + vertex_buffer.to_vk_format()}; + + VkDescriptorGetInfoEXT vk_descriptor_get_info{ + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, nullptr, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER}; + vk_descriptor_get_info.data.pUniformTexelBuffer = &descriptor_address_info; + VkDeviceSize descriptor_size = vk_descriptor_buffer_properties.uniformTexelBufferDescriptorSize; + + uint8_t *descriptor_ptr = get_descriptor_binding_ptr(location); + device.functions.vkGetDescriptor( + device.vk_handle(), &vk_descriptor_get_info, descriptor_size, descriptor_ptr); +} + +void VKDescriptorBufferUpdator::bind_image(VkDescriptorType vk_descriptor_type, + VkSampler vk_sampler, + VkImageView vk_image_view, + VkImageLayout vk_image_layout, + VKDescriptorSet::Location location) +{ + VKDevice &device = VKBackend::get().device; + const VkPhysicalDeviceDescriptorBufferPropertiesEXT &vk_descriptor_buffer_properties = + device.physical_device_descriptor_buffer_properties_get(); + VkDescriptorImageInfo vk_descriptor_image_info = {vk_sampler, vk_image_view, vk_image_layout}; + VkDescriptorGetInfoEXT vk_descriptor_get_info{ + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, nullptr, vk_descriptor_type}; + VkDeviceSize descriptor_size = 0; + switch (vk_descriptor_type) { + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + vk_descriptor_get_info.data.pCombinedImageSampler = &vk_descriptor_image_info; + descriptor_size = vk_descriptor_buffer_properties.combinedImageSamplerDescriptorSize; + break; + + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + vk_descriptor_get_info.data.pStorageImage = &vk_descriptor_image_info; + descriptor_size = vk_descriptor_buffer_properties.storageImageDescriptorSize; + break; + + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + vk_descriptor_get_info.data.pInputAttachmentImage = &vk_descriptor_image_info; + descriptor_size = vk_descriptor_buffer_properties.inputAttachmentDescriptorSize; + break; + + default: + BLI_assert_unreachable(); + } + + uint8_t *descriptor_ptr = get_descriptor_binding_ptr(location); + device.functions.vkGetDescriptor( + device.vk_handle(), &vk_descriptor_get_info, descriptor_size, descriptor_ptr); +} + +void VKDescriptorBufferUpdator::upload_descriptor_sets() +{ + /* Buffers have already been updated. only need to discard the buffers. */ + buffers.clear(); + descriptor_buffer_data = nullptr; + descriptor_buffer_device_address = 0; + descriptor_buffer_offset = 0; +} + +/** \} */ + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_descriptor_set.hh b/source/blender/gpu/vulkan/vk_descriptor_set.hh index 659d071a12c..71feb3c27ba 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set.hh +++ b/source/blender/gpu/vulkan/vk_descriptor_set.hh @@ -13,9 +13,11 @@ #include "gpu_shader_private.hh" +#include "render_graph/nodes/vk_pipeline_data.hh" #include "render_graph/vk_resource_access_info.hh" #include "vk_buffer.hh" #include "vk_common.hh" +#include "vk_descriptor_set_layouts.hh" #include "vk_resource_tracker.hh" #include "vk_uniform_buffer.hh" @@ -25,6 +27,8 @@ class VKStateManager; class VKDevice; class VKPushConstants; class VKShader; +class VKDescriptorSetTracker; +class VKVertexBuffer; /** * In vulkan shader resources (images and buffers) are grouped in descriptor sets. @@ -76,42 +80,22 @@ class VKDescriptorSet : NonCopyable { }; }; -class VKDescriptorSetTracker { - friend class VKDescriptorSet; - - Vector vk_buffer_views_; - Vector vk_descriptor_buffer_infos_; - Vector vk_descriptor_image_infos_; - Vector vk_write_descriptor_sets_; - - /* Last used layout to identify changes. */ - VkDescriptorSetLayout vk_descriptor_set_layout_ = VK_NULL_HANDLE; - +class VKDescriptorSetUpdator { public: - VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE; + virtual ~VKDescriptorSetUpdator(){}; - VKDescriptorSetTracker() {} - - /** - * Update the descriptor set. Reuses previous descriptor set when no changes are detected. This - * improves performance when working with large grease pencil scenes. - */ - void update_descriptor_set(VKContext &context, - render_graph::VKResourceAccessInfo &resource_access_info); - - /** - * Upload all descriptor sets to the device. - * - * NOTE: Caller should discard the associated descriptor pools. (VKDescriptorPools::discard) - */ - void upload_descriptor_sets(); - - private: + virtual void allocate_new_descriptor_set(VKDevice &device, + VKContext &context, + VKShader &shader, + VkDescriptorSetLayout vk_descriptor_set_layout, + render_graph::VKPipelineData &r_pipeline_data) = 0; void bind_shader_resources(const VKDevice &device, const VKStateManager &state_manager, VKShader &shader, render_graph::VKResourceAccessInfo &access_info); + virtual void upload_descriptor_sets() = 0; + private: void bind_image_resource(const VKStateManager &state_manager, const VKResourceBinding &resource_binding, render_graph::VKResourceAccessInfo &access_info); @@ -129,21 +113,134 @@ class VKDescriptorSetTracker { const VKStateManager &state_manager, const VKResourceBinding &resource_binding, render_graph::VKResourceAccessInfo &access_info); - void bind_push_constants(VKPushConstants &push_constants, + void bind_push_constants(VKPushConstants &push_constants, render_graph::VKResourceAccessInfo &access_info); - void bind_texel_buffer(VkBufferView vk_buffer_view, VKDescriptorSet::Location location); + protected: + virtual void bind_texel_buffer(VKVertexBuffer &vertex_buffer, + VKDescriptorSet::Location location) = 0; + virtual void bind_buffer(VkDescriptorType vk_descriptor_type, + VkBuffer vk_buffer, + VkDeviceAddress vk_device_address, + VkDeviceSize buffer_offset, + VkDeviceSize size_in_bytes, + VKDescriptorSet::Location location) = 0; + virtual void bind_image(VkDescriptorType vk_descriptor_type, + VkSampler vk_sampler, + VkImageView vk_image_view, + VkImageLayout vk_image_layout, + VKDescriptorSet::Location location) = 0; +}; + +class VKDescriptorSetPoolUpdator : public VKDescriptorSetUpdator { + public: + VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE; + + void allocate_new_descriptor_set(VKDevice &device, + VKContext &context, + VKShader &shader, + VkDescriptorSetLayout vk_descriptor_set_layout, + render_graph::VKPipelineData &r_pipeline_data) override; + + void upload_descriptor_sets() override; + + protected: + void bind_texel_buffer(VKVertexBuffer &vertex_buffer, + VKDescriptorSet::Location location) override; void bind_buffer(VkDescriptorType vk_descriptor_type, VkBuffer vk_buffer, + VkDeviceAddress vk_device_address, VkDeviceSize buffer_offset, VkDeviceSize size_in_bytes, - VKDescriptorSet::Location location); + VKDescriptorSet::Location location) override; void bind_image(VkDescriptorType vk_descriptor_type, VkSampler vk_sampler, VkImageView vk_image_view, VkImageLayout vk_image_layout, - VKDescriptorSet::Location location); + VKDescriptorSet::Location location) override; + + private: + Vector vk_buffer_views_; + Vector vk_descriptor_buffer_infos_; + Vector vk_descriptor_image_infos_; + Vector vk_write_descriptor_sets_; +}; + +class VKDescriptorBufferUpdator : public VKDescriptorSetUpdator { + public: + /* Offset to the beginning of the current descriptor set. */ + VkDeviceSize descriptor_set_head = 0; + /* Offset to the end (+1) of the current descriptor set. */ + VkDeviceSize descriptor_set_tail = 0; + /* Current layout of the descriptor set being filled. */ + VKDescriptorBufferLayout layout; + /* Descriptor buffers */ + Vector buffers; + + /* Current descriptor buffer handle and offset. */ + VkDeviceAddress descriptor_buffer_device_address = 0; + uint8_t *descriptor_buffer_data = nullptr; + VkDeviceSize descriptor_buffer_offset = 0; + + void allocate_new_descriptor_set(VKDevice &device, + VKContext &context, + VKShader &shader, + VkDescriptorSetLayout vk_descriptor_set_layout, + render_graph::VKPipelineData &r_pipeline_data) override; + + void upload_descriptor_sets() override; + + protected: + void bind_texel_buffer(VKVertexBuffer &vertex_buffer, + VKDescriptorSet::Location location) override; + void bind_buffer(VkDescriptorType vk_descriptor_type, + VkBuffer vk_buffer, + VkDeviceAddress vk_device_address, + VkDeviceSize buffer_offset, + VkDeviceSize size_in_bytes, + VKDescriptorSet::Location location) override; + void bind_image(VkDescriptorType vk_descriptor_type, + VkSampler vk_sampler, + VkImageView vk_image_view, + VkImageLayout vk_image_layout, + VKDescriptorSet::Location location) override; + + private: + inline uint8_t *get_descriptor_binding_ptr(uint32_t binding) const + { + return descriptor_buffer_data + descriptor_buffer_offset + layout.binding_offsets[binding]; + } +}; + +class VKDescriptorSetTracker { + friend class VKDescriptorSet; + + /* Last used layout to identify changes. */ + VkDescriptorSetLayout vk_descriptor_set_layout_ = VK_NULL_HANDLE; + + public: + class VKDescriptorBufferUpdator descriptor_buffers; + class VKDescriptorSetPoolUpdator descriptor_sets; + + VKDescriptorSetTracker() {} + + /** + * Update the descriptor set. Reuses previous descriptor set when no changes are detected. This + * improves performance when working with large grease pencil scenes. + */ + void update_descriptor_set(VKContext &context, + render_graph::VKResourceAccessInfo &resource_access_info, + render_graph::VKPipelineData &r_pipeline_data); + + /** + * Upload all descriptor sets to the device. + * + * NOTE: Caller should discard the associated descriptor pools. (VKDescriptorPools::discard) + */ + void upload_descriptor_sets(); + + private: }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_descriptor_set_layouts.cc b/source/blender/gpu/vulkan/vk_descriptor_set_layouts.cc index 6fab7ff83bf..2ee05383803 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set_layouts.cc +++ b/source/blender/gpu/vulkan/vk_descriptor_set_layouts.cc @@ -40,11 +40,16 @@ VkDescriptorSetLayout VKDescriptorSetLayouts::get_or_create(const VKDescriptorSe } update_layout_bindings(info); + const VKDevice &device = VKBackend::get().device; + const bool use_descriptor_buffer = device.extensions_get().descriptor_buffer; vk_descriptor_set_layout_create_info_.bindingCount = vk_descriptor_set_layout_bindings_.size(); vk_descriptor_set_layout_create_info_.pBindings = vk_descriptor_set_layout_bindings_.data(); + if (use_descriptor_buffer) { + vk_descriptor_set_layout_create_info_.flags = + VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + } - const VKDevice &device = VKBackend::get().device; VkDescriptorSetLayout vk_descriptor_set_layout = VK_NULL_HANDLE; vkCreateDescriptorSetLayout(device.vk_handle(), &vk_descriptor_set_layout_create_info_, @@ -58,6 +63,12 @@ VkDescriptorSetLayout VKDescriptorSetLayouts::get_or_create(const VKDescriptorSe vk_descriptor_set_layouts_.add(info, vk_descriptor_set_layout); r_created = true; + if (use_descriptor_buffer) { + descriptor_buffer_layouts_.add( + vk_descriptor_set_layout, + create_descriptor_buffer_layout(device, info, vk_descriptor_set_layout)); + } + return vk_descriptor_set_layout; } @@ -90,4 +101,32 @@ void VKDescriptorSetLayouts::deinit() vk_descriptor_set_layouts_.clear(); } +static void align_size(VkDeviceSize &r_size, VkDeviceSize alignment) +{ + if (alignment > 1) { + r_size = (r_size + alignment - 1) & ~(alignment - 1); + } +} +VKDescriptorBufferLayout VKDescriptorSetLayouts::create_descriptor_buffer_layout( + const VKDevice &device, + const VKDescriptorSetLayoutInfo &info, + VkDescriptorSetLayout vk_descriptor_set_layout) const +{ + const VkPhysicalDeviceDescriptorBufferPropertiesEXT &properties = + device.physical_device_descriptor_buffer_properties_get(); + VKDescriptorBufferLayout result = {}; + + device.functions.vkGetDescriptorSetLayoutSize( + device.vk_handle(), vk_descriptor_set_layout, &result.size); + align_size(result.size, properties.descriptorBufferOffsetAlignment); + + result.binding_offsets.resize(info.bindings.size()); + for (uint32_t binding : IndexRange(info.bindings.size())) { + device.functions.vkGetDescriptorSetLayoutBindingOffset( + device.vk_handle(), vk_descriptor_set_layout, binding, &result.binding_offsets[binding]); + } + + return result; +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_descriptor_set_layouts.hh b/source/blender/gpu/vulkan/vk_descriptor_set_layouts.hh index 91229f25826..78b89a4b166 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set_layouts.hh +++ b/source/blender/gpu/vulkan/vk_descriptor_set_layouts.hh @@ -25,6 +25,9 @@ #include "vk_common.hh" namespace blender::gpu { + +class VKDevice; + /** * Key of descriptor set layout * @@ -63,6 +66,25 @@ template<> struct DefaultHash { } // namespace blender namespace blender::gpu { + +struct VKDescriptorBufferLayout { + /** + * Total size of the descriptor buffer. + * + * Size is aligned to + * VkPhysicalDeviceDescriptorBufferProperties.descriptorBufferOffsetAlignment. + */ + VkDeviceSize size; + + /** + * Offsets of each binding inside the buffer. + * + * Offsets are aligned to + * VkPhysicalDeviceDescriptorBufferProperties.descriptorBufferOffsetAlignment. + */ + Vector binding_offsets; +}; + /** * Registries of descriptor set layouts. */ @@ -74,6 +96,7 @@ class VKDescriptorSetLayouts : NonCopyable { * Map containing all created descriptor set layouts. */ Map vk_descriptor_set_layouts_; + Map descriptor_buffer_layouts_; /** * Reusable descriptor set layout create info. @@ -96,6 +119,23 @@ class VKDescriptorSetLayouts : NonCopyable { bool &r_created, bool &r_needed); + /** + * Return the descriptor buffer layout offsets and alignment for the given + * vk_descriptor_set_layout handle. + * + * A copy of the buffer layout is returned due to other threads can alter the location of the + * items. + * + * This function has undefined behavior when descriptor buffers extension isn't enabled on the + * VKDevice. + */ + VKDescriptorBufferLayout descriptor_buffer_layout_get( + VkDescriptorSetLayout vk_descriptor_set_layout) + { + std::scoped_lock lock(mutex_); + return descriptor_buffer_layouts_.lookup(vk_descriptor_set_layout); + } + /** * Free all descriptor set layouts. * @@ -113,6 +153,10 @@ class VKDescriptorSetLayouts : NonCopyable { private: void update_layout_bindings(const VKDescriptorSetLayoutInfo &info); + VKDescriptorBufferLayout create_descriptor_buffer_layout( + const VKDevice &device, + const VKDescriptorSetLayoutInfo &info, + VkDescriptorSetLayout vk_descriptor_set_layout) const; }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_device.cc b/source/blender/gpu/vulkan/vk_device.cc index 5c4728de817..fbe027704e1 100644 --- a/source/blender/gpu/vulkan/vk_device.cc +++ b/source/blender/gpu/vulkan/vk_device.cc @@ -39,6 +39,7 @@ void VKExtensions::log() const " - [%c] shader output layer\n" " - [%c] fragment shader barycentric\n" "Device extensions\n" + " - [%c] descriptor buffer\n" " - [%c] dynamic rendering\n" " - [%c] dynamic rendering local read\n" " - [%c] dynamic rendering unused attachments\n" @@ -47,6 +48,7 @@ void VKExtensions::log() const shader_output_viewport_index ? 'X' : ' ', shader_output_layer ? 'X' : ' ', fragment_shader_barycentric ? 'X' : ' ', + descriptor_buffer ? 'X' : ' ', dynamic_rendering ? 'X' : ' ', dynamic_rendering_local_read ? 'X' : ' ', dynamic_rendering_unused_attachments ? 'X' : ' ', @@ -126,10 +128,10 @@ void VKDevice::init(void *ghost_context) vk_queue_ = handles.queue; queue_mutex_ = static_cast(handles.queue_mutex); + init_physical_device_extensions(); init_physical_device_properties(); init_physical_device_memory_properties(); init_physical_device_features(); - init_physical_device_extensions(); VKBackend::platform_init(*this); VKBackend::capabilities_init(*this); init_functions(); @@ -177,6 +179,14 @@ void VKDevice::init_functions() #endif } + /* VK_EXT_descriptor_buffer */ + functions.vkGetDescriptorSetLayoutSize = LOAD_FUNCTION(vkGetDescriptorSetLayoutSizeEXT); + functions.vkGetDescriptorSetLayoutBindingOffset = LOAD_FUNCTION( + vkGetDescriptorSetLayoutBindingOffsetEXT); + functions.vkGetDescriptor = LOAD_FUNCTION(vkGetDescriptorEXT); + functions.vkCmdBindDescriptorBuffers = LOAD_FUNCTION(vkCmdBindDescriptorBuffersEXT); + functions.vkCmdSetDescriptorBufferOffsets = LOAD_FUNCTION(vkCmdSetDescriptorBufferOffsetsEXT); + #undef LOAD_FUNCTION } @@ -197,6 +207,15 @@ void VKDevice::init_physical_device_properties() vk_physical_device_properties.pNext = &vk_physical_device_driver_properties_; vk_physical_device_driver_properties_.pNext = &vk_physical_device_id_properties_; + if (supports_extension(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME)) { + vk_physical_device_descriptor_buffer_properties_ = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT}; + vk_physical_device_descriptor_buffer_properties_.pNext = + vk_physical_device_driver_properties_.pNext; + vk_physical_device_driver_properties_.pNext = + &vk_physical_device_descriptor_buffer_properties_; + } + vkGetPhysicalDeviceProperties2(vk_physical_device_, &vk_physical_device_properties); vk_physical_device_properties_ = vk_physical_device_properties.properties; } @@ -251,6 +270,9 @@ void VKDevice::init_memory_allocator() info.physicalDevice = vk_physical_device_; info.device = vk_device_; info.instance = vk_instance_; + if (extensions_.descriptor_buffer) { + info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + } vmaCreateAllocator(&info, &mem_allocator_); if (!extensions_.external_memory) { diff --git a/source/blender/gpu/vulkan/vk_device.hh b/source/blender/gpu/vulkan/vk_device.hh index 11e62096dc6..fc3d85932d0 100644 --- a/source/blender/gpu/vulkan/vk_device.hh +++ b/source/blender/gpu/vulkan/vk_device.hh @@ -59,6 +59,11 @@ struct VKExtensions { */ bool external_memory = false; + /** + * Does the device support VK_EXT_descriptor_buffer. + */ + bool descriptor_buffer = false; + /** * Does the device support logic ops. */ @@ -208,6 +213,8 @@ class VKDevice : public NonCopyable { VkPhysicalDeviceDriverProperties vk_physical_device_driver_properties_ = {}; VkPhysicalDeviceIDProperties vk_physical_device_id_properties_ = {}; VkPhysicalDeviceMemoryProperties vk_physical_device_memory_properties_ = {}; + VkPhysicalDeviceDescriptorBufferPropertiesEXT vk_physical_device_descriptor_buffer_properties_ = + {}; /** Features support. */ VkPhysicalDeviceFeatures vk_physical_device_features_ = {}; VkPhysicalDeviceVulkan11Features vk_physical_device_vulkan_11_features_ = {}; @@ -258,6 +265,14 @@ class VKDevice : public NonCopyable { /* Extension: VK_KHR_external_memory_win32 */ PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32Handle = nullptr; #endif + + /* Extension: VK_EXT_descriptor_buffer */ + PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSize = nullptr; + PFN_vkGetDescriptorSetLayoutBindingOffsetEXT vkGetDescriptorSetLayoutBindingOffset = nullptr; + PFN_vkGetDescriptorEXT vkGetDescriptor = nullptr; + PFN_vkCmdBindDescriptorBuffersEXT vkCmdBindDescriptorBuffers = nullptr; + PFN_vkCmdSetDescriptorBufferOffsetsEXT vkCmdSetDescriptorBufferOffsets = nullptr; + } functions; struct { @@ -288,6 +303,12 @@ class VKDevice : public NonCopyable { return vk_physical_device_id_properties_; } + inline const VkPhysicalDeviceDescriptorBufferPropertiesEXT & + physical_device_descriptor_buffer_properties_get() const + { + return vk_physical_device_descriptor_buffer_properties_; + } + const VkPhysicalDeviceFeatures &physical_device_features_get() const { return vk_physical_device_features_; @@ -365,7 +386,7 @@ class VKDevice : public NonCopyable { { return workarounds_; } - const VKExtensions &extensions_get() const + inline const VKExtensions &extensions_get() const { return extensions_; } diff --git a/source/blender/gpu/vulkan/vk_index_buffer.hh b/source/blender/gpu/vulkan/vk_index_buffer.hh index ae4265e53cb..56d624ccdc5 100644 --- a/source/blender/gpu/vulkan/vk_index_buffer.hh +++ b/source/blender/gpu/vulkan/vk_index_buffer.hh @@ -31,6 +31,10 @@ class VKIndexBuffer : public IndexBuf { { return buffer_get().vk_handle(); } + inline VkDeviceAddress device_address_get() const + { + return buffer_get().device_address_get(); + } VkIndexType vk_index_type() const { return to_vk_index_type(index_type_); diff --git a/source/blender/gpu/vulkan/vk_pipeline_pool.cc b/source/blender/gpu/vulkan/vk_pipeline_pool.cc index f6d4166c597..961273ac304 100644 --- a/source/blender/gpu/vulkan/vk_pipeline_pool.cc +++ b/source/blender/gpu/vulkan/vk_pipeline_pool.cc @@ -202,6 +202,9 @@ VkPipeline VKPipelinePool::get_or_create_compute_pipeline(VKComputeInfo &compute /* Build pipeline. */ VKBackend &backend = VKBackend::get(); VKDevice &device = backend.device; + if (device.extensions_get().descriptor_buffer) { + vk_compute_pipeline_create_info_.flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + } VkPipeline pipeline = VK_NULL_HANDLE; vkCreateComputePipelines(device.vk_handle(), @@ -214,6 +217,7 @@ VkPipeline VKPipelinePool::get_or_create_compute_pipeline(VKComputeInfo &compute compute_pipelines_.add(compute_info, pipeline); /* Reset values to initial value. */ + vk_compute_pipeline_create_info_.flags = 0; vk_compute_pipeline_create_info_.layout = VK_NULL_HANDLE; vk_compute_pipeline_create_info_.stage.module = VK_NULL_HANDLE; vk_compute_pipeline_create_info_.stage.pSpecializationInfo = nullptr; @@ -586,6 +590,9 @@ VkPipeline VKPipelinePool::get_or_create_graphics_pipeline(VKGraphicsInfo &graph /* Build pipeline. */ VKBackend &backend = VKBackend::get(); VKDevice &device = backend.device; + if (device.extensions_get().descriptor_buffer) { + vk_graphics_pipeline_create_info_.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + } VkPipeline pipeline = VK_NULL_HANDLE; vkCreateGraphicsPipelines(device.vk_handle(), @@ -599,6 +606,7 @@ VkPipeline VKPipelinePool::get_or_create_graphics_pipeline(VKGraphicsInfo &graph /* Reset values to initial value. */ specialization_info_reset(); + vk_graphics_pipeline_create_info_.flags = 0; vk_graphics_pipeline_create_info_.stageCount = 0; vk_graphics_pipeline_create_info_.layout = VK_NULL_HANDLE; vk_graphics_pipeline_create_info_.basePipelineHandle = VK_NULL_HANDLE; diff --git a/source/blender/gpu/vulkan/vk_state_manager.hh b/source/blender/gpu/vulkan/vk_state_manager.hh index 8b64cbbc243..ada7cf19abd 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.hh +++ b/source/blender/gpu/vulkan/vk_state_manager.hh @@ -204,7 +204,7 @@ class BindSpaceTextures { }; class VKStateManager : public StateManager { - friend class VKDescriptorSetTracker; + friend class VKDescriptorSetUpdator; uint texture_unpack_row_length_ = 0; diff --git a/source/blender/gpu/vulkan/vk_storage_buffer.hh b/source/blender/gpu/vulkan/vk_storage_buffer.hh index ec7d2749323..123b24597cb 100644 --- a/source/blender/gpu/vulkan/vk_storage_buffer.hh +++ b/source/blender/gpu/vulkan/vk_storage_buffer.hh @@ -43,6 +43,10 @@ class VKStorageBuffer : public StorageBuf { { return buffer_.vk_handle(); } + inline VkDeviceAddress device_address_get() const + { + return buffer_.device_address_get(); + } int64_t size_in_bytes() const { diff --git a/source/blender/gpu/vulkan/vk_texture.hh b/source/blender/gpu/vulkan/vk_texture.hh index 832827dd2de..c66259c9708 100644 --- a/source/blender/gpu/vulkan/vk_texture.hh +++ b/source/blender/gpu/vulkan/vk_texture.hh @@ -29,7 +29,7 @@ enum class VKImageViewFlags { ENUM_OPERATORS(VKImageViewFlags, VKImageViewFlags::NO_SWIZZLING) class VKTexture : public Texture { - friend class VKDescriptorSetTracker; + friend class VKDescriptorSetUpdator; /** * Texture format how the texture is stored on the device. diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.hh b/source/blender/gpu/vulkan/vk_uniform_buffer.hh index 75f8adbd546..3647a2f3614 100644 --- a/source/blender/gpu/vulkan/vk_uniform_buffer.hh +++ b/source/blender/gpu/vulkan/vk_uniform_buffer.hh @@ -42,6 +42,10 @@ class VKUniformBuffer : public UniformBuf, NonCopyable { { return buffer_.vk_handle(); } + inline VkDeviceAddress device_address_get() const + { + return buffer_.device_address_get(); + } size_t size_in_bytes() const { diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.cc b/source/blender/gpu/vulkan/vk_vertex_buffer.cc index 50391fb7190..44d92780f0e 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.cc +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.cc @@ -48,11 +48,9 @@ void VKVertexBuffer::ensure_buffer_view() } VkBufferViewCreateInfo buffer_view_info = {}; - eGPUTextureFormat texture_format = to_texture_format(&format); - buffer_view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; buffer_view_info.buffer = buffer_.vk_handle(); - buffer_view_info.format = to_vk_format(texture_format); + buffer_view_info.format = to_vk_format(); buffer_view_info.range = buffer_.size_in_bytes(); const VKDevice &device = VKBackend::get().device; diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.hh b/source/blender/gpu/vulkan/vk_vertex_buffer.hh index 63fefe03a16..4ba3006d45f 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.hh +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.hh @@ -11,6 +11,7 @@ #include "GPU_vertex_buffer.hh" #include "vk_buffer.hh" +#include "vk_common.hh" #include "vk_data_conversion.hh" namespace blender::gpu { @@ -38,6 +39,11 @@ class VKVertexBuffer : public VertBuf { return buffer_.vk_handle(); } + inline VkDeviceAddress device_address_get() const + { + return buffer_.device_address_get(); + } + VkBufferView vk_buffer_view_get() const { BLI_assert(vk_buffer_view_ != VK_NULL_HANDLE); @@ -47,6 +53,11 @@ class VKVertexBuffer : public VertBuf { void ensure_updated(); void ensure_buffer_view(); + inline VkFormat to_vk_format() + { + return blender::gpu::to_vk_format(to_texture_format(&format)); + } + protected: void acquire_data() override; void resize_data() override;