AtlasEngine: Fix Present() of out of bounds glyphs (#15403)
`til::rect`'s truthiness check (= rect is valid) returns `false` for any rects that have negative coordinates. This makes sense for buffer handling, but breaks AtlasEngine, where glyph coordinates can go out of bounds and it's entirely valid for that to happen. Closes #15416 ## Validation Steps Performed * Use MesloLGM NF and print NF glyphs in the first row * Text rendering, selection, etc., still works ✅ --------- Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
This commit is contained in:
parent
c589784b54
commit
a9f34e3095
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue