GPU audio reliability on macOS¶
Status: experimental, default-OFF. This page states what Pulp's GPU-audio runtime guarantees on macOS today, and where each guarantee stops. Work that is planned but not implemented is marked Coming and is not part of the current guarantee.
Read the GPU audio runtime guide first for the transport model. This page is about failure: what happens to audio when the GPU is slow, hung or lost, how a host finds out, and what has been validated in real DAWs.
The five reliability claims, at their current scope¶
Each claim below is worded at the scope Pulp has proven. The work that would widen it is listed under each claim.
1. The audio callback never waits for the GPU¶
On the GpuAudioTransport path, the audio callback never waits on, locks
against, allocates for, or calls into the GPU. All provider work runs on a
non-realtime worker or service thread, and every wait there is bounded. The
bounds are not all audio-sized:
- the staged
GpuConvolverworker does not wait on the GPU when a transport hosts it with a lead of two or more blocks. Called directly, or with a one-block lead, it waits at most one block period minus its CPU cost. TheStagedBlockingescape hatch keeps the older in-block readback wait of up to 2 s; GpuMultiConvolverand the spectral classes still read back synchronously, with the same 2 s bound;- the Dawn shared-I/O provider bounds each drain at 15 s. The same bound covers the drain at the transport's offline fence (on the host's offline render thread), provider creation, and error-scope waits after a failed submission.
The callback is unaffected by these waits, because it reads a result a fixed number of blocks later and falls back when the result is not there.
Not covered: nodes used outside the transport, and the explicitly offline
process_offline() path, are synchronous. Keep them off the audio thread.
GpuNeuralRealtimeNode's CPU shadow may run on the audio callback, depending
on its shadow policy (see shadow policy). That is CPU work on
the callback, not GPU work.
GpuAudioNode::release_bounded(budget_ns) bounds teardown only on
GpuWaveNetRealtimeNode. A provider that cannot drain within the budget has
its device destroyed and stays owned (quarantined) until a later release
completes it, and the call returns false. Every other node keeps the default,
which holds nothing and returns true, so its teardown is not bounded.
Coming: bounded teardown for GpuNeuralRealtimeNode and the convolvers.
2. Device loss and hung providers are detected and fenced¶
Pulp's audio GPU provider owns its own Dawn device; it does not share the editor's render device. Today:
- Loss on the shared-I/O path.
GpuWaveNetRealtimeNode, the GPU path ofGpuNeuralRealtimeNode(WaveNet is the only family with one) and the shared-I/OGpuConvolversession detect device loss through the provider's loss query, latch it and fence. - Loss on the staged path. The staged
GpuConvolverlatchesProviderLostand routes every later block to its CPU convolution, so the output continues exactly.GpuMultiConvolverrefuses a lost device and is silent, as itsSilencepolicy declares.GpuStft,GpuSpectralStack,GpuSpectralFreezeandGpuSpectralMorphrefuse a borrowed device that is lost or has a new generation, and write a silent frame. - Hang.
GpuWaveNetRealtimeNodeand the GPU path ofGpuNeuralRealtimeNoderun a completion watchdog. Each latchesProviderUnresponsivewhen the provider completes nothing forConfig::watchdog_blockscallback blocks while work is in flight. The default is 200 ms of blocks (150 at 64 frames and 48 kHz), orcapacity - 1if that is larger. OnGpuWaveNetRealtimeNode, a worker that is starved while its provider keeps completing is tested not to be reported as unresponsive. - Slow but alive. The staged
GpuConvolvertrips a circuit breaker after a run of late results and moves to the CPU, reportingSustainedDeadlineMisses. - Fence. On the stamped shared-I/O nodes (both WaveNet nodes and the shared
GpuConvolver), any latched failure fences the current epoch. A stale or late GPU result is never delivered. - Recovery is manual by default. After a fence or a loss, audio continues
on the CPU path where the node has one, and on silence where it does not.
Under the default
GpuAudioRecoveryPolicy::Manual, GPU delivery stays off until the host releases and prepares the node again. The staged breaker is the one exception: it probes a slow, live GPU and re-enables it. - Opt-in re-acquire,
GpuWaveNetRealtimeNodeonly. WithGpuAudioTransport::Config::recovery_policy = AutoReacquire, the transport's worker releases a fencedGpuWaveNetRealtimeNode's provider, prepares a new one and opens a new delivery epoch on the same stream, while the callback stays on its fallback. An offline fence is never re-acquired. For any other node the transport'scapability_report().recovery_policyreadsManualwhatever is configured, and the node stays fenced. See re-acquire a lost provider. - Warm providers. A host-owned
GpuAudioProviderPool(GpuWaveNetRealtimeNode::Config::provider_pool) keeps released providers forkeep_warm_ns(30 s by default), so a laterprepare()of the same program borrows one instead of creating a device.
Not covered:
- a driver or process crash;
- render-device loss. The editor's render device is not linked to the audio provider. Render-owner loss propagation, resource replay, teardown callback suppression and authenticated render-loss DAW continuity are gated on the Vellum render-owner seam and are not proven;
- a hung provider behind the convolvers: only the two WaveNet GPU paths run a watchdog.
Coming: automatic re-acquire for GpuNeuralRealtimeNode and the
convolvers.
3. A missed GPU block is substituted without waiting¶
A block whose GPU result is not ready by its deadline is substituted from a continuously primed, latency-aligned CPU path without waiting, for these nodes:
GpuConvolver: the callback's continuously fed CPU convolution covers a block the worker did not deliver. On the staged path the worker also routes each late, failed, non-finite or device-lost block to its own CPU convolution, so the worker's output is the exact continuation of the convolution;GpuNeuralRealtimeNode: a built-in CPU shadow for every family (WaveNet, TCN, SSM, LSTM, GRU). Families without a GPU kernel run the same contract CPU-only and report it. Under theWorkershadow policy, a block that both the GPU and the shadow miss is silent and is counted inshadow_double_miss_blocks. UnderNone, a realtime GPU miss is silent;GpuWaveNetRealtimeNode: the CPU shadow your derived class supplies, oncecpu_fallback_prepared()is true.
GpuMultiConvolver declares Silence on a miss. The spectral classes
(GpuStft, GpuSpectralFreeze, GpuSpectralMorph, GpuSpectralMaskSession,
GpuSpectralStack) are not GpuAudioNodes: they are synchronous, are not
hosted by the transport, and have no miss policy or status.
4. Failures are reported through the versioned status¶
Missed deadlines, fallback use, device loss, unresponsive-provider detection,
provider state and the first latched recovery reason are reported through
GpuAudioStatus (schema v4) for every transport-hosted node. Read them from
GpuAudioTransport::status_snapshot(). See reading health
for which fields the transport fills.
Not yet:
- Trace. The status is not emitted as Perfetto or JSONL records. The trace carries per-block terminal and delivery dispositions; see the tracing guide.
- Control protocol and MCP. There is no read operation for GPU-audio health
yet.
dev.pulp.gpu/health.read@1reports rendering health, not audio.
Coming: a GPU-audio health read operation for the control protocol and MCP.
5. The contracts carry no backend types¶
The node, transport, status, capability and program contracts contain no Dawn,
Metal or other backend types. On macOS there is one GPU provider, Dawn.
GpuAudioProvider::Cpu labels a node that runs CPU-only; it is not a
pluggable CPU implementation of the provider seam.
A provider conformance kit (test/harness/shared_io_provider_conformance.hpp)
drives a provider through the production shared-I/O arena and writes a
pulp.gpu-audio.provider-conformance.v1 receipt. It runs against the test
fake and the Dawn provider. A host-memory CPU provider exists behind the seam
as a private implementation with its own tests; no node uses it, and the
conformance kit does not run against it.
Coming: a provider registry and a public CPU provider.
Reading health¶
GpuAudioTransport::status_snapshot() is the UI-safe read. It is
allocation-free and never touches the node, so a host may re-prepare or destroy
the node while a UI thread reads status. The transport worker samples the
node's health after every service step or pump, so a snapshot can trail the
node by one pump.
The transport projects the lifecycle, the recovery reason and sequence, the
epoch, the delivery counters, terminal_counts, the shadow_* fields,
gpu_kernel_available, completion_mode, timestamps_available and
last_delivery_ns from the node's health sample. A field the node does not
report stays at its default.
The transport fills the schema v4 re-acquire fields itself:
reacquire_attempts counts provider replacements it attempted after a fence,
reacquire_successes counts attempts that opened a new delivery epoch, and
last_reacquire_to_gpu_ns is the time from first observing the fence to the
first GPU-delivered block of the replacing epoch (zero until measured). All
three stay zero under Manual and for a node that cannot re-acquire.
Provider state and recovery reason¶
provider_state is the lifecycle summary. recovery_reason is the first
cause that latched, with recovery_sequence set to the stream position where it
latched (UINT64_MAX when nothing has latched).
recovery_reason |
provider_state |
Produced by |
|---|---|---|
None |
Ready (GPU delivering, or a CPU-only neural family), or Degraded (staged path, no provider, or a lagging GPU; see below) |
every node |
SequenceGap, InvalidCallback |
Degraded |
both WaveNet GPU paths and the shared GpuConvolver |
InputSaturated, OfflineFence |
Degraded |
both WaveNet nodes and the shared GpuConvolver |
ProviderFailure |
Degraded |
a failed provider completion; a staged GpuConvolver map failure or non-finite readback; a WaveNet model whose GPU provider could not be prepared |
ProviderLost |
Unavailable |
both WaveNet GPU paths, both GpuConvolver paths, GpuMultiConvolver |
ProviderUnresponsive |
Unavailable |
the watchdog on the two WaveNet GPU paths |
SustainedDeadlineMisses |
Degraded |
the staged GpuConvolver's circuit breaker |
ProviderReacquired |
informational; the new provider's own state applies | GpuWaveNetRealtimeNode under AutoReacquire, after a successful re-acquire |
Every node reports a device loss observed after a failure, a stall or
sustained misses (ProviderFailure, ProviderUnresponsive or
SustainedDeadlineMisses) as ProviderLost, both in its own status and in the
transport's. Independent causes (SequenceGap, InputSaturated,
InvalidCallback or OfflineFence) stay first: a node that latched one of
those and then lost its device keeps that reason and reports Unavailable.
A provider that keeps completing, but too late for the callback, latches no
cause. On the stamped shared-I/O nodes (GpuWaveNetRealtimeNode, the GPU path
of GpuNeuralRealtimeNode and the shared GpuConvolver), Ready on the Gpu
engine also requires the GPU to carry the recent stream. The status reads
Degraded, with recovery_reason None, when:
- at least 8 of the last 32 callback blocks were counted and the GPU delivered fewer than half of them; or
- at least one block was counted and the GPU delivered none of them, as during a first preparation's or a re-acquired epoch's warmup.
Priming blocks are not counted. The status returns to Ready once the GPU
carries half the window again, and a re-acquire starts a new window.
ProviderReacquired replaces the cause that fenced the old epoch, with
recovery_sequence set to the first block the new provider can deliver. The
replaced cause stays in the trace and in the reacquire_* counters.
On the staged GpuConvolver and GpuMultiConvolver, recovery_sequence is
the worker block at which the reason latched. A loss the worker has not reached
yet is attributed to the next block it will run.
On the shared path, GpuConvolver::fenced() reads true until the first
health sample, because an unsampled session is reported as Degraded. A
transport samples immediately at prepare(), so this only affects a convolver
read directly before it is hosted.
A CPU-only neural node (TCN, SSM, LSTM, GRU, or WaveNet built without a GPU
session) has nothing to fence. An invalid or out-of-sequence callback is
counted in callback_contract_violations() instead of being latched, so the
node stays Ready while its CPU output continues. On the GPU path the same
violation fences GPU delivery and is reported as the recovery reason.
Which engine is delivering¶
selected_engine names the engine that actually produces the audio:
Gpuonly while the provider isReadywith a GPU provider, so not while a lagging GPU holds the status atDegraded;Cpuwhen a prepared CPU path covers misses, for a CPU-only family, and for a stagedGpuConvolverwhose breaker has tripped or whose device is lost;Unknownwhen misses are silent, or on the staged path while it uses the GPU, where a live device does not prove which engine produced a block.
A Degraded or Unavailable status never reports Gpu. provider still
names the provider that was prepared, so a CPU engine with a Dawn provider is
a valid combination.
Counters¶
produced_blocksis worker progress, not proof that the callback used a GPU result. UseGpuAudioTransport::delivery_snapshot()for what the callback actually selected.missed_blockscounts genuine GPU misses. It is not a synonym forfallback_blocks. A CPU-only node's blocks count underfallback_blocksand never undermissed_blocks, because the CPU model is its primary path.terminal_counts[]counts admitted blocks byGpuAudioTerminalDisposition(index 1 isGpuDelivered, index 2 isCpuFallback). A block is counted once its audio is certain. OnlyGpuWaveNetRealtimeNodeand the sharedGpuConvolverfill them; the neural node, the staged convolver andGpuMultiConvolverleave them at zero. Read through the transport, they come from the node's health sample. The sharedGpuConvolver's ownstatus_snapshot()fills them only while trace recording is enabled.epochanddelivery_epoch_activeidentify the GPU delivery epoch. A CPU-only node stays at epoch 0.gpu_kernel_availablemeans a GPU kernel is prepared, not that it is usable. It is true forGpuWaveNetRealtimeNode, for the sharedGpuConvolver, for a stagedGpuConvolverthat has a device, and forGpuNeuralRealtimeNodewhile its GPU backend is prepared, and false for a family with no GPU kernel. A stagedGpuConvolverkeeps it true after its device is lost, while its status reportsUnavailable; readprovider_statefor usability.completion_modeandtimestamps_availableare reported only byGpuWaveNetRealtimeNode; other nodes leaveUnknownandfalse. This provider path records no GPU timestamps, sotimestamps_availableis always false today.last_delivery_nsis reserved: the transport passes it through, but no node fills it.
Shadow fields¶
shadow_mode, shadow_inline_cost_pct, shadow_delivered_blocks,
shadow_late_blocks, shadow_double_miss_blocks and shadow_model_differs
describe GpuNeuralRealtimeNode's CPU shadow, and the transport passes them
through:
shadow_modeis the policy in effect:Inline,WorkerorNone;shadow_inline_cost_pctis the measured total cost across channels, as a percentage of the block period;shadow_delivered_blockscounts blocks the shadow delivered in place of the GPU;shadow_late_blockscounts shadow results dropped because they arrived after their slot, in a slot the GPU also missed;shadow_double_miss_blockscounts blocks that both the GPU and the shadow missed. It is the authoritative count of silent blocks. The transport'sCpuFallbackDeliveredlabel means only that the fallback path ran;shadow_model_differsis set when a separate, cheapershadow_modelis in use, so the substitute does not null against the GPU output.
GpuNeuralRealtimeNode::shadow_status() adds the requested policy, per-channel
cost, queue drops, resyncs and workgroup membership.
Validating a snapshot¶
validate_gpu_audio_status(status) checks a snapshot's internal consistency and
returns a typed GpuAudioStatusError. Among other things it rejects:
- a
DegradedorUnavailablestatus that selects the GPU engine; ProviderLostorProviderUnresponsivewithoutUnavailable;SustainedDeadlineMisseswithoutDegraded;Readywith a cause that fences delivery (SequenceGap,InputSaturated,ProviderFailure,InvalidCallbackorOfflineFence);Readyon a stamped node (nonzeroepoch) whosedelivery_epoch_activeis false;- the GPU engine without
gpu_kernel_available; - a CPU provider with a GPU kernel or a GPU epoch;
- shadow counts without a shadow;
- terminal counts beyond the delivery counters;
- a recovery reason without its sequence, or a sequence without a reason;
CpuFallbackdeclared with no prepared fallback.
Validate a snapshot taken at a quiescent point. Counters sampled while a node
runs can be momentarily out of step, so a live caller should treat
TerminalCountsInconsistent as advisory. A status that validates is consistent;
it does not show that a ready provider met a realtime deadline.
Per-architecture behaviour¶
| Model family | Node | GPU path | Miss handling | Reports |
|---|---|---|---|---|
| WaveNet (NAM) | GpuNeuralRealtimeNode with NeuralFamily::WaveNet and a GPU session |
Dawn shared-I/O | built-in CPU shadow, per shadow_policy |
gpu_kernel_available = true while the backend is prepared; ProviderLost; watchdog |
| WaveNet (NAM) | GpuWaveNetRealtimeNode (derive from it) |
Dawn shared-I/O | your CPU shadow | gpu_kernel_available = true; ProviderLost; watchdog |
| TCN, SSM, LSTM, GRU | GpuNeuralRealtimeNode |
none | the CPU model is the output, always inline | path Cpu, provider Cpu, selected_engine = Cpu, gpu_kernel_available = false, epoch 0, Ready; callback-contract violations are counted, not latched |
- No recurrent GPU kernels. LSTM, GRU and SSM run one dependent step per sample, so a single-instance GPU kernel cannot beat the CPU at small blocks. They run CPU-only by design.
- WaveNet without a provider. When the GPU provider cannot be prepared,
preparation still succeeds and the node runs CPU-only with an inline shadow.
It reports
DegradedwithProviderFailure, notReady. - Non-finite input. NaN and infinite samples are replaced with zero before
either the GPU or the shadow sees them.
sanitized_input_samples()counts them.GpuWaveNetRealtimeNodeapplies the same policy before GPU submission and counts in its ownsanitized_input_samples(); neither latches a cause. - Recovery. On the GPU path, any latched cause, including a single invalid callback, fences GPU delivery until the host prepares the node again.
- Model construction. Build a model on the host thread with
make_gpu_neural_model()from<pulp/gpu_audio/gpu_neural_model.hpp>. You supply a provenance manifest, the family's shape and oneGpuNeuralCpuKernelper channel; the kernels are the CPU model the node runs as its output or shadow. Only a WaveNet request may also carry a GPU session configuration; the factory refuses one for any other family. It never throws and reports a refusal as aGpuNeuralModelError. Each model binds to one node.
Shadow policy¶
Config::shadow_policy decides where a GPU family's CPU shadow runs. It
governs only realtime GPU misses:
Inlineruns the CPU model on the audio thread for every block, even when the GPU delivers.Workerruns it on a dedicated non-realtime thread. The callback only copies input in and, on a GPU miss, copies the shadow's stamped result out. A late shadow result is dropped, never shifted. The thread joinsshadow_audio_workgroupwhen one is given, and otherwise runs atUSER_INTERACTIVEQoS.Nonemakes a realtime GPU miss silent. The model keeps running on the shadow thread so that an offline render can continue it. It is rejected (ShadowRequired) for a family without a GPU kernel. WhileNoneis in effect, the node's ownstatus_snapshot()andcapability_report()reportfallback_policy = Silenceandfallback_available = false. During an offline render, which runs the shadow inline, they reportCpuFallback. The transport's status still reports the descriptor'sCpuFallbackfor such a node, so read the node's status orshadow_modeto see that misses are silent.Auto, the default, times one block of the CPU model atprepare(). It picksInlinewhen that cost times the channel count is at mostkAutoInlineMaxCostPct(15%) of the block period, andWorkerotherwise.
CPU-only families, and a WaveNet node whose provider could not be prepared,
always run Inline, because the CPU model is their output. An offline render
always runs the CPU model inline. When an offline render starts, a Worker or
None shadow finishes its queue and continues inline on the same timeline, so
a bounce does not go silent. The first realtime callback after the bounce
returns the model to the shadow thread, copying only the pending results, so
an over-budget model never runs on the audio thread after an offline render.
A Worker shadow for a large model can still miss: its result must arrive
within the lead. Inline, the shadow's cost is paid on the audio thread for every
block. Measure it for the model size and channel count you ship.
Convolution¶
Staged GpuConvolver (the default Auto/StagedOnly policy).
- Pipelining. When a transport hosts the convolver with a lead of two or
more blocks, the worker submits block N without waiting and judges it
latency_blocks - 1calls later. The node declares that asworker_pipeline_blocks, and the transport primes that many fewer blocks, so total latency and the reported delay compensation are unchanged. Called directly, or with a one-block lead, it decides each block inside the call with a bounded wait. The transport'srelease()opts a pipelining convolver back out; one destroyed before that call detaches itself from the transport first, and a node that does not pipeline is never touched there. That holds only once neitherpump()norprocess()runs on its transport: the audio callback'sprocess()calls the node's CPU fallback, and a transport with its own worker pumps it untilrelease()joins the worker. Otherwise release that transport before destroying the node. - Exact routing. A block whose result is on time and finite uses the GPU. Any other block goes to the worker's CPU convolution, so the output is the exact continuation of the convolution, never silence. A miss costs one CPU block plus the IR's tail; with a long IR that tail is long.
- Circuit breaker. After 8 consecutive misses the convolver switches to the
CPU. It probes the GPU every 32 blocks and re-enables it after 4 on-time
results. Set different values with
configure_staged_breaker()beforeprepare(). A trip reportsSustainedDeadlineMisses,Degraded, and the CPU engine. - Status. A failed map or non-finite readback latches
ProviderFailure. A lost device latchesProviderLost(Unavailable, CPU engine) and replaces any other cause. A single late block latches nothing. - Non-finite input. NaN and infinite input samples are zeroed before
submission, so they cannot cause a non-finite readback. The CPU convolution
and the miss substitute get the same zeroed block, keeping the IR tail
seamless.
sanitized_input_samples()counts them.
ProviderPolicy::StagedBlocking keeps the older in-block readback wait and is
a transition escape hatch.
Shared-I/O GpuConvolver (SharedRequired). This path runs on the
authenticated Dawn provider and reports Ready with provider Dawn while it
delivers. After the transport's offline fence, the session stays CPU-only until
the next preparation; it does not re-prime onto the GPU.
NaN and infinite input samples are replaced with zero before the GPU input
slot, so one bad sample neither rings through the IR tail nor fences the
session on readback. sanitized_input_samples() counts them and latches no
cause.
GpuMultiConvolver. No CPU fallback: a miss or a lost device is silent,
and the status says so with fallback_policy = Silence. device_lost()
reports why.
Known host behaviours¶
These come from pre-merge runs of the REAPER continuity harness and from the out-of-process probe used to design the multi-host matrix. Neither is merged evidence. Each is a host fact to plan around, not a Pulp guarantee.
- REAPER anticipative FX starves the GPU path. With anticipative FX processing enabled, REAPER calls the plug-in in back-to-back bursts, so a pipelined GPU result is never ready in time and every block comes from the CPU fallback. Audio stays correct, but the GPU does no useful work. Turn anticipative FX off for the track to test the GPU path. A processing quantum smaller than the host block has the same effect.
- The GPU does not resume after an audio fault (under the default
Manualpolicy). After any injected provider fault, delivery stays on the CPU fallback for the rest of the run. This matches the manual recovery policy described in claim 2. - No real host has shown a named loss reason yet. In those runs, which used
a code base from before the v3 status work,
recovery_reasonstayedNoneafter a loss. The code now latches a reason; a real-host run against it has not been done. - AUHostingService. On Apple Silicon, an AUv3, or an AU whose
architecture does not match the host, is hosted out of process in
AUHostingService, which is sandboxed and launched by launchd. A native arm64
AUv2 normally loads in process. Out-of-process native AU hosting has not been
validated. A probe through the Rosetta path (
AUHostingServiceXPC_arrow) stayed on the CPU fallback without silence after an injected loss. That probe is design evidence, not a validated result. - Process-wide Dawn state. A Dawn proc-table override affects every Dawn client in the process, including other plug-ins that share an AUHostingService process. The editor's render surface also reinstalls Dawn's native proc table each time it initializes.
What has been validated in real DAWs¶
Nothing on this page has a merged real-DAW receipt yet.
| Host | Status |
|---|---|
| REAPER 7.82, CLAP and VST3 | Pre-merge harness runs exercised only the shared-I/O GpuConvolver, built with experimental build flags. They passed for injected loss, a 100 ms completion stall, a 3 s worker stall (the GPU kept completing, so this is not a test of hang detection) and editor render-device loss, with the plug-in's own captured output sample-exact against a CPU reference. They ran on a host that was not quiet. The VST3 matrix is partial, and a run with anticipative FX on was inconclusive. One early stall run produced a near-silent offline bounce, recorded as a verified FAIL; it did not reproduce in two later runs and is unexplained. |
| Logic Pro, GarageBand, Bitwig Studio | Not run. |
| AUv2 and AUv3 | Not run in a DAW. |
A PASS from that harness means the plug-in's own capture stayed sample-exact across one injected fault. The REAPER bounce is an offline render made afterwards, so it checks only the offline path and delay compensation. A PASS does not prove recovery, hang detection, render-owner loss propagation, resource replay or teardown suppression.
Testing¶
- Unit and contract tests. The GPU-audio suites drive fake providers through
loss, hang, lateness and fencing, and every status they produce passes
validate_gpu_audio_status. They prove the sequencing and fallback contracts, not hardware deadline reliability. - Real DAW. Use the real-DAW smoke harness. Its
--mode gpu-faultinjects one GPU fault into the test plug-in inexamples/gpu-fault-continuity/while REAPER renders the track, and writes apulp.gpu-audio.daw-continuity.v1receipt thattools/validation/verify_gpu_audio_daw_continuity_receipt.pychecks. The other hosts' runs write av2receipt, checked byverify_gpu_audio_daw_continuity_receipt_v2.py. Run it with anticipative FX off for the GPU path, and record the setting either way. No real-DAW receipt from it is merged yet. - Provider conformance. The kit in
test/harness/shared_io_provider_conformance.hppruns the same contract scenarios against the test fake and the Dawn provider, andtools/validation/verify_gpu_audio_provider_conformance.pychecks its receipt. - Dawn versus native Metal comparator. Coming: a matched comparator for the provider decision. Its first campaign was inconclusive on a loaded host.
Related guides¶
- GPU audio runtime: transport, nodes, capability reports.
- Stamped WaveNet audio bridge: WaveNet lifecycle, loss and watchdog details.
- Diagnosing GPU audio with Perfetto: per-block trace dispositions.
- Real-DAW smoke: the REAPER harness.