Skip to content

perf(core): incremental drawlist emission#29

Merged
RtlZeroMemory merged 5 commits into
mainfrom
perf/partial-drawlist-emission
Feb 11, 2026
Merged

perf(core): incremental drawlist emission#29
RtlZeroMemory merged 5 commits into
mainfrom
perf/partial-drawlist-emission

Conversation

@RtlZeroMemory

Copy link
Copy Markdown
Owner

Implements internal damage-based incremental rendering in WidgetRenderer (no public API changes).

  • Damage rect computation via structural-sharing identity diff
  • Render culling via damageRect (renderTree + row/column child-range pruning)
  • v2 cursor handling preserved in incremental mode
  • Fix ui.text props detection for textOverflow/variant/maxWidth
  • Add regression tests for partial emission + commit fast reuse

Bench: content-update improved to ~1.1ms on this machine (still behind blessed ~0.7ms).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99dba499e1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/src/runtime/commit.ts Outdated
Comment on lines +78 to +82
const ap = a.props as { style?: unknown; textOverflow?: unknown; variant?: unknown; maxWidth?: unknown };
const bp = b.props as { style?: unknown; textOverflow?: unknown; variant?: unknown; maxWidth?: unknown };
if (ap.textOverflow !== bp.textOverflow) return false;
if (ap.variant !== bp.variant) return false;
if (ap.maxWidth !== bp.maxWidth) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Compare text ids before fast-reusing leaf nodes

This equality check treats two text vnodes as equivalent even when their id differs, so commitNode reuses the previous RuntimeInstance and keeps the old vnode.props.id. That makes id-based lookups (for example, rect indexing in buildLayoutRectIndexes and any anchorId references) continue pointing at the stale id when only the id changes but text/style stays the same. This regression appears whenever apps dynamically retag text nodes without changing their rendered content.

Useful? React with 👍 / 👎.

Comment on lines +473 to +474
if (prev && prev.vnode.kind === vnode.kind && leafVNodeEqual(prev.vnode, vnode)) {
return { ok: true, value: { root: prev } };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep lifecycle reuse tracking on leaf fast path

The leaf fast-return exits before lifecycle bookkeeping, so unchanged leaf nodes are no longer appended to reusedInstanceIds even when collectLifecycleInstanceIds is left at its default (true). Any caller that relies on commitVNodeTree(...).reusedInstanceIds to represent all reused nodes in a commit now gets incomplete data for text/spacer/divider subtrees.

Useful? React with 👍 / 👎.

@RtlZeroMemory
RtlZeroMemory force-pushed the perf/partial-drawlist-emission branch from 99dba49 to a021d3d Compare February 11, 2026 09:57
RezoR0xFF and others added 3 commits February 11, 2026 14:00
Key optimizations across commit, reconciliation, and render phases:

- commit.ts: Add leaf VNode equality fast path to reuse RuntimeInstance
  for unchanged text/spacer/divider nodes. Add container subtree reuse
  when all children return identical references. Use shared EMPTY_CHILDREN
  constant for leaf nodes. Remove Object.freeze+slice from commitVNodeTree.
- reconcile.ts: Pre-allocate arrays in reconcileUnkeyedChildren with known
  sizes, use indexed assignment instead of push. Add EMPTY_INSTANCE_IDS
  shared constant.
- spacing.ts: Return shared ZERO_SPACING constant for the common no-padding
  case, avoiding per-node object allocation.
- containers.ts: Skip childClip allocation and extra clip operations when
  spacing is zero. Avoid mergeTextStyle call when no style override.
- basic.ts: Add ASCII fast path for text nodes to skip measureTextCells
  when text fits within rect. Add early-exit for unstyled text nodes.

Results (content-update, 500-row list):
  Before: 3.25ms → After: 1.86ms (-43%)
  Now beats terminal-kit (1.98ms) and 2.7x of blessed (679µs)

Results (rerender, 10-item list):
  Rezi: 30µs — fastest across all frameworks
  blessed: 57µs, terminal-kit: 64µs

All 873 tests pass with zero regressions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add damage-based rendering in WidgetRenderer for render-only updates and content-update bench. Includes identity-diff damage tracking, damage-rect culling, and regression tests.
@RtlZeroMemory
RtlZeroMemory force-pushed the perf/partial-drawlist-emission branch from a021d3d to ce56631 Compare February 11, 2026 10:00
@RtlZeroMemory
RtlZeroMemory merged commit 045a076 into main Feb 11, 2026
14 checks passed
@RtlZeroMemory
RtlZeroMemory deleted the perf/partial-drawlist-emission branch February 19, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants