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); double ey = localY(event.getY(), target);
KeyModifiers modifiers = modifiers(event); KeyModifiers modifiers = modifiers(event);
for (int i = 0; i < rows; i++) { 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) { if (!sent) {

View File

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

View File

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