expose dirty on render state for incremental rendering
This commit is contained in:
@@ -29,10 +29,17 @@ public final class RenderState implements AutoCloseable {
|
|||||||
GhosttyLibrary.RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE
|
GhosttyLibrary.RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// When only some rows changed (PARTIAL), skip marshalling cells for clean rows;
|
||||||
|
// FULL means the whole frame must be redrawn, so every row's cells are needed. A
|
||||||
|
// throwaway render state (a single update) always reports FULL, so callers that do
|
||||||
|
// not keep the state around keep getting fully-populated rows exactly as before.
|
||||||
|
int dirty = library.renderStateGetI32(handle, GhosttyLibrary.RENDER_STATE_DATA_DIRTY);
|
||||||
|
boolean dirtyRowsOnly = dirty != GhosttyLibrary.RENDER_STATE_DIRTY_FULL;
|
||||||
|
|
||||||
return new RenderStateSnapshot(
|
return new RenderStateSnapshot(
|
||||||
library.renderStateGetU16(handle, GhosttyLibrary.RENDER_STATE_DATA_COLS),
|
library.renderStateGetU16(handle, GhosttyLibrary.RENDER_STATE_DATA_COLS),
|
||||||
library.renderStateGetU16(handle, GhosttyLibrary.RENDER_STATE_DATA_ROWS),
|
library.renderStateGetU16(handle, GhosttyLibrary.RENDER_STATE_DATA_ROWS),
|
||||||
library.renderStateGetI32(handle, GhosttyLibrary.RENDER_STATE_DATA_DIRTY),
|
dirty,
|
||||||
RenderCursorStyle.fromNative(
|
RenderCursorStyle.fromNative(
|
||||||
library.renderStateGetI32(handle, GhosttyLibrary.RENDER_STATE_DATA_CURSOR_VISUAL_STYLE)
|
library.renderStateGetI32(handle, GhosttyLibrary.RENDER_STATE_DATA_CURSOR_VISUAL_STYLE)
|
||||||
),
|
),
|
||||||
@@ -48,7 +55,7 @@ public final class RenderState implements AutoCloseable {
|
|||||||
: -1,
|
: -1,
|
||||||
cursorViewportHasValue
|
cursorViewportHasValue
|
||||||
&& library.renderStateGetBoolean(handle, GhosttyLibrary.RENDER_STATE_DATA_CURSOR_VIEWPORT_WIDE_TAIL),
|
&& library.renderStateGetBoolean(handle, GhosttyLibrary.RENDER_STATE_DATA_CURSOR_VIEWPORT_WIDE_TAIL),
|
||||||
rows()
|
library.renderStateRows(handle, dirtyRowsOnly)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +64,16 @@ public final class RenderState implements AutoCloseable {
|
|||||||
return library.renderStateRows(handle);
|
return library.renderStateRows(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the global and per-row dirty flags. Call after rendering a frame so the next
|
||||||
|
* {@link #update} reports only the rows that change after this point. Required for
|
||||||
|
* incremental rendering with a render state reused across frames.
|
||||||
|
*/
|
||||||
|
public void resetDirty() {
|
||||||
|
ensureOpen();
|
||||||
|
library.renderStateResetDirty(handle);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (closed.compareAndSet(false, true)) {
|
if (closed.compareAndSet(false, true)) {
|
||||||
|
|||||||
@@ -94,6 +94,15 @@ public final class GhosttyLibrary {
|
|||||||
public static final int RENDER_STATE_ROW_DATA_DIRTY = 1;
|
public static final int RENDER_STATE_ROW_DATA_DIRTY = 1;
|
||||||
public static final int RENDER_STATE_ROW_DATA_CELLS = 3;
|
public static final int RENDER_STATE_ROW_DATA_CELLS = 3;
|
||||||
|
|
||||||
|
// ghostty_render_state_set / _row_set option selectors (both DIRTY == 0).
|
||||||
|
public static final int RENDER_STATE_OPTION_DIRTY = 0;
|
||||||
|
public static final int RENDER_STATE_ROW_OPTION_DIRTY = 0;
|
||||||
|
|
||||||
|
// GhosttyRenderStateDirty values returned by RENDER_STATE_DATA_DIRTY.
|
||||||
|
public static final int RENDER_STATE_DIRTY_FALSE = 0;
|
||||||
|
public static final int RENDER_STATE_DIRTY_PARTIAL = 1;
|
||||||
|
public static final int RENDER_STATE_DIRTY_FULL = 2;
|
||||||
|
|
||||||
public static final int RENDER_STATE_ROW_CELLS_DATA_STYLE = 2;
|
public static final int RENDER_STATE_ROW_CELLS_DATA_STYLE = 2;
|
||||||
public static final int RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_LEN = 3;
|
public static final int RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_LEN = 3;
|
||||||
public static final int RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_BUF = 4;
|
public static final int RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_BUF = 4;
|
||||||
@@ -330,6 +339,8 @@ public final class GhosttyLibrary {
|
|||||||
private final MethodHandle renderStateFree;
|
private final MethodHandle renderStateFree;
|
||||||
private final MethodHandle renderStateUpdate;
|
private final MethodHandle renderStateUpdate;
|
||||||
private final MethodHandle renderStateGet;
|
private final MethodHandle renderStateGet;
|
||||||
|
private final MethodHandle renderStateSet;
|
||||||
|
private final MethodHandle renderStateRowSet;
|
||||||
private final MethodHandle renderStateRowIteratorNew;
|
private final MethodHandle renderStateRowIteratorNew;
|
||||||
private final MethodHandle renderStateRowIteratorFree;
|
private final MethodHandle renderStateRowIteratorFree;
|
||||||
private final MethodHandle renderStateRowIteratorNext;
|
private final MethodHandle renderStateRowIteratorNext;
|
||||||
@@ -409,6 +420,10 @@ public final class GhosttyLibrary {
|
|||||||
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
|
||||||
renderStateGet = downcall(symbols, "ghostty_render_state_get",
|
renderStateGet = downcall(symbols, "ghostty_render_state_get",
|
||||||
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
||||||
|
renderStateSet = downcall(symbols, "ghostty_render_state_set",
|
||||||
|
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
||||||
|
renderStateRowSet = downcall(symbols, "ghostty_render_state_row_set",
|
||||||
|
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
||||||
renderStateRowIteratorNew = downcall(symbols, "ghostty_render_state_row_iterator_new",
|
renderStateRowIteratorNew = downcall(symbols, "ghostty_render_state_row_iterator_new",
|
||||||
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
|
||||||
renderStateRowIteratorFree = downcall(symbols, "ghostty_render_state_row_iterator_free",
|
renderStateRowIteratorFree = downcall(symbols, "ghostty_render_state_row_iterator_free",
|
||||||
@@ -855,6 +870,16 @@ public final class GhosttyLibrary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<RenderRow> renderStateRows(MemorySegment state) {
|
public List<RenderRow> renderStateRows(MemorySegment state) {
|
||||||
|
return renderStateRows(state, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the row list. When {@code dirtyRowsOnly} is true, cells are marshalled only
|
||||||
|
* for rows ghostty flagged dirty; clean rows get an empty cell list. This lets callers
|
||||||
|
* that keep a persistent render state skip the (expensive) per-cell marshalling for
|
||||||
|
* rows that have not changed since the last {@link #renderStateResetDirty}.
|
||||||
|
*/
|
||||||
|
public List<RenderRow> renderStateRows(MemorySegment state, boolean dirtyRowsOnly) {
|
||||||
MemorySegment iterator = renderStateRowIteratorNew();
|
MemorySegment iterator = renderStateRowIteratorNew();
|
||||||
try {
|
try {
|
||||||
renderStatePopulateRowIterator(state, iterator);
|
renderStatePopulateRowIterator(state, iterator);
|
||||||
@@ -862,7 +887,10 @@ public final class GhosttyLibrary {
|
|||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
while (renderStateRowIteratorNext(iterator)) {
|
while (renderStateRowIteratorNext(iterator)) {
|
||||||
boolean dirty = renderStateRowGetBoolean(iterator, RENDER_STATE_ROW_DATA_DIRTY);
|
boolean dirty = renderStateRowGetBoolean(iterator, RENDER_STATE_ROW_DATA_DIRTY);
|
||||||
rows.add(new RenderRow(rowIndex, dirty, renderStateRowCells(iterator)));
|
List<RenderCell> cells = (dirtyRowsOnly && !dirty)
|
||||||
|
? List.of()
|
||||||
|
: renderStateRowCells(iterator);
|
||||||
|
rows.add(new RenderRow(rowIndex, dirty, cells));
|
||||||
rowIndex++;
|
rowIndex++;
|
||||||
}
|
}
|
||||||
return List.copyOf(rows);
|
return List.copyOf(rows);
|
||||||
@@ -871,6 +899,41 @@ public final class GhosttyLibrary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderStateSetDirty(MemorySegment state, int dirtyValue) {
|
||||||
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
|
MemorySegment value = arena.allocate(C_INT);
|
||||||
|
value.set(C_INT, 0, dirtyValue);
|
||||||
|
int result = (int) renderStateSet.invoke(state, RENDER_STATE_OPTION_DIRTY, value);
|
||||||
|
checkResult("ghostty_render_state_set", result);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
rethrow(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the global dirty flag and every per-row dirty flag. The {@code update} call
|
||||||
|
* does not reset dirty state, so a caller reusing a render state across frames must
|
||||||
|
* call this after consuming a frame; the next {@code update} then reports only what
|
||||||
|
* changed since now.
|
||||||
|
*/
|
||||||
|
public void renderStateResetDirty(MemorySegment state) {
|
||||||
|
MemorySegment iterator = renderStateRowIteratorNew();
|
||||||
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
|
renderStatePopulateRowIterator(state, iterator);
|
||||||
|
MemorySegment falseValue = arena.allocate(C_BOOL);
|
||||||
|
falseValue.set(C_BOOL, 0, false);
|
||||||
|
while (renderStateRowIteratorNext(iterator)) {
|
||||||
|
int result = (int) renderStateRowSet.invoke(iterator, RENDER_STATE_ROW_OPTION_DIRTY, falseValue);
|
||||||
|
checkResult("ghostty_render_state_row_set", result);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
rethrow(t);
|
||||||
|
} finally {
|
||||||
|
renderStateRowIteratorFree(iterator);
|
||||||
|
}
|
||||||
|
renderStateSetDirty(state, RENDER_STATE_DIRTY_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
private MemorySegment renderStateRowIteratorNew() {
|
private MemorySegment renderStateRowIteratorNew() {
|
||||||
try (Arena arena = Arena.ofConfined()) {
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
MemorySegment out = arena.allocate(C_POINTER);
|
MemorySegment out = arena.allocate(C_POINTER);
|
||||||
|
|||||||
Reference in New Issue
Block a user