diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp index aae8407103..78c598bc03 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp @@ -519,21 +519,79 @@ namespace vk fmt::throw_exception("Unexpected descriptor structure at index %u", idx); }; - m_descriptor_set = allocate_descriptor_set(); - - for (unsigned i = 0; i < m_descriptor_slots.size(); ++i) + auto update_descriptor_slot = [this](unsigned idx) { - if (m_descriptors_dirty[i]) + const auto& slot = m_descriptor_slots[idx]; + const VkDescriptorType type = m_descriptor_types[idx]; + if (auto ptr = std::get_if(&slot)) { - // Push - push_descriptor_slot(i); - m_descriptors_dirty[i] = false; - continue; + m_descriptor_template[idx].pImageInfo = m_descriptor_set.store(*ptr); + return; } - // We should copy here if possible. - // Without descriptor_buffer, the most efficient option is to just use the normal bind logic due to the pointer-based nature of the descriptor inputs and no stride. - push_descriptor_slot(i); + if (auto ptr = std::get_if(&slot)) + { + m_descriptor_template[idx].pBufferInfo = m_descriptor_set.store(*ptr); + return; + } + + if (auto ptr = std::get_if(&slot)) + { + m_descriptor_template[idx].pTexelBufferView = m_descriptor_set.store(*ptr); + return; + } + + // FIXME: This sucks even if only used by interpreter. Do better. + if (auto ptr = std::get_if(&slot)) + { + ensure(type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + ensure((ptr->first + ptr->count) <= m_scratch_images_array.size()); + m_descriptor_set.push(m_scratch_images_array.data() + ptr->first, ptr->count, type, idx); + return; + } + + fmt::throw_exception("Unexpected descriptor structure at index %u", idx); + }; + + m_descriptor_set = allocate_descriptor_set(); + + if (!m_descriptor_template.empty()) [[ likely ]] + { + for (unsigned i = 0; i < m_descriptor_slots.size(); ++i) + { + m_descriptor_template[i].dstSet = m_descriptor_set.value(); + if (!m_descriptors_dirty[i]) + { + continue; + } + + // Update + update_descriptor_slot(i); + m_descriptors_dirty[i] = false; + } + + // Push + m_descriptor_set.push(m_descriptor_template, m_descriptor_template_typemask); + } + else + { + m_descriptor_template_typemask = 0u; + + for (unsigned i = 0; i < m_descriptor_slots.size(); ++i) + { + m_descriptor_template_typemask |= (1u << static_cast(m_descriptor_types[i])); + if (m_descriptors_dirty[i]) + { + // Push + push_descriptor_slot(i); + m_descriptors_dirty[i] = false; + continue; + } + + push_descriptor_slot(i); + } + + m_descriptor_template = m_descriptor_set.peek(); } m_descriptor_set.on_bind(); diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h index 6f5011ee25..39dd4923e0 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h @@ -130,6 +130,9 @@ namespace vk rsx::simple_array m_descriptor_pool_sizes; rsx::simple_array m_descriptor_types; + u32 m_descriptor_template_typemask = 0u; + rsx::simple_array m_descriptor_template; + std::vector m_descriptor_slots; std::vector m_descriptors_dirty; bool m_any_descriptors_dirty = false; diff --git a/rpcs3/Emu/RSX/VK/vkutils/descriptors.h b/rpcs3/Emu/RSX/VK/vkutils/descriptors.h index 12f7589c7c..b9c408a3d1 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/descriptors.h +++ b/rpcs3/Emu/RSX/VK/vkutils/descriptors.h @@ -85,43 +85,6 @@ namespace vk void init(VkDescriptorSet new_set); public: - descriptor_set(VkDescriptorSet set); - descriptor_set() = default; - ~descriptor_set(); - - descriptor_set(const descriptor_set&) = delete; - - void swap(descriptor_set& other); - descriptor_set& operator = (VkDescriptorSet set); - - VkDescriptorSet value() const { return m_handle; } - operator bool() const { return m_handle != VK_NULL_HANDLE; } - - VkDescriptorSet* ptr(); - void push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding); - void push(const VkDescriptorBufferInfo& buffer_info, VkDescriptorType type, u32 binding); - void push(const VkDescriptorImageInfo& image_info, VkDescriptorType type, u32 binding); - void push(const VkDescriptorImageInfo* image_info, u32 count, VkDescriptorType type, u32 binding); - void push(const rsx::simple_array& copy_cmd, u32 type_mask = umax); - void push(const rsx::simple_array& write_cmds, u32 type_mask = umax); - void push(const descriptor_set_dynamic_offset_t& offset); - - void on_bind(); - void bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point, VkPipelineLayout layout); - - void flush(); - - private: - VkDescriptorSet m_handle = VK_NULL_HANDLE; - u64 m_update_after_bind_mask = 0; - u64 m_push_type_mask = 0; - bool m_in_use = false; - - rsx::simple_array m_buffer_view_pool; - rsx::simple_array m_buffer_info_pool; - rsx::simple_array m_image_info_pool; - rsx::simple_array m_dynamic_offsets; - #if defined(__clang__) && (__clang_major__ < 16) // Clang (pre 16.x) does not support LWG 2089, std::construct_at for POD types struct WriteDescriptorSetT : public VkWriteDescriptorSet @@ -154,6 +117,67 @@ namespace vk using WriteDescriptorSetT = VkWriteDescriptorSet; #endif + public: + descriptor_set(VkDescriptorSet set); + descriptor_set() = default; + ~descriptor_set(); + + descriptor_set(const descriptor_set&) = delete; + + void swap(descriptor_set& other); + descriptor_set& operator = (VkDescriptorSet set); + + VkDescriptorSet value() const { return m_handle; } + operator bool() const { return m_handle != VK_NULL_HANDLE; } + + VkDescriptorSet* ptr(); + void push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding); + void push(const VkDescriptorBufferInfo& buffer_info, VkDescriptorType type, u32 binding); + void push(const VkDescriptorImageInfo& image_info, VkDescriptorType type, u32 binding); + void push(const VkDescriptorImageInfo* image_info, u32 count, VkDescriptorType type, u32 binding); + void push(const rsx::simple_array& copy_cmd, u32 type_mask = umax); + void push(const rsx::simple_array& write_cmds, u32 type_mask = umax); + void push(const descriptor_set_dynamic_offset_t& offset); + + // Event handlers + void on_bind(); + void bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point, VkPipelineLayout layout); + + void flush(); + + // Typed temporary storage access. Should be inline, the overhead is significant + FORCE_INLINE VkBufferView* store(const VkBufferView& buffer_view) + { + m_buffer_view_pool.push_back(buffer_view); + return &m_buffer_view_pool.back(); + } + + FORCE_INLINE VkDescriptorBufferInfo* store(const VkDescriptorBufferInfo& buffer_info) + { + m_buffer_info_pool.push_back(buffer_info); + return &m_buffer_info_pool.back(); + } + + FORCE_INLINE VkDescriptorImageInfo* store(const VkDescriptorImageInfo& image_info) + { + m_image_info_pool.push_back(image_info); + return &m_image_info_pool.back(); + } + + // Temporary storage accessor + const rsx::simple_array peek() const { return m_pending_writes; } + + private: + VkDescriptorSet m_handle = VK_NULL_HANDLE; + u64 m_update_after_bind_mask = 0; + u64 m_push_type_mask = 0; + bool m_in_use = false; + + rsx::simple_array m_buffer_view_pool; + rsx::simple_array m_buffer_info_pool; + rsx::simple_array m_image_info_pool; + rsx::simple_array m_dynamic_offsets; + rsx::simple_array m_pending_writes; rsx::simple_array m_pending_copies; };