diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/Renderer.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/Renderer.cpp | 447 |
1 files changed, 282 insertions, 165 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 3fac85e340..066f3f512a 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -13,6 +13,7 @@ #include "Common/MsgHandler.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" #include "VideoBackends/Vulkan/BoundingBox.h" #include "VideoBackends/Vulkan/CommandBufferManager.h" @@ -63,15 +64,18 @@ Renderer::~Renderer() { g_Config.bRunning = false; UpdateActiveConfig(); - DestroyScreenshotResources(); + DestroyFrameDumpResources(); DestroyShaders(); DestroySemaphores(); } -bool Renderer::Initialize(FramebufferManager* framebuffer_mgr) +Renderer* Renderer::GetInstance() +{ + return static_cast<Renderer*>(g_renderer.get()); +} + +bool Renderer::Initialize() { - m_framebuffer_mgr = framebuffer_mgr; - m_state_tracker = std::make_unique<StateTracker>(); BindEFBToStateTracker(); if (!CreateSemaphores()) @@ -103,9 +107,9 @@ bool Renderer::Initialize(FramebufferManager* framebuffer_mgr) if (g_vulkan_context->SupportsBoundingBox()) { // Bind bounding box to state tracker - m_state_tracker->SetBBoxBuffer(m_bounding_box->GetGPUBuffer(), - m_bounding_box->GetGPUBufferOffset(), - m_bounding_box->GetGPUBufferSize()); + StateTracker::GetInstance()->SetBBoxBuffer(m_bounding_box->GetGPUBuffer(), + m_bounding_box->GetGPUBufferOffset(), + m_bounding_box->GetGPUBufferSize()); } // Various initialization routines will have executed commands on the command buffer. @@ -170,7 +174,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) { if (type == PEEK_COLOR) { - u32 color = m_framebuffer_mgr->PeekEFBColor(m_state_tracker.get(), x, y); + u32 color = FramebufferManager::GetInstance()->PeekEFBColor(x, y); // a little-endian value is expected to be returned color = ((color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000)); @@ -207,7 +211,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) else // if (type == PEEK_Z) { // Depth buffer is inverted for improved precision near far plane - float depth = 1.0f - m_framebuffer_mgr->PeekEFBDepth(m_state_tracker.get(), x, y); + float depth = 1.0f - FramebufferManager::GetInstance()->PeekEFBDepth(x, y); u32 ret = 0; if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16) @@ -235,7 +239,7 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num const EfbPokeData& point = points[i]; u32 color = ((point.data & 0xFF00FF00) | ((point.data >> 16) & 0xFF) | ((point.data << 16) & 0xFF0000)); - m_framebuffer_mgr->PokeEFBColor(m_state_tracker.get(), point.x, point.y, color); + FramebufferManager::GetInstance()->PokeEFBColor(point.x, point.y, color); } } else // if (type == POKE_Z) @@ -245,14 +249,14 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num // Convert to floating-point depth. const EfbPokeData& point = points[i]; float depth = (1.0f - float(point.data & 0xFFFFFF) / 16777216.0f); - m_framebuffer_mgr->PokeEFBDepth(m_state_tracker.get(), point.x, point.y, depth); + FramebufferManager::GetInstance()->PokeEFBDepth(point.x, point.y, depth); } } } u16 Renderer::BBoxRead(int index) { - s32 value = m_bounding_box->Get(m_state_tracker.get(), static_cast<size_t>(index)); + s32 value = m_bounding_box->Get(static_cast<size_t>(index)); // Here we get the min/max value of the truncated position of the upscaled framebuffer. // So we have to correct them to the unscaled EFB sizes. @@ -294,7 +298,7 @@ void Renderer::BBoxWrite(int index, u16 value) scaled_value = scaled_value * s_target_height / EFB_HEIGHT; } - m_bounding_box->Set(m_state_tracker.get(), static_cast<size_t>(index), scaled_value); + m_bounding_box->Set(static_cast<size_t>(index), scaled_value); } TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) @@ -314,8 +318,8 @@ void Renderer::BeginFrame() // Ensure that the state tracker rebinds everything, and allocates a new set // of descriptors out of the next pool. - m_state_tracker->InvalidateDescriptorSets(); - m_state_tracker->SetPendingRebind(); + StateTracker::GetInstance()->InvalidateDescriptorSets(); + StateTracker::GetInstance()->SetPendingRebind(); } void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, @@ -327,15 +331,6 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha {target_rc.left, target_rc.top}, {static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}}; - // Convert RGBA8 -> floating-point values. - VkClearValue clear_color_value = {}; - VkClearValue clear_depth_value = {}; - clear_color_value.color.float32[0] = static_cast<float>((color >> 16) & 0xFF) / 255.0f; - clear_color_value.color.float32[1] = static_cast<float>((color >> 8) & 0xFF) / 255.0f; - clear_color_value.color.float32[2] = static_cast<float>((color >> 0) & 0xFF) / 255.0f; - clear_color_value.color.float32[3] = static_cast<float>((color >> 24) & 0xFF) / 255.0f; - clear_depth_value.depthStencil.depth = (1.0f - (static_cast<float>(z & 0xFFFFFF) / 16777216.0f)); - // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha // channel to 0xFF. This hopefully allows us to use the fast path in most cases. if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16 || @@ -347,10 +342,19 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha color |= 0xFF000000; } + // Convert RGBA8 -> floating-point values. + VkClearValue clear_color_value = {}; + VkClearValue clear_depth_value = {}; + clear_color_value.color.float32[0] = static_cast<float>((color >> 16) & 0xFF) / 255.0f; + clear_color_value.color.float32[1] = static_cast<float>((color >> 8) & 0xFF) / 255.0f; + clear_color_value.color.float32[2] = static_cast<float>((color >> 0) & 0xFF) / 255.0f; + clear_color_value.color.float32[3] = static_cast<float>((color >> 24) & 0xFF) / 255.0f; + clear_depth_value.depthStencil.depth = (1.0f - (static_cast<float>(z & 0xFFFFFF) / 16777216.0f)); + // If we're not in a render pass (start of the frame), we can use a clear render pass // to discard the data, rather than loading and then clearing. bool use_clear_render_pass = (color_enable && alpha_enable && z_enable); - if (m_state_tracker->InRenderPass()) + if (StateTracker::GetInstance()->InRenderPass()) { // Prefer not to end a render pass just to do a clear. use_clear_render_pass = false; @@ -360,7 +364,7 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha if (use_clear_render_pass) { VkClearValue clear_values[2] = {clear_color_value, clear_depth_value}; - m_state_tracker->BeginClearRenderPass(target_vk_rc, clear_values); + StateTracker::GetInstance()->BeginClearRenderPass(target_vk_rc, clear_values); return; } @@ -388,17 +392,17 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha } if (num_clear_attachments > 0) { - VkClearRect clear_rect = {target_vk_rc, 0, m_framebuffer_mgr->GetEFBLayers()}; - if (!m_state_tracker->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y, - target_vk_rc.extent.width, - target_vk_rc.extent.height)) + VkClearRect vk_rect = {target_vk_rc, 0, FramebufferManager::GetInstance()->GetEFBLayers()}; + if (!StateTracker::GetInstance()->IsWithinRenderArea( + target_vk_rc.offset.x, target_vk_rc.offset.y, target_vk_rc.extent.width, + target_vk_rc.extent.height)) { - m_state_tracker->EndClearRenderPass(); + StateTracker::GetInstance()->EndClearRenderPass(); } - m_state_tracker->BeginRenderPass(); + StateTracker::GetInstance()->BeginRenderPass(); vkCmdClearAttachments(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_clear_attachments, - clear_attachments, 1, &clear_rect); + clear_attachments, 1, &vk_rect); } } @@ -407,13 +411,14 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha return; // Clearing must occur within a render pass. - if (!m_state_tracker->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y, - target_vk_rc.extent.width, target_vk_rc.extent.height)) + if (!StateTracker::GetInstance()->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y, + target_vk_rc.extent.width, + target_vk_rc.extent.height)) { - m_state_tracker->EndClearRenderPass(); + StateTracker::GetInstance()->EndClearRenderPass(); } - m_state_tracker->BeginRenderPass(); - m_state_tracker->SetPendingRebind(); + StateTracker::GetInstance()->BeginRenderPass(); + StateTracker::GetInstance()->SetPendingRebind(); // Mask away the appropriate colors and use a shader BlendState blend_state = Util::GetNoBlendingBlendState(); @@ -431,13 +436,14 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha RasterizationState rs_state = Util::GetNoCullRasterizationState(); rs_state.per_sample_shading = g_ActiveConfig.bSSAA ? VK_TRUE : VK_FALSE; - rs_state.samples = m_framebuffer_mgr->GetEFBSamples(); + rs_state.samples = FramebufferManager::GetInstance()->GetEFBSamples(); // No need to start a new render pass, but we do need to restore viewport state - UtilityShaderDraw draw( - g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetStandardPipelineLayout(), - m_framebuffer_mgr->GetEFBLoadRenderPass(), g_object_cache->GetPassthroughVertexShader(), - g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader); + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), + g_object_cache->GetStandardPipelineLayout(), + FramebufferManager::GetInstance()->GetEFBLoadRenderPass(), + g_object_cache->GetPassthroughVertexShader(), + g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader); draw.SetRasterizationState(rs_state); draw.SetDepthStencilState(depth_state); @@ -451,9 +457,9 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha void Renderer::ReinterpretPixelData(unsigned int convtype) { - m_state_tracker->EndRenderPass(); - m_state_tracker->SetPendingRebind(); - m_framebuffer_mgr->ReinterpretPixelData(convtype); + StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->SetPendingRebind(); + FramebufferManager::GetInstance()->ReinterpretPixelData(convtype); // EFB framebuffer has now changed, so update accordingly. BindEFBToStateTracker(); @@ -462,37 +468,38 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, const EFBRectangle& rc, u64 ticks, float gamma) { - // Flush any pending EFB pokes. - m_framebuffer_mgr->FlushEFBPokes(m_state_tracker.get()); - - // End the current render pass. - m_state_tracker->EndRenderPass(); - m_state_tracker->OnEndFrame(); + // Pending/batched EFB pokes should be included in the final image. + FramebufferManager::GetInstance()->FlushEFBPokes(); - // Scale the source rectangle to the selected internal resolution. - TargetRectangle source_rc = Renderer::ConvertEFBRectangle(rc); + // Check that we actually have an image to render in XFB-on modes. + if ((!XFBWrited && !g_ActiveConfig.RealXFBEnabled()) || !fb_width || !fb_height) + { + Core::Callback_VideoCopiedToXFB(false); + return; + } + u32 xfb_count = 0; + const XFBSourceBase* const* xfb_sources = + FramebufferManager::GetXFBSource(xfb_addr, fb_stride, fb_height, &xfb_count); + if (g_ActiveConfig.VirtualXFBEnabled() && (!xfb_sources || xfb_count == 0)) + { + Core::Callback_VideoCopiedToXFB(false); + return; + } - // Transition the EFB render target to a shader resource. - VkRect2D src_region = {{0, 0}, - {m_framebuffer_mgr->GetEFBWidth(), m_framebuffer_mgr->GetEFBHeight()}}; - Texture2D* efb_color_texture = - m_framebuffer_mgr->ResolveEFBColorTexture(m_state_tracker.get(), src_region); - efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + // End the current render pass. + StateTracker::GetInstance()->EndRenderPass(); + StateTracker::GetInstance()->OnEndFrame(); // Draw to the screenshot buffer if needed. - if (IsFrameDumping() && DrawScreenshot(source_rc, efb_color_texture)) - { - DumpFrameData(reinterpret_cast<const u8*>(m_screenshot_readback_texture->GetMapPointer()), - static_cast<int>(m_screenshot_render_texture->GetWidth()), - static_cast<int>(m_screenshot_render_texture->GetHeight()), - static_cast<int>(m_screenshot_readback_texture->GetRowStride()), ticks); - FinishFrameData(); - } + // We don't actually copy it to the frame dump here, instead we submit the present to the screen + // and the readback in one command buffer, wait for it, and then dump the frame. This allows us + // to render to the screen and dump the frame in one command buffer instead of two. + VkFence frame_dump_fence = g_command_buffer_mgr->GetCurrentCommandBufferFence(); + bool dump_this_frame = IsFrameDumping() && DrawFrameDump(rc, xfb_addr, xfb_sources, xfb_count, + fb_width, fb_stride, fb_height); - // Restore the EFB color texture to color attachment ready for rendering the next frame. - m_framebuffer_mgr->GetEFBColorTexture()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // If we're dumping frames, don't bother waking the worker thread, since we have to wait anyway. + bool submit_on_background_thread = !dump_this_frame; // Ensure the worker thread is not still submitting a previous command buffer. // In other words, the last frame has been submitted (otherwise the next call would @@ -502,20 +509,30 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height // Draw to the screen if we have a swap chain. if (m_swap_chain) { - DrawScreen(source_rc, efb_color_texture); + DrawScreen(rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height); // Submit the current command buffer, signaling rendering finished semaphore when it's done // Because this final command buffer is rendering to the swap chain, we need to wait for // the available semaphore to be signaled before executing the buffer. This final submission // can happen off-thread in the background while we're preparing the next frame. g_command_buffer_mgr->SubmitCommandBuffer( - true, m_image_available_semaphore, m_rendering_finished_semaphore, + submit_on_background_thread, m_image_available_semaphore, m_rendering_finished_semaphore, m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex()); } else { // No swap chain, just execute command buffer. - g_command_buffer_mgr->SubmitCommandBuffer(true); + g_command_buffer_mgr->SubmitCommandBuffer(submit_on_background_thread); + } + + // If we're dumping frames, wait for the GPU to complete these commands, and then copy the image. + // NOTE: This call must come immediately after submitting the command buffer. Placing any other + // function calls between the submit and wait could cause another command buffer to be submitted, + // making frame_dump_fence refer to an incorrect fence. + if (dump_this_frame) + { + g_command_buffer_mgr->WaitForFence(frame_dump_fence); + DumpFrame(ticks); } // NOTE: It is important that no rendering calls are made to the EFB between submitting the @@ -542,7 +559,97 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height TextureCacheBase::Cleanup(frameCount); } -void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_tex) +void Renderer::DrawFrame(VkRenderPass render_pass, const EFBRectangle& rc, u32 xfb_addr, + const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width, + u32 fb_stride, u32 fb_height) +{ + if (!g_ActiveConfig.bUseXFB) + DrawEFB(render_pass, rc); + else if (!g_ActiveConfig.bUseRealXFB) + DrawVirtualXFB(render_pass, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height); + else + DrawRealXFB(render_pass, xfb_sources, xfb_count, fb_width, fb_stride, fb_height); +} + +void Renderer::DrawEFB(VkRenderPass render_pass, const EFBRectangle& rc) +{ + // Scale the source rectangle to the selected internal resolution. + TargetRectangle scaled_rc = Renderer::ConvertEFBRectangle(rc); + scaled_rc.left = std::max(scaled_rc.left, 0); + scaled_rc.right = std::max(scaled_rc.right, 0); + scaled_rc.top = std::max(scaled_rc.top, 0); + scaled_rc.bottom = std::max(scaled_rc.bottom, 0); + + // Transition the EFB render target to a shader resource. + VkRect2D src_region = { + {0, 0}, {static_cast<u32>(scaled_rc.GetWidth()), static_cast<u32>(scaled_rc.GetHeight())}}; + Texture2D* efb_color_texture = + FramebufferManager::GetInstance()->ResolveEFBColorTexture(src_region); + efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Copy EFB -> backbuffer + BlitScreen(render_pass, GetTargetRectangle(), scaled_rc, efb_color_texture, true); + + // Restore the EFB color texture to color attachment ready for rendering the next frame. + if (efb_color_texture == FramebufferManager::GetInstance()->GetEFBColorTexture()) + { + FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } +} + +void Renderer::DrawVirtualXFB(VkRenderPass render_pass, u32 xfb_addr, + const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width, + u32 fb_stride, u32 fb_height) +{ + const TargetRectangle& target_rect = GetTargetRectangle(); + for (u32 i = 0; i < xfb_count; ++i) + { + const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]); + TargetRectangle source_rect = xfb_source->sourceRc; + TargetRectangle draw_rect; + + int xfb_width = static_cast<int>(xfb_source->srcWidth); + int xfb_height = static_cast<int>(xfb_source->srcHeight); + int h_offset = (static_cast<s32>(xfb_source->srcAddr) - static_cast<s32>(xfb_addr)) / + (static_cast<s32>(fb_stride) * 2); + draw_rect.top = + target_rect.top + h_offset * target_rect.GetHeight() / static_cast<s32>(fb_height); + draw_rect.bottom = + target_rect.top + + (h_offset + xfb_height) * target_rect.GetHeight() / static_cast<s32>(fb_height); + draw_rect.left = target_rect.left + + (target_rect.GetWidth() - + xfb_width * target_rect.GetWidth() / static_cast<s32>(fb_stride)) / + 2; + draw_rect.right = target_rect.left + + (target_rect.GetWidth() + + xfb_width * target_rect.GetWidth() / static_cast<s32>(fb_stride)) / + 2; + + source_rect.right -= Renderer::EFBToScaledX(fb_stride - fb_width); + BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture(), true); + } +} + +void Renderer::DrawRealXFB(VkRenderPass render_pass, const XFBSourceBase* const* xfb_sources, + u32 xfb_count, u32 fb_width, u32 fb_stride, u32 fb_height) +{ + const TargetRectangle& target_rect = GetTargetRectangle(); + for (u32 i = 0; i < xfb_count; ++i) + { + const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]); + TargetRectangle source_rect = xfb_source->sourceRc; + TargetRectangle draw_rect = target_rect; + source_rect.right -= fb_stride - fb_width; + BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture(), true); + } +} + +void Renderer::DrawScreen(const EFBRectangle& rc, u32 xfb_addr, + const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width, + u32 fb_stride, u32 fb_height) { // Grab the next image from the swap chain in preparation for drawing the window. VkResult res = m_swap_chain->AcquireNextImage(m_image_available_semaphore); @@ -563,32 +670,31 @@ void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_ backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - // Blit the EFB to the back buffer (Swap chain) - UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetStandardPipelineLayout(), m_swap_chain->GetRenderPass(), - g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, - m_blit_fragment_shader); - - // Begin the present render pass + // Begin render pass for rendering to the swap chain. VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; - VkRect2D target_region = {{0, 0}, {backbuffer->GetWidth(), backbuffer->GetHeight()}}; - draw.BeginRenderPass(m_swap_chain->GetCurrentFramebuffer(), target_region, &clear_value); + VkRenderPassBeginInfo info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + nullptr, + m_swap_chain->GetRenderPass(), + m_swap_chain->GetCurrentFramebuffer(), + {{0, 0}, {backbuffer->GetWidth(), backbuffer->GetHeight()}}, + 1, + &clear_value}; + vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &info, + VK_SUBPASS_CONTENTS_INLINE); - // Copy EFB -> backbuffer - const TargetRectangle& dst_rect = GetTargetRectangle(); - BlitScreen(m_swap_chain->GetRenderPass(), dst_rect, src_rect, src_tex, true); + // Draw guest buffers (EFB or XFB) + DrawFrame(m_swap_chain->GetRenderPass(), rc, xfb_addr, xfb_sources, xfb_count, fb_width, + fb_stride, fb_height); - // OSD stuff + // Draw OSD Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, backbuffer->GetWidth(), backbuffer->GetHeight()); DrawDebugText(); - - // Do our OSD callbacks OSD::DoCallbacks(OSD::CallbackType::OnFrame); OSD::DrawMessages(); // End drawing to backbuffer - draw.EndRenderPass(); + vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer()); // Transition the backbuffer to PRESENT_SRC to ensure all commands drawing // to it have finished before present. @@ -596,7 +702,9 @@ void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } -bool Renderer::DrawScreenshot(const TargetRectangle& src_rect, const Texture2D* src_tex) +bool Renderer::DrawFrameDump(const EFBRectangle& rc, u32 xfb_addr, + const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width, + u32 fb_stride, u32 fb_height) { // Draw the screenshot to an image containing only the active screen area, removing any // borders as a result of the game rendering in a different aspect ratio. @@ -607,37 +715,45 @@ bool Renderer::DrawScreenshot(const TargetRectangle& src_rect, const Texture2D* target_rect.top = 0; u32 width = std::max(1u, static_cast<u32>(target_rect.GetWidth())); u32 height = std::max(1u, static_cast<u32>(target_rect.GetHeight())); - if (!ResizeScreenshotBuffer(width, height)) + if (!ResizeFrameDumpBuffer(width, height)) return false; VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; VkClearRect clear_rect = {{{0, 0}, {width, height}}, 0, 1}; VkClearAttachment clear_attachment = {VK_IMAGE_ASPECT_COLOR_BIT, 0, clear_value}; - VkRenderPassBeginInfo info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - nullptr, - m_framebuffer_mgr->GetColorCopyForReadbackRenderPass(), - m_screenshot_framebuffer, - {{0, 0}, {width, height}}, - 1, - &clear_value}; + VkRenderPassBeginInfo info = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + nullptr, + FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass(), + m_frame_dump_framebuffer, + {{0, 0}, {width, height}}, + 1, + &clear_value}; vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &info, VK_SUBPASS_CONTENTS_INLINE); vkCmdClearAttachments(g_command_buffer_mgr->GetCurrentCommandBuffer(), 1, &clear_attachment, 1, &clear_rect); - BlitScreen(m_framebuffer_mgr->GetColorCopyForReadbackRenderPass(), target_rect, src_rect, src_tex, - true); + DrawFrame(FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass(), rc, xfb_addr, + xfb_sources, xfb_count, fb_width, fb_stride, fb_height); vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer()); // Copy to the readback texture. - m_screenshot_readback_texture->CopyFromImage( - g_command_buffer_mgr->GetCurrentCommandBuffer(), m_screenshot_render_texture->GetImage(), + m_frame_dump_readback_texture->CopyFromImage( + g_command_buffer_mgr->GetCurrentCommandBuffer(), m_frame_dump_render_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width, height, 0, 0); - // Wait for the command buffer to complete. - g_command_buffer_mgr->ExecuteCommandBuffer(false, true); return true; } +void Renderer::DumpFrame(u64 ticks) +{ + DumpFrameData(reinterpret_cast<const u8*>(m_frame_dump_readback_texture->GetMapPointer()), + static_cast<int>(m_frame_dump_render_texture->GetWidth()), + static_cast<int>(m_frame_dump_render_texture->GetHeight()), + static_cast<int>(m_frame_dump_readback_texture->GetRowStride()), ticks); + FinishFrameData(); +} + void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, const TargetRectangle& src_rect, const Texture2D* src_tex, bool linear_filter) @@ -658,10 +774,7 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r { TargetRectangle left_rect; TargetRectangle right_rect; - if (g_ActiveConfig.iStereoMode == STEREO_TAB) - ConvertStereoRectangle(dst_rect, right_rect, left_rect); - else - ConvertStereoRectangle(dst_rect, left_rect, right_rect); + ConvertStereoRectangle(dst_rect, left_rect, right_rect); draw.DrawQuad(left_rect.left, left_rect.top, left_rect.GetWidth(), left_rect.GetHeight(), src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), @@ -679,40 +792,40 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r } } -bool Renderer::ResizeScreenshotBuffer(u32 new_width, u32 new_height) +bool Renderer::ResizeFrameDumpBuffer(u32 new_width, u32 new_height) { - if (m_screenshot_render_texture && m_screenshot_render_texture->GetWidth() == new_width && - m_screenshot_render_texture->GetHeight() == new_height) + if (m_frame_dump_render_texture && m_frame_dump_render_texture->GetWidth() == new_width && + m_frame_dump_render_texture->GetHeight() == new_height) { return true; } - if (m_screenshot_framebuffer != VK_NULL_HANDLE) + if (m_frame_dump_framebuffer != VK_NULL_HANDLE) { - vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_screenshot_framebuffer, nullptr); - m_screenshot_framebuffer = VK_NULL_HANDLE; + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_frame_dump_framebuffer, nullptr); + m_frame_dump_framebuffer = VK_NULL_HANDLE; } - m_screenshot_render_texture = + m_frame_dump_render_texture = Texture2D::Create(new_width, new_height, 1, 1, EFB_COLOR_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - m_screenshot_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, new_width, + m_frame_dump_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, new_width, new_height, EFB_COLOR_TEXTURE_FORMAT); - if (!m_screenshot_render_texture || !m_screenshot_readback_texture || - !m_screenshot_readback_texture->Map()) + if (!m_frame_dump_render_texture || !m_frame_dump_readback_texture || + !m_frame_dump_readback_texture->Map()) { WARN_LOG(VIDEO, "Failed to resize screenshot render texture"); - m_screenshot_render_texture.reset(); - m_screenshot_readback_texture.reset(); + m_frame_dump_render_texture.reset(); + m_frame_dump_readback_texture.reset(); return false; } - VkImageView attachment = m_screenshot_render_texture->GetView(); + VkImageView attachment = m_frame_dump_render_texture->GetView(); VkFramebufferCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - info.renderPass = m_framebuffer_mgr->GetColorCopyForReadbackRenderPass(); + info.renderPass = FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass(); info.attachmentCount = 1; info.pAttachments = &attachment; info.width = new_width; @@ -720,32 +833,32 @@ bool Renderer::ResizeScreenshotBuffer(u32 new_width, u32 new_height) info.layers = 1; VkResult res = - vkCreateFramebuffer(g_vulkan_context->GetDevice(), &info, nullptr, &m_screenshot_framebuffer); + vkCreateFramebuffer(g_vulkan_context->GetDevice(), &info, nullptr, &m_frame_dump_framebuffer); if (res != VK_SUCCESS) { WARN_LOG(VIDEO, "Failed to resize screenshot framebuffer"); - m_screenshot_render_texture.reset(); - m_screenshot_readback_texture.reset(); + m_frame_dump_render_texture.reset(); + m_frame_dump_readback_texture.reset(); return false; } // Render pass expects texture is in transfer src to start with. - m_screenshot_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + m_frame_dump_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); return true; } -void Renderer::DestroyScreenshotResources() +void Renderer::DestroyFrameDumpResources() { - if (m_screenshot_framebuffer != VK_NULL_HANDLE) + if (m_frame_dump_framebuffer != VK_NULL_HANDLE) { - vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_screenshot_framebuffer, nullptr); - m_screenshot_framebuffer = VK_NULL_HANDLE; + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_frame_dump_framebuffer, nullptr); + m_frame_dump_framebuffer = VK_NULL_HANDLE; } - m_screenshot_render_texture.reset(); - m_screenshot_readback_texture.reset(); + m_frame_dump_render_texture.reset(); + m_frame_dump_readback_texture.reset(); } void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height) @@ -889,8 +1002,8 @@ void Renderer::CheckForConfigChanges() if (msaa_changed || stereo_changed) { g_command_buffer_mgr->WaitForGPUIdle(); - m_framebuffer_mgr->RecreateRenderPass(); - m_framebuffer_mgr->ResizeEFBTextures(); + FramebufferManager::GetInstance()->RecreateRenderPass(); + FramebufferManager::GetInstance()->ResizeEFBTextures(); BindEFBToStateTracker(); } @@ -900,8 +1013,9 @@ void Renderer::CheckForConfigChanges() { g_command_buffer_mgr->WaitForGPUIdle(); RecompileShaders(); - m_framebuffer_mgr->RecompileShaders(); + FramebufferManager::GetInstance()->RecompileShaders(); g_object_cache->ClearPipelineCache(); + g_object_cache->RecompileSharedShaders(); } // For vsync, we need to change the present mode, which means recreating the swap chain. @@ -931,25 +1045,28 @@ void Renderer::OnSwapChainResized() void Renderer::BindEFBToStateTracker() { // Update framebuffer in state tracker - VkRect2D framebuffer_size = { - {0, 0}, {m_framebuffer_mgr->GetEFBWidth(), m_framebuffer_mgr->GetEFBHeight()}}; - m_state_tracker->SetRenderPass(m_framebuffer_mgr->GetEFBLoadRenderPass(), - m_framebuffer_mgr->GetEFBClearRenderPass()); - m_state_tracker->SetFramebuffer(m_framebuffer_mgr->GetEFBFramebuffer(), framebuffer_size); + VkRect2D framebuffer_size = {{0, 0}, + {FramebufferManager::GetInstance()->GetEFBWidth(), + FramebufferManager::GetInstance()->GetEFBHeight()}}; + StateTracker::GetInstance()->SetRenderPass( + FramebufferManager::GetInstance()->GetEFBLoadRenderPass(), + FramebufferManager::GetInstance()->GetEFBClearRenderPass()); + StateTracker::GetInstance()->SetFramebuffer( + FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size); // Update rasterization state with MSAA info RasterizationState rs_state = {}; - rs_state.bits = m_state_tracker->GetRasterizationState().bits; - rs_state.samples = m_framebuffer_mgr->GetEFBSamples(); + rs_state.bits = StateTracker::GetInstance()->GetRasterizationState().bits; + rs_state.samples = FramebufferManager::GetInstance()->GetEFBSamples(); rs_state.per_sample_shading = g_ActiveConfig.bSSAA ? VK_TRUE : VK_FALSE; - m_state_tracker->SetRasterizationState(rs_state); + StateTracker::GetInstance()->SetRasterizationState(rs_state); } void Renderer::ResizeEFBTextures() { // Ensure the GPU is finished with the current EFB textures. g_command_buffer_mgr->WaitForGPUIdle(); - m_framebuffer_mgr->ResizeEFBTextures(); + FramebufferManager::GetInstance()->ResizeEFBTextures(); BindEFBToStateTracker(); // Viewport and scissor rect have to be reset since they will be scaled differently. @@ -976,19 +1093,19 @@ void Renderer::ApplyState(bool bUseDstAlpha) void Renderer::ResetAPIState() { // End the EFB render pass if active - m_state_tracker->EndRenderPass(); + StateTracker::GetInstance()->EndRenderPass(); } void Renderer::RestoreAPIState() { // Instruct the state tracker to re-bind everything before the next draw - m_state_tracker->SetPendingRebind(); + StateTracker::GetInstance()->SetPendingRebind(); } void Renderer::SetGenerationMode() { RasterizationState new_rs_state = {}; - new_rs_state.bits = m_state_tracker->GetRasterizationState().bits; + new_rs_state.bits = StateTracker::GetInstance()->GetRasterizationState().bits; switch (bpmem.genMode.cullmode) { @@ -1009,7 +1126,7 @@ void Renderer::SetGenerationMode() break; } - m_state_tracker->SetRasterizationState(new_rs_state); + StateTracker::GetInstance()->SetRasterizationState(new_rs_state); } void Renderer::SetDepthMode() @@ -1050,7 +1167,7 @@ void Renderer::SetDepthMode() break; } - m_state_tracker->SetDepthStencilState(new_ds_state); + StateTracker::GetInstance()->SetDepthStencilState(new_ds_state); } void Renderer::SetColorMask() @@ -1066,16 +1183,16 @@ void Renderer::SetColorMask() } BlendState new_blend_state = {}; - new_blend_state.bits = m_state_tracker->GetBlendState().bits; + new_blend_state.bits = StateTracker::GetInstance()->GetBlendState().bits; new_blend_state.write_mask = color_mask; - m_state_tracker->SetBlendState(new_blend_state); + StateTracker::GetInstance()->SetBlendState(new_blend_state); } void Renderer::SetBlendMode(bool force_update) { BlendState new_blend_state = {}; - new_blend_state.bits = m_state_tracker->GetBlendState().bits; + new_blend_state.bits = StateTracker::GetInstance()->GetBlendState().bits; // Fast path for blending disabled if (!bpmem.blendmode.blendenable) @@ -1087,7 +1204,7 @@ void Renderer::SetBlendMode(bool force_update) new_blend_state.alpha_blend_op = VK_BLEND_OP_ADD; new_blend_state.src_alpha_blend = VK_BLEND_FACTOR_ONE; new_blend_state.dst_alpha_blend = VK_BLEND_FACTOR_ZERO; - m_state_tracker->SetBlendState(new_blend_state); + StateTracker::GetInstance()->SetBlendState(new_blend_state); return; } // Fast path for subtract blending @@ -1100,7 +1217,7 @@ void Renderer::SetBlendMode(bool force_update) new_blend_state.alpha_blend_op = VK_BLEND_OP_REVERSE_SUBTRACT; new_blend_state.src_alpha_blend = VK_BLEND_FACTOR_ONE; new_blend_state.dst_alpha_blend = VK_BLEND_FACTOR_ONE; - m_state_tracker->SetBlendState(new_blend_state); + StateTracker::GetInstance()->SetBlendState(new_blend_state); return; } @@ -1195,13 +1312,13 @@ void Renderer::SetBlendMode(bool force_update) new_blend_state.dst_alpha_blend = Util::GetAlphaBlendFactor(new_blend_state.dst_blend); } - m_state_tracker->SetBlendState(new_blend_state); + StateTracker::GetInstance()->SetBlendState(new_blend_state); } void Renderer::SetLogicOpMode() { BlendState new_blend_state = {}; - new_blend_state.bits = m_state_tracker->GetBlendState().bits; + new_blend_state.bits = StateTracker::GetInstance()->GetBlendState().bits; // Does our device support logic ops? bool logic_op_enable = bpmem.blendmode.logicopenable && !bpmem.blendmode.blendenable; @@ -1224,7 +1341,7 @@ void Renderer::SetLogicOpMode() new_blend_state.logic_op = VK_LOGIC_OP_CLEAR; } - m_state_tracker->SetBlendState(new_blend_state); + StateTracker::GetInstance()->SetBlendState(new_blend_state); } else { @@ -1269,7 +1386,7 @@ void Renderer::SetLogicOpMode() new_blend_state.src_alpha_blend = Util::GetAlphaBlendFactor(new_blend_state.src_blend); new_blend_state.dst_alpha_blend = Util::GetAlphaBlendFactor(new_blend_state.dst_blend); - m_state_tracker->SetBlendState(new_blend_state); + StateTracker::GetInstance()->SetBlendState(new_blend_state); } else { @@ -1338,7 +1455,7 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) sampler = g_object_cache->GetPointSampler(); } - m_state_tracker->SetSampler(bind_index, sampler); + StateTracker::GetInstance()->SetSampler(bind_index, sampler); m_sampler_states[bind_index].bits = new_state.bits; } @@ -1352,7 +1469,7 @@ void Renderer::ResetSamplerStates() for (size_t i = 0; i < m_sampler_states.size(); i++) { m_sampler_states[i].bits = std::numeric_limits<decltype(m_sampler_states[i].bits)>::max(); - m_state_tracker->SetSampler(i, g_object_cache->GetPointSampler()); + StateTracker::GetInstance()->SetSampler(i, g_object_cache->GetPointSampler()); } // Invalidate all sampler objects (some will be unused now). @@ -1375,7 +1492,7 @@ void Renderer::SetScissorRect(const EFBRectangle& rc) {target_rc.left, target_rc.top}, {static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}}; - m_state_tracker->SetScissor(scissor); + StateTracker::GetInstance()->SetScissor(scissor); } void Renderer::SetViewport() @@ -1420,7 +1537,7 @@ void Renderer::SetViewport() } VkViewport viewport = {x, y, width, height, min_depth, max_depth}; - m_state_tracker->SetViewport(viewport); + StateTracker::GetInstance()->SetViewport(viewport); } void Renderer::ChangeSurface(void* new_surface_handle) |
