RealClassDriver_ForIssue_Cleaned.zip
Summary
iplReflectionEffectApply() reliably fills its entire output buffer with non-finite values (-nan(ind), bit pattern 0xFFC00000) on the very first block where an early-reflections effect is applied, when the effect's type is IPL_REFLECTIONEFFECTTYPE_CONVOLUTION or IPL_REFLECTIONEFFECTTYPE_HYBRID. Running the exact same scene, simulation parameters, and call sequence with IPL_REFLECTIONEFFECTTYPE_PARAMETRIC instead never reproduces the problem.
This is 100% reproducible, not intermittent, in our real application (every run, first use of the effect). A minimal repro using our actual audio-source class (attached) also reproduces it reliably.
Environment
- Steam Audio version: 4.8.1 (file version
4.08.1.0 on the shipped phonon.dll; confirmed byte-for-byte identical, via MD5, to phonon.dll inside the official steamaudio_4.8.1.3 download — ruled out any local build/version mismatch)
- Platform: Windows x64
- Compiler: MSVC, Visual Studio 2017 (toolset v141)
- Configuration tested: Debug (not yet tested in Release)
- CPU SIMD level: reproduces with both
IPL_SIMDLEVEL_SSE2 and IPL_SIMDLEVEL_AVX2
- Note: the same scene/parameters/call sequence, built and run against the Linux x64 build (
libphonon.so) from the same SDK release, does not reproduce the problem (ran 2000 blocks / ~10.7s cleanly, both CONVOLUTION and HYBRID). We have not been able to test on other Windows machines, so we can't yet say whether this is Windows-specific or specific to our particular machine/build environment.
Repro configuration
- Scene: a single closed box mesh, 10m × 3m × 10m, 12 triangles,
IPL_SCENETYPE_DEFAULT
- Materials: reproduces both with the default per-surface mix (Concrete walls / Plaster ceiling / Wood floor) and with a single uniform
Generic material on every triangle — material choice has no effect on the outcome
- One static source at
(0.5, 0.5, 0.5), listener static at (0.5, 1.5, 1.5) (both well inside room bounds, ~1.4m apart, irradianceMinDistance = 1.0)
samplingRate = 48000, frameSize = 256, ambisonics order 1 (4 channels)
IPLSimulationSettings: sceneType = IPL_SCENETYPE_DEFAULT, reflectionType = the type under test, maxNumRays = 4096, numDiffuseSamples = 1024, maxDuration = 0.1, maxOrder = 1, maxNumSources = 1, numThreads = 1, rayBatchSize = 16, maxNumOcclusionSamples = 16
IPLSimulationSharedInputs.numBounces = 4
IPLReflectionEffectSettings: type = type under test, irSize = 4800 (matches maxDuration * samplingRate), numChannels = 4
- At apply time:
IPLReflectionEffectParams.irSize = 2400 (a 0.05s early-reflections window, well under the 4800-sample capacity used at effect creation — confirmed no size mismatch between creation and apply)
- Everything runs synchronously on a single thread — no worker thread of any kind is created by our code; only whatever thread(s) Steam Audio itself spawns internally (confirmed, via the Threads window, that a
phonon.dll thread exists even with numThreads = 1)
Sequence per block, every block:
iplSimulatorSetSharedInputs(sim, IPL_SIMULATIONFLAGS_REFLECTIONS, &shared) // every ~0.1s
iplSourceSetInputs(source, IPL_SIMULATIONFLAGS_REFLECTIONS, &inputs) // every ~0.1s
iplSimulatorRunReflections(sim) // every ~0.1s
iplSourceGetOutputs(source, IPL_SIMULATIONFLAGS_REFLECTIONS, &outputs) // every ~0.1s
iplReflectionEffectApply(reflEffect, ¶ms, &inMono, &outBuffer, nullptr) // every block
where params.ir = outputs.reflections.ir (the most recently committed IR), params.reverbTimes[0..2] are sanitized to a finite fallback (0.5) if the SDK returns non-finite/zero, and for HYBRID params.eq[0..2]/params.delay are likewise sanitized/set.
Steps to reproduce
- Build and run the attached project (
RealClassDriver, VS2017, x64, Debug) as-is (earlyEffectType = IPL_REFLECTIONEFFECTTYPE_CONVOLUTION is compiled in).
- Observe the debug break / non-finite check firing on
outEarlyReflectionBuffer immediately after the first iplReflectionEffectApply() call for this source (i.e. on the first block after the first successful iplSimulatorRunReflections/iplSourceGetOutputs pair).
- Change
earlyEffectType in SteamAudioProcess.h to IPL_REFLECTIONEFFECTTYPE_HYBRID, rebuild: same result.
- Change it to
IPL_REFLECTIONEFFECTTYPE_PARAMETRIC, rebuild: runs cleanly for the full 2000-block test with no non-finite values, every time.
What we've ruled out
Before narrowing this down to the SDK call itself, we eliminated (each individually tested, one variable at a time):
- Any threading on our side (fully synchronous, single-thread reproduction)
- CPU/heap contention (reproduces identically with 20+ background noise threads doing CPU-spin and heap churn, and identically with none)
- Windows Application Verifier / Page Heap (no change either way; also not the cause, since the NaN occurs with or without it)
- MSVC debug-CRT/uninitialized-memory artifacts (checked
_mm_getcsr(); standard 0x1F80, no FTZ/DAZ set)
- DLL/version mismatch (verified MD5-identical to the official release binary)
- SIMD level (SSE2 vs AVX2 — both reproduce)
- Room materials (uniform vs. mixed absorption — both reproduce)
- Ray count / diffuse sample count (tested from 128/32 up to 4096/1024 — both reproduce)
- IR duration (tested 1.0s and 0.1s — both reproduce)
- Source/listener geometry (confirmed both well inside room bounds and away from any boundary singularity, via logging at the exact failure point)
- Number of sources /
maxNumSources (reproduces with exactly one real source, tested with maxNumSources set to both 1 and 3)
- DIRECT simulation and effect, binaural effect, and the late/parametric reverb effect running alongside REFLECTIONS on the same source (all three individually disabled, one at a time, in the minimal repro — no change; the attached repro has all three removed entirely and still reproduces)
- Calling
iplReflectionEffectReset() immediately after detecting the non-finite output does not prevent it from recurring on the very next application of the effect
- Whether the first-ever simulated IR (vs. the second) is the one applied first — reproduces either way
Possibly related
We noticed #324, which describes (via source inspection) a crossfade/buffer-swap mechanism inside the convolution reflection effect (OverlapSaveConvolutionEffect::apply, swapping mPrevFFTIR/readBuffer). We haven't been able to confirm this is the same mechanism, but it's the closest thing we found to a plausible internal explanation, since our own testing shows the failure is specific to code paths that read IPLReflectionEffectParams.ir (CONVOLUTION and HYBRID), never PARAMETRIC (which never touches ir).
We also found a changelog entry for a fixed bug ("changing the IR duration at runtime to a value larger than the max duration configured at initialization would cause the reflection effect to emit garbage, due to a buffer overrun in the overlap-save convolution partitioner"), fixed in 4.5.2. This doesn't appear to be our case (our maxDuration/irSize are consistent between effect creation and every simulation run, and the fix predates 4.8.1 in any case), but it confirms this general class of bug (garbage output from the convolution partitioner) has existed before.
Attached
A minimal, standalone Visual Studio 2017 project (RealClassDriver) containing:
- our actual
SteamAudioProcess/SteamAudioSource classes, trimmed to the bare minimum needed to reproduce this (only REFLECTIONS simulation + the early reflection effect + its ambisonics decode remain; DIRECT, binaural, late reverb, and all our own threading have been removed and confirmed not necessary to reproduce the bug)
- a small
driver_main.cpp that drives them directly with no host engine involved
- the SDK 4.8.1 headers,
phonon.lib, and the three runtime DLLs, so it builds standalone
No changes are needed to reproduce with each type other than editing the single earlyEffectType constant in SteamAudioProcess.h and rebuilding.
Happy to provide any additional logs, a smaller/synthetic repro (not using our real class, if preferred), or test candidate fixes.
RealClassDriver_ForIssue_Cleaned.zip
Summary
iplReflectionEffectApply()reliably fills its entire output buffer with non-finite values (-nan(ind), bit pattern0xFFC00000) on the very first block where an early-reflections effect is applied, when the effect'stypeisIPL_REFLECTIONEFFECTTYPE_CONVOLUTIONorIPL_REFLECTIONEFFECTTYPE_HYBRID. Running the exact same scene, simulation parameters, and call sequence withIPL_REFLECTIONEFFECTTYPE_PARAMETRICinstead never reproduces the problem.This is 100% reproducible, not intermittent, in our real application (every run, first use of the effect). A minimal repro using our actual audio-source class (attached) also reproduces it reliably.
Environment
4.08.1.0on the shippedphonon.dll; confirmed byte-for-byte identical, via MD5, tophonon.dllinside the officialsteamaudio_4.8.1.3download — ruled out any local build/version mismatch)IPL_SIMDLEVEL_SSE2andIPL_SIMDLEVEL_AVX2libphonon.so) from the same SDK release, does not reproduce the problem (ran 2000 blocks / ~10.7s cleanly, both CONVOLUTION and HYBRID). We have not been able to test on other Windows machines, so we can't yet say whether this is Windows-specific or specific to our particular machine/build environment.Repro configuration
IPL_SCENETYPE_DEFAULTGenericmaterial on every triangle — material choice has no effect on the outcome(0.5, 0.5, 0.5), listener static at(0.5, 1.5, 1.5)(both well inside room bounds, ~1.4m apart,irradianceMinDistance = 1.0)samplingRate = 48000,frameSize = 256, ambisonics order 1 (4 channels)IPLSimulationSettings:sceneType = IPL_SCENETYPE_DEFAULT,reflectionType= the type under test,maxNumRays = 4096,numDiffuseSamples = 1024,maxDuration = 0.1,maxOrder = 1,maxNumSources = 1,numThreads = 1,rayBatchSize = 16,maxNumOcclusionSamples = 16IPLSimulationSharedInputs.numBounces = 4IPLReflectionEffectSettings:type= type under test,irSize = 4800(matchesmaxDuration * samplingRate),numChannels = 4IPLReflectionEffectParams.irSize = 2400(a 0.05s early-reflections window, well under the 4800-sample capacity used at effect creation — confirmed no size mismatch between creation and apply)phonon.dllthread exists even withnumThreads = 1)Sequence per block, every block:
where
params.ir = outputs.reflections.ir(the most recently committed IR),params.reverbTimes[0..2]are sanitized to a finite fallback (0.5) if the SDK returns non-finite/zero, and for HYBRIDparams.eq[0..2]/params.delayare likewise sanitized/set.Steps to reproduce
RealClassDriver, VS2017, x64, Debug) as-is (earlyEffectType = IPL_REFLECTIONEFFECTTYPE_CONVOLUTIONis compiled in).outEarlyReflectionBufferimmediately after the firstiplReflectionEffectApply()call for this source (i.e. on the first block after the first successfuliplSimulatorRunReflections/iplSourceGetOutputspair).earlyEffectTypeinSteamAudioProcess.htoIPL_REFLECTIONEFFECTTYPE_HYBRID, rebuild: same result.IPL_REFLECTIONEFFECTTYPE_PARAMETRIC, rebuild: runs cleanly for the full 2000-block test with no non-finite values, every time.What we've ruled out
Before narrowing this down to the SDK call itself, we eliminated (each individually tested, one variable at a time):
_mm_getcsr(); standard0x1F80, no FTZ/DAZ set)maxNumSources(reproduces with exactly one real source, tested withmaxNumSourcesset to both 1 and 3)iplReflectionEffectReset()immediately after detecting the non-finite output does not prevent it from recurring on the very next application of the effectPossibly related
We noticed #324, which describes (via source inspection) a crossfade/buffer-swap mechanism inside the convolution reflection effect (
OverlapSaveConvolutionEffect::apply, swappingmPrevFFTIR/readBuffer). We haven't been able to confirm this is the same mechanism, but it's the closest thing we found to a plausible internal explanation, since our own testing shows the failure is specific to code paths that readIPLReflectionEffectParams.ir(CONVOLUTION and HYBRID), never PARAMETRIC (which never touchesir).We also found a changelog entry for a fixed bug ("changing the IR duration at runtime to a value larger than the max duration configured at initialization would cause the reflection effect to emit garbage, due to a buffer overrun in the overlap-save convolution partitioner"), fixed in 4.5.2. This doesn't appear to be our case (our
maxDuration/irSizeare consistent between effect creation and every simulation run, and the fix predates 4.8.1 in any case), but it confirms this general class of bug (garbage output from the convolution partitioner) has existed before.Attached
A minimal, standalone Visual Studio 2017 project (
RealClassDriver) containing:SteamAudioProcess/SteamAudioSourceclasses, trimmed to the bare minimum needed to reproduce this (only REFLECTIONS simulation + the early reflection effect + its ambisonics decode remain; DIRECT, binaural, late reverb, and all our own threading have been removed and confirmed not necessary to reproduce the bug)driver_main.cppthat drives them directly with no host engine involvedphonon.lib, and the three runtime DLLs, so it builds standaloneNo changes are needed to reproduce with each type other than editing the single
earlyEffectTypeconstant inSteamAudioProcess.hand rebuilding.Happy to provide any additional logs, a smaller/synthetic repro (not using our real class, if preferred), or test candidate fixes.