From 25e2fa44ff04a0b6007b8a6a3f5052527524d584 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 29 Aug 2024 16:14:51 +0100 Subject: [PATCH] Direct2D: Correctly recreate ID2D1DeviceContext when moving transparent windows between devices --- .../juce_Direct2DGraphicsContext_windows.cpp | 73 +++++++++---------- .../juce_Direct2DHwndContext_windows.cpp | 46 ++++++++---- .../juce_Direct2DImageContext_windows.cpp | 5 ++ .../native/juce_Direct2DResources_windows.cpp | 16 +--- 4 files changed, 73 insertions(+), 67 deletions(-) diff --git a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp index 4022d393ad..61f8479c75 100644 --- a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp +++ b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp @@ -255,9 +255,11 @@ public: // Constructor for first stack entry SavedState (Direct2DGraphicsContext& ownerIn, Rectangle frameSizeIn, + ComSmartPtr deviceContext, ComSmartPtr& colourBrushIn, Direct2DDeviceResources& deviceResourcesIn) : owner (ownerIn), + context (deviceContext), currentBrush (colourBrushIn), colourBrush (colourBrushIn), deviceResources (deviceResourcesIn), @@ -267,7 +269,7 @@ public: void pushLayer (const D2D1_LAYER_PARAMETERS1& layerParameters) { - layers.push (deviceResources.deviceContext, layerParameters); + layers.push (context, layerParameters); } void pushGeometryClipLayer (ComSmartPtr geometry) @@ -290,7 +292,7 @@ public: { JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, pushAliasedAxisAlignedLayerTime) - layers.push (deviceResources.deviceContext, r); + layers.push (context, r); } void pushTransparencyLayer (float opacity) @@ -301,12 +303,12 @@ public: void popLayers() { while (! layers.isEmpty()) - layers.popOne (deviceResources.deviceContext); + layers.popOne (context); } void popTopLayer() { - layers.popOne (deviceResources.deviceContext); + layers.popOne (context); } void setFont (const Font& newFont) @@ -349,17 +351,17 @@ public: JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime); - return Direct2DBitmap::toBitmap (fillType.image, deviceResources.deviceContext, Image::ARGB); + return Direct2DBitmap::toBitmap (fillType.image, context, Image::ARGB); }(); if (d2d1Bitmap != nullptr) { D2D1_BRUSH_PROPERTIES brushProps { fillType.getOpacity(), D2DUtilities::transformToMatrix (fillType.transform) }; auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP); - if (const auto hr = deviceResources.deviceContext->CreateBitmapBrush (d2d1Bitmap, - bmProps, - brushProps, - bitmapBrush.resetAndGetPointerAddress()); SUCCEEDED (hr)) + if (const auto hr = context->CreateBitmapBrush (d2d1Bitmap, + bmProps, + brushProps, + bitmapBrush.resetAndGetPointerAddress()); SUCCEEDED (hr)) { currentBrush = bitmapBrush; } @@ -369,12 +371,12 @@ public: { if (fillType.gradient->isRadial) { - radialGradient = deviceResources.radialGradientCache.get (*fillType.gradient, deviceResources.deviceContext, owner.metrics.get()); + radialGradient = deviceResources.radialGradientCache.get (*fillType.gradient, context, owner.metrics.get()); currentBrush = radialGradient; } else { - linearGradient = deviceResources.linearGradientCache.get (*fillType.gradient, deviceResources.deviceContext, owner.metrics.get()); + linearGradient = deviceResources.linearGradientCache.get (*fillType.gradient, context, owner.metrics.get()); currentBrush = linearGradient; } } @@ -509,7 +511,8 @@ public: Direct2DGraphicsContext& owner; - ComSmartPtr currentBrush = nullptr; + ComSmartPtr context; + ComSmartPtr currentBrush; ComSmartPtr& colourBrush; // reference to shared colour brush ComSmartPtr bitmapBrush; ComSmartPtr linearGradient; @@ -520,7 +523,7 @@ public: Direct2DDeviceResources& deviceResources; RectangleList deviceSpaceClipList; - Font font { FontOptions {} }; + Font font { FontOptions{} }; FillType fillType; @@ -552,12 +555,12 @@ protected: std::vector> savedClientStates; - virtual HRESULT prepare() + virtual bool prepare() { if (! deviceResources.has_value()) - deviceResources = Direct2DDeviceResources::create (directX->adapters.getDefaultAdapter()); + deviceResources = Direct2DDeviceResources::create (getDeviceContext()); - return deviceResources.has_value() ? S_OK : E_FAIL; + return deviceResources.has_value(); } virtual void teardown() @@ -565,8 +568,6 @@ protected: deviceResources.reset(); } - virtual ComSmartPtr getDeviceContextTarget() const = 0; - virtual bool checkPaintReady() { return deviceResources.has_value(); @@ -607,15 +608,17 @@ public: JUCE_TRACE_EVENT_INT_RECT_LIST (etw::startD2DFrame, etw::direct2dKeyword, owner.getFrameId(), paintAreas); + const auto deviceContext = getDeviceContext(); + // Init device context transform - resetTransform (deviceResources->deviceContext); + resetTransform (deviceContext); const auto effectiveDpi = USER_DEFAULT_SCREEN_DPI * dpiScale; - deviceResources->deviceContext->SetDpi (effectiveDpi, effectiveDpi); + deviceContext->SetDpi (effectiveDpi, effectiveDpi); // Start drawing - deviceResources->deviceContext->SetTarget (getDeviceContextTarget()); - deviceResources->deviceContext->BeginDraw(); + deviceContext->SetTarget (getDeviceContextTarget()); + deviceContext->BeginDraw(); // Init the save state stack and return the first saved state return pushFirstSavedState (paintBounds); @@ -633,8 +636,9 @@ public: JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, endDrawDuration) JUCE_SCOPED_TRACE_EVENT_FRAME (etw::endDraw, etw::direct2dKeyword, owner.getFrameId()); - hr = deviceResources->deviceContext->EndDraw(); - deviceResources->deviceContext->SetTarget (nullptr); + const auto deviceContext = getDeviceContext(); + hr = deviceContext->EndDraw(); + deviceContext->SetTarget (nullptr); } jassert (SUCCEEDED (hr)); @@ -660,6 +664,7 @@ public: savedClientStates.push_back (std::make_unique (owner, initialClipRegion, + getDeviceContext(), deviceResources->colourBrush, *deviceResources)); @@ -689,22 +694,19 @@ public: popSavedState(); } - ComSmartPtr getDeviceContext() const noexcept - { - return deviceResources->deviceContext; - } - virtual RectangleList getPaintAreas() const = 0; virtual Rectangle getFrameSize() const = 0; + virtual ComSmartPtr getDeviceContext() const = 0; + virtual ComSmartPtr getDeviceContextTarget() const = 0; void setDeviceContextTransform (AffineTransform transform) { - setTransform (deviceResources->deviceContext, transform); + setTransform (getDeviceContext(), transform); } void resetDeviceContextTransform() { - resetTransform (deviceResources->deviceContext); + resetTransform (getDeviceContext()); } auto getDirect2DFactory() @@ -737,7 +739,7 @@ public: if (rectangleListSpriteBatch == nullptr) return false; - auto deviceContext = getDeviceContext(); + const auto deviceContext = getDeviceContext(); if (deviceContext == nullptr) return false; @@ -798,7 +800,7 @@ public: owner.applyPendingClipList(); - auto deviceContext = deviceResources->deviceContext; + auto deviceContext = getDeviceContext(); if (deviceContext == nullptr) return; @@ -838,10 +840,7 @@ private: DxgiAdapter::Ptr findAdapter() const { - if (! deviceResources.has_value()) - return {}; - - return deviceResources->findAdapter (directX->adapters); + return Direct2DDeviceResources::findAdapter (directX->adapters, getDeviceContext()); } void adapterCreated (DxgiAdapter::Ptr newAdapter) override diff --git a/modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp b/modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp index 66e7539065..ff26fb5f69 100644 --- a/modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp +++ b/modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp @@ -313,6 +313,7 @@ private: }; SwapChain swap; + ComSmartPtr deviceContext; std::unique_ptr swapChainThread; BackBufferLock presentation; std::optional compositionTree; @@ -324,29 +325,35 @@ private: HWND hwnd = nullptr; - HRESULT prepare() override + bool prepare() override { const auto adapter = directX->adapters.getAdapterForHwnd (hwnd); if (adapter == nullptr) - return E_FAIL; + return false; + + if (deviceContext == nullptr) + deviceContext = Direct2DDeviceContext::create (adapter); + + if (deviceContext == nullptr) + return false; if (! deviceResources.has_value()) - deviceResources = Direct2DDeviceResources::create (adapter); + deviceResources = Direct2DDeviceResources::create (deviceContext); if (! deviceResources.has_value()) - return E_FAIL; + return false; if (! hwnd || frameSize.isEmpty()) - return E_FAIL; + return false; if (! swap.canPaint()) { if (auto hr = swap.create (hwnd, frameSize, adapter); FAILED (hr)) - return hr; + return false; - if (auto hr = swap.createBuffer (deviceResources->deviceContext); FAILED (hr)) - return hr; + if (auto hr = swap.createBuffer (getDeviceContext()); FAILED (hr)) + return false; } if (! swapChainThread && swap.swapChainEvent.has_value()) @@ -356,9 +363,9 @@ private: compositionTree = CompositionTree::create (adapter->dxgiDevice, hwnd, swap.chain); if (! compositionTree.has_value()) - return E_FAIL; + return false; - return S_OK; + return true; } void teardown() override @@ -435,10 +442,15 @@ public: return getClientRect(); } + ComSmartPtr getDeviceContext() const override + { + return deviceContext; + } + ComSmartPtr getDeviceContextTarget() const override { if (auto* p = presentation.getPresentation()) - return p->getPresentationBitmap (swap.getSize(), deviceResources->deviceContext); + return p->getPresentationBitmap (swap.getSize(), getDeviceContext()); return {}; } @@ -466,11 +478,11 @@ public: // Resize/scale the swap chain prepare(); - if (auto deviceContext = deviceResources->deviceContext) + if (auto dc = getDeviceContext()) { ScopedMultithread scopedMultithread { directX->getD2DMultithread() }; - auto hr = swap.resize (size, deviceContext); + auto hr = swap.resize (size, dc); jassert (SUCCEEDED (hr)); if (FAILED (hr)) teardown(); @@ -609,7 +621,9 @@ public: // This won't capture child windows. Perhaps a better approach would be to use // IGraphicsCaptureItemInterop, although this is only supported on Windows 10 v1903+ - if (frameSize.isEmpty() || deviceResources->deviceContext == nullptr || swap.buffer == nullptr) + const auto context = getDeviceContext(); + + if (frameSize.isEmpty() || context == nullptr || swap.buffer == nullptr) return {}; // Create the bitmap to receive the snapshot @@ -621,7 +635,7 @@ public: ComSmartPtr snapshot; - if (const auto hr = deviceResources->deviceContext->CreateBitmap (size, nullptr, 0, bitmapProperties, snapshot.resetAndGetPointerAddress()); FAILED (hr)) + if (const auto hr = context->CreateBitmap (size, nullptr, 0, bitmapProperties, snapshot.resetAndGetPointerAddress()); FAILED (hr)) return {}; const ScopedMultithread scope { directX->getD2DMultithread() }; @@ -636,7 +650,7 @@ public: return {}; const Image result { Direct2DPixelData::fromDirect2DBitmap (directX->adapters.getAdapterForHwnd (hwnd), - deviceResources->deviceContext, + context, snapshot) }; swap.chain->Present (0, DXGI_PRESENT_DO_NOT_WAIT); diff --git a/modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp b/modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp index dbbb990255..eafa0e43d7 100644 --- a/modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp +++ b/modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp @@ -58,6 +58,11 @@ public: return { (int) size.width, (int) size.height }; } + ComSmartPtr getDeviceContext() const override + { + return context; + } + ComSmartPtr getDeviceContextTarget() const override { return bitmap; diff --git a/modules/juce_graphics/native/juce_Direct2DResources_windows.cpp b/modules/juce_graphics/native/juce_Direct2DResources_windows.cpp index 96b3c4fcb0..68204d3a2f 100644 --- a/modules/juce_graphics/native/juce_Direct2DResources_windows.cpp +++ b/modules/juce_graphics/native/juce_Direct2DResources_windows.cpp @@ -332,21 +332,15 @@ public: return desc.AdapterLuid; } - static std::optional create (DxgiAdapter::Ptr adapter) - { - return create (Direct2DDeviceContext::create (adapter)); - } - static std::optional create (ComSmartPtr context) { if (context == nullptr) return {}; Direct2DDeviceResources result; - result.deviceContext = context; - if (const auto hr = result.deviceContext->CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f), - result.colourBrush.resetAndGetPointerAddress()); + if (const auto hr = context->CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f), + result.colourBrush.resetAndGetPointerAddress()); FAILED (hr)) { jassertfalse; @@ -358,12 +352,6 @@ public: return result; } - DxgiAdapter::Ptr findAdapter (const DxgiAdapters& adapters) const - { - return findAdapter (adapters, deviceContext); - } - - ComSmartPtr deviceContext; ComSmartPtr colourBrush; LinearGradientCache linearGradientCache; RadialGradientCache radialGradientCache;