vk: Refactor descriptor handling

This commit is contained in:
kd-11 2023-05-29 17:01:03 +03:00 committed by kd-11
parent 7e131f8fb6
commit c99ef4b09f
19 changed files with 239 additions and 177 deletions

View File

@ -5,10 +5,7 @@
namespace rsx namespace rsx
{ {
template <typename Ty> template <typename Ty> requires std::is_trivially_destructible_v<Ty>
concept is_simple_pod_v = (std::is_trivially_constructible_v<Ty>) && (std::is_trivially_destructible_v <Ty>);
template <typename Ty> requires is_simple_pod_v<Ty>
struct simple_array struct simple_array
{ {
public: public:

View File

@ -16,13 +16,13 @@ namespace vk
void compute_task::init_descriptors() void compute_task::init_descriptors()
{ {
std::vector<VkDescriptorPoolSize> descriptor_pool_sizes; rsx::simple_array<VkDescriptorPoolSize> descriptor_pool_sizes;
std::vector<VkDescriptorSetLayoutBinding> bindings; rsx::simple_array<VkDescriptorSetLayoutBinding> bindings;
const auto layout = get_descriptor_layout(); const auto layout = get_descriptor_layout();
for (const auto &e : layout) for (const auto &e : layout)
{ {
descriptor_pool_sizes.push_back({e.first, u32(VK_MAX_COMPUTE_TASKS * e.second)}); descriptor_pool_sizes.push_back({e.first, e.second});
for (unsigned n = 0; n < e.second; ++n) for (unsigned n = 0; n < e.second; ++n)
{ {
@ -38,7 +38,7 @@ namespace vk
} }
// Reserve descriptor pools // Reserve descriptor pools
m_descriptor_pool.create(*g_render_device, descriptor_pool_sizes.data(), ::size32(descriptor_pool_sizes), VK_MAX_COMPUTE_TASKS, 3); m_descriptor_pool.create(*g_render_device, descriptor_pool_sizes);
m_descriptor_layout = vk::descriptors::create_layout(bindings); m_descriptor_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {}; VkPipelineLayoutCreateInfo layout_info = {};
@ -146,7 +146,7 @@ namespace vk
ensure(m_used_descriptors < VK_MAX_COMPUTE_TASKS); ensure(m_used_descriptors < VK_MAX_COMPUTE_TASKS);
m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout, VK_TRUE, m_used_descriptors++); m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout, VK_TRUE);
bind_resources(); bind_resources();

View File

@ -993,7 +993,6 @@ void VKGSRender::end()
} }
// Allocate descriptor set // Allocate descriptor set
check_descriptors();
m_current_frame->descriptor_set = allocate_descriptor_set(); m_current_frame->descriptor_set = allocate_descriptor_set();
// Load program execution environment // Load program execution environment

View File

@ -395,9 +395,9 @@ namespace
std::tuple<VkPipelineLayout, VkDescriptorSetLayout> get_shared_pipeline_layout(VkDevice dev) std::tuple<VkPipelineLayout, VkDescriptorSetLayout> get_shared_pipeline_layout(VkDevice dev)
{ {
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table(); const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
std::vector<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings); rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings);
usz idx = 0; u32 idx = 0;
// Vertex stream, one stream for cacheable data, one stream for transient data // Vertex stream, one stream for cacheable data, one stream for transient data
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
@ -595,7 +595,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all); m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all);
//Precalculated stuff //Precalculated stuff
std::tie(pipeline_layout, descriptor_layouts) = get_shared_pipeline_layout(*m_device); std::tie(m_pipeline_layout, m_descriptor_layouts) = get_shared_pipeline_layout(*m_device);
//Occlusion //Occlusion
m_occlusion_query_manager = std::make_unique<vk::query_pool_manager>(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE); m_occlusion_query_manager = std::make_unique<vk::query_pool_manager>(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE);
@ -614,13 +614,16 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
const auto& binding_table = m_device->get_pipeline_binding_table(); const auto& binding_table = m_device->get_pipeline_binding_table();
const u32 num_fs_samplers = binding_table.vertex_textures_first_bind_slot - binding_table.textures_first_bind_slot; const u32 num_fs_samplers = binding_table.vertex_textures_first_bind_slot - binding_table.textures_first_bind_slot;
std::vector<VkDescriptorPoolSize> sizes; rsx::simple_array<VkDescriptorPoolSize> descriptor_type_sizes =
sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 6 * max_draw_calls }); {
sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 3 * max_draw_calls }); { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 6 },
sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , (num_fs_samplers + 4) * max_draw_calls }); { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 3 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , (num_fs_samplers + 4) },
// Conditional rendering predicate slot; refactor to allow skipping this when not needed // Conditional rendering predicate slot; refactor to allow skipping this when not needed
sizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 * max_draw_calls }); { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 }
};
m_descriptor_pool.create(*m_device, descriptor_type_sizes, max_draw_calls);
VkSemaphoreCreateInfo semaphore_info = {}; VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@ -665,7 +668,6 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
{ {
vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.present_wait_semaphore); vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.present_wait_semaphore);
vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.acquire_signal_semaphore); vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.acquire_signal_semaphore);
ctx.descriptor_pool.create(*m_device, sizes.data(), static_cast<u32>(sizes.size()), max_draw_calls, 1);
} }
const auto& memory_map = m_device->get_memory_mapping(); const auto& memory_map = m_device->get_memory_mapping();
@ -920,11 +922,12 @@ VKGSRender::~VKGSRender()
{ {
vkDestroySemaphore((*m_device), ctx.present_wait_semaphore, nullptr); vkDestroySemaphore((*m_device), ctx.present_wait_semaphore, nullptr);
vkDestroySemaphore((*m_device), ctx.acquire_signal_semaphore, nullptr); vkDestroySemaphore((*m_device), ctx.acquire_signal_semaphore, nullptr);
ctx.descriptor_pool.destroy();
ctx.buffer_views_to_clean.clear(); ctx.buffer_views_to_clean.clear();
} }
m_descriptor_pool.destroy();
// Textures // Textures
m_rtts.destroy(); m_rtts.destroy();
m_texture_cache.destroy(); m_texture_cache.destroy();
@ -935,8 +938,8 @@ VKGSRender::~VKGSRender()
m_text_writer.reset(); m_text_writer.reset();
//Pipeline descriptors //Pipeline descriptors
vkDestroyPipelineLayout(*m_device, pipeline_layout, nullptr); vkDestroyPipelineLayout(*m_device, m_pipeline_layout, nullptr);
vkDestroyDescriptorSetLayout(*m_device, descriptor_layouts, nullptr); vkDestroyDescriptorSetLayout(*m_device, m_descriptor_layouts, nullptr);
// Queries // Queries
m_occlusion_query_manager.reset(); m_occlusion_query_manager.reset();
@ -1318,22 +1321,11 @@ void VKGSRender::check_present_status()
} }
} }
void VKGSRender::check_descriptors()
{
// Ease resource pressure if the number of draw calls becomes too high or we are running low on memory resources
const auto required_descriptors = rsx::method_registers.current_draw_clause.pass_count();
if (!m_current_frame->descriptor_pool.can_allocate(required_descriptors, 0))
{
// Should hard sync before resetting descriptors for spec compliance
flush_command_queue(true);
}
}
VkDescriptorSet VKGSRender::allocate_descriptor_set() VkDescriptorSet VKGSRender::allocate_descriptor_set()
{ {
if (!m_shader_interpreter.is_interpreter(m_program)) [[likely]] if (!m_shader_interpreter.is_interpreter(m_program)) [[likely]]
{ {
return m_current_frame->descriptor_pool.allocate(descriptor_layouts, VK_TRUE, 0); return m_descriptor_pool.allocate(m_descriptor_layouts, VK_TRUE);
} }
else else
{ {
@ -1414,7 +1406,7 @@ void VKGSRender::on_init_thread()
if (!m_overlay_manager) if (!m_overlay_manager)
{ {
m_frame->hide(); m_frame->hide();
m_shaders_cache->load(nullptr, pipeline_layout); m_shaders_cache->load(nullptr, m_pipeline_layout);
m_frame->show(); m_frame->show();
} }
else else
@ -1422,7 +1414,7 @@ void VKGSRender::on_init_thread()
rsx::shader_loading_dialog_native dlg(this); rsx::shader_loading_dialog_native dlg(this);
// TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support // TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support
m_shaders_cache->load(&dlg, pipeline_layout); m_shaders_cache->load(&dlg, m_pipeline_layout);
} }
} }
@ -2009,7 +2001,7 @@ bool VKGSRender::load_program()
// Load current program from cache // Load current program from cache
std::tie(m_program, m_vertex_prog, m_fragment_prog) = m_prog_buffer->get_graphics_pipeline(vertex_program, fragment_program, m_pipeline_properties, std::tie(m_program, m_vertex_prog, m_fragment_prog) = m_prog_buffer->get_graphics_pipeline(vertex_program, fragment_program, m_pipeline_properties,
shadermode != shader_mode::recompiler, true, pipeline_layout); shadermode != shader_mode::recompiler, true, m_pipeline_layout);
vk::leave_uninterruptible(); vk::leave_uninterruptible();
@ -2268,7 +2260,7 @@ void VKGSRender::update_vertex_env(u32 id, const vk::vertex_upload_info& vertex_
data_size = 20; data_size = 20;
} }
vkCmdPushConstants(*m_current_command_buffer, pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, data_size, draw_info); vkCmdPushConstants(*m_current_command_buffer, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, data_size, draw_info);
const usz data_offset = (id * 128) + m_vertex_layout_stream_info.offset; const usz data_offset = (id * 128) + m_vertex_layout_stream_info.offset;
auto dst = m_vertex_layout_ring_info.map(data_offset, 128); auto dst = m_vertex_layout_ring_info.map(data_offset, 128);

View File

@ -120,8 +120,9 @@ private:
volatile vk::host_data_t* m_host_data_ptr = nullptr; volatile vk::host_data_t* m_host_data_ptr = nullptr;
std::unique_ptr<vk::buffer> m_host_object_data; std::unique_ptr<vk::buffer> m_host_object_data;
VkDescriptorSetLayout descriptor_layouts; vk::descriptor_pool m_descriptor_pool;
VkPipelineLayout pipeline_layout; VkDescriptorSetLayout m_descriptor_layouts;
VkPipelineLayout m_pipeline_layout;
vk::framebuffer_holder* m_draw_fbo = nullptr; vk::framebuffer_holder* m_draw_fbo = nullptr;
@ -229,7 +230,6 @@ private:
void check_heap_status(u32 flags = VK_HEAP_CHECK_ALL); void check_heap_status(u32 flags = VK_HEAP_CHECK_ALL);
void check_present_status(); void check_present_status();
void check_descriptors();
VkDescriptorSet allocate_descriptor_set(); VkDescriptorSet allocate_descriptor_set();
vk::vertex_upload_info upload_vertex_data(); vk::vertex_upload_info upload_vertex_data();

View File

@ -176,7 +176,6 @@ namespace vk
VkSemaphore present_wait_semaphore = VK_NULL_HANDLE; VkSemaphore present_wait_semaphore = VK_NULL_HANDLE;
vk::descriptor_set descriptor_set; vk::descriptor_set descriptor_set;
vk::descriptor_pool descriptor_pool;
rsx::flags32_t flags = 0; rsx::flags32_t flags = 0;
@ -185,7 +184,7 @@ namespace vk
u32 present_image = -1; u32 present_image = -1;
command_buffer_chunk* swap_command_buffer = nullptr; command_buffer_chunk* swap_command_buffer = nullptr;
//Heap pointers // Heap pointers
s64 attrib_heap_ptr = 0; s64 attrib_heap_ptr = 0;
s64 vtx_env_heap_ptr = 0; s64 vtx_env_heap_ptr = 0;
s64 frag_env_heap_ptr = 0; s64 frag_env_heap_ptr = 0;
@ -199,14 +198,12 @@ namespace vk
u64 last_frame_sync_time = 0; u64 last_frame_sync_time = 0;
//Copy shareable information // Copy shareable information
void grab_resources(frame_context_t& other) void grab_resources(frame_context_t& other)
{ {
present_wait_semaphore = other.present_wait_semaphore; present_wait_semaphore = other.present_wait_semaphore;
acquire_signal_semaphore = other.acquire_signal_semaphore; acquire_signal_semaphore = other.acquire_signal_semaphore;
descriptor_set.swap(other.descriptor_set); descriptor_set.swap(other.descriptor_set);
descriptor_pool = other.descriptor_pool;
used_descriptors = other.used_descriptors;
flags = other.flags; flags = other.flags;
attrib_heap_ptr = other.attrib_heap_ptr; attrib_heap_ptr = other.attrib_heap_ptr;
@ -221,7 +218,7 @@ namespace vk
rasterizer_env_heap_ptr = other.rasterizer_env_heap_ptr; rasterizer_env_heap_ptr = other.rasterizer_env_heap_ptr;
} }
//Exchange storage (non-copyable) // Exchange storage (non-copyable)
void swap_storage(frame_context_t& other) void swap_storage(frame_context_t& other)
{ {
std::swap(buffer_views_to_clean, other.buffer_views_to_clean); std::swap(buffer_views_to_clean, other.buffer_views_to_clean);

View File

@ -35,14 +35,6 @@ namespace vk
u64 g_num_processed_frames = 0; u64 g_num_processed_frames = 0;
u64 g_num_total_frames = 0; u64 g_num_total_frames = 0;
void reset_compute_tasks()
{
for (const auto &p : g_compute_tasks)
{
p.second->free_resources();
}
}
void reset_overlay_passes() void reset_overlay_passes()
{ {
for (const auto& p : g_overlay_passes) for (const auto& p : g_overlay_passes)
@ -53,7 +45,7 @@ namespace vk
void reset_global_resources() void reset_global_resources()
{ {
vk::reset_compute_tasks(); // FIXME: These two shouldn't exist
vk::reset_resolve_resources(); vk::reset_resolve_resources();
vk::reset_overlay_passes(); vk::reset_overlay_passes();

View File

@ -15,8 +15,6 @@
#include "util/fnv_hash.hpp" #include "util/fnv_hash.hpp"
#define VK_OVERLAY_MAX_DRAW_CALLS 1024
namespace vk namespace vk
{ {
overlay_pass::overlay_pass() overlay_pass::overlay_pass()
@ -49,26 +47,26 @@ namespace vk
void overlay_pass::init_descriptors() void overlay_pass::init_descriptors()
{ {
std::vector<VkDescriptorPoolSize> descriptor_pool_sizes = rsx::simple_array<VkDescriptorPoolSize> descriptor_pool_sizes =
{ {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_OVERLAY_MAX_DRAW_CALLS } { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 }
}; };
if (m_num_usable_samplers) if (m_num_usable_samplers)
{ {
descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_OVERLAY_MAX_DRAW_CALLS * m_num_usable_samplers }); descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_num_usable_samplers });
} }
if (m_num_input_attachments) if (m_num_input_attachments)
{ {
descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, VK_OVERLAY_MAX_DRAW_CALLS * m_num_input_attachments }); descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, m_num_input_attachments });
} }
// Reserve descriptor pools // Reserve descriptor pools
m_descriptor_pool.create(*m_device, descriptor_pool_sizes.data(), ::size32(descriptor_pool_sizes), VK_OVERLAY_MAX_DRAW_CALLS, 2); m_descriptor_pool.create(*m_device, descriptor_pool_sizes);
const auto num_bindings = 1 + m_num_usable_samplers + m_num_input_attachments; const auto num_bindings = 1 + m_num_usable_samplers + m_num_input_attachments;
std::vector<VkDescriptorSetLayoutBinding> bindings(num_bindings); rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(num_bindings);
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[0].descriptorCount = 1; bindings[0].descriptorCount = 1;
@ -222,16 +220,7 @@ namespace vk
else else
program = build_pipeline(key, pass); program = build_pipeline(key, pass);
ensure(m_used_descriptors < VK_OVERLAY_MAX_DRAW_CALLS); m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.descriptorPool = m_descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &m_descriptor_layout;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
CHECK_RESULT(vkAllocateDescriptorSets(*m_device, &alloc_info, m_descriptor_set.ptr()));
m_used_descriptors++;
if (!m_sampler && !src.empty()) if (!m_sampler && !src.empty())
{ {
@ -288,6 +277,7 @@ namespace vk
void overlay_pass::free_resources() void overlay_pass::free_resources()
{ {
// FIXME: Allocation sizes are known, we don't need to use a data_heap structure
m_vao.reset_allocation_stats(); m_vao.reset_allocation_stats();
m_ubo.reset_allocation_stats(); m_ubo.reset_allocation_stats();
} }

View File

@ -199,11 +199,6 @@ void VKGSRender::frame_context_cleanup(vk::frame_context_t *ctx)
// Resource cleanup. // Resource cleanup.
// TODO: This is some outdated crap. // TODO: This is some outdated crap.
{ {
if (m_text_writer)
{
m_text_writer->reset_descriptors();
}
if (m_overlay_manager && m_overlay_manager->has_dirty()) if (m_overlay_manager && m_overlay_manager->has_dirty())
{ {
auto ui_renderer = vk::get_overlay_pass<vk::ui_overlay_renderer>(); auto ui_renderer = vk::get_overlay_pass<vk::ui_overlay_renderer>();

View File

@ -246,9 +246,6 @@ namespace vk
void reset_resolve_resources() void reset_resolve_resources()
{ {
for (auto &e : g_resolve_helpers) e.second->free_resources();
for (auto &e : g_unresolve_helpers) e.second->free_resources();
if (g_depth_resolver) g_depth_resolver->free_resources(); if (g_depth_resolver) g_depth_resolver->free_resources();
if (g_depth_unresolver) g_depth_unresolver->free_resources(); if (g_depth_unresolver) g_depth_unresolver->free_resources();
if (g_stencil_resolver) g_stencil_resolver->free_resources(); if (g_stencil_resolver) g_stencil_resolver->free_resources();

View File

@ -41,6 +41,11 @@ namespace vk
return &g_resource_manager; return &g_resource_manager;
} }
garbage_collector* get_gc()
{
return &g_resource_manager;
}
void resource_manager::trim() void resource_manager::trim()
{ {
// For any managed resources, try to keep the number of unused/idle resources as low as possible. // For any managed resources, try to keep the number of unused/idle resources as low as possible.

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "vkutils/image.h" #include "vkutils/image.h"
#include "vkutils/garbage_collector.h"
#include "vkutils/query_pool.hpp" #include "vkutils/query_pool.hpp"
#include "vkutils/sampler.h" #include "vkutils/sampler.h"
@ -16,42 +17,6 @@ namespace vk
u64 last_completed_event_id(); u64 last_completed_event_id();
void on_event_completed(u64 event_id, bool flush = false); void on_event_completed(u64 event_id, bool flush = false);
class disposable_t
{
void* ptr;
std::function<void(void*)> deleter;
disposable_t(void* ptr_, std::function<void(void*)> deleter_) :
ptr(ptr_), deleter(deleter_) {}
public:
disposable_t() = delete;
disposable_t(const disposable_t&) = delete;
disposable_t(disposable_t&& other):
ptr(std::exchange(other.ptr, nullptr)),
deleter(other.deleter)
{}
~disposable_t()
{
if (ptr)
{
deleter(ptr);
ptr = nullptr;
}
}
template <typename T>
static disposable_t make(T* raw)
{
return disposable_t(raw, [](void *raw)
{
delete static_cast<T*>(raw);
});
}
};
struct eid_scope_t struct eid_scope_t
{ {
u64 eid; u64 eid;
@ -83,7 +48,7 @@ namespace vk
} }
}; };
class resource_manager class resource_manager : public garbage_collector
{ {
private: private:
sampler_pool_t m_sampler_pool; sampler_pool_t m_sampler_pool;
@ -151,7 +116,7 @@ namespace vk
return ret; return ret;
} }
inline void dispose(vk::disposable_t& disposable) void dispose(vk::disposable_t& disposable) override
{ {
get_current_eid_scope().m_disposables.emplace_back(std::move(disposable)); get_current_eid_scope().m_disposables.emplace_back(std::move(disposable));
} }

View File

@ -233,7 +233,7 @@ namespace vk
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev) std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev)
{ {
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table(); const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
std::vector<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings); rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings);
u32 idx = 0; u32 idx = 0;
@ -378,13 +378,15 @@ namespace vk
{ {
const auto max_draw_calls = dev.get_descriptor_max_draw_calls(); const auto max_draw_calls = dev.get_descriptor_max_draw_calls();
std::vector<VkDescriptorPoolSize> sizes; rsx::simple_array<VkDescriptorPoolSize> sizes =
sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 6 * max_draw_calls }); {
sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 3 * max_draw_calls }); { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 6 },
sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 68 * max_draw_calls }); { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 3 },
sizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 * max_draw_calls }); { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 68 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 }
};
m_descriptor_pool.create(dev, sizes.data(), ::size32(sizes), max_draw_calls, 2); m_descriptor_pool.create(dev, sizes, max_draw_calls);
} }
void shader_interpreter::init(const vk::render_device& dev) void shader_interpreter::init(const vk::render_device& dev)
@ -518,7 +520,7 @@ namespace vk
VkDescriptorSet shader_interpreter::allocate_descriptor_set() VkDescriptorSet shader_interpreter::allocate_descriptor_set()
{ {
return m_descriptor_pool.allocate(m_shared_descriptor_layout, VK_TRUE, 0); return m_descriptor_pool.allocate(m_shared_descriptor_layout);
} }
glsl::program* shader_interpreter::get(const vk::pipeline_props& properties, const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata) glsl::program* shader_interpreter::get(const vk::pipeline_props& properties, const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata)

View File

@ -40,16 +40,16 @@ namespace vk
void init_descriptor_set(vk::render_device &dev) void init_descriptor_set(vk::render_device &dev)
{ {
VkDescriptorPoolSize descriptor_pools[1] = rsx::simple_array<VkDescriptorPoolSize> descriptor_pools =
{ {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 120 }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 },
}; };
// Reserve descriptor pools // Reserve descriptor pools
m_descriptor_pool.create(dev, descriptor_pools, 1, 120, 2); m_descriptor_pool.create(dev, descriptor_pools);
// Scale and offset data plus output color // Scale and offset data plus output color
std::vector<VkDescriptorSetLayoutBinding> bindings = rsx::simple_array<VkDescriptorSetLayoutBinding> bindings =
{ {
{ {
.binding = 0, .binding = 0,
@ -205,7 +205,7 @@ namespace vk
{ {
ensure(m_used_descriptors < 120); ensure(m_used_descriptors < 120);
m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout, VK_TRUE, m_used_descriptors++); m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout);
float scale[] = { scale_x, scale_y }; float scale[] = { scale_x, scale_y };
float colors[] = { color[0], color[1], color[2], color[3] }; float colors[] = { color[0], color[1], color[2], color[3] };

View File

@ -1,5 +1,6 @@
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
#include "descriptors.h" #include "descriptors.h"
#include "garbage_collector.h"
namespace vk namespace vk
{ {
@ -63,7 +64,7 @@ namespace vk
g_fxo->get<dispatch_manager>().flush_all(); g_fxo->get<dispatch_manager>().flush_all();
} }
VkDescriptorSetLayout create_layout(const std::vector<VkDescriptorSetLayoutBinding>& bindings) VkDescriptorSetLayout create_layout(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings)
{ {
VkDescriptorSetLayoutCreateInfo infos = {}; VkDescriptorSetLayoutCreateInfo infos = {};
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
@ -105,65 +106,56 @@ namespace vk
} }
} }
void descriptor_pool::create(const vk::render_device& dev, VkDescriptorPoolSize* sizes, u32 size_descriptors_count, u32 max_sets, u8 subpool_count) void descriptor_pool::create(const vk::render_device& dev, const rsx::simple_array<VkDescriptorPoolSize>& pool_sizes, u32 max_sets)
{ {
ensure(subpool_count); ensure(max_sets > 16);
auto scaled_pool_sizes = pool_sizes;
for (auto& size : scaled_pool_sizes)
{
ensure(size.descriptorCount < 32); // Sanity check. Remove before commit.
size.descriptorCount *= max_sets;
}
info.flags = dev.get_descriptor_update_after_bind_support() ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT : 0; info.flags = dev.get_descriptor_update_after_bind_support() ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT : 0;
info.maxSets = max_sets; info.maxSets = max_sets;
info.poolSizeCount = size_descriptors_count; info.poolSizeCount = scaled_pool_sizes.size();
info.pPoolSizes = sizes; info.pPoolSizes = scaled_pool_sizes.data();
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
m_owner = &dev; m_owner = &dev;
m_device_pools.resize(subpool_count); next_subpool();
for (auto& pool : m_device_pools)
{
CHECK_RESULT(vkCreateDescriptorPool(dev, &info, nullptr, &pool));
}
m_current_pool_handle = m_device_pools[0];
} }
void descriptor_pool::destroy() void descriptor_pool::destroy()
{ {
if (m_device_pools.empty()) return; if (m_device_subpools.empty()) return;
for (auto& pool : m_device_pools) for (auto& pool : m_device_subpools)
{ {
vkDestroyDescriptorPool((*m_owner), pool, nullptr); vkDestroyDescriptorPool((*m_owner), pool.handle, nullptr);
pool = VK_NULL_HANDLE; pool.handle = VK_NULL_HANDLE;
} }
m_owner = nullptr; m_owner = nullptr;
} }
void descriptor_pool::reset(VkDescriptorPoolResetFlags flags) void descriptor_pool::reset(u32 subpool_id, VkDescriptorPoolResetFlags flags)
{ {
m_descriptor_set_cache.clear(); std::lock_guard lock(m_subpool_lock);
m_current_pool_index = (m_current_pool_index + 1) % u32(m_device_pools.size());
m_current_pool_handle = m_device_pools[m_current_pool_index]; CHECK_RESULT(vkResetDescriptorPool(*m_owner, m_device_subpools[subpool_id].handle, flags));
CHECK_RESULT(vkResetDescriptorPool(*m_owner, m_current_pool_handle, flags)); m_device_subpools[subpool_id].busy = VK_FALSE;
} }
VkDescriptorSet descriptor_pool::allocate(VkDescriptorSetLayout layout, VkBool32 use_cache, u32 used_count) VkDescriptorSet descriptor_pool::allocate(VkDescriptorSetLayout layout, VkBool32 use_cache)
{ {
if (use_cache) if (use_cache)
{ {
if (m_descriptor_set_cache.empty()) if (m_descriptor_set_cache.empty())
{ {
// For optimal cache utilization, each pool should only allocate one layout // For optimal cache utilization, each pool should only allocate one layout
if (m_cached_layout != layout) m_cached_layout = layout;
{
m_cached_layout = layout;
m_allocation_request_cache.resize(max_cache_size);
for (auto& layout_ : m_allocation_request_cache)
{
layout_ = m_cached_layout;
}
}
} }
else if (m_cached_layout != layout) else if (m_cached_layout != layout)
{ {
@ -175,6 +167,11 @@ namespace vk
} }
} }
if (!can_allocate(use_cache ? 4 : 1, m_current_subpool_offset))
{
next_subpool();
}
VkDescriptorSet new_descriptor_set; VkDescriptorSet new_descriptor_set;
VkDescriptorSetAllocateInfo alloc_info = {}; VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
@ -184,8 +181,12 @@ namespace vk
if (use_cache) if (use_cache)
{ {
ensure(used_count < info.maxSets); const auto alloc_size = std::min<u32>(info.maxSets - m_current_subpool_offset, max_cache_size);
const auto alloc_size = std::min<u32>(info.maxSets - used_count, 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()); ensure(m_descriptor_set_cache.empty());
alloc_info.descriptorSetCount = alloc_size; alloc_info.descriptorSetCount = alloc_size;
@ -204,6 +205,51 @@ namespace vk
return 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<gc_wrapper_t>(release_func);
vk::get_gc()->dispose(cleanup_obj);
}
std::lock_guard lock(m_subpool_lock);
m_current_subpool_offset = 0;
m_current_subpool_index = umax;
for (u32 index = 0; index < m_device_subpools.size(); ++index)
{
if (!m_device_subpools[index].busy)
{
m_current_subpool_index = index;
break;
}
}
if (m_current_subpool_index == umax)
{
VkDescriptorPool subpool = VK_NULL_HANDLE;
CHECK_RESULT(vkCreateDescriptorPool(*m_owner, &info, nullptr, &subpool));
m_device_subpools.push_back(
{
.handle = subpool,
.busy = VK_FALSE
});
m_current_subpool_index = m_device_subpools.size() - 1;
}
m_device_subpools[m_current_subpool_index].busy = VK_TRUE;
}
descriptor_set::descriptor_set(VkDescriptorSet set) descriptor_set::descriptor_set(VkDescriptorSet set)
{ {
flush(); flush();

View File

@ -10,30 +10,55 @@
namespace vk namespace vk
{ {
struct gc_wrapper_t
{
std::function<void()> m_callback;
gc_wrapper_t(std::function<void()> callback)
: m_callback(callback)
{}
~gc_wrapper_t()
{
m_callback();
}
};
class descriptor_pool class descriptor_pool
{ {
public: public:
descriptor_pool() = default; descriptor_pool() = default;
~descriptor_pool() = default; ~descriptor_pool() = default;
void create(const vk::render_device& dev, VkDescriptorPoolSize* sizes, u32 size_descriptors_count, u32 max_sets, u8 subpool_count); void create(const vk::render_device& dev, const rsx::simple_array<VkDescriptorPoolSize>& pool_sizes, u32 max_sets = 1024);
void destroy(); void destroy();
void reset(VkDescriptorPoolResetFlags flags);
VkDescriptorSet allocate(VkDescriptorSetLayout layout, VkBool32 use_cache, u32 used_count); VkDescriptorSet allocate(VkDescriptorSetLayout layout, VkBool32 use_cache = VK_TRUE);
operator VkDescriptorPool() { return m_current_pool_handle; } operator VkDescriptorPool() { return m_current_pool_handle; }
FORCE_INLINE bool valid() const { return (!m_device_pools.empty()); } FORCE_INLINE bool valid() const { return (!m_device_subpools.empty()); }
FORCE_INLINE u32 max_sets() const { return info.maxSets; } FORCE_INLINE u32 max_sets() const { return info.maxSets; }
FORCE_INLINE bool can_allocate(u32 required_count, u32 used_count) const { return (used_count + required_count) <= info.maxSets; };
private: private:
FORCE_INLINE bool can_allocate(u32 required_count, u32 already_used_count = 0) const { return (required_count + already_used_count) <= info.maxSets; };
void reset(u32 subpool_id, VkDescriptorPoolResetFlags flags);
void next_subpool();
struct logical_subpool_t
{
VkDescriptorPool handle;
VkBool32 busy;
};
const vk::render_device* m_owner = nullptr; const vk::render_device* m_owner = nullptr;
VkDescriptorPoolCreateInfo info = {}; VkDescriptorPoolCreateInfo info = {};
rsx::simple_array<VkDescriptorPool> m_device_pools; rsx::simple_array<logical_subpool_t> m_device_subpools;
VkDescriptorPool m_current_pool_handle = VK_NULL_HANDLE; VkDescriptorPool m_current_pool_handle = VK_NULL_HANDLE;
u32 m_current_pool_index = 0; u32 m_current_subpool_index = umax;
u32 m_current_subpool_offset = 0;
shared_mutex m_subpool_lock;
static constexpr size_t max_cache_size = 64; static constexpr size_t max_cache_size = 64;
VkDescriptorSetLayout m_cached_layout = VK_NULL_HANDLE; VkDescriptorSetLayout m_cached_layout = VK_NULL_HANDLE;
@ -122,6 +147,6 @@ namespace vk
void init(); void init();
void flush(); void flush();
VkDescriptorSetLayout create_layout(const std::vector<VkDescriptorSetLayoutBinding>& bindings); VkDescriptorSetLayout create_layout(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings);
} }
} }

View File

@ -0,0 +1,56 @@
#include <util/types.hpp>
#include <functional>
namespace vk
{
class disposable_t
{
void* ptr;
std::function<void(void*)> deleter;
disposable_t(void* ptr_, std::function<void(void*)> deleter_) :
ptr(ptr_), deleter(deleter_) {}
public:
disposable_t() = delete;
disposable_t(const disposable_t&) = delete;
disposable_t(disposable_t&& other) :
ptr(std::exchange(other.ptr, nullptr)),
deleter(other.deleter)
{}
~disposable_t()
{
if (ptr)
{
deleter(ptr);
ptr = nullptr;
}
}
template <typename T>
static disposable_t make(T* raw)
{
return disposable_t(raw, [](void* raw)
{
delete static_cast<T*>(raw);
});
}
};
struct garbage_collector
{
virtual void dispose(vk::disposable_t& object) = 0;
template<typename T>
void dispose(std::unique_ptr<T>& object)
{
auto ptr = vk::disposable_t::make(object.release());
dispose(ptr);
}
};
garbage_collector* get_gc();
}

View File

@ -45,6 +45,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\descriptors.h" /> <ClInclude Include="Emu\RSX\VK\vkutils\descriptors.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\barriers.h" /> <ClInclude Include="Emu\RSX\VK\vkutils\barriers.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\framebuffer_object.hpp" /> <ClInclude Include="Emu\RSX\VK\vkutils\framebuffer_object.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\garbage_collector.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\image.h" /> <ClInclude Include="Emu\RSX\VK\vkutils\image.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\image_helpers.h" /> <ClInclude Include="Emu\RSX\VK\vkutils\image_helpers.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\scratch.h" /> <ClInclude Include="Emu\RSX\VK\vkutils\scratch.h" />

View File

@ -171,6 +171,9 @@
<ClInclude Include="Emu\RSX\VK\upscalers\nearest_pass.hpp"> <ClInclude Include="Emu\RSX\VK\upscalers\nearest_pass.hpp">
<Filter>upscalers</Filter> <Filter>upscalers</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\garbage_collector.h">
<Filter>vkutils</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="vkutils"> <Filter Include="vkutils">