From 0a004361fce52b74a6a9c300dac9922e652abd11 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 9 Apr 2020 00:14:19 +1000 Subject: [PATCH] Qt: Work around flip model swap chains being limited to vsync when parented Fixes fast forward not working on some systems. --- src/duckstation-qt/d3d11displaywidget.cpp | 48 +++++++++++++++++------ src/duckstation-qt/d3d11displaywidget.h | 6 ++- src/duckstation-qt/mainwindow.cpp | 1 + src/duckstation-qt/qtdisplaywidget.cpp | 18 ++++----- src/duckstation-qt/qtdisplaywidget.h | 7 ++-- 5 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/duckstation-qt/d3d11displaywidget.cpp b/src/duckstation-qt/d3d11displaywidget.cpp index 18ebb4922..f25c768c4 100644 --- a/src/duckstation-qt/d3d11displaywidget.cpp +++ b/src/duckstation-qt/d3d11displaywidget.cpp @@ -231,7 +231,7 @@ bool D3D11DisplayWidget::createDeviceContext(QThread* worker_thread, bool debug_ m_allow_tearing_supported = (allow_tearing_supported == TRUE); } - if (!createSwapChain(reinterpret_cast(winId()))) + if (!createSwapChain()) return false; if (!QtDisplayWidget::createDeviceContext(worker_thread, debug_device)) @@ -263,9 +263,16 @@ void D3D11DisplayWidget::destroyDeviceContext() m_device.Reset(); } -bool D3D11DisplayWidget::createSwapChain(HWND hwnd) +bool D3D11DisplayWidget::shouldUseFlipModelSwapChain() const { - HRESULT hr; + // For some reason DXGI gets stuck waiting for some kernel object when the Qt window has a parent (render-to-main) on + // some computers, unless the window is completely occluded. The legacy swap chain mode does not have this problem. + return parent() == nullptr; +} + +bool D3D11DisplayWidget::createSwapChain() +{ + m_using_flip_model_swap_chain = shouldUseFlipModelSwapChain(); DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; swap_chain_desc.BufferDesc.Width = m_window_width; @@ -274,20 +281,22 @@ bool D3D11DisplayWidget::createSwapChain(HWND hwnd) swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.BufferCount = 3; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.OutputWindow = hwnd; + swap_chain_desc.OutputWindow = reinterpret_cast(winId()); swap_chain_desc.Windowed = TRUE; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; - if (m_allow_tearing_supported) + m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain); + if (m_using_allow_tearing) swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf()); - if (FAILED(hr)) + HRESULT hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf()); + if (FAILED(hr) && m_using_flip_model_swap_chain) { Log_WarningPrintf("Failed to create a flip-discard swap chain, trying discard."); swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; swap_chain_desc.Flags = 0; - m_allow_tearing_supported = false; + m_using_flip_model_swap_chain = false; + m_using_allow_tearing = false; hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf()); if (FAILED(hr)) @@ -297,13 +306,22 @@ bool D3D11DisplayWidget::createSwapChain(HWND hwnd) } } - hr = m_dxgi_factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES); + hr = m_dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES); if (FAILED(hr)) Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed"); return true; } +void D3D11DisplayWidget::recreateSwapChain() +{ + m_swap_chain_rtv.Reset(); + m_swap_chain.Reset(); + + if (!createSwapChain() || !createSwapChainRTV()) + Panic("Failed to recreate swap chain"); +} + void D3D11DisplayWidget::windowResized(s32 new_window_width, s32 new_window_height) { QtDisplayWidget::windowResized(new_window_width, new_window_height); @@ -312,10 +330,16 @@ void D3D11DisplayWidget::windowResized(s32 new_window_width, s32 new_window_heig if (!m_swap_chain) return; + if (m_using_flip_model_swap_chain != shouldUseFlipModelSwapChain()) + { + recreateSwapChain(); + return; + } + m_swap_chain_rtv.Reset(); HRESULT hr = m_swap_chain->ResizeBuffers(0, new_window_width, new_window_height, DXGI_FORMAT_UNKNOWN, - m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); + m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); if (FAILED(hr)) Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); @@ -439,7 +463,7 @@ void D3D11DisplayWidget::Render() ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - if (!m_vsync && m_allow_tearing_supported) + if (!m_vsync && m_using_allow_tearing) m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); else m_swap_chain->Present(BoolToUInt32(m_vsync), 0); diff --git a/src/duckstation-qt/d3d11displaywidget.h b/src/duckstation-qt/d3d11displaywidget.h index 803a1c441..927c1d545 100644 --- a/src/duckstation-qt/d3d11displaywidget.h +++ b/src/duckstation-qt/d3d11displaywidget.h @@ -55,8 +55,10 @@ private: bool createDeviceResources() override; void destroyDeviceResources() override; - bool createSwapChain(HWND hwnd); + bool shouldUseFlipModelSwapChain() const; + bool createSwapChain(); bool createSwapChainRTV(); + void recreateSwapChain(); void renderDisplay(); @@ -80,5 +82,7 @@ private: D3D11::AutoStagingTexture m_readback_staging_texture; bool m_allow_tearing_supported = false; + bool m_using_flip_model_swap_chain = false; + bool m_using_allow_tearing = false; bool m_vsync = false; }; diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 8b6ffe6bc..53b01187d 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -153,6 +153,7 @@ void MainWindow::updateDisplayWindow(bool fullscreen, bool render_to_main) m_display_widget->setCursor(QCursor()); } + m_display_widget->windowResizedEvent(m_display_widget->scaledWindowWidth(), m_display_widget->scaledWindowHeight()); m_display_widget->setFocus(); QSignalBlocker blocker(m_ui.actionFullscreen); diff --git a/src/duckstation-qt/qtdisplaywidget.cpp b/src/duckstation-qt/qtdisplaywidget.cpp index ccd1affc9..11e3a4aba 100644 --- a/src/duckstation-qt/qtdisplaywidget.cpp +++ b/src/duckstation-qt/qtdisplaywidget.cpp @@ -51,7 +51,7 @@ void QtDisplayWidget::destroyDeviceContext() destroyDeviceResources(); } -qreal QtDisplayWidget::getDevicePixelRatioFromScreen() const +qreal QtDisplayWidget::devicePixelRatioFromScreen() const { QScreen* screen_for_ratio; #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) @@ -65,14 +65,14 @@ qreal QtDisplayWidget::getDevicePixelRatioFromScreen() const return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); } -int QtDisplayWidget::getScaledWindowWidth() const +int QtDisplayWidget::scaledWindowWidth() const { - return static_cast(std::ceil(static_cast(width()) * getDevicePixelRatioFromScreen())); + return static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())); } -int QtDisplayWidget::getScaledWindowHeight() const +int QtDisplayWidget::scaledWindowHeight() const { - return static_cast(std::ceil(static_cast(height()) * getDevicePixelRatioFromScreen())); + return static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())); } bool QtDisplayWidget::createImGuiContext() @@ -81,10 +81,10 @@ bool QtDisplayWidget::createImGuiContext() auto& io = ImGui::GetIO(); io.IniFilename = nullptr; - io.DisplaySize.x = static_cast(getScaledWindowWidth()); - io.DisplaySize.y = static_cast(getScaledWindowHeight()); + io.DisplaySize.x = static_cast(scaledWindowWidth()); + io.DisplaySize.y = static_cast(scaledWindowHeight()); - const float framebuffer_scale = static_cast(getDevicePixelRatioFromScreen()); + const float framebuffer_scale = static_cast(devicePixelRatioFromScreen()); io.DisplayFramebufferScale.x = framebuffer_scale; io.DisplayFramebufferScale.y = framebuffer_scale; ImGui::GetStyle().ScaleAllSizes(framebuffer_scale); @@ -141,7 +141,7 @@ bool QtDisplayWidget::event(QEvent* event) { QWidget::event(event); - emit windowResizedEvent(getScaledWindowWidth(), getScaledWindowHeight()); + emit windowResizedEvent(scaledWindowWidth(), scaledWindowHeight()); return true; } diff --git a/src/duckstation-qt/qtdisplaywidget.h b/src/duckstation-qt/qtdisplaywidget.h index 4f5e1037b..2c07fc29f 100644 --- a/src/duckstation-qt/qtdisplaywidget.h +++ b/src/duckstation-qt/qtdisplaywidget.h @@ -29,14 +29,15 @@ public: virtual QPaintEngine* paintEngine() const override; + int scaledWindowWidth() const; + int scaledWindowHeight() const; + Q_SIGNALS: void windowResizedEvent(int width, int height); void windowRestoredEvent(); protected: - qreal getDevicePixelRatioFromScreen() const; - int getScaledWindowWidth() const; - int getScaledWindowHeight() const; + qreal devicePixelRatioFromScreen() const; virtual bool createImGuiContext(); virtual void destroyImGuiContext();