summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/Renderer.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2016-11-04 23:11:10 +1000
committerGitHub <noreply@github.com>2016-11-04 23:11:10 +1000
commitac2971b30e015a17c8aac937092a0dfd7c4beaf5 (patch)
tree5bb0b8686a3b2e3b01155a000077a2226b70a26f /Source/Core/VideoBackends/Vulkan/Renderer.cpp
parentd2fdafa1556aaa2b71017d9e2ac2dffc2e551008 (diff)
parentc880c37244d4c9d63ce3e3560383825097818d85 (diff)
Merge pull request #4374 from stenzek/vulkan-xfb
Vulkan: Cleanup and implement XFB support
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/Renderer.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/Renderer.cpp447
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)