Skip to content

Commit 2902494

Browse files
committed
Merge branch 'per-side-border-69' of github.com:bombshell-dev/clayterm into per-side-border-69
2 parents 0cf029c + a46256a commit 2902494

3 files changed

Lines changed: 42 additions & 17 deletions

File tree

ops.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,11 @@ export function pack(
190190
);
191191
o += 4;
192192

193-
// Resolved per-side attributes (CSS-like fallback expansion done
194-
// here, not in C): fg/bg word pairs in top, right, bottom, left
195-
// order. The C renderer consumes these as explicit values.
193+
// Must match render_border() in src/clayterm.c.
194+
// Resolve CSS-like side fallbacks here, then write eight required
195+
// attribute words: fg/bg pairs in top, right, bottom, left order.
196+
// C treats the presence and order of these words as a wire-format
197+
// invariant and only consumes the explicit values.
196198
for (let side of [b.top, b.right, b.bottom, b.left]) {
197199
view.setUint32(o, sideFg(side, b.color), true);
198200
o += 4;

src/clayterm.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,17 +303,26 @@ static void render_text(struct Clayterm *ct, int x0, int y0,
303303
static void render_border(struct Clayterm *ct, int x0, int y0, int x1, int y1,
304304
Clay_RenderCommand *cmd) {
305305
Clay_BorderRenderData *b = &cmd->renderData.border;
306-
/* userData points at eight words in the command buffer carrying resolved
307-
* per-side attributes as fg/bg pairs in top, right, bottom, left order.
308-
* Fallback resolution (shared color/bg vs side overrides) happens on the
309-
* TypeScript side; this renderer consumes explicit values only. The
310-
* command buffer outlives the render pass within reduce(). */
306+
/* Must match border packing in ops.ts.
307+
* userData points at eight required words in the command buffer: resolved
308+
* fg/bg pairs in top, right, bottom, left order. Fallback resolution
309+
* (shared color/bg vs side overrides) happens on the TypeScript side; this
310+
* renderer consumes explicit values only. The command buffer outlives the
311+
* render pass within reduce(). Missing userData is a wire-format violation.
312+
*/
311313
const uint32_t *s = (const uint32_t *)cmd->userData;
312-
uint32_t deffg = color(b->color);
313-
uint32_t top_fg = s ? s[0] : deffg, top_bg = s ? s[1] : ATTR_DEFAULT;
314-
uint32_t right_fg = s ? s[2] : deffg, right_bg = s ? s[3] : ATTR_DEFAULT;
315-
uint32_t bot_fg = s ? s[4] : deffg, bot_bg = s ? s[5] : ATTR_DEFAULT;
316-
uint32_t left_fg = s ? s[6] : deffg, left_bg = s ? s[7] : ATTR_DEFAULT;
314+
if (s == NULL) {
315+
__builtin_trap();
316+
}
317+
318+
uint32_t top_fg = s[0];
319+
uint32_t top_bg = s[1];
320+
uint32_t right_fg = s[2];
321+
uint32_t right_bg = s[3];
322+
uint32_t bot_fg = s[4];
323+
uint32_t bot_bg = s[5];
324+
uint32_t left_fg = s[6];
325+
uint32_t left_bg = s[7];
317326
int top = b->width.top > 0;
318327
int bot = b->width.bottom > 0;
319328
int left = b->width.left > 0;

term-native.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ const BoundingBoxStruct = struct<BoundingBox>({
1616

1717
const BOUNDING_BOX = offsets(BoundingBoxStruct);
1818

19+
const WASM_PAGE_BYTES = 65536;
20+
const TEXT_TRANSFER_BUFFER_BYTES = 1024 * 1024;
21+
const CLAY_DEFAULT_MAX_ELEMENT_COUNT = 8192;
22+
23+
// Conservative fixed wire-format budget per element. This covers the largest
24+
// non-text open/close element encoding we currently support; text, element id,
25+
// and snapshot payload bytes live in TEXT_TRANSFER_BUFFER_BYTES.
26+
const MAX_FIXED_ELEMENT_WIRE_BYTES = 116;
27+
1928
export interface Native {
2029
memory: WebAssembly.Memory;
2130
statePtr: number;
@@ -92,10 +101,15 @@ export async function createTermNative(
92101
let heap = ct.__heap_base.value as number;
93102
let size = ct.clayterm_size(w, h);
94103

95-
// grow memory to fit heap + state + ops buffer (1MB headroom for ops)
96-
let needed = heap + size + 1024 * 1024;
97-
let pages = Math.ceil(needed / 65536);
98-
let current = memory.buffer.byteLength / 65536;
104+
// Grow memory once to fit heap + renderer state + fixed transfer buffer.
105+
// The transfer budget is intentionally fixed: text/id/snapshot payload bytes
106+
// get 1MB, and fixed op overhead gets one max-sized element per Clay element.
107+
// Do not grow this dynamically per render; improve the wire format instead.
108+
let transferBytes = TEXT_TRANSFER_BUFFER_BYTES +
109+
CLAY_DEFAULT_MAX_ELEMENT_COUNT * MAX_FIXED_ELEMENT_WIRE_BYTES;
110+
let needed = heap + size + transferBytes;
111+
let pages = Math.ceil(needed / WASM_PAGE_BYTES);
112+
let current = memory.buffer.byteLength / WASM_PAGE_BYTES;
99113
if (pages > current) {
100114
memory.grow(pages - current);
101115
}

0 commit comments

Comments
 (0)