Skip to content

Paced shared-memory convolution probe

This source-build diagnostic drives the public GpuAudioTransport and GpuConvolver with a stereo, 257-tap convolution. The transport's worker owns GPU submission and completion service. A separate thread supplies paced audio blocks and never calls pump(). The probe checks the delivered stream against independent direct-double convolution after the timed interval, including latency-aligned CPU fallback.

Configure a Release Apple Silicon build with the pinned provider and these options:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 \
  -DPULP_BUILD_TESTS=ON -DPULP_ENABLE_GPU=ON \
  -DPULP_GPU_AUDIO_EXACT_PROVIDER_PROOF=ON \
  -DPULP_GPU_AUDIO_ENABLE_EXPERIMENTAL_SHARED_IO_CONVOLVER=ON
tools/ci/governed-build.sh cmake --build build \
  --target pulp-gpu-shared-io-paced-convolution-probe
ctest --test-dir build -R '^pulp-gpu-shared-io-paced-convolution-probe$' \
  --output-on-failure

SDK-only builds can use the same provider options with PULP_BUILD_TESTS=OFF. Production pulp-gpu-audio initializes the expected Dawn revision independently of test registration. Exact proof validates the manifest, retained provider archive, extracted headers/libraries, and generation receipt at configure time and again before building the library. A mismatch fails the build. Configure and library prelink receipts are under build/gpu-audio-provider-identity/; probe executable binding receipts remain separate evidence when tests are built. The experimental convolver option is still required to enable its shared path. With exact proof off, the expected revision remains empty: providers may check header/procedure-table agreement, but no manifest authentication is claimed. These build checks establish provider identity, not successful GPU delivery or a realtime performance claim.

Run a bounded diagnostic, choosing a new output directory:

build/test/pulp-gpu-shared-io-paced-convolution-probe \
  --frames=32 --lead=2 --blocks=4096 --warmup=64 \
  --output-dir=/absolute/path/to/new-capture

Supported frames are 32, 64, and 128; lead is 1, 2, 4, or 8 blocks. The sample rate is 48 kHz and physical provider slots remain two. --wake-on-write enables the transport's existing semaphore notification instead of its default polling worker. Completion service remains the provider's default ProcessEvents policy. The lower-level provider probe can compare this with --completion-policy=wait-any or --completion-policy=timed-wait-any; those policies retain each submission's Dawn Future handles and wait on the serialized non-realtime dispatcher. timed-wait-any requests Dawn's TimedWaitAny instance feature and accepts --completion-wait-ns=N as a dispatcher wait bound. Pop-error-scope and device-lost callbacks still use AllowProcessEvents and are explicitly pumped before terminal completion is published. These policies keep Dawn calls off the audio callback and do not promise hard realtime behavior or GPU scheduling priority. The maximum is 100,000 measured blocks per invocation. The probe keeps the stereo input, output, and per-callback records in memory so that it can emit a complete CSV; the bound supports an approximately 67-second run at 48 kHz and 32 frames without turning the diagnostic into a multi-hundred-megabyte or unbounded allocation. A million blocks would be about 11 minutes at that period, but longer campaigns should use a streaming harness rather than increasing this in-memory limit.

receipt.json contains configuration, numerical failures, callback overruns, late callback starts, and the transport's miss-counter delta. blocks.csv preserves every callback position, source position, scheduled time, observed callback begin/end, miss-counter delta, and maximum numerical error. Warmup and initial pipeline priming are retained but excluded from measured-block counts. No file output or numerical-oracle work occurs during the paced interval.

Exit 0 means the stream passed the numerical oracle and the worker made GPU progress. It does not mean that every deadline was met. Exit 1 is a completed failed check; exit 2 is invalid configuration, unavailable shared provider, or an execution/artifact error. --negative-control corrupts one measured output sample after processing; the verifier requires exactly one failed block and exit 1. Unavailability cannot satisfy that negative control.

This is a simulated callback driven by a non-realtime thread, not an audio device or host acceptance test. Callback begin/end are external CPU clock observations, not GPU timestamps. Miss-counter deltas are not a substitute for the full per-block terminal/disposition trace. Retain source revision, binary and provider hashes, build flags, machine/load/thermal context alongside these artifacts before making comparisons. The receipt deliberately leaves the performance verdict unassigned; matched staged/shared trials and host tests remain separate requirements.