Repository navigation
APU DSP Performance Investigations
Reviewed: November 1, 2026 · Source:
b7140848
Release availability: Delivered mechanisms span v0.8.136-0-g458730bf5373 (September 30, 2026) and later main; see the matrix below.
Scope: Current delivery, measured research and remaining investigations. Source presence, release availability and demonstrated benefit are separate facts.
The strongest non-graphics performance questions in the reviewed fork are concentrated in the MCPX APU pipeline. The recurring pattern is not a vague “southbridge bottleneck.” It is a chain of small 32-sample audio blocks crossing worker scheduling, guest-memory translation, backend dispatch, DSP memory, DMA conversion, and a long-lived APU mutex.
This page is the entry point. Every formal item has its own technical reference. Two source findings extend existing subject pages rather than creating duplicates.
| Mechanism | Current implementation / release | Evidence boundary | Full reference |
|---|---|---|---|
| DSP JIT default #183 | Merged; in September 30 stable | Existing engine now selected by default; broader parity remains; real-time processing still defaults off | DSP JIT |
| Mono sinc #182 | Merged; in stable | One interpolation channel then output expansion; historical results are not November-head proof | Mono resampling |
| Decoded ADPCM reuse #187 | Merged; main only | Fresh full-byte comparison; no repeatable native game speedup established; some PGR2 tails worsened | ADPCM cache |
| SGE / ADPCM payload mappings #190/#275 | Merged; main only | Live descriptor/payload reads; adverse Deck Morrowind outcome retained in #310 | Sample mappings and span research |
| CFG_FMT snapshot #191 | Merged November 1; main only | Local to one sample-fetch callback; persistent controls/coefficient caches remain research | Prepared voice controls |
| Auto-four workers #200 | Merged; main only | Auto clamps host logical count to 1–4; explicit 1–16, restart required; independent of linear resampling | Worker policy and research |
| Worker mutex / completion #201/#305 | Merged; #201 in stable, #305 main only | Reliability fixes; no fabricated speedup percentage | Worker reliability |
| Attenuation table #193 | Measured, closed/unmerged | +0.70% cadence, −0.62% CPU, worse p99 +6.32%; retained research | Attenuation lookup |
| Linear resampler #189 | Open/unmerged/HOLD | Separate quality tradeoff; normal current path remains sinc | Linear research |
| Bulk VP → DSP transfer | Unimplemented research | Existing 32 × 32 handoff uses generic DSP writes; quantify route cost before changing it | Bulk transfer |
| DSP DMA block transfers | Unimplemented research | Preserve descriptor, memory and completion semantics | DMA research |
| Frame-lock contention / HRTF | Remaining research | Need runtime contention and cost evidence | Frame lock · HRTF |
| Lazy GP/EP execution | Destructive research | Skipping processor execution can change guest observations | Lazy DSP |
| Secondary-device counters | Instrumentation backlog | No speedup inferred from source inspection | Opportunity counters |
The matrix is a delivery map, not a pooled performance result. Current Status, Releases and Main, and Measured Results keep product availability and timed campaigns explicit.
#292 delivers a read-only Linux GDB ownership inspector for an already attached, paused xemu process with matching DWARF. It reports ownership/state observations; it does not itself prove PCM output or audibility. #302 validates offline Vulkan finish attribution and is a diagnostic capability, not a runtime speedup.
The ownership inspector is scripts/xemu-audio-state.py. Load it through GDB's source command using the maintained runner recipe. It reads the selected live inferior and /proc/<pid>/exe, validates required audio DWARF fields, and creates a new audio-state.json plus an XEMU_AUDIO_STATE console record. Output includes executable SHA-256, backend/driver identity, timer/VM flags and hardware/software voice activity. It makes no inferior calls, writes, breakpoints or PCM changes; attach/pause and matching symbols are prerequisites, not actions supplied by the script. An instantaneous ownership snapshot provides no playback-duration, acoustic or performance qualification.
For offline finish analysis, vk-perf-summary.py --finish consumes a complete stopped schema-8 log and retains its source hash and timing coverage. The current contract distinguishes calls, submissions, waits and sampled host elapsed durations. Those durations are not CPU-exclusive or GPU time and are not extrapolated into a speedup. Both diagnostics are main-only additions relative to the September 30 stable release.
GP trace/replay/HLE #376–#380 remains open/unmerged/HOLD. Its reported 94.673% GP share refers to summed GP/EP host elapsed cost in one capture, not total emulator CPU. Fewer replay FFI calls in recognized routines do not establish native FPS improvement, full replay fidelity or a delivered HLE engine. Branch-only diagnostic variables are not current-main settings.
flowchart TD
A[Existing APU frame] --> B[VP voice processing]
B --> C[Worker wake / clear / process / reduce]
B --> D[Voice-state and SGE/sample memory access]
C --> E[32 x 32 VP mix matrix]
D --> E
E --> F[1,024 generic writes into GP X memory]
F --> G[GP DSP interpreter or JIT]
G --> H[DSP DMA / EP DSP / monitor]
I[vCPU APU MMIO] --> J[APU main mutex]
J --> A
The priority rule is:
frequency × cost × realistically removable fraction × affected workload
A source loop that can theoretically execute 1.5 million backend writes per second deserves counters before a rare timer conversion. A function merely appearing “clever” does not justify a project.
The candidate preserves the current guest-visible result and changes host organization only. Examples:
- bulk VP-to-DSP writes with identical 24-bit conversion and layout;
- safe SGE spans that split at every required boundary;
- queryable worker-count or serial-path selection;
- mirrored HRTF history and exact SIMD kernels;
- shorter mutex ownership after immutable frame inputs are proven.
The DSP interpreter and JIT already exist, and JIT-default selection has landed. Remaining qualification concerns output, state transfer, lifecycle and host/title compatibility, not building another engine or treating the delivered default as future work.
The experiment intentionally changes timing, numerical output, or guest-observable processor execution to estimate an upper bound. Examples:
- skipping GP/EP while attempting to detect the first later observation;
- treating a mid-block guest voice record as stable without an ownership proof;
- “close enough” HRTF parameter convergence;
- immediate DSP DMA completion where the guest currently observes RUNNING for several reads.
These routes must never be described as normal optimizations until their behavioral contract is established.
A source pattern exists, but call frequency or total cost is unknown. OHCI descriptor reads, PTIMER conversion, NVNet descriptor reuse, IDE copies, and inactive APU wakeups start here.
The reviewed code already establishes several facts:
-
dsp_init()selects eitherdsp_jit_init()ordsp_c_init()fromg_config.audio.use_dsp_jit. -
dsp_set_engine()synchronizes through the canonical VM state and supports runtime engine changes. - The JIT owns X, Y, and program-memory buffers and aliases the DSP mix buffer into X memory.
-
se_frame()creates one 32 × 32 mix matrix, runs VP, then DSP, then monitor processing while the frame thread owns the APU lock. - Voice workers clear complete mix matrices and reduce complete matrices into a central result.
- The VP → GP handoff invokes
dsp_write_memory()once per mixbin/sample entry. - DSP DMA decodes descriptors, performs word-level backend accesses, and uses an explicitly marked intermediate scratch buffer.
- HRTF uses 31 taps, two channels, a circular history buffer, fractional delay interpolation, and per-sample coefficient smoothing.
Those facts should be treated as the starting model. New investigations should refine them with runtime counts rather than rediscovering the same call graph.
Counters must be low overhead, disabled by default, and owned by the thread/subsystem producing them.
APU subframes
active voices by format and 2D/3D/MP class
HRTF-active voices
worker dispatches and queue lengths
workers awakened / productive / empty
worker clear, processing, wait, and reduction time
physical voice-memory reads
SGE entry reads and resolved spans
VP→DSP generic write calls and words
DSP DMA descriptors, words, backend calls, and scratch copies
GP/EP requested and executed cycles
APU mutex wait and hold time by caller/phase
idle timed wakeups and BQL activity
audio underruns and APU frame deadline misses
For new optimization work, establish route reachability before claiming that a source loop is the bottleneck. Counters listed here are proposed evidence, not a promise that every field exists in current main.
Reference and candidate runs must start from independent, equivalent state. Compare:
audio samples or a declared numerical/error metric
DSP registers, stack, X/Y/P memory and mix buffer
DMA descriptor/cursor/control state
interrupt and notifier sequence
voice position, envelope and loop state
snapshot result
reset, pause/resume and engine-switch behavior
Include negative controls that violate the optimization’s assumptions. A span test must cross a page/SGE/loop boundary. A cached voice-control test must modify a guest record during the block. A DSP bulk-write test must run a program that reads the mix-buffer alias. A lazy-DSP test must provoke a memory/status/DMA/interrupt observation.
Release build with matching symbols for attribution
A–B–B–A and B–A–A–B where state allows it
same executable where possible
same save state, input and guest-progress window
worker counts 1 / 2 / 4 / 8 / 16 / Auto
DSP interpreter and JIT
HRTF enabled and disabled
x86-64 and ARM64 where backend-specific behavior matters
Record whole-emulator outcomes:
- vCPU progress;
- APU frame utilization;
- CPU time per thread;
- p95/p99 frame and audio-block time;
- audio underruns;
- mutex contention;
- output divergence;
- memory use.
Local timers are explanatory evidence, not the acceptance result.
| Idea | Disposition | Reason |
|---|---|---|
| Generic PCI IRQ deduplication | Remove | The generic path already exits when INTx level does not change |
| “Optimize the northbridge” | Remove | No substantial cycle-level northbridge transaction engine exists in this model |
| APU idle wakeup as a standalone project | Subtask | Measure under the lock-contention/instrumentation page first |
| OHCI descriptor batching | Instrument | Ownership and guest-RAM completion semantics are nontrivial; runtime cost unknown |
| PTIMER conversion | Instrument | Frequency and guest-PC ownership unknown; current truncation must be preserved |
| NVNet descriptor reuse | Conditional backlog | Relevant only under network traffic and needs frequency evidence |
| IDE/ATAPI direct transfer | Do not add yet | Current path already uses SG/chunk transfers; storage/block latency may dominate |
flowchart LR
A[Source-backed question] --> B[Performance - Unproven]
B --> C[Route counters + correctness comparison]
C -->|Useful evidence, not in main| D[Performance - Researched]
C -->|No opportunity or failed idea| E[Record result in Researched / lesson]
D -->|Lands in main| F[Performance - Implemented]
A failed experiment with real evidence does not return to Unproven. It becomes Researched so the same mistake is not repeated. Implemented records must separately state release availability, defaults, validation limits and adverse results; merging alone is not performance proof.
DSP tooling guide and navigation updated November 2, 2026 for experimental PR #384, source 8f51d237. Capture/replay usage and limits.
The earlier wiki-wide review was published November 1, 2026. Emulator source: b7140848. Reviewed wiki content: 1d2b671. Current status · Release availability · Publication process.
- Home
- Current status
- Stable versus main
- Implemented mechanisms
- Controls and defaults
- Measured results and limits
- Complete change register
- System
- CPU and vCPU
- Memory and DMA
- Graphics / NV2A
- APU and audio
- Timing and timers
- Platform and I/O
- Hardware observations
- Fidelity boundaries
- Methodology
- PGR2 routes
- Debugging lag / hot paths
- Tools and commands
- DSP capture, replay and fuzzing
- HLE readiness and public XISO proof
- Concurrent DSP shadow and OpenAL proof
- DSP HLE operation inventory
- DSP HLE fix examples and testing
- Development
- Upstream shortlist
- Wiki maintenance
- Research / retired work
- Optimization catalog
- Hot-path shortcuts
- Researched experiments
- Unproven proposal directory
- PR lessons
- September26 snapshot
Repository · PRs · Issues