#include "Emu/IdManager.h" #include "descriptors.h" #include "garbage_collector.h" namespace vk { // Error handler callback extern void on_descriptor_pool_fragmentation(bool fatal); namespace descriptors { class dispatch_manager { public: inline void flush_all() { for (auto& set : m_notification_list) { set->flush(); } } void register_(descriptor_set* set) { // Rare event, upon creation of a new set tracker. // Check for spurious 'new' events when the aux context is taking over for (const auto& set_ : m_notification_list) { if (set_ == set) return; } m_notification_list.push_back(set); rsx_log.warning("[descriptor_manager::register] Now monitoring %u descriptor sets", m_notification_list.size()); } void deregister(descriptor_set* set) { for (auto it = m_notification_list.begin(); it != m_notification_list.end(); ++it) { if (*it == set) { *it = m_notification_list.back(); m_notification_list.pop_back(); break; } } rsx_log.warning("[descriptor_manager::deregister] Now monitoring %u descriptor sets", m_notification_list.size()); } dispatch_manager() = default; private: rsx::simple_array m_notification_list; dispatch_manager(const dispatch_manager&) = delete; dispatch_manager& operator = (const dispatch_manager&) = delete; }; void init() { g_fxo->init(); } void flush() { g_fxo->get().flush_all(); } VkDescriptorSetLayout create_layout(const rsx::simple_array& bindings) { VkDescriptorSetLayoutCreateInfo infos = {}; infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; infos.pBindings = bindings.data(); infos.bindingCount = ::size32(bindings); VkDescriptorSetLayoutBindingFlagsCreateInfo binding_infos = {}; rsx::simple_array binding_flags; if (g_render_device->get_descriptor_indexing_support()) { const auto deferred_mask = g_render_device->get_descriptor_update_after_bind_support(); binding_flags.resize(::size32(bindings)); for (u32 i = 0; i < binding_flags.size(); ++i) { if ((1ull << bindings[i].descriptorType) & ~deferred_mask) { binding_flags[i] = 0u; } else { binding_flags[i] = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT; } } binding_infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT; binding_infos.pNext = nullptr; binding_infos.bindingCount = ::size32(binding_flags); binding_infos.pBindingFlags = binding_flags.data(); infos.pNext = &binding_infos; infos.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT; } VkDescriptorSetLayout result; CHECK_RESULT(vkCreateDescriptorSetLayout(*g_render_device, &infos, nullptr, &result)); return result; } } void descriptor_pool::create(const vk::render_device& dev, const rsx::simple_array& pool_sizes, u32 max_sets) { ensure(max_sets > 16); m_create_info_pool_sizes = pool_sizes; for (auto& size : m_create_info_pool_sizes) { ensure(size.descriptorCount < 128); // Sanity check. Remove before commit. size.descriptorCount *= max_sets; } m_create_info.flags = dev.get_descriptor_update_after_bind_support() ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT : 0; m_create_info.maxSets = max_sets; m_create_info.poolSizeCount = m_create_info_pool_sizes.size(); m_create_info.pPoolSizes = m_create_info_pool_sizes.data(); m_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; m_owner = &dev; next_subpool(); } void descriptor_pool::destroy() { if (m_device_subpools.empty()) return; for (auto& pool : m_device_subpools) { vkDestroyDescriptorPool((*m_owner), pool.handle, nullptr); pool.handle = VK_NULL_HANDLE; } m_owner = nullptr; } void descriptor_pool::reset(u32 subpool_id, VkDescriptorPoolResetFlags flags) { std::lock_guard lock(m_subpool_lock); CHECK_RESULT(vkResetDescriptorPool(*m_owner, m_device_subpools[subpool_id].handle, flags)); m_device_subpools[subpool_id].busy = VK_FALSE; } VkDescriptorSet descriptor_pool::allocate(VkDescriptorSetLayout layout, VkBool32 use_cache) { if (use_cache) { if (m_descriptor_set_cache.empty()) { // For optimal cache utilization, each pool should only allocate one layout m_cached_layout = layout; } else if (m_cached_layout != layout) { use_cache = VK_FALSE; } else { return m_descriptor_set_cache.pop_back(); } } if (!can_allocate(use_cache ? 4 : 1, m_current_subpool_offset)) { next_subpool(); } VkDescriptorSet new_descriptor_set; VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = m_current_pool_handle; alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &layout; if (use_cache) { const auto alloc_size = std::min(m_create_info.maxSets - m_current_subpool_offset, max_cache_size); m_allocation_request_cache.resize(alloc_size); for (auto& layout_ : m_allocation_request_cache) { layout_ = m_cached_layout; } ensure(m_descriptor_set_cache.empty()); alloc_info.descriptorSetCount = alloc_size; alloc_info.pSetLayouts = m_allocation_request_cache.data(); m_descriptor_set_cache.resize(alloc_size); CHECK_RESULT(vkAllocateDescriptorSets(*m_owner, &alloc_info, m_descriptor_set_cache.data())); m_current_subpool_offset += alloc_size; new_descriptor_set = m_descriptor_set_cache.pop_back(); } else { m_current_subpool_offset++; CHECK_RESULT(vkAllocateDescriptorSets(*m_owner, &alloc_info, &new_descriptor_set)); } return new_descriptor_set; } void descriptor_pool::next_subpool() { if (m_current_subpool_index != umax) { // Enqueue release using gc auto release_func = [subpool_index=m_current_subpool_index, this]() { this->reset(subpool_index, 0); }; auto cleanup_obj = std::make_unique(release_func); vk::get_gc()->dispose(cleanup_obj); } m_current_subpool_offset = 0; m_current_subpool_index = umax; const int max_retries = 2; int retries = max_retries; do { for (u32 index = 0; index < m_device_subpools.size(); ++index) { if (!m_device_subpools[index].busy) { m_current_subpool_index = index; goto done; // Nested break } } VkDescriptorPool subpool = VK_NULL_HANDLE; if (VkResult result = vkCreateDescriptorPool(*m_owner, &m_create_info, nullptr, &subpool)) { if (retries-- && (result == VK_ERROR_FRAGMENTATION_EXT)) { rsx_log.warning("Descriptor pool creation failed with fragmentation error. Will attempt to recover."); vk::on_descriptor_pool_fragmentation(!retries); continue; } vk::die_with_error(result); fmt::throw_exception("Unreachable"); } // New subpool created successfully std::lock_guard lock(m_subpool_lock); m_device_subpools.push_back( { .handle = subpool, .busy = VK_FALSE }); m_current_subpool_index = m_device_subpools.size() - 1; } while (m_current_subpool_index == umax); done: m_device_subpools[m_current_subpool_index].busy = VK_TRUE; m_current_pool_handle = m_device_subpools[m_current_subpool_index].handle; } descriptor_set::descriptor_set(VkDescriptorSet set) { flush(); m_handle = set; } descriptor_set::~descriptor_set() { if (m_update_after_bind_mask) { g_fxo->get().deregister(this); } } void descriptor_set::init(VkDescriptorSet new_set) { if (!m_in_use) [[unlikely]] { m_image_info_pool.reserve(m_pool_size); m_buffer_view_pool.reserve(m_pool_size); m_buffer_info_pool.reserve(m_pool_size); m_in_use = true; m_update_after_bind_mask = g_render_device->get_descriptor_update_after_bind_support(); if (m_update_after_bind_mask) { g_fxo->get().register_(this); } } else if (m_push_type_mask & ~m_update_after_bind_mask) { flush(); } m_handle = new_set; } void descriptor_set::swap(descriptor_set& other) { const auto other_handle = other.m_handle; other.flush(); other.m_handle = m_handle; init(other_handle); } descriptor_set& descriptor_set::operator = (VkDescriptorSet set) { init(set); return *this; } VkDescriptorSet* descriptor_set::ptr() { if (!m_in_use) [[likely]] { init(m_handle); } return &m_handle; } VkDescriptorSet descriptor_set::value() const { return m_handle; } void descriptor_set::push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding) { m_push_type_mask |= (1ull << type); m_buffer_view_pool.push_back(buffer_view); m_pending_writes.emplace_back( VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType nullptr, // pNext m_handle, // dstSet binding, // dstBinding 0, // dstArrayElement 1, // descriptorCount type, // descriptorType nullptr, // pImageInfo nullptr, // pBufferInfo &m_buffer_view_pool.back() // pTexelBufferView ); } void descriptor_set::push(const VkDescriptorBufferInfo& buffer_info, VkDescriptorType type, u32 binding) { m_push_type_mask |= (1ull << type); m_buffer_info_pool.push_back(buffer_info); m_pending_writes.emplace_back( VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType nullptr, // pNext m_handle, // dstSet binding, // dstBinding 0, // dstArrayElement 1, // descriptorCount type, // descriptorType nullptr, // pImageInfo &m_buffer_info_pool.back(), // pBufferInfo nullptr // pTexelBufferView ); } void descriptor_set::push(const VkDescriptorImageInfo& image_info, VkDescriptorType type, u32 binding) { m_push_type_mask |= (1ull << type); m_image_info_pool.push_back(image_info); m_pending_writes.emplace_back( VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType nullptr, // pNext m_handle, // dstSet binding, // dstBinding 0, // dstArrayElement 1, // descriptorCount type, // descriptorType &m_image_info_pool.back(), // pImageInfo nullptr, // pBufferInfo nullptr // pTexelBufferView ); } void descriptor_set::push(const VkDescriptorImageInfo* image_info, u32 count, VkDescriptorType type, u32 binding) { VkWriteDescriptorSet writer = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType nullptr, // pNext m_handle, // dstSet binding, // dstBinding 0, // dstArrayElement count, // descriptorCount type, // descriptorType image_info, // pImageInfo nullptr, // pBufferInfo nullptr // pTexelBufferView }; vkUpdateDescriptorSets(*g_render_device, 1, &writer, 0, nullptr); } void descriptor_set::push(rsx::simple_array& copy_cmd, u32 type_mask) { m_push_type_mask |= type_mask; if (m_pending_copies.empty()) [[likely]] { m_pending_copies = std::move(copy_cmd); } else { const auto old_size = m_pending_copies.size(); const auto new_size = copy_cmd.size() + old_size; m_pending_copies.resize(new_size); std::copy(copy_cmd.begin(), copy_cmd.end(), m_pending_copies.begin() + old_size); } } void descriptor_set::bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point, VkPipelineLayout layout) { if ((m_push_type_mask & ~m_update_after_bind_mask) || (m_pending_writes.size() >= max_cache_size)) { flush(); } vkCmdBindDescriptorSets(cmd, bind_point, layout, 0, 1, &m_handle, 0, nullptr); } void descriptor_set::flush() { if (!m_push_type_mask) { return; } const auto num_writes = ::size32(m_pending_writes); const auto num_copies = ::size32(m_pending_copies); vkUpdateDescriptorSets(*g_render_device, num_writes, m_pending_writes.data(), num_copies, m_pending_copies.data()); m_push_type_mask = 0; m_pending_writes.clear(); m_pending_copies.clear(); m_image_info_pool.clear(); m_buffer_info_pool.clear(); m_buffer_view_pool.clear(); } }