renderer_vulkan: Move framebuffer handling to the renderpass cache

This commit is contained in:
GPUCode
2023-01-27 00:25:14 +02:00
parent 920492925c
commit a5f86e9813
7 changed files with 175 additions and 162 deletions

View File

@@ -216,13 +216,25 @@ void RendererVulkan::BeginRendering() {
{}); {});
}); });
const RenderpassState renderpass_info = { renderpass_cache.ExitRenderpass();
.renderpass = renderpass_cache.GetPresentRenderpass(),
.framebuffer = swapchain.GetFramebuffer(),
.render_area = vk::Rect2D{.offset = {0, 0}, .extent = swapchain.GetExtent()},
.clear = vk::ClearValue{.color = clear_color}};
renderpass_cache.EnterRenderpass(renderpass_info); scheduler.Record([this, framebuffer = swapchain.GetFramebuffer(),
extent = swapchain.GetExtent()](vk::CommandBuffer cmdbuf) {
const vk::ClearValue clear{.color = clear_color};
const vk::RenderPassBeginInfo renderpass_begin_info = {
.renderPass = renderpass_cache.GetPresentRenderpass(),
.framebuffer = framebuffer,
.renderArea =
vk::Rect2D{
.offset = {0, 0},
.extent = extent,
},
.clearValueCount = 1,
.pClearValues = &clear,
};
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
});
} }
void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
@@ -896,7 +908,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
} }
} }
renderpass_cache.ExitRenderpass(); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.endRenderPass(); });
} }
void RendererVulkan::SwapBuffers() { void RendererVulkan::SwapBuffers() {

View File

@@ -144,10 +144,6 @@ RasterizerVulkan::~RasterizerVulkan() {
device.destroySampler(sampler); device.destroySampler(sampler);
} }
for (auto& [key, framebuffer] : framebuffers) {
device.destroyFramebuffer(framebuffer);
}
device.destroySampler(default_sampler); device.destroySampler(default_sampler);
device.destroyBufferView(texture_lf_view); device.destroyBufferView(texture_lf_view);
device.destroyBufferView(texture_rg_view); device.destroyBufferView(texture_rg_view);
@@ -674,48 +670,18 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
// NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will // NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will
// cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state // cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state
const vk::Rect2D render_area = {
// Sometimes the dimentions of the color and depth framebuffers might not be the same .offset{
// In that case select the minimum one to abide by the spec .x = static_cast<s32>(draw_rect.left),
u32 width = 0; .y = static_cast<s32>(draw_rect.bottom),
u32 height = 0; },
if (color_surface && depth_surface) { .extent{
width = std::min(color_surface->GetScaledWidth(), depth_surface->GetScaledWidth()); .width = draw_rect.GetWidth(),
height = std::min(color_surface->GetScaledHeight(), depth_surface->GetScaledHeight()); .height = draw_rect.GetHeight(),
} else if (color_surface) { },
width = color_surface->GetScaledWidth();
height = color_surface->GetScaledHeight();
} else if (depth_surface) {
width = depth_surface->GetScaledWidth();
height = depth_surface->GetScaledHeight();
}
const FramebufferInfo framebuffer_info = {
.color = color_surface ? color_surface->GetFramebufferView() : VK_NULL_HANDLE,
.depth = depth_surface ? depth_surface->GetFramebufferView() : VK_NULL_HANDLE,
.renderpass = renderpass_cache.GetRenderpass(pipeline_info.attachments.color_format,
pipeline_info.attachments.depth_format, false),
.width = width,
.height = height,
}; };
auto [it, new_framebuffer] = framebuffers.try_emplace(framebuffer_info, vk::Framebuffer{}); renderpass_cache.EnterRenderpass(color_surface.get(), depth_surface.get(), render_area);
if (new_framebuffer) {
it->second = CreateFramebuffer(framebuffer_info);
}
const RenderpassState renderpass_info = {
.renderpass = framebuffer_info.renderpass,
.framebuffer = it->second,
.render_area =
vk::Rect2D{
.offset = {static_cast<s32>(draw_rect.left), static_cast<s32>(draw_rect.bottom)},
.extent = {draw_rect.GetWidth(), draw_rect.GetHeight()},
},
.clear = {},
};
renderpass_cache.EnterRenderpass(renderpass_info);
// Sync and bind the shader // Sync and bind the shader
if (shader_dirty) { if (shader_dirty) {
@@ -1110,31 +1076,6 @@ vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& info) {
return device.createSampler(sampler_info); return device.createSampler(sampler_info);
} }
vk::Framebuffer RasterizerVulkan::CreateFramebuffer(const FramebufferInfo& info) {
u32 attachment_count = 0;
std::array<vk::ImageView, 2> attachments;
if (info.color) {
attachments[attachment_count++] = info.color;
}
if (info.depth) {
attachments[attachment_count++] = info.depth;
}
const vk::FramebufferCreateInfo framebuffer_info = {
.renderPass = info.renderpass,
.attachmentCount = attachment_count,
.pAttachments = attachments.data(),
.width = info.width,
.height = info.height,
.layers = 1,
};
vk::Device device = instance.GetDevice();
return device.createFramebuffer(framebuffer_info);
}
void RasterizerVulkan::SyncClipEnabled() { void RasterizerVulkan::SyncClipEnabled() {
bool clip_enabled = Pica::g_state.regs.rasterizer.clip_enable != 0; bool clip_enabled = Pica::g_state.regs.rasterizer.clip_enable != 0;
if (clip_enabled != uniform_block_data.data.enable_clip1) { if (clip_enabled != uniform_block_data.data.enable_clip1) {

View File

@@ -41,16 +41,6 @@ struct SamplerInfo {
auto operator<=>(const SamplerInfo&) const noexcept = default; auto operator<=>(const SamplerInfo&) const noexcept = default;
}; };
struct FramebufferInfo {
vk::ImageView color;
vk::ImageView depth;
vk::RenderPass renderpass;
u32 width = 1;
u32 height = 1;
auto operator<=>(const FramebufferInfo&) const noexcept = default;
};
} // namespace Vulkan } // namespace Vulkan
namespace std { namespace std {
@@ -60,13 +50,6 @@ struct hash<Vulkan::SamplerInfo> {
return Common::ComputeHash64(&info, sizeof(Vulkan::SamplerInfo)); return Common::ComputeHash64(&info, sizeof(Vulkan::SamplerInfo));
} }
}; };
template <>
struct hash<Vulkan::FramebufferInfo> {
std::size_t operator()(const Vulkan::FramebufferInfo& info) const noexcept {
return Common::ComputeHash64(&info, sizeof(Vulkan::FramebufferInfo));
}
};
} // namespace std } // namespace std
namespace Vulkan { namespace Vulkan {
@@ -167,9 +150,6 @@ private:
/// Creates a new sampler object /// Creates a new sampler object
vk::Sampler CreateSampler(const SamplerInfo& info); vk::Sampler CreateSampler(const SamplerInfo& info);
/// Creates a new Vulkan framebuffer object
vk::Framebuffer CreateFramebuffer(const FramebufferInfo& info);
private: private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
@@ -190,7 +170,6 @@ private:
std::array<SamplerInfo, 3> texture_samplers; std::array<SamplerInfo, 3> texture_samplers;
SamplerInfo texture_cube_sampler; SamplerInfo texture_cube_sampler;
std::unordered_map<SamplerInfo, vk::Sampler> samplers; std::unordered_map<SamplerInfo, vk::Sampler> samplers;
std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers;
PipelineInfo pipeline_info; PipelineInfo pipeline_info;
StreamBuffer stream_buffer; ///< Vertex+Index+Uniform buffer StreamBuffer stream_buffer; ///< Vertex+Index+Uniform buffer

View File

@@ -2,10 +2,12 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <limits>
#include "common/assert.h" #include "common/assert.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h" #include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
namespace Vulkan { namespace Vulkan {
@@ -26,12 +28,61 @@ RenderpassCache::~RenderpassCache() {
} }
} }
for (auto& [key, framebuffer] : framebuffers) {
device.destroyFramebuffer(framebuffer);
}
device.destroyRenderPass(present_renderpass); device.destroyRenderPass(present_renderpass);
} }
void RenderpassCache::EnterRenderpass(const RenderpassState& state) { void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth_stencil,
vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) {
ASSERT(color || depth_stencil);
u32 width = UINT32_MAX;
u32 height = UINT32_MAX;
u32 cursor = 0;
std::array<VideoCore::PixelFormat, 2> formats{};
std::array<vk::ImageView, 2> views{};
const auto Prepare = [&](Surface* const surface) {
if (!surface) {
formats[cursor++] = VideoCore::PixelFormat::Invalid;
return;
}
width = std::min(width, surface->GetScaledWidth());
height = std::min(height, surface->GetScaledHeight());
formats[cursor] = surface->pixel_format;
views[cursor++] = surface->GetFramebufferView();
};
Prepare(color);
Prepare(depth_stencil);
const vk::RenderPass renderpass = GetRenderpass(formats[0], formats[1], do_clear);
const FramebufferInfo framebuffer_info = {
.color = views[0],
.depth = views[1],
.width = width,
.height = height,
};
auto [it, new_framebuffer] = framebuffers.try_emplace(framebuffer_info);
if (new_framebuffer) {
it->second = CreateFramebuffer(framebuffer_info, renderpass);
}
const RenderpassState new_state = {
.renderpass = renderpass,
.framebuffer = it->second,
.render_area = render_area,
.clear = clear,
};
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
if (current_state == state && !is_dirty) { if (current_state == new_state && !is_dirty) {
cmd_count++; cmd_count++;
return; return;
} }
@@ -40,23 +91,20 @@ void RenderpassCache::EnterRenderpass(const RenderpassState& state) {
ExitRenderpass(); ExitRenderpass();
} }
scheduler.Record([state](vk::CommandBuffer cmdbuf) { scheduler.Record([new_state](vk::CommandBuffer cmdbuf) {
const vk::RenderPassBeginInfo renderpass_begin_info = { const vk::RenderPassBeginInfo renderpass_begin_info = {
.renderPass = state.renderpass, .renderPass = new_state.renderpass,
.framebuffer = state.framebuffer, .framebuffer = new_state.framebuffer,
.renderArea = state.render_area, .renderArea = new_state.render_area,
.clearValueCount = 1, .clearValueCount = 1,
.pClearValues = &state.clear, .pClearValues = &new_state.clear,
}; };
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
}); });
if (is_dirty) { scheduler.MarkStateNonDirty(StateFlags::Renderpass);
scheduler.MarkStateNonDirty(StateFlags::Renderpass); current_state = new_state;
}
current_state = state;
} }
void RenderpassCache::ExitRenderpass() { void RenderpassCache::ExitRenderpass() {
@@ -178,8 +226,32 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de
.pDependencies = nullptr, .pDependencies = nullptr,
}; };
const vk::Device device = instance.GetDevice(); return instance.GetDevice().createRenderPass(renderpass_info);
return device.createRenderPass(renderpass_info); }
vk::Framebuffer RenderpassCache::CreateFramebuffer(const FramebufferInfo& info,
vk::RenderPass renderpass) {
u32 attachment_count = 0;
std::array<vk::ImageView, 2> attachments;
if (info.color) {
attachments[attachment_count++] = info.color;
}
if (info.depth) {
attachments[attachment_count++] = info.depth;
}
const vk::FramebufferCreateInfo framebuffer_info = {
.renderPass = renderpass,
.attachmentCount = attachment_count,
.pAttachments = attachments.data(),
.width = info.width,
.height = info.height,
.layers = 1,
};
return instance.GetDevice().createFramebuffer(framebuffer_info);
} }
} // namespace Vulkan } // namespace Vulkan

View File

@@ -5,6 +5,7 @@
#pragma once #pragma once
#include <cstring> #include <cstring>
#include "common/hash.h"
#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
@@ -12,17 +13,29 @@ namespace Vulkan {
class Instance; class Instance;
class Scheduler; class Scheduler;
class Surface;
struct RenderpassState { struct FramebufferInfo {
vk::RenderPass renderpass; vk::ImageView color;
vk::Framebuffer framebuffer; vk::ImageView depth;
vk::Rect2D render_area; u32 width = 1;
vk::ClearValue clear; u32 height = 1;
[[nodiscard]] bool operator==(const RenderpassState& other) const { auto operator<=>(const FramebufferInfo&) const noexcept = default;
return std::memcmp(this, &other, sizeof(RenderpassState)) == 0; };
} // namespace Vulkan
namespace std {
template <>
struct hash<Vulkan::FramebufferInfo> {
std::size_t operator()(const Vulkan::FramebufferInfo& info) const noexcept {
return Common::ComputeStructHash64(info);
} }
}; };
} // namespace std
namespace Vulkan {
class RenderpassCache { class RenderpassCache {
static constexpr u32 MAX_COLOR_FORMATS = 5; static constexpr u32 MAX_COLOR_FORMATS = 5;
@@ -33,7 +46,8 @@ public:
~RenderpassCache(); ~RenderpassCache();
/// Begins a new renderpass only when no other renderpass is currently active /// Begins a new renderpass only when no other renderpass is currently active
void EnterRenderpass(const RenderpassState& state); void EnterRenderpass(Surface* const color, Surface* const depth_stencil, vk::Rect2D render_area,
bool do_clear = false, vk::ClearValue clear = {});
/// Exits from any currently active renderpass instance /// Exits from any currently active renderpass instance
void ExitRenderpass(); void ExitRenderpass();
@@ -56,12 +70,27 @@ private:
vk::AttachmentLoadOp load_op, vk::ImageLayout initial_layout, vk::AttachmentLoadOp load_op, vk::ImageLayout initial_layout,
vk::ImageLayout final_layout) const; vk::ImageLayout final_layout) const;
/// Creates a new Vulkan framebuffer object
vk::Framebuffer CreateFramebuffer(const FramebufferInfo& info, vk::RenderPass renderpass);
private: private:
struct RenderpassState {
vk::RenderPass renderpass;
vk::Framebuffer framebuffer;
vk::Rect2D render_area;
vk::ClearValue clear;
[[nodiscard]] bool operator==(const RenderpassState& other) const {
return std::memcmp(this, &other, sizeof(RenderpassState)) == 0;
}
};
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
RenderpassState current_state{}; RenderpassState current_state{};
vk::RenderPass present_renderpass{}; vk::RenderPass present_renderpass{};
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2]; vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2];
std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers;
u32 cmd_count{}; u32 cmd_count{};
}; };

View File

@@ -102,7 +102,7 @@ constexpr u64 DOWNLOAD_BUFFER_SIZE = 32 * 1024 * 1024;
TextureRuntime::TextureRuntime(const Instance& instance, Scheduler& scheduler, TextureRuntime::TextureRuntime(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorManager& desc_manager) RenderpassCache& renderpass_cache, DescriptorManager& desc_manager)
: instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache}, : instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache},
desc_manager{desc_manager}, blit_helper{instance, scheduler, desc_manager, renderpass_cache}, blit_helper{instance, scheduler, desc_manager, renderpass_cache},
upload_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferSrc, UPLOAD_BUFFER_SIZE}, upload_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferSrc, UPLOAD_BUFFER_SIZE},
download_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferDst, download_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferDst,
DOWNLOAD_BUFFER_SIZE, true} { DOWNLOAD_BUFFER_SIZE, true} {
@@ -137,10 +137,6 @@ TextureRuntime::~TextureRuntime() {
} }
} }
for (const auto& [key, framebuffer] : clear_framebuffers) {
device.destroyFramebuffer(framebuffer);
}
texture_recycler.clear(); texture_recycler.clear();
} }
@@ -449,41 +445,6 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface,
is_color ? vk::PipelineStageFlagBits::eColorAttachmentOutput is_color ? vk::PipelineStageFlagBits::eColorAttachmentOutput
: vk::PipelineStageFlagBits::eEarlyFragmentTests; : vk::PipelineStageFlagBits::eEarlyFragmentTests;
const vk::RenderPass clear_renderpass =
is_color ? renderpass_cache.GetRenderpass(surface.pixel_format,
VideoCore::PixelFormat::Invalid, true)
: renderpass_cache.GetRenderpass(VideoCore::PixelFormat::Invalid,
surface.pixel_format, true);
const vk::ImageView framebuffer_view = surface.GetFramebufferView();
auto [it, new_framebuffer] =
clear_framebuffers.try_emplace(framebuffer_view, vk::Framebuffer{});
if (new_framebuffer) {
const vk::FramebufferCreateInfo framebuffer_info = {
.renderPass = clear_renderpass,
.attachmentCount = 1,
.pAttachments = &framebuffer_view,
.width = surface.GetScaledWidth(),
.height = surface.GetScaledHeight(),
.layers = 1,
};
it->second = instance.GetDevice().createFramebuffer(framebuffer_info);
}
const RenderpassState clear_info = {
.renderpass = clear_renderpass,
.framebuffer = it->second,
.render_area =
vk::Rect2D{
.offset = {static_cast<s32>(clear.texture_rect.left),
static_cast<s32>(clear.texture_rect.bottom)},
.extent = {clear.texture_rect.GetWidth(), clear.texture_rect.GetHeight()},
},
.clear = MakeClearValue(value),
};
const RecordParams params = { const RecordParams params = {
.aspect = surface.alloc.aspect, .aspect = surface.alloc.aspect,
.pipeline_flags = surface.PipelineStageFlags(), .pipeline_flags = surface.PipelineStageFlags(),
@@ -513,7 +474,27 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface,
vk::DependencyFlagBits::eByRegion, {}, {}, pre_barrier); vk::DependencyFlagBits::eByRegion, {}, {}, pre_barrier);
}); });
renderpass_cache.EnterRenderpass(clear_info); Surface* color_surface{};
Surface* depth_surface{};
if (is_color) {
color_surface = &surface;
} else {
depth_surface = &surface;
}
const vk::Rect2D render_area = {
.offset{
.x = static_cast<s32>(clear.texture_rect.left),
.y = static_cast<s32>(clear.texture_rect.bottom),
},
.extent{
.width = clear.texture_rect.GetWidth(),
.height = clear.texture_rect.GetHeight(),
},
};
renderpass_cache.EnterRenderpass(color_surface, depth_surface, render_area, true,
MakeClearValue(value));
renderpass_cache.ExitRenderpass(); renderpass_cache.ExitRenderpass();
scheduler.Record([params, access_flag, pipeline_flags](vk::CommandBuffer cmdbuf) { scheduler.Record([params, access_flag, pipeline_flags](vk::CommandBuffer cmdbuf) {

View File

@@ -44,6 +44,7 @@ struct ImageAlloc {
vk::ImageUsageFlags usage; vk::ImageUsageFlags usage;
vk::Format format; vk::Format format;
vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor; vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor;
vk::ImageLayout layout;
}; };
struct HostTextureTag { struct HostTextureTag {
@@ -152,13 +153,11 @@ private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
RenderpassCache& renderpass_cache; RenderpassCache& renderpass_cache;
DescriptorManager& desc_manager;
BlitHelper blit_helper; BlitHelper blit_helper;
StreamBuffer upload_buffer; StreamBuffer upload_buffer;
StreamBuffer download_buffer; StreamBuffer download_buffer;
std::array<ReinterpreterList, VideoCore::PIXEL_FORMAT_COUNT> reinterpreters; std::array<ReinterpreterList, VideoCore::PIXEL_FORMAT_COUNT> reinterpreters;
std::unordered_multimap<HostTextureTag, ImageAlloc> texture_recycler; std::unordered_multimap<HostTextureTag, ImageAlloc> texture_recycler;
std::unordered_map<vk::ImageView, vk::Framebuffer> clear_framebuffers;
}; };
class Surface : public VideoCore::SurfaceBase<Surface> { class Surface : public VideoCore::SurfaceBase<Surface> {