From 22ae5baa42edba3ee5ee56836bd04fb405025df7 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 15 Dec 2025 17:54:38 +0000 Subject: [PATCH] Direct2D: Remove SwapChainThread completely D2D repaints are always driven by a vblank timer, and D2D presentation can't happen any faster than the vblank callbacks, so I think it's safe to remove the swapchain machinery and to rely entirely on the vblank callbacks instead. --- .../juce_Direct2DHwndContext_windows.cpp | 119 +----------------- .../native/juce_Direct2DHwndContext_windows.h | 8 +- .../native/juce_Windowing_windows.cpp | 21 ++-- 3 files changed, 13 insertions(+), 135 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.cpp b/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.cpp index 5b9a11bae5..c8dfd6c742 100644 --- a/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.cpp @@ -300,98 +300,10 @@ private: struct Direct2DHwndContext::HwndPimpl : public Pimpl { private: - struct SwapChainThread - { - SwapChainThread (HwndPimpl& ownerIn, HANDLE swapHandle) - : owner (ownerIn), - swapChainEventHandle (swapHandle) - { - SetWindowSubclass (owner.hwnd, subclassWindowProc, (UINT_PTR) this, (DWORD_PTR) this); - } - - ~SwapChainThread() - { - RemoveWindowSubclass (owner.hwnd, subclassWindowProc, (UINT_PTR) this); - SetEvent (quitEvent.getHandle()); - thread.join(); - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SwapChainThread) - - private: - HwndPimpl& owner; - HANDLE swapChainEventHandle = nullptr; - - WindowsScopedEvent quitEvent; - std::thread thread { [&] { threadLoop(); } }; - - static constexpr uint32_t swapchainReadyMessageID = WM_USER + 124; - - bool handleWindowProcMessage (UINT message) - { - if (message == swapchainReadyMessageID) - { - owner.onSwapchainEvent(); - return true; - } - - return false; - } - - static LRESULT CALLBACK subclassWindowProc (HWND hwnd, - UINT message, - WPARAM wParam, - LPARAM lParam, - UINT_PTR, - DWORD_PTR referenceData) - { - auto* that = reinterpret_cast (referenceData); - - if (that != nullptr && that->handleWindowProcMessage (message)) - return 0; - - return DefSubclassProc (hwnd, message, wParam, lParam); - } - - void threadLoop() - { - Thread::setCurrentThreadName ("JUCE D2D swap chain thread"); - - for (;;) - { - const HANDLE handles[] { swapChainEventHandle, quitEvent.getHandle() }; - - const auto waitResult = WaitForMultipleObjects ((DWORD) std::size (handles), - handles, - FALSE, - INFINITE); - - switch (waitResult) - { - case WAIT_OBJECT_0: - { - PostMessage (owner.hwnd, swapchainReadyMessageID, 0, 0); - break; - } - - case WAIT_OBJECT_0 + 1: - return; - - case WAIT_FAILED: - default: - jassertfalse; - break; - } - } - } - }; - HWND hwnd; SwapChain swap; ComSmartPtr deviceContext; - std::unique_ptr swapChainThread; std::optional compositionTree; - SwapchainDelegate& delegate; // Areas that must be repainted during the next paint call, between startFrame/endFrame RectangleList deferredRepaints; @@ -402,16 +314,6 @@ private: std::vector dirtyRectangles; int64 lastFinishFrameTicks = 0; - // Set to true after the swap event is signalled, indicating that we're allowed to try presenting - // a new frame. - bool swapEventReceived = false; - - void onSwapchainEvent() - { - swapEventReceived = true; - delegate.onSwapchainEvent(); - } - bool prepare() override { const auto adapter = getDefaultAdapter(); @@ -437,10 +339,6 @@ private: return false; } - if (swapChainThread == nullptr) - if (auto* e = swap.getEvent()) - swapChainThread = std::make_unique (*this, e->getHandle()); - if (! compositionTree.has_value()) compositionTree = CompositionTree::create (adapter->dxgiDevice, hwnd, swap.getChain()); @@ -453,7 +351,6 @@ private: void teardown() override { compositionTree.reset(); - swapChainThread = nullptr; deviceContext = nullptr; swap = {}; @@ -476,7 +373,6 @@ private: bool ready = Pimpl::checkPaintReady(); ready &= swap.canPaint(); ready &= compositionTree.has_value(); - ready &= swapEventReceived; return ready; } @@ -484,10 +380,9 @@ private: JUCE_DECLARE_WEAK_REFERENCEABLE (HwndPimpl) public: - HwndPimpl (Direct2DHwndContext& ownerIn, HWND hwndIn, SwapchainDelegate& swapDelegate) + HwndPimpl (Direct2DHwndContext& ownerIn, HWND hwndIn) : Pimpl (ownerIn), - hwnd (hwndIn), - delegate (swapDelegate) + hwnd (hwndIn) { } @@ -589,7 +484,7 @@ public: { JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (getMetrics(), present1Duration); - if (swap.getBuffer() == nullptr || dirtyRegionsInBackBuffer.isEmpty() || ! swapEventReceived) + if (swap.getBuffer() == nullptr || dirtyRegionsInBackBuffer.isEmpty()) return; auto const swapChainSize = swap.getSize(); @@ -622,10 +517,6 @@ public: if (FAILED (hr)) return; - // We managed to present a frame, so we should avoid rendering anything or calling - // present again until that frame has been shown on-screen. - swapEventReceived = false; - // There's nothing waiting to be displayed in the backbuffer. dirtyRegionsInBackBuffer.clear(); @@ -687,7 +578,7 @@ public: }; //============================================================================== -Direct2DHwndContext::Direct2DHwndContext (HWND windowHandle, SwapchainDelegate& swapDelegate) +Direct2DHwndContext::Direct2DHwndContext (HWND windowHandle) { #if JUCE_DIRECT2D_METRICS metrics = new Direct2DMetrics { Direct2DMetricsHub::getInstance()->lock, @@ -696,7 +587,7 @@ Direct2DHwndContext::Direct2DHwndContext (HWND windowHandle, SwapchainDelegate& Direct2DMetricsHub::getInstance()->add (metrics); #endif - pimpl = std::make_unique (*this, windowHandle, swapDelegate); + pimpl = std::make_unique (*this, windowHandle); } Direct2DHwndContext::~Direct2DHwndContext() diff --git a/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.h b/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.h index 78161eb244..70ac6196a5 100644 --- a/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.h +++ b/modules/juce_gui_basics/native/juce_Direct2DHwndContext_windows.h @@ -38,13 +38,7 @@ namespace juce class Direct2DHwndContext : public Direct2DGraphicsContext { public: - struct SwapchainDelegate - { - virtual ~SwapchainDelegate() = default; - virtual void onSwapchainEvent() = 0; - }; - - Direct2DHwndContext (HWND windowHandle, SwapchainDelegate& swapDelegate); + explicit Direct2DHwndContext (HWND windowHandle); ~Direct2DHwndContext() override; void handleShowWindow(); diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp index d457c6c8d8..5a1c9b9765 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp @@ -5047,8 +5047,7 @@ private: RectangleList deferredRepaints; }; -class D2DRenderContext : public RenderContext, - private Direct2DHwndContext::SwapchainDelegate +class D2DRenderContext : public RenderContext { public: static constexpr auto name = "Direct2D"; @@ -5067,7 +5066,7 @@ public: if (transparent != direct2DContext->supportsTransparency()) { direct2DContext.reset(); - direct2DContext = getContextForPeer (peer, *this); + direct2DContext = getContextForPeer (peer); } if (direct2DContext->supportsTransparency()) @@ -5114,11 +5113,6 @@ public: } private: - void onSwapchainEvent() override - { - handleDirect2DPaint(); - } - struct WrappedD2DHwndContextBase { virtual ~WrappedD2DHwndContextBase() = default; @@ -5149,8 +5143,8 @@ private: class WrappedD2DHwndContext : public WrappedD2DHwndContextBase { public: - WrappedD2DHwndContext (HWND hwnd, SwapchainDelegate& swapDelegate) - : ctx (hwnd, swapDelegate) {} + explicit WrappedD2DHwndContext (HWND hwnd) + : ctx (hwnd) {} void addDeferredRepaint (Rectangle area) override { @@ -5466,18 +5460,17 @@ private: #endif } - static std::unique_ptr getContextForPeer (HWNDComponentPeer& peer, - SwapchainDelegate& delegate) + static std::unique_ptr getContextForPeer (HWNDComponentPeer& peer) { if (peer.getTransparencyKind() != HWNDComponentPeer::TransparencyKind::opaque) return std::make_unique (peer); - return std::make_unique (peer.getHWND(), delegate); + return std::make_unique (peer.getHWND()); } HWNDComponentPeer& peer; - std::unique_ptr direct2DContext = getContextForPeer (peer, *this); + std::unique_ptr direct2DContext = getContextForPeer (peer); UpdateRegion updateRegion; #if JUCE_ETW_TRACELOGGING