import type { Framebuffer } from "../engine/render/Framebuffer" import type { RenderConfig } from "../engine/render/RenderConfig" import { renderBand, type Scene } from "./renderScene" /** One-time setup: shared framebuffer + control/param buffers, the (cloned) * scene, this worker's row band, and its index into the per-worker times array. */ type Init = { colorSAB: SharedArrayBuffer depthSAB: SharedArrayBuffer width: number height: number scene: Scene band: [number, number] config: RenderConfig skyStep: number ctrlSAB: SharedArrayBuffer camSAB: SharedArrayBuffer vpSAB: SharedArrayBuffer visSAB: SharedArrayBuffer timesSAB: SharedArrayBuffer index: number } const FRAME = 0 const DONE = 1 const VIS = 2 const ctx = globalThis as unknown as { addEventListener: (type: "message", handler: (e: { data: Init }) => void) => void } ctx.addEventListener("message", (e) => { const m = e.data const fb: Framebuffer = { width: m.width, height: m.height, color: new Uint32Array(m.colorSAB), depth: new Float32Array(m.depthSAB), } const ctrl = new Int32Array(m.ctrlSAB) const cam = new Float64Array(m.camSAB) const vp = new Float32Array(m.vpSAB) const vis = new Int32Array(m.visSAB) const times = new Float64Array(m.timesSAB) const { scene, band, config, skyStep, index } = m // Lock-free frame loop: block until main bumps the frame counter, render this // band, record the band time, and signal done. No messages per frame. let last = 0 for (;;) { Atomics.wait(ctrl, FRAME, last) last = Atomics.load(ctrl, FRAME) const t0 = performance.now() const camera = { position: { x: cam[0], y: cam[1], z: cam[2] }, yaw: cam[3], pitch: cam[4], fov: cam[5] } const count = Atomics.load(ctrl, VIS) const visible = [...vis.subarray(0, count)] renderBand(fb, scene, camera, vp, visible, config, skyStep, cam[6], band[0], band[1]) times[index] = performance.now() - t0 Atomics.add(ctrl, DONE, 1) } })