← All decisions

Centralize WebGPU device acquisition in one shared module (gpu_ffi.mjs), not per-FFI-file

webgpucsgpu-deviceconcurrency

Context

Adding a second cs module (entity_batch) alongside the first (vector2_batch) reproducibly crashed/hung the whole gleeunit process (native futex/glibc pthread_mutex errors) whenever both modules' tests ran together. Root-caused by isolating entity_batch alone (stable) vs. combined with vector2_batch (broken every time, not flaky): each FFI file had its own local fallback to acquire a GPU instance, so two modules meant two independent native GPU instances in one process.

Options considered

Leave each FFI file with its own local GPU-acquisition fallback — rejected
This was the bug -- confirmed by the isolation test.
Extract a single shared module (gpu_ffi.mjs) exporting one getDevice(), imported by every FFI file needing GPU access — chosen
ES module caching (same resolved import path -> same module instance) makes this a true process-wide singleton for free, no extra synchronization code needed.

Decision

Created src/glemy/gpu_ffi.mjs as the one place that acquires a GPUDevice; every cs (and later render) FFI file imports getDevice() from it instead of acquiring its own.

Verification

Ran the previously-reliably-broken combined test suite (vector2_batch + entity_batch together) 15 times after the fix: 15/15 clean, versus consistent failures before.

Consequences

This is also the foundation render needs to eventually share GPU-resident buffers with cs without a CPU roundtrip (development plan decision #6) -- both will call the same getDevice().

References