diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index c8f70aa3b2..0773277e5c 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -177,7 +177,6 @@ public: return result; } - // TODO(crueter): utilize layout binding flags vk::DescriptorSetLayout CreateDescriptorSetLayout(bool use_push_descriptor, bool use_descriptor_buffer = false) const { if (bindings.empty()) { @@ -190,9 +189,22 @@ public: if (use_descriptor_buffer) { flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; } + boost::container::small_vector binding_flags; + VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_ci{}; + const void* pnext = nullptr; + if (!use_push_descriptor && device->IsDescriptorBindingPartiallyBoundSupported()) { + binding_flags.assign(bindings.size(), VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT); + binding_flags_ci = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .pNext = nullptr, + .bindingCount = static_cast(binding_flags.size()), + .pBindingFlags = binding_flags.data(), + }; + pnext = &binding_flags_ci; + } return device->GetLogical().CreateDescriptorSetLayout({ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, + .pNext = pnext, .flags = flags, .bindingCount = static_cast(bindings.size()), .pBindings = bindings.data(), diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 2508cbde9d..1133132c30 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -251,13 +251,15 @@ GraphicsPipeline::GraphicsPipeline( Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, - GuestDescriptorQueue& guest_descriptor_queue_, Common::ThreadWorker* worker_thread, + GuestDescriptorQueue& guest_descriptor_queue_, DescriptorBufferRing& descriptor_buffer_ring_, + Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, pipeline_cache(pipeline_cache_), scheduler{scheduler_}, - guest_descriptor_queue{guest_descriptor_queue_}, spv_modules{std::move(stages)} { + guest_descriptor_queue{guest_descriptor_queue_}, + descriptor_buffer_ring{descriptor_buffer_ring_}, spv_modules{std::move(stages)} { if (shader_notify) { shader_notify->MarkShaderBuilding(); } @@ -277,20 +279,36 @@ GraphicsPipeline::GraphicsPipeline( num_descriptor_entries += NumDescriptorEntries(*info); } fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0]; - auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] { - DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; - uses_push_descriptor = builder.CanUsePushDescriptor(); - descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); - if (!uses_push_descriptor) { - descriptor_allocator = descriptor_pool.Allocator(device, scheduler, *descriptor_set_layout, stage_infos); + DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; + uses_push_descriptor = builder.CanUsePushDescriptor(); + uses_descriptor_buffer = builder.CanUseDescriptorBuffer() && descriptor_buffer_ring.IsValid(); + descriptor_set_layout = + builder.CreateDescriptorSetLayout(uses_push_descriptor, uses_descriptor_buffer); + if (uses_descriptor_buffer) { + descriptor_buffer_layout = builder.MakeDescriptorBufferLayout(*descriptor_set_layout); + if (descriptor_buffer_layout.size > DescriptorBufferRing::MaxAllocationSize()) { + LOG_WARNING(Render_Vulkan, + "Graphics pipeline {:016X} needs {} descriptor bytes, falling back to sets", + key.Hash(), descriptor_buffer_layout.size); + uses_descriptor_buffer = false; + descriptor_buffer_layout = {}; + descriptor_set_layout = builder.CreateDescriptorSetLayout(false); } + } - const VkDescriptorSetLayout set_layout{*descriptor_set_layout}; - pipeline_layout = builder.CreatePipelineLayout(set_layout); + const VkDescriptorSetLayout set_layout{*descriptor_set_layout}; + pipeline_layout = builder.CreatePipelineLayout(set_layout); + if (!uses_descriptor_buffer) { descriptor_update_template = builder.CreateTemplate(set_layout, *pipeline_layout, uses_push_descriptor); + if (!uses_push_descriptor) { + descriptor_allocator = + descriptor_pool.Allocator(device, scheduler, set_layout, stage_infos); + } + } + auto func{[this, shader_notify, &render_pass_cache, pipeline_statistics] { VkRenderPass render_pass{}; if (!device.IsKhrDynamicRenderingSupported()) { render_pass = render_pass_cache.Get(MakeRenderPassKey(key.state, device)); @@ -500,7 +518,7 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.BindHostGeometryBuffers(is_indexed); - guest_descriptor_queue.Acquire(scheduler, num_descriptor_entries); + guest_descriptor_queue.Acquire(scheduler, num_descriptor_entries, uses_descriptor_buffer); RescalingPushConstant rescaling; RenderAreaPushConstant render_area; @@ -570,7 +588,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, const void* const descriptor_data{guest_descriptor_queue.UpdateData()}; bool update_descriptors = true; - if (descriptor_set_layout && !uses_push_descriptor) { + if (descriptor_set_layout && !uses_push_descriptor && !uses_descriptor_buffer) { const auto* const entries = static_cast(descriptor_data); update_descriptors = bind_pipeline || last_descriptor_payload.size() != num_descriptor_entries || @@ -580,7 +598,21 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, last_descriptor_payload.assign(entries, entries + num_descriptor_entries); } } + VkDeviceSize descriptor_buffer_offset{}; + bool descriptor_buffer_ready{false}; + if (descriptor_set_layout && uses_descriptor_buffer) { + const DescriptorBufferRing::Allocation alloc{ + descriptor_buffer_ring.Allocate(scheduler, descriptor_buffer_layout.size)}; + if (alloc.host) { + WriteDescriptorBuffer(device, descriptor_buffer_layout, + static_cast(descriptor_data), + alloc.host); + descriptor_buffer_offset = alloc.offset; + descriptor_buffer_ready = true; + } + } scheduler.Record([this, descriptor_data, bind_pipeline, update_descriptors, + descriptor_buffer_offset, descriptor_buffer_ready, rescaling_data = rescaling.Data(), is_rescaling, update_rescaling, uses_render_area = render_area.uses_render_area, render_area_data = render_area.words](vk::CommandBuffer cmdbuf) { @@ -608,7 +640,17 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, if (!descriptor_set_layout) { return; } - if (uses_push_descriptor) { + if (uses_descriptor_buffer) { + if (!descriptor_buffer_ready) { + return; + } + const VkDescriptorBufferBindingInfoEXT binding_info{ + descriptor_buffer_ring.BindingInfo()}; + cmdbuf.BindDescriptorBuffersEXT(binding_info); + const u32 buffer_index{}; + cmdbuf.SetDescriptorBufferOffsetsEXT(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, + 0, buffer_index, descriptor_buffer_offset); + } else if (uses_push_descriptor) { cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout, 0, descriptor_data); } else if (update_descriptors) { @@ -1017,6 +1059,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } + if (uses_descriptor_buffer) { + flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; + } const RenderPassKey renderpass_key{MakeRenderPassKey(key.state, device)}; std::array color_attachment_formats{}; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index d4555c246b..6e3481d609 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -18,7 +18,9 @@ #include "shader_recompiler/shader_info.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" +#include "video_core/renderer_vulkan/pipeline_helper.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" +#include "video_core/renderer_vulkan/vk_descriptor_buffer.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" @@ -78,7 +80,8 @@ public: Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify, const Device& device, DescriptorPool& descriptor_pool, - GuestDescriptorQueue& guest_descriptor_queue, Common::ThreadWorker* worker_thread, + GuestDescriptorQueue& guest_descriptor_queue, + DescriptorBufferRing& descriptor_buffer_ring, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, std::array stages, const std::array& infos); @@ -150,6 +153,7 @@ private: vk::PipelineCache& pipeline_cache; Scheduler& scheduler; GuestDescriptorQueue& guest_descriptor_queue; + DescriptorBufferRing& descriptor_buffer_ring; bool (*configure_func)(GraphicsPipeline*, bool){}; @@ -172,12 +176,14 @@ private: vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; + DescriptorBufferLayout descriptor_buffer_layout; std::vector last_descriptor_payload; std::condition_variable build_condvar; std::mutex build_mutex; std::atomic_bool is_built{false}; bool uses_push_descriptor{false}; + bool uses_descriptor_buffer{false}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 2ebdd33e53..6724df44fc 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -873,8 +873,8 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique( scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, - descriptor_pool, guest_descriptor_queue, thread_worker, statistics, render_pass_cache, key, - std::move(modules), infos); + descriptor_pool, guest_descriptor_queue, descriptor_buffer_ring, thread_worker, statistics, + render_pass_cache, key, std::move(modules), infos); } catch (const Shader::Exception& exception) { auto hash = key.Hash(); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 9c392fae29..e87d3e88b8 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -905,6 +905,10 @@ FN_MAX_LIMIT_LIST return extensions.external_memory_host; } + bool IsDescriptorBindingPartiallyBoundSupported() const { + return features.descriptor_indexing.descriptorBindingPartiallyBound; + } + u64 GetMinImportedHostPointerAlignment() const { return properties.external_memory_host.minImportedHostPointerAlignment; }