From b9428acf122ee01c55d9d15098233de8f3279646 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 30 Jul 2026 17:09:21 -0400 Subject: [PATCH] Cut per-draw descriptor set work Only query the timeline semaphore when no set looks free, and skip the update plus rebind when the payload matches the previous draw. --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 18 +++++++++++++++--- .../renderer_vulkan/vk_graphics_pipeline.h | 4 ++++ .../renderer_vulkan/vk_resource_pool.cpp | 19 +++++++++++-------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8112087730..3fdf0851c7 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include @@ -568,8 +569,19 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, } const void* const descriptor_data{guest_descriptor_queue.UpdateData()}; - scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), - is_rescaling, update_rescaling, + bool update_descriptors = true; + if (descriptor_set_layout && !uses_push_descriptor) { + const auto* const entries = static_cast(descriptor_data); + update_descriptors = + bind_pipeline || last_descriptor_payload.size() != num_descriptor_entries || + std::memcmp(last_descriptor_payload.data(), entries, + num_descriptor_entries * sizeof(DescriptorUpdateEntry)) != 0; + if (update_descriptors) { + last_descriptor_payload.assign(entries, entries + num_descriptor_entries); + } + } + scheduler.Record([this, descriptor_data, bind_pipeline, update_descriptors, + 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) { if (bind_pipeline) { @@ -599,7 +611,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, if (uses_push_descriptor) { cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout, 0, descriptor_data); - } else { + } else if (update_descriptors) { const VkDescriptorSet descriptor_set{descriptor_allocator.Commit()}; const vk::Device& dev{device.GetLogical()}; dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index d1caeaee8e..d4555c246b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "common/thread_worker.h" #include "shader_recompiler/shader_info.h" @@ -20,6 +21,7 @@ #include "video_core/renderer_vulkan/vk_buffer_cache.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" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace VideoCore { @@ -170,6 +172,8 @@ private: vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; + std::vector last_descriptor_payload; + std::condition_variable build_condvar; std::mutex build_mutex; std::atomic_bool is_built{false}; diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp index 6572f82ba9..9a922e6c85 100644 --- a/src/video_core/renderer_vulkan/vk_resource_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp @@ -12,10 +12,7 @@ ResourcePool::ResourcePool(MasterSemaphore& master_semaphore_, size_t grow_step_ : master_semaphore{&master_semaphore_}, grow_step{grow_step_} {} size_t ResourcePool::CommitResource() { - // Refresh semaphore to query updated results - master_semaphore->Refresh(); - const u64 gpu_tick = master_semaphore->KnownGpuTick(); - const auto search = [this, gpu_tick](size_t begin, size_t end) -> std::optional { + const auto search = [this](size_t begin, size_t end, u64 gpu_tick) -> std::optional { for (size_t iterator = begin; iterator < end; ++iterator) { if (gpu_tick >= ticks[iterator]) { ticks[iterator] = master_semaphore->CurrentTick(); @@ -24,11 +21,17 @@ size_t ResourcePool::CommitResource() { } return std::nullopt; }; - // Try to find a free resource from the hinted position to the end. - std::optional found = search(hint_iterator, ticks.size()); + const auto find_free = [&](u64 gpu_tick) -> std::optional { + std::optional result = search(hint_iterator, ticks.size(), gpu_tick); + if (!result) { + result = search(0, hint_iterator, gpu_tick); + } + return result; + }; + std::optional found = find_free(master_semaphore->KnownGpuTick()); if (!found) { - // Search from beginning to the hinted position. - found = search(0, hint_iterator); + master_semaphore->Refresh(); + found = find_free(master_semaphore->KnownGpuTick()); if (!found) { // Both searches failed, the pool is full; handle it. const size_t free_resource = ManageOverflow();