Decouple XFB scanout from presentation

This commit is contained in:
Stenzek
2018-11-28 14:30:47 +10:00
parent f1e7fb505b
commit e4b205c769
16 changed files with 319 additions and 383 deletions

View File

@ -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.