diff --git a/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp b/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp new file mode 100644 index 0000000000..961c784c77 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" + +#include "VKFramebuffer.h" + +namespace vk +{ + std::unordered_map>> g_framebuffers_cache; + + vk::framebuffer_holder *get_framebuffer(VkDevice dev, u16 width, u16 height, VkRenderPass renderpass, const std::vector& image_list) + { + u64 key = u64(width) | (u64(height) << 16); + auto &queue = g_framebuffers_cache[key]; + + for (auto &fbo : queue) + { + if (fbo->matches(image_list, width, height)) + { + return fbo.get(); + } + } + + std::vector> image_views; + image_views.reserve(image_list.size()); + + for (auto &e : image_list) + { + const VkImageSubresourceRange subres = { e->aspect(), 0, 1, 0, 1 }; + image_views.push_back(std::make_unique(dev, e, vk::default_component_map(), subres)); + } + + auto value = std::make_unique(dev, renderpass, width, height, std::move(image_views)); + auto ret = value.get(); + + queue.push_back(std::move(value)); + return ret; + } + + vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkRenderPass renderpass, VkFormat format, VkImage attachment) + { + u64 key = u64(width) | (u64(height) << 16); + auto &queue = g_framebuffers_cache[key]; + + for (const auto &e : queue) + { + if (e->attachments[0]->info.image == attachment) + { + return e.get(); + } + } + + VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + std::vector> views; + + views.push_back(std::make_unique(dev, attachment, VK_IMAGE_VIEW_TYPE_2D, format, vk::default_component_map(), range)); + auto value = std::make_unique(dev, renderpass, width, height, std::move(views)); + auto ret = value.get(); + + queue.push_back(std::move(value)); + return ret; + } + + void remove_unused_framebuffers() + { + // Remove stale framebuffers. Ref counted to prevent use-after-free + for (auto It = g_framebuffers_cache.begin(); It != g_framebuffers_cache.end();) + { + It->second.erase( + std::remove_if(It->second.begin(), It->second.end(), [](const auto& fbo) + { + return (fbo->unused_check_count() >= 2); + }), + It->second.end() + ); + + if (It->second.empty()) + { + It = g_framebuffers_cache.erase(It); + } + else + { + ++It; + } + } + } + + void clear_framebuffer_cache() + { + g_framebuffers_cache.clear(); + } +} diff --git a/rpcs3/Emu/RSX/VK/VKFramebuffer.h b/rpcs3/Emu/RSX/VK/VKFramebuffer.h new file mode 100644 index 0000000000..d6064dac5e --- /dev/null +++ b/rpcs3/Emu/RSX/VK/VKFramebuffer.h @@ -0,0 +1,17 @@ +#pragma once + +#include "VKHelpers.h" + +namespace vk +{ + struct framebuffer_holder : public vk::framebuffer, public rsx::ref_counted + { + using framebuffer::framebuffer; + }; + + vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkRenderPass renderpass, const std::vector& image_list); + vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkRenderPass renderpass, VkFormat format, VkImage attachment); + + void remove_unused_framebuffers(); + void clear_framebuffer_cache(); +} diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 92e5fe8514..619bcdef59 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -603,9 +603,6 @@ VKGSRender::~VKGSRender() null_buffer.reset(); null_buffer_view.reset(); - //Frame context - m_framebuffers_to_clean.clear(); - if (m_current_frame == &m_aux_frame_context) { //Return resources back to the owner @@ -625,8 +622,6 @@ VKGSRender::~VKGSRender() ctx.buffer_views_to_clean.clear(); } - m_draw_fbo.reset(); - //Textures m_rtts.destroy(); m_texture_cache.destroy(); @@ -1352,7 +1347,7 @@ void VKGSRender::end() ds->old_contents.src_rect(), ds->old_contents.dst_rect(), vk::as_rtt(ds->old_contents.source)->get_view(0xAAE4, rsx::default_remap_vector), - ds, render_pass, m_framebuffers_to_clean); + ds, render_pass); // TODO: Flush management to avoid pass running out of ubo space (very unlikely) ds->on_write(); @@ -2137,7 +2132,7 @@ void VKGSRender::clear_surface(u32 mask) } m_attachment_clear_pass->run(*m_current_command_buffer, rtt, - region.rect, renderpass, m_framebuffers_to_clean); + region.rect, renderpass); rtt->change_layout(*m_current_command_buffer, old_layout); } @@ -2252,12 +2247,7 @@ void VKGSRender::advance_queued_frames() m_texture_cache.on_frame_end(); m_samplers_dirty.store(true); - //Remove stale framebuffers. Ref counted to prevent use-after-free - m_framebuffers_to_clean.remove_if([](std::unique_ptr& fbo) - { - if (fbo->unused_check_count() >= 2) return true; - return false; - }); + vk::remove_unused_framebuffers(); m_vertex_cache->purge(); m_current_frame->tag_frame_end(m_attrib_ring_info.get_current_put_pos_minus_one(), @@ -3049,10 +3039,9 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context) } m_current_renderpass_key = vk::get_renderpass_key(m_fbo_images); - m_cached_renderpass = VK_NULL_HANDLE; + m_cached_renderpass = vk::get_renderpass(*m_device, m_current_renderpass_key); // Search old framebuffers for this same configuration - bool framebuffer_found = false; const auto fbo_width = rsx::apply_resolution_scale(layout.width, true); const auto fbo_height = rsx::apply_resolution_scale(layout.height, true); @@ -3062,60 +3051,8 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context) m_draw_fbo->release(); } - for (auto &fbo : m_framebuffers_to_clean) - { - if (fbo->matches(m_fbo_images, fbo_width, fbo_height)) - { - m_draw_fbo.swap(fbo); - m_draw_fbo->add_ref(); - framebuffer_found = true; - break; - } - } - - if (!framebuffer_found) - { - std::vector> fbo_images; - fbo_images.reserve(5); - - for (u8 index : draw_buffers) - { - if (vk::image *raw = std::get<1>(m_rtts.m_bound_render_targets[index])) - { - VkImageSubresourceRange subres = {}; - subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subres.baseArrayLayer = 0; - subres.baseMipLevel = 0; - subres.layerCount = 1; - subres.levelCount = 1; - - fbo_images.push_back(std::make_unique(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres)); - } - } - - if (std::get<1>(m_rtts.m_bound_depth_stencil) != nullptr) - { - vk::image *raw = (std::get<1>(m_rtts.m_bound_depth_stencil)); - - VkImageSubresourceRange subres = {}; - subres.aspectMask = (rsx::method_registers.surface_depth_fmt() == rsx::surface_depth_format::z24s8) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_DEPTH_BIT; - subres.baseArrayLayer = 0; - subres.baseMipLevel = 0; - subres.layerCount = 1; - subres.levelCount = 1; - - fbo_images.push_back(std::make_unique(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres)); - } - - VkRenderPass current_render_pass = vk::get_renderpass(*m_device, m_current_renderpass_key); - verify("Usupported renderpass configuration" HERE), current_render_pass != VK_NULL_HANDLE; - - if (m_draw_fbo) - m_framebuffers_to_clean.push_back(std::move(m_draw_fbo)); - - m_draw_fbo.reset(new vk::framebuffer_holder(*m_device, current_render_pass, fbo_width, fbo_height, std::move(fbo_images))); - m_draw_fbo->add_ref(); - } + m_draw_fbo = vk::get_framebuffer(*m_device, fbo_width, fbo_height, m_cached_renderpass, m_fbo_images); + m_draw_fbo->add_ref(); set_viewport(); set_scissor(); @@ -3465,8 +3402,6 @@ void VKGSRender::flip(int buffer, bool emu_flip) vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, present_layout, range); } - std::unique_ptr direct_fbo; - std::vector> swap_image_view; const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible()); if (g_cfg.video.overlay || has_overlay) { @@ -3489,24 +3424,8 @@ void VKGSRender::flip(int buffer, bool emu_flip) VkRenderPass single_target_pass = vk::get_renderpass(*m_device, key); verify("Usupported renderpass configuration" HERE), single_target_pass != VK_NULL_HANDLE; - for (auto It = m_framebuffers_to_clean.begin(); It != m_framebuffers_to_clean.end(); It++) - { - auto &fbo = *It; - if (fbo->attachments[0]->info.image == target_image) - { - direct_fbo.swap(fbo); - direct_fbo->add_ref(); - m_framebuffers_to_clean.erase(It); - break; - } - } - - if (!direct_fbo) - { - swap_image_view.push_back(std::make_unique(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swapchain->get_surface_format(), vk::default_component_map(), subres)); - direct_fbo.reset(new vk::framebuffer_holder(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view))); - direct_fbo->add_ref(); - } + auto direct_fbo = vk::get_framebuffer(*m_device, m_client_width, m_client_height, single_target_pass, m_swapchain->get_surface_format(), target_image); + direct_fbo->add_ref(); if (has_overlay) { @@ -3515,7 +3434,7 @@ void VKGSRender::flip(int buffer, bool emu_flip) for (const auto& view : m_overlay_manager->get_views()) { - m_ui_renderer->run(*m_current_command_buffer, direct_fbo->width(), direct_fbo->height(), direct_fbo.get(), single_target_pass, m_texture_upload_buffer_ring_info, *view.get()); + m_ui_renderer->run(*m_current_command_buffer, direct_fbo->width(), direct_fbo->height(), direct_fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get()); } } @@ -3547,7 +3466,6 @@ void VKGSRender::flip(int buffer, bool emu_flip) vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, present_layout, subres); direct_fbo->release(); - m_framebuffers_to_clean.push_back(std::move(direct_fbo)); } queue_swap_request(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 07a22d1ac1..fd47a51428 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -7,6 +7,7 @@ #include "VKTextOut.h" #include "VKOverlays.h" #include "VKProgramBuffer.h" +#include "VKFramebuffer.h" #include "../GCM.h" #include "../rsx_utils.h" #include @@ -409,7 +410,7 @@ private: VkDescriptorSetLayout descriptor_layouts; VkPipelineLayout pipeline_layout; - std::unique_ptr m_draw_fbo; + vk::framebuffer_holder* m_draw_fbo = nullptr; bool present_surface_dirty_flag = false; bool renderer_unavailable = false; @@ -438,9 +439,6 @@ private: //Temp frame context to use if the real frame queue is overburdened. Only used for storage frame_context_t m_aux_frame_context; - //framebuffers are shared between frame contexts - std::list> m_framebuffers_to_clean; - u32 m_current_queue_index = 0; frame_context_t* m_current_frame = nullptr; std::deque m_queued_frames; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index 752f71152b..7568f3e0d3 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -2,6 +2,7 @@ #include "VKHelpers.h" #include "VKCompute.h" #include "VKRenderPass.h" +#include "VKFramebuffer.h" #include "Utilities/mutex.h" namespace vk @@ -237,6 +238,7 @@ namespace vk { VkDevice dev = *g_current_renderer; vk::clear_renderpass_cache(dev); + vk::clear_framebuffer_cache(); g_null_texture.reset(); g_null_image_view.reset(); diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index eca4c7f1d5..456c694ff7 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -3,6 +3,7 @@ #include "VKVertexProgram.h" #include "VKFragmentProgram.h" #include "VKRenderTargets.h" +#include "VKFramebuffer.h" #include "../Overlays/overlays.h" @@ -304,42 +305,10 @@ namespace vk m_ubo.reset_allocation_stats(); } - vk::framebuffer* get_framebuffer(vk::image* target, VkRenderPass render_pass, std::list>& framebuffer_resources) + vk::framebuffer* get_framebuffer(vk::image* target, VkRenderPass render_pass) { - std::vector test = {target}; - for (auto It = framebuffer_resources.begin(); It != framebuffer_resources.end(); It++) - { - auto fbo = It->get(); - if (fbo->matches(test, target->width(), target->height())) - { - fbo->add_ref(); - return fbo; - } - } - - //No match, create new fbo and add to the list - std::vector> views; - VkComponentMapping mapping = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; - VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - - switch (target->info.format) - { - case VK_FORMAT_D16_UNORM: - case VK_FORMAT_D24_UNORM_S8_UINT: - case VK_FORMAT_D32_SFLOAT_S8_UINT: - range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; //We are only writing to depth - break; - } - - auto view = std::make_unique(*m_device, target->value, VK_IMAGE_VIEW_TYPE_2D, target->info.format, mapping, range); - views.push_back(std::move(view)); - - auto fbo = std::make_unique(*m_device, render_pass, target->width(), target->height(), std::move(views)); - auto result = fbo.get(); - framebuffer_resources.push_back(std::move(fbo)); - - result->add_ref(); - return result; + VkDevice dev = (*vk::get_current_renderer()); + return vk::get_framebuffer(dev, target->width(), target->height(), render_pass, { target }); } virtual void emit_geometry(vk::command_buffer &cmd) @@ -379,19 +348,19 @@ namespace vk vkCmdEndRenderPass(cmd); } - void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, const std::vector& src, VkRenderPass render_pass, std::list>& framebuffer_resources) + void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, const std::vector& src, VkRenderPass render_pass) { - vk::framebuffer *fbo = get_framebuffer(target, render_pass, framebuffer_resources); + vk::framebuffer *fbo = get_framebuffer(target, render_pass); run(cmd, w, h, fbo, src, render_pass); static_cast(fbo)->release(); } - void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, vk::image_view* src, VkRenderPass render_pass, std::list>& framebuffer_resources) + void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, vk::image_view* src, VkRenderPass render_pass) { std::vector views = { src }; - run(cmd, w, h, target, views, render_pass, framebuffer_resources); + run(cmd, w, h, target, views, render_pass); } }; @@ -448,7 +417,7 @@ namespace vk m_ubo.unmap(); } - void run(vk::command_buffer& cmd, const areai& src_area, const areai& dst_area, vk::image_view* src, vk::image* dst, VkRenderPass render_pass, std::list>& framebuffer_resources) + void run(vk::command_buffer& cmd, const areai& src_area, const areai& dst_area, vk::image_view* src, vk::image* dst, VkRenderPass render_pass) { auto real_src = src->image(); verify(HERE), real_src; @@ -456,7 +425,7 @@ namespace vk src_scale_x = f32(src_area.x2) / real_src->width(); src_scale_y = f32(src_area.y2) / real_src->height(); - overlay_pass::run(cmd, dst_area.x2, dst_area.y2, dst, src, render_pass, framebuffer_resources); + overlay_pass::run(cmd, dst_area.x2, dst_area.y2, dst, src, render_pass); } }; @@ -928,13 +897,13 @@ namespace vk return false; } - void run(vk::command_buffer &cmd, vk::render_target* target, VkRect2D rect, VkRenderPass render_pass, std::list>& framebuffer_resources) + void run(vk::command_buffer &cmd, vk::render_target* target, VkRect2D rect, VkRenderPass render_pass) { region = rect; overlay_pass::run(cmd, target->width(), target->height(), target, target->get_view(0xAAE4, rsx::default_remap_vector), - render_pass, framebuffer_resources); + render_pass); } }; } diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index 5333a09da9..bbc1c4a2f7 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -2,16 +2,16 @@ #include "stdafx.h" #include "VKHelpers.h" +#include "VKFormats.h" #include "../GCM.h" #include "../Common/surface_store.h" #include "../Common/TextureUtils.h" #include "../Common/texture_cache_utils.h" -#include "VKFormats.h" #include "../rsx_utils.h" namespace vk { - struct render_target : public viewable_image, public rsx::ref_counted, public rsx::render_target_descriptor + struct render_target : public viewable_image, public rsx::ref_counted, public rsx::render_target_descriptor { u16 native_pitch = 0; u16 rsx_pitch = 0; @@ -23,9 +23,9 @@ namespace vk using viewable_image::viewable_image; - vk::image* get_surface() override + vk::viewable_image* get_surface() override { - return (vk::image*)this; + return (vk::viewable_image*)this; } u16 get_surface_width() const override @@ -53,7 +53,7 @@ namespace vk return !!(aspect() & VK_IMAGE_ASPECT_DEPTH_BIT); } - void release_ref(vk::image* t) const override + void release_ref(vk::viewable_image* t) const override { static_cast(t)->release(); } @@ -166,17 +166,6 @@ namespace vk void write_barrier(vk::command_buffer& cmd) { memory_barrier(cmd, false); } }; - struct framebuffer_holder: public vk::framebuffer, public rsx::ref_counted - { - framebuffer_holder(VkDevice dev, - VkRenderPass pass, - u32 width, u32 height, - std::vector> &&atts) - - : framebuffer(dev, pass, width, height, std::move(atts)) - {} - }; - static inline vk::render_target* as_rtt(vk::image* t) { return static_cast(t); diff --git a/rpcs3/VKGSRender.vcxproj b/rpcs3/VKGSRender.vcxproj index 6349f07737..6c47594e9a 100644 --- a/rpcs3/VKGSRender.vcxproj +++ b/rpcs3/VKGSRender.vcxproj @@ -27,6 +27,7 @@ + @@ -42,10 +43,11 @@ + - + diff --git a/rpcs3/VKGSRender.vcxproj.filters b/rpcs3/VKGSRender.vcxproj.filters index 3051de64c0..c0440bbb31 100644 --- a/rpcs3/VKGSRender.vcxproj.filters +++ b/rpcs3/VKGSRender.vcxproj.filters @@ -49,6 +49,9 @@ Source Files + + Source Files + @@ -87,5 +90,8 @@ Source Files + + Source Files + \ No newline at end of file