1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Direct2D: Correctly recreate ID2D1DeviceContext when moving transparent windows between devices

This commit is contained in:
reuk 2024-08-29 16:14:51 +01:00
parent 14d52769dc
commit 25e2fa44ff
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
4 changed files with 73 additions and 67 deletions

View file

@ -255,9 +255,11 @@ public:
// Constructor for first stack entry // Constructor for first stack entry
SavedState (Direct2DGraphicsContext& ownerIn, SavedState (Direct2DGraphicsContext& ownerIn,
Rectangle<int> frameSizeIn, Rectangle<int> frameSizeIn,
ComSmartPtr<ID2D1DeviceContext1> deviceContext,
ComSmartPtr<ID2D1SolidColorBrush>& colourBrushIn, ComSmartPtr<ID2D1SolidColorBrush>& colourBrushIn,
Direct2DDeviceResources& deviceResourcesIn) Direct2DDeviceResources& deviceResourcesIn)
: owner (ownerIn), : owner (ownerIn),
context (deviceContext),
currentBrush (colourBrushIn), currentBrush (colourBrushIn),
colourBrush (colourBrushIn), colourBrush (colourBrushIn),
deviceResources (deviceResourcesIn), deviceResources (deviceResourcesIn),
@ -267,7 +269,7 @@ public:
void pushLayer (const D2D1_LAYER_PARAMETERS1& layerParameters) void pushLayer (const D2D1_LAYER_PARAMETERS1& layerParameters)
{ {
layers.push (deviceResources.deviceContext, layerParameters); layers.push (context, layerParameters);
} }
void pushGeometryClipLayer (ComSmartPtr<ID2D1Geometry> geometry) void pushGeometryClipLayer (ComSmartPtr<ID2D1Geometry> geometry)
@ -290,7 +292,7 @@ public:
{ {
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, pushAliasedAxisAlignedLayerTime) JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, pushAliasedAxisAlignedLayerTime)
layers.push (deviceResources.deviceContext, r); layers.push (context, r);
} }
void pushTransparencyLayer (float opacity) void pushTransparencyLayer (float opacity)
@ -301,12 +303,12 @@ public:
void popLayers() void popLayers()
{ {
while (! layers.isEmpty()) while (! layers.isEmpty())
layers.popOne (deviceResources.deviceContext); layers.popOne (context);
} }
void popTopLayer() void popTopLayer()
{ {
layers.popOne (deviceResources.deviceContext); layers.popOne (context);
} }
void setFont (const Font& newFont) void setFont (const Font& newFont)
@ -349,17 +351,17 @@ public:
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime); 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) if (d2d1Bitmap != nullptr)
{ {
D2D1_BRUSH_PROPERTIES brushProps { fillType.getOpacity(), D2DUtilities::transformToMatrix (fillType.transform) }; D2D1_BRUSH_PROPERTIES brushProps { fillType.getOpacity(), D2DUtilities::transformToMatrix (fillType.transform) };
auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP); auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
if (const auto hr = deviceResources.deviceContext->CreateBitmapBrush (d2d1Bitmap, if (const auto hr = context->CreateBitmapBrush (d2d1Bitmap,
bmProps, bmProps,
brushProps, brushProps,
bitmapBrush.resetAndGetPointerAddress()); SUCCEEDED (hr)) bitmapBrush.resetAndGetPointerAddress()); SUCCEEDED (hr))
{ {
currentBrush = bitmapBrush; currentBrush = bitmapBrush;
} }
@ -369,12 +371,12 @@ public:
{ {
if (fillType.gradient->isRadial) 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; currentBrush = radialGradient;
} }
else else
{ {
linearGradient = deviceResources.linearGradientCache.get (*fillType.gradient, deviceResources.deviceContext, owner.metrics.get()); linearGradient = deviceResources.linearGradientCache.get (*fillType.gradient, context, owner.metrics.get());
currentBrush = linearGradient; currentBrush = linearGradient;
} }
} }
@ -509,7 +511,8 @@ public:
Direct2DGraphicsContext& owner; Direct2DGraphicsContext& owner;
ComSmartPtr<ID2D1Brush> currentBrush = nullptr; ComSmartPtr<ID2D1DeviceContext1> context;
ComSmartPtr<ID2D1Brush> currentBrush;
ComSmartPtr<ID2D1SolidColorBrush>& colourBrush; // reference to shared colour brush ComSmartPtr<ID2D1SolidColorBrush>& colourBrush; // reference to shared colour brush
ComSmartPtr<ID2D1BitmapBrush> bitmapBrush; ComSmartPtr<ID2D1BitmapBrush> bitmapBrush;
ComSmartPtr<ID2D1LinearGradientBrush> linearGradient; ComSmartPtr<ID2D1LinearGradientBrush> linearGradient;
@ -520,7 +523,7 @@ public:
Direct2DDeviceResources& deviceResources; Direct2DDeviceResources& deviceResources;
RectangleList<float> deviceSpaceClipList; RectangleList<float> deviceSpaceClipList;
Font font { FontOptions {} }; Font font { FontOptions{} };
FillType fillType; FillType fillType;
@ -552,12 +555,12 @@ protected:
std::vector<std::unique_ptr<Direct2DGraphicsContext::SavedState>> savedClientStates; std::vector<std::unique_ptr<Direct2DGraphicsContext::SavedState>> savedClientStates;
virtual HRESULT prepare() virtual bool prepare()
{ {
if (! deviceResources.has_value()) 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() virtual void teardown()
@ -565,8 +568,6 @@ protected:
deviceResources.reset(); deviceResources.reset();
} }
virtual ComSmartPtr<ID2D1Image> getDeviceContextTarget() const = 0;
virtual bool checkPaintReady() virtual bool checkPaintReady()
{ {
return deviceResources.has_value(); return deviceResources.has_value();
@ -607,15 +608,17 @@ public:
JUCE_TRACE_EVENT_INT_RECT_LIST (etw::startD2DFrame, etw::direct2dKeyword, owner.getFrameId(), paintAreas); JUCE_TRACE_EVENT_INT_RECT_LIST (etw::startD2DFrame, etw::direct2dKeyword, owner.getFrameId(), paintAreas);
const auto deviceContext = getDeviceContext();
// Init device context transform // Init device context transform
resetTransform (deviceResources->deviceContext); resetTransform (deviceContext);
const auto effectiveDpi = USER_DEFAULT_SCREEN_DPI * dpiScale; const auto effectiveDpi = USER_DEFAULT_SCREEN_DPI * dpiScale;
deviceResources->deviceContext->SetDpi (effectiveDpi, effectiveDpi); deviceContext->SetDpi (effectiveDpi, effectiveDpi);
// Start drawing // Start drawing
deviceResources->deviceContext->SetTarget (getDeviceContextTarget()); deviceContext->SetTarget (getDeviceContextTarget());
deviceResources->deviceContext->BeginDraw(); deviceContext->BeginDraw();
// Init the save state stack and return the first saved state // Init the save state stack and return the first saved state
return pushFirstSavedState (paintBounds); return pushFirstSavedState (paintBounds);
@ -633,8 +636,9 @@ public:
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, endDrawDuration) JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, endDrawDuration)
JUCE_SCOPED_TRACE_EVENT_FRAME (etw::endDraw, etw::direct2dKeyword, owner.getFrameId()); JUCE_SCOPED_TRACE_EVENT_FRAME (etw::endDraw, etw::direct2dKeyword, owner.getFrameId());
hr = deviceResources->deviceContext->EndDraw(); const auto deviceContext = getDeviceContext();
deviceResources->deviceContext->SetTarget (nullptr); hr = deviceContext->EndDraw();
deviceContext->SetTarget (nullptr);
} }
jassert (SUCCEEDED (hr)); jassert (SUCCEEDED (hr));
@ -660,6 +664,7 @@ public:
savedClientStates.push_back (std::make_unique<SavedState> (owner, savedClientStates.push_back (std::make_unique<SavedState> (owner,
initialClipRegion, initialClipRegion,
getDeviceContext(),
deviceResources->colourBrush, deviceResources->colourBrush,
*deviceResources)); *deviceResources));
@ -689,22 +694,19 @@ public:
popSavedState(); popSavedState();
} }
ComSmartPtr<ID2D1DeviceContext1> getDeviceContext() const noexcept
{
return deviceResources->deviceContext;
}
virtual RectangleList<int> getPaintAreas() const = 0; virtual RectangleList<int> getPaintAreas() const = 0;
virtual Rectangle<int> getFrameSize() const = 0; virtual Rectangle<int> getFrameSize() const = 0;
virtual ComSmartPtr<ID2D1DeviceContext1> getDeviceContext() const = 0;
virtual ComSmartPtr<ID2D1Image> getDeviceContextTarget() const = 0;
void setDeviceContextTransform (AffineTransform transform) void setDeviceContextTransform (AffineTransform transform)
{ {
setTransform (deviceResources->deviceContext, transform); setTransform (getDeviceContext(), transform);
} }
void resetDeviceContextTransform() void resetDeviceContextTransform()
{ {
resetTransform (deviceResources->deviceContext); resetTransform (getDeviceContext());
} }
auto getDirect2DFactory() auto getDirect2DFactory()
@ -737,7 +739,7 @@ public:
if (rectangleListSpriteBatch == nullptr) if (rectangleListSpriteBatch == nullptr)
return false; return false;
auto deviceContext = getDeviceContext(); const auto deviceContext = getDeviceContext();
if (deviceContext == nullptr) if (deviceContext == nullptr)
return false; return false;
@ -798,7 +800,7 @@ public:
owner.applyPendingClipList(); owner.applyPendingClipList();
auto deviceContext = deviceResources->deviceContext; auto deviceContext = getDeviceContext();
if (deviceContext == nullptr) if (deviceContext == nullptr)
return; return;
@ -838,10 +840,7 @@ private:
DxgiAdapter::Ptr findAdapter() const DxgiAdapter::Ptr findAdapter() const
{ {
if (! deviceResources.has_value()) return Direct2DDeviceResources::findAdapter (directX->adapters, getDeviceContext());
return {};
return deviceResources->findAdapter (directX->adapters);
} }
void adapterCreated (DxgiAdapter::Ptr newAdapter) override void adapterCreated (DxgiAdapter::Ptr newAdapter) override

View file

@ -313,6 +313,7 @@ private:
}; };
SwapChain swap; SwapChain swap;
ComSmartPtr<ID2D1DeviceContext1> deviceContext;
std::unique_ptr<SwapChainThread> swapChainThread; std::unique_ptr<SwapChainThread> swapChainThread;
BackBufferLock presentation; BackBufferLock presentation;
std::optional<CompositionTree> compositionTree; std::optional<CompositionTree> compositionTree;
@ -324,29 +325,35 @@ private:
HWND hwnd = nullptr; HWND hwnd = nullptr;
HRESULT prepare() override bool prepare() override
{ {
const auto adapter = directX->adapters.getAdapterForHwnd (hwnd); const auto adapter = directX->adapters.getAdapterForHwnd (hwnd);
if (adapter == nullptr) if (adapter == nullptr)
return E_FAIL; return false;
if (deviceContext == nullptr)
deviceContext = Direct2DDeviceContext::create (adapter);
if (deviceContext == nullptr)
return false;
if (! deviceResources.has_value()) if (! deviceResources.has_value())
deviceResources = Direct2DDeviceResources::create (adapter); deviceResources = Direct2DDeviceResources::create (deviceContext);
if (! deviceResources.has_value()) if (! deviceResources.has_value())
return E_FAIL; return false;
if (! hwnd || frameSize.isEmpty()) if (! hwnd || frameSize.isEmpty())
return E_FAIL; return false;
if (! swap.canPaint()) if (! swap.canPaint())
{ {
if (auto hr = swap.create (hwnd, frameSize, adapter); FAILED (hr)) if (auto hr = swap.create (hwnd, frameSize, adapter); FAILED (hr))
return hr; return false;
if (auto hr = swap.createBuffer (deviceResources->deviceContext); FAILED (hr)) if (auto hr = swap.createBuffer (getDeviceContext()); FAILED (hr))
return hr; return false;
} }
if (! swapChainThread && swap.swapChainEvent.has_value()) if (! swapChainThread && swap.swapChainEvent.has_value())
@ -356,9 +363,9 @@ private:
compositionTree = CompositionTree::create (adapter->dxgiDevice, hwnd, swap.chain); compositionTree = CompositionTree::create (adapter->dxgiDevice, hwnd, swap.chain);
if (! compositionTree.has_value()) if (! compositionTree.has_value())
return E_FAIL; return false;
return S_OK; return true;
} }
void teardown() override void teardown() override
@ -435,10 +442,15 @@ public:
return getClientRect(); return getClientRect();
} }
ComSmartPtr<ID2D1DeviceContext1> getDeviceContext() const override
{
return deviceContext;
}
ComSmartPtr<ID2D1Image> getDeviceContextTarget() const override ComSmartPtr<ID2D1Image> getDeviceContextTarget() const override
{ {
if (auto* p = presentation.getPresentation()) if (auto* p = presentation.getPresentation())
return p->getPresentationBitmap (swap.getSize(), deviceResources->deviceContext); return p->getPresentationBitmap (swap.getSize(), getDeviceContext());
return {}; return {};
} }
@ -466,11 +478,11 @@ public:
// Resize/scale the swap chain // Resize/scale the swap chain
prepare(); prepare();
if (auto deviceContext = deviceResources->deviceContext) if (auto dc = getDeviceContext())
{ {
ScopedMultithread scopedMultithread { directX->getD2DMultithread() }; ScopedMultithread scopedMultithread { directX->getD2DMultithread() };
auto hr = swap.resize (size, deviceContext); auto hr = swap.resize (size, dc);
jassert (SUCCEEDED (hr)); jassert (SUCCEEDED (hr));
if (FAILED (hr)) if (FAILED (hr))
teardown(); teardown();
@ -609,7 +621,9 @@ public:
// This won't capture child windows. Perhaps a better approach would be to use // This won't capture child windows. Perhaps a better approach would be to use
// IGraphicsCaptureItemInterop, although this is only supported on Windows 10 v1903+ // 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 {}; return {};
// Create the bitmap to receive the snapshot // Create the bitmap to receive the snapshot
@ -621,7 +635,7 @@ public:
ComSmartPtr<ID2D1Bitmap1> snapshot; ComSmartPtr<ID2D1Bitmap1> 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 {}; return {};
const ScopedMultithread scope { directX->getD2DMultithread() }; const ScopedMultithread scope { directX->getD2DMultithread() };
@ -636,7 +650,7 @@ public:
return {}; return {};
const Image result { Direct2DPixelData::fromDirect2DBitmap (directX->adapters.getAdapterForHwnd (hwnd), const Image result { Direct2DPixelData::fromDirect2DBitmap (directX->adapters.getAdapterForHwnd (hwnd),
deviceResources->deviceContext, context,
snapshot) }; snapshot) };
swap.chain->Present (0, DXGI_PRESENT_DO_NOT_WAIT); swap.chain->Present (0, DXGI_PRESENT_DO_NOT_WAIT);

View file

@ -58,6 +58,11 @@ public:
return { (int) size.width, (int) size.height }; return { (int) size.width, (int) size.height };
} }
ComSmartPtr<ID2D1DeviceContext1> getDeviceContext() const override
{
return context;
}
ComSmartPtr<ID2D1Image> getDeviceContextTarget() const override ComSmartPtr<ID2D1Image> getDeviceContextTarget() const override
{ {
return bitmap; return bitmap;

View file

@ -332,21 +332,15 @@ public:
return desc.AdapterLuid; return desc.AdapterLuid;
} }
static std::optional<Direct2DDeviceResources> create (DxgiAdapter::Ptr adapter)
{
return create (Direct2DDeviceContext::create (adapter));
}
static std::optional<Direct2DDeviceResources> create (ComSmartPtr<ID2D1DeviceContext1> context) static std::optional<Direct2DDeviceResources> create (ComSmartPtr<ID2D1DeviceContext1> context)
{ {
if (context == nullptr) if (context == nullptr)
return {}; return {};
Direct2DDeviceResources result; Direct2DDeviceResources result;
result.deviceContext = context;
if (const auto hr = result.deviceContext->CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f), if (const auto hr = context->CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f),
result.colourBrush.resetAndGetPointerAddress()); result.colourBrush.resetAndGetPointerAddress());
FAILED (hr)) FAILED (hr))
{ {
jassertfalse; jassertfalse;
@ -358,12 +352,6 @@ public:
return result; return result;
} }
DxgiAdapter::Ptr findAdapter (const DxgiAdapters& adapters) const
{
return findAdapter (adapters, deviceContext);
}
ComSmartPtr<ID2D1DeviceContext1> deviceContext;
ComSmartPtr<ID2D1SolidColorBrush> colourBrush; ComSmartPtr<ID2D1SolidColorBrush> colourBrush;
LinearGradientCache linearGradientCache; LinearGradientCache linearGradientCache;
RadialGradientCache radialGradientCache; RadialGradientCache radialGradientCache;