Skip to content

Commit

Permalink
Fix dirty area calculation, Add ATLAS_DEBUG_SHOW_DIRTY
Browse files Browse the repository at this point in the history
  • Loading branch information
lhecker committed Mar 20, 2023
1 parent d44974a commit 694daa7
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 114 deletions.
4 changes: 2 additions & 2 deletions src/host/CursorBlinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ void CursorBlinker::SettingsChanged() noexcept
{
KillCaretTimer();
_uCaretBlinkTime = dwCaretBlinkTime;
SetCaretTimer();
//SetCaretTimer();
}
}

Expand All @@ -66,7 +66,7 @@ void CursorBlinker::FocusEnd() const noexcept

void CursorBlinker::FocusStart() const noexcept
{
SetCaretTimer();
//SetCaretTimer();

This comment has been minimized.

Copy link
@DHowett

DHowett Mar 20, 2023

Member

nit: commented

This comment has been minimized.

Copy link
@lhecker

lhecker Mar 20, 2023

Author Member

Yep this is in progress, because I'm actively debugging what's wrong with my dirty rects. If you just knew how weird this bug is...

}

// Routine Description:
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/atlas/AtlasEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ try
_handleSettingsUpdate();
}

if constexpr (debugDisablePartialInvalidation)
if constexpr (ATLAS_DEBUG_DISABLE_PARTIAL_INVALIDATION)
{
_api.invalidatedRows = invalidatedRowsAll;
_api.scrollOffset = 0;
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/atlas/AtlasEngine.r.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ CATCH_RETURN()

[[nodiscard]] bool AtlasEngine::RequiresContinuousRedraw() noexcept
{
return debugContinuousRedraw || (_b && _b->RequiresContinuousRedraw());
return ATLAS_DEBUG_CONTINUOUS_REDRAW || (_b && _b->RequiresContinuousRedraw());
}

void AtlasEngine::WaitUntilCanRender() noexcept
Expand Down Expand Up @@ -123,7 +123,7 @@ void AtlasEngine::_recreateBackend()
// IID_PPV_ARGS doesn't work here for some reason.
THROW_IF_FAILED(CreateDXGIFactory2(flags, __uuidof(_p.dxgiFactory), _p.dxgiFactory.put_void()));

auto d2dMode = debugForceD2DMode;
auto d2dMode = ATLAS_DEBUG_FORCE_D2D_MODE;
auto deviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED
#ifndef NDEBUG
//| D3D11_CREATE_DEVICE_DEBUG
Expand Down
94 changes: 21 additions & 73 deletions src/renderer/atlas/Backend.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "Backend.h"

Expand Down Expand Up @@ -36,24 +39,27 @@ void SwapChainManager::Present(const RenderingPayload& p)
dirtyRect.right = std::min(dirtyRect.right, til::CoordType{ _targetSize.x });
dirtyRect.bottom = std::min(dirtyRect.bottom, til::CoordType{ _targetSize.y });

if (dirtyRect != fullRect)
if constexpr (!ATLAS_DEBUG_SHOW_DIRTY)
{
params.DirtyRectsCount = 1;
params.pDirtyRects = dirtyRect.as_win32_rect();

if (p.scrollOffset)
if (dirtyRect != fullRect)
{
const auto offsetInPx = p.scrollOffset * p.s->font->cellSize.y;
const auto width = p.s->targetSize.x;
const auto height = p.s->cellCount.y * p.s->font->cellSize.y;
const auto top = std::max(0, offsetInPx);
const auto bottom = height + std::min(0, offsetInPx);
params.DirtyRectsCount = 1;
params.pDirtyRects = dirtyRect.as_win32_rect();

if (p.scrollOffset)
{
const auto offsetInPx = p.scrollOffset * p.s->font->cellSize.y;
const auto width = p.s->targetSize.x;
const auto height = p.s->cellCount.y * p.s->font->cellSize.y;
const auto top = std::max(0, offsetInPx);
const auto bottom = height + std::min(0, offsetInPx);

scrollRect = { 0, top, width, bottom };
scrollOffset = { 0, offsetInPx };
scrollRect = { 0, top, width, bottom };
scrollOffset = { 0, offsetInPx };

params.pScrollRect = &scrollRect;
params.pScrollOffset = &scrollOffset;
params.pScrollRect = &scrollRect;
params.pScrollOffset = &scrollOffset;
}
}
}

Expand All @@ -66,7 +72,7 @@ void SwapChainManager::WaitUntilCanRender() noexcept
// IDXGISwapChain2::GetFrameLatencyWaitableObject returns an auto-reset event.
// Once we've waited on the event, waiting on it again will block until the timeout elapses.
// _waitForPresentation guards against this.
if constexpr (!debugDisableFrameLatencyWaitableObject)
if constexpr (!ATLAS_DEBUG_DISABLE_FRAME_LATENCY_WAITABLE_OBJECT)
{
if (_waitForPresentation)
{
Expand Down Expand Up @@ -155,64 +161,6 @@ void SwapChainManager::_updateMatrixTransform(const RenderingPayload& p) const
}
}

// Returns the theoretical/design design size of the given `DWRITE_GLYPH_RUN`, relative the the given baseline origin.
f32r Microsoft::Console::Render::Atlas::GetGlyphRunBlackBox(const DWRITE_GLYPH_RUN& glyphRun, f32 baselineX, f32 baselineY)
{
DWRITE_FONT_METRICS fontMetrics;
glyphRun.fontFace->GetMetrics(&fontMetrics);

std::unique_ptr<DWRITE_GLYPH_METRICS[]> glyphRunMetricsHeap;
std::array<DWRITE_GLYPH_METRICS, 8> glyphRunMetricsStack;
DWRITE_GLYPH_METRICS* glyphRunMetrics = glyphRunMetricsStack.data();

if (glyphRun.glyphCount > glyphRunMetricsStack.size())
{
glyphRunMetricsHeap = std::make_unique_for_overwrite<DWRITE_GLYPH_METRICS[]>(glyphRun.glyphCount);
glyphRunMetrics = glyphRunMetricsHeap.get();
}

glyphRun.fontFace->GetDesignGlyphMetrics(glyphRun.glyphIndices, glyphRun.glyphCount, glyphRunMetrics, false);

f32 const fontScale = glyphRun.fontEmSize / fontMetrics.designUnitsPerEm;
f32r accumulatedBounds{
FLT_MAX,
FLT_MAX,
FLT_MIN,
FLT_MIN,
};

for (uint32_t i = 0; i < glyphRun.glyphCount; ++i)
{
const auto& glyphMetrics = glyphRunMetrics[i];
const auto glyphAdvance = glyphRun.glyphAdvances ? glyphRun.glyphAdvances[i] : glyphMetrics.advanceWidth * fontScale;

const auto left = static_cast<f32>(glyphMetrics.leftSideBearing) * fontScale;
const auto top = static_cast<f32>(glyphMetrics.topSideBearing - glyphMetrics.verticalOriginY) * fontScale;
const auto right = static_cast<f32>(gsl::narrow_cast<INT32>(glyphMetrics.advanceWidth) - glyphMetrics.rightSideBearing) * fontScale;
const auto bottom = static_cast<f32>(gsl::narrow_cast<INT32>(glyphMetrics.advanceHeight) - glyphMetrics.bottomSideBearing - glyphMetrics.verticalOriginY) * fontScale;

if (left < right && top < bottom)
{
auto glyphX = baselineX;
auto glyphY = baselineY;
if (glyphRun.glyphOffsets)
{
glyphX += glyphRun.glyphOffsets[i].advanceOffset;
glyphY -= glyphRun.glyphOffsets[i].ascenderOffset;
}

accumulatedBounds.left = std::min(accumulatedBounds.left, left + glyphX);
accumulatedBounds.top = std::min(accumulatedBounds.top, top + glyphY);
accumulatedBounds.right = std::max(accumulatedBounds.right, right + glyphX);
accumulatedBounds.bottom = std::max(accumulatedBounds.bottom, bottom + glyphY);
}

baselineX += glyphAdvance;
}

return accumulatedBounds;
}

// Draws a `DWRITE_GLYPH_RUN` at `baselineOrigin` into the given `ID2D1DeviceContext`.
// `d2dRenderTarget4` and `dwriteFactory4` are optional and used to draw colored glyphs.
// Returns true if the `DWRITE_GLYPH_RUN` contained a color glyph.
Expand Down
14 changes: 9 additions & 5 deletions src/renderer/atlas/Backend.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

#include "common.h"

namespace Microsoft::Console::Render::Atlas
{
inline constexpr bool debugContinuousRedraw = false;
inline constexpr bool debugDisableFrameLatencyWaitableObject = false;
inline constexpr bool debugDisablePartialInvalidation = false;
inline constexpr bool debugForceD2DMode = false;
#define ATLAS_DEBUG_CONTINUOUS_REDRAW 0

This comment has been minimized.

Copy link
@DHowett

DHowett Mar 20, 2023

Member

using defines so that you can use the preprocessor?

#define ATLAS_DEBUG_DISABLE_FRAME_LATENCY_WAITABLE_OBJECT 0
#define ATLAS_DEBUG_DISABLE_PARTIAL_INVALIDATION 0
#define ATLAS_DEBUG_FORCE_D2D_MODE 0
#define ATLAS_DEBUG_SHOW_DIRTY 0

struct SwapChainManager
{
Expand Down Expand Up @@ -39,7 +43,7 @@ namespace Microsoft::Console::Render::Atlas
void _createSwapChain(const RenderingPayload& p, IUnknown* device);
void _updateMatrixTransform(const RenderingPayload& p) const;

static constexpr DXGI_SWAP_CHAIN_FLAG flags = debugDisableFrameLatencyWaitableObject ? DXGI_SWAP_CHAIN_FLAG{} : DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
static constexpr DXGI_SWAP_CHAIN_FLAG flags = ATLAS_DEBUG_DISABLE_FRAME_LATENCY_WAITABLE_OBJECT ? DXGI_SWAP_CHAIN_FLAG{} : DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;

wil::com_ptr<IDXGISwapChain2> _swapChain;
wil::unique_handle _swapChainHandle;
Expand Down
73 changes: 68 additions & 5 deletions src/renderer/atlas/BackendD2D.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "BackendD2D.h"

Expand Down Expand Up @@ -193,20 +196,20 @@ void BackendD2D::_drawText(RenderingPayload& p)

DrawGlyphRun(_renderTarget.get(), _renderTarget4.get(), p.dwriteFactory4.get(), { baselineX, baselineY }, &glyphRun, brush);

const auto blackBox = GetGlyphRunBlackBox(glyphRun, baselineX, baselineY);
const auto blackBox = _getGlyphRunBlackBox(glyphRun, baselineX, baselineY);
// Add a 1px padding to avoid inaccuracies with the blackbox measurement.
// It's only an estimate based on the design size after all.
row->top = std::min(row->top, static_cast<i32>(lround(blackBox.top - 1.5f)));
row->bottom = std::max(row->bottom, static_cast<i32>(lround(blackBox.bottom + 1.5f)));
row->top = std::min(row->top, static_cast<i32>(lround(blackBox.top) - 1));
row->bottom = std::max(row->bottom, static_cast<i32>(lround(blackBox.bottom) + 1));

for (UINT32 i = 0; i < glyphRun.glyphCount; ++i)
{
baselineX += glyphRun.glyphAdvances[i];
}
} while (it != end);
}

if (row->top < p.dirtyRectInPx.bottom && p.dirtyRectInPx.top < row->bottom)
if (y >= p.invalidatedRows.x && y < p.invalidatedRows.y)
{
dirtyTop = std::min(dirtyTop, row->top);
dirtyBottom = std::max(dirtyBottom, row->bottom);
Expand All @@ -222,6 +225,66 @@ void BackendD2D::_drawText(RenderingPayload& p)
}
}

// Returns the theoretical/design design size of the given `DWRITE_GLYPH_RUN`, relative the the given baseline origin.
// This algorithm replicates what DirectWrite does internally to provide `IDWriteTextLayout::GetMetrics`.
f32r BackendD2D::_getGlyphRunBlackBox(const DWRITE_GLYPH_RUN& glyphRun, f32 baselineX, f32 baselineY)
{
DWRITE_FONT_METRICS fontMetrics;
glyphRun.fontFace->GetMetrics(&fontMetrics);

if (glyphRun.glyphCount > _glyphMetrics.size())
{
// Growth factor 1.5x.
auto size = _glyphMetrics.size();
size = size + (size >> 1);
size = std::max<size_t>(size, glyphRun.glyphCount);
// Overflow check.
Expects(size > _glyphMetrics.size());
_glyphMetrics = Buffer<DWRITE_GLYPH_METRICS>{ size };
}

glyphRun.fontFace->GetDesignGlyphMetrics(glyphRun.glyphIndices, glyphRun.glyphCount, _glyphMetrics.data(), false);

const f32 fontScale = glyphRun.fontEmSize / fontMetrics.designUnitsPerEm;
f32r accumulatedBounds{
FLT_MAX,
FLT_MAX,
FLT_MIN,
FLT_MIN,
};

for (uint32_t i = 0; i < glyphRun.glyphCount; ++i)
{
const auto& glyphMetrics = _glyphMetrics[i];
const auto glyphAdvance = glyphRun.glyphAdvances ? glyphRun.glyphAdvances[i] : glyphMetrics.advanceWidth * fontScale;

const auto left = static_cast<f32>(glyphMetrics.leftSideBearing) * fontScale;
const auto top = static_cast<f32>(glyphMetrics.topSideBearing - glyphMetrics.verticalOriginY) * fontScale;
const auto right = static_cast<f32>(gsl::narrow_cast<INT32>(glyphMetrics.advanceWidth) - glyphMetrics.rightSideBearing) * fontScale;
const auto bottom = static_cast<f32>(gsl::narrow_cast<INT32>(glyphMetrics.advanceHeight) - glyphMetrics.bottomSideBearing - glyphMetrics.verticalOriginY) * fontScale;

if (left < right && top < bottom)
{
auto glyphX = baselineX;
auto glyphY = baselineY;
if (glyphRun.glyphOffsets)
{
glyphX += glyphRun.glyphOffsets[i].advanceOffset;
glyphY -= glyphRun.glyphOffsets[i].ascenderOffset;
}

accumulatedBounds.left = std::min(accumulatedBounds.left, left + glyphX);
accumulatedBounds.top = std::min(accumulatedBounds.top, top + glyphY);
accumulatedBounds.right = std::max(accumulatedBounds.right, right + glyphX);
accumulatedBounds.bottom = std::max(accumulatedBounds.bottom, bottom + glyphY);
}

baselineX += glyphAdvance;
}

return accumulatedBounds;
}

void BackendD2D::_drawGridlines(const RenderingPayload& p)
{
u16 y = 0;
Expand Down
5 changes: 5 additions & 0 deletions src/renderer/atlas/BackendD2D.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

#include "Backend.h"
Expand All @@ -16,6 +19,7 @@ namespace Microsoft::Console::Render::Atlas
__declspec(noinline) void _handleSettingsUpdate(const RenderingPayload& p);
void _drawBackground(const RenderingPayload& p) noexcept;
void _drawText(RenderingPayload& p);
f32r _getGlyphRunBlackBox(const DWRITE_GLYPH_RUN& glyphRun, f32 baselineX, f32 baselineY);
void _drawGridlines(const RenderingPayload& p);
void _drawGridlineRow(const RenderingPayload& p, const ShapedRow* row, u16 y);
void _drawCursor(const RenderingPayload& p);
Expand All @@ -36,6 +40,7 @@ namespace Microsoft::Console::Render::Atlas
wil::com_ptr<ID2D1BitmapBrush> _backgroundBrush;
til::generation_t _backgroundBitmapGeneration;

Buffer<DWRITE_GLYPH_METRICS> _glyphMetrics;
u32 _brushColor = 0;

til::generation_t _generation;
Expand Down
Loading

0 comments on commit 694daa7

Please sign in to comment.