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 = renderpass_cache.GetPresentRenderpass(),
.framebuffer = swapchain.GetFramebuffer(),
.render_area = vk::Rect2D{.offset = {0, 0}, .extent = swapchain.GetExtent()},
.clear = vk::ClearValue{.color = clear_color}};
renderpass_cache.ExitRenderpass();
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,
@@ -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() {

View File

@@ -144,10 +144,6 @@ RasterizerVulkan::~RasterizerVulkan() {
device.destroySampler(sampler);
}
for (auto& [key, framebuffer] : framebuffers) {
device.destroyFramebuffer(framebuffer);
}
device.destroySampler(default_sampler);
device.destroyBufferView(texture_lf_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
// cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state
// Sometimes the dimentions of the color and depth framebuffers might not be the same
// In that case select the minimum one to abide by the spec
u32 width = 0;
u32 height = 0;
if (color_surface && depth_surface) {
width = std::min(color_surface->GetScaledWidth(), depth_surface->GetScaledWidth());
height = std::min(color_surface->GetScaledHeight(), depth_surface->GetScaledHeight());
} 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,
const vk::Rect2D render_area = {
.offset{
.x = static_cast<s32>(draw_rect.left),
.y = static_cast<s32>(draw_rect.bottom),
},
.extent{
.width = draw_rect.GetWidth(),
.height = draw_rect.GetHeight(),
},
};
auto [it, new_framebuffer] = framebuffers.try_emplace(framebuffer_info, vk::Framebuffer{});
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);
renderpass_cache.EnterRenderpass(color_surface.get(), depth_surface.get(), render_area);
// Sync and bind the shader
if (shader_dirty) {
@@ -1110,31 +1076,6 @@ vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& 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() {
bool clip_enabled = Pica::g_state.regs.rasterizer.clip_enable != 0;
if (clip_enabled != uniform_block_data.data.enable_clip1) {

View File

@@ -41,16 +41,6 @@ struct SamplerInfo {
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 std {
@@ -60,13 +50,6 @@ struct hash<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 Vulkan {
@@ -167,9 +150,6 @@ private:
/// Creates a new sampler object
vk::Sampler CreateSampler(const SamplerInfo& info);
/// Creates a new Vulkan framebuffer object
vk::Framebuffer CreateFramebuffer(const FramebufferInfo& info);
private:
const Instance& instance;
Scheduler& scheduler;
@@ -190,7 +170,6 @@ private:
std::array<SamplerInfo, 3> texture_samplers;
SamplerInfo texture_cube_sampler;
std::unordered_map<SamplerInfo, vk::Sampler> samplers;
std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers;
PipelineInfo pipeline_info;
StreamBuffer stream_buffer; ///< Vertex+Index+Uniform buffer

View File

@@ -2,10 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <limits>
#include "common/assert.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
namespace Vulkan {
@@ -26,12 +28,61 @@ RenderpassCache::~RenderpassCache() {
}
}
for (auto& [key, framebuffer] : framebuffers) {
device.destroyFramebuffer(framebuffer);
}
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);
if (current_state == state && !is_dirty) {
if (current_state == new_state && !is_dirty) {
cmd_count++;
return;
}
@@ -40,23 +91,20 @@ void RenderpassCache::EnterRenderpass(const RenderpassState& state) {
ExitRenderpass();
}
scheduler.Record([state](vk::CommandBuffer cmdbuf) {
scheduler.Record([new_state](vk::CommandBuffer cmdbuf) {
const vk::RenderPassBeginInfo renderpass_begin_info = {
.renderPass = state.renderpass,
.framebuffer = state.framebuffer,
.renderArea = state.render_area,
.renderPass = new_state.renderpass,
.framebuffer = new_state.framebuffer,
.renderArea = new_state.render_area,
.clearValueCount = 1,
.pClearValues = &state.clear,
.pClearValues = &new_state.clear,
};
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
});
if (is_dirty) {
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
}
current_state = state;
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
current_state = new_state;
}
void RenderpassCache::ExitRenderpass() {
@@ -178,8 +226,32 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de
.pDependencies = nullptr,
};
const vk::Device device = instance.GetDevice();
return device.createRenderPass(renderpass_info);
return instance.GetDevice().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

View File

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

View File

@@ -102,7 +102,7 @@ constexpr u64 DOWNLOAD_BUFFER_SIZE = 32 * 1024 * 1024;
TextureRuntime::TextureRuntime(const Instance& instance, Scheduler& scheduler,
RenderpassCache& renderpass_cache, DescriptorManager& desc_manager)
: 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},
download_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferDst,
DOWNLOAD_BUFFER_SIZE, true} {
@@ -137,10 +137,6 @@ TextureRuntime::~TextureRuntime() {
}
}
for (const auto& [key, framebuffer] : clear_framebuffers) {
device.destroyFramebuffer(framebuffer);
}
texture_recycler.clear();
}
@@ -449,41 +445,6 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface,
is_color ? vk::PipelineStageFlagBits::eColorAttachmentOutput
: 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 = {
.aspect = surface.alloc.aspect,
.pipeline_flags = surface.PipelineStageFlags(),
@@ -513,7 +474,27 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface,
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();
scheduler.Record([params, access_flag, pipeline_flags](vk::CommandBuffer cmdbuf) {

View File

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