Repository navigation
APU HRTF Optimization
Lifecycle: Performance - Unproven · Type: Exact-oriented signal-processing optimization
Reviewed source:322986f4c5502b3fa887a9605ac8cd88e9b2c579
Question: How much host time does the current HRTF path consume, and which exact transformations can remove indexing and scalar convolution overhead without changing output?
APU/DSP investigation directory · MCPX APU & Audio
HRTF is not merely another voice flag. The current path performs a 31-tap convolution for both channels for every output sample, advances a circular history buffer, interpolates fractional interaural delay, and smooths every coefficient and the delay value toward a target.
At 32 samples per APU subframe and a nominal 1,500-subframe cadence, the convolution alone has a source-derived upper shape of:
31 taps × 2 channels × 32 samples × 1,500 subframes
≈ 2.98 million tap iterations per second per HRTF voice
That number is not measured CPU time. It does establish that HRTF has a repeated inner loop worthy of dedicated counters rather than being hidden inside a general worker investigation.
-
hw/xbox/mcpx/apu/vp/hrtf.hHRTF_NUM_TAPS = 31HRTF_SAMPLES_PER_FRAME = NUM_SAMPLES_PER_FRAMEhrtf_filter_step_parameters()hrtf_filter_process()- circular indexing and fractional-delay interpolation
-
hw/xbox/mcpx/apu/vp/vp.c- HRTF admission for 3D voices
- target selection and surrounding filter/mix work
- Audio configuration and worker scheduling determine how many active voices can reach the route.
flowchart TD
A[32-sample voice block] --> B{3D voice and HRTF enabled?}
B -->|No| C[Continue normal filtering/mixing]
B -->|Yes| D[Load HRTF target entry]
D --> E[For each of 32 samples]
E --> F[Smooth 62 coefficients and ITD]
F --> G[For left and right channel]
G --> H[Write new sample into circular history]
H --> I[For each of 31 taps]
I --> J[Modulo index history]
J --> K[Optional second modulo index for fractional delay]
K --> L[Interpolate sample and multiply-accumulate]
L --> M[Advance circular position]
M --> C
The modulo operations, parameter smoothing, interpolation branch, scalar multiply-adds, and history access all occur inside a route that may run for many voices.
Run the same title and voice workload with HRTF enabled and disabled. This is not an equivalent-output performance comparison; it is a cost-removal experiment that bounds the amount of time the complete HRTF route could explain.
Record active HRTF voices and voice-worker distribution so a faster run is not confused with changed voice scheduling.
The current inner loop computes circular indices with % HRTF_BUFLEN. A mirrored history layout can provide a contiguous tap window.
typedef struct MirroredHrtfHistory {
float data[2 * HRTF_BUFLEN];
unsigned write_pos;
} MirroredHrtfHistory;
static inline void history_push(MirroredHrtfHistory *h, float sample)
{
unsigned p = h->write_pos;
h->data[p] = sample;
h->data[p + HRTF_BUFLEN] = sample;
h->write_pos = (p + 1 == HRTF_BUFLEN) ? 0 : p + 1;
}A pointer can then address a contiguous reverse or prepared-order span without modulo per tap. The exact storage order should be selected to make both integer and fractional-delay source samples contiguous.
Keep one scalar reference implementation and add separately testable kernels:
float hrtf_convolve_scalar(const float *samples,
const float *coeff,
unsigned taps);
float hrtf_convolve_sse2(const float *samples,
const float *coeff,
unsigned taps);
float hrtf_convolve_neon(const float *samples,
const float *coeff,
unsigned taps);The vector route must define its floating-point contract. Reordering additions changes rounding even when every input and coefficient is identical. Possible policies are:
- bit-identical accumulation order, which limits vectorization;
- bounded numerical difference with an explicit audio error metric;
- platform-specific exactness only where the instruction sequence reproduces the scalar order.
Do not call a reordered reduction exact without evidence.
The current code smooths all coefficients and ITD every sample. Measure how often target parameters actually change and how long they remain stable.
An exact stable route can be used when current values are exactly equal to target values:
if (params_exactly_stable(filter)) {
process_fixed_coefficients(filter, in, out);
} else {
process_smoothed_coefficients(filter, in, out);
}Clamping a value that is merely close to the target is an approximation. It should be a separate experiment with spectral/error reporting, not silently folded into the exact route.
flowchart TD
A[HRTF voice block] --> B{Parameters exactly stable?}
B -->|No| C[Reference smoothing route]
B -->|Yes| D[Fixed-coefficient route]
C --> E{Mirrored history available?}
D --> E
E -->|No| F[Reference circular scalar convolution]
E -->|Yes| G{Qualified host SIMD kernel?}
G -->|No| H[Contiguous scalar convolution]
G -->|Yes| I[SSE/NEON convolution]
F --> J[Output samples]
H --> J
I --> J
Every stage should be benchmarked independently: mirrored history, stable-parameter bypass, and SIMD remove different work.
- Modulo arithmetic inside the tap loop.
- Branching and a second history lookup when fractional delay is zero.
- Per-sample parameter smoothing when parameters are exactly stable.
- Scalar multiply-add loop overhead on qualified SIMD hosts.
- Some worker time and APU frame utilization for HRTF-heavy scenes.
- The same history and delay state after every processed sample.
- Correct smoothing progression while targets are moving.
- Correct fractional-delay interpolation.
- Correct left/right delay sign and channel behavior.
- Reset, voice reuse, pause, and snapshot history semantics.
- A scalar reference route on unsupported hosts.
- Deterministic handling of NaN, denormal, saturation/clamping, and host floating-point policy where they can appear.
- Strong repeated-loop structure with clear local timers.
- Natural scalar oracle for SIMD and history-layout variants.
- x86-64 and ARM64 can have independent kernels behind one contract.
- Can improve a default-enabled, per-voice path without changing guest scheduling.
- Supports incremental patches rather than one large rewrite.
- Real workloads may have few HRTF-active voices.
- Reordered floating-point sums can alter audio subtly.
- Mirrored history increases state and snapshot complexity.
- Stable-parameter detection can cost more than it saves if checked poorly.
- SIMD kernels require alignment, tail handling, and platform maintenance.
- HRTF-disabled comparisons are not correctness comparisons.
APU subframes
active 3D voices
HRTF-active voices
HRTF blocks and samples
parameter target changes
stable-parameter blocks
fractional-delay tap iterations
zero-fraction blocks
scalar tap iterations
SIMD tap iterations
history modulo operations
HRTF CPU time per voice/block
worker and whole APU frame time
- Unit impulse through each channel.
- Fixed tones, noise, and chirps.
- Static source with fixed coefficients and delay.
- Moving source with rapid target changes.
- Positive and negative ITD targets.
- Integer and fractional delays near boundaries.
- History-buffer wrap on every possible write position.
- Voice reset/reuse and HRTF handle changes.
- Snapshot save/restore in the middle of a transition.
- Many simultaneous HRTF voices under worker scheduling.
- x86-64 scalar/SIMD and ARM64 scalar/NEON comparisons.
For intended-exact paths, compare output samples and internal filter state. For approximate accumulation or convergence variants, report maximum absolute error, RMS error, spectral difference, and listening-test methodology.
Use at least three workload classes:
no HRTF voices
one persistent HRTF voice
many HRTF voices with moving targets
Run worker counts 1/2/4/8/16 because a faster kernel can shift the worker crossover point. Report local HRTF time alongside whole APU utilization, vCPU progress, p95/p99 frame time, underruns, and total CPU use.
Promote an exact-oriented subexperiment to Researched when route counters show meaningful HRTF use, state/output comparisons pass for its declared arithmetic contract, and an uninstrumented Release build improves HRTF-heavy workloads without regressions elsewhere.
Reject or narrow a route when HRTF is rarely reached, vector/reordered output exceeds the declared error contract, maintenance cost outweighs the measured gain, or improvements disappear at whole-emulator scale.
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