Skip to content

Commit 80e8261

Browse files
author
yokotoka
committed
Fix progressive input lag from per-frame heap allocations and redundant font metrics
Three targeted optimizations in the terminal rendering hot path: 1. Replace per-frame heap allocations with pre-allocated buffers - updateImage() allocated/freed wchar_t[] and char[] arrays every frame - Over long sessions this caused heap fragmentation, progressively slowing allocation and increasing input latency - Now uses std::vector member variables that resize only on terminal dimension changes 2. Hoist QFontMetrics construction out of per-line loop - QFontMetrics was recreated for every line in updateImage(), up to 50+ times per frame for a typical terminal - Now created once per updateImage() call 3. Skip horizontalAdvance() for fixed-width fonts - For monospace terminal fonts (_fixedFont == true), character width is constant and already known as _fontWidth - Eliminates expensive font shaping engine lookups in both updateImage() and drawContents()
1 parent ce8e09a commit 80e8261

2 files changed

Lines changed: 14 additions & 9 deletions

File tree

lib/TerminalDisplay.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,9 +1205,14 @@ void TerminalDisplay::updateImage()
12051205
const int linesToUpdate = qMin(this->_lines, qMax(0,lines ));
12061206
const int columnsToUpdate = qMin(this->_columns,qMax(0,columns));
12071207

1208-
wchar_t *disstrU = new wchar_t[columnsToUpdate];
1209-
char *dirtyMask = new char[columnsToUpdate+2];
1208+
if (static_cast<int>(_updateDisstrU.size()) < columnsToUpdate)
1209+
_updateDisstrU.resize(columnsToUpdate);
1210+
if (static_cast<int>(_updateDirtyMask.size()) < columnsToUpdate + 2)
1211+
_updateDirtyMask.resize(columnsToUpdate + 2);
1212+
wchar_t *disstrU = _updateDisstrU.data();
1213+
char *dirtyMask = _updateDirtyMask.data();
12101214
QRegion dirtyRegion;
1215+
QFontMetrics fm(font());
12111216

12121217
for (y = 0; y < linesToUpdate; ++y)
12131218
{
@@ -1229,7 +1234,6 @@ void TerminalDisplay::updateImage()
12291234
}
12301235
}
12311236

1232-
QFontMetrics fm(font());
12331237
if (!_resizing) // not while _resizing, we're expecting a paintEvent
12341238
for (x = 0; x < columnsToUpdate; ++x)
12351239
{
@@ -1249,7 +1253,7 @@ void TerminalDisplay::updateImage()
12491253
disstrU[p++] = c; //fontMap(c);
12501254
bool lineDraw = isLineChar(newLine[x+0]);
12511255
bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0);
1252-
int charWidth = fm.horizontalAdvance(QChar(c));
1256+
int charWidth = _fixedFont ? _fontWidth : fm.horizontalAdvance(QChar(c));
12531257
bool bigWidth = _fixedFont && !doubleWidth && charWidth > _fontWidth;
12541258
bool smallWidth = _fixedFont && charWidth < _fontWidth;
12551259
cr = newLine[x].rendition;
@@ -1264,7 +1268,7 @@ void TerminalDisplay::updateImage()
12641268
continue; // Skip trailing part of multi-col chars.
12651269

12661270
bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0);
1267-
int nxtCharWidth = fm.horizontalAdvance(QChar(newLine[x+len].character));
1271+
int nxtCharWidth = _fixedFont ? _fontWidth : fm.horizontalAdvance(QChar(newLine[x+len].character));
12681272
bool nextIsbigWidth = _fixedFont && !nextIsDoubleWidth && nxtCharWidth > _fontWidth;
12691273
bool nextIsSmallWidth = _fixedFont && newLine[x+len].character && nxtCharWidth < _fontWidth;
12701274

@@ -1355,8 +1359,6 @@ void TerminalDisplay::updateImage()
13551359

13561360
if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( TEXT_BLINK_DELAY );
13571361
if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; }
1358-
delete[] dirtyMask;
1359-
delete[] disstrU;
13601362

13611363
}
13621364

@@ -1926,7 +1928,7 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
19261928

19271929
bool lineDraw = isLineChar(_image[loc(x,y)]);
19281930
bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0);
1929-
int charWidth = fm.horizontalAdvance(QChar(c));
1931+
int charWidth = _fixedFont ? _fontWidth : fm.horizontalAdvance(QChar(c));
19301932
bool bigWidth = _fixedFont && !doubleWidth && charWidth > _fontWidth;
19311933
bool tooWide = bigWidth && charWidth >= 2 * _fontWidth;
19321934
bool smallWidth = _fixedFont && c && charWidth < _fontWidth;
@@ -1943,7 +1945,7 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
19431945
_image[loc(x+len,y)].rendition == currentRendition &&
19441946
(nxtDoubleWidth = (_image[qMin(loc(x+len,y)+1,_imageSize)].character == 0)) == doubleWidth &&
19451947
!smallWidth &&
1946-
!(_fixedFont && (nxtC = _image[loc(x+len,y)].character) && (nxtCharWidth = fm.horizontalAdvance(QChar(nxtC))) < _fontWidth) &&
1948+
!(_fixedFont && (nxtC = _image[loc(x+len,y)].character) && (nxtCharWidth = (_fixedFont ? _fontWidth : fm.horizontalAdvance(QChar(nxtC)))) < _fontWidth) &&
19471949
!bigWidth &&
19481950
!(_fixedFont && !nxtDoubleWidth && nxtC && nxtCharWidth > _fontWidth) &&
19491951
isLineChar(_image[loc(x+len,y)]) == lineDraw) // Assignment!

lib/TerminalDisplay.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <QString>
2929
#include <QObject>
3030
#include <QSize>
31+
#include <vector>
3132

3233
// Konsole
3334
#include "Filter.h"
@@ -875,6 +876,8 @@ private slots:
875876
// only the area [usedLines][usedColumns] in the image contains valid data
876877

877878
int _imageSize;
879+
std::vector<wchar_t> _updateDisstrU;
880+
std::vector<char> _updateDirtyMask;
878881
QVector<LineProperty> _lineProperties;
879882

880883
ColorEntry _colorTable[TABLE_COLORS];

0 commit comments

Comments
 (0)