port feasable performance improvements

This commit is contained in:
Gregor Lohaus
2026-05-31 19:38:06 +02:00
parent dba6474491
commit d8447d9e29
3 changed files with 82 additions and 36 deletions

View File

@@ -365,7 +365,10 @@ public final class Compositor {
double ey = localY(event.getY(), target);
KeyModifiers modifiers = modifiers(event);
for (int i = 0; i < rows; i++) {
sent |= send(pane, target, MouseInput.press(wheelButton, ex, ey, modifiers), mouseButtonPressed, event);
if (!send(pane, target, MouseInput.press(wheelButton, ex, ey, modifiers), mouseButtonPressed, event)) {
break;
}
sent = true;
}
}
if (!sent) {

View File

@@ -14,6 +14,7 @@ import dev.jlibghostty.Terminal;
import dev.jlibghostty.TerminalOptions;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
/**
* One terminal: owns its ghostty {@link Terminal}, the {@link ShellSession}/pty driving it,
@@ -41,8 +42,8 @@ public final class TerminalPane implements AutoCloseable {
private int rows;
private int pixelWidth;
private int pixelHeight;
private long contentVersion;
private long snapshotVersion = -1;
private final AtomicLong contentVersion = new AtomicLong();
private volatile long snapshotVersion = -1;
private TerminalPane(Terminal terminal, TerminalMetrics metrics, boolean kittyEnabled,
Runnable onContentChange, int columns, int rows) {
@@ -157,16 +158,17 @@ public final class TerminalPane implements AutoCloseable {
private RenderStateSnapshot takeSnapshot(boolean full) {
synchronized (terminal) {
long version = contentVersion.get();
if (full) {
renderState.update(terminal);
cachedSnapshot = renderState.snapshot();
renderState.resetDirty();
snapshotVersion = contentVersion;
} else if (snapshotVersion != contentVersion) {
snapshotVersion = version;
} else if (snapshotVersion != version) {
renderState.update(terminal);
cachedSnapshot = renderState.snapshotIncremental();
renderState.resetDirty();
snapshotVersion = contentVersion;
snapshotVersion = version;
}
return cachedSnapshot;
}
@@ -180,16 +182,12 @@ public final class TerminalPane implements AutoCloseable {
/** This pane's own content revision, bumped on every change (see {@link #refresh()}). */
public long contentVersion() {
synchronized (terminal) {
return contentVersion;
}
return contentVersion.get();
}
long snapshotVersion() {
synchronized (terminal) {
return snapshotVersion;
}
}
public boolean kittyEnabled() {
return kittyEnabled;
@@ -256,7 +254,7 @@ public final class TerminalPane implements AutoCloseable {
// Mark this pane's content dirty (the snapshot is computed lazily in the paint path,
// so a burst of writes collapses into one snapshot per frame) and tell the owning tab
// one of its panes changed.
contentVersion++;
contentVersion.incrementAndGet();
onContentChange.run();
}

View File

@@ -603,6 +603,27 @@ final class TerminalPaneNode extends Region {
return cellBackgroundColor(firstCell ? cells.get(0) : cells.get(cells.size() - 1));
}
private static Color cellBackgroundOverride(RenderCell cell) {
if (cell.inverse()) {
var fg = cell.foreground();
return fg.isPresent() ? toFxColor(fg.get()) : DEFAULT_FOREGROUND;
}
var bg = cell.background();
return bg.isPresent() ? toFxColor(bg.get()) : null;
}
private static Color cellForegroundColor(RenderCell cell) {
var fgOpt = cell.foreground();
var bgOpt = cell.background();
Color fg = fgOpt.isPresent() ? toFxColor(fgOpt.get()) : DEFAULT_FOREGROUND;
Color bg = bgOpt.isPresent() ? toFxColor(bgOpt.get()) : null;
if (cell.inverse()) {
return (bg != null) ? bg : PANE_BACKGROUND;
}
return fg;
}
private static Color toFxColor(RenderColor color) {
int key = (color.red() << 16) | (color.green() << 8) | color.blue();
Color cached = COLOR_CACHE.get(key);
@@ -709,37 +730,61 @@ final class TerminalPaneNode extends Region {
double contentTop = TerminalMetrics.PADDING + row.row() * lineHeight;
double localCellTop = contentTop - rowTop;
double baseline = TerminalMetrics.PADDING + metrics.baselineOffset() + row.row() * lineHeight - rowTop;
drawRowBackgrounds(gc, row, localCellTop, cellWidth, lineHeight);
drawRowText(gc, row, baseline, cellWidth);
}
private void drawRowBackgrounds(GraphicsContext gc, RenderRow row, double localCellTop, double cellWidth, double lineHeight) {
Color runBackground = null;
int runStartColumn = 0;
int previousColumn = -1;
for (RenderCell cell : row.cells()) {
if (cell.kittyPlaceholder().isPresent()) {
flushBackgroundRun(gc, runBackground, localCellTop, cellWidth, lineHeight, runStartColumn, previousColumn);
runBackground = null;
previousColumn = -1;
continue;
}
double x = TerminalMetrics.PADDING + cell.column() * cellWidth;
var fgOpt = cell.foreground();
var bgOpt = cell.background();
Color fg = fgOpt.isPresent() ? toFxColor(fgOpt.get()) : DEFAULT_FOREGROUND;
Color bg = bgOpt.isPresent() ? toFxColor(bgOpt.get()) : null;
if (cell.inverse()) {
Color swappedBg = fg;
fg = (bg != null) ? bg : PANE_BACKGROUND;
bg = swappedBg;
}
if (bg != null) {
gc.setFill(bg);
gc.fillRect(x, localCellTop, cellWidth, lineHeight);
}
if (cell.selected()) {
gc.setFill(SELECTED_BACKGROUND);
gc.fillRect(x, localCellTop, cellWidth, lineHeight);
}
if (cell.codepoints().length == 0) {
Color background = cell.selected() ? SELECTED_BACKGROUND : cellBackgroundOverride(cell);
if (background == null) {
flushBackgroundRun(gc, runBackground, localCellTop, cellWidth, lineHeight, runStartColumn, previousColumn);
runBackground = null;
previousColumn = -1;
continue;
}
gc.setFill(fg);
gc.fillText(cell.text(), x, baseline);
if (runBackground == null || background != runBackground || cell.column() != previousColumn + 1) {
flushBackgroundRun(gc, runBackground, localCellTop, cellWidth, lineHeight, runStartColumn, previousColumn);
runBackground = background;
runStartColumn = cell.column();
}
previousColumn = cell.column();
}
flushBackgroundRun(gc, runBackground, localCellTop, cellWidth, lineHeight, runStartColumn, previousColumn);
}
private void flushBackgroundRun(GraphicsContext gc, Color background, double localCellTop,
double cellWidth, double lineHeight, int startColumn, int endColumn) {
if (background == null || endColumn < startColumn) {
return;
}
gc.setFill(background);
gc.fillRect(
TerminalMetrics.PADDING + startColumn * cellWidth,
localCellTop,
(endColumn - startColumn + 1) * cellWidth,
lineHeight);
}
private void drawRowText(GraphicsContext gc, RenderRow row, double baseline, double cellWidth) {
for (RenderCell cell : row.cells()) {
if (cell.kittyPlaceholder().isPresent() || cell.codepoints().length == 0) {
continue;
}
gc.setFill(cellForegroundColor(cell));
gc.fillText(cell.text(), TerminalMetrics.PADDING + cell.column() * cellWidth, baseline);
}
}
}