vk: Refactor framebuffers

- Refactor out framebuffers from the renderer core
- Use a proper cache with sorted queues for faster searching
This commit is contained in:
kd-11 2019-05-27 12:16:18 +03:00 committed by kd-11
parent 71b71537a0
commit 57eb892153
9 changed files with 146 additions and 155 deletions

View File

@ -0,0 +1,90 @@
#include "stdafx.h"
#include "VKFramebuffer.h"
namespace vk
{
std::unordered_map<u64, std::vector<std::unique_ptr<vk::framebuffer_holder>>> g_framebuffers_cache;
vk::framebuffer_holder *get_framebuffer(VkDevice dev, u16 width, u16 height, VkRenderPass renderpass, const std::vector<vk::image*>& 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<std::unique_ptr<vk::image_view>> 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<vk::image_view>(dev, e, vk::default_component_map(), subres));
}
auto value = std::make_unique<vk::framebuffer_holder>(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<std::unique_ptr<vk::image_view>> views;
views.push_back(std::make_unique<vk::image_view>(dev, attachment, VK_IMAGE_VIEW_TYPE_2D, format, vk::default_component_map(), range));
auto value = std::make_unique<vk::framebuffer_holder>(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();
}
}

View File

@ -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<vk::image*>& 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();
}

View File

@ -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<vk::framebuffer_holder>& 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<std::unique_ptr<vk::image_view>> 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<vk::image_view>(*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<vk::image_view>(*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<vk::framebuffer_holder> direct_fbo;
std::vector<std::unique_ptr<vk::image_view>> 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<vk::image_view>(*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();

View File

@ -7,6 +7,7 @@
#include "VKTextOut.h"
#include "VKOverlays.h"
#include "VKProgramBuffer.h"
#include "VKFramebuffer.h"
#include "../GCM.h"
#include "../rsx_utils.h"
#include <thread>
@ -409,7 +410,7 @@ private:
VkDescriptorSetLayout descriptor_layouts;
VkPipelineLayout pipeline_layout;
std::unique_ptr<vk::framebuffer_holder> 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<std::unique_ptr<vk::framebuffer_holder>> m_framebuffers_to_clean;
u32 m_current_queue_index = 0;
frame_context_t* m_current_frame = nullptr;
std::deque<frame_context_t*> m_queued_frames;

View File

@ -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();

View File

@ -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<std::unique_ptr<vk::framebuffer_holder>>& framebuffer_resources)
vk::framebuffer* get_framebuffer(vk::image* target, VkRenderPass render_pass)
{
std::vector<vk::image*> 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<std::unique_ptr<vk::image_view>> 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<vk::image_view>(*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<vk::framebuffer_holder>(*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<vk::image_view*>& src, VkRenderPass render_pass, std::list<std::unique_ptr<vk::framebuffer_holder>>& framebuffer_resources)
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, const std::vector<vk::image_view*>& 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<vk::framebuffer_holder*>(fbo)->release();
}
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, vk::image_view* src, VkRenderPass render_pass, std::list<std::unique_ptr<vk::framebuffer_holder>>& framebuffer_resources)
void run(vk::command_buffer &cmd, u16 w, u16 h, vk::image* target, vk::image_view* src, VkRenderPass render_pass)
{
std::vector<vk::image_view*> 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<std::unique_ptr<vk::framebuffer_holder>>& 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<std::unique_ptr<vk::framebuffer_holder>>& 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);
}
};
}

View File

@ -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<vk::image*>
struct render_target : public viewable_image, public rsx::ref_counted, public rsx::render_target_descriptor<vk::viewable_image*>
{
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<vk::render_target*>(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<std::unique_ptr<vk::image_view>> &&atts)
: framebuffer(dev, pass, width, height, std::move(atts))
{}
};
static inline vk::render_target* as_rtt(vk::image* t)
{
return static_cast<vk::render_target*>(t);

View File

@ -27,6 +27,7 @@
<ClInclude Include="Emu\RSX\VK\VKCompute.h" />
<ClInclude Include="Emu\RSX\VK\VKFormats.h" />
<ClInclude Include="Emu\RSX\VK\VKFragmentProgram.h" />
<ClInclude Include="Emu\RSX\VK\VKFramebuffer.h" />
<ClInclude Include="Emu\RSX\VK\VKGSRender.h" />
<ClInclude Include="Emu\RSX\VK\VKHelpers.h" />
<ClInclude Include="Emu\RSX\VK\VKOverlays.h" />
@ -42,10 +43,11 @@
<ClCompile Include="Emu\RSX\VK\VKCommonDecompiler.cpp" />
<ClCompile Include="Emu\RSX\VK\VKFormats.cpp" />
<ClCompile Include="Emu\RSX\VK\VKFragmentProgram.cpp" />
<ClCompile Include="Emu\RSX\VK\VKFramebuffer.cpp" />
<ClCompile Include="Emu\RSX\VK\VKGSRender.cpp" />
<ClCompile Include="Emu\RSX\VK\VKHelpers.cpp" />
<ClCompile Include="Emu\RSX\VK\VKProgramPipeline.cpp" />
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp" />
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp" />
<ClCompile Include="Emu\RSX\VK\VKTexture.cpp" />
<ClCompile Include="Emu\RSX\VK\VKVertexBuffers.cpp" />
<ClCompile Include="Emu\RSX\VK\VKVertexProgram.cpp" />

View File

@ -49,6 +49,9 @@
<ClInclude Include="Emu\RSX\VK\VKRenderPass.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKFramebuffer.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\RSX\VK\VKGSRender.cpp">
@ -87,5 +90,8 @@
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKFramebuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>