diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 752fa319a..3ca3cbed0 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -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() { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 8c7364378..8dfe08fb6 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -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(draw_rect.left), + .y = static_cast(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(draw_rect.left), static_cast(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 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) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 6faf36692..c70d2d3a7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -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 { return Common::ComputeHash64(&info, sizeof(Vulkan::SamplerInfo)); } }; - -template <> -struct hash { - 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 texture_samplers; SamplerInfo texture_cube_sampler; std::unordered_map samplers; - std::unordered_map framebuffers; PipelineInfo pipeline_info; StreamBuffer stream_buffer; ///< Vertex+Index+Uniform buffer diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp index 167054088..5d5f97e3c 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -2,10 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #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 formats{}; + std::array 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 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 diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.h b/src/video_core/renderer_vulkan/vk_renderpass_cache.h index bb4831be5..4c6e79610 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.h +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.h @@ -5,6 +5,7 @@ #pragma once #include +#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 { + 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 framebuffers; u32 cmd_count{}; }; diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 3ccb41f40..a8ec5cccc 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -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(clear.texture_rect.left), - static_cast(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(clear.texture_rect.left), + .y = static_cast(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) { diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index e088927d4..cf9dda18e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -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 reinterpreters; std::unordered_multimap texture_recycler; - std::unordered_map clear_framebuffers; }; class Surface : public VideoCore::SurfaceBase {