a73x

68df7b96

Clarify dirty-row rendering edge cases

a73x   2026-04-08 18:20

Commit message
Clarify dirty-row rendering edge cases

docs/superpowers/specs/2026-04-08-dirty-row-rendering-design.md
Old New
@@ -82,6 +82,19 @@ Behavior:
82 - If `layout_dirty == true`, repack all rows and upload the full packed buffer. 82 - If `layout_dirty == true`, repack all rows and upload the full packed buffer.
83 - Otherwise, overwrite only the changed row byte ranges and cursor byte range in the GPU instance buffer. 83 - Otherwise, overwrite only the changed row byte ranges and cursor byte range in the GPU instance buffer.
84 84
85 ### Dirty-flag lifecycle
86
87 Dirty flags are consumed by the renderer cache and must be cleared only after cache state has been brought in sync with the current render state.
88
89 Rules:
90
91 - Read `term.render_state.dirty` and `row_data.items(.dirty)` immediately after `term.snapshot()`.
92 - Complete all required row-cache rebuilds, cursor-cache rebuilds, and any repack decisions before clearing flags.
93 - Clear `term.render_state.dirty` and any consumed row dirty flags only after CPU cache state is updated successfully.
94 - If a rebuild, repack, atlas insertion, or upload-preparation step fails, do not clear dirty flags for that frame.
95
96 This prevents both permanent “always dirty” behavior and lost updates caused by clearing too early.
97
85 ### No redraw 98 ### No redraw
86 99
87 If terminal state is not dirty and no window/swapchain event forces a frame, skip both cache work and draw submission. 100 If terminal state is not dirty and no window/swapchain event forces a frame, skip both cache work and draw submission.
@@ -99,6 +112,12 @@ Behavior:
99 - Copy the provided slice into the instance buffer at `offset_instances * @sizeOf(Instance)`. 112 - Copy the provided slice into the instance buffer at `offset_instances * @sizeOf(Instance)`.
100 - Unmap memory. 113 - Unmap memory.
101 114
115 Important fallback rule:
116
117 - If capacity growth is required, `uploadInstanceRange` must not attempt to preserve prior GPU contents implicitly.
118 - Buffer growth forces `layout_dirty = true`, followed by full repack and a full-buffer upload from `packed_instances`.
119 - Partial range uploads are only valid when the underlying GPU buffer remains allocated and existing offsets stay valid.
120
102 Keep `uploadInstances` for full-buffer writes. 121 Keep `uploadInstances` for full-buffer writes.
103 122
104 No pipeline or draw-call structure changes are required. 123 No pipeline or draw-call structure changes are required.
@@ -150,6 +169,9 @@ Add tests before implementation for:
150 - Layout remains stable when a rebuilt row keeps the same instance count. 169 - Layout remains stable when a rebuilt row keeps the same instance count.
151 - Layout becomes dirty when a rebuilt row changes instance count. 170 - Layout becomes dirty when a rebuilt row changes instance count.
152 - Packing offsets remain contiguous after full repack. 171 - Packing offsets remain contiguous after full repack.
172 - Cursor-only updates rebuild cursor cache without requiring unrelated row rebuilds.
173 - Instance-buffer growth during a partial update forces full repack/full upload fallback.
174 - Dirty flags are cleared only after cache refresh planning succeeds.
153 175
154 Verification after implementation: 176 Verification after implementation:
155 177