From de632fc9c8d7d026ccad1afc2f72224ee9639023 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 26 Jan 2018 16:23:24 +1000 Subject: [PATCH] Renderer: Handle resize events on-demand instead of polling We now differentiate between a resize event and surface change/destroyed event, reducing the overhead for resizes in the Vulkan backend. It is also now now safe to change the surface multiple times if the video thread is lagging behind. --- Source/Core/DolphinNoGUI/MainNoGUI.cpp | 6 +- Source/Core/DolphinQt2/Host.cpp | 4 +- Source/Core/DolphinQt2/Host.h | 2 +- Source/Core/DolphinQt2/RenderWidget.cpp | 8 +- Source/Core/DolphinQt2/RenderWidget.h | 2 +- Source/Core/DolphinWX/FrameTools.cpp | 7 +- Source/Core/VideoBackends/D3D/D3DBase.cpp | 257 ++++++++---------- Source/Core/VideoBackends/D3D/D3DBase.h | 9 +- Source/Core/VideoBackends/D3D/D3DUtil.cpp | 6 +- Source/Core/VideoBackends/D3D/Render.cpp | 114 ++++---- Source/Core/VideoBackends/D3D/Render.h | 9 +- Source/Core/VideoBackends/D3D/main.cpp | 12 +- Source/Core/VideoBackends/OGL/Render.cpp | 89 +++--- Source/Core/VideoBackends/OGL/Render.h | 5 +- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 122 +++++---- Source/Core/VideoBackends/Vulkan/Renderer.h | 3 +- .../Core/VideoBackends/Vulkan/SwapChain.cpp | 27 +- Source/Core/VideoCommon/RenderBase.cpp | 20 +- Source/Core/VideoCommon/RenderBase.h | 12 +- 19 files changed, 364 insertions(+), 350 deletions(-) diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index c78a67589a..def748832f 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -321,12 +321,8 @@ class PlatformX11 : public Platform { last_window_width = event.xconfigure.width; last_window_height = event.xconfigure.height; - - // We call Renderer::ChangeSurface here to indicate the size has changed, - // but pass the same window handle. This is needed for the Vulkan backend, - // otherwise it cannot tell that the window has been resized on some drivers. if (g_renderer) - g_renderer->ChangeSurface(s_window_handle); + g_renderer->ResizeSurface(last_window_width, last_window_height); } } break; diff --git a/Source/Core/DolphinQt2/Host.cpp b/Source/Core/DolphinQt2/Host.cpp index e23f355884..715872c995 100644 --- a/Source/Core/DolphinQt2/Host.cpp +++ b/Source/Core/DolphinQt2/Host.cpp @@ -51,10 +51,10 @@ void Host::SetRenderFullscreen(bool fullscreen) m_render_fullscreen = fullscreen; } -void Host::UpdateSurface() +void Host::ResizeSurface(int new_width, int new_height) { if (g_renderer) - g_renderer->ChangeSurface(GetRenderHandle()); + g_renderer->ResizeSurface(new_width, new_height); } void Host_Message(int id) diff --git a/Source/Core/DolphinQt2/Host.h b/Source/Core/DolphinQt2/Host.h index 0b998722d2..dc74882ae5 100644 --- a/Source/Core/DolphinQt2/Host.h +++ b/Source/Core/DolphinQt2/Host.h @@ -26,7 +26,7 @@ public: void SetRenderHandle(void* handle); void SetRenderFocus(bool focus); void SetRenderFullscreen(bool fullscreen); - void UpdateSurface(); + void ResizeSurface(int new_width, int new_height); signals: void RequestTitle(const QString& title); diff --git a/Source/Core/DolphinQt2/RenderWidget.cpp b/Source/Core/DolphinQt2/RenderWidget.cpp index dd284c4862..019cae9975 100644 --- a/Source/Core/DolphinQt2/RenderWidget.cpp +++ b/Source/Core/DolphinQt2/RenderWidget.cpp @@ -22,7 +22,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) Qt::DirectConnection); connect(this, &RenderWidget::HandleChanged, Host::GetInstance(), &Host::SetRenderHandle, Qt::DirectConnection); - connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::UpdateSurface, + connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::ResizeSurface, Qt::DirectConnection); emit HandleChanged((void*)winId()); @@ -84,8 +84,12 @@ bool RenderWidget::event(QEvent* event) Host::GetInstance()->SetRenderFocus(false); break; case QEvent::Resize: - emit SizeChanged(); + { + const QResizeEvent* se = static_cast(event); + QSize new_size = se->size(); + emit SizeChanged(new_size.width(), new_size.height()); break; + } case QEvent::WindowStateChange: emit StateChanged(isFullScreen()); break; diff --git a/Source/Core/DolphinQt2/RenderWidget.h b/Source/Core/DolphinQt2/RenderWidget.h index 478ac8cda3..4a01c0ac1f 100644 --- a/Source/Core/DolphinQt2/RenderWidget.h +++ b/Source/Core/DolphinQt2/RenderWidget.h @@ -23,7 +23,7 @@ signals: void Closed(); void HandleChanged(void* handle); void StateChanged(bool fullscreen); - void SizeChanged(); + void SizeChanged(int new_width, int new_height); private: void HandleCursorTimer(); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 7f35afbc5f..6542e46d84 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -603,21 +603,18 @@ void CFrame::OnRenderParentResize(wxSizeEvent& event) if (Core::GetState() != Core::State::Uninitialized) { int width, height; + m_render_frame->GetClientSize(&width, &height); if (!SConfig::GetInstance().bRenderToMain && !RendererIsFullscreen() && !m_render_frame->IsMaximized() && !m_render_frame->IsIconized()) { - m_render_frame->GetClientSize(&width, &height); SConfig::GetInstance().iRenderWindowWidth = width; SConfig::GetInstance().iRenderWindowHeight = height; } m_log_window->Refresh(); m_log_window->Update(); - // We call Renderer::ChangeSurface here to indicate the size has changed, - // but pass the same window handle. This is needed for the Vulkan backend, - // otherwise it cannot tell that the window has been resized on some drivers. if (g_renderer) - g_renderer->ChangeSurface(GetRenderHandle()); + g_renderer->ResizeSurface(width, height); } event.Skip(); } diff --git a/Source/Core/VideoBackends/D3D/D3DBase.cpp b/Source/Core/VideoBackends/D3D/D3DBase.cpp index ff2669ec8b..2f81ef45da 100644 --- a/Source/Core/VideoBackends/D3D/D3DBase.cpp +++ b/Source/Core/VideoBackends/D3D/D3DBase.cpp @@ -37,9 +37,9 @@ namespace D3D ID3D11Device* device = nullptr; ID3D11Device1* device1 = nullptr; ID3D11DeviceContext* context = nullptr; -HWND hWnd; +IDXGISwapChain1* swapchain = nullptr; -static IDXGISwapChain1* s_swapchain; +static IDXGIFactory2* s_dxgi_factory; static ID3D11Debug* s_debug; static D3D_FEATURE_LEVEL s_featlevel; static D3DTexture2D* s_backbuf; @@ -49,9 +49,6 @@ static std::vector s_aa_modes; // supported AA modes of the c static bool s_bgra_textures_supported; static bool s_allow_tearing_supported; -static unsigned int s_xres; -static unsigned int s_yres; - constexpr UINT NUM_SUPPORTED_FEATURE_LEVELS = 3; constexpr D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}; @@ -247,17 +244,74 @@ static bool SupportsBPTCTextures(ID3D11Device* dev) return (bc7_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0; } +static bool CreateSwapChainTextures() +{ + ID3D11Texture2D* buf; + HRESULT hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf); + CHECK(SUCCEEDED(hr), "GetBuffer for swap chain failed with HRESULT %08X", hr); + if (FAILED(hr)) + return false; + + s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); + SAFE_RELEASE(buf); + SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture"); + SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view"); + return true; +} + +static bool CreateSwapChain(HWND hWnd) +{ + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; + swap_chain_desc.BufferCount = 2; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.Stereo = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer; + + // This flag is necessary if we want to use a flip-model swapchain without locking the framerate + swap_chain_desc.Flags = s_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; + + HRESULT hr = s_dxgi_factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, + nullptr, &swapchain); + if (FAILED(hr)) + { + // Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to + // a sequential swapchain + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + hr = s_dxgi_factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr, + &swapchain); + } + + if (FAILED(hr)) + { + // Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy + // BitBlt-model swapchain + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + hr = s_dxgi_factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr, + &swapchain); + } + + if (FAILED(hr)) + { + ERROR_LOG(VIDEO, "Failed to create swap chain with HRESULT %08X", hr); + return false; + } + + if (!CreateSwapChainTextures()) + { + SAFE_RELEASE(swapchain); + return false; + } + + return true; +} + HRESULT Create(HWND wnd) { - hWnd = wnd; - HRESULT hr; - - RECT client; - GetClientRect(hWnd, &client); - s_xres = client.right - client.left; - s_yres = client.bottom - client.top; - - hr = LoadDXGI(); + HRESULT hr = LoadDXGI(); if (SUCCEEDED(hr)) hr = LoadD3D(); if (SUCCEEDED(hr)) @@ -270,18 +324,17 @@ HRESULT Create(HWND wnd) return hr; } - IDXGIFactory2* factory; - hr = PCreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)&factory); + hr = PCreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)&s_dxgi_factory); if (FAILED(hr)) MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR); IDXGIAdapter* adapter; - hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter); + hr = s_dxgi_factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter); if (FAILED(hr)) { // try using the first one - hr = factory->EnumAdapters(0, &adapter); + hr = s_dxgi_factory->EnumAdapters(0, &adapter); if (FAILED(hr)) MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR); @@ -301,7 +354,7 @@ HRESULT Create(HWND wnd) // Check support for allow tearing, we query the interface for backwards compatibility UINT allow_tearing = FALSE; IDXGIFactory5* factory5; - hr = factory->QueryInterface(&factory5); + hr = s_dxgi_factory->QueryInterface(&factory5); if (SUCCEEDED(hr)) { hr = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, @@ -310,21 +363,6 @@ HRESULT Create(HWND wnd) } s_allow_tearing_supported = SUCCEEDED(hr) && allow_tearing; - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; - swap_chain_desc.BufferCount = 2; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.SampleDesc.Count = 1; - swap_chain_desc.SampleDesc.Quality = 0; - swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swap_chain_desc.Width = s_xres; - swap_chain_desc.Height = s_yres; - swap_chain_desc.Stereo = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer; - - // This flag is necessary if we want to use a flip-model swapchain without locking the framerate - swap_chain_desc.Flags = s_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; - // Creating debug devices can sometimes fail if the user doesn't have the correct // version of the DirectX SDK. If it does, simply fallback to a non-debug device. if (g_Config.bEnableValidationLayer) @@ -360,30 +398,9 @@ HRESULT Create(HWND wnd) D3D11_SDK_VERSION, &device, &s_featlevel, &context); } - if (SUCCEEDED(hr)) - { - hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr, - &s_swapchain); - if (FAILED(hr)) - { - // Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to - // a sequential swapchain - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr, - &s_swapchain); - } + SAFE_RELEASE(adapter); - if (FAILED(hr)) - { - // Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy - // BitBlt-model swapchain - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr, - &s_swapchain); - } - } - - if (FAILED(hr)) + if (FAILED(hr) || (wnd && !CreateSwapChain(wnd))) { MessageBox( wnd, @@ -391,7 +408,7 @@ HRESULT Create(HWND wnd) _T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR); SAFE_RELEASE(device); SAFE_RELEASE(context); - SAFE_RELEASE(s_swapchain); + SAFE_RELEASE(s_dxgi_factory); return E_FAIL; } @@ -399,52 +416,23 @@ HRESULT Create(HWND wnd) if (FAILED(hr)) WARN_LOG(VIDEO, "Missing Direct3D 11.1 support. Logical operations will not be supported."); - // prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER - // does not work so we disable all monitoring of window messages. However this - // may make it more difficult for DXGI to handle display mode changes. - hr = factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES); - if (FAILED(hr)) - MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 11 backend"), - MB_OK | MB_ICONERROR); - - SetDebugObjectName(context, "device context"); - SAFE_RELEASE(factory); - SAFE_RELEASE(adapter); - - if (SConfig::GetInstance().bFullscreen && !g_ActiveConfig.bBorderlessFullscreen) - { - s_swapchain->SetFullscreenState(true, nullptr); - s_swapchain->ResizeBuffers(0, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, - swap_chain_desc.Flags); - } - - ID3D11Texture2D* buf; - hr = s_swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf); - if (FAILED(hr)) - { - MessageBox(wnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"), - MB_OK | MB_ICONERROR); - SAFE_RELEASE(device); - SAFE_RELEASE(context); - SAFE_RELEASE(s_swapchain); - return E_FAIL; - } - s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); - SAFE_RELEASE(buf); - CHECK(s_backbuf != nullptr, "Create back buffer texture"); - SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture"); - SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view"); - - context->OMSetRenderTargets(1, &s_backbuf->GetRTV(), nullptr); - - // BGRA textures are easier to deal with in TextureCache, but might not be supported by the - // hardware + // BGRA textures are easier to deal with in TextureCache, but might not be supported UINT format_support; device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &format_support); s_bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0; g_Config.backend_info.bSupportsST3CTextures = SupportsS3TCTextures(device); g_Config.backend_info.bSupportsBPTCTextures = SupportsBPTCTextures(device); + // prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER + // does not work so we disable all monitoring of window messages. However this + // may make it more difficult for DXGI to handle display mode changes. + hr = s_dxgi_factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES); + if (FAILED(hr)) + MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 11 backend"), + MB_OK | MB_ICONERROR); + + SetDebugObjectName(context, "device context"); + stateman = new StateManager; return S_OK; } @@ -452,12 +440,13 @@ HRESULT Create(HWND wnd) void Close() { // we can't release the swapchain while in fullscreen. - s_swapchain->SetFullscreenState(false, nullptr); + if (swapchain) + swapchain->SetFullscreenState(false, nullptr); // release all bound resources context->ClearState(); SAFE_RELEASE(s_backbuf); - SAFE_RELEASE(s_swapchain); + SAFE_RELEASE(swapchain); SAFE_DELETE(stateman); context->Flush(); // immediately destroy device objects @@ -465,7 +454,6 @@ void Close() SAFE_RELEASE(device1); ULONG references = device->Release(); -#if defined(_DEBUG) || defined(DEBUGFAST) if (s_debug) { --references; // the debug interface increases the refcount of the device, subtract that. @@ -477,7 +465,6 @@ void Close() } SAFE_RELEASE(s_debug) } -#endif if (references) { @@ -524,19 +511,10 @@ const char* PixelShaderVersionString() return "ps_4_0"; } -D3DTexture2D*& GetBackBuffer() +D3DTexture2D* GetBackBuffer() { return s_backbuf; } -unsigned int GetBackBufferWidth() -{ - return s_xres; -} -unsigned int GetBackBufferHeight() -{ - return s_yres; -} - bool BGRATexturesSupported() { return s_bgra_textures_supported; @@ -572,37 +550,31 @@ u32 GetMaxTextureSize(D3D_FEATURE_LEVEL feature_level) } } -void Reset() +void Reset(HWND new_wnd) { - // release all back buffer references SAFE_RELEASE(s_backbuf); - UINT swap_chain_flags = AllowTearingSupported() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; - - // resize swapchain buffers - RECT client; - GetClientRect(hWnd, &client); - s_xres = client.right - client.left; - s_yres = client.bottom - client.top; - s_swapchain->ResizeBuffers(0, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_flags); - - // recreate back buffer texture - ID3D11Texture2D* buf; - HRESULT hr = s_swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf); - if (FAILED(hr)) + if (swapchain) { - MessageBox(hWnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"), - MB_OK | MB_ICONERROR); - SAFE_RELEASE(device); - SAFE_RELEASE(context); - SAFE_RELEASE(s_swapchain); - return; + if (GetFullscreenState()) + swapchain->SetFullscreenState(FALSE, nullptr); + SAFE_RELEASE(swapchain); + } + + if (new_wnd) + CreateSwapChain(new_wnd); +} + +void ResizeSwapChain() +{ + SAFE_RELEASE(s_backbuf); + const UINT swap_chain_flags = AllowTearingSupported() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; + swapchain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_flags); + if (!CreateSwapChainTextures()) + { + PanicAlert("Failed to get swap chain textures"); + SAFE_RELEASE(swapchain); } - s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); - SAFE_RELEASE(buf); - CHECK(s_backbuf != nullptr, "Create back buffer texture"); - SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture"); - SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view"); } void Present() @@ -616,25 +588,24 @@ void Present() if (AllowTearingSupported() && !g_ActiveConfig.IsVSync() && !GetFullscreenState()) present_flags |= DXGI_PRESENT_ALLOW_TEARING; - if (s_swapchain->IsTemporaryMonoSupported() && - g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer) + if (swapchain->IsTemporaryMonoSupported() && g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer) { present_flags |= DXGI_PRESENT_STEREO_TEMPORARY_MONO; } // TODO: Is 1 the correct value for vsyncing? - s_swapchain->Present((UINT)g_ActiveConfig.IsVSync(), present_flags); + swapchain->Present(static_cast(g_ActiveConfig.IsVSync()), present_flags); } HRESULT SetFullscreenState(bool enable_fullscreen) { - return s_swapchain->SetFullscreenState(enable_fullscreen, nullptr); + return swapchain->SetFullscreenState(enable_fullscreen, nullptr); } bool GetFullscreenState() { BOOL state = FALSE; - s_swapchain->GetFullscreenState(&state, nullptr); + swapchain->GetFullscreenState(&state, nullptr); return !!state; } diff --git a/Source/Core/VideoBackends/D3D/D3DBase.h b/Source/Core/VideoBackends/D3D/D3DBase.h index 94fe8ddf5e..64c19fb5ed 100644 --- a/Source/Core/VideoBackends/D3D/D3DBase.h +++ b/Source/Core/VideoBackends/D3D/D3DBase.h @@ -59,14 +59,13 @@ void Close(); extern ID3D11Device* device; extern ID3D11Device1* device1; extern ID3D11DeviceContext* context; -extern HWND hWnd; +extern IDXGISwapChain1* swapchain; -void Reset(); +void Reset(HWND new_wnd); +void ResizeSwapChain(); void Present(); -unsigned int GetBackBufferWidth(); -unsigned int GetBackBufferHeight(); -D3DTexture2D*& GetBackBuffer(); +D3DTexture2D* GetBackBuffer(); const char* PixelShaderVersionString(); const char* GeometryShaderVersionString(); const char* VertexShaderVersionString(); diff --git a/Source/Core/VideoBackends/D3D/D3DUtil.cpp b/Source/Core/VideoBackends/D3D/D3DUtil.cpp index 2abb356524..bd95004bfe 100644 --- a/Source/Core/VideoBackends/D3D/D3DUtil.cpp +++ b/Source/Core/VideoBackends/D3D/D3DUtil.cpp @@ -387,9 +387,9 @@ int CD3DFont::DrawTextScaled(float x, float y, float size, float spacing, u32 dw UINT stride = sizeof(FONT2DVERTEX); UINT bufoffset = 0; - float scalex = 1 / (float)D3D::GetBackBufferWidth() * 2.f; - float scaley = 1 / (float)D3D::GetBackBufferHeight() * 2.f; - float sizeratio = size / (float)m_LineHeight; + float scalex = 1.0f / g_renderer->GetBackbufferWidth() * 2.f; + float scaley = 1.0f / g_renderer->GetBackbufferHeight() * 2.f; + float sizeratio = size / m_LineHeight; // translate starting positions float sx = x * scalex - 1.f; diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 1e6e7d5290..0e8c822d23 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -53,12 +53,12 @@ typedef struct _Nv_Stereo_Image_Header #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e -Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight()) +Renderer::Renderer(int backbuffer_width, int backbuffer_height) + : ::Renderer(backbuffer_width, backbuffer_height) { m_last_multisamples = g_ActiveConfig.iMultisamples; m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - m_last_fullscreen_mode = D3D::GetFullscreenState(); - + m_last_fullscreen_state = D3D::GetFullscreenState(); g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); SetupDeviceObjects(); @@ -239,25 +239,6 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) return result; } -// With D3D, we have to resize the backbuffer if the window changed -// size. -bool Renderer::CheckForResize() -{ - RECT rcWindow; - GetClientRect(D3D::hWnd, &rcWindow); - int client_width = rcWindow.right - rcWindow.left; - int client_height = rcWindow.bottom - rcWindow.top; - - // Sanity check - if ((client_width != GetBackbufferWidth() || client_height != GetBackbufferHeight()) && - client_width >= 4 && client_height >= 4) - { - return true; - } - - return false; -} - void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) { const RECT rect = {rc.left, rc.top, rc.right, rc.bottom}; @@ -549,12 +530,13 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region 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); - - constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color.data()); // activate linear filtering for the buffer copies @@ -565,8 +547,8 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region xfb_texture->GetConfig().width, xfb_texture->GetConfig().height, Gamma); // Reset viewport for drawing text - D3D11_VIEWPORT vp = - CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetBackbufferWidth(), (float)GetBackbufferHeight()); + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast(m_backbuffer_width), + static_cast(m_backbuffer_height)); D3D::context->RSSetViewports(1, &vp); Renderer::DrawDebugText(); @@ -580,41 +562,21 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region g_texture_cache->OnConfigChanged(g_ActiveConfig); VertexShaderCache::RetreiveAsyncShaders(); - const bool window_resized = CheckForResize(); - const bool fullscreen = D3D::GetFullscreenState(); - const bool fs_changed = m_last_fullscreen_mode != fullscreen; - // Flip/present backbuffer to frontbuffer here - D3D::Present(); + if (D3D::swapchain) + D3D::Present(); // Resize the back buffers NOW to avoid flickering - if (CalculateTargetSize() || window_resized || fs_changed || - m_last_multisamples != g_ActiveConfig.iMultisamples || + if (CalculateTargetSize() || m_last_multisamples != g_ActiveConfig.iMultisamples || m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off)) { m_last_multisamples = g_ActiveConfig.iMultisamples; - m_last_fullscreen_mode = fullscreen; - PixelShaderCache::InvalidateMSAAShaders(); - - if (window_resized || fs_changed) - { - // TODO: Aren't we still holding a reference to the back buffer right now? - D3D::Reset(); - SAFE_RELEASE(m_screenshot_texture); - SAFE_RELEASE(m_3d_vision_texture); - m_backbuffer_width = D3D::GetBackBufferWidth(); - m_backbuffer_height = D3D::GetBackBufferHeight(); - } - - UpdateDrawRectangle(); - m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; - - D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); + 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(), @@ -633,6 +595,54 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region FramebufferManager::BindEFBRenderTarget(); } +void Renderer::CheckForSurfaceChange() +{ + if (!m_surface_changed.TestAndClear()) + return; + + m_surface_handle = m_new_surface_handle; + m_new_surface_handle = nullptr; + + SAFE_RELEASE(m_screenshot_texture); + SAFE_RELEASE(m_3d_vision_texture); + D3D::Reset(reinterpret_cast(m_new_surface_handle)); + UpdateBackbufferSize(); +} + +void Renderer::CheckForSurfaceResize() +{ + const bool fullscreen_state = D3D::GetFullscreenState(); + const bool exclusive_fullscreen_changed = fullscreen_state != m_last_fullscreen_state; + if (!m_surface_resized.TestAndClear() && !exclusive_fullscreen_changed) + return; + + m_backbuffer_width = m_new_backbuffer_width; + m_backbuffer_height = m_new_backbuffer_height; + + SAFE_RELEASE(m_screenshot_texture); + SAFE_RELEASE(m_3d_vision_texture); + m_last_fullscreen_state = fullscreen_state; + if (D3D::swapchain) + D3D::ResizeSwapChain(); + UpdateBackbufferSize(); +} + +void Renderer::UpdateBackbufferSize() +{ + if (D3D::swapchain) + { + DXGI_SWAP_CHAIN_DESC1 desc = {}; + D3D::swapchain->GetDesc1(&desc); + m_backbuffer_width = std::max(desc.Width, 1u); + m_backbuffer_height = std::max(desc.Height, 1u); + } + else + { + m_backbuffer_width = 1; + m_backbuffer_height = 1; + } +} + // ALWAYS call RestoreAPIState for each ResetAPIState call you're doing void Renderer::ResetAPIState() { diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 986d80c3ce..4a01225c2d 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -18,7 +18,7 @@ class D3DTexture2D; class Renderer : public ::Renderer { public: - Renderer(); + Renderer(int backbuffer_width, int backbuffer_height); ~Renderer() override; StateCache& GetStateCache() { return m_state_cache; } @@ -63,8 +63,6 @@ public: void ReinterpretPixelData(unsigned int convtype) override; - bool CheckForResize(); - private: struct GXPipelineState { @@ -77,6 +75,9 @@ private: void SetupDeviceObjects(); void TeardownDeviceObjects(); void Create3DVisionTexture(int width, int height); + void CheckForSurfaceChange(); + void CheckForSurfaceResize(); + void UpdateBackbufferSize(); void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, u32 src_width, u32 src_height, float Gamma); @@ -95,6 +96,6 @@ private: u32 m_last_multisamples = 1; bool m_last_stereo_mode = false; - bool m_last_fullscreen_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 8093a47315..294cd0aebc 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -137,7 +137,17 @@ bool VideoBackend::Initialize(void* window_handle) return false; } - g_renderer = std::make_unique(); + int backbuffer_width = 1, backbuffer_height = 1; + if (D3D::swapchain) + { + DXGI_SWAP_CHAIN_DESC1 desc = {}; + D3D::swapchain->GetDesc1(&desc); + backbuffer_width = std::max(desc.Width, 1u); + backbuffer_height = std::max(desc.Height, 1u); + } + + // internal interfaces + g_renderer = std::make_unique(backbuffer_width, backbuffer_height); g_texture_cache = std::make_unique(); g_vertex_manager = std::make_unique(); g_perf_query = std::make_unique(); diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index d84674cf55..7e11d06574 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -845,12 +845,10 @@ std::unique_ptr Renderer::CreateStagingTexture(StagingTe void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { - u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u); - u32 backbuffer_height = std::max(GLInterface->GetBackBufferHeight(), 1u); - - s_raster_font->printMultilineText(text, left * 2.0f / static_cast(backbuffer_width) - 1.0f, - 1.0f - top * 2.0f / static_cast(backbuffer_height), 0, - backbuffer_width, backbuffer_height, color); + s_raster_font->printMultilineText(text, + left * 2.0f / static_cast(m_backbuffer_width) - 1.0f, + 1.0f - top * 2.0f / static_cast(m_backbuffer_height), 0, + m_backbuffer_width, m_backbuffer_height, color); } TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) @@ -1319,15 +1317,16 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region ResetAPIState(); - UpdateDrawRectangle(); - TargetRectangle flipped_trc = GetTargetRectangle(); - - // Flip top and bottom for some reason; TODO: Fix the code to suck less? - std::swap(flipped_trc.top, flipped_trc.bottom); - // Do our OSD callbacks OSD::DoCallbacks(OSD::CallbackType::OnFrame); + // 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()) { @@ -1357,32 +1356,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region glFlush(); } -#ifdef ANDROID - // Handle surface changes on Android. - if (m_surface_needs_change.IsSet()) - { - GLInterface->UpdateHandle(m_new_surface_handle); - GLInterface->UpdateSurface(); - m_surface_handle = m_new_surface_handle; - m_new_surface_handle = nullptr; - m_surface_needs_change.Clear(); - m_surface_changed.Set(); - } -#endif - - GLInterface->Update(); - // Was the size changed since the last frame? - bool window_resized = false; - int window_width = static_cast(std::max(GLInterface->GetBackBufferWidth(), 1u)); - int window_height = static_cast(std::max(GLInterface->GetBackBufferHeight(), 1u)); - if (window_width != m_backbuffer_width || window_height != m_backbuffer_height) - { - window_resized = true; - m_backbuffer_width = window_width; - m_backbuffer_height = window_height; - } - bool target_size_changed = CalculateTargetSize(); bool stencil_buffer_enabled = static_cast(g_framebuffer_manager.get())->HasStencilBuffer(); @@ -1392,10 +1366,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() || s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off); - if (window_resized || fb_needs_update) - { - UpdateDrawRectangle(); - } if (fb_needs_update) { s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off; @@ -1415,6 +1385,7 @@ 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()) @@ -1455,6 +1426,30 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region ClearEFBCache(); } +void Renderer::CheckForSurfaceChange() +{ + if (!m_surface_changed.TestAndClear()) + return; + + m_surface_handle = m_new_surface_handle; + m_new_surface_handle = nullptr; + GLInterface->UpdateHandle(m_surface_handle); + GLInterface->UpdateSurface(); + + // With a surface change, the window likely has new dimensions. + m_backbuffer_width = GLInterface->GetBackBufferWidth(); + m_backbuffer_height = GLInterface->GetBackBufferHeight(); +} + +void Renderer::CheckForSurfaceResize() +{ + if (!m_surface_resized.TestAndClear()) + return; + + m_backbuffer_width = m_new_backbuffer_width; + m_backbuffer_height = m_new_backbuffer_height; +} + void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, const TargetRectangle& source_rc) { @@ -1572,16 +1567,4 @@ void Renderer::SetInterlacingMode() { // TODO } - -void Renderer::ChangeSurface(void* new_surface_handle) -{ -// Win32 polls the window size when redrawing, X11 runs an event loop in another thread. -// This is only necessary for Android at this point, although handling resizes here -// would be more efficient than polling. -#ifdef ANDROID - m_new_surface_handle = new_surface_handle; - m_surface_needs_change.Set(); - m_surface_changed.Wait(); -#endif -} } diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 68e173f568..9a8207b595 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -121,8 +121,6 @@ public: void ReinterpretPixelData(unsigned int convtype) override; - void ChangeSurface(void* new_surface_handle) override; - private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const void* data); @@ -133,6 +131,9 @@ private: void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width, int src_height); + void CheckForSurfaceChange(); + void CheckForSurfaceResize(); + std::array m_bound_textures{}; }; } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 413f4bb750..7b65884075 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -500,6 +500,10 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region 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(); @@ -543,9 +547,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Determine what (if anything) has changed in the config. CheckForConfigChanges(); - // Handle host window resizes. - CheckForSurfaceChange(); - // Clean up stale textures. TextureCache::GetInstance()->Cleanup(frameCount); @@ -650,71 +651,83 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r void Renderer::CheckForSurfaceChange() { - if (!m_surface_needs_change.IsSet()) + if (!m_surface_changed.TestAndClear()) return; - // Wait for the GPU to catch up since we're going to destroy the swap chain. + m_surface_handle = m_new_surface_handle; + m_new_surface_handle = nullptr; + + // Submit the current draws up until rendering the XFB. + g_command_buffer_mgr->ExecuteCommandBuffer(false, false); g_command_buffer_mgr->WaitForGPUIdle(); // Clear the present failed flag, since we don't want to resize after recreating. g_command_buffer_mgr->CheckLastPresentFail(); - // Fast path, if the surface handle is the same, the window has just been resized. - if (m_swap_chain && m_new_surface_handle == m_swap_chain->GetNativeHandle()) + // Did we previously have a swap chain? + if (m_swap_chain) { - INFO_LOG(VIDEO, "Detected window resize."); - m_swap_chain->RecreateSwapChain(); - - // Notify the main thread we are done. - m_surface_needs_change.Clear(); - m_new_surface_handle = nullptr; - m_surface_changed.Set(); - } - else - { - // Did we previously have a swap chain? - if (m_swap_chain) + if (!m_surface_handle) { - if (!m_new_surface_handle) - { - // If there is no surface now, destroy the swap chain. - m_swap_chain.reset(); - } - else - { - // Recreate the surface. If this fails we're in trouble. - if (!m_swap_chain->RecreateSurface(m_new_surface_handle)) - PanicAlert("Failed to recreate Vulkan surface. Cannot continue."); - } + // If there is no surface now, destroy the swap chain. + m_swap_chain.reset(); } else { - // Previously had no swap chain. So create one. - VkSurfaceKHR surface = SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), - m_new_surface_handle); - if (surface != VK_NULL_HANDLE) - { - m_swap_chain = SwapChain::Create(m_new_surface_handle, surface, g_ActiveConfig.IsVSync()); - if (!m_swap_chain) - PanicAlert("Failed to create swap chain."); - } - else - { - PanicAlert("Failed to create surface."); - } + // Recreate the surface. If this fails we're in trouble. + if (!m_swap_chain->RecreateSurface(m_surface_handle)) + PanicAlert("Failed to recreate Vulkan surface. Cannot continue."); + } + } + else + { + // Previously had no swap chain. So create one. + VkSurfaceKHR surface = + SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_surface_handle); + if (surface != VK_NULL_HANDLE) + { + m_swap_chain = SwapChain::Create(m_surface_handle, surface, g_ActiveConfig.IsVSync()); + if (!m_swap_chain) + PanicAlert("Failed to create swap chain."); + } + else + { + PanicAlert("Failed to create surface."); } - - // Notify calling thread. - m_surface_needs_change.Clear(); - m_surface_handle = m_new_surface_handle; - m_new_surface_handle = nullptr; - m_surface_changed.Set(); } // Handle case where the dimensions are now different. OnSwapChainResized(); } +void Renderer::CheckForSurfaceResize() +{ + if (!m_surface_resized.TestAndClear()) + return; + + m_backbuffer_width = m_new_backbuffer_width; + m_backbuffer_height = m_new_backbuffer_height; + + // If we don't have a surface, how can we resize the swap chain? + // CheckForSurfaceChange should handle this case. + if (!m_swap_chain) + { + WARN_LOG(VIDEO, "Surface resize event received without active surface, ignoring"); + return; + } + + // Wait for the GPU to catch up since we're going to destroy the swap chain. + g_command_buffer_mgr->ExecuteCommandBuffer(false, false); + g_command_buffer_mgr->WaitForGPUIdle(); + + // Clear the present failed flag, since we don't want to resize after recreating. + g_command_buffer_mgr->CheckLastPresentFail(); + + // Resize the swap chain. + m_swap_chain->RecreateSwapChain(); + OnSwapChainResized(); +} + void Renderer::CheckForConfigChanges() { // Save the video config so we can compare against to determine which settings have changed. @@ -782,9 +795,6 @@ void Renderer::OnSwapChainResized() { m_backbuffer_width = m_swap_chain->GetWidth(); m_backbuffer_height = m_swap_chain->GetHeight(); - UpdateDrawRectangle(); - if (CalculateTargetSize()) - RecreateEFBFramebuffer(); } void Renderer::BindEFBToStateTracker() @@ -914,14 +924,6 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne StateTracker::GetInstance()->SetViewport(viewport); } -void Renderer::ChangeSurface(void* new_surface_handle) -{ - // Called by the main thread when the window is resized. - m_new_surface_handle = new_surface_handle; - m_surface_needs_change.Set(); - m_surface_changed.Set(); -} - void Renderer::RecompileShaders() { DestroyShaders(); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index d5a2e9ae1d..b695291c2d 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -70,8 +70,6 @@ public: void SetViewport(float x, float y, float width, float height, float near_depth, float far_depth) override; - void ChangeSurface(void* new_surface_handle) override; - private: bool CreateSemaphores(); void DestroySemaphores(); @@ -79,6 +77,7 @@ private: void BeginFrame(); void CheckForSurfaceChange(); + void CheckForSurfaceResize(); void CheckForConfigChanges(); void ResetSamplerStates(); diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp index 672affeaea..88bb00ee29 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp @@ -14,6 +14,7 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/VulkanContext.h" +#include "VideoCommon/RenderBase.h" #if defined(VK_USE_PLATFORM_XLIB_KHR) #include @@ -261,11 +262,13 @@ bool SwapChain::CreateSwapChain() VkExtent2D size = surface_capabilities.currentExtent; if (size.width == UINT32_MAX) { - size.width = std::min(std::max(surface_capabilities.minImageExtent.width, 640u), - surface_capabilities.maxImageExtent.width); - size.height = std::min(std::max(surface_capabilities.minImageExtent.height, 480u), - surface_capabilities.maxImageExtent.height); + size.width = std::max(g_renderer->GetBackbufferWidth(), 1); + size.height = std::max(g_renderer->GetBackbufferHeight(), 1); } + size.width = MathUtil::Clamp(size.width, surface_capabilities.minImageExtent.width, + surface_capabilities.maxImageExtent.width); + size.height = MathUtil::Clamp(size.height, surface_capabilities.minImageExtent.height, + surface_capabilities.maxImageExtent.height); // Prefer identity transform if possible VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; @@ -468,6 +471,22 @@ bool SwapChain::RecreateSurface(void* native_handle) if (m_surface == VK_NULL_HANDLE) return false; + // The validation layers get angry at us if we don't call this before creating the swapchain. + VkBool32 present_supported = VK_TRUE; + VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR( + g_vulkan_context->GetPhysicalDevice(), g_vulkan_context->GetPresentQueueFamilyIndex(), + m_surface, &present_supported); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: "); + return false; + } + if (!present_supported) + { + PanicAlert("Recreated surface does not support presenting."); + return false; + } + // Finally re-create the swap chain if (!CreateSwapChain() || !SetupSwapChainImages() || !CreateRenderPass()) return false; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 9179fe0fa6..1f0da14269 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -400,6 +400,21 @@ bool Renderer::IsHeadless() const return !m_surface_handle; } +void Renderer::ChangeSurface(void* new_surface_handle) +{ + std::lock_guard lock(m_swap_mutex); + m_new_surface_handle = new_surface_handle; + m_surface_changed.Set(); +} + +void Renderer::ResizeSurface(int new_width, int new_height) +{ + std::lock_guard lock(m_swap_mutex); + m_new_backbuffer_width = new_width; + m_new_backbuffer_height = new_height; + m_surface_resized.Set(); +} + std::tuple Renderer::ScaleToDisplayAspectRatio(const int width, const int height) const { @@ -651,7 +666,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const m_last_xfb_region = xfb_rect; // TODO: merge more generic parts into VideoCommon - g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks, xfb_entry->gamma); + { + std::lock_guard guard(m_swap_mutex); + g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks, xfb_entry->gamma); + } // Update the window size based on the frame that was just rendered. // Due to depending on guest state, we need to call this every frame. diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 9ffd378d67..7b1cba2a44 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -154,7 +154,8 @@ public: PostProcessingShaderImplementation* GetPostProcessor() const { return m_post_processor.get(); } // Final surface changing // This is called when the surface is resized (WX) or the window changes (Android). - virtual void ChangeSurface(void* new_surface_handle) {} + void ChangeSurface(void* new_surface_handle); + void ResizeSurface(int new_width, int new_height); bool UseVertexDepthRange() const; virtual void Shutdown(); @@ -178,9 +179,11 @@ protected: int m_target_width = 0; int m_target_height = 0; - // TODO: Add functionality to reinit all the render targets when the window is resized. + // Backbuffer (window) size and render area int m_backbuffer_width = 0; int m_backbuffer_height = 0; + int m_new_backbuffer_width = 0; + int m_new_backbuffer_height = 0; TargetRectangle m_target_rectangle = {}; FPSCounter m_fps_counter; @@ -189,8 +192,9 @@ protected: void* m_surface_handle = nullptr; void* m_new_surface_handle = nullptr; - Common::Flag m_surface_needs_change; - Common::Event m_surface_changed; + Common::Flag m_surface_changed; + Common::Flag m_surface_resized; + std::mutex m_swap_mutex; u32 m_last_host_config_bits = 0;