From 49729086ac3f53b2493781a4bb7bb8020a93c405 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 8 Jun 2025 21:21:51 +0300 Subject: [PATCH] vk: Move descriptor management to the pipeline layer - Frees up callers from managing descriptors themselves (ewww) - Makes descriptor reuse possible - Opens up the door to techniques like descriptor_buffer by abstracting away management to an implementation detail --- rpcs3/Emu/RSX/VK/VKCompute.cpp | 118 +++----- rpcs3/Emu/RSX/VK/VKCompute.h | 26 +- rpcs3/Emu/RSX/VK/VKDraw.cpp | 38 +-- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 65 ++-- rpcs3/Emu/RSX/VK/VKGSRender.h | 7 - rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp | 3 - rpcs3/Emu/RSX/VK/VKOverlays.cpp | 212 +++++-------- rpcs3/Emu/RSX/VK/VKOverlays.h | 26 +- rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp | 63 ++-- rpcs3/Emu/RSX/VK/VKPipelineCompiler.h | 38 ++- rpcs3/Emu/RSX/VK/VKProgramBuffer.h | 9 +- rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp | 296 ++++++++++++++++--- rpcs3/Emu/RSX/VK/VKProgramPipeline.h | 134 +++++++-- rpcs3/Emu/RSX/VK/VKRenderTargets.h | 2 +- rpcs3/Emu/RSX/VK/VKResolveHelper.h | 74 +++-- rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp | 46 +-- rpcs3/Emu/RSX/VK/VKShaderInterpreter.h | 10 +- rpcs3/Emu/RSX/VK/upscalers/fsr1/fsr_pass.cpp | 46 ++- rpcs3/Emu/RSX/VK/upscalers/fsr_pass.h | 3 +- 19 files changed, 677 insertions(+), 539 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKCompute.cpp b/rpcs3/Emu/RSX/VK/VKCompute.cpp index 637642d8bf..ae36723b81 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.cpp +++ b/rpcs3/Emu/RSX/VK/VKCompute.cpp @@ -8,64 +8,41 @@ namespace vk { - std::vector> compute_task::get_descriptor_layout() + std::vector compute_task::get_inputs() { - std::vector> result; - result.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, ssbo_count); + std::vector result; + for (unsigned i = 0; i < ssbo_count; ++i) + { + const auto input = glsl::program_input::make + ( + ::glsl::glsl_compute_program, + "ssbo" + std::to_string(i), + glsl::program_input_type::input_type_storage_buffer, + i + ); + result.push_back(input); + } + + if (use_push_constants && push_constants_size > 0) + { + const auto input = glsl::program_input::make + ( + ::glsl::glsl_compute_program, + "push_constants", + glsl::program_input_type::input_type_push_constant, + 0, + glsl::push_constant_ref{ .offset = 0, .size = push_constants_size } + ); + result.push_back(input); + } + return result; } - void compute_task::init_descriptors() - { - rsx::simple_array descriptor_pool_sizes; - rsx::simple_array bindings; - - const auto layout = get_descriptor_layout(); - for (const auto &e : layout) - { - descriptor_pool_sizes.push_back({e.first, e.second}); - - for (unsigned n = 0; n < e.second; ++n) - { - bindings.push_back - ({ - u32(bindings.size()), - e.first, - 1, - VK_SHADER_STAGE_COMPUTE_BIT, - nullptr - }); - } - } - - // Reserve descriptor pools - m_descriptor_pool.create(*g_render_device, descriptor_pool_sizes); - m_descriptor_layout = vk::descriptors::create_layout(bindings); - - VkPipelineLayoutCreateInfo layout_info = {}; - layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_info.setLayoutCount = 1; - layout_info.pSetLayouts = &m_descriptor_layout; - - VkPushConstantRange push_constants{}; - if (use_push_constants) - { - push_constants.size = push_constants_size; - push_constants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - - layout_info.pushConstantRangeCount = 1; - layout_info.pPushConstantRanges = &push_constants; - } - - CHECK_RESULT(vkCreatePipelineLayout(*g_render_device, &layout_info, nullptr, &m_pipeline_layout)); - } - void compute_task::create() { if (!initialized) { - init_descriptors(); - switch (vk::get_driver_vendor()) { case vk::driver_vendor::unknown: @@ -121,10 +98,6 @@ namespace vk m_program.reset(); m_param_buffer.reset(); - vkDestroyDescriptorSetLayout(*g_render_device, m_descriptor_layout, nullptr); - vkDestroyPipelineLayout(*g_render_device, m_pipeline_layout, nullptr); - m_descriptor_pool.destroy(); - initialized = false; } } @@ -142,26 +115,23 @@ namespace vk shader_stage.module = handle; shader_stage.pName = "main"; - VkComputePipelineCreateInfo info{}; - info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - info.stage = shader_stage; - info.layout = m_pipeline_layout; - info.basePipelineIndex = -1; - info.basePipelineHandle = VK_NULL_HANDLE; + VkComputePipelineCreateInfo create_info + { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = handle, + .pName = "main" + }, + }; auto compiler = vk::get_pipe_compiler(); - m_program = compiler->compile(info, m_pipeline_layout, vk::pipe_compiler::COMPILE_INLINE); - declare_inputs(); + m_program = compiler->compile(create_info, vk::pipe_compiler::COMPILE_INLINE, {}, get_inputs()); } - ensure(m_used_descriptors < VK_MAX_COMPUTE_TASKS); - - m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout, VK_TRUE); - bind_resources(); - - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_program->pipeline); - m_descriptor_set.bind(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline_layout); + m_program->bind(cmd, VK_PIPELINE_BIND_POINT_COMPUTE); } void compute_task::run(const vk::command_buffer& cmd, u32 invocations_x, u32 invocations_y, u32 invocations_z) @@ -273,13 +243,13 @@ namespace vk void cs_shuffle_base::bind_resources() { - m_program->bind_buffer({ m_data->value, m_data_offset, m_data_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); + m_program->bind_buffer({ m_data->value, m_data_offset, m_data_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } void cs_shuffle_base::set_parameters(const vk::command_buffer& cmd, const u32* params, u8 count) { ensure(use_push_constants); - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, count * 4, params); + vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, count * 4, params); } void cs_shuffle_base::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_length, u32 data_offset) @@ -319,7 +289,7 @@ namespace vk void cs_interleave_task::bind_resources() { - m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); + m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } void cs_interleave_task::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset) @@ -379,8 +349,8 @@ namespace vk void cs_aggregator::bind_resources() { - m_program->bind_buffer({ src->value, 0, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); - m_program->bind_buffer({ dst->value, 0, 4 }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); + m_program->bind_buffer({ src->value, 0, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + m_program->bind_buffer({ dst->value, 0, 4 }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } void cs_aggregator::run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words) diff --git a/rpcs3/Emu/RSX/VK/VKCompute.h b/rpcs3/Emu/RSX/VK/VKCompute.h index 4f9a3f2a3a..d4e99d8cf3 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.h +++ b/rpcs3/Emu/RSX/VK/VKCompute.h @@ -19,12 +19,6 @@ namespace vk std::unique_ptr m_program; std::unique_ptr m_param_buffer; - vk::descriptor_pool m_descriptor_pool; - descriptor_set m_descriptor_set; - VkDescriptorSetLayout m_descriptor_layout = nullptr; - VkPipelineLayout m_pipeline_layout = nullptr; - u32 m_used_descriptors = 0; - bool initialized = false; bool unroll_loops = true; bool use_push_constants = false; @@ -37,15 +31,11 @@ namespace vk compute_task() = default; virtual ~compute_task() { destroy(); } - virtual std::vector> get_descriptor_layout(); - - void init_descriptors(); - void create(); void destroy(); + virtual std::vector get_inputs(); virtual void bind_resources() {} - virtual void declare_inputs() {} void load_program(const vk::command_buffer& cmd); @@ -354,7 +344,7 @@ namespace vk void bind_resources() override { - m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); + m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 src_offset, u32 src_length, u32 dst_offset) @@ -455,13 +445,13 @@ namespace vk void bind_resources() override { - m_program->bind_buffer({ src_buffer->value, in_offset, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); - m_program->bind_buffer({ dst_buffer->value, out_offset, block_length }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); + m_program->bind_buffer({ src_buffer->value, in_offset, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + m_program->bind_buffer({ dst_buffer->value, out_offset, block_length }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } void set_parameters(const vk::command_buffer& cmd) { - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, params.data); + vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, params.data); } void run(const vk::command_buffer& cmd, const vk::buffer* dst, u32 out_offset, const vk::buffer* src, u32 in_offset, u32 data_length, u32 width, u32 height, u32 depth, u32 mipmaps) override @@ -584,13 +574,13 @@ namespace vk void bind_resources() override { const auto op = static_cast(Op); - m_program->bind_buffer({ src_buffer->value, in_offset, in_block_length }, 0 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); - m_program->bind_buffer({ dst_buffer->value, out_offset, out_block_length }, 1 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set); + m_program->bind_buffer({ src_buffer->value, in_offset, in_block_length }, 0 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + m_program->bind_buffer({ dst_buffer->value, out_offset, out_block_length }, 1 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } void set_parameters(const vk::command_buffer& cmd) { - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, ¶ms); + vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, ¶ms); } void run(const vk::command_buffer& cmd, const RSX_detiler_config& config) diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 3b760f103f..4d1253340d 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -555,8 +555,7 @@ bool VKGSRender::bind_texture_env() { m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout }, i, - ::glsl::program_domain::glsl_fragment_program, - m_current_frame->descriptor_set); + ::glsl::program_domain::glsl_fragment_program); if (current_fragment_program.texture_state.redirected_textures & (1 << i)) { @@ -578,7 +577,6 @@ bool VKGSRender::bind_texture_env() m_program->bind_uniform({ m_stencil_mirror_sampler->value, stencil_view->value, stencil_view->image()->current_layout }, i, ::glsl::program_domain::glsl_fragment_program, - m_current_frame->descriptor_set, true); } } @@ -587,15 +585,13 @@ bool VKGSRender::bind_texture_env() const VkImageViewType view_type = vk::get_view_type(current_fragment_program.get_texture_dimension(i)); m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, i, - ::glsl::program_domain::glsl_fragment_program, - m_current_frame->descriptor_set); + ::glsl::program_domain::glsl_fragment_program); if (current_fragment_program.texture_state.redirected_textures & (1 << i)) { m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, i, ::glsl::program_domain::glsl_fragment_program, - m_current_frame->descriptor_set, true); } } @@ -611,8 +607,7 @@ bool VKGSRender::bind_texture_env() const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i)); m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, i, - ::glsl::program_domain::glsl_vertex_program, - m_current_frame->descriptor_set); + ::glsl::program_domain::glsl_vertex_program); continue; } @@ -635,8 +630,7 @@ bool VKGSRender::bind_texture_env() m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, i, - ::glsl::program_domain::glsl_vertex_program, - m_current_frame->descriptor_set); + ::glsl::program_domain::glsl_vertex_program); continue; } @@ -645,8 +639,7 @@ bool VKGSRender::bind_texture_env() m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout }, i, - ::glsl::program_domain::glsl_vertex_program, - m_current_frame->descriptor_set); + ::glsl::program_domain::glsl_vertex_program); } return out_of_memory; @@ -721,7 +714,7 @@ bool VKGSRender::bind_interpreter_texture_env() } } - m_shader_interpreter.update_fragment_textures(texture_env, m_current_frame->descriptor_set); + m_shader_interpreter.update_fragment_textures(texture_env); return out_of_memory; } @@ -850,6 +843,7 @@ void VKGSRender::emit_geometry(u32 sub_index) } else if (persistent_buffer != old_persistent_buffer || volatile_buffer != old_volatile_buffer) { + /* // Need to update descriptors; make a copy for the next draw VkDescriptorSet previous_set = m_current_frame->descriptor_set.value(); m_current_frame->descriptor_set.flush(); @@ -874,6 +868,8 @@ void VKGSRender::emit_geometry(u32 sub_index) m_current_frame->descriptor_set.push(copy_cmds); update_descriptors = true; + */ + fmt::throw_exception("Not implemented"); } // Update vertex fetch parameters @@ -882,9 +878,9 @@ void VKGSRender::emit_geometry(u32 sub_index) ensure(m_vertex_layout_storage); if (update_descriptors) { - m_program->bind_uniform(persistent_buffer, binding_table.vertex_buffers_first_bind_slot, m_current_frame->descriptor_set); - m_program->bind_uniform(volatile_buffer, binding_table.vertex_buffers_first_bind_slot + 1, m_current_frame->descriptor_set); - m_program->bind_uniform(m_vertex_layout_storage->value, binding_table.vertex_buffers_first_bind_slot + 2, m_current_frame->descriptor_set); + m_program->bind_uniform(persistent_buffer, binding_table.vertex_buffers_first_bind_slot); + m_program->bind_uniform(volatile_buffer, binding_table.vertex_buffers_first_bind_slot + 1); + m_program->bind_uniform(m_vertex_layout_storage->value, binding_table.vertex_buffers_first_bind_slot + 2); } bool reload_state = (!m_current_draw.subdraw_id++); @@ -908,10 +904,12 @@ void VKGSRender::emit_geometry(u32 sub_index) reload_state = true; }); + // Bind both pipe and descriptors in one go + // FIXME: We only need to rebind the pipeline when reload state is set. Flags? + m_program->bind(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS); + if (reload_state) { - vkCmdBindPipeline(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline); - update_draw_state(); begin_render_pass(); @@ -929,7 +927,6 @@ void VKGSRender::emit_geometry(u32 sub_index) } // Bind the new set of descriptors for use with this draw call - m_current_frame->descriptor_set.bind(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline_layout); m_frame_stats.setup_time += m_profiler.duration(); if (!upload_info.index_info) @@ -1083,9 +1080,6 @@ void VKGSRender::end() return; } - // Allocate descriptor set - m_current_frame->descriptor_set = allocate_descriptor_set(); - // Load program execution environment load_program_env(); m_frame_stats.setup_time += m_profiler.duration(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 107cd7b399..17f42f45e8 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -423,8 +423,8 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) std::vector& gpus = m_instance.enumerate_devices(); - //Actually confirm that the loader found at least one compatible device - //This should not happen unless something is wrong with the driver setup on the target system + // Actually confirm that the loader found at least one compatible device + // This should not happen unless something is wrong with the driver setup on the target system if (gpus.empty()) { //We can't throw in Emulator::Load, so we show error and return @@ -482,20 +482,16 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) swapchain_unavailable = true; } - //create command buffer... + // create command buffer... m_command_buffer_pool.create((*m_device), m_device->get_graphics_queue_family()); m_primary_cb_list.create(m_command_buffer_pool, vk::command_buffer::access_type_hint::flush_only); m_current_command_buffer = m_primary_cb_list.get(); m_current_command_buffer->begin(); - //Create secondary command_buffer for parallel operations + // Create secondary command_buffer for parallel operations m_secondary_command_buffer_pool.create((*m_device), m_device->get_graphics_queue_family()); m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all); - //Precalculated stuff - rsx::simple_array binding_layout; - std::tie(m_pipeline_layout, m_descriptor_layouts, binding_layout) = vk::get_common_pipeline_layout(*m_device); - //Occlusion m_occlusion_query_manager = std::make_unique(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE); m_occlusion_map.resize(rsx::reports::occlusion_query_count); @@ -508,11 +504,6 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) m_occlusion_query_manager->set_control_flags(VK_QUERY_CONTROL_PRECISE_BIT, 0); } - // Generate frame contexts - const u32 max_draw_calls = m_device->get_descriptor_max_draw_calls(); - const auto descriptor_type_sizes = vk::get_descriptor_pool_sizes(binding_layout); - m_descriptor_pool.create(*m_device, descriptor_type_sizes, max_draw_calls); - VkSemaphoreCreateInfo semaphore_info = {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -852,12 +843,6 @@ VKGSRender::~VKGSRender() m_stencil_mirror_sampler.reset(); - // Pipeline descriptors - m_descriptor_pool.destroy(); - - vkDestroyPipelineLayout(*m_device, m_pipeline_layout, nullptr); - vkDestroyDescriptorSetLayout(*m_device, m_descriptor_layouts, nullptr); - // Queries m_occlusion_query_manager.reset(); m_cond_render_buffer.reset(); @@ -1157,18 +1142,6 @@ void VKGSRender::check_present_status() } } -VkDescriptorSet VKGSRender::allocate_descriptor_set() -{ - if (!m_shader_interpreter.is_interpreter(m_program)) [[likely]] - { - return m_descriptor_pool.allocate(m_descriptor_layouts, VK_TRUE); - } - else - { - return m_shader_interpreter.allocate_descriptor_set(); - } -} - void VKGSRender::set_viewport() { const auto [clip_width, clip_height] = rsx::apply_resolution_scale( @@ -1242,7 +1215,7 @@ void VKGSRender::on_init_thread() if (!m_overlay_manager) { m_frame->hide(); - m_shaders_cache->load(nullptr, m_pipeline_layout); + m_shaders_cache->load(nullptr); m_frame->show(); } else @@ -1250,7 +1223,7 @@ void VKGSRender::on_init_thread() rsx::shader_loading_dialog_native dlg(this); // TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support - m_shaders_cache->load(&dlg, m_pipeline_layout); + m_shaders_cache->load(&dlg); } } @@ -1870,7 +1843,7 @@ bool VKGSRender::load_program() vertex_program, fragment_program, m_pipeline_properties, - shadermode != shader_mode::recompiler, true, m_pipeline_layout); + shadermode != shader_mode::recompiler, true); vk::leave_uninterruptible(); @@ -2103,32 +2076,32 @@ void VKGSRender::load_program_env() const auto& binding_table = m_device->get_pipeline_binding_table(); - m_program->bind_uniform(m_vertex_env_buffer_info, binding_table.vertex_params_bind_slot, m_current_frame->descriptor_set); - m_program->bind_buffer(m_vertex_constants_buffer_info, binding_table.vertex_constant_buffers_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set); - m_program->bind_uniform(m_fragment_env_buffer_info, binding_table.fragment_state_bind_slot, m_current_frame->descriptor_set); - m_program->bind_uniform(m_fragment_texture_params_buffer_info, binding_table.fragment_texture_params_bind_slot, m_current_frame->descriptor_set); - m_program->bind_uniform(m_raster_env_buffer_info, binding_table.rasterizer_env_bind_slot, m_current_frame->descriptor_set); + m_program->bind_uniform(m_vertex_env_buffer_info, binding_table.vertex_params_bind_slot); + m_program->bind_buffer(m_vertex_constants_buffer_info, binding_table.vertex_constant_buffers_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + m_program->bind_uniform(m_fragment_env_buffer_info, binding_table.fragment_state_bind_slot); + m_program->bind_uniform(m_fragment_texture_params_buffer_info, binding_table.fragment_texture_params_bind_slot); + m_program->bind_uniform(m_raster_env_buffer_info, binding_table.rasterizer_env_bind_slot); if (!m_shader_interpreter.is_interpreter(m_program)) { - m_program->bind_uniform(m_fragment_constants_buffer_info, binding_table.fragment_constant_buffers_bind_slot, m_current_frame->descriptor_set); + m_program->bind_uniform(m_fragment_constants_buffer_info, binding_table.fragment_constant_buffers_bind_slot); } else { - m_program->bind_buffer(m_vertex_instructions_buffer_info, m_shader_interpreter.get_vertex_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set); - m_program->bind_buffer(m_fragment_instructions_buffer_info, m_shader_interpreter.get_fragment_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set); + m_program->bind_buffer(m_vertex_instructions_buffer_info, m_shader_interpreter.get_vertex_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + m_program->bind_buffer(m_fragment_instructions_buffer_info, m_shader_interpreter.get_fragment_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } if (vk::emulate_conditional_rendering()) { auto predicate = m_cond_render_buffer ? m_cond_render_buffer->value : vk::get_scratch_buffer(*m_current_command_buffer, 4)->value; - m_program->bind_buffer({ predicate, 0, 4 }, binding_table.conditional_render_predicate_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set); + m_program->bind_buffer({ predicate, 0, 4 }, binding_table.conditional_render_predicate_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } if (current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS) { - m_program->bind_buffer(m_instancing_indirection_buffer_info, binding_table.instancing_lookup_table_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set); - m_program->bind_buffer(m_instancing_constants_array_buffer_info, binding_table.instancing_constants_buffer_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set); + m_program->bind_buffer(m_instancing_indirection_buffer_info, binding_table.instancing_lookup_table_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + m_program->bind_buffer(m_instancing_constants_array_buffer_info, binding_table.instancing_constants_buffer_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); } // Clear flags @@ -2215,7 +2188,7 @@ void VKGSRender::update_vertex_env(u32 id, const vk::vertex_upload_info& vertex_ vkCmdPushConstants( *m_current_command_buffer, - m_pipeline_layout, + m_program->layout(), VK_SHADER_STAGE_VERTEX_BIT, 0, data_length, diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index e16d8d1afa..61dc496402 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -105,11 +105,6 @@ private: vk::command_buffer_chunk* m_current_command_buffer = nullptr; std::unique_ptr m_host_object_data; - - vk::descriptor_pool m_descriptor_pool; - VkDescriptorSetLayout m_descriptor_layouts = VK_NULL_HANDLE; - VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; - vk::framebuffer_holder* m_draw_fbo = nullptr; sizeu m_swapchain_dims{}; @@ -220,8 +215,6 @@ private: void update_draw_state(); void check_present_status(); - VkDescriptorSet allocate_descriptor_set(); - vk::vertex_upload_info upload_vertex_data(); rsx::simple_array m_scratch_mem; diff --git a/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp b/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp index acd4c42cb2..8f38378f52 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp +++ b/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp @@ -178,8 +178,6 @@ namespace vk VkSemaphore acquire_signal_semaphore = VK_NULL_HANDLE; VkSemaphore present_wait_semaphore = VK_NULL_HANDLE; - vk::descriptor_set descriptor_set; - rsx::flags32_t flags = 0; u32 present_image = -1; @@ -193,7 +191,6 @@ namespace vk { present_wait_semaphore = other.present_wait_semaphore; acquire_signal_semaphore = other.acquire_signal_semaphore; - descriptor_set.swap(other.descriptor_set); flags = other.flags; heap_snapshot = other.heap_snapshot; } diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.cpp b/rpcs3/Emu/RSX/VK/VKOverlays.cpp index f4e7d092f9..5cd4761983 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.cpp +++ b/rpcs3/Emu/RSX/VK/VKOverlays.cpp @@ -47,102 +47,38 @@ namespace vk } } - void overlay_pass::init_descriptors() - { - rsx::simple_array descriptor_pool_sizes = {}; - - if (m_num_uniform_buffers) - { - descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, m_num_uniform_buffers }); - }; - - if (m_num_usable_samplers) - { - descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_num_usable_samplers }); - } - - if (m_num_input_attachments) - { - descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, m_num_input_attachments }); - } - - // Reserve descriptor pools - m_descriptor_pool.create(*m_device, descriptor_pool_sizes); - - const auto num_bindings = m_num_uniform_buffers + m_num_usable_samplers + m_num_input_attachments; - rsx::simple_array bindings(num_bindings); - u32 binding_slot = 0; - - for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding_slot) - { - bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - bindings[binding_slot].descriptorCount = 1; - bindings[binding_slot].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[binding_slot].binding = binding_slot; - bindings[binding_slot].pImmutableSamplers = nullptr; - } - - for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding_slot) - { - bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - bindings[binding_slot].descriptorCount = 1; - bindings[binding_slot].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[binding_slot].binding = binding_slot; - bindings[binding_slot].pImmutableSamplers = nullptr; - } - - for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding_slot) - { - bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - bindings[binding_slot].descriptorCount = 1; - bindings[binding_slot].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[binding_slot].binding = binding_slot; - bindings[binding_slot].pImmutableSamplers = nullptr; - } - - ensure(binding_slot == num_bindings); - m_descriptor_layout = vk::descriptors::create_layout(bindings); - - VkPipelineLayoutCreateInfo layout_info = {}; - layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_info.setLayoutCount = 1; - layout_info.pSetLayouts = &m_descriptor_layout; - - std::vector push_constants = get_push_constants(); - if (!push_constants.empty()) - { - layout_info.pushConstantRangeCount = u32(push_constants.size()); - layout_info.pPushConstantRanges = push_constants.data(); - } - - CHECK_RESULT(vkCreatePipelineLayout(*m_device, &layout_info, nullptr, &m_pipeline_layout)); - } - std::vector overlay_pass::get_vertex_inputs() { check_heap(); - return{}; + return {}; } std::vector overlay_pass::get_fragment_inputs() { - std::vector fs_inputs; + using namespace vk::glsl; + + std::vector fs_inputs; u32 binding = 0; for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding) { const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : ""); - fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_uniform_buffer,{},{}, 0, name }); + const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_uniform_buffer, 0); + fs_inputs.push_back(input); } for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding) { - fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture,{},{}, binding, "fs" + std::to_string(n) }); + const std::string name = "fs" + std::to_string(n); + const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, binding); + fs_inputs.push_back(input); } for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding) { - fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture,{},{}, binding, "sp" + std::to_string(n) }); + const std::string name = "sp" + std::to_string(n); + const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, binding); + fs_inputs.push_back(input); } return fs_inputs; @@ -208,20 +144,20 @@ namespace vk info.stageCount = 2; info.pStages = shader_stages; info.pDynamicState = &dynamic_state_info; - info.layout = m_pipeline_layout; + info.layout = VK_NULL_HANDLE; info.basePipelineIndex = -1; info.basePipelineHandle = VK_NULL_HANDLE; info.renderPass = render_pass; auto compiler = vk::get_pipe_compiler(); - auto program = compiler->compile(info, m_pipeline_layout, vk::pipe_compiler::COMPILE_INLINE, {}, get_vertex_inputs(), get_fragment_inputs()); + auto program = compiler->compile(info, vk::pipe_compiler::COMPILE_INLINE, {}, get_vertex_inputs(), get_fragment_inputs()); auto result = program.get(); m_program_cache[storage_key] = std::move(program); return result; } - void overlay_pass::load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector& src) + vk::glsl::program* overlay_pass::load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector& src) { vk::glsl::program *program = nullptr; const auto key = get_pipeline_key(pass); @@ -232,8 +168,6 @@ namespace vk else program = build_pipeline(key, pass); - m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout); - if (!m_sampler && !src.empty()) { m_sampler = std::make_unique(*m_device, @@ -245,21 +179,22 @@ namespace vk if (m_num_uniform_buffers > 0) { - program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, m_descriptor_set); + program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0); } for (uint n = 0; n < src.size(); ++n) { VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout }; - program->bind_uniform(info, "fs" + std::to_string(n), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_descriptor_set); + program->bind_uniform(info, "fs" + std::to_string(n), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); } - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, program->pipeline); - m_descriptor_set.bind(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout); + program->bind(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS); VkBuffer buffers = m_vao.heap->value; VkDeviceSize offsets = m_vao_offset; vkCmdBindVertexBuffers(cmd, 0, 1, &buffers, &offsets); + + return program; } void overlay_pass::create(const vk::render_device& dev) @@ -267,8 +202,6 @@ namespace vk if (!initialized) { m_device = &dev; - init_descriptors(); - initialized = true; } } @@ -282,10 +215,6 @@ namespace vk m_program_cache.clear(); m_sampler.reset(); - vkDestroyDescriptorSetLayout(*m_device, m_descriptor_layout, nullptr); - vkDestroyPipelineLayout(*m_device, m_pipeline_layout, nullptr); - m_descriptor_pool.destroy(); - initialized = false; } } @@ -303,7 +232,7 @@ namespace vk return vk::get_framebuffer(dev, target->width(), target->height(), m_num_input_attachments > 0, render_pass, { target }); } - void overlay_pass::emit_geometry(vk::command_buffer& cmd) + void overlay_pass::emit_geometry(vk::command_buffer& cmd, glsl::program* /*program*/) { vkCmdDraw(cmd, num_drawable_elements, 1, first_vertex, 0); } @@ -328,11 +257,11 @@ namespace vk // This call clobbers dynamic state cmd.flags |= vk::command_buffer::cb_reload_dynamic_state; - load_program(cmd, render_pass, src); + auto program = load_program(cmd, render_pass, src); set_up_viewport(cmd, viewport.x1, viewport.y1, viewport.width(), viewport.height()); vk::begin_renderpass(cmd, render_pass, fbo->value, { positionu{0u, 0u}, sizeu{fbo->width(), fbo->height()} }); - emit_geometry(cmd); + emit_geometry(cmd, program); } void overlay_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::image* target, const std::vector& src, VkRenderPass render_pass) @@ -550,24 +479,37 @@ namespace vk false, true, desc->get_data(), owner_uid); } - std::vector ui_overlay_renderer::get_push_constants() + std::vector ui_overlay_renderer::get_vertex_inputs() { - return - { - { - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .offset = 0, - .size = 68 - }, - { - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .offset = 68, - .size = 12 - } - }; + auto result = overlay_pass::get_vertex_inputs(); + result.push_back( + glsl::program_input::make( + ::glsl::glsl_vertex_program, + "push_constants", + glsl::input_type_push_constant, + 0, + glsl::push_constant_ref { .size = 68 } + ) + ); + return result; } - void ui_overlay_renderer::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) + std::vector ui_overlay_renderer::get_fragment_inputs() + { + auto result = overlay_pass::get_fragment_inputs(); + result.push_back( + glsl::program_input::make( + ::glsl::glsl_fragment_program, + "push_constants", + glsl::input_type_push_constant, + 0, + glsl::push_constant_ref {.offset = 68, .size = 12 } + ) + ); + return result; + } + + void ui_overlay_renderer::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) { // Byte Layout // 00: vec4 ui_scale; @@ -600,7 +542,7 @@ namespace vk .get(); push_buf[16] = std::bit_cast(vert_config); - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 68, push_buf); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_VERTEX_BIT, 0, 68, push_buf); // 2. Fragment stuff rsx::overlays::fragment_options frag_opts; @@ -614,7 +556,7 @@ namespace vk push_buf[1] = m_time; push_buf[2] = m_blur_strength; - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 68, 12, push_buf); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 68, 12, push_buf); } void ui_overlay_renderer::set_primitive_type(rsx::overlays::primitive_type type) @@ -641,7 +583,7 @@ namespace vk } } - void ui_overlay_renderer::emit_geometry(vk::command_buffer& cmd) + void ui_overlay_renderer::emit_geometry(vk::command_buffer& cmd, glsl::program* program) { if (m_current_primitive_type == rsx::overlays::primitive_type::quad_list) { @@ -657,7 +599,7 @@ namespace vk } else { - overlay_pass::emit_geometry(cmd); + overlay_pass::emit_geometry(cmd, program); } } @@ -764,17 +706,20 @@ namespace vk renderpass_config.set_attachment_count(1); } - std::vector attachment_clear_pass::get_push_constants() + std::vector attachment_clear_pass::get_vertex_inputs() { - VkPushConstantRange constant; - constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - constant.offset = 0; - constant.size = 32; - - return { constant }; + return + { + vk::glsl::program_input::make( + ::glsl::glsl_vertex_program, + "push_constants", + vk::glsl::input_type_push_constant, + 0, + glsl::push_constant_ref{ .size = 32 }) + }; } - void attachment_clear_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) + void attachment_clear_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) { f32 data[8]; data[0] = clear_color.r; @@ -786,7 +731,7 @@ namespace vk data[6] = colormask.b; data[7] = colormask.a; - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 32, data); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_VERTEX_BIT, 0, 32, data); } void attachment_clear_pass::set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h) @@ -910,19 +855,24 @@ namespace vk m_num_usable_samplers = 2; } - std::vector video_out_calibration_pass::get_push_constants() + std::vector video_out_calibration_pass::get_fragment_inputs() { - VkPushConstantRange constant; - constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - constant.offset = 0; - constant.size = 16; - - return { constant }; + auto result = overlay_pass::get_fragment_inputs(); + result.push_back( + vk::glsl::program_input::make( + ::glsl::glsl_fragment_program, + "push_constants", + vk::glsl::input_type_push_constant, + 0, + glsl::push_constant_ref{ .size = 16 } + ) + ); + return result; } - void video_out_calibration_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) + void video_out_calibration_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) { - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, config.data); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, config.data); } void video_out_calibration_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index 7308a5c894..a968f706a1 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -44,11 +44,6 @@ namespace vk vk::glsl::shader m_vertex_shader; vk::glsl::shader m_fragment_shader; - vk::descriptor_pool m_descriptor_pool; - descriptor_set m_descriptor_set; - VkDescriptorSetLayout m_descriptor_layout = nullptr; - VkPipelineLayout m_pipeline_layout = nullptr; - VkFilter m_sampler_filter = VK_FILTER_LINEAR; u32 m_num_usable_samplers = 1; u32 m_num_input_attachments = 0; @@ -83,8 +78,6 @@ namespace vk void check_heap(); - void init_descriptors(); - virtual void update_uniforms(vk::command_buffer& /*cmd*/, vk::glsl::program* /*program*/) {} virtual std::vector get_vertex_inputs(); @@ -92,11 +85,6 @@ namespace vk virtual void get_dynamic_state_entries(std::vector& /*state_descriptors*/) {} - virtual std::vector get_push_constants() - { - return {}; - } - int sampler_location(int index) const { return 1 + index; } int input_attachment_location(int index) const { return 1 + m_num_usable_samplers + index; } @@ -113,8 +101,7 @@ namespace vk } vk::glsl::program* build_pipeline(u64 storage_key, VkRenderPass render_pass); - - void load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector& src); + vk::glsl::program* load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector& src); virtual void create(const vk::render_device& dev); virtual void destroy(); @@ -123,7 +110,7 @@ namespace vk vk::framebuffer* get_framebuffer(vk::image* target, VkRenderPass render_pass); - virtual void emit_geometry(vk::command_buffer& cmd); + virtual void emit_geometry(vk::command_buffer& cmd, glsl::program* program); virtual void set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h); @@ -169,13 +156,14 @@ namespace vk vk::image_view* find_font(rsx::overlays::font* font, vk::command_buffer& cmd, vk::data_heap& upload_heap); vk::image_view* find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid); - std::vector get_push_constants() override; + std::vector get_vertex_inputs() override; + std::vector get_fragment_inputs() override; void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override; void set_primitive_type(rsx::overlays::primitive_type type); - void emit_geometry(vk::command_buffer& cmd) override; + void emit_geometry(vk::command_buffer& cmd, glsl::program* program) override; void run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, VkRenderPass render_pass, vk::data_heap& upload_heap, rsx::overlays::overlay& ui); @@ -189,7 +177,7 @@ namespace vk attachment_clear_pass(); - std::vector get_push_constants() override; + std::vector get_vertex_inputs() override; void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override; @@ -227,7 +215,7 @@ namespace vk video_out_calibration_pass(); - std::vector get_push_constants() override; + std::vector get_fragment_inputs() override; void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override; diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp index 52742e1241..13c16513d3 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp @@ -36,12 +36,12 @@ namespace vk { if (job.is_graphics_job) { - auto compiled = int_compile_graphics_pipe(job.graphics_data, job.graphics_modules, job.pipe_layout, job.inputs, {}); + auto compiled = int_compile_graphics_pipe(job.graphics_data, job.graphics_modules, job.inputs, {}); job.callback_func(compiled); } else { - auto compiled = int_compile_compute_pipe(job.compute_data, job.pipe_layout); + auto compiled = int_compile_compute_pipe(job.compute_data, job.inputs); job.callback_func(compiled); } } @@ -50,25 +50,26 @@ namespace vk } } - std::unique_ptr pipe_compiler::int_compile_compute_pipe(const VkComputePipelineCreateInfo& create_info, VkPipelineLayout pipe_layout) + std::unique_ptr pipe_compiler::int_compile_compute_pipe( + const VkComputePipelineCreateInfo& create_info, + const std::vector& cs_inputs) { - VkPipeline pipeline; - vkCreateComputePipelines(*g_render_device, nullptr, 1, &create_info, nullptr, &pipeline); - return std::make_unique(*m_device, pipeline, pipe_layout); + return std::make_unique(*m_device, create_info, cs_inputs); } - std::unique_ptr pipe_compiler::int_compile_graphics_pipe(const VkGraphicsPipelineCreateInfo& create_info, VkPipelineLayout pipe_layout, - const std::vector& vs_inputs, const std::vector& fs_inputs) + std::unique_ptr pipe_compiler::int_compile_graphics_pipe( + const VkGraphicsPipelineCreateInfo& create_info, + const std::vector& vs_inputs, + const std::vector& fs_inputs) { - VkPipeline pipeline; - CHECK_RESULT(vkCreateGraphicsPipelines(*m_device, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline)); - auto result = std::make_unique(*m_device, pipeline, pipe_layout, vs_inputs, fs_inputs); - result->link(); - return result; + return std::make_unique(*m_device, create_info, vs_inputs, fs_inputs); } - std::unique_ptr pipe_compiler::int_compile_graphics_pipe(const vk::pipeline_props &create_info, VkShaderModule modules[2], VkPipelineLayout pipe_layout, - const std::vector& vs_inputs, const std::vector& fs_inputs) + std::unique_ptr pipe_compiler::int_compile_graphics_pipe( + const vk::pipeline_props &create_info, + VkShaderModule modules[2], + const std::vector& vs_inputs, + const std::vector& fs_inputs) { VkPipelineShaderStageCreateInfo shader_stages[2] = {}; shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -157,52 +158,54 @@ namespace vk info.stageCount = 2; info.pStages = shader_stages; info.pDynamicState = &dynamic_state_info; - info.layout = pipe_layout; + info.layout = VK_NULL_HANDLE; info.basePipelineIndex = -1; info.basePipelineHandle = VK_NULL_HANDLE; info.renderPass = vk::get_renderpass(*m_device, create_info.renderpass_key); - return int_compile_graphics_pipe(info, pipe_layout, vs_inputs, fs_inputs); + return int_compile_graphics_pipe(info, vs_inputs, fs_inputs); } std::unique_ptr pipe_compiler::compile( const VkComputePipelineCreateInfo& create_info, - VkPipelineLayout pipe_layout, - op_flags flags, callback_t callback) + op_flags flags, callback_t callback, + const std::vector& cs_inputs) { if (flags == COMPILE_INLINE) { - return int_compile_compute_pipe(create_info, pipe_layout); + return int_compile_compute_pipe(create_info, cs_inputs); } - m_work_queue.push(create_info, pipe_layout, callback); + m_work_queue.push(create_info, cs_inputs, callback); return {}; } std::unique_ptr pipe_compiler::compile( const VkGraphicsPipelineCreateInfo& create_info, - VkPipelineLayout pipe_layout, op_flags flags, callback_t /*callback*/, - const std::vector& vs_inputs, const std::vector& fs_inputs) + const std::vector& vs_inputs, + const std::vector& fs_inputs) { // It is very inefficient to defer this as all pointers need to be saved ensure(flags == COMPILE_INLINE); - return int_compile_graphics_pipe(create_info, pipe_layout, vs_inputs, fs_inputs); + return int_compile_graphics_pipe(create_info, vs_inputs, fs_inputs); } std::unique_ptr pipe_compiler::compile( - const vk::pipeline_props& create_info, - VkShaderModule module_handles[2], - VkPipelineLayout pipe_layout, + const vk::pipeline_props &create_info, + VkShaderModule vs, + VkShaderModule fs, op_flags flags, callback_t callback, - const std::vector& vs_inputs, const std::vector& fs_inputs) + const std::vector& vs_inputs, + const std::vector& fs_inputs) { + VkShaderModule modules[] = { vs, fs }; if (flags == COMPILE_INLINE) { - return int_compile_graphics_pipe(create_info, module_handles, pipe_layout, vs_inputs, fs_inputs); + return int_compile_graphics_pipe(create_info, modules, vs_inputs, fs_inputs); } - m_work_queue.push(create_info, pipe_layout, module_handles, vs_inputs, fs_inputs, callback); + m_work_queue.push(create_info, modules, vs_inputs, fs_inputs, callback); return {}; } diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h index 836bc5f14f..a915595e62 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h @@ -68,21 +68,20 @@ namespace vk void initialize(const vk::render_device* pdev); std::unique_ptr compile( - const VkComputePipelineCreateInfo& create_info, - VkPipelineLayout pipe_layout, - op_flags flags, callback_t callback = {}); + const VkComputePipelineCreateInfo& cs, + op_flags flags, callback_t callback = {}, + const std::vector& cs_inputs = {}); std::unique_ptr compile( const VkGraphicsPipelineCreateInfo& create_info, - VkPipelineLayout pipe_layout, op_flags flags, callback_t callback = {}, const std::vector& vs_inputs = {}, const std::vector& fs_inputs = {}); std::unique_ptr compile( const vk::pipeline_props &create_info, - VkShaderModule module_handles[2], - VkPipelineLayout pipe_layout, + VkShaderModule vs, + VkShaderModule fs, op_flags flags, callback_t callback = {}, const std::vector& vs_inputs = {}, const std::vector& fs_inputs = {}); @@ -112,13 +111,11 @@ namespace vk vk::pipeline_props graphics_data; compute_pipeline_props compute_data; - VkPipelineLayout pipe_layout; VkShaderModule graphics_modules[2]; std::vector inputs; pipe_compiler_job( const vk::pipeline_props& props, - VkPipelineLayout layout, VkShaderModule modules[2], const std::vector& vs_in, const std::vector& fs_in, @@ -126,7 +123,6 @@ namespace vk { callback_func = func; graphics_data = props; - pipe_layout = layout; graphics_modules[0] = modules[0]; graphics_modules[1] = modules[1]; is_graphics_job = true; @@ -138,24 +134,34 @@ namespace vk pipe_compiler_job( const VkComputePipelineCreateInfo& props, - VkPipelineLayout layout, + const std::vector& cs_in, callback_t func) { callback_func = func; compute_data = props; - pipe_layout = layout; is_graphics_job = false; + + inputs = cs_in; } }; const vk::render_device* m_device = nullptr; lf_queue m_work_queue; - std::unique_ptr int_compile_compute_pipe(const VkComputePipelineCreateInfo& create_info, VkPipelineLayout pipe_layout); - std::unique_ptr int_compile_graphics_pipe(const VkGraphicsPipelineCreateInfo& create_info, VkPipelineLayout pipe_layout, - const std::vector& vs_inputs, const std::vector& fs_inputs); - std::unique_ptr int_compile_graphics_pipe(const vk::pipeline_props &create_info, VkShaderModule modules[2], VkPipelineLayout pipe_layout, - const std::vector& vs_inputs, const std::vector& fs_inputs); + std::unique_ptr int_compile_compute_pipe( + const VkComputePipelineCreateInfo& create_info, + const std::vector& cs_inputs); + + std::unique_ptr int_compile_graphics_pipe( + const VkGraphicsPipelineCreateInfo& create_info, + const std::vector& vs_inputs, + const std::vector& fs_inputs); + + std::unique_ptr int_compile_graphics_pipe( + const vk::pipeline_props &create_info, + VkShaderModule modules[2], + const std::vector& vs_inputs, + const std::vector& fs_inputs); }; void initialize_pipe_compiler(int num_worker_threads = -1); diff --git a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h index 4f9f535a76..647b21adc4 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h +++ b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h @@ -46,15 +46,14 @@ namespace vk const fragment_program_type& fragmentProgramData, const vk::pipeline_props& pipelineProperties, bool compile_async, - std::function callback, - VkPipelineLayout common_pipeline_layout) + std::function callback) { const auto compiler_flags = compile_async ? vk::pipe_compiler::COMPILE_DEFERRED : vk::pipe_compiler::COMPILE_INLINE; - VkShaderModule modules[2] = { vertexProgramData.handle, fragmentProgramData.handle }; - auto compiler = vk::get_pipe_compiler(); auto result = compiler->compile( - pipelineProperties, modules, common_pipeline_layout, + pipelineProperties, + vertexProgramData.handle, + fragmentProgramData.handle, compiler_flags, callback, vertexProgramData.uniforms, fragmentProgramData.uniforms); diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp index 57174caa98..34bb4d1331 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "VKProgramPipeline.h" +#include "VKResourceManager.h" #include "vkutils/descriptors.h" #include "vkutils/device.h" @@ -7,10 +8,61 @@ namespace vk { + extern vk::render_device* get_current_renderer(); + namespace glsl { using namespace ::glsl; + VkDescriptorType to_descriptor_type(program_input_type type) + { + switch (type) + { + case input_type_uniform_buffer: + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case input_type_texel_buffer: + return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + case input_type_texture: + return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + case input_type_storage_buffer: + return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + case input_type_storage_texture: + return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + default: + fmt::throw_exception("Unexpected program input type %d", static_cast(type)); + } + } + + VkShaderStageFlags to_shader_stage_flags(::glsl::program_domain domain) + { + switch (domain) + { + case glsl_vertex_program: + return VK_SHADER_STAGE_VERTEX_BIT; + case glsl_fragment_program: + return VK_SHADER_STAGE_FRAGMENT_BIT; + case glsl_compute_program: + return VK_SHADER_STAGE_COMPUTE_BIT; + default: + fmt::throw_exception("Unexpected domain %d", static_cast(domain)); + } + } + + const char* to_string(::glsl::program_domain domain) + { + switch (domain) + { + case glsl_vertex_program: + return "vertex"; + case glsl_fragment_program: + return "fragment"; + case glsl_compute_program: + return "compute"; + default: + fmt::throw_exception("Unexpected domain %d", static_cast(domain)); + } + } + void shader::create(::glsl::program_domain domain, const std::string& source) { type = domain; @@ -23,11 +75,8 @@ namespace vk if (!spirv::compile_glsl_to_spv(m_compiled, m_source, type, ::glsl::glsl_rules_vulkan)) { - const std::string shader_type = type == ::glsl::program_domain::glsl_vertex_program ? "vertex" : - type == ::glsl::program_domain::glsl_fragment_program ? "fragment" : "compute"; - rsx_log.notice("%s", m_source); - fmt::throw_exception("Failed to compile %s shader", shader_type); + fmt::throw_exception("Failed to compile %s shader", to_string(type)); } VkShaderModuleCreateInfo vs_info; @@ -69,34 +118,56 @@ namespace vk return m_handle; } - void program::create_impl() + void program::init() { linked = false; - attribute_location_mask = 0; - vertex_attributes_mask = 0; fs_texture_bindings.fill(~0u); fs_texture_mirror_bindings.fill(~0u); vs_texture_bindings.fill(~0u); } - program::program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector &vertex_input, const std::vector& fragment_inputs) - : m_device(dev), pipeline(p), pipeline_layout(layout) + program::program(VkDevice dev, const VkGraphicsPipelineCreateInfo& create_info, const std::vector &vertex_inputs, const std::vector& fragment_inputs) + : m_device(dev) { - create_impl(); - load_uniforms(vertex_input); + init(); + + load_uniforms(vertex_inputs); load_uniforms(fragment_inputs); + + create_pipeline_layout(); + ensure(m_pipeline_layout); + + auto _create_info = create_info; + _create_info.layout = m_pipeline_layout; + CHECK_RESULT(vkCreateGraphicsPipelines(dev, nullptr, 1, &create_info, nullptr, &m_pipeline)); } - program::program(VkDevice dev, VkPipeline p, VkPipelineLayout layout) - : m_device(dev), pipeline(p), pipeline_layout(layout) + program::program(VkDevice dev, const VkComputePipelineCreateInfo& create_info, const std::vector& compute_inputs) + : m_device(dev) { - create_impl(); + init(); + + load_uniforms(compute_inputs); + + create_pipeline_layout(); + ensure(m_pipeline_layout); + + auto _create_info = create_info; + _create_info.layout = m_pipeline_layout; + CHECK_RESULT(vkCreateComputePipelines(dev, nullptr, 1, &create_info, nullptr, &m_pipeline)); } program::~program() { - vkDestroyPipeline(m_device, pipeline, nullptr); + vkDestroyPipeline(m_device, m_pipeline, nullptr); + + if (m_pipeline_layout) + { + vkDestroyPipelineLayout(m_device, m_pipeline_layout, nullptr); + vkDestroyDescriptorSetLayout(m_device, m_descriptor_set_layout, nullptr); + vk::get_resource_manager()->dispose(m_descriptor_pool); + } } program& program::load_uniforms(const std::vector& inputs) @@ -160,14 +231,36 @@ namespace vk }); } - void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorType type, vk::descriptor_set &set) + u32 program::get_uniform_location(program_input_type type, const std::string& uniform_name) + { + const auto& uniform = uniforms[type]; + const auto result = std::find_if(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u) + { + return u.name == uniform_name; + }); + + if (result == uniform.end()) + { + return { umax }; + } + + return result->location; + } + + void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorType type) { for (const auto &uniform : uniforms[program_input_type::input_type_texture]) { if (uniform.name == uniform_name) { - set.push(image_descriptor, type, uniform.location); - attribute_location_mask |= (1ull << uniform.location); + if (m_descriptor_slots[uniform.location].matches(image_descriptor)) + { + return; + } + + next_descriptor_set(); + m_descriptor_set.push(image_descriptor, type, uniform.location); + m_descriptors_dirty[uniform.location] = false; return; } } @@ -175,7 +268,7 @@ namespace vk rsx_log.notice("texture not found in program: %s", uniform_name.c_str()); } - void program::bind_uniform(const VkDescriptorImageInfo & image_descriptor, int texture_unit, ::glsl::program_domain domain, vk::descriptor_set &set, bool is_stencil_mirror) + void program::bind_uniform(const VkDescriptorImageInfo & image_descriptor, int texture_unit, ::glsl::program_domain domain, bool is_stencil_mirror) { ensure(domain != ::glsl::program_domain::glsl_compute_program); @@ -189,34 +282,46 @@ namespace vk binding = vs_texture_bindings[texture_unit]; } - if (binding != ~0u) + if (binding == ~0u) [[ unlikely ]] { - set.push(image_descriptor, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding); - attribute_location_mask |= (1ull << binding); + rsx_log.notice("texture not found in program: %stex%u", (domain == ::glsl::program_domain::glsl_vertex_program) ? "v" : "", texture_unit); return; } - rsx_log.notice("texture not found in program: %stex%u", (domain == ::glsl::program_domain::glsl_vertex_program)? "v" : "", texture_unit); + if (m_descriptor_slots[binding].matches(image_descriptor)) + { + return; + } + + next_descriptor_set(); + m_descriptor_set.push(image_descriptor, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding); + m_descriptors_dirty[binding] = false; } - void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, vk::descriptor_set &set) + void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point) { - bind_buffer(buffer_descriptor, binding_point, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, set); + bind_buffer(buffer_descriptor, binding_point, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); } - void program::bind_uniform(const VkBufferView &buffer_view, u32 binding_point, vk::descriptor_set &set) + void program::bind_uniform(const VkBufferView &buffer_view, u32 binding_point) { - set.push(buffer_view, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, binding_point); - attribute_location_mask |= (1ull << binding_point); + if (m_descriptor_slots[binding_point].matches(buffer_view)) + { + return; + } + + next_descriptor_set(); + m_descriptor_set.push(buffer_view, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, binding_point); + m_descriptors_dirty[binding_point] = false; } - void program::bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, vk::descriptor_set &set) + void program::bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name) { for (const auto &uniform : uniforms[type]) { if (uniform.name == binding_name) { - bind_uniform(buffer_view, uniform.location, set); + bind_uniform(buffer_view, uniform.location); return; } } @@ -224,10 +329,135 @@ namespace vk rsx_log.notice("vertex buffer not found in program: %s", binding_name.c_str()); } - void program::bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type, vk::descriptor_set &set) + void program::bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type) { - set.push(buffer_descriptor, type, binding_point); - attribute_location_mask |= (1ull << binding_point); + m_descriptor_set.push(buffer_descriptor, type, binding_point); + m_descriptors_dirty[binding_point] = false; + } + + VkDescriptorSet program::allocate_descriptor_set() + { + if (!m_descriptor_pool) + { + create_descriptor_pool(); + } + + return m_descriptor_pool->allocate(m_descriptor_set_layout); + } + + void program::next_descriptor_set() + { + const auto new_set = allocate_descriptor_set(); + const auto old_set = m_descriptor_set.value(); + + if (old_set) + { + m_copy_cmds.clear(); + for (unsigned i = 0; i < m_copy_cmds.size(); ++i) + { + if (!m_descriptors_dirty[i]) + { + continue; + } + + // Reuse already initialized memory. Each command is the same anyway. + m_copy_cmds.resize(m_copy_cmds.size() + 1); + auto& cmd = m_copy_cmds.back(); + cmd.srcBinding = cmd.dstBinding = i; + cmd.srcSet = old_set; + cmd.dstSet = new_set; + } + + m_descriptor_set.push(m_copy_cmds); + } + + m_descriptor_set = allocate_descriptor_set(); + } + + program& program::bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point) + { + VkDescriptorSet set = m_descriptor_set.value(); + vkCmdBindPipeline(cmd, bind_point, m_pipeline); + vkCmdBindDescriptorSets(cmd, bind_point, m_pipeline_layout, 0, 1, &set, 0, nullptr); + return *this; + } + + void program::create_descriptor_set_layout() + { + ensure(m_descriptor_set_layout == VK_NULL_HANDLE); + + rsx::simple_array bindings; + bindings.reserve(16); + + m_descriptor_pool_sizes.clear(); + m_descriptor_pool_sizes.reserve(input_type_max_enum); + + for (const auto& type_arr : uniforms) + { + if (type_arr.empty() || type_arr.front().type == input_type_push_constant) + { + continue; + } + + VkDescriptorType type = to_descriptor_type(type_arr.front().type); + m_descriptor_pool_sizes.push_back({ .type = type }); + + for (const auto& input : type_arr) + { + VkDescriptorSetLayoutBinding binding + { + .binding = input.location, + .descriptorType = type, + .descriptorCount = 1, + .stageFlags = to_shader_stage_flags(input.domain) + }; + bindings.push_back(binding); + m_descriptor_pool_sizes.back().descriptorCount++; + } + } + + VkDescriptorSetLayoutCreateInfo set_layout_create_info + { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .flags = 0, + .bindingCount = ::size32(bindings), + .pBindings = bindings.data() + }; + CHECK_RESULT(vkCreateDescriptorSetLayout(m_device, &set_layout_create_info, nullptr, &m_descriptor_set_layout)); + } + + void program::create_pipeline_layout() + { + ensure(!linked); + ensure(m_pipeline_layout == VK_NULL_HANDLE); + + create_descriptor_set_layout(); + + rsx::simple_array push_constants{}; + for (const auto& input : uniforms[input_type_push_constant]) + { + const auto& range = input.as_push_constant(); + push_constants.push_back({ .offset = range.offset, .size = range.size }); + } + + VkPipelineLayoutCreateInfo create_info + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = &m_descriptor_set_layout, + .pushConstantRangeCount = ::size32(push_constants), + .pPushConstantRanges = push_constants.data() + }; + CHECK_RESULT(vkCreatePipelineLayout(m_device, &create_info, nullptr, &m_pipeline_layout)); + } + + void program::create_descriptor_pool() + { + ensure(linked); + + m_descriptor_pool = std::make_unique(); + m_descriptor_pool->create(*vk::get_current_renderer(), m_descriptor_pool_sizes); } } } diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h index 06dbaf877f..0b3e8ed7d1 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h @@ -7,6 +7,7 @@ #include #include +#include namespace vk { @@ -15,18 +16,20 @@ namespace vk enum program_input_type : u32 { input_type_uniform_buffer = 0, - input_type_texel_buffer = 1, - input_type_texture = 2, - input_type_storage_buffer = 3, + input_type_texel_buffer, + input_type_texture, + input_type_storage_buffer, + input_type_storage_texture, + input_type_push_constant, - input_type_max_enum = 4 + input_type_max_enum }; struct bound_sampler { - VkFormat format; - VkImage image; - VkComponentMapping mapping; + VkFormat format = VK_FORMAT_UNDEFINED; + VkImage image = VK_NULL_HANDLE; + VkComponentMapping mapping{}; }; struct bound_buffer @@ -37,16 +40,73 @@ namespace vk u64 size = 0; }; + struct push_constant_ref + { + u32 offset = 0; + u32 size = 0; + }; + struct program_input { ::glsl::program_domain domain; program_input_type type; - bound_buffer as_buffer; - bound_sampler as_sampler; + using bound_data_t = std::variant; + bound_data_t bound_data; u32 location; std::string name; + + inline bound_buffer& as_buffer() { return *std::get_if(&bound_data); } + inline bound_sampler& as_sampler() { return *std::get_if(&bound_data); } + inline push_constant_ref& as_push_constant() { return *std::get_if(&bound_data); } + + inline const bound_buffer& as_buffer() const { return *std::get_if(&bound_data); } + inline const bound_sampler& as_sampler() const { return *std::get_if(&bound_data); } + inline const push_constant_ref& as_push_constant() const { return *std::get_if(&bound_data); } + + static program_input make( + ::glsl::program_domain domain, + const std::string& name, + program_input_type type, + u32 location, + const bound_data_t& data = bound_buffer{}) + { + return program_input + { + .domain = domain, + .type = type, + .bound_data = data, + .location = location, + .name = name + }; + } + }; + + union descriptor_slot_t + { + VkDescriptorImageInfo image_info; + VkDescriptorBufferInfo buffer_info; + VkBufferView buffer_view; + + bool matches(const VkDescriptorImageInfo& test) const + { + return test.imageView == image_info.imageView && + test.sampler == image_info.sampler && + test.imageLayout == image_info.imageLayout; + } + + bool matches(const VkDescriptorBufferInfo& test) const + { + return test.buffer == buffer_info.buffer && + test.offset == buffer_info.offset && + test.range == buffer_info.range; + } + + bool matches(VkBufferView test) const + { + return test == buffer_view; + } }; class shader @@ -75,37 +135,61 @@ namespace vk class program { std::array, input_type_max_enum> uniforms; - VkDevice m_device; + VkDevice m_device = VK_NULL_HANDLE; + + VkPipeline m_pipeline = VK_NULL_HANDLE; + VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; std::array fs_texture_bindings; std::array fs_texture_mirror_bindings; std::array vs_texture_bindings; - bool linked; + bool linked = false; - void create_impl(); + std::unique_ptr m_descriptor_pool; + VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; + vk::descriptor_set m_descriptor_set{}; + rsx::simple_array m_descriptor_pool_sizes; + + std::vector m_descriptor_slots; + std::vector m_descriptors_dirty; + rsx::simple_array m_copy_cmds; + + void init(); + + void create_descriptor_set_layout(); + void create_pipeline_layout(); + void create_descriptor_pool(); + + VkDescriptorSet allocate_descriptor_set(); + void next_descriptor_set(); + + program& load_uniforms(const std::vector& inputs); public: - VkPipeline pipeline; - VkPipelineLayout pipeline_layout; - u64 attribute_location_mask; - u64 vertex_attributes_mask; - program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector &vertex_input, const std::vector& fragment_inputs); - program(VkDevice dev, VkPipeline p, VkPipelineLayout layout); + program(VkDevice dev, const VkGraphicsPipelineCreateInfo& create_info, const std::vector &vertex_inputs, const std::vector& fragment_inputs); + program(VkDevice dev, const VkComputePipelineCreateInfo& create_info, const std::vector& compute_inputs); program(const program&) = delete; program(program&& other) = delete; ~program(); - program& load_uniforms(const std::vector& inputs); program& link(); + program& bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point); bool has_uniform(program_input_type type, const std::string &uniform_name); - void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type, vk::descriptor_set &set); - void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, vk::descriptor_set &set, bool is_stencil_mirror = false); - void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, vk::descriptor_set &set); - void bind_uniform(const VkBufferView &buffer_view, u32 binding_point, vk::descriptor_set &set); - void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, vk::descriptor_set &set); - void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type, vk::descriptor_set &set); + u32 get_uniform_location(program_input_type type, const std::string& uniform_name); + + void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type); + void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, bool is_stencil_mirror = false); + void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point); + void bind_uniform(const VkBufferView &buffer_view, u32 binding_point); + void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name); + void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type); + + void bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, VkDescriptorType type, int count, u32 binding_point); + + inline VkPipelineLayout layout() const { return m_pipeline_layout; } + inline VkPipeline value() const { return m_pipeline; } }; } } diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index caa85dcc84..3c3ef0acbd 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -154,7 +154,7 @@ namespace vk // If we have driver support for FBO loops, set the usage flag for it. if (vk::get_current_renderer()->get_framebuffer_loops_support()) { - return { VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT, 0 }; + return { VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT }; } // Workarounds to force transition to GENERAL to decompress. diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.h b/rpcs3/Emu/RSX/VK/VKResolveHelper.h index 7cf6631b67..2403f5bc59 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.h +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.h @@ -23,43 +23,36 @@ namespace vk void build(const std::string& format_prefix, bool unresolve, bool bgra_swap); - std::vector> get_descriptor_layout() override - { - return - { - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2 } - }; - } - - void declare_inputs() override + std::vector get_inputs() override { std::vector inputs = { - { + glsl::program_input::make( ::glsl::program_domain::glsl_compute_program, - vk::glsl::program_input_type::input_type_texture, - {}, {}, - 0, - "multisampled" - }, - { + "multisampled", + glsl::input_type_storage_texture, + 0 + ), + + glsl::program_input::make( ::glsl::program_domain::glsl_compute_program, - vk::glsl::program_input_type::input_type_texture, - {}, {}, - 1, - "resolve" - } + "resolve", + glsl::input_type_storage_texture, + 1 + ), }; - m_program->load_uniforms(inputs); + auto result = compute_task::get_inputs(); + result.insert(result.end(), inputs.begin(), inputs.end()); + return result; } void bind_resources() override { auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_VIEW_MULTISAMPLED)); auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY)); - m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, "multisampled", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set); - m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, "resolve", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set); + m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, "multisampled", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); + m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, "resolve", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); } void run(const vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image) @@ -116,19 +109,22 @@ namespace vk void build(bool resolve_depth, bool resolve_stencil, bool unresolve); - std::vector get_push_constants() override + std::vector get_fragment_inputs() override { - VkPushConstantRange constant; - constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - constant.offset = 0; - constant.size = 16; - - return { constant }; + auto result = overlay_pass::get_fragment_inputs(); + result.push_back(glsl::program_input::make( + ::glsl::glsl_fragment_program, + "push_constants", + glsl::input_type_push_constant, + umax, + glsl::push_constant_ref{ .size = 16 } + )); + return result; } - void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override + void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override { - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, static_parameters_width * 4, static_parameters); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, static_parameters_width * 4, static_parameters); } void update_sample_configuration(vk::image* msaa_image) @@ -226,16 +222,16 @@ namespace vk state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); } - void emit_geometry(vk::command_buffer& cmd) override + void emit_geometry(vk::command_buffer& cmd, glsl::program* program) override { vkCmdClearAttachments(cmd, 1, &clear_info, 1, ®ion); for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) { vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask); - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); - overlay_pass::emit_geometry(cmd); + overlay_pass::emit_geometry(cmd, program); } } @@ -285,16 +281,16 @@ namespace vk state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); } - void emit_geometry(vk::command_buffer& cmd) override + void emit_geometry(vk::command_buffer& cmd, glsl::program* program) override { vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region); for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) { vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask); - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); - overlay_pass::emit_geometry(cmd); + overlay_pass::emit_geometry(cmd, program); } } diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp index 3c9188fd60..da10965be9 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp @@ -254,7 +254,7 @@ namespace vk m_shader_cache[compiler_options].m_fs = std::move(fs); return ret; } - +/* std::pair shader_interpreter::create_layout(VkDevice dev) { const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table(); @@ -356,24 +356,16 @@ namespace vk CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result)); return { set_layout, result }; } - - void shader_interpreter::create_descriptor_pools(const vk::render_device& dev) - { - const auto max_draw_calls = dev.get_descriptor_max_draw_calls(); - m_descriptor_pool.create(dev, m_descriptor_pool_sizes, max_draw_calls); - } +*/ void shader_interpreter::init(const vk::render_device& dev) { m_device = dev; - std::tie(m_shared_descriptor_layout, m_shared_pipeline_layout) = create_layout(dev); - create_descriptor_pools(dev); } void shader_interpreter::destroy() { m_program_cache.clear(); - m_descriptor_pool.destroy(); for (auto &fs : m_shader_cache) { @@ -382,18 +374,6 @@ namespace vk } m_shader_cache.clear(); - - if (m_shared_pipeline_layout) - { - vkDestroyPipelineLayout(m_device, m_shared_pipeline_layout, nullptr); - m_shared_pipeline_layout = VK_NULL_HANDLE; - } - - if (m_shared_descriptor_layout) - { - vkDestroyDescriptorSetLayout(m_device, m_shared_descriptor_layout, nullptr); - m_shared_descriptor_layout = VK_NULL_HANDLE; - } } glsl::program* shader_interpreter::link(const vk::pipeline_props& properties, u64 compiler_opt) @@ -478,28 +458,30 @@ namespace vk info.stageCount = 2; info.pStages = shader_stages; info.pDynamicState = &dynamic_state_info; - info.layout = m_shared_pipeline_layout; + info.layout = VK_NULL_HANDLE; info.basePipelineIndex = -1; info.basePipelineHandle = VK_NULL_HANDLE; info.renderPass = vk::get_renderpass(m_device, properties.renderpass_key); auto compiler = vk::get_pipe_compiler(); - auto program = compiler->compile(info, m_shared_pipeline_layout, vk::pipe_compiler::COMPILE_INLINE, {}, m_vs_inputs, m_fs_inputs); + auto program = compiler->compile(info, vk::pipe_compiler::COMPILE_INLINE, {}, m_vs_inputs, m_fs_inputs); return program.release(); } - void shader_interpreter::update_fragment_textures(const std::array& sampled_images, vk::descriptor_set &set) + void shader_interpreter::update_fragment_textures(const std::array& sampled_images) { - const VkDescriptorImageInfo* texture_ptr = sampled_images.data(); - for (u32 i = 0, binding = m_fragment_textures_start; i < 4; ++i, ++binding, texture_ptr += 16) + // FIXME: Cannot use m_fragment_textures.start now since each interpreter has its own binding layout + u32 binding = m_current_interpreter->get_uniform_location(glsl::input_type_texture, "texture1D_array"); + if (binding == umax) { - set.push(texture_ptr, 16, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding); + return; } - } - VkDescriptorSet shader_interpreter::allocate_descriptor_set() - { - return m_descriptor_pool.allocate(m_shared_descriptor_layout); + const VkDescriptorImageInfo* texture_ptr = sampled_images.data(); + for (u32 i = 0; i < 4; ++i, ++binding, texture_ptr += 16) + { + m_current_interpreter->bind_uniform_array(texture_ptr, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16, binding); + } } glsl::program* shader_interpreter::get( diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h index d359ca343e..aeaad698fb 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h @@ -16,8 +16,6 @@ namespace vk std::vector m_fs_inputs; VkDevice m_device = VK_NULL_HANDLE; - VkDescriptorSetLayout m_shared_descriptor_layout = VK_NULL_HANDLE; - VkPipelineLayout m_shared_pipeline_layout = VK_NULL_HANDLE; glsl::program* m_current_interpreter = nullptr; struct pipeline_key @@ -47,8 +45,6 @@ namespace vk std::unordered_map, key_hasher> m_program_cache; std::unordered_map m_shader_cache; - rsx::simple_array m_descriptor_pool_sizes; - vk::descriptor_pool m_descriptor_pool; u32 m_vertex_instruction_start = 0; u32 m_fragment_instruction_start = 0; @@ -56,9 +52,6 @@ namespace vk pipeline_key m_current_key{}; - std::pair create_layout(VkDevice dev); - void create_descriptor_pools(const vk::render_device& dev); - glsl::shader* build_vs(u64 compiler_opt); glsl::shader* build_fs(u64 compiler_opt); glsl::program* link(const vk::pipeline_props& properties, u64 compiler_opt); @@ -78,7 +71,6 @@ namespace vk u32 get_vertex_instruction_location() const; u32 get_fragment_instruction_location() const; - void update_fragment_textures(const std::array& sampled_images, vk::descriptor_set &set); - VkDescriptorSet allocate_descriptor_set(); + void update_fragment_textures(const std::array& sampled_images); }; } diff --git a/rpcs3/Emu/RSX/VK/upscalers/fsr1/fsr_pass.cpp b/rpcs3/Emu/RSX/VK/upscalers/fsr1/fsr_pass.cpp index c256070490..d0b972765c 100644 --- a/rpcs3/Emu/RSX/VK/upscalers/fsr1/fsr_pass.cpp +++ b/rpcs3/Emu/RSX/VK/upscalers/fsr1/fsr_pass.cpp @@ -68,36 +68,28 @@ namespace vk create(); } - std::vector> fsr_pass::get_descriptor_layout() - { - return - { - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 } - }; - } - - void fsr_pass::declare_inputs() + std::vector fsr_pass::get_inputs() { std::vector inputs = { - { + glsl::program_input::make( ::glsl::program_domain::glsl_compute_program, - vk::glsl::program_input_type::input_type_texture, - {}, {}, - 0, - "InputTexture" - }, - { + "InputTexture", + vk::glsl::input_type_texture, + 0 + ), + + glsl::program_input::make( ::glsl::program_domain::glsl_compute_program, - vk::glsl::program_input_type::input_type_texture, - {}, {}, - 1, - "OutputTexture" - } + "OutputTexture", + vk::glsl::input_type_storage_texture, + 1 + ), }; - m_program->load_uniforms(inputs); + auto result = compute_task::get_inputs(); + result.insert(result.end(), inputs.begin(), inputs.end()); + return result; } void fsr_pass::bind_resources() @@ -111,8 +103,8 @@ namespace vk VK_FALSE, 0.f, 1.f, 0.f, 0.f, VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK); } - m_program->bind_uniform({ m_sampler->value, m_input_image->value, m_input_image->image()->current_layout }, "InputTexture", VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_descriptor_set); - m_program->bind_uniform({ VK_NULL_HANDLE, m_output_image->value, m_output_image->image()->current_layout }, "OutputTexture", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set); + m_program->bind_uniform({ m_sampler->value, m_input_image->value, m_input_image->image()->current_layout }, "InputTexture", VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + m_program->bind_uniform({ VK_NULL_HANDLE, m_output_image->value, m_output_image->image()->current_layout }, "OutputTexture", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); } void fsr_pass::run(const vk::command_buffer& cmd, vk::viewable_image* src, vk::viewable_image* dst, const size2u& input_size, const size2u& output_size) @@ -158,7 +150,7 @@ namespace vk static_cast(src_image->width()), static_cast(src_image->height()), // Size of the raw image to upscale (in case viewport does not cover it all) static_cast(m_output_size.width), static_cast(m_output_size.height)); // Size of output viewport (target size) - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, m_constants_buf); + vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, m_constants_buf); } rcas_pass::rcas_pass() @@ -177,7 +169,7 @@ namespace vk auto cas_attenuation = 2.f - (g_cfg.video.vk.rcas_sharpening_intensity / 50.f); FsrRcasCon(&m_constants_buf[0], cas_attenuation); - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, m_constants_buf); + vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, m_constants_buf); } } // Namespace FidelityFX diff --git a/rpcs3/Emu/RSX/VK/upscalers/fsr_pass.h b/rpcs3/Emu/RSX/VK/upscalers/fsr_pass.h index c5b5b30e73..6d9b15d72a 100644 --- a/rpcs3/Emu/RSX/VK/upscalers/fsr_pass.h +++ b/rpcs3/Emu/RSX/VK/upscalers/fsr_pass.h @@ -19,8 +19,7 @@ namespace vk size2u m_output_size; u32 m_constants_buf[20]; - std::vector> get_descriptor_layout() override; - void declare_inputs() override; + std::vector get_inputs() override; void bind_resources() override; virtual void configure(const vk::command_buffer& cmd) = 0;