mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Decouple XFB scanout from presentation
This commit is contained in:
@ -557,76 +557,24 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
|
||||
BindEFBToStateTracker();
|
||||
}
|
||||
|
||||
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks)
|
||||
void Renderer::Flush()
|
||||
{
|
||||
// Pending/batched EFB pokes should be included in the final image.
|
||||
FramebufferManager::GetInstance()->FlushEFBPokes();
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(true, false);
|
||||
}
|
||||
|
||||
auto* xfb_texture = static_cast<VKTexture*>(texture);
|
||||
|
||||
// End the current render pass.
|
||||
void Renderer::BindBackbuffer(const ClearColor& clear_color)
|
||||
{
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
StateTracker::GetInstance()->OnEndFrame();
|
||||
|
||||
// Handle host window resizes.
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
|
||||
// There are a few variables which can alter the final window draw rectangle, and some of them
|
||||
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
||||
UpdateDrawRectangle();
|
||||
|
||||
// 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
|
||||
// be a race, as the image may not have been consumed yet).
|
||||
g_command_buffer_mgr->PrepareToSubmitCommandBuffer();
|
||||
|
||||
// Draw to the screen if we have a swap chain.
|
||||
if (m_swap_chain)
|
||||
{
|
||||
DrawScreen(xfb_texture, xfb_region);
|
||||
|
||||
// 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,
|
||||
m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
// No swap chain, just execute command buffer.
|
||||
g_command_buffer_mgr->SubmitCommandBuffer(true);
|
||||
}
|
||||
|
||||
// NOTE: It is important that no rendering calls are made to the EFB between submitting the
|
||||
// (now-previous) frame and after the below config checks are completed. If the target size
|
||||
// changes, as the resize methods to not defer the destruction of the framebuffer, the current
|
||||
// command buffer will contain references to a now non-existent framebuffer.
|
||||
|
||||
// Prep for the next frame (get command buffer ready) before doing anything else.
|
||||
BeginFrame();
|
||||
|
||||
// Restore the EFB color texture to color attachment ready for rendering the next frame.
|
||||
FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout(
|
||||
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
RestoreAPIState();
|
||||
|
||||
// Determine what (if anything) has changed in the config.
|
||||
CheckForConfigChanges();
|
||||
|
||||
// Clean up stale textures.
|
||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||
}
|
||||
|
||||
void Renderer::Flush()
|
||||
{
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(true, false);
|
||||
}
|
||||
|
||||
void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region)
|
||||
{
|
||||
VkResult res;
|
||||
if (!g_command_buffer_mgr->CheckLastPresentFail())
|
||||
{
|
||||
@ -676,48 +624,65 @@ void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region
|
||||
// Begin render pass for rendering to the swap chain.
|
||||
VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
StateTracker::GetInstance()->BeginClearRenderPass(region, &clear_value, 1);
|
||||
}
|
||||
|
||||
// Draw
|
||||
BlitScreen(m_swap_chain_render_pass, GetTargetRectangle(), xfb_region,
|
||||
xfb_texture->GetRawTexIdentifier());
|
||||
|
||||
// Draw OSD
|
||||
SetViewport(0.0f, 0.0f, static_cast<float>(backbuffer->GetWidth()),
|
||||
static_cast<float>(backbuffer->GetHeight()), 0.0f, 1.0f);
|
||||
StateTracker::GetInstance()->SetPendingRebind();
|
||||
DrawImGui();
|
||||
|
||||
void Renderer::PresentBackbuffer()
|
||||
{
|
||||
// End drawing to backbuffer
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
StateTracker::GetInstance()->OnEndFrame();
|
||||
|
||||
// Transition the backbuffer to PRESENT_SRC to ensure all commands drawing
|
||||
// to it have finished before present.
|
||||
Texture2D* backbuffer = m_swap_chain->GetCurrentTexture();
|
||||
backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
// 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,
|
||||
m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex());
|
||||
|
||||
BeginFrame();
|
||||
}
|
||||
|
||||
void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect,
|
||||
const TargetRectangle& src_rect, const Texture2D* src_tex)
|
||||
void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc)
|
||||
{
|
||||
const TargetRectangle target_rc = GetTargetRectangle();
|
||||
|
||||
VulkanPostProcessing* post_processor = static_cast<VulkanPostProcessing*>(m_post_processor.get());
|
||||
if (g_ActiveConfig.stereo_mode == StereoMode::SBS ||
|
||||
g_ActiveConfig.stereo_mode == StereoMode::TAB)
|
||||
{
|
||||
TargetRectangle left_rect;
|
||||
TargetRectangle right_rect;
|
||||
std::tie(left_rect, right_rect) = ConvertStereoRectangle(dst_rect);
|
||||
std::tie(left_rect, right_rect) = ConvertStereoRectangle(target_rc);
|
||||
|
||||
post_processor->BlitFromTexture(left_rect, src_rect, src_tex, 0, render_pass);
|
||||
post_processor->BlitFromTexture(right_rect, src_rect, src_tex, 1, render_pass);
|
||||
post_processor->BlitFromTexture(left_rect, rc,
|
||||
static_cast<const VKTexture*>(texture)->GetRawTexIdentifier(),
|
||||
0, m_swap_chain_render_pass);
|
||||
post_processor->BlitFromTexture(right_rect, rc,
|
||||
static_cast<const VKTexture*>(texture)->GetRawTexIdentifier(),
|
||||
1, m_swap_chain_render_pass);
|
||||
}
|
||||
else if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer)
|
||||
{
|
||||
post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, -1, render_pass);
|
||||
post_processor->BlitFromTexture(target_rc, rc,
|
||||
static_cast<const VKTexture*>(texture)->GetRawTexIdentifier(),
|
||||
-1, m_swap_chain_render_pass);
|
||||
}
|
||||
else
|
||||
{
|
||||
post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, 0, render_pass);
|
||||
post_processor->BlitFromTexture(target_rc, rc,
|
||||
static_cast<const VKTexture*>(texture)->GetRawTexIdentifier(),
|
||||
0, m_swap_chain_render_pass);
|
||||
}
|
||||
|
||||
// The post-processor uses the old-style Vulkan draws, which mess with the tracked state.
|
||||
StateTracker::GetInstance()->SetPendingRebind();
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceChange()
|
||||
@ -766,36 +731,20 @@ void Renderer::CheckForSurfaceResize()
|
||||
OnSwapChainResized();
|
||||
}
|
||||
|
||||
void Renderer::CheckForConfigChanges()
|
||||
void Renderer::OnConfigChanged(u32 bits)
|
||||
{
|
||||
// Save the video config so we can compare against to determine which settings have changed.
|
||||
const u32 old_multisamples = g_ActiveConfig.iMultisamples;
|
||||
const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy;
|
||||
const bool old_force_filtering = g_ActiveConfig.bForceFiltering;
|
||||
|
||||
// Copy g_Config to g_ActiveConfig.
|
||||
// NOTE: This can potentially race with the UI thread, however if it does, the changes will be
|
||||
// delayed until the next time CheckForConfigChanges is called.
|
||||
UpdateActiveConfig();
|
||||
|
||||
// Determine which (if any) settings have changed.
|
||||
const bool multisamples_changed = old_multisamples != g_ActiveConfig.iMultisamples;
|
||||
const bool anisotropy_changed = old_anisotropy != g_ActiveConfig.iMaxAnisotropy;
|
||||
const bool force_texture_filtering_changed =
|
||||
old_force_filtering != g_ActiveConfig.bForceFiltering;
|
||||
|
||||
// Update texture cache settings with any changed options.
|
||||
TextureCache::GetInstance()->OnConfigChanged(g_ActiveConfig);
|
||||
|
||||
// Handle settings that can cause the EFB framebuffer to change.
|
||||
if (CalculateTargetSize() || multisamples_changed)
|
||||
if (bits & CONFIG_CHANGE_BIT_TARGET_SIZE)
|
||||
RecreateEFBFramebuffer();
|
||||
|
||||
// MSAA samples changed, we need to recreate the EFB render pass.
|
||||
// If the stereoscopy mode changed, we need to recreate the buffers as well.
|
||||
// SSAA changed on/off, we have to recompile shaders.
|
||||
// Changing stereoscopy from off<->on also requires shaders to be recompiled.
|
||||
if (CheckForHostConfigChanges())
|
||||
if (bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES))
|
||||
{
|
||||
RecreateEFBFramebuffer();
|
||||
RecompileShaders();
|
||||
@ -805,22 +754,21 @@ void Renderer::CheckForConfigChanges()
|
||||
}
|
||||
|
||||
// For vsync, we need to change the present mode, which means recreating the swap chain.
|
||||
if (m_swap_chain && g_ActiveConfig.IsVSync() != m_swap_chain->IsVSyncEnabled())
|
||||
if (m_swap_chain && bits & CONFIG_CHANGE_BIT_VSYNC)
|
||||
{
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
m_swap_chain->SetVSync(g_ActiveConfig.IsVSync());
|
||||
}
|
||||
|
||||
// For quad-buffered stereo we need to change the layer count, so recreate the swap chain.
|
||||
if (m_swap_chain &&
|
||||
(g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) != m_swap_chain->IsStereoEnabled())
|
||||
if (m_swap_chain && bits & CONFIG_CHANGE_BIT_STEREO_MODE)
|
||||
{
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
m_swap_chain->RecreateSwapChain();
|
||||
}
|
||||
|
||||
// Wipe sampler cache if force texture filtering or anisotropy changes.
|
||||
if (anisotropy_changed || force_texture_filtering_changed)
|
||||
if (bits & (CONFIG_CHANGE_BIT_ANISOTROPY | CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING))
|
||||
ResetSamplerStates();
|
||||
|
||||
// Check for a changed post-processing shader and recompile if needed.
|
||||
|
Reference in New Issue
Block a user