diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index c743a1c1254..881b5e612ac 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -134,12 +134,12 @@ try // the contents of the entire swap chain is redundant, but more importantly because the scroll rect // is the subset of the contents that are being scrolled into. If you scroll the entire viewport // then the scroll rect is empty, which Present1() will loudly complain about. - if (_api.invalidatedRows == range{ 0, _p.s->cellCount.y }) + if (_p.invalidatedRows == range{ 0, _p.s->cellCount.y }) { _p.MarkAllAsDirty(); } - if (const auto offset = _api.scrollOffset) + if (const auto offset = _p.scrollOffset) { if (offset < 0) { @@ -212,17 +212,17 @@ try // * Get the old dirty rect and mark that region as needing invalidation during the upcoming Present1(), // because it'll now be replaced with something else (for instance nothing/whitespace). // * Clear() them to prepare them for the new incoming content from the TextBuffer. - if (_api.invalidatedRows.non_empty()) + if (_p.invalidatedRows.non_empty()) { const til::CoordType targetSizeX = _p.s->targetSize.x; const til::CoordType targetSizeY = _p.s->targetSize.y; _p.dirtyRectInPx.left = 0; - _p.dirtyRectInPx.top = std::min(_p.dirtyRectInPx.top, _api.invalidatedRows.start * _p.s->font->cellSize.y); + _p.dirtyRectInPx.top = std::min(_p.dirtyRectInPx.top, _p.invalidatedRows.start * _p.s->font->cellSize.y); _p.dirtyRectInPx.right = targetSizeX; - _p.dirtyRectInPx.bottom = std::max(_p.dirtyRectInPx.bottom, _api.invalidatedRows.end * _p.s->font->cellSize.y); + _p.dirtyRectInPx.bottom = std::max(_p.dirtyRectInPx.bottom, _p.invalidatedRows.end * _p.s->font->cellSize.y); - for (auto y = _api.invalidatedRows.start; y < _api.invalidatedRows.end; ++y) + for (auto y = _p.invalidatedRows.start; y < _p.invalidatedRows.end; ++y) { const auto r = _p.rows[y]; const auto clampedTop = clamp(r->dirtyTop, 0, targetSizeY); diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index c627a7de43b..b135a8989f8 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -99,6 +99,7 @@ namespace Microsoft::Console::Render::Atlas ATLAS_ATTR_COLD void _recreateBackend(); ATLAS_ATTR_COLD void _handleSwapChainUpdate(); void _createSwapChain(); + void _destroySwapChain(); void _resizeBuffers(); void _updateMatrixTransform(); void _waitUntilCanRender() noexcept; diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index fb85d8e3e0b..22b01baa953 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -162,6 +162,10 @@ void AtlasEngine::_recreateAdapter() void AtlasEngine::_recreateBackend() { + // D3D11 defers the destruction of objects and only one swap chain can be associated with a + // HWND, IWindow, or composition surface at a time. --> Destroy it while we still have the old device. + _destroySwapChain(); + auto d2dMode = ATLAS_DEBUG_FORCE_D2D_MODE; auto deviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED @@ -260,7 +264,6 @@ void AtlasEngine::_recreateBackend() d2dMode |= !options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x; } - _p.swapChain = {}; _p.device = std::move(device); _p.deviceContext = std::move(deviceContext); @@ -285,18 +288,10 @@ void AtlasEngine::_handleSwapChainUpdate() { if (_p.swapChain.targetGeneration != _p.s->target.generation()) { - if (_p.swapChain.swapChain) - { - _b->ReleaseResources(); - _p.deviceContext->ClearState(); - _p.deviceContext->Flush(); - } _createSwapChain(); } else if (_p.swapChain.targetSize != _p.s->targetSize) { - _b->ReleaseResources(); - _p.deviceContext->ClearState(); _resizeBuffers(); } @@ -312,8 +307,7 @@ static constexpr DXGI_SWAP_CHAIN_FLAG swapChainFlags = ATLAS_DEBUG_DISABLE_FRAME void AtlasEngine::_createSwapChain() { - _p.swapChain.swapChain.reset(); - _p.swapChain.frameLatencyWaitableObject.reset(); + _destroySwapChain(); DXGI_SWAP_CHAIN_DESC1 desc{ .Width = _p.s->targetSize.x, @@ -340,6 +334,7 @@ void AtlasEngine::_createSwapChain() }; wil::com_ptr swapChain1; + wil::unique_handle handle; if (_p.s->target->hwnd) { @@ -354,14 +349,14 @@ void AtlasEngine::_createSwapChain() // As per: https://docs.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-dcompositioncreatesurfacehandle static constexpr DWORD COMPOSITIONSURFACE_ALL_ACCESS = 0x0003L; - THROW_IF_FAILED(DCompositionCreateSurfaceHandle(COMPOSITIONSURFACE_ALL_ACCESS, nullptr, _p.swapChain.handle.addressof())); - THROW_IF_FAILED(_p.dxgi.factory.query()->CreateSwapChainForCompositionSurfaceHandle(_p.device.get(), _p.swapChain.handle.get(), &desc, nullptr, swapChain1.addressof())); + THROW_IF_FAILED(DCompositionCreateSurfaceHandle(COMPOSITIONSURFACE_ALL_ACCESS, nullptr, handle.addressof())); + THROW_IF_FAILED(_p.dxgi.factory.query()->CreateSwapChainForCompositionSurfaceHandle(_p.device.get(), handle.get(), &desc, nullptr, swapChain1.addressof())); } _p.swapChain.swapChain = swapChain1.query(); + _p.swapChain.handle = std::move(handle); _p.swapChain.frameLatencyWaitableObject.reset(_p.swapChain.swapChain->GetFrameLatencyWaitableObject()); _p.swapChain.targetGeneration = _p.s->target.generation(); - _p.swapChain.fontGeneration = {}; _p.swapChain.targetSize = _p.s->targetSize; _p.swapChain.waitForPresentation = true; @@ -377,8 +372,30 @@ void AtlasEngine::_createSwapChain() } } +void AtlasEngine::_destroySwapChain() +{ + if (_p.swapChain.swapChain) + { + // D3D11 defers the destruction of objects and only one swap chain can be associated with a + // HWND, IWindow, or composition surface at a time. --> Force the destruction of all objects. + _p.swapChain = {}; + if (_b) + { + _b->ReleaseResources(); + } + if (_p.deviceContext) + { + _p.deviceContext->ClearState(); + _p.deviceContext->Flush(); + } + } +} + void AtlasEngine::_resizeBuffers() { + _b->ReleaseResources(); + _p.deviceContext->ClearState(); + THROW_IF_FAILED(_p.swapChain.swapChain->ResizeBuffers(0, _p.s->targetSize.x, _p.s->targetSize.y, DXGI_FORMAT_UNKNOWN, swapChainFlags)); _p.swapChain.targetSize = _p.s->targetSize; }