From e4b205c76937d379cd9043939e2cef1651b1a21b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 28 Nov 2018 14:30:47 +1000 Subject: [PATCH] Decouple XFB scanout from presentation --- .../VideoBackends/D3D/FramebufferManager.cpp | 4 + Source/Core/VideoBackends/D3D/Render.cpp | 127 +++++------- Source/Core/VideoBackends/D3D/Render.h | 10 +- Source/Core/VideoBackends/D3D/main.cpp | 5 +- Source/Core/VideoBackends/Null/Render.cpp | 5 - Source/Core/VideoBackends/Null/Render.h | 2 - Source/Core/VideoBackends/OGL/Render.cpp | 180 ++++++------------ Source/Core/VideoBackends/OGL/Render.h | 11 +- .../VideoBackends/Software/SWOGLWindow.cpp | 4 +- .../Core/VideoBackends/Software/SWOGLWindow.h | 2 +- .../VideoBackends/Software/SWRenderer.cpp | 4 +- .../Core/VideoBackends/Software/SWRenderer.h | 2 +- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 142 +++++--------- Source/Core/VideoBackends/Vulkan/Renderer.h | 13 +- Source/Core/VideoCommon/RenderBase.cpp | 139 ++++++++++---- Source/Core/VideoCommon/RenderBase.h | 52 ++++- 16 files changed, 319 insertions(+), 383 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp index e7e8fa9ddc..5a2088f40d 100644 --- a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp @@ -125,6 +125,7 @@ void FramebufferManager::BindEFBRenderTarget(bool bind_depth) FramebufferManager::FramebufferManager(int target_width, int target_height) { + static constexpr std::array clear_color = {0.0f, 0.0f, 0.0f, 1.0f}; m_target_width = static_cast(std::max(target_width, 1)); m_target_height = static_cast(std::max(target_height, 1)); DXGI_SAMPLE_DESC sample_desc; @@ -154,6 +155,7 @@ FramebufferManager::FramebufferManager(int target_width, int target_height) D3D::SetDebugObjectName(m_efb.color_tex->GetTex(), "EFB color texture"); D3D::SetDebugObjectName(m_efb.color_tex->GetSRV(), "EFB color texture shader resource view"); D3D::SetDebugObjectName(m_efb.color_tex->GetRTV(), "EFB color texture render target view"); + D3D::context->ClearRenderTargetView(m_efb.color_tex->GetRTV(), clear_color.data()); // Temporary EFB color texture - used in ReinterpretPixelData texdesc = @@ -173,6 +175,7 @@ FramebufferManager::FramebufferManager(int target_width, int target_height) "EFB color temp texture shader resource view"); D3D::SetDebugObjectName(m_efb.color_temp_tex->GetRTV(), "EFB color temp texture render target view"); + D3D::context->ClearRenderTargetView(m_efb.color_temp_tex->GetRTV(), clear_color.data()); // Integer render targets for EFB, used for logic op CD3D11_RENDER_TARGET_VIEW_DESC int_rtv_desc(m_efb.color_tex->GetTex(), @@ -222,6 +225,7 @@ FramebufferManager::FramebufferManager(int target_width, int target_height) D3D::SetDebugObjectName(m_efb.depth_tex->GetTex(), "EFB depth texture"); D3D::SetDebugObjectName(m_efb.depth_tex->GetDSV(), "EFB depth texture depth stencil view"); D3D::SetDebugObjectName(m_efb.depth_tex->GetSRV(), "EFB depth texture shader resource view"); + D3D::context->ClearDepthStencilView(m_efb.depth_tex->GetDSV(), D3D11_CLEAR_DEPTH, 0.0f, 0); // Render buffer for AccessEFB (depth data) texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R32_FLOAT, 1, 1, 1, 1, D3D11_BIND_RENDER_TARGET); diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 8e98fc8832..2a82924708 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -66,19 +66,10 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer : ::Renderer(backbuffer_width, backbuffer_height, backbuffer_scale, AbstractTextureFormat::RGBA8) { - m_last_multisamples = g_ActiveConfig.iMultisamples; - m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; m_last_fullscreen_state = D3D::GetFullscreenState(); g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); SetupDeviceObjects(); - // Clear EFB textures - constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; - D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), - clear_color.data()); - D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), - D3D11_CLEAR_DEPTH, 0.f, 0); - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height); D3D::context->RSSetViewports(1, &vp); FramebufferManager::BindEFBRenderTarget(); @@ -560,68 +551,33 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) RestoreAPIState(); } -// This function has the final picture. We adjust the aspect ratio here. -void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void Renderer::BindBackbuffer(const ClearColor& clear_color) { - ResetAPIState(); - - // Prepare to copy the XFBs to our backbuffer CheckForSurfaceChange(); CheckForSurfaceResize(); - UpdateDrawRectangle(); - TargetRectangle targetRc = GetTargetRectangle(); - static constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color.data()); m_current_framebuffer = nullptr; m_current_framebuffer_width = m_backbuffer_width; m_current_framebuffer_height = m_backbuffer_height; +} - // activate linear filtering for the buffer copies - D3D::SetLinearCopySampler(); - auto* xfb_texture = static_cast(texture); - - BlitScreen(xfb_region, targetRc, xfb_texture->GetRawTexIdentifier(), - xfb_texture->GetConfig().width, xfb_texture->GetConfig().height); - - // Reset viewport for drawing text - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast(m_backbuffer_width), - static_cast(m_backbuffer_height)); - D3D::context->RSSetViewports(1, &vp); - DrawImGui(); - - g_texture_cache->Cleanup(frameCount); - - // Enable configuration changes - UpdateActiveConfig(); - g_texture_cache->OnConfigChanged(g_ActiveConfig); - - // Flip/present backbuffer to frontbuffer here - if (D3D::swapchain) - D3D::Present(); +void Renderer::PresentBackbuffer() +{ + D3D::Present(); +} +void Renderer::OnConfigChanged(u32 bits) +{ // Resize the back buffers NOW to avoid flickering - if (CalculateTargetSize() || m_last_multisamples != g_ActiveConfig.iMultisamples || - m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off)) + if (bits & (CONFIG_CHANGE_BIT_TARGET_SIZE | CONFIG_CHANGE_BIT_MULTISAMPLES | + CONFIG_CHANGE_BIT_STEREO_MODE)) { - m_last_multisamples = g_ActiveConfig.iMultisamples; - m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; PixelShaderCache::InvalidateMSAAShaders(); - UpdateDrawRectangle(); - g_framebuffer_manager.reset(); g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); - D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), - clear_color.data()); - D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(), - D3D11_CLEAR_DEPTH, 0.f, 0); } - - CheckForHostConfigChanges(); - - // begin next frame - RestoreAPIState(); } void Renderer::CheckForSurfaceChange() @@ -780,28 +736,34 @@ void Renderer::BBoxWrite(int index, u16 _value) BBox::Set(index, value); } -void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, - u32 src_width, u32 src_height) +void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) { + const CD3D11_RECT source_rc(rc.left, rc.top, rc.right, rc.bottom); + const TargetRectangle target_rc = GetTargetRectangle(); + + // activate linear filtering for the buffer copies + D3D::SetLinearCopySampler(); + if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { - TargetRectangle leftRc, rightRc; - std::tie(leftRc, rightRc) = ConvertStereoRectangle(dst); + TargetRectangle left_rc, right_rc; + std::tie(left_rc, right_rc) = ConvertStereoRectangle(target_rc); - D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)leftRc.left, (float)leftRc.top, - (float)leftRc.GetWidth(), (float)leftRc.GetHeight()); - D3D11_VIEWPORT rightVp = CD3D11_VIEWPORT((float)rightRc.left, (float)rightRc.top, - (float)rightRc.GetWidth(), (float)rightRc.GetHeight()); - - D3D::context->RSSetViewports(1, &leftVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + SetViewport(static_cast(left_rc.left), static_cast(left_rc.top), + static_cast(left_rc.GetWidth()), static_cast(right_rc.GetHeight()), + 0.0f, 1.0f); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 0); - D3D::context->RSSetViewports(1, &rightVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + SetViewport(static_cast(right_rc.left), static_cast(right_rc.top), + static_cast(right_rc.GetWidth()), static_cast(right_rc.GetHeight()), + 0.0f, 1.0f); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 1); @@ -811,29 +773,33 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D if (!m_3d_vision_texture) Create3DVisionTexture(m_backbuffer_width, m_backbuffer_height); - D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)dst.left, (float)dst.top, (float)dst.GetWidth(), - (float)dst.GetHeight()); - D3D11_VIEWPORT rightVp = CD3D11_VIEWPORT((float)(dst.left + m_backbuffer_width), (float)dst.top, - (float)dst.GetWidth(), (float)dst.GetHeight()); + const CD3D11_VIEWPORT left_vp( + static_cast(target_rc.left), static_cast(target_rc.top), + static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight())); + const CD3D11_VIEWPORT right_vp( + static_cast(target_rc.left + m_backbuffer_width), static_cast(target_rc.top), + static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight())); // Render to staging texture which is double the width of the backbuffer D3D::context->OMSetRenderTargets(1, &m_3d_vision_texture->GetRTV(), nullptr); - D3D::context->RSSetViewports(1, &leftVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + D3D::context->RSSetViewports(1, &left_vp); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 0); - D3D::context->RSSetViewports(1, &rightVp); - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, + D3D::context->RSSetViewports(1, &right_vp); + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), nullptr, 1); // Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should // recognize the signature and automatically include the right eye frame. - D3D11_BOX box = CD3D11_BOX(0, 0, 0, m_backbuffer_width, m_backbuffer_height, 1); + const CD3D11_BOX box(0, 0, 0, m_backbuffer_width, m_backbuffer_height, 1); D3D::context->CopySubresourceRegion(D3D::GetBackBuffer()->GetTex(), 0, 0, 0, 0, m_3d_vision_texture->GetTex(), 0, &box); @@ -842,9 +808,9 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D } else { - D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)dst.left, (float)dst.top, (float)dst.GetWidth(), - (float)dst.GetHeight()); - D3D::context->RSSetViewports(1, &vp); + SetViewport(static_cast(target_rc.left), static_cast(target_rc.top), + static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight()), + 0.0f, 1.0f); ID3D11PixelShader* pixelShader = (g_Config.stereo_mode == StereoMode::Anaglyph) ? PixelShaderCache::GetAnaglyphProgram() : @@ -852,7 +818,8 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D ID3D11GeometryShader* geomShader = (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) ? GeometryShaderCache::GetCopyGeometryShader() : nullptr; - D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, pixelShader, + D3D::drawShadedTexQuad(static_cast(texture)->GetRawTexIdentifier()->GetSRV(), + &source_rc, texture->GetWidth(), texture->GetHeight(), pixelShader, VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), geomShader); } diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index fc0b95acfc..399babffad 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -52,6 +52,8 @@ public: float far_depth) override; void Draw(u32 base_vertex, u32 num_vertices) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; + void BindBackbuffer(const ClearColor& clear_color = {}) override; + void PresentBackbuffer() override; void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; @@ -66,7 +68,8 @@ public: TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; + void OnConfigChanged(u32 bits) override; void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; @@ -81,9 +84,6 @@ private: void CheckForSurfaceResize(); void UpdateBackbufferSize(); - void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, - u32 src_width, u32 src_height); - StateCache m_state_cache; std::array m_clear_blend_states{}; @@ -95,8 +95,6 @@ private: ID3D11Texture2D* m_screenshot_texture = nullptr; D3DTexture2D* m_3d_vision_texture = nullptr; - u32 m_last_multisamples = 1; - bool m_last_stereo_mode = false; bool m_last_fullscreen_state = false; }; } diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index 27c880e730..5795fce3a2 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -157,13 +157,14 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) VertexShaderCache::Init(); PixelShaderCache::Init(); GeometryShaderCache::Init(); - if (!g_shader_cache->Initialize()) + + if (!g_renderer->Initialize() || !g_shader_cache->Initialize()) return false; D3D::InitUtils(); BBox::Init(); - return g_renderer->Initialize(); + return true; } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/Null/Render.cpp b/Source/Core/VideoBackends/Null/Render.cpp index ff427626d3..d076e0f091 100644 --- a/Source/Core/VideoBackends/Null/Render.cpp +++ b/Source/Core/VideoBackends/Null/Render.cpp @@ -92,9 +92,4 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) return result; } -void Renderer::SwapImpl(AbstractTexture*, const EFBRectangle&, u64) -{ - UpdateActiveConfig(); -} - } // namespace Null diff --git a/Source/Core/VideoBackends/Null/Render.h b/Source/Core/VideoBackends/Null/Render.h index 6567b239fb..75c4adfffc 100644 --- a/Source/Core/VideoBackends/Null/Render.h +++ b/Source/Core/VideoBackends/Null/Render.h @@ -35,8 +35,6 @@ public: void BBoxWrite(int index, u16 value) override {} TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; - void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override { diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 630cea738e..35ec1a6715 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -59,10 +59,6 @@ VideoConfig g_ogl_config; // 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA. static int s_MSAASamples = 1; -static u32 s_last_multisamples = 1; -static bool s_last_stereo_mode = false; - -static bool s_vsync; // EFB cache related static const u32 EFB_CACHE_RECT_SIZE = 64; // Cache 64x64 blocks. @@ -725,9 +721,6 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ g_Config.VerifyValidity(); UpdateActiveConfig(); - // Since we modify the config here, we need to update the last host bits, it may have changed. - m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; - OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", g_ogl_config.gl_vendor, g_ogl_config.gl_renderer, g_ogl_config.gl_version), 5000); @@ -756,15 +749,9 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ g_ogl_config.bSupportsCopySubImage ? "" : "CopyImageSubData ", g_ActiveConfig.backend_info.bSupportsDepthClamp ? "" : "DepthClamp "); - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_MSAASamples = s_last_multisamples; - - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - // Handle VSync on/off - s_vsync = g_ActiveConfig.IsVSync(); if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) - m_main_gl_context->SwapInterval(s_vsync); + m_main_gl_context->SwapInterval(g_ActiveConfig.IsVSync()); // Because of the fixed framebuffer size we need to disable the resolution // options while running @@ -1220,37 +1207,55 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE ClearEFBCache(); } -void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, - int src_width, int src_height) +void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) { + TargetRectangle source_rc = rc; + source_rc.top = rc.GetHeight(); + source_rc.bottom = 0; + + // Check if we need to render to a new surface. + TargetRectangle flipped_trc = GetTargetRectangle(); + std::swap(flipped_trc.top, flipped_trc.bottom); + + // Copy the framebuffer to screen. OpenGLPostProcessing* post_processor = static_cast(m_post_processor.get()); if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) { - TargetRectangle leftRc, rightRc; + TargetRectangle left_rc, right_rc; // Top-and-Bottom mode needs to compensate for inverted vertical screen coordinates. if (g_ActiveConfig.stereo_mode == StereoMode::TAB) - std::tie(rightRc, leftRc) = ConvertStereoRectangle(dst); + std::tie(right_rc, left_rc) = ConvertStereoRectangle(flipped_trc); else - std::tie(leftRc, rightRc) = ConvertStereoRectangle(dst); + std::tie(left_rc, right_rc) = ConvertStereoRectangle(flipped_trc); - post_processor->BlitFromTexture(src, leftRc, src_texture, src_width, src_height, 0); - post_processor->BlitFromTexture(src, rightRc, src_texture, src_width, src_height, 1); + post_processor->BlitFromTexture(source_rc, left_rc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 0); + post_processor->BlitFromTexture(source_rc, right_rc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 1); } else if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) { glDrawBuffer(GL_BACK_LEFT); - post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 0); + post_processor->BlitFromTexture(source_rc, flipped_trc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 0); glDrawBuffer(GL_BACK_RIGHT); - post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 1); + post_processor->BlitFromTexture(source_rc, flipped_trc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 1); glDrawBuffer(GL_BACK); } else { - post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 0); + post_processor->BlitFromTexture(source_rc, flipped_trc, + static_cast(texture)->GetRawTexIdentifier(), + texture->GetWidth(), texture->GetHeight(), 0); } } @@ -1385,8 +1390,20 @@ void Renderer::ApplyBlendingState(const BlendingState state, bool force) m_current_blend_state = state; } -// This function has the final picture. We adjust the aspect ratio here. -void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void Renderer::BindBackbuffer(const ClearColor& clear_color) +{ + CheckForSurfaceChange(); + CheckForSurfaceResize(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_current_framebuffer = nullptr; + m_current_framebuffer_width = m_backbuffer_width; + m_current_framebuffer_height = m_backbuffer_height; +} + +void Renderer::PresentBackbuffer() { if (g_ogl_config.bSupportsDebug) { @@ -1396,72 +1413,22 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region glDisable(GL_DEBUG_OUTPUT); } - auto* xfb_texture = static_cast(texture); + // Swap the back and front buffers, presenting the image. + m_main_gl_context->Swap(); +} - TargetRectangle sourceRc = xfb_region; - sourceRc.top = xfb_region.GetHeight(); - sourceRc.bottom = 0; - - ResetAPIState(); - - // Check if we need to render to a new surface. - CheckForSurfaceChange(); - CheckForSurfaceResize(); - UpdateDrawRectangle(); - TargetRectangle flipped_trc = GetTargetRectangle(); - std::swap(flipped_trc.top, flipped_trc.bottom); - - // Skip screen rendering when running in headless mode. - if (!IsHeadless()) +void Renderer::OnConfigChanged(u32 bits) +{ + if (bits & (CONFIG_CHANGE_BIT_TARGET_SIZE | CONFIG_CHANGE_BIT_MULTISAMPLES | + CONFIG_CHANGE_BIT_STEREO_MODE | CONFIG_CHANGE_BIT_BBOX)) { - // Clear the framebuffer before drawing anything. - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_current_framebuffer = nullptr; - m_current_framebuffer_width = m_backbuffer_width; - m_current_framebuffer_height = m_backbuffer_height; - - // Copy the framebuffer to screen. - BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(), - xfb_texture->GetConfig().width, xfb_texture->GetConfig().height); - - // Render OSD messages. - glViewport(0, 0, m_backbuffer_width, m_backbuffer_height); - DrawImGui(); - - // Swap the back and front buffers, presenting the image. - m_main_gl_context->Swap(); - } - else - { - // Since we're not swapping in headless mode, ensure all commands are sent to the GPU. - // Otherwise the driver could batch several frames togehter. - glFlush(); - } - - // Was the size changed since the last frame? - bool target_size_changed = CalculateTargetSize(); - bool stencil_buffer_enabled = - static_cast(g_framebuffer_manager.get())->HasStencilBuffer(); - - bool fb_needs_update = target_size_changed || - s_last_multisamples != g_ActiveConfig.iMultisamples || - stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() || - s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off); - - if (fb_needs_update) - { - s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - s_last_multisamples = g_ActiveConfig.iMultisamples; - s_MSAASamples = s_last_multisamples; - + s_MSAASamples = g_ActiveConfig.iMultisamples; if (s_MSAASamples > 1 && s_MSAASamples > g_ogl_config.max_samples) { s_MSAASamples = g_ogl_config.max_samples; OSD::AddMessage( StringFromFormat("%d Anti Aliasing samples selected, but only %d supported by your GPU.", - s_last_multisamples, g_ogl_config.max_samples), + s_MSAASamples, g_ogl_config.max_samples), 10000); } @@ -1469,40 +1436,13 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region g_framebuffer_manager = std::make_unique( m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer()); BoundingBox::SetTargetSizeChanged(m_target_width, m_target_height); - UpdateDrawRectangle(); } - if (s_vsync != g_ActiveConfig.IsVSync()) - { - s_vsync = g_ActiveConfig.IsVSync(); - if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) - m_main_gl_context->SwapInterval(s_vsync); - } + if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) + m_main_gl_context->SwapInterval(g_ActiveConfig.IsVSync()); - // Clean out old stuff from caches. It's not worth it to clean out the shader caches. - g_texture_cache->Cleanup(frameCount); - - RestoreAPIState(); - - g_Config.iSaveTargetId = 0; - - int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; - UpdateActiveConfig(); - g_texture_cache->OnConfigChanged(g_ActiveConfig); - - if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) + if (bits & CONFIG_CHANGE_BIT_ANISOTROPY) g_sampler_cache->Clear(); - - // Invalidate shader cache when the host config changes. - CheckForHostConfigChanges(); - - // For testing zbuffer targets. - // Renderer::SetZBufferRender(); - // SaveTexture("tex.png", GL_TEXTURE_2D, s_FakeZTarget, - // GetTargetWidth(), GetTargetHeight()); - - // Invalidate EFB cache - ClearEFBCache(); } void Renderer::Flush() @@ -1535,15 +1475,6 @@ void Renderer::CheckForSurfaceResize() m_backbuffer_height = m_main_gl_context->GetBackBufferHeight(); } -void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, - const TargetRectangle& source_rc) -{ - // for msaa mode, we must resolve the efb content to non-msaa - GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc); - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - BlitScreen(source_rc, target_rc, tex, m_target_width, m_target_height); -} - // ALWAYS call RestoreAPIState for each ResetAPIState call you're doing void Renderer::ResetAPIState() { @@ -1671,6 +1602,7 @@ void Renderer::UnbindTexture(const AbstractTexture* texture) glActiveTexture(static_cast(GL_TEXTURE0 + i)); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + m_bound_textures[i] = nullptr; } } diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 43c110996b..398a49377d 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -118,6 +118,8 @@ public: float far_depth) override; void Draw(u32 base_vertex, u32 num_vertices) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; + void BindBackbuffer(const ClearColor& clear_color = {}) override; + void PresentBackbuffer() override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override; @@ -130,8 +132,9 @@ public: TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; void Flush() override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; + void OnConfigChanged(u32 bits) override; void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; @@ -150,12 +153,6 @@ private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const void* data); - void DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, - const TargetRectangle& source_rc); - - void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width, - int src_height); - void CheckForSurfaceChange(); void CheckForSurfaceResize(); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp index 2d5b82daa2..91c8cf0806 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp @@ -84,9 +84,9 @@ bool SWOGLWindow::Initialize(const WindowSystemInfo& wsi) return true; } -void SWOGLWindow::ShowImage(AbstractTexture* image, const EFBRectangle& xfb_region) +void SWOGLWindow::ShowImage(const AbstractTexture* image, const EFBRectangle& xfb_region) { - SW::SWTexture* sw_image = static_cast(image); + const SW::SWTexture* sw_image = static_cast(image); m_gl_context->Update(); // just updates the render window position and the backbuffer size GLsizei glWidth = (GLsizei)m_gl_context->GetBackBufferWidth(); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.h b/Source/Core/VideoBackends/Software/SWOGLWindow.h index 403ec3dbcb..f1eadc4dd5 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.h +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.h @@ -25,7 +25,7 @@ public: bool IsHeadless() const; // Image to show, will be swapped immediately - void ShowImage(AbstractTexture* image, const EFBRectangle& xfb_region); + void ShowImage(const AbstractTexture* image, const EFBRectangle& xfb_region); static std::unique_ptr Create(const WindowSystemInfo& wsi); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index edddd5156f..6369b35b20 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -90,12 +90,10 @@ std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipel } // Called on the GPU thread -void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks) +void SWRenderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& xfb_region) { if (!IsHeadless()) m_window->ShowImage(texture, xfb_region); - - UpdateActiveConfig(); } u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 563f9ace1a..88c5ece360 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -39,7 +39,7 @@ public: TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 683df63d21..dbb0c6e582 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -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(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(backbuffer->GetWidth()), - static_cast(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(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(texture)->GetRawTexIdentifier(), + 0, m_swap_chain_render_pass); + post_processor->BlitFromTexture(right_rect, rc, + static_cast(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(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(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. diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 1fa3db7008..46a57a036e 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -60,8 +60,9 @@ public: void BBoxWrite(int index, u16 value) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; - void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override; void Flush() override; + void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override; + void OnConfigChanged(u32 bits) override; void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; @@ -88,6 +89,8 @@ public: float far_depth) override; void Draw(u32 base_vertex, u32 num_vertices) override; void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override; + void BindBackbuffer(const ClearColor& clear_color = {}) override; + void PresentBackbuffer() override; private: bool CreateSemaphores(); @@ -97,7 +100,6 @@ private: void CheckForSurfaceChange(); void CheckForSurfaceResize(); - void CheckForConfigChanges(); void ResetSamplerStates(); @@ -110,13 +112,6 @@ private: bool CompileShaders(); void DestroyShaders(); - // Draw the frame, as well as the OSD to the swap chain. - void DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region); - - // Copies/scales an image to the currently-bound framebuffer. - void BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, - const TargetRectangle& src_rect, const Texture2D* src_tex); - std::tuple UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size); VkSemaphore m_image_available_semaphore = VK_NULL_HANDLE; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 97903d7c53..aef425a726 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -91,9 +91,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer CalculateTargetSize(); m_aspect_wide = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN); - - m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; - m_last_efb_multisamples = g_ActiveConfig.iMultisamples; } Renderer::~Renderer() = default; @@ -239,24 +236,56 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet } } -bool Renderer::CheckForHostConfigChanges() +void Renderer::CheckForConfigChanges() { + const ShaderHostConfig old_shader_host_config = ShaderHostConfig::GetCurrent(); + const StereoMode old_stereo = g_ActiveConfig.stereo_mode; + const u32 old_multisamples = g_ActiveConfig.iMultisamples; + const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; + const bool old_force_filtering = g_ActiveConfig.bForceFiltering; + const bool old_vsync = g_ActiveConfig.IsVSync(); + const bool old_bbox = g_ActiveConfig.bBBoxEnable; + + UpdateActiveConfig(); + + // Update texture cache settings with any changed options. + g_texture_cache->OnConfigChanged(g_ActiveConfig); + + // Determine which (if any) settings have changed. ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); - if (new_host_config.bits == m_last_host_config_bits && - m_last_efb_multisamples == g_ActiveConfig.iMultisamples) + u32 changed_bits = 0; + if (old_shader_host_config.bits != new_host_config.bits) + changed_bits |= CONFIG_CHANGE_BIT_HOST_CONFIG; + if (old_stereo != g_ActiveConfig.stereo_mode) + changed_bits |= CONFIG_CHANGE_BIT_STEREO_MODE; + if (old_multisamples != g_ActiveConfig.iMultisamples) + changed_bits |= CONFIG_CHANGE_BIT_MULTISAMPLES; + if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) + changed_bits |= CONFIG_CHANGE_BIT_ANISOTROPY; + if (old_force_filtering != g_ActiveConfig.bForceFiltering) + changed_bits |= CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING; + if (old_vsync != g_ActiveConfig.IsVSync()) + changed_bits |= CONFIG_CHANGE_BIT_VSYNC; + if (old_bbox != g_ActiveConfig.bBBoxEnable) + changed_bits |= CONFIG_CHANGE_BIT_BBOX; + if (CalculateTargetSize()) + changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; + + // No changes? + if (changed_bits == 0) + return; + + // Notify the backend of the changes, if any. + OnConfigChanged(changed_bits); + + // Reload shaders if host config has changed. + if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) { - return false; + OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); + SetPipeline(nullptr); + g_vertex_manager->InvalidatePipelineObject(); + g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples); } - - m_last_host_config_bits = new_host_config.bits; - m_last_efb_multisamples = g_ActiveConfig.iMultisamples; - - // Reload shaders. - OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); - SetPipeline(nullptr); - g_vertex_manager->InvalidatePipelineObject(); - g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples); - return true; } // Create On-Screen-Messages @@ -754,6 +783,8 @@ void Renderer::ShutdownImGui() void Renderer::BeginImGuiFrame() { + std::unique_lock imgui_lock(m_imgui_mutex); + const u64 current_time_us = Common::Timer::GetTimeUs(); const u64 time_diff_us = current_time_us - m_imgui_last_frame_time; const float time_diff_secs = static_cast(time_diff_us / 1000000.0); @@ -768,12 +799,17 @@ void Renderer::BeginImGuiFrame() ImGui::NewFrame(); } -void Renderer::DrawImGui() +void Renderer::RenderImGui() { + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); if (!draw_data) return; + SetViewport(0.0f, 0.0f, static_cast(m_backbuffer_width), + static_cast(m_backbuffer_height), 0.0f, 1.0f); + // Uniform buffer for draws. struct ImGuiUbo { @@ -783,8 +819,9 @@ void Renderer::DrawImGui() ImGuiUbo ubo = {{1.0f / m_backbuffer_width * 2.0f, 1.0f / m_backbuffer_height * 2.0f}}; // Set up common state for drawing. - g_vertex_manager->UploadUtilityUniforms(&ubo, sizeof(ubo)); + SetPipeline(m_imgui_pipeline.get()); SetSamplerState(0, RenderState::GetPointSamplerState()); + g_vertex_manager->UploadUtilityUniforms(&ubo, sizeof(ubo)); for (int i = 0; i < draw_data->CmdListsCount; i++) { @@ -805,7 +842,6 @@ void Renderer::DrawImGui() continue; } - SetPipeline(m_imgui_pipeline.get()); SetScissorRect(MathUtil::Rectangle( static_cast(cmd.ClipRect.x), static_cast(cmd.ClipRect.y), static_cast(cmd.ClipRect.z), static_cast(cmd.ClipRect.w))); @@ -821,13 +857,31 @@ std::unique_lock Renderer::GetImGuiLock() return std::unique_lock(m_imgui_mutex); } +void Renderer::BeginUIFrame() +{ + ResetAPIState(); + BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); +} + +void Renderer::EndUIFrame() +{ + { + auto lock = GetImGuiLock(); + RenderImGui(); + } + + { + std::lock_guard guard(m_swap_mutex); + PresentBackbuffer(); + } + + BeginImGuiFrame(); + RestoreAPIState(); +} + void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks) { - // Hold the imgui lock while we're presenting. - // It's only to prevent races on inputs anyway, at this point. - std::unique_lock imgui_lock(m_imgui_mutex); - const AspectMode suggested = g_ActiveConfig.suggested_aspect_mode; if (suggested == AspectMode::Analog || suggested == AspectMode::AnalogWide) { @@ -892,16 +946,27 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const // with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this. g_vertex_manager->Flush(); - // Draw any imgui overlays we have. Note that "draw" here means "create commands", the actual - // draw calls don't get issued until DrawImGui is called, which happens in SwapImpl. - DrawDebugText(); - OSD::DrawMessages(); - ImGui::Render(); + // Render the XFB to the screen. + ResetAPIState(); + BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); + UpdateDrawRectangle(); + RenderXFBToScreen(xfb_entry->texture.get(), xfb_rect); - // TODO: merge more generic parts into VideoCommon + // Hold the imgui lock while we're presenting. + // It's only to prevent races on inputs anyway, at this point. + { + auto lock = GetImGuiLock(); + + DrawDebugText(); + OSD::DrawMessages(); + + RenderImGui(); + } + + // Present to the window system. { std::lock_guard guard(m_swap_mutex); - g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks); + PresentBackbuffer(); } // Update the window size based on the frame that was just rendered. @@ -923,10 +988,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const GFX_DEBUGGER_PAUSE_AT(NEXT_FRAME, true); // Begin new frame - // Set default viewport and scissor, for the clear to work correctly - // New frame stats.ResetFrame(); g_shader_cache->RetrieveAsyncShaders(); + BeginImGuiFrame(); // We invalidate the pipeline object at the start of the frame. // This is for the rare case where only a single pipeline configuration is used, @@ -938,7 +1002,14 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending copies. g_texture_cache->FlushEFBCopies(); - BeginImGuiFrame(); + // Remove stale EFB/XFB copies. + g_texture_cache->Cleanup(frameCount); + + // Handle any config changes, this gets propogated to the backend. + CheckForConfigChanges(); + g_Config.iSaveTargetId = 0; + + RestoreAPIState(); Core::Callback_VideoCopiedToXFB(true); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 410d998497..45a82c1968 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -109,6 +109,14 @@ public: virtual void Draw(u32 base_vertex, u32 num_vertices) {} virtual void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) {} + // Binds the backbuffer for rendering. The buffer will be cleared immediately after binding. + // This is where any window size changes are detected, therefore m_backbuffer_width and/or + // m_backbuffer_height may change after this function returns. + virtual void BindBackbuffer(const ClearColor& clear_color = {}) {} + + // Presents the backbuffer to the window system, or "swaps buffers". + virtual void PresentBackbuffer() {} + // Shader modules/objects. virtual std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) = 0; @@ -173,11 +181,18 @@ public: virtual u16 BBoxRead(int index) = 0; virtual void BBoxWrite(int index, u16 value) = 0; + virtual void Flush() {} + // Finish up the current frame, print some stats void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks); - virtual void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) = 0; - virtual void Flush() {} + + // Draws the specified XFB buffer to the screen, performing any post-processing. + // Assumes that the backbuffer has already been bound and cleared. + virtual void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) {} + + // Called when the configuration changes, and backend structures need to be updated. + virtual void OnConfigChanged(u32 bits) {} PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; } @@ -195,23 +210,43 @@ public: // as the drawing is tied to a "frame". std::unique_lock GetImGuiLock(); + // Begins/presents a "UI frame". UI frames do not draw any of the console XFB, but this could + // change in the future. + void BeginUIFrame(); + void EndUIFrame(); + protected: + // Bitmask containing information about which configuration has changed for the backend. + enum ConfigChangeBits : u32 + { + CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0), + CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1), + CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2), + CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3), + CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), + CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), + CONFIG_CHANGE_BIT_VSYNC = (1 << 6), + CONFIG_CHANGE_BIT_BBOX = (1 << 7) + }; + std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); - bool CheckForHostConfigChanges(); + void CheckForConfigChanges(); void CheckFifoRecording(); void RecordVideoMemory(); - // Renders ImGui windows to the currently-bound framebuffer. - void DrawImGui(); + // Sets up ImGui state for the next frame. + // This function itself acquires the ImGui lock, so it should not be held. + void BeginImGuiFrame(); // Destroys all ImGui GPU resources, must do before shutdown. void ShutdownImGui(); - // Sets up ImGui state for the next frame. - void BeginImGuiFrame(); + // Renders ImGui windows to the currently-bound framebuffer. + // Should be called with the ImGui lock held. + void RenderImGui(); // TODO: Remove the width/height parameters once we make the EFB an abstract framebuffer. const AbstractFramebuffer* m_current_framebuffer = nullptr; @@ -244,9 +279,6 @@ protected: Common::Flag m_surface_resized; std::mutex m_swap_mutex; - u32 m_last_host_config_bits = 0; - u32 m_last_efb_multisamples = 1; - // ImGui resources. std::unique_ptr m_imgui_vertex_format; std::vector> m_imgui_textures;