mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-19 18:11:39 -05:00
Fix horizontal scrolling bugs in AtlasEngine/DxEngine (#15707)
This commit fixes a number of issues around horizontal scrolling. DxEngine only had one bug, where the clip rect would cause any content outside of the actual viewport to be invisible. AtlasEngine had more bugs, mostly around the conversion from textbuffer-relative coordinates to viewport-relative coordinates, since AtlasEngine stores things like the cursor position, attributes, etc., relative to the viewport. It also renames `cellCount` to `viewportCellCount`, because I realized that it might have to deal with a `textBufferCellCount` or similar in the future. I hope that the new name is more descriptive of what it refers to. Future improvements to AtlasEngine in particular would be to not copy the entire `Settings` struct every time the horizontal scroll offset changes, and to trim trailing whitespace before shaping text. This is in preparation for #1860 ## Validation Steps Performed * Patch `RenderingTests` to run in the main (and not alt) buffer * Horizontal scrolling of line renditions and attributes works ✅ * Selection retains its position (mostly) ✅
This commit is contained in:
@@ -119,7 +119,7 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2<T>& out) noexcept
|
|||||||
if (delta < 0)
|
if (delta < 0)
|
||||||
{
|
{
|
||||||
_api.invalidatedRows.start = gsl::narrow_cast<u16>(clamp<int>(_api.invalidatedRows.start + delta, u16min, u16max));
|
_api.invalidatedRows.start = gsl::narrow_cast<u16>(clamp<int>(_api.invalidatedRows.start + delta, u16min, u16max));
|
||||||
_api.invalidatedRows.end = _api.s->cellCount.y;
|
_api.invalidatedRows.end = _api.s->viewportCellCount.y;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -182,17 +182,29 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2<T>& out) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const til::inclusive_rect& srNewViewport) noexcept
|
[[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const til::inclusive_rect& srNewViewport) noexcept
|
||||||
|
try
|
||||||
{
|
{
|
||||||
const u16x2 cellCount{
|
const u16x2 viewportCellCount{
|
||||||
gsl::narrow_cast<u16>(std::max(1, srNewViewport.right - srNewViewport.left + 1)),
|
gsl::narrow<u16>(std::max(1, srNewViewport.right - srNewViewport.left + 1)),
|
||||||
gsl::narrow_cast<u16>(std::max(1, srNewViewport.bottom - srNewViewport.top + 1)),
|
gsl::narrow<u16>(std::max(1, srNewViewport.bottom - srNewViewport.top + 1)),
|
||||||
};
|
};
|
||||||
if (_api.s->cellCount != cellCount)
|
const u16x2 viewportOffset{
|
||||||
|
gsl::narrow<u16>(srNewViewport.left),
|
||||||
|
gsl::narrow<u16>(srNewViewport.top),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_api.s->viewportCellCount != viewportCellCount)
|
||||||
{
|
{
|
||||||
_api.s.write()->cellCount = cellCount;
|
_api.s.write()->viewportCellCount = viewportCellCount;
|
||||||
}
|
}
|
||||||
|
if (_api.s->viewportOffset != viewportOffset)
|
||||||
|
{
|
||||||
|
_api.s.write()->viewportOffset = viewportOffset;
|
||||||
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
CATCH_RETURN()
|
||||||
|
|
||||||
[[nodiscard]] HRESULT AtlasEngine::GetProposedFont(const FontInfoDesired& fontInfoDesired, _Out_ FontInfo& fontInfo, const int dpi) noexcept
|
[[nodiscard]] HRESULT AtlasEngine::GetProposedFont(const FontInfoDesired& fontInfoDesired, _Out_ FontInfo& fontInfo, const int dpi) noexcept
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -78,17 +78,17 @@ try
|
|||||||
|
|
||||||
// Clamp invalidation rects into valid value ranges.
|
// Clamp invalidation rects into valid value ranges.
|
||||||
{
|
{
|
||||||
_api.invalidatedCursorArea.left = std::min(_api.invalidatedCursorArea.left, _p.s->cellCount.x);
|
_api.invalidatedCursorArea.left = std::min(_api.invalidatedCursorArea.left, _p.s->viewportCellCount.x);
|
||||||
_api.invalidatedCursorArea.top = std::min(_api.invalidatedCursorArea.top, _p.s->cellCount.y);
|
_api.invalidatedCursorArea.top = std::min(_api.invalidatedCursorArea.top, _p.s->viewportCellCount.y);
|
||||||
_api.invalidatedCursorArea.right = clamp(_api.invalidatedCursorArea.right, _api.invalidatedCursorArea.left, _p.s->cellCount.x);
|
_api.invalidatedCursorArea.right = clamp(_api.invalidatedCursorArea.right, _api.invalidatedCursorArea.left, _p.s->viewportCellCount.x);
|
||||||
_api.invalidatedCursorArea.bottom = clamp(_api.invalidatedCursorArea.bottom, _api.invalidatedCursorArea.top, _p.s->cellCount.y);
|
_api.invalidatedCursorArea.bottom = clamp(_api.invalidatedCursorArea.bottom, _api.invalidatedCursorArea.top, _p.s->viewportCellCount.y);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
_api.invalidatedRows.start = std::min(_api.invalidatedRows.start, _p.s->cellCount.y);
|
_api.invalidatedRows.start = std::min(_api.invalidatedRows.start, _p.s->viewportCellCount.y);
|
||||||
_api.invalidatedRows.end = clamp(_api.invalidatedRows.end, _api.invalidatedRows.start, _p.s->cellCount.y);
|
_api.invalidatedRows.end = clamp(_api.invalidatedRows.end, _api.invalidatedRows.start, _p.s->viewportCellCount.y);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto limit = gsl::narrow_cast<i16>(_p.s->cellCount.y & 0x7fff);
|
const auto limit = gsl::narrow_cast<i16>(_p.s->viewportCellCount.y & 0x7fff);
|
||||||
const auto offset = gsl::narrow_cast<i16>(clamp<int>(_api.scrollOffset, -limit, limit));
|
const auto offset = gsl::narrow_cast<i16>(clamp<int>(_api.scrollOffset, -limit, limit));
|
||||||
const auto nothingInvalid = _api.invalidatedRows.start == _api.invalidatedRows.end;
|
const auto nothingInvalid = _api.invalidatedRows.start == _api.invalidatedRows.end;
|
||||||
|
|
||||||
@@ -97,9 +97,9 @@ try
|
|||||||
// Mark the newly scrolled in rows as invalidated
|
// Mark the newly scrolled in rows as invalidated
|
||||||
if (offset < 0)
|
if (offset < 0)
|
||||||
{
|
{
|
||||||
const u16 begRow = _p.s->cellCount.y + offset;
|
const u16 begRow = _p.s->viewportCellCount.y + offset;
|
||||||
_api.invalidatedRows.start = nothingInvalid ? begRow : std::min(_api.invalidatedRows.start, begRow);
|
_api.invalidatedRows.start = nothingInvalid ? begRow : std::min(_api.invalidatedRows.start, begRow);
|
||||||
_api.invalidatedRows.end = _p.s->cellCount.y;
|
_api.invalidatedRows.end = _p.s->viewportCellCount.y;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -112,7 +112,7 @@ try
|
|||||||
_api.dirtyRect = {
|
_api.dirtyRect = {
|
||||||
0,
|
0,
|
||||||
_api.invalidatedRows.start,
|
_api.invalidatedRows.start,
|
||||||
_p.s->cellCount.x,
|
_p.s->viewportCellCount.x,
|
||||||
_api.invalidatedRows.end,
|
_api.invalidatedRows.end,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ try
|
|||||||
// the contents of the entire swap chain is redundant, but more importantly because the scroll rect
|
// 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
|
// 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.
|
// then the scroll rect is empty, which Present1() will loudly complain about.
|
||||||
if (_p.invalidatedRows == range<u16>{ 0, _p.s->cellCount.y })
|
if (_p.invalidatedRows == range<u16>{ 0, _p.s->viewportCellCount.y })
|
||||||
{
|
{
|
||||||
_p.MarkAllAsDirty();
|
_p.MarkAllAsDirty();
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ CATCH_RETURN()
|
|||||||
|
|
||||||
[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept
|
[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept
|
||||||
{
|
{
|
||||||
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(targetRow, 0, _p.s->cellCount.y));
|
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(targetRow, 0, _p.s->viewportCellCount.y));
|
||||||
_p.rows[y]->lineRendition = lineRendition;
|
_p.rows[y]->lineRendition = lineRendition;
|
||||||
_api.lineRendition = lineRendition;
|
_api.lineRendition = lineRendition;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@@ -296,14 +296,15 @@ CATCH_RETURN()
|
|||||||
[[nodiscard]] HRESULT AtlasEngine::PaintBufferLine(std::span<const Cluster> clusters, til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept
|
[[nodiscard]] HRESULT AtlasEngine::PaintBufferLine(std::span<const Cluster> clusters, til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto y = gsl::narrow_cast<u16>(clamp<int>(coord.y, 0, _p.s->cellCount.y));
|
const auto y = gsl::narrow_cast<u16>(clamp<int>(coord.y, 0, _p.s->viewportCellCount.y));
|
||||||
|
|
||||||
if (_api.lastPaintBufferLineCoord.y != y)
|
if (_api.lastPaintBufferLineCoord.y != y)
|
||||||
{
|
{
|
||||||
_flushBufferLine();
|
_flushBufferLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto x = gsl::narrow_cast<u16>(clamp<int>(coord.x, 0, _p.s->cellCount.x));
|
const auto shift = gsl::narrow_cast<u8>(_api.lineRendition != LineRendition::SingleWidth);
|
||||||
|
const auto x = gsl::narrow_cast<u16>(clamp<int>(coord.x - (_p.s->viewportOffset.x >> shift), 0, _p.s->viewportCellCount.x));
|
||||||
auto columnEnd = x;
|
auto columnEnd = x;
|
||||||
|
|
||||||
// _api.bufferLineColumn contains 1 more item than _api.bufferLine, as it represents the
|
// _api.bufferLineColumn contains 1 more item than _api.bufferLine, as it represents the
|
||||||
@@ -330,7 +331,6 @@ try
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto shift = gsl::narrow_cast<u8>(_api.lineRendition != LineRendition::SingleWidth);
|
|
||||||
const auto row = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y;
|
const auto row = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y;
|
||||||
auto beg = row + (static_cast<size_t>(x) << shift);
|
auto beg = row + (static_cast<size_t>(x) << shift);
|
||||||
auto end = row + (static_cast<size_t>(columnEnd) << shift);
|
auto end = row + (static_cast<size_t>(columnEnd) << shift);
|
||||||
@@ -368,9 +368,10 @@ CATCH_RETURN()
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto shift = gsl::narrow_cast<u8>(_api.lineRendition != LineRendition::SingleWidth);
|
const auto shift = gsl::narrow_cast<u8>(_api.lineRendition != LineRendition::SingleWidth);
|
||||||
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(coordTarget.y, 0, _p.s->cellCount.y));
|
const auto x = std::max(0, coordTarget.x - (_p.s->viewportOffset.x >> shift));
|
||||||
const auto from = gsl::narrow_cast<u16>(clamp<til::CoordType>(coordTarget.x << shift, 0, _p.s->cellCount.x - 1));
|
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(coordTarget.y, 0, _p.s->viewportCellCount.y));
|
||||||
const auto to = gsl::narrow_cast<u16>(clamp<size_t>((coordTarget.x + cchLine) << shift, from, _p.s->cellCount.x));
|
const auto from = gsl::narrow_cast<u16>(clamp<til::CoordType>(x << shift, 0, _p.s->viewportCellCount.x - 1));
|
||||||
|
const auto to = gsl::narrow_cast<u16>(clamp<size_t>((x + cchLine) << shift, from, _p.s->viewportCellCount.x));
|
||||||
const auto fg = gsl::narrow_cast<u32>(color) | 0xff000000;
|
const auto fg = gsl::narrow_cast<u32>(color) | 0xff000000;
|
||||||
_p.rows[y]->gridLineRanges.emplace_back(lines, fg, from, to);
|
_p.rows[y]->gridLineRanges.emplace_back(lines, fg, from, to);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@@ -385,9 +386,9 @@ try
|
|||||||
// As such we got to call _flushBufferLine() here just to be sure.
|
// As such we got to call _flushBufferLine() here just to be sure.
|
||||||
_flushBufferLine();
|
_flushBufferLine();
|
||||||
|
|
||||||
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.top, 0, _p.s->cellCount.y));
|
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.top, 0, _p.s->viewportCellCount.y));
|
||||||
const auto from = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.left, 0, _p.s->cellCount.x - 1));
|
const auto from = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.left, 0, _p.s->viewportCellCount.x - 1));
|
||||||
const auto to = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.right, from, _p.s->cellCount.x));
|
const auto to = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.right, from, _p.s->viewportCellCount.x));
|
||||||
|
|
||||||
auto& row = *_p.rows[y];
|
auto& row = *_p.rows[y];
|
||||||
row.selectionFrom = from;
|
row.selectionFrom = from;
|
||||||
@@ -433,30 +434,30 @@ try
|
|||||||
|
|
||||||
if (options.isOn)
|
if (options.isOn)
|
||||||
{
|
{
|
||||||
const auto point = options.coordCursor;
|
|
||||||
// TODO: options.coordCursor can contain invalid out of bounds coordinates when
|
|
||||||
// the window is being resized and the cursor is on the last line of the viewport.
|
|
||||||
const auto top = clamp(point.y, 0, _p.s->cellCount.y - 1);
|
|
||||||
const auto bottom = top + 1;
|
|
||||||
const auto cursorWidth = 1 + (options.fIsDoubleWidth & (options.cursorType != CursorType::VerticalBar));
|
const auto cursorWidth = 1 + (options.fIsDoubleWidth & (options.cursorType != CursorType::VerticalBar));
|
||||||
|
const auto top = options.coordCursor.y;
|
||||||
|
const auto bottom = top + 1;
|
||||||
|
const auto shift = gsl::narrow_cast<u8>(_p.rows[top]->lineRendition != LineRendition::SingleWidth);
|
||||||
|
auto left = options.coordCursor.x - (_p.s->viewportOffset.x >> shift);
|
||||||
|
auto right = left + cursorWidth;
|
||||||
|
|
||||||
auto left = std::max(point.x, 0);
|
left <<= shift;
|
||||||
auto right = std::max(left + cursorWidth, 0);
|
right <<= shift;
|
||||||
|
|
||||||
if (_p.rows[top]->lineRendition != LineRendition::SingleWidth)
|
_p.cursorRect = {
|
||||||
|
std::max<til::CoordType>(left, 0),
|
||||||
|
std::max<til::CoordType>(top, 0),
|
||||||
|
std::min<til::CoordType>(right, _p.s->viewportCellCount.x),
|
||||||
|
std::min<til::CoordType>(bottom, _p.s->viewportCellCount.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_p.cursorRect)
|
||||||
{
|
{
|
||||||
left <<= 1;
|
_p.dirtyRectInPx.left = std::min(_p.dirtyRectInPx.left, left * _p.s->font->cellSize.x);
|
||||||
right <<= 1;
|
_p.dirtyRectInPx.top = std::min(_p.dirtyRectInPx.top, top * _p.s->font->cellSize.y);
|
||||||
|
_p.dirtyRectInPx.right = std::max(_p.dirtyRectInPx.right, right * _p.s->font->cellSize.x);
|
||||||
|
_p.dirtyRectInPx.bottom = std::max(_p.dirtyRectInPx.bottom, bottom * _p.s->font->cellSize.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
left = std::min(left, _p.s->cellCount.x - cursorWidth);
|
|
||||||
right = std::min(right, i32{ _p.s->cellCount.x });
|
|
||||||
|
|
||||||
_p.cursorRect = { left, top, right, bottom };
|
|
||||||
_p.dirtyRectInPx.left = std::min(_p.dirtyRectInPx.left, left * _p.s->font->cellSize.x);
|
|
||||||
_p.dirtyRectInPx.top = std::min(_p.dirtyRectInPx.top, top * _p.s->font->cellSize.y);
|
|
||||||
_p.dirtyRectInPx.right = std::max(_p.dirtyRectInPx.right, right * _p.s->font->cellSize.x);
|
|
||||||
_p.dirtyRectInPx.bottom = std::max(_p.dirtyRectInPx.bottom, bottom * _p.s->font->cellSize.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@@ -501,7 +502,7 @@ void AtlasEngine::_handleSettingsUpdate()
|
|||||||
{
|
{
|
||||||
const auto targetChanged = _p.s->target != _api.s->target;
|
const auto targetChanged = _p.s->target != _api.s->target;
|
||||||
const auto fontChanged = _p.s->font != _api.s->font;
|
const auto fontChanged = _p.s->font != _api.s->font;
|
||||||
const auto cellCountChanged = _p.s->cellCount != _api.s->cellCount;
|
const auto cellCountChanged = _p.s->viewportCellCount != _api.s->viewportCellCount;
|
||||||
|
|
||||||
_p.s = _api.s;
|
_p.s = _api.s;
|
||||||
|
|
||||||
@@ -573,7 +574,7 @@ void AtlasEngine::_recreateFontDependentResources()
|
|||||||
void AtlasEngine::_recreateCellCountDependentResources()
|
void AtlasEngine::_recreateCellCountDependentResources()
|
||||||
{
|
{
|
||||||
// Let's guess that every cell consists of a surrogate pair.
|
// Let's guess that every cell consists of a surrogate pair.
|
||||||
const auto projectedTextSize = static_cast<size_t>(_p.s->cellCount.x) * 2;
|
const auto projectedTextSize = static_cast<size_t>(_p.s->viewportCellCount.x) * 2;
|
||||||
// IDWriteTextAnalyzer::GetGlyphs says:
|
// IDWriteTextAnalyzer::GetGlyphs says:
|
||||||
// The recommended estimate for the per-glyph output buffers is (3 * textLength / 2 + 16).
|
// The recommended estimate for the per-glyph output buffers is (3 * textLength / 2 + 16).
|
||||||
const auto projectedGlyphSize = 3 * projectedTextSize / 2 + 16;
|
const auto projectedGlyphSize = 3 * projectedTextSize / 2 + 16;
|
||||||
@@ -590,16 +591,16 @@ void AtlasEngine::_recreateCellCountDependentResources()
|
|||||||
_api.glyphAdvances = Buffer<f32>{ projectedGlyphSize };
|
_api.glyphAdvances = Buffer<f32>{ projectedGlyphSize };
|
||||||
_api.glyphOffsets = Buffer<DWRITE_GLYPH_OFFSET>{ projectedGlyphSize };
|
_api.glyphOffsets = Buffer<DWRITE_GLYPH_OFFSET>{ projectedGlyphSize };
|
||||||
|
|
||||||
_p.unorderedRows = Buffer<ShapedRow>(_p.s->cellCount.y);
|
_p.unorderedRows = Buffer<ShapedRow>(_p.s->viewportCellCount.y);
|
||||||
_p.rowsScratch = Buffer<ShapedRow*>(_p.s->cellCount.y);
|
_p.rowsScratch = Buffer<ShapedRow*>(_p.s->viewportCellCount.y);
|
||||||
_p.rows = Buffer<ShapedRow*>(_p.s->cellCount.y);
|
_p.rows = Buffer<ShapedRow*>(_p.s->viewportCellCount.y);
|
||||||
|
|
||||||
// Our render loop heavily relies on memcpy() which is up to between 1.5x (Intel)
|
// Our render loop heavily relies on memcpy() which is up to between 1.5x (Intel)
|
||||||
// and 40x (AMD) faster for allocations with an alignment of 32 or greater.
|
// and 40x (AMD) faster for allocations with an alignment of 32 or greater.
|
||||||
// backgroundBitmapStride is a "count" of u32 and not in bytes,
|
// backgroundBitmapStride is a "count" of u32 and not in bytes,
|
||||||
// so we round up to multiple of 8 because 8 * sizeof(u32) == 32.
|
// so we round up to multiple of 8 because 8 * sizeof(u32) == 32.
|
||||||
_p.colorBitmapRowStride = (static_cast<size_t>(_p.s->cellCount.x) + 7) & ~7;
|
_p.colorBitmapRowStride = (static_cast<size_t>(_p.s->viewportCellCount.x) + 7) & ~7;
|
||||||
_p.colorBitmapDepthStride = _p.colorBitmapRowStride * _p.s->cellCount.y;
|
_p.colorBitmapDepthStride = _p.colorBitmapRowStride * _p.s->viewportCellCount.y;
|
||||||
_p.colorBitmap = Buffer<u32, 32>(_p.colorBitmapDepthStride * 2);
|
_p.colorBitmap = Buffer<u32, 32>(_p.colorBitmapDepthStride * 2);
|
||||||
_p.backgroundBitmap = { _p.colorBitmap.data(), _p.colorBitmapDepthStride };
|
_p.backgroundBitmap = { _p.colorBitmap.data(), _p.colorBitmapDepthStride };
|
||||||
_p.foregroundBitmap = { _p.colorBitmap.data() + _p.colorBitmapDepthStride, _p.colorBitmapDepthStride };
|
_p.foregroundBitmap = { _p.colorBitmap.data() + _p.colorBitmapDepthStride, _p.colorBitmapDepthStride };
|
||||||
|
|||||||
@@ -463,7 +463,10 @@ void AtlasEngine::_present()
|
|||||||
{
|
{
|
||||||
const auto offsetInPx = _p.scrollOffset * _p.s->font->cellSize.y;
|
const auto offsetInPx = _p.scrollOffset * _p.s->font->cellSize.y;
|
||||||
const auto width = _p.s->targetSize.x;
|
const auto width = _p.s->targetSize.x;
|
||||||
const auto height = _p.s->cellCount.y * _p.s->font->cellSize.y;
|
// We don't use targetSize.y here, because "height" refers to the bottom coordinate of the last text row
|
||||||
|
// in the buffer. We then add the "offsetInPx" (which is negative when scrolling text upwards) and thus
|
||||||
|
// end up with a "bottom" value that is the bottom of the last row of text that we haven't invalidated.
|
||||||
|
const auto height = _p.s->viewportCellCount.y * _p.s->font->cellSize.y;
|
||||||
const auto top = std::max(0, offsetInPx);
|
const auto top = std::max(0, offsetInPx);
|
||||||
const auto bottom = height + std::min(0, offsetInPx);
|
const auto bottom = height + std::min(0, offsetInPx);
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void BackendD2D::_handleSettingsUpdate(const RenderingPayload& p)
|
|||||||
const auto renderTargetChanged = !_renderTarget;
|
const auto renderTargetChanged = !_renderTarget;
|
||||||
const auto fontChanged = _fontGeneration != p.s->font.generation();
|
const auto fontChanged = _fontGeneration != p.s->font.generation();
|
||||||
const auto cursorChanged = _cursorGeneration != p.s->cursor.generation();
|
const auto cursorChanged = _cursorGeneration != p.s->cursor.generation();
|
||||||
const auto cellCountChanged = _cellCount != p.s->cellCount;
|
const auto cellCountChanged = _viewportCellCount != p.s->viewportCellCount;
|
||||||
|
|
||||||
if (renderTargetChanged)
|
if (renderTargetChanged)
|
||||||
{
|
{
|
||||||
@@ -120,8 +120,8 @@ void BackendD2D::_handleSettingsUpdate(const RenderingPayload& p)
|
|||||||
.dpiY = static_cast<f32>(p.s->font->dpi),
|
.dpiY = static_cast<f32>(p.s->font->dpi),
|
||||||
};
|
};
|
||||||
const D2D1_SIZE_U size{
|
const D2D1_SIZE_U size{
|
||||||
p.s->cellCount.x,
|
p.s->viewportCellCount.x,
|
||||||
p.s->cellCount.y,
|
p.s->viewportCellCount.y,
|
||||||
};
|
};
|
||||||
const D2D1_MATRIX_3X2_F transform{
|
const D2D1_MATRIX_3X2_F transform{
|
||||||
.m11 = static_cast<f32>(p.s->font->cellSize.x),
|
.m11 = static_cast<f32>(p.s->font->cellSize.x),
|
||||||
@@ -145,7 +145,7 @@ void BackendD2D::_handleSettingsUpdate(const RenderingPayload& p)
|
|||||||
_generation = p.s.generation();
|
_generation = p.s.generation();
|
||||||
_fontGeneration = p.s->font.generation();
|
_fontGeneration = p.s->font.generation();
|
||||||
_cursorGeneration = p.s->cursor.generation();
|
_cursorGeneration = p.s->cursor.generation();
|
||||||
_cellCount = p.s->cellCount;
|
_viewportCellCount = p.s->viewportCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendD2D::_drawBackground(const RenderingPayload& p) noexcept
|
void BackendD2D::_drawBackground(const RenderingPayload& p) noexcept
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace Microsoft::Console::Render::Atlas
|
|||||||
til::generation_t _generation;
|
til::generation_t _generation;
|
||||||
til::generation_t _fontGeneration;
|
til::generation_t _fontGeneration;
|
||||||
til::generation_t _cursorGeneration;
|
til::generation_t _cursorGeneration;
|
||||||
u16x2 _cellCount{};
|
u16x2 _viewportCellCount{};
|
||||||
|
|
||||||
#if ATLAS_DEBUG_SHOW_DIRTY
|
#if ATLAS_DEBUG_SHOW_DIRTY
|
||||||
i32r _presentRects[9]{};
|
i32r _presentRects[9]{};
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ void BackendD3D::_handleSettingsUpdate(const RenderingPayload& p)
|
|||||||
|
|
||||||
const auto fontChanged = _fontGeneration != p.s->font.generation();
|
const auto fontChanged = _fontGeneration != p.s->font.generation();
|
||||||
const auto miscChanged = _miscGeneration != p.s->misc.generation();
|
const auto miscChanged = _miscGeneration != p.s->misc.generation();
|
||||||
const auto cellCountChanged = _cellCount != p.s->cellCount;
|
const auto cellCountChanged = _viewportCellCount != p.s->viewportCellCount;
|
||||||
|
|
||||||
if (fontChanged)
|
if (fontChanged)
|
||||||
{
|
{
|
||||||
@@ -298,7 +298,7 @@ void BackendD3D::_handleSettingsUpdate(const RenderingPayload& p)
|
|||||||
_fontGeneration = p.s->font.generation();
|
_fontGeneration = p.s->font.generation();
|
||||||
_miscGeneration = p.s->misc.generation();
|
_miscGeneration = p.s->misc.generation();
|
||||||
_targetSize = p.s->targetSize;
|
_targetSize = p.s->targetSize;
|
||||||
_cellCount = p.s->cellCount;
|
_viewportCellCount = p.s->viewportCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendD3D::_updateFontDependents(const RenderingPayload& p)
|
void BackendD3D::_updateFontDependents(const RenderingPayload& p)
|
||||||
@@ -509,8 +509,8 @@ void BackendD3D::_recreateBackgroundColorBitmap(const RenderingPayload& p)
|
|||||||
_backgroundBitmapView.reset();
|
_backgroundBitmapView.reset();
|
||||||
|
|
||||||
const D3D11_TEXTURE2D_DESC desc{
|
const D3D11_TEXTURE2D_DESC desc{
|
||||||
.Width = p.s->cellCount.x,
|
.Width = p.s->viewportCellCount.x,
|
||||||
.Height = p.s->cellCount.y,
|
.Height = p.s->viewportCellCount.y,
|
||||||
.MipLevels = 1,
|
.MipLevels = 1,
|
||||||
.ArraySize = 1,
|
.ArraySize = 1,
|
||||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||||
@@ -534,8 +534,8 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const
|
|||||||
{
|
{
|
||||||
PSConstBuffer data{};
|
PSConstBuffer data{};
|
||||||
data.backgroundColor = colorFromU32Premultiply<f32x4>(p.s->misc->backgroundColor);
|
data.backgroundColor = colorFromU32Premultiply<f32x4>(p.s->misc->backgroundColor);
|
||||||
data.cellSize = { static_cast<f32>(p.s->font->cellSize.x), static_cast<f32>(p.s->font->cellSize.y) };
|
data.backgroundCellSize = { static_cast<f32>(p.s->font->cellSize.x), static_cast<f32>(p.s->font->cellSize.y) };
|
||||||
data.cellCount = { static_cast<f32>(p.s->cellCount.x), static_cast<f32>(p.s->cellCount.y) };
|
data.backgroundCellCount = { static_cast<f32>(p.s->viewportCellCount.x), static_cast<f32>(p.s->viewportCellCount.y) };
|
||||||
DWrite_GetGammaRatios(_gamma, data.gammaRatios);
|
DWrite_GetGammaRatios(_gamma, data.gammaRatios);
|
||||||
data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast;
|
data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast;
|
||||||
data.underlineWidth = p.s->font->underline.height;
|
data.underlineWidth = p.s->font->underline.height;
|
||||||
@@ -891,7 +891,7 @@ void BackendD3D::_flushQuads(const RenderingPayload& p)
|
|||||||
void BackendD3D::_recreateInstanceBuffers(const RenderingPayload& p)
|
void BackendD3D::_recreateInstanceBuffers(const RenderingPayload& p)
|
||||||
{
|
{
|
||||||
// We use the viewport size of the terminal as the initial estimate for the amount of instances we'll see.
|
// We use the viewport size of the terminal as the initial estimate for the amount of instances we'll see.
|
||||||
const auto minCapacity = static_cast<size_t>(p.s->cellCount.x) * p.s->cellCount.y;
|
const auto minCapacity = static_cast<size_t>(p.s->viewportCellCount.x) * p.s->viewportCellCount.y;
|
||||||
auto newCapacity = std::max(_instancesCount, minCapacity);
|
auto newCapacity = std::max(_instancesCount, minCapacity);
|
||||||
auto newSize = newCapacity * sizeof(QuadInstance);
|
auto newSize = newCapacity * sizeof(QuadInstance);
|
||||||
// Round up to multiples of 64kB to avoid reallocating too often.
|
// Round up to multiples of 64kB to avoid reallocating too often.
|
||||||
@@ -1090,7 +1090,7 @@ void BackendD3D::_drawTextOverlapSplit(const RenderingPayload& p, u16 y)
|
|||||||
|
|
||||||
i32 columnAdvance = 1;
|
i32 columnAdvance = 1;
|
||||||
i32 columnAdvanceInPx{ p.s->font->cellSize.x };
|
i32 columnAdvanceInPx{ p.s->font->cellSize.x };
|
||||||
i32 cellCount{ p.s->cellCount.x };
|
i32 cellCount{ p.s->viewportCellCount.x };
|
||||||
|
|
||||||
if (p.rows[y]->lineRendition != LineRendition::SingleWidth)
|
if (p.rows[y]->lineRendition != LineRendition::SingleWidth)
|
||||||
{
|
{
|
||||||
@@ -2092,8 +2092,8 @@ void BackendD3D::_executeCustomShader(RenderingPayload& p)
|
|||||||
.time = std::chrono::duration<f32>(std::chrono::steady_clock::now() - _customShaderStartTime).count(),
|
.time = std::chrono::duration<f32>(std::chrono::steady_clock::now() - _customShaderStartTime).count(),
|
||||||
.scale = static_cast<f32>(p.s->font->dpi) / static_cast<f32>(USER_DEFAULT_SCREEN_DPI),
|
.scale = static_cast<f32>(p.s->font->dpi) / static_cast<f32>(USER_DEFAULT_SCREEN_DPI),
|
||||||
.resolution = {
|
.resolution = {
|
||||||
static_cast<f32>(_cellCount.x * p.s->font->cellSize.x),
|
static_cast<f32>(_viewportCellCount.x * p.s->font->cellSize.x),
|
||||||
static_cast<f32>(_cellCount.y * p.s->font->cellSize.y),
|
static_cast<f32>(_viewportCellCount.y * p.s->font->cellSize.y),
|
||||||
},
|
},
|
||||||
.background = colorFromU32Premultiply<f32x4>(p.s->misc->backgroundColor),
|
.background = colorFromU32Premultiply<f32x4>(p.s->misc->backgroundColor),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ namespace Microsoft::Console::Render::Atlas
|
|||||||
struct alignas(16) PSConstBuffer
|
struct alignas(16) PSConstBuffer
|
||||||
{
|
{
|
||||||
alignas(sizeof(f32x4)) f32x4 backgroundColor;
|
alignas(sizeof(f32x4)) f32x4 backgroundColor;
|
||||||
alignas(sizeof(f32x2)) f32x2 cellSize;
|
alignas(sizeof(f32x2)) f32x2 backgroundCellSize;
|
||||||
alignas(sizeof(f32x2)) f32x2 cellCount;
|
alignas(sizeof(f32x2)) f32x2 backgroundCellCount;
|
||||||
alignas(sizeof(f32x4)) f32 gammaRatios[4]{};
|
alignas(sizeof(f32x4)) f32 gammaRatios[4]{};
|
||||||
alignas(sizeof(f32)) f32 enhancedContrast = 0;
|
alignas(sizeof(f32)) f32 enhancedContrast = 0;
|
||||||
alignas(sizeof(f32)) f32 underlineWidth = 0;
|
alignas(sizeof(f32)) f32 underlineWidth = 0;
|
||||||
@@ -281,7 +281,7 @@ namespace Microsoft::Console::Render::Atlas
|
|||||||
til::generation_t _fontGeneration;
|
til::generation_t _fontGeneration;
|
||||||
til::generation_t _miscGeneration;
|
til::generation_t _miscGeneration;
|
||||||
u16x2 _targetSize{};
|
u16x2 _targetSize{};
|
||||||
u16x2 _cellCount{};
|
u16x2 _viewportCellCount{};
|
||||||
ShadingType _textShadingType = ShadingType::Default;
|
ShadingType _textShadingType = ShadingType::Default;
|
||||||
|
|
||||||
// An empty-box cursor spanning a wide glyph that has different
|
// An empty-box cursor spanning a wide glyph that has different
|
||||||
|
|||||||
@@ -387,8 +387,12 @@ namespace Microsoft::Console::Render::Atlas
|
|||||||
til::generational<FontSettings> font;
|
til::generational<FontSettings> font;
|
||||||
til::generational<CursorSettings> cursor;
|
til::generational<CursorSettings> cursor;
|
||||||
til::generational<MiscellaneousSettings> misc;
|
til::generational<MiscellaneousSettings> misc;
|
||||||
|
// Size of the viewport / swap chain in pixel.
|
||||||
u16x2 targetSize{ 1, 1 };
|
u16x2 targetSize{ 1, 1 };
|
||||||
u16x2 cellCount{ 1, 1 };
|
// Size of the portion of the text buffer that we're drawing on the screen.
|
||||||
|
u16x2 viewportCellCount{ 1, 1 };
|
||||||
|
// The position of the viewport inside the text buffer (in cells).
|
||||||
|
u16x2 viewportOffset{ 0, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
using GenerationalSettings = til::generational<Settings>;
|
using GenerationalSettings = til::generational<Settings>;
|
||||||
@@ -545,7 +549,7 @@ namespace Microsoft::Console::Render::Atlas
|
|||||||
void MarkAllAsDirty() noexcept
|
void MarkAllAsDirty() noexcept
|
||||||
{
|
{
|
||||||
dirtyRectInPx = { 0, 0, s->targetSize.x, s->targetSize.y };
|
dirtyRectInPx = { 0, 0, s->targetSize.x, s->targetSize.y };
|
||||||
invalidatedRows = { 0, s->cellCount.y };
|
invalidatedRows = { 0, s->viewportCellCount.y };
|
||||||
scrollOffset = 0;
|
scrollOffset = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
cbuffer ConstBuffer : register(b0)
|
cbuffer ConstBuffer : register(b0)
|
||||||
{
|
{
|
||||||
float4 backgroundColor;
|
float4 backgroundColor;
|
||||||
float2 cellSize;
|
float2 backgroundCellSize;
|
||||||
float2 cellCount;
|
float2 backgroundCellCount;
|
||||||
float4 gammaRatios;
|
float4 gammaRatios;
|
||||||
float enhancedContrast;
|
float enhancedContrast;
|
||||||
float underlineWidth;
|
float underlineWidth;
|
||||||
@@ -34,8 +34,8 @@ Output main(PSData data) : SV_Target
|
|||||||
{
|
{
|
||||||
case SHADING_TYPE_TEXT_BACKGROUND:
|
case SHADING_TYPE_TEXT_BACKGROUND:
|
||||||
{
|
{
|
||||||
const float2 cell = data.position.xy / cellSize;
|
const float2 cell = data.position.xy / backgroundCellSize;
|
||||||
color = all(cell < cellCount) ? background[cell] : backgroundColor;
|
color = all(cell < backgroundCellCount) ? background[cell] : backgroundColor;
|
||||||
weights = float4(1, 1, 1, 1);
|
weights = float4(1, 1, 1, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -509,7 +509,7 @@ CATCH_RETURN()
|
|||||||
clipRect.top = origin.y + drawingContext->topClipOffset;
|
clipRect.top = origin.y + drawingContext->topClipOffset;
|
||||||
clipRect.bottom = origin.y + drawingContext->cellSize.height - drawingContext->bottomClipOffset;
|
clipRect.bottom = origin.y + drawingContext->cellSize.height - drawingContext->bottomClipOffset;
|
||||||
clipRect.left = 0;
|
clipRect.left = 0;
|
||||||
clipRect.right = drawingContext->targetSize.width;
|
clipRect.right = FLT_MAX;
|
||||||
|
|
||||||
// If we already have a clip rectangle, check if it different than the previous one.
|
// If we already have a clip rectangle, check if it different than the previous one.
|
||||||
if (_clipRect.has_value())
|
if (_clipRect.has_value())
|
||||||
|
|||||||
@@ -146,11 +146,18 @@
|
|||||||
<DisplayString Condition="_occupied">{glyphIndex}</DisplayString>
|
<DisplayString Condition="_occupied">{glyphIndex}</DisplayString>
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
<Type Name="Microsoft::Console::Render::Atlas::BackendD3D::AtlasFontFaceEntry">
|
<Type Name="Microsoft::Console::Render::Atlas::BackendD3D::AtlasFontFaceEntryInner">
|
||||||
<DisplayString Condition="!_occupied">(empty)</DisplayString>
|
<DisplayString>{(void*)fontFace.m_ptr}, {lineRendition}</DisplayString>
|
||||||
<DisplayString Condition="_occupied">{(void*)fontFace.m_ptr}, {lineRendition}</DisplayString>
|
|
||||||
<Expand>
|
<Expand>
|
||||||
<ExpandedItem>glyphs</ExpandedItem>
|
<ExpandedItem>glyphs</ExpandedItem>
|
||||||
</Expand>
|
</Expand>
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="Microsoft::Console::Render::Atlas::BackendD3D::AtlasFontFaceEntry">
|
||||||
|
<DisplayString Condition="!inner._Mypair._Myval2">(empty)</DisplayString>
|
||||||
|
<DisplayString Condition="inner._Mypair._Myval2">{*inner._Mypair._Myval2}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>*inner._Mypair._Myval2</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
</AutoVisualizer>
|
</AutoVisualizer>
|
||||||
|
|||||||
Reference in New Issue
Block a user