1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-16 00:34:19 +00:00

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.
This commit is contained in:
reuk 2025-12-15 17:54:38 +00:00
parent cd6176bd00
commit 22ae5baa42
No known key found for this signature in database
3 changed files with 13 additions and 135 deletions

View file

@ -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<SwapChainThread*> (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<ID2D1DeviceContext1> deviceContext;
std::unique_ptr<SwapChainThread> swapChainThread;
std::optional<CompositionTree> compositionTree;
SwapchainDelegate& delegate;
// Areas that must be repainted during the next paint call, between startFrame/endFrame
RectangleList<int> deferredRepaints;
@ -402,16 +314,6 @@ private:
std::vector<RECT> 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<SwapChainThread> (*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<HwndPimpl> (*this, windowHandle, swapDelegate);
pimpl = std::make_unique<HwndPimpl> (*this, windowHandle);
}
Direct2DHwndContext::~Direct2DHwndContext()

View file

@ -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();

View file

@ -5047,8 +5047,7 @@ private:
RectangleList<int> 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<int> area) override
{
@ -5466,18 +5460,17 @@ private:
#endif
}
static std::unique_ptr<WrappedD2DHwndContextBase> getContextForPeer (HWNDComponentPeer& peer,
SwapchainDelegate& delegate)
static std::unique_ptr<WrappedD2DHwndContextBase> getContextForPeer (HWNDComponentPeer& peer)
{
if (peer.getTransparencyKind() != HWNDComponentPeer::TransparencyKind::opaque)
return std::make_unique<WrappedD2DHwndContextTransparent> (peer);
return std::make_unique<WrappedD2DHwndContext> (peer.getHWND(), delegate);
return std::make_unique<WrappedD2DHwndContext> (peer.getHWND());
}
HWNDComponentPeer& peer;
std::unique_ptr<WrappedD2DHwndContextBase> direct2DContext = getContextForPeer (peer, *this);
std::unique_ptr<WrappedD2DHwndContextBase> direct2DContext = getContextForPeer (peer);
UpdateRegion updateRegion;
#if JUCE_ETW_TRACELOGGING