Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

shared/rast: Makefile + README with cross-browser compat matrix

Build targets for every shipping variant:
make test → self-tests (currently 4 green)
make native → libac_rast.a for linking into ac-native
make wasm → four modules (baseline / simd / threads / full)

README captures the integration plan, the cross-browser compat table
(what features unlock on which Safari/Firefox/Chrome versions), and the
COOP/COEP prereq for the threads+SAB parallel path. Status checklist
marks what's done and what's next so the next session doesn't need to
re-derive the plan from the commit log.

+213
+92
shared/rast/Makefile
··· 1 + ## shared/rast — build the cross-platform rasterizer for every target. 2 + ## 3 + ## Targets: 4 + ## test → native test binary (gcc), runs the self-tests 5 + ## native → static archive (libac_rast.a) for linking into ac-native 6 + ## wasm → wasm modules for the web runtime (needs emscripten in PATH): 7 + ## raster-baseline.{wasm,js} — no SIMD, no threads 8 + ## raster-simd.{wasm,js} — SIMD, no threads 9 + ## raster-threads.{wasm,js} — threads + SAB 10 + ## raster-full.{wasm,js} — SIMD + threads 11 + ## clean 12 + ## 13 + ## Pixel output must stay identical across test / native / every wasm 14 + ## variant. raster_test.c exercises the correctness path; a follow-up 15 + ## snapshot harness (golden PNG diff) covers the richer drift catch. 16 + 17 + CC ?= gcc 18 + CFLAGS ?= -O3 -Wall -Wextra -std=c99 -fPIC 19 + AR ?= ar 20 + EMCC ?= emcc 21 + 22 + RAST_SRC := raster.c 23 + RAST_OBJ := raster.o 24 + 25 + .PHONY: all test native wasm clean 26 + 27 + all: test 28 + 29 + # ── self-test (gcc native) ─────────────────────────────────────────── 30 + raster_test: $(RAST_SRC) raster_test.c raster.h 31 + $(CC) $(CFLAGS) -o $@ $(RAST_SRC) raster_test.c -lm 32 + 33 + test: raster_test 34 + ./raster_test 35 + 36 + # ── static archive for ac-native link ──────────────────────────────── 37 + $(RAST_OBJ): $(RAST_SRC) raster.h 38 + $(CC) $(CFLAGS) -c -o $@ $< 39 + 40 + libac_rast.a: $(RAST_OBJ) 41 + $(AR) rcs $@ $^ 42 + 43 + native: libac_rast.a 44 + 45 + # ── WASM variants (emscripten) ─────────────────────────────────────── 46 + # Each variant exports the same C API, so the JS loader probes features 47 + # and picks the best one the browser can actually run. See raster.h for 48 + # the exported entry points. 49 + # 50 + # Build flags (same across variants for everything NOT related to the 51 + # capability being toggled, so variants stay pixel-identical): 52 + # -O3 — maximum inlining and loop opts 53 + # -flto — link-time optimization 54 + # -s STANDALONE_WASM=1 — emit a standalone module (no Emscripten JS shim) 55 + # -s EXPORTED_FUNCTIONS=['_ac_rast_triangle','_ac_rast_clear'] 56 + # -s ENVIRONMENT='web,worker' — only ship web code paths 57 + # 58 + # Variant-specific flags: 59 + # baseline → (none) 60 + # simd → -msimd128 61 + # threads → -pthread -s PTHREAD_POOL_SIZE='navigator.hardwareConcurrency' 62 + # full → -msimd128 -pthread … 63 + 64 + EMCC_COMMON := -O3 -flto \ 65 + -s STANDALONE_WASM=1 \ 66 + -s EXPORTED_FUNCTIONS='["_ac_rast_triangle","_ac_rast_clear","_malloc","_free"]' \ 67 + -s EXPORTED_RUNTIME_METHODS='["HEAPU8","HEAPU32","HEAPF32"]' \ 68 + -s ENVIRONMENT='web,worker' \ 69 + -s ALLOW_MEMORY_GROWTH=1 \ 70 + -s MALLOC=emmalloc 71 + 72 + raster-baseline.wasm: $(RAST_SRC) raster.h 73 + $(EMCC) $(EMCC_COMMON) -o $@ $(RAST_SRC) 74 + 75 + raster-simd.wasm: $(RAST_SRC) raster.h 76 + $(EMCC) $(EMCC_COMMON) -msimd128 -o $@ $(RAST_SRC) 77 + 78 + raster-threads.wasm: $(RAST_SRC) raster.h 79 + $(EMCC) $(EMCC_COMMON) -pthread \ 80 + -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=8 \ 81 + -o $@ $(RAST_SRC) 82 + 83 + raster-full.wasm: $(RAST_SRC) raster.h 84 + $(EMCC) $(EMCC_COMMON) -msimd128 -pthread \ 85 + -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=8 \ 86 + -o $@ $(RAST_SRC) 87 + 88 + wasm: raster-baseline.wasm raster-simd.wasm raster-threads.wasm raster-full.wasm 89 + 90 + clean: 91 + rm -f raster_test $(RAST_OBJ) libac_rast.a \ 92 + raster-baseline.* raster-simd.* raster-threads.* raster-full.*
+121
shared/rast/README.md
··· 1 + # shared/rast — cross-platform triangle rasterizer 2 + 3 + One C99 triangle rasterizer used by both **ac-native** (fedac/native) and the 4 + **web runtime** (system/public/aesthetic.computer). Same pixel output on both 5 + platforms — no more drift between what a piece looks like in a browser and 6 + what it looks like on bare metal. 7 + 8 + ## Why 9 + 10 + Before this, arena.mjs (and other 3D pieces) ran through two completely 11 + independent rasterizers — `graph.mjs`'s JS triangle path on web, and 12 + `graph3d.c`'s software rasterizer on native. Both implementations had to be 13 + maintained in parallel and drifted in subtle ways: different pixel coverage 14 + rules, different near-plane behaviour, different color precision. The fix 15 + is structural: one source of truth, multiple build targets. 16 + 17 + ## Source 18 + 19 + - `raster.h` — public API (pixel format, vertex struct, options, entry points) 20 + - `raster.c` — implementation (~180 lines, pure C99, no libc allocs on the 21 + hot path, no mutable globals — fully re-entrant so worker threads can 22 + rasterize independent tiles against shared framebuffers) 23 + - `raster_test.c` — self-tests (solid fill, perspective-correct color 24 + interpolation, depth occlusion, scissor clipping) 25 + 26 + ## Build 27 + 28 + make test # run self-tests (gcc) 29 + make native # libac_rast.a for linking into ac-native 30 + make wasm # four WASM variants for the web runtime (needs emcc) 31 + make clean 32 + 33 + The `wasm` target emits four modules with progressive feature sets. The 34 + web loader probes browser capability and picks the best one available: 35 + 36 + | Variant | Tradeoff | 37 + |---|---| 38 + | `raster-baseline.wasm` | ubiquitous; 2-3× faster than current JS | 39 + | `raster-simd.wasm` | needs WASM SIMD (Chrome 91+, FF 89+, Safari 16.4+); 4-6× | 40 + | `raster-threads.wasm` | needs SharedArrayBuffer (COOP+COEP on server); 8-15× | 41 + | `raster-full.wasm` | SIMD + threads; 20-40× on multicore desktops | 42 + 43 + See the [compatibility matrix](#cross-browser-support) below. 44 + 45 + ## Integration status 46 + 47 + - [x] Pure-C rasterizer + self-tests 48 + - [x] Makefile for native + wasm build targets 49 + - [ ] Wire into `fedac/native/src/graph3d.c` (replace `rasterize_triangle`) 50 + - [ ] emcc build variants produced + smoke-tested via wasmtime or node 51 + - [ ] JS loader in `system/public/aesthetic.computer/lib/graph.mjs` that 52 + probes caps and instantiates the best module 53 + - [ ] Replace `graph.mjs`'s JS triangle inner loop with a WASM call 54 + - [ ] Golden PNG snapshot diff in CI (catches any drift between variants) 55 + - [ ] Tile-binning + multi-worker pipeline (unlocks `threads`/`full` variants) 56 + 57 + ## Cross-browser support 58 + 59 + Graceful fallback — every current browser gets at minimum the baseline 60 + WASM (2-3× faster than today's JS). Better variants unlock automatically: 61 + 62 + | Feature | Chrome/Edge | Firefox | Safari | 63 + |---|---|---|---| 64 + | Baseline WASM | 57+ (2017) | 52+ (2017) | 11+ (2017) | 65 + | SharedArrayBuffer | 68+ (2018, isolated 91+) | 79+ (2020) | 15.2+ (Dec 2021) | 66 + | Atomics | same as SAB | same | same | 67 + | WASM SIMD (128-bit) | 91+ (2021) | 89+ (2021) | 16.4+ (Mar 2023) | 68 + | WASM threads | same as SAB | same | same | 69 + 70 + ### Hosting prerequisites for the parallel path 71 + 72 + The server must send these headers to enable `crossOriginIsolated`: 73 + 74 + Cross-Origin-Opener-Policy: same-origin 75 + Cross-Origin-Embedder-Policy: require-corp 76 + 77 + The docs route already does this (see `system/netlify/functions/docs.js`). 78 + Extending to the piece-hosting routes is a separate commit. If third-party 79 + iframes (YouTube, Spotify, arbitrary user URLs) need to still work, use 80 + `COEP: credentialless` instead of `require-corp` — same isolation, fewer 81 + embedding breakages. 82 + 83 + ## Pixel format 84 + 85 + All framebuffers use `uint32_t` per pixel packed as: 86 + 87 + (A << 24) | (R << 16) | (G << 8) | B 88 + 89 + Matches `graph3d.c`'s existing convention and web's `Uint32Array` view over 90 + a `Uint8ClampedArray`. The `AC_RAST_PACK(a,r,g,b)` macro constructs one. 91 + 92 + ## Depth buffer 93 + 94 + 32-bit float per pixel, parallel to the color buffer. Lower values = 95 + closer to camera (matching `graph3d.c` but inverted from WebGL's default 96 + — callers that pass post-perspective-divide Z in `[-1,+1]` should negate 97 + before calling if they want standard z-buffer semantics). 98 + 99 + Modes: 100 + 101 + - `AC_RAST_DEPTH_RW` — read + write (standard opaque pass) 102 + - `AC_RAST_DEPTH_READONLY` — read only (transparent / overlay pass) 103 + - `AC_RAST_DEPTH_NONE` — no test / no write (painter's-order fallback, 104 + also what pieces without a depth buffer at all use) 105 + 106 + ## Parallelism plan 107 + 108 + The rasterizer is designed so each triangle call is independent. A tile 109 + binner — not yet implemented here — will: 110 + 111 + 1. Split screen into 64×64 tiles 112 + 2. Transform all vertices on the main thread 113 + 3. Bucket each triangle into the tiles it covers 114 + 4. Dispatch tiles to a worker pool (Web Workers on web, pthreads on native) 115 + 5. Each worker rasterizes its tiles using `ac_rast_triangle` with a 116 + scissor rect set to the tile bounds 117 + 6. Main thread waits on a completion counter, flips buffers, presents 118 + 119 + Workers never touch each other's tiles, so no locking is needed beyond 120 + the submit/complete atomics. SharedArrayBuffer holds the color + depth 121 + backing store; workers write with normal pointer ops.