|
| | WebGpuConvolver (uint32_t depth=kDefaultDepth) |
| | ~WebGpuConvolver () |
| | WebGpuConvolver (const WebGpuConvolver &)=delete |
| WebGpuConvolver & | operator= (const WebGpuConvolver &)=delete |
| bool | prepare (render::GpuCompute *gpu, int sample_rate, int block, int channels, const float *ir, int ir_len) |
| | Allocate scratch, transform the IR on the CPU, and upload the resident IR spectrum.
|
| bool | submit (uint32_t seq, const float *in_planar, int frames, std::chrono::microseconds deadline) |
| | Issue one block.
|
| uint32_t | collect (const Sink &sink) |
| | Harvest.
|
| bool | prepared () const |
| bool | device_lost () const |
| | Device loss is normal browser behavior, not an init failure: the owner is expected to re-acquire a device and prepare() again.
|
| uint32_t | fft_size () const |
| uint32_t | depth () const |
| uint32_t | in_flight () const |
| uint64_t | submits () const |
| uint64_t | resolves () const |
| | maps that landed with a usable payload
|
| uint64_t | expired () const |
| | deadline passed before the map resolved
|
| uint64_t | failed () const |
| | map error, device loss, or non-finite payload
|
Submit/collect FFT convolver for a single-threaded event loop.
Same math as GpuConvolver (zero-padded complex block → fused GPU forward-FFT / complex-multiply by the resident IR spectrum / inverse-FFT → overlap-add carry), with two structural differences that a browser forces:
- NO GPU READBACK IN prepare(). GpuConvolver builds the IR spectrum with the BLOCKING GpuCompute::fft_forward(). That call pumps the device event queue until the map resolves — on a browser's event loop the pump IS the loop, so it can never resolve its own map and would simply burn the deadline. The IR transform is a one-time offline cost, so it is done on the CPU (signal::FftT) and handed to prepare_convolution_batch(), which is upload-only. One readback deleted from the critical path.
- NO BLOCKING PER BLOCK. submit() issues convolve_batch_async() and returns; collect() pumps GpuCompute::poll_readbacks() exactly ONCE (never spins) and drains whatever landed. Up to depth blocks may be in flight, each with its own scratch slot.
THE OVERLAP-ADD CARRY IS SEQUENTIAL STATE and is therefore applied in collect(), in completion (== submission) order — never at submit time, where blocks may be issued out of the order their results land in. A block that Expires, Fails, or reads back non-finite does NOT land: it is reported to the sink with ok == false and the carry is NOT mutated (mirroring GpuConvolver::process_block, which on a failed readback emits silence and deliberately leaves the carry alone — a stale result added to the carry would poison every later block of that channel).
This class carries NO CPU fallback. In the browser the safety net lives in the plugin, on the audio side of the ring, because the GPU work runs in a DedicatedWorker with its own wasm heap — a fallback here would be in the wrong linear memory and, worse, on the wrong thread.
Not real-time-safe: submit/collect from the GPU worker, never from the audio callback.