Repository: microsoft/terminal (language: cpp)

Issue:
[1.18] Certain fonts will cause a renderer error (MesloLGM NF, Cascadia Code Nerd Font)
### Windows Terminal version

1.18.1421.0

### Windows build number

10.0.19045.2965

### Other Software

oh-my-posh v16.8.0
Ubuntu 22.04.02 LTS

### Steps to reproduce

- Make sure oh-my-posh is started automatically on startup
- Open a new tab
- Notice the render problems
- Press enters, type text
- Notice even more render problems
- Wait a while and see the renderer catch up

It seems impossible to create a screenshot with the snipping tool because when the focus on WT is lost, the renderer catches up immediately.

Tearing of a tab created an error the very first time, but couldn't reproduce it anymore.

### Expected Behavior

_No response_

### Actual Behavior

Various rendering issues


Discussion / hints:

Fix patch:
diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp
index 1e848884955..fb85d8e3e0b 100644
--- a/src/renderer/atlas/AtlasEngine.r.cpp
+++ b/src/renderer/atlas/AtlasEngine.r.cpp
@@ -415,31 +415,32 @@ void AtlasEngine::_waitUntilCanRender() noexcept
 
 void AtlasEngine::_present()
 {
-    // Present1() dislikes being called with an empty dirty rect.
-    if (!_p.dirtyRectInPx)
-    {
-        return;
-    }
-
-    const til::rect fullRect{ 0, 0, _p.swapChain.targetSize.x, _p.swapChain.targetSize.y };
+    const RECT fullRect{ 0, 0, _p.swapChain.targetSize.x, _p.swapChain.targetSize.y };
 
     DXGI_PRESENT_PARAMETERS params{};
     RECT scrollRect{};
     POINT scrollOffset{};
 
     // Since rows might be taller than their cells, they might have drawn outside of the viewport.
-    auto dirtyRect = _p.dirtyRectInPx;
-    dirtyRect.left = std::max(dirtyRect.left, 0);
-    dirtyRect.top = std::max(dirtyRect.top, 0);
-    dirtyRect.right = std::min(dirtyRect.right, fullRect.right);
-    dirtyRect.bottom = std::min(dirtyRect.bottom, fullRect.bottom);
+    RECT dirtyRect{
+        .left = std::max(_p.dirtyRectInPx.left, 0),
+        .top = std::max(_p.dirtyRectInPx.top, 0),
+        .right = std::min<LONG>(_p.dirtyRectInPx.right, fullRect.right),
+        .bottom = std::min<LONG>(_p.dirtyRectInPx.bottom, fullRect.bottom),
+    };
+
+    // Present1() dislikes being called with an empty dirty rect.
+    if (dirtyRect.left >= dirtyRect.right || dirtyRect.top >= dirtyRect.bottom)
+    {
+        return;
+    }
 
     if constexpr (!ATLAS_DEBUG_SHOW_DIRTY)
     {
-        if (dirtyRect != fullRect)
+        if (memcmp(&dirtyRect, &fullRect, sizeof(RECT)) != 0)
         {
             params.DirtyRectsCount = 1;
-            params.pDirtyRects = dirtyRect.as_win32_rect();
+            params.pDirtyRects = &dirtyRect;
 
             if (_p.scrollOffset)
             {
diff --git a/src/renderer/atlas/BackendD2D.cpp b/src/renderer/atlas/BackendD2D.cpp
index f9e14275117..50e6e90d9b4 100644
--- a/src/renderer/atlas/BackendD2D.cpp
+++ b/src/renderer/atlas/BackendD2D.cpp
@@ -627,7 +627,8 @@ void BackendD2D::_debugShowDirty(const RenderingPayload& p)
 
     for (size_t i = 0; i < std::size(_presentRects); ++i)
     {
-        if (const auto& rect = _presentRects[i])
+        const auto& rect = _presentRects[(_presentRectsPos + i) % std::size(_presentRects)];
+        if (rect.non_empty())
         {
             const D2D1_RECT_F rectF{
                 static_cast<f32>(rect.left),
diff --git a/src/renderer/atlas/BackendD2D.h b/src/renderer/atlas/BackendD2D.h
index a68dcd52619..1f70c14c75e 100644
--- a/src/renderer/atlas/BackendD2D.h
+++ b/src/renderer/atlas/BackendD2D.h
@@ -56,7 +56,7 @@ namespace Microsoft::Console::Render::Atlas
         u16x2 _cellCount{};
 
 #if ATLAS_DEBUG_SHOW_DIRTY
-        til::rect _presentRects[9]{};
+        i32r _presentRects[9]{};
         size_t _presentRectsPos = 0;
 #endif
 
diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp
index 6e26a2cbdad..9063573fcfd 100644
--- a/src/renderer/atlas/BackendD3D.cpp
+++ b/src/renderer/atlas/BackendD3D.cpp
@@ -2042,10 +2042,11 @@ void BackendD3D::_debugShowDirty(const RenderingPayload& p)
 
     for (size_t i = 0; i < std::size(_presentRects); ++i)
     {
-        if (const auto& rect = _presentRects[i])
+        const auto& rect = _presentRects[(_presentRectsPos + i) % std::size(_presentRects)];
+        if (rect.non_empty())
         {
             _appendQuad() = {
-                .shadingType = ShadingType::SolidFill,
+                .shadingType = ShadingType::Selection,
                 .position = {
                     static_cast<i16>(rect.left),
                     static_cast<i16>(rect.top),
diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h
index 020ecd575cc..d9f9a13c172 100644
--- a/src/renderer/atlas/BackendD3D.h
+++ b/src/renderer/atlas/BackendD3D.h
@@ -293,7 +293,7 @@ namespace Microsoft::Console::Render::Atlas
         bool _requiresContinuousRedraw = false;
 
 #if ATLAS_DEBUG_SHOW_DIRTY
-        til::rect _presentRects[9]{};
+        i32r _presentRects[9]{};
         size_t _presentRectsPos = 0;
 #endif
 
diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h
index 9f53c398b5f..c0b69b808ec 100644
--- a/src/renderer/atlas/common.h
+++ b/src/renderer/atlas/common.h
@@ -531,8 +531,12 @@ namespace Microsoft::Console::Render::Atlas
         std::array<til::generation_t, 2> colorBitmapGenerations{ 1, 1 };
         // In columns/rows.
         til::rect cursorRect;
-        // In pixel.
-        til::rect dirtyRectInPx;
+        // The viewport/SwapChain area to be presented. In pixel.
+        // NOTE:
+        //   This cannot use til::rect, because til::rect generally expects positive coordinates only
+        //   (`operator!()` checks for negative values), whereas this one can go out of bounds,
+        //   whenever glyphs go out of bounds. `AtlasEngine::_present()` will clamp it.
+        i32r dirtyRectInPx{};
         // In rows.
         range<u16> invalidatedRows{};
         // In pixel.


Test patch: