Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::render::GpuCompute Class Referenceabstract

GPU compute pipeline for non-rendering workloads (e.g. More...

#include <gpu_compute.hpp>

Classes

struct  WavenetLayerArraySpec
 One WaveNet layer-array's static shape. More...
struct  ReadbackResult
struct  AsyncStats
 Counters for the async path, incremented where the work actually happens: submits at the queue submit, map_resolves when the GPU map callback fires, expired/failed when a request completes with that status. More...
struct  CapabilityReport
 Runtime GPU capability/limit report for the compute device. More...
struct  DeviceSharingReport
struct  BenchmarkResult

Public Types

enum class  ReadbackStatus { Success , Expired , Failed }
using ReadbackCallback = std::function<void(const ReadbackResult&)>

Public Member Functions

virtual ~GpuCompute ()=default
virtual bool initialize_from_surface (GpuSurface &surface)=0
 Initialize from an existing GpuSurface's device (device sharing).
virtual bool initialize_standalone ()=0
 Initialize standalone (creates its own device, no rendering surface).
virtual bool initialize_from_device (void *wgpu_device)=0
 Initialize by adopting an already-created WebGPU device.
bool is_initialized () const
virtual bool device_lost () const =0
 True once the WebGPU device has been lost.
virtual std::uint64_t device_generation () const
 Monotonic device generation.
virtual void notify_device_lost ()=0
 Report that an ADOPTED device (initialize_from_device / initialize_from_surface) has been lost.
virtual void notify_device_lost (std::uint64_t generation)
virtual void post_device_lost (std::uint64_t generation)
 Publish a loss from a backend callback without touching readback queues or invoking user completions.
virtual bool compute_magnitude (const float *complex_pairs, float *magnitudes, uint32_t num_bins)=0
 Compute magnitude spectrum from interleaved complex float pairs.
virtual bool complex_multiply (const float *a, const float *b, float *result, uint32_t count)=0
 Element-wise complex multiply (frequency-domain convolution step).
virtual bool batch_magnitude (const float *complex_frames, float *magnitude_frames, uint32_t bins_per_frame, uint32_t num_frames)=0
 Batch spectral magnitude: processes multiple FFT frames in one dispatch.
virtual bool fft_forward (const float *complex_in, float *complex_out, uint32_t n)=0
 Forward DFT: X[k] = sum_n x[n] exp(-2*pi*i*k*n/N).
virtual bool fft_inverse (const float *complex_in, float *complex_out, uint32_t n)=0
 Inverse DFT, normalized by 1/N (so ifft(fft(x)) == x within tolerance).
virtual bool fft_forward_timed (const float *complex_in, float *complex_out, uint32_t n, double *gpu_compute_us)=0
 Forward FFT that also reports the TRUE GPU compute time (microseconds) of the transform passes, measured with compute-pass timestamp queries — i.e.
virtual bool prepare_convolution (uint32_t n, const float *ir_spec)=0
 Build the resident convolution plan for size n (power of two) and upload ir_spec — the interleaved-complex spectrum of the zero-padded IR (length 2*n, e.g.
virtual bool convolve (const float *in_complex, float *out_complex, uint32_t n)=0
 Run the fused convolution for a prepared n.
virtual bool prepare_convolution_batch (uint32_t n, const float *ir_spec, uint32_t batch)=0
 Batched fused convolution: batch independent length-n blocks convolved with the same resident IR, in ONE submit with ONE readback — amortizing the dominant round-trip across all blocks.
virtual bool convolve_batch (const float *in_complex, float *out_complex, uint32_t n, uint32_t batch)=0
virtual bool prepare_multi_convolution (uint32_t n, const float *ir_specs, uint32_t num_ir)=0
 Build the multi-IR plan for size n (power of two) and upload ir_specs — num_ir back-to-back interleaved-complex IR spectra (length 2*n*num_ir).
virtual bool multi_convolve (const float *in_complex, const float *pan_l, const float *pan_r, float *out_lr, uint32_t n, uint32_t num_ir)=0
 Run the multi-IR convolution for a prepared (n, num_ir).
virtual bool multi_convolve_timed (const float *in_complex, const float *pan_l, const float *pan_r, float *out_lr, uint32_t n, uint32_t num_ir, double *gpu_compute_us)=0
 Same as multi_convolve, but also reports the true GPU-busy time of the fused pass sequence (forward FFT → broadcast multiply → batched inverse FFT → pan-combine) in microseconds via gpu_compute_us, measured with a compute-pass timestamp query — excluding the input upload and the blocking readback.
virtual bool prepare_multi_fdl (uint32_t n, const float *ir_part_specs, uint32_t num_ir, uint32_t num_part)=0
 Build the partitioned-FDL plan for n = 2*block.
virtual bool multi_fdl_convolve (const float *in_block, const float *pan_l, const float *pan_r, float *out_block, uint32_t n, uint32_t num_ir)=0
 Run one block of partitioned-FDL convolution for a prepared (n, num_ir).
virtual bool prepare_spectral_stack (uint32_t n, uint32_t hop, uint32_t num_layers)=0
 Build the spectral-stack plan for FFT size n (power of two), STFT hop hop, and num_layers resident layer-spectra.
virtual bool spectral_stack_set_layer (uint32_t layer, const float *mag, const float *phase, uint32_t n, uint32_t num_layers)=0
 Upload one captured layer's magnitude (mag, n reals) and initial phase (phase, n reals) into the resident plan for (n, num_layers), activating it.
virtual bool spectral_stack_render (const float *layer_weights, uint32_t num_layers, float smear, float jitter, uint32_t rng_seed, float *frame_out, uint32_t n)=0
 Advance + smear + weighted-sum all resident layers and inverse-FFT to one real frame.
virtual bool matmul (const float *a, const float *b, float *c, uint32_t m, uint32_t k, uint32_t n)=0
 Dense matrix multiply C[M×N] = A[M×K] · B[K×N], all row-major f32.
virtual bool additive_synth (const float *partials, float *out, uint32_t num_partials, uint32_t num_samples, float sample_rate, float t0_samples)=0
 GPU additive synthesis: out[s] = Σ_p amp_p · sin(2π·freq_p·(t0+s)/sr + phase_p), one thread per output sample summing all partials.
virtual bool modal_strike (const float *modes, float *out, uint32_t num_modes, uint32_t num_samples, float sample_rate, float t0_samples)=0
 GPU struck modal synthesis: out[s] = Σ_m amp_m·exp(-decay_m·t)· sin(2π·freq_m·t + phase_m), t=(t0+s)/sr.
virtual bool granular_cloud (const float *grains, const float *source, float *out, uint32_t num_grains, uint32_t source_len, uint32_t num_samples)=0
 GPU granular synthesis ("grain cloud"): sum many Hann-windowed, pitch-shifted snippets of source into out, one GPU thread per output sample.
virtual bool dense_tanh (const float *input, const float *weights, const float *bias, float *output, uint32_t in_dim, uint32_t out_dim)=0
 GPU dense (fully-connected) layer with tanh activation: out[j] = tanh(Σ_i W[j][i]·x[i] + b[j]), one thread per output neuron.
virtual bool prepare_conv_stack (uint32_t channels, uint32_t kernel, const uint32_t *dilations, uint32_t num_layers, const float *weights, uint32_t weights_len, uint32_t block_size, float head_scale)=0
 Build the conv-stack plan: upload weights, allocate the per-layer device-resident activation buffers for block_size samples, and compile the fused pass pipeline.
virtual bool conv_stack_forward (const float *in_block, float *out_block, uint32_t block_size)=0
 Run the fused forward for one mono block (block_size samples in/out): input projection, the dilated gated conv layers, and the linear head, all in one submit.
virtual bool prepare_wavenet (const WavenetLayerArraySpec *arrays, uint32_t num_arrays, const float *weights, uint32_t weights_len, uint32_t block_size, float head_scale, uint32_t instance=0)=0
 Build the WaveNet plan: upload weights (flat, length weights_len, head_scale is the trailing scalar passed separately here), allocate the per-array/per-layer device-resident activation + head buffers for block_size samples, and compile the fused passes.
virtual bool wavenet_forward (const float *in_block, float *out_block, uint32_t block_size, uint32_t instance=0)=0
 Run the fused WaveNet forward for one mono block (block_size in/out): the per-array rechannel, dilated conv layers (+ condition mixin, activation, residual, head accumulate), head rechannels, and the final head_scale, all in one submit.
virtual bool prepare_wavenet_group (const WavenetLayerArraySpec *, uint32_t, const float *const *, uint32_t, const float *, uint32_t, uint32_t, uint32_t)
 Prepare a group of count independent WaveNet instances that share one shape (arrays) and run as one unit: weights[i] / head_scales[i] are instance i's blob and head_scale, laid out exactly as for prepare_wavenet().
virtual bool wavenet_forward_group (const float *const *, float *const *, uint32_t, uint32_t, uint32_t, double *gpu_busy_us=nullptr)
 Run one block for every instance of a prepared group: in_blocks[i] / out_blocks[i] are instance i's block_size samples, and count must equal the prepared count.
virtual bool wavenet_forward_batch (const float *const *in_blocks, float *const *out_blocks, const uint32_t *instances, uint32_t count, uint32_t block_size, double *gpu_busy_us=nullptr)
 Run one block for count independent WaveNet instances at once.
virtual uint64_t compute_magnitude_async (const float *complex_pairs, float *magnitudes, uint32_t num_bins, std::chrono::microseconds deadline, ReadbackCallback on_complete)=0
 Non-blocking counterpart of compute_magnitude().
virtual uint64_t convolve_batch_async (const float *in_complex, float *out_complex, uint32_t n, uint32_t batch, std::chrono::microseconds deadline, ReadbackCallback on_complete)=0
 Non-blocking counterpart of convolve_batch().
virtual std::size_t poll_readbacks ()=0
 Pump the device event queue once and fire the completions that are ready or past their deadline.
virtual std::size_t readbacks_in_flight () const =0
 Requests submitted whose completion has not fired yet.
virtual void set_max_readbacks_in_flight (std::size_t max_in_flight)=0
 Cap on concurrently in-flight async readbacks (default 4).
virtual AsyncStats async_stats () const =0
virtual void set_async_timing_enabled (bool)
 Opt the ASYNC convolution path into GPU-busy timing (default OFF).
virtual double last_gpu_busy_ns () const
 The most recent GPU-busy time of the async convolution shader, in nanoseconds (EMA-smoothed), or 0 when timing is disabled, unsupported, or the device quantized a small dispatch to 0 ns (Metal/Chrome do).
virtual const char * kernel_source (const char *label) const =0
 Built-in WGSL for a named pipeline (e.g.
virtual bool override_kernel_source (const char *label, const char *wgsl)=0
 Replace the WGSL of a named pipeline.
virtual CapabilityReport capabilities () const =0
 Query the compute device's backend, optional features, and limits.
virtual DeviceSharingReport verify_device_sharing (GpuSurface &surface)=0
 Test device sharing between Skia Graphite and compute pipelines.
virtual std::vector< BenchmarkResult > benchmark_magnitude (const std::vector< uint32_t > &sizes, int iterations=10)=0
 Benchmark magnitude computation at various sizes.
virtual std::vector< BenchmarkResult > benchmark_complex_multiply (const std::vector< uint32_t > &sizes, int iterations=10)=0
 Benchmark complex multiply at various sizes.

Static Public Member Functions

static std::unique_ptr< GpuCompute > create ()

Protected Attributes

std::atomic< bool > initialized_ {false}

Detailed Description

GPU compute pipeline for non-rendering workloads (e.g.

spectral processing).

Experimental API for background GPU compute work. It operates on a Dawn wgpu::Device shared with GpuSurface/SkiaSurface.

Design constraints:

  • Never runs in the audio callback (upload/readback latency is too high)
  • Operates on pre-allocated GPU buffers for batch processing
  • Separate from SkSL rendering shaders — this is WebGPU compute, not fragment

Intended use: offline or background batch processing of audio data (spectral analysis, batch convolution, spectrogram generation).

Member Typedef Documentation

◆ ReadbackCallback

using pulp::render::GpuCompute::ReadbackCallback = std::function<void(const ReadbackResult&)>

Member Enumeration Documentation

◆ ReadbackStatus

Enumerator
Success 

dest holds the full payload.

Expired 

Deadline passed before the map resolved; dest untouched.

Failed 

Map error, or the device went away before the map landed.

Constructor & Destructor Documentation

◆ ~GpuCompute()

virtual pulp::render::GpuCompute::~GpuCompute ( )
virtualdefault

Member Function Documentation

◆ additive_synth()

virtual bool pulp::render::GpuCompute::additive_synth ( const float * partials,
float * out,
uint32_t num_partials,
uint32_t num_samples,
float sample_rate,
float t0_samples )
pure virtual

GPU additive synthesis: out[s] = Σ_p amp_p · sin(2π·freq_p·(t0+s)/sr + phase_p), one thread per output sample summing all partials.

partials is num_partials × [freq(Hz), amp, phase(rad)] (3·num_partials floats). Thousands of partials parallelize cleanly — the basis for additive / spectral resynthesis. t0_samples is the absolute start sample (block continuity). Not real-time-safe. Returns false on invalid args.

◆ async_stats()

virtual AsyncStats pulp::render::GpuCompute::async_stats ( ) const
pure virtual

◆ batch_magnitude()

virtual bool pulp::render::GpuCompute::batch_magnitude ( const float * complex_frames,
float * magnitude_frames,
uint32_t bins_per_frame,
uint32_t num_frames )
pure virtual

Batch spectral magnitude: processes multiple FFT frames in one dispatch.

Input: frames of interleaved complex data, packed contiguously Output: frames of magnitude data, packed contiguously

◆ benchmark_complex_multiply()

virtual std::vector< BenchmarkResult > pulp::render::GpuCompute::benchmark_complex_multiply ( const std::vector< uint32_t > & sizes,
int iterations = 10 )
pure virtual

Benchmark complex multiply at various sizes.

◆ benchmark_magnitude()

virtual std::vector< BenchmarkResult > pulp::render::GpuCompute::benchmark_magnitude ( const std::vector< uint32_t > & sizes,
int iterations = 10 )
pure virtual

Benchmark magnitude computation at various sizes.

Returns results for each size in the vector.

◆ capabilities()

virtual CapabilityReport pulp::render::GpuCompute::capabilities ( ) const
pure virtual

Query the compute device's backend, optional features, and limits.

◆ complex_multiply()

virtual bool pulp::render::GpuCompute::complex_multiply ( const float * a,
const float * b,
float * result,
uint32_t count )
pure virtual

Element-wise complex multiply (frequency-domain convolution step).

Inputs: a, b — interleaved complex [re, im, re, im, ...] (2 * count floats each) Output: result — interleaved complex product (2 * count floats)

◆ compute_magnitude()

virtual bool pulp::render::GpuCompute::compute_magnitude ( const float * complex_pairs,
float * magnitudes,
uint32_t num_bins )
pure virtual

Compute magnitude spectrum from interleaved complex float pairs.

Input: complex_pairs — [re0, im0, re1, im1, ...] (2 * num_bins floats) Output: magnitudes — [mag0, mag1, ...] (num_bins floats, linear scale) Returns false if GPU dispatch fails.

◆ compute_magnitude_async()

virtual uint64_t pulp::render::GpuCompute::compute_magnitude_async ( const float * complex_pairs,
float * magnitudes,
uint32_t num_bins,
std::chrono::microseconds deadline,
ReadbackCallback on_complete )
pure virtual

Non-blocking counterpart of compute_magnitude().

Submits the dispatch and returns a request id; magnitudes receives num_bins floats when the completion reports Success. deadline is measured from this call. Returns 0 if the request was rejected (not initialized, invalid args, no callback) — the callback does NOT fire in that case.

◆ conv_stack_forward()

virtual bool pulp::render::GpuCompute::conv_stack_forward ( const float * in_block,
float * out_block,
uint32_t block_size )
pure virtual

Run the fused forward for one mono block (block_size samples in/out): input projection, the dilated gated conv layers, and the linear head, all in one submit.

Carries each layer's dilation history across calls (the activation window slides every block), so streaming blocks are continuous and bit-for-bit match the CPU reference run sample-continuously. Returns false if the plan was not prepared for block_size.

◆ convolve()

virtual bool pulp::render::GpuCompute::convolve ( const float * in_complex,
float * out_complex,
uint32_t n )
pure virtual

Run the fused convolution for a prepared n.

in/out are interleaved complex, length 2*n. Returns false if prepare_convolution(n) was not called, args are invalid, or dispatch fails.

◆ convolve_batch()

virtual bool pulp::render::GpuCompute::convolve_batch ( const float * in_complex,
float * out_complex,
uint32_t n,
uint32_t batch )
pure virtual

◆ convolve_batch_async()

virtual uint64_t pulp::render::GpuCompute::convolve_batch_async ( const float * in_complex,
float * out_complex,
uint32_t n,
uint32_t batch,
std::chrono::microseconds deadline,
ReadbackCallback on_complete )
pure virtual

Non-blocking counterpart of convolve_batch().

Same plan requirement (prepare_convolution_batch(n, ir_spec, batch) first) and same result: out_complex receives batch * n * 2 floats, already scaled by the 1/n inverse-FFT factor, when the completion reports Success. deadline is measured from this call. Returns 0 if the request was rejected (not initialized, no plan for n, batch mismatch, null pointers, no callback, or the in-flight cap is reached) — the callback does NOT fire in that case, and the caller counts a miss.

Multiple blocks may be in flight at once. The plan's compute buffers are SHARED across them, which is safe because the WebGPU queue executes submissions in order: submit N's copy into its readback buffer runs before submit N+1 overwrites the plan's input buffer. Only the mapped readback buffer is per-submit (one per request, from the staging pool) — a shared one is precisely what forces a blocking design.

◆ create()

std::unique_ptr< GpuCompute > pulp::render::GpuCompute::create ( )
static

◆ dense_tanh()

virtual bool pulp::render::GpuCompute::dense_tanh ( const float * input,
const float * weights,
const float * bias,
float * output,
uint32_t in_dim,
uint32_t out_dim )
pure virtual

GPU dense (fully-connected) layer with tanh activation: out[j] = tanh(Σ_i W[j][i]·x[i] + b[j]), one thread per output neuron.

W is row-major [out_dim × in_dim], b is [out_dim]. The core building block for GPU neural inference — NAM dense / output layers and LSTM gate evaluations are dense layers (the WaveNet path reuses the conv primitives). Not real-time-safe. Returns false on invalid args.

◆ device_generation()

virtual std::uint64_t pulp::render::GpuCompute::device_generation ( ) const
inlinevirtual

Monotonic device generation.

A generation changes at every initialize attempt, so an owner can tag an adopted-device loss notification and reject a callback from an older device after reinitialization.

◆ device_lost()

virtual bool pulp::render::GpuCompute::device_lost ( ) const
pure virtual

True once the WebGPU device has been lost.

Device loss is normal — a browser tab can lose its device at any time — so it is reported as state, not as an initialization failure. On loss every in-flight readback completes as Failed and is_initialized() goes false; the owner is expected to re-acquire a device and re-prepare_*.

◆ fft_forward()

virtual bool pulp::render::GpuCompute::fft_forward ( const float * complex_in,
float * complex_out,
uint32_t n )
pure virtual

Forward DFT: X[k] = sum_n x[n] exp(-2*pi*i*k*n/N).

Unnormalized. Returns false if n is not a power of two, exceeds the buffer limit, either pointer is null, or dispatch fails.

◆ fft_forward_timed()

virtual bool pulp::render::GpuCompute::fft_forward_timed ( const float * complex_in,
float * complex_out,
uint32_t n,
double * gpu_compute_us )
pure virtual

Forward FFT that also reports the TRUE GPU compute time (microseconds) of the transform passes, measured with compute-pass timestamp queries — i.e.

excluding upload and the blocking readback that dominates wall-clock. Sets *gpu_compute_us to -1 when timestamp queries are unavailable (capabilities().timestamp_query == false) or N == 1. Diagnostic/benchmark path. Returns false on the same conditions as fft_forward.

◆ fft_inverse()

virtual bool pulp::render::GpuCompute::fft_inverse ( const float * complex_in,
float * complex_out,
uint32_t n )
pure virtual

Inverse DFT, normalized by 1/N (so ifft(fft(x)) == x within tolerance).

Returns false if n is not a power of two, exceeds the buffer limit, either pointer is null, or dispatch fails.

◆ granular_cloud()

virtual bool pulp::render::GpuCompute::granular_cloud ( const float * grains,
const float * source,
float * out,
uint32_t num_grains,
uint32_t source_len,
uint32_t num_samples )
pure virtual

GPU granular synthesis ("grain cloud"): sum many Hann-windowed, pitch-shifted snippets of source into out, one GPU thread per output sample.

grains is num_grains × [onset(sample), duration(samples), source_pos(sample), pitch(ratio), amp]. Tens of thousands of grains parallelize across samples. Not real-time-safe. Returns false on bad args.

◆ initialize_from_device()

virtual bool pulp::render::GpuCompute::initialize_from_device ( void * wgpu_device)
pure virtual

Initialize by adopting an already-created WebGPU device.

wgpu_device is an opaque WGPUDevice, the same handle style as GpuSurface::dawn_device_handle(). Acquires the device's queue, reads its limits/features, installs the uncaptured-error and device-lost callbacks, and builds the compute pipelines.

This is the browser entry point. WebGPU is exposed on Window and DedicatedWorker only, so the worker's JS awaits navigator.gpu.requestAdapter()/requestDevice(), publishes the result as Module.preinitializedWebGPUDevice, and the C++ side takes it via emscripten_webgpu_get_device(). Every asynchronous step of device acquisition stays in JS: no Asyncify is used or needed.

Returns false on a null handle or pipeline-creation failure.

◆ initialize_from_surface()

virtual bool pulp::render::GpuCompute::initialize_from_surface ( GpuSurface & surface)
pure virtual

Initialize from an existing GpuSurface's device (device sharing).

Returns false if compute pipelines cannot be created.

◆ initialize_standalone()

virtual bool pulp::render::GpuCompute::initialize_standalone ( )
pure virtual

Initialize standalone (creates its own device, no rendering surface).

Useful for benchmarking and headless compute.

Native only. It requests the adapter and device and then pumps the event queue once; in a browser those futures resolve only after control returns to the JS event loop, so there is no correct in-line spelling of it. The browser build fails this call explicitly rather than returning a mysterious false — use initialize_from_device() there.

◆ is_initialized()

bool pulp::render::GpuCompute::is_initialized ( ) const
inline

◆ kernel_source()

virtual const char * pulp::render::GpuCompute::kernel_source ( const char * label) const
pure virtual

Built-in WGSL for a named pipeline (e.g.

"conv_bmul", "fft_stockham", "magnitude"). Returns nullptr for an unknown label. Pipeline labels are the ones passed to the internal create_pipeline().

◆ last_gpu_busy_ns()

virtual double pulp::render::GpuCompute::last_gpu_busy_ns ( ) const
inlinevirtual

The most recent GPU-busy time of the async convolution shader, in nanoseconds (EMA-smoothed), or 0 when timing is disabled, unsupported, or the device quantized a small dispatch to 0 ns (Metal/Chrome do).

0 means "no honest number", never an error — callers must render it as absent, not a stall.

◆ matmul()

virtual bool pulp::render::GpuCompute::matmul ( const float * a,
const float * b,
float * c,
uint32_t m,
uint32_t k,
uint32_t n )
pure virtual

Dense matrix multiply C[M×N] = A[M×K] · B[K×N], all row-major f32.

The foundational GPU primitive for neural inference (dense layers, LSTM gate matmuls) and matrix-heavy DSP. Not real-time-safe (uploads + blocks on readback). Returns false on invalid args / dispatch failure.

◆ modal_strike()

virtual bool pulp::render::GpuCompute::modal_strike ( const float * modes,
float * out,
uint32_t num_modes,
uint32_t num_samples,
float sample_rate,
float t0_samples )
pure virtual

GPU struck modal synthesis: out[s] = Σ_m amp_m·exp(-decay_m·t)· sin(2π·freq_m·t + phase_m), t=(t0+s)/sr.

modes is num_modes × [freq(Hz), amp, decay(1/s), phase(rad)] (4·num_modes floats) — a struck/plucked modal object (bell / string / body) as a bank of decaying resonant modes. Thousands of modes parallelize. Not real-time-safe.

◆ multi_convolve()

virtual bool pulp::render::GpuCompute::multi_convolve ( const float * in_complex,
const float * pan_l,
const float * pan_r,
float * out_lr,
uint32_t n,
uint32_t num_ir )
pure virtual

Run the multi-IR convolution for a prepared (n, num_ir).

in_complex is one zero-padded interleaved-complex input block (length 2*n). pan_l / pan_r are per-room linear gains (length num_ir) applied in the GPU reduce. out_lr receives the stereo result: out_lr[0..n) = L, out_lr[n..2n) = R (length 2*n). Returns false if prepare_multi_convolution was not called for (n, num_ir), args are invalid, or dispatch fails.

◆ multi_convolve_timed()

virtual bool pulp::render::GpuCompute::multi_convolve_timed ( const float * in_complex,
const float * pan_l,
const float * pan_r,
float * out_lr,
uint32_t n,
uint32_t num_ir,
double * gpu_compute_us )
pure virtual

Same as multi_convolve, but also reports the true GPU-busy time of the fused pass sequence (forward FFT → broadcast multiply → batched inverse FFT → pan-combine) in microseconds via gpu_compute_us, measured with a compute-pass timestamp query — excluding the input upload and the blocking readback.

Sets *gpu_compute_us = -1 when timing is unavailable (capabilities().timestamp_query == false). Diagnostic / roofline path: comparing this against the call's wall time separates a GPU-busy (bandwidth/compute-bound) kernel from an upload/readback-bound one. Mirrors fft_forward_timed.

◆ multi_fdl_convolve()

virtual bool pulp::render::GpuCompute::multi_fdl_convolve ( const float * in_block,
const float * pan_l,
const float * pan_r,
float * out_block,
uint32_t n,
uint32_t num_ir )
pure virtual

Run one block of partitioned-FDL convolution for a prepared (n, num_ir).

in_block is block (= n/2) real input samples; pan_l/pan_r are per-room linear gains (length num_ir). out_block receives the stereo output block: out_block[0..block) = L, out_block[block..2*block) = R (length 2*block = n). The plan carries the delay line and the previous block across calls. Returns false if prepare_multi_fdl was not called for (n, num_ir), args are invalid, or dispatch fails.

◆ notify_device_lost() [1/2]

virtual void pulp::render::GpuCompute::notify_device_lost ( )
pure virtual

Report that an ADOPTED device (initialize_from_device / initialize_from_surface) has been lost.

A device-lost callback can only be installed on the device descriptor, i.e. at creation, so a device this object did not create cannot observe its own loss — its owner must say so. In the browser that owner is the worker's JS, which awaits device.lost. Idempotent. A device created by initialize_standalone() observes loss on its own and needs no call. The owner must call this on the instance's worker strand. Backend callbacks must call post_device_lost() to publish a transition and never drain readbacks directly.

◆ notify_device_lost() [2/2]

virtual void pulp::render::GpuCompute::notify_device_lost ( std::uint64_t generation)
inlinevirtual

◆ override_kernel_source()

virtual bool pulp::render::GpuCompute::override_kernel_source ( const char * label,
const char * wgsl )
pure virtual

Replace the WGSL of a named pipeline.

MUST be called before the pipelines are built (i.e. before initialize_*), because pipelines are compiled once at initialization. Returns false for an unknown label, null WGSL, or a call made after initialization — a late override is refused rather than silently ignored.

◆ poll_readbacks()

virtual std::size_t pulp::render::GpuCompute::poll_readbacks ( )
pure virtual

Pump the device event queue once and fire the completions that are ready or past their deadline.

Never blocks. Returns the number of requests whose completion has not fired yet. Re-entrant calls (from inside a completion) are a no-op. Detached late-map cleanup is not included.

◆ post_device_lost()

virtual void pulp::render::GpuCompute::post_device_lost ( std::uint64_t generation)
inlinevirtual

Publish a loss from a backend callback without touching readback queues or invoking user completions.

The owner strand applies the transition on its next operation. A backend with asynchronous callbacks must override this method with a publish-only implementation. The compatibility default forwards synchronously to notify_device_lost() for existing downstream implementations, so it is only safe on the owner strand; new backends must override it before publishing from a callback thread.

◆ prepare_conv_stack()

virtual bool pulp::render::GpuCompute::prepare_conv_stack ( uint32_t channels,
uint32_t kernel,
const uint32_t * dilations,
uint32_t num_layers,
const float * weights,
uint32_t weights_len,
uint32_t block_size,
float head_scale )
pure virtual

Build the conv-stack plan: upload weights, allocate the per-layer device-resident activation buffers for block_size samples, and compile the fused pass pipeline.

Returns false on invalid args, if the buffers exceed a device limit, or on GPU failure. Call once before conv_stack_forward(block_size).

◆ prepare_convolution()

virtual bool pulp::render::GpuCompute::prepare_convolution ( uint32_t n,
const float * ir_spec )
pure virtual

Build the resident convolution plan for size n (power of two) and upload ir_spec — the interleaved-complex spectrum of the zero-padded IR (length 2*n, e.g.

produced by fft_forward of the padded IR). Call once before convolve(n). Returns false on invalid args / GPU failure.

◆ prepare_convolution_batch()

virtual bool pulp::render::GpuCompute::prepare_convolution_batch ( uint32_t n,
const float * ir_spec,
uint32_t batch )
pure virtual

Batched fused convolution: batch independent length-n blocks convolved with the same resident IR, in ONE submit with ONE readback — amortizing the dominant round-trip across all blocks.

in/out hold batch back-to-back interleaved-complex blocks (length 2*n*batch). Returns false if prepare_convolution_batch(n, _, batch) was not called.

◆ prepare_multi_convolution()

virtual bool pulp::render::GpuCompute::prepare_multi_convolution ( uint32_t n,
const float * ir_specs,
uint32_t num_ir )
pure virtual

Build the multi-IR plan for size n (power of two) and upload ir_specs — num_ir back-to-back interleaved-complex IR spectra (length 2*n*num_ir).

Call once before multi_convolve(n, num_ir). Returns false on invalid args / GPU failure.

◆ prepare_multi_fdl()

virtual bool pulp::render::GpuCompute::prepare_multi_fdl ( uint32_t n,
const float * ir_part_specs,
uint32_t num_ir,
uint32_t num_part )
pure virtual

Build the partitioned-FDL plan for n = 2*block.

ir_part_specs holds num_ir * num_part back-to-back interleaved-complex partition spectra (each length 2*n): room r partition p at offset (r*num_part + p)*2n. Each partition spectrum is the forward FFT of that IR block zero-padded to n. Call once before multi_fdl_convolve(n, num_ir). Returns false on invalid args or if the resident IR spectra exceed the storage-buffer limit.

◆ prepare_spectral_stack()

virtual bool pulp::render::GpuCompute::prepare_spectral_stack ( uint32_t n,
uint32_t hop,
uint32_t num_layers )
pure virtual

Build the spectral-stack plan for FFT size n (power of two), STFT hop hop, and num_layers resident layer-spectra.

hop fixes the per-bin phase advance (2*pi*k*hop/n) applied each render. Allocates the resident magnitude / phase buffers (zeroed: every layer starts inactive/silent) and the combine + inverse-FFT pipeline. Call once before spectral_stack_render. Returns false on invalid args, if the resident buffers would exceed the device storage-buffer binding limit, or on GPU failure.

◆ prepare_wavenet()

virtual bool pulp::render::GpuCompute::prepare_wavenet ( const WavenetLayerArraySpec * arrays,
uint32_t num_arrays,
const float * weights,
uint32_t weights_len,
uint32_t block_size,
float head_scale,
uint32_t instance = 0 )
pure virtual

Build the WaveNet plan: upload weights (flat, length weights_len, head_scale is the trailing scalar passed separately here), allocate the per-array/per-layer device-resident activation + head buffers for block_size samples, and compile the fused passes.

Returns false on invalid args (channels > 64, condition_size != 1, an array-chaining shape mismatch, a weight-count mismatch, a zero dilation, a layer whose causal reach dilation*(kernel-1) plus block_size exceeds 2^20 samples, or a history buffer larger than the device can bind) or GPU failure. The reach bound is checked before anything is allocated, so an untrusted model cannot stall preparation. Call once before wavenet_forward(block_size).

instance selects an independent plan slot on THIS device: two instances (e.g. stereo channels) prepared with the same block_size get separate device-resident buffers + dilation history but share the device, queue, and compiled pipelines — so a stereo plugin needs one device, not one per channel. Instances are identified by (block_size, instance); the default 0 preserves the single-stream behavior.

◆ prepare_wavenet_group()

virtual bool pulp::render::GpuCompute::prepare_wavenet_group ( const WavenetLayerArraySpec * ,
uint32_t ,
const float *const * ,
uint32_t ,
const float * ,
uint32_t ,
uint32_t ,
uint32_t  )
inlinevirtual

Prepare a group of count independent WaveNet instances that share one shape (arrays) and run as one unit: weights[i] / head_scales[i] are instance i's blob and head_scale, laid out exactly as for prepare_wavenet().

All instances live in shared device buffers and every pass dispatches the whole group at once, so the per-block GPU command count stays that of ONE instance however many instances run — the shape to use for many plugin instances of the same model architecture. Groups are identified by (block_size, group), separately from prepare_wavenet() plans; re-preparing a group replaces it and resets its history. Returns false on invalid args (the prepare_wavenet() shape rules apply), on a group too large for 32-bit buffer sizes, or on GPU failure. The default implementation reports the capability as unavailable.

◆ readbacks_in_flight()

virtual std::size_t pulp::render::GpuCompute::readbacks_in_flight ( ) const
pure virtual

Requests submitted whose completion has not fired yet.

A request whose deadline fired before its map resolved is removed from this count after its Expired completion, but its detached map cleanup still consumes the admission cap until the late map callback arrives or bounded cleanup grace expires.

◆ set_async_timing_enabled()

virtual void pulp::render::GpuCompute::set_async_timing_enabled ( bool )
inlinevirtual

Opt the ASYNC convolution path into GPU-busy timing (default OFF).

When on, convolve_batch_async() occasionally brackets its compute passes with a timestamp query and reads the result back through the SAME non-blocking readback machinery (never a blocking map), so the shader's own execution time becomes observable without stalling the audio path. Default-off is a hard guarantee: with timing disabled the async path is byte-for-byte what it was, so the native realtime audio path pays and risks nothing. The browser GPU demo turns it on to show honest GPU-compute-vs-round-trip.

◆ set_max_readbacks_in_flight()

virtual void pulp::render::GpuCompute::set_max_readbacks_in_flight ( std::size_t max_in_flight)
pure virtual

Cap on concurrently in-flight async readbacks (default 4).

Admission control, not a hint: at the cap, an async submit is REJECTED (returns 0) instead of queueing. Without it a stalled or throttled GPU — a backgrounded browser tab is the ordinary case — would grow staging memory by one buffer per audio block forever. A rejected submit is a miss, which the caller already knows how to route (MissPolicy).

◆ spectral_stack_render()

virtual bool pulp::render::GpuCompute::spectral_stack_render ( const float * layer_weights,
uint32_t num_layers,
float smear,
float jitter,
uint32_t rng_seed,
float * frame_out,
uint32_t n )
pure virtual

Advance + smear + weighted-sum all resident layers and inverse-FFT to one real frame.

layer_weights (length num_layers) scales each layer (drive it from a Morph control). smear (0..1) circular box-blurs each layer's magnitude across frequency; jitter (0..1) adds conjugate-symmetric phase wander seeded by rng_seed. The phase advance is conjugate-symmetric so frame_out (n reals) is real. Returns false if the plan was not prepared for (n, num_layers), args are invalid, or dispatch fails.

◆ spectral_stack_set_layer()

virtual bool pulp::render::GpuCompute::spectral_stack_set_layer ( uint32_t layer,
const float * mag,
const float * phase,
uint32_t n,
uint32_t num_layers )
pure virtual

Upload one captured layer's magnitude (mag, n reals) and initial phase (phase, n reals) into the resident plan for (n, num_layers), activating it.

The phase then persists and advances on the GPU each render. Returns false if prepare_spectral_stack(n, num_layers) was not called, args are invalid, or dispatch fails.

◆ verify_device_sharing()

virtual DeviceSharingReport pulp::render::GpuCompute::verify_device_sharing ( GpuSurface & surface)
pure virtual

Test device sharing between Skia Graphite and compute pipelines.

Creates render targets and compute buffers on the same device, submits from both, checks for corruption.

◆ wavenet_forward()

virtual bool pulp::render::GpuCompute::wavenet_forward ( const float * in_block,
float * out_block,
uint32_t block_size,
uint32_t instance = 0 )
pure virtual

Run the fused WaveNet forward for one mono block (block_size in/out): the per-array rechannel, dilated conv layers (+ condition mixin, activation, residual, head accumulate), head rechannels, and the final head_scale, all in one submit.

Carries each layer's dilation history across calls so streaming blocks match the CPU reference run sample-continuously. Returns false if prepare_wavenet was not called for (block_size, instance). instance selects the plan slot prepared above (default 0).

◆ wavenet_forward_batch()

virtual bool pulp::render::GpuCompute::wavenet_forward_batch ( const float *const * in_blocks,
float *const * out_blocks,
const uint32_t * instances,
uint32_t count,
uint32_t block_size,
double * gpu_busy_us = nullptr )
inlinevirtual

Run one block for count independent WaveNet instances at once.

Each instances[i] must have been prepared with block_size, and an instance may appear at most once. Every instance's passes are encoded into ONE command buffer, submitted once, and the count output blocks come back through ONE readback map — so the fixed submit + map round trip is paid once per block for the whole set instead of once per instance. Each instance's output and dilation history are exactly what count separate wavenet_forward() calls would produce. When gpu_busy_us is non-null it receives the GPU-busy microseconds spanning the batch, or -1 when the device has no timestamp queries. Returns false on an unprepared or repeated instance; the Dawn implementation validates the whole set before encoding, so a rejected batch advances no instance's history. Blocks on the readback like wavenet_forward(): run it on a GPU worker or offline, never on the audio thread. The default implementation calls wavenet_forward() per instance.

◆ wavenet_forward_group()

virtual bool pulp::render::GpuCompute::wavenet_forward_group ( const float *const * ,
float *const * ,
uint32_t ,
uint32_t ,
uint32_t ,
double * gpu_busy_us = nullptr )
inlinevirtual

Run one block for every instance of a prepared group: in_blocks[i] / out_blocks[i] are instance i's block_size samples, and count must equal the prepared count.

One submit, one readback. Each instance's output is bit-identical to running it alone through wavenet_forward(). When gpu_busy_us is non-null it receives the GPU-busy microseconds of the block, or -1 without timestamp queries. Blocks on the readback: GPU worker or offline only.

Member Data Documentation

◆ initialized_

std::atomic<bool> pulp::render::GpuCompute::initialized_ {false}
protected

The documentation for this class was generated from the following file:
  • /home/runner/work/pulp/pulp/core/render/include/pulp/render/gpu_compute.hpp