back the audio cap off to opt-in: 7-23 already fixed the complaint, and the cap is a governor

Correcting my own framing in 7e57816. The dropout players actually reported was
fixed on 2026-07-23 by a999e5c, and nobody has complained since. I read the
leftover ACQUIRE FAILED lines in the night-5 logs as the same live bug and
raised the ceiling by default. They are not the same bug.

What is still in the logs is transient exhaustion, not a leak: the census sits
at 45-60 live, spikes during a firefight, and drains straight back (226 -> 42),
which is the priority steal loop working. A voice dropped in that window is
competing with ~256 already sounding, so it is very likely sub-perceptual --
which is consistent with the thing nobody is reporting.

And raising the cap is not free. The 256 ceiling doubles as a governor: the
steal loop only steals when the incoming source outranks a running one, so a
higher cap means many more voices mixing simultaneously, and with EFX reverb and
the lowpass chains live that is real CPU -- spent precisely during heavy combat,
when the frame budget is already tightest. I verified the ceiling moves. I never
measured frame time under a real firefight, and a solo bench cannot produce one.
Shipping that to testers tonight would be gambling their framerate against a
complaint nobody is making.

So the knob stays and the default does not move. BT_AUDIO_SOURCES=<n> raises it
with no rebuild if dropouts are ever reported again -- measure frame time while
you do. Unset reports `granted mono=255 stereo=1`, exactly what testers have
been running.

Keeping the diagnostic read-back either way, since it earned its keep: asking
for 64 grants 240, because OpenAL Soft has a floor of its own, which is also why
the NULL default lands on 256. A request is not a promise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-07-28 16:30:36 -05:00
co-authored by Claude Fable 5
parent 7e57816d39
commit 1af2dfc769
2 changed files with 51 additions and 25 deletions
+18 -14
View File
@@ -16,21 +16,25 @@ on top of it. Full detail: `docs/PROGRESS_LOG.md §5b, §8`.
## What WinTesla already did (do NOT rebuild)
- **Renderer bypass: DONE** — `MUNGA_L4/L4D3D.cpp` + `DXUtils` replace libDPL / the IG board with
Direct3D9. No `libdpl.lib` in the tree. (The early from-scratch D3D9 viewer was retired and removed.) [T1]
- **Audio SOURCE BUDGET — dropouts in heavy combat, FIXED 2026-07-28** [T2]. Field report "audio
cutting in and out toward the end of the match"; night-5 logs carried **2017 `ACQUIRE FAILED` lines in
a single match, every one at `live=256`**. It is **NOT a leak** — the census sits at a healthy 45-60
live, spikes during firefights, and drains straight back, so the engine's priority steal loop
- **Audio SOURCE BUDGET — the dropout complaint was fixed 2026-07-23; the leftover is NOT the same
bug** [T2, 2026-07-28]. **The reported bug is closed**: `a999e5c` (per-source delete instead of the
spec-atomic bulk `alDeleteSources`) stopped the pool leaking, and there have been **no field
complaints since**. Night-5 logs still show 2017 `ACQUIRE FAILED` lines in a match, all at
`live=256` — but that is **transient exhaustion, not a leak**: the census sits at 45-60 live, spikes
during firefights, and drains straight back (226→42), i.e. the engine's priority steal loop
(`AudioSourceStop`/`SuspendMaintenance``ReleaseChannels``ReleaseSourceSet`
`alDeleteSources`) works. The cause: `L4AudioRenderer` called `alcCreateContext(device, NULL)` with
**no attribute list**, so OpenAL Soft applied its DEFAULT budget of 256 mono sources — an OpenAL
default, unrelated to the 1995 audio hardware. A busy match logs ~7200 explosions × 3
`DPLIndependantEffect` voices each; while the pool is pinned every acquire fails and that sound is
dropped outright. FIX: pass `ALC_MONO_SOURCES` (default **1024**, `BT_AUDIO_SOURCES` overrides) and
**read back what was granted** — a request is not a promise. Measured: 1024→1024, 2048→2048, and
64→**240** (OpenAL Soft's own floor, which is why the NULL default landed at 256). Observed bursts
peaked at 226 against the old 256 cap, i.e. they were clipping the ceiling. Note the earlier
2026-07-23 fix (`a999e5c`, per-source delete instead of the spec-atomic bulk `alDeleteSources`) was
real and is still needed — it is what makes the pool drain; it just was not the ceiling problem.
`alDeleteSources`) is working. A dropped voice there competes with ~256 already sounding, so it is
very likely sub-perceptual — treat the log noise as a symptom to watch, **not** as the old bug.
Mechanism if it ever does matter: `alcCreateContext(device, NULL)` passes no attribute list, so
OpenAL Soft applies its DEFAULT 256 mono sources (an OpenAL default, unrelated to the 1995 audio
hardware), while a busy match logs ~7200 explosions × 3 `DPLIndependantEffect` voices.
**Raising the cap is deliberately OPT-IN (`BT_AUDIO_SOURCES=<n>`, no rebuild needed)** — the 256
ceiling doubles as a **governor**. The steal loop only steals when the incoming source outranks a
running one, so a higher cap means far more voices mixing at once, and with EFX reverb + lowpass
chains live that is real CPU spent exactly during heavy combat. **Measure frame time before making
it default.** Granted values measured: 1024→1024, 2048→2048, 64→**240** (OpenAL Soft's own floor,
which is also why the NULL default lands on 256) — always read back what was granted, a request is
not a promise.
- **Audio: BACKEND now REAL + soundbank loaded; only game-triggering remains** [T2, updated
2026-07-15]. OpenAL replaces the HMI "SOS" engine and the renderer→device→buffer→source→play chain
IS all implemented (`L4AUDRND`/`L4AUDRES`/`L4AUDLVL`; `PatchLevelOfDetail::PlayNote` really calls
+33 -11
View File
@@ -395,29 +395,51 @@ void
// DPLIndependantEffect voices, and while the pool is pinned every new
// acquire fails outright and that sound is silently dropped.
//
// Ask for a budget that fits the content. OpenAL Soft mixes in software,
// so this costs mixing headroom, not hardware voices, and it clamps to what
// it can honour -- hence the read-back below rather than an assumption.
// BT_AUDIO_SOURCES overrides for A/B testing.
ALCint monoWanted = 1024;
// ⚠ OFF BY DEFAULT, DELIBERATELY. Raising the cap is NOT a free win, and
// the reported dropout was already fixed on 2026-07-23 by a999e5c (the
// atomic-delete leak) -- no field complaints since. What remains in the
// night-5 logs is transient exhaustion at the peak of a firefight, where
// a dropped voice competes with ~256 already sounding, so it is very
// likely sub-perceptual. Against that: the 256 ceiling also acts as a
// GOVERNOR. The steal loop only steals when the incoming source outranks
// a running one, so a higher cap means many more voices mixing at once --
// with EFX reverb + the lowpass chains live, that is real CPU, spent
// exactly during heavy combat when the frame budget is tightest.
//
// So this stays opt-in: set BT_AUDIO_SOURCES=<n> to raise it (no rebuild
// needed) if dropouts are ever reported again, and measure frame time
// while you do. Unset = the shipped OpenAL Soft default, unchanged.
ALCint monoWanted = 0;
if (const char *sv = getenv("BT_AUDIO_SOURCES"))
{
int n = atoi(sv);
if (n >= 64 && n <= 4096)
monoWanted = n;
}
ALCint attrs[5];
attrs[0] = ALC_MONO_SOURCES; attrs[1] = monoWanted;
attrs[2] = ALC_STEREO_SOURCES; attrs[3] = 16;
attrs[4] = 0;
ALCcontext *context = alcCreateContext(device, attrs);
ALCcontext *context;
if (monoWanted > 0)
{
ALCint attrs[5];
attrs[0] = ALC_MONO_SOURCES; attrs[1] = monoWanted;
attrs[2] = ALC_STEREO_SOURCES; attrs[3] = 16;
attrs[4] = 0;
context = alcCreateContext(device, attrs);
}
else
{
context = alcCreateContext(device, NULL); // default budget (256)
}
alcMakeContextCurrent(context);
// Report what the driver actually GRANTED -- a request is not a promise.
// (Asking for 64 grants 240: OpenAL Soft has a floor of its own, which is
// also why the NULL default lands on 256.)
if (getenv("BT_AUDIO_LOG") || monoWanted > 0)
{
ALCint monoGot = 0, stereoGot = 0;
alcGetIntegerv(device, ALC_MONO_SOURCES, 1, &monoGot);
alcGetIntegerv(device, ALC_STEREO_SOURCES, 1, &stereoGot);
DEBUG_STREAM << "[audio] source budget: requested mono=" << monoWanted
DEBUG_STREAM << "[audio] source budget: requested mono="
<< (monoWanted > 0 ? monoWanted : 0) << " (0 = driver default)"
<< " granted mono=" << monoGot << " stereo=" << stereoGot
<< std::endl << std::flush;
}