diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d36553da4a..8056faba25 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 @@ -308,6 +309,37 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { transitions.push_back(transition); } +bool GraphicsPipeline::UpdateDescriptorDirtyMask(const DescriptorUpdateEntry* data, size_t count) { + if (count == 0) { + descriptor_dirty_mask.clear(); + last_descriptor_data.clear(); + last_descriptor_count = 0; + return false; + } + const size_t word_count = (count + 63) / 64; + descriptor_dirty_mask.assign(word_count, 0); + + if (last_descriptor_count != count || last_descriptor_data.size() != count) { + last_descriptor_data.assign(data, data + count); + last_descriptor_count = count; + std::fill(descriptor_dirty_mask.begin(), descriptor_dirty_mask.end(), ~0ULL); + if ((count % 64) != 0) { + descriptor_dirty_mask.back() = (1ULL << (count % 64)) - 1; + } + return true; + } + + bool any_dirty = false; + for (size_t i = 0; i < count; ++i) { + if (std::memcmp(&last_descriptor_data[i], &data[i], sizeof(DescriptorUpdateEntry)) != 0) { + last_descriptor_data[i] = data[i]; + descriptor_dirty_mask[i / 64] |= (1ULL << (i % 64)); + any_dirty = true; + } + } + return any_dirty; +} + template bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; @@ -524,9 +556,22 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, GPU::Logging::GPULogger::GetInstance().LogPipelineBind(false, pipeline_info); } - const void* const descriptor_data{guest_descriptor_queue.UpdateData()}; + const DescriptorUpdateEntry* const descriptor_data{guest_descriptor_queue.UpdateData()}; + const size_t descriptor_count = guest_descriptor_queue.UpdateCount(); + const bool has_descriptor_layout = descriptor_set_layout != nullptr; + const bool descriptors_dirty = has_descriptor_layout && + UpdateDescriptorDirtyMask(descriptor_data, descriptor_count); + VkDescriptorSet descriptor_set = last_descriptor_set; + if (has_descriptor_layout && !uses_push_descriptor) { + if (descriptors_dirty || descriptor_set == VK_NULL_HANDLE) { + descriptor_set = descriptor_allocator.Commit(); + const vk::Device& dev{device.GetLogical()}; + dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data); + last_descriptor_set = descriptor_set; + } + } scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), - is_rescaling, update_rescaling, + is_rescaling, update_rescaling, descriptors_dirty, descriptor_set, uses_render_area = render_area.uses_render_area, render_area_data = render_area.words](vk::CommandBuffer cmdbuf) { if (bind_pipeline) { @@ -551,12 +596,12 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, return; } if (uses_push_descriptor) { + if (!descriptors_dirty) { + return; + } cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout, 0, descriptor_data); } else { - const VkDescriptorSet descriptor_set{descriptor_allocator.Commit()}; - const vk::Device& dev{device.GetLogical()}; - dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, descriptor_set, nullptr); } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 34941d6e8d..b5b1d09a75 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -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 { @@ -168,6 +170,13 @@ private: vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; + bool UpdateDescriptorDirtyMask(const DescriptorUpdateEntry* data, size_t count); + + std::vector last_descriptor_data; + std::vector descriptor_dirty_mask; + size_t last_descriptor_count{}; + VkDescriptorSet last_descriptor_set{VK_NULL_HANDLE}; + std::condition_variable build_condvar; std::mutex build_mutex; std::atomic_bool is_built{false}; diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index 82fce298da..5e1034d3c3 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -47,32 +50,44 @@ public: return upload_start; } + size_t UpdateCount() const noexcept { + return static_cast(payload_cursor - upload_start); + } + void AddSampledImage(VkImageView image_view, VkSampler sampler) { - *(payload_cursor++) = VkDescriptorImageInfo{ + DescriptorUpdateEntry entry{}; + entry.image = VkDescriptorImageInfo{ .sampler = sampler, .imageView = image_view, .imageLayout = VK_IMAGE_LAYOUT_GENERAL, }; + *(payload_cursor++) = entry; } void AddImage(VkImageView image_view) { - *(payload_cursor++) = VkDescriptorImageInfo{ + DescriptorUpdateEntry entry{}; + entry.image = VkDescriptorImageInfo{ .sampler = VK_NULL_HANDLE, .imageView = image_view, .imageLayout = VK_IMAGE_LAYOUT_GENERAL, }; + *(payload_cursor++) = entry; } void AddBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) { - *(payload_cursor++) = VkDescriptorBufferInfo{ + DescriptorUpdateEntry entry{}; + entry.buffer = VkDescriptorBufferInfo{ .buffer = buffer, .offset = offset, .range = size, }; + *(payload_cursor++) = entry; } void AddTexelBuffer(VkBufferView texel_buffer) { - *(payload_cursor++) = texel_buffer; + DescriptorUpdateEntry entry{}; + entry.texel_buffer = texel_buffer; + *(payload_cursor++) = entry; } private: