Skip to content

Commit

Permalink
AtlasEngine: Improve scroll and swap chain invalidation (#15425)
Browse files Browse the repository at this point in the history
`_p.MarkAllAsDirty()` sets `_p.scrollOffset` to 0, so we need to use
that instead of `_api.scrollOffset` when getting the offset.
Additionally, the previous code failed to release the swap chain
when recreating the backend, which is technically not correct.
I'm not sure to what issues this might have led, as it didn't had any
negative effects on my PC, but it's definitely not according to spec.

## Validation Steps Performed
Difficult to test but it seems alright.

(cherry picked from commit a19d30a)
Service-Card-Id: 89315195
Service-Version: 1.18
  • Loading branch information
lhecker authored and DHowett committed May 26, 2023
1 parent e4f0ab6 commit e1e3acf
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 20 deletions.
12 changes: 6 additions & 6 deletions src/renderer/atlas/AtlasEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<u16>{ 0, _p.s->cellCount.y })
if (_p.invalidatedRows == range<u16>{ 0, _p.s->cellCount.y })
{
_p.MarkAllAsDirty();
}

if (const auto offset = _api.scrollOffset)
if (const auto offset = _p.scrollOffset)
{
if (offset < 0)
{
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/renderer/atlas/AtlasEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
45 changes: 31 additions & 14 deletions src/renderer/atlas/AtlasEngine.r.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

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

Expand All @@ -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,
Expand All @@ -340,6 +334,7 @@ void AtlasEngine::_createSwapChain()
};

wil::com_ptr<IDXGISwapChain1> swapChain1;
wil::unique_handle handle;

if (_p.s->target->hwnd)
{
Expand All @@ -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<IDXGIFactoryMedia>()->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<IDXGIFactoryMedia>()->CreateSwapChainForCompositionSurfaceHandle(_p.device.get(), handle.get(), &desc, nullptr, swapChain1.addressof()));
}

_p.swapChain.swapChain = swapChain1.query<IDXGISwapChain2>();
_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;

Expand All @@ -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;
}
Expand Down

0 comments on commit e1e3acf

Please sign in to comment.