Repository: microsoft/terminal (language: cpp)

Issue:
Unlimited zoom-in using Ctrl+Wheel causes renderer to crash
### Windows Terminal version

Current main

### Windows build number

10.0.19045.4291

### Other Software

_No response_

### Steps to reproduce

- ~~Set Direct3D backend active.~~ All rendering backends are affected.
- Run tab with any profile.
- Zoom-in by Ctrl+Wheel until it crashes.
  - Direct3D: ~ One minute of scrolling for my HW setup.
  - Direct2D: ~10 minutes of "doing nothing" with same result (crash).

### Expected Behavior

Reasonable zoom limits. E.g.: One text cell should not exceed the size of the terminal window.

### Actual Behavior

Direct3D:
![image](https://github.com/microsoft/terminal/assets/11535558/9eb06214-ec23-4bd6-a924-21995f8771b7)

Direct2D:
![image](https://github.com/microsoft/terminal/assets/11535558/ec4c1308-6fad-4dac-9b6f-8840b9355245)


Discussion / hints:
Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!


### Closed similar issues:
- [Ctrl-mousewheel causes crash (#5687)](https://github.com/microsoft/terminal/issues/5687),  similarity score: 0.80
- [Crashes if zoom font size up and down rapidly with ctrl + scroll wheel (#5479)](https://github.com/microsoft/terminal/issues/5479),  similarity score: 0.77

> Note: You can give me feedback by thumbs upping or thumbs downing this comment.
> Direct2D: ~10 minutes of "doing nothing" with same result (crash).

What do you mean with "doing nothing"?
> What do you mean with "doing nothing"?

I mean I scrolled (Ctrl+Wheel) intensively with the mouse for 10 minutes and did nothing else 😅

Fix patch:
diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp
index 2fbaf7cd52a..15535ad89f0 100644
--- a/src/renderer/atlas/AtlasEngine.api.cpp
+++ b/src/renderer/atlas/AtlasEngine.api.cpp
@@ -636,7 +636,7 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo
     const auto& faceName = fontInfoDesired.GetFaceName();
     const auto requestedFamily = fontInfoDesired.GetFamily();
     auto requestedWeight = fontInfoDesired.GetWeight();
-    auto fontSize = fontInfoDesired.GetFontSize();
+    auto fontSize = std::clamp(fontInfoDesired.GetFontSize(), 1.0f, 100.0f);
     auto requestedSize = fontInfoDesired.GetEngineSize();
 
     if (!requestedSize.height)
diff --git a/src/renderer/atlas/BackendD2D.cpp b/src/renderer/atlas/BackendD2D.cpp
index d0909de361f..5a663b49d04 100644
--- a/src/renderer/atlas/BackendD2D.cpp
+++ b/src/renderer/atlas/BackendD2D.cpp
@@ -39,18 +39,28 @@ void BackendD2D::Render(RenderingPayload& p)
     }
 
     _renderTarget->BeginDraw();
+    try
+    {
 #if ATLAS_DEBUG_SHOW_DIRTY || ATLAS_DEBUG_DUMP_RENDER_TARGET
-    // Invalidating the render target helps with spotting Present1() bugs.
-    _renderTarget->Clear();
+        // Invalidating the render target helps with spotting Present1() bugs.
+        _renderTarget->Clear();
 #endif
-    _drawBackground(p);
-    _drawCursorPart1(p);
-    _drawText(p);
-    _drawCursorPart2(p);
-    _drawSelection(p);
+        _drawBackground(p);
+        _drawCursorPart1(p);
+        _drawText(p);
+        _drawCursorPart2(p);
+        _drawSelection(p);
 #if ATLAS_DEBUG_SHOW_DIRTY
-    _debugShowDirty(p);
+        _debugShowDirty(p);
 #endif
+    }
+    catch (...)
+    {
+        // In case an exception is thrown for some reason between BeginDraw() and EndDraw()
+        // we still technically need to call EndDraw() before releasing any resources.
+        LOG_IF_FAILED(_renderTarget->EndDraw());
+        throw;
+    }
     THROW_IF_FAILED(_renderTarget->EndDraw());
 
 #if ATLAS_DEBUG_DUMP_RENDER_TARGET
diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp
index e5b3d061f54..fe07271c16a 100644
--- a/src/renderer/atlas/BackendD3D.cpp
+++ b/src/renderer/atlas/BackendD3D.cpp
@@ -184,6 +184,18 @@ BackendD3D::BackendD3D(const RenderingPayload& p)
 #endif
 }
 
+#pragma warning(suppress : 26432) // If you define or delete any default operation in the type '...', define or delete them all (c.21).
+BackendD3D::~BackendD3D()
+{
+    // In case an exception is thrown for some reason between BeginDraw() and EndDraw()
+    // we still technically need to call EndDraw() before releasing any resources.
+    if (_d2dBeganDrawing)
+    {
+#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function '...' which may throw exceptions (f.6).
+        LOG_IF_FAILED(_d2dRenderTarget->EndDraw());
+    }
+}
+
 void BackendD3D::ReleaseResources() noexcept
 {
     _renderTargetView.reset();
diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h
index ae9803d51f8..218df1f0179 100644
--- a/src/renderer/atlas/BackendD3D.h
+++ b/src/renderer/atlas/BackendD3D.h
@@ -13,6 +13,7 @@ namespace Microsoft::Console::Render::Atlas
     struct BackendD3D : IBackend
     {
         BackendD3D(const RenderingPayload& p);
+        ~BackendD3D() override;
 
         void ReleaseResources() noexcept override;
         void Render(RenderingPayload& payload) override;
diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h
index 120b3d86662..53b093e8a49 100644
--- a/src/renderer/atlas/common.h
+++ b/src/renderer/atlas/common.h
@@ -404,9 +404,9 @@ namespace Microsoft::Console::Render::Atlas
         til::generational<CursorSettings> cursor;
         til::generational<MiscellaneousSettings> misc;
         // Size of the viewport / swap chain in pixel.
-        u16x2 targetSize{ 1, 1 };
+        u16x2 targetSize{ 0, 0 };
         // Size of the portion of the text buffer that we're drawing on the screen.
-        u16x2 viewportCellCount{ 1, 1 };
+        u16x2 viewportCellCount{ 0, 0 };
         // The position of the viewport inside the text buffer (in cells).
         u16x2 viewportOffset{ 0, 0 };
     };


Test patch: