https://bugs.winehq.org/show_bug.cgi?id=46725
Bug ID: 46725 Summary: Sniper Elite 3 crashes when using FAudio Product: Wine Version: 4.2 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: xaudio2 Assignee: wine-bugs@winehq.org Reporter: andrey.goosev@gmail.com Distribution: ---
Created attachment 63696 --> https://bugs.winehq.org/attachment.cgi?id=63696 log
Crashes after around 10 seconds of gameplay.
wine-4.2-195-gf784cabd34
https://bugs.winehq.org/show_bug.cgi?id=46725
Ethan Lee flibitijibibo@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |flibitijibibo@gmail.com
--- Comment #1 from Ethan Lee flibitijibibo@gmail.com --- This one might actually be an FAudio bug... there seems to be trouble with XAPO parameter reads/writes.
With a debug version of FAudio (i.e. `cmake .. -DCMAKE_BUILD_TYPE=Debug`) you can run `FAUDIO_LOG_FUNC_CALLS=1 wine SniperElite3.exe` and it should dump a whole bunch of enter/exit lines that will tell us exactly where the program gets stopped.
https://bugs.winehq.org/show_bug.cgi?id=46725
pattietreutel katyaberezyaka@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |katyaberezyaka@gmail.com
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #2 from Andrey Gusev andrey.goosev@gmail.com --- Created attachment 63798 --> https://bugs.winehq.org/attachment.cgi?id=63798 hack
This solves crashes for me.
https://bugs.winehq.org/show_bug.cgi?id=46725
Andrey Gusev andrey.goosev@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Summary|Sniper Elite 3 crashes when |Sniper Elite 3 and Sniper |using FAudio |Elite V2 crash when using | |FAudio
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #3 from Ethan Lee flibitijibibo@gmail.com --- Created attachment 63813 --> https://bugs.winehq.org/attachment.cgi?id=63813 FAudio hackpatch
This is similar to the above hack but narrows it down to just SetParameters in FAudio - does this fix the issue as well?
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #4 from Ethan Lee flibitijibibo@gmail.com --- A recent FAudio revision may have fixed this by accident; this game calls SetOutputVoices which had a use-after-free in FAudio. Seems unlikely that filter sends would cause issues in effect processing, but you never know with memory corruption...
https://bugs.winehq.org/show_bug.cgi?id=46725
Anthony Jagers noonetinone@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |noonetinone@gmail.com
https://bugs.winehq.org/show_bug.cgi?id=46725
Andrew Eikum aeikum@codeweavers.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |aeikum@codeweavers.com
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #5 from Ethan Lee flibitijibibo@gmail.com --- Created attachment 64034 --> https://bugs.winehq.org/attachment.cgi?id=64034 wrap_xapo rewrite
Here's a patch that redoes the XAPO wrapping entirely. This makes it a bit more like the FAudio COM wrapper which we know works, so in theory this should work as well...?
https://bugs.winehq.org/show_bug.cgi?id=46725
Alistair Leslie-Hughes leslie_alistair@hotmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |patch
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #6 from Andrey Gusev andrey.goosev@gmail.com --- Unfortunately, wrap_xapo_rewrite doesn't change the behaviour. I noticed one thing while playing Shooting Range DLC - using Welrod doesn't cause the crash. Using 'Empty Lung' before shooting with a rifle also doesn't break the thing. My guess it's something with sound sources or samples mixing.
https://bugs.winehq.org/show_bug.cgi?id=46725
Paul Gofman gofmanp@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |gofmanp@gmail.com
--- Comment #7 from Paul Gofman gofmanp@gmail.com --- Created attachment 66676 --> https://bugs.winehq.org/attachment.cgi?id=66676 PoC patch for FAudio
I've tested the issue in Sniper Elite v2 demo where it is also reproducible. To reproduce I just had to start the game and make a shot, it crashes immediately.
The immediate reason for the crash (which happens in xaudio2_7/xaudio_dll.c:XAPO_Process()) is that vtbl for game's xapo (This->xapo) was previously overwritten in application code during previous _Process called for the same xapo. It does not happen during the many earlier calls to _Process for the same xapo as the application actually skips the actual processing until you shoot the gun.
When processing the buffer the game is using some intermediate buffer previously allocated by HeapAlloc() with the size sufficient for 480 samples while input / output buffer lengths are 512 samples. The same 512 samples were specified in IXAPO_LockForProcess call for xapo, but the application seems to ignore that. It allocates that buffer before _LockForProcess is called, right after _CreateSubmixVoice(). It happens that the allocated xapo object data (with vtbl) goes right after that buffer for samples, thus it is overwritten.
The attached tweak fixes the crash. I observed which buffer lengths the application gets on Windows for xapo _Process and it is 480. So it looks like that on Windows the buffers might be limited to a smaller values and application doesn't bother to mind _LockForProcess values and allocates "big enough" buffer. I did not though make unit tests with those xapos so far.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #8 from Ethan Lee flibitijibibo@gmail.com --- The 480 figure makes perfect sense; for whatever reason WASAPI likes to make the sample block size very specific depending on the device (for 48KHz it's 480, for 44.1KHz it's something even weirder like 520), which I suspect is what this game is doing for the mastering voice. The only question is, is it hardcoded and setting the sound card output to a different rate/configuration is enough to crash the game even on Windows (surprisingly likely), or is the game querying WASAPI directly to get the buffer size and allocating the buffer based on that... for FAudio I'm not sure what the correct solution is since the quantum is _supposed_ to be 512 by default (unless you specify XAUDIO2_QUANTUM_1024) but there isn't really anything in the spec that guarantees it, just an off-hand mention of a ~10ms update interval.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #9 from Paul Gofman gofmanp@gmail.com --- (In reply to Ethan Lee from comment #8)
The 480 figure makes perfect sense; for whatever reason WASAPI likes to make the sample block size very specific depending on the device (for 48KHz it's 480, for 44.1KHz it's something even weirder like 520), which I suspect is what this game is doing for the mastering voice. The only question is, is it hardcoded and setting the sound card output to a different rate/configuration is enough to crash the game even on Windows (surprisingly likely), or is the game querying WASAPI directly to get the buffer size and allocating the buffer based on that... for FAudio I'm not sure what the correct solution is since the quantum is _supposed_ to be 512 by default (unless you specify XAUDIO2_QUANTUM_1024) but there isn't really anything in the spec that guarantees it, just an off-hand mention of a ~10ms update interval.
I changed frequency for audio device but it did not affect anything here: the processing buffer is still 480. Can it be that this size is related to input stream and not to the device? Or is that it is not related to the input / output streams at all but Win xaudio is calling _Process for parts of the buffer? Anyway, I really didn't look much into audio stuff yet and might be missing many important aspects.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #10 from Paul Gofman gofmanp@gmail.com --- Created attachment 66678 --> https://bugs.winehq.org/attachment.cgi?id=66678 Unit test
So I created a brief unit test which hopefully shows what is going wrong.
It looks like at least for an xapo on submix voice the maximum number of frames depends on submix voice parameters. I can get different number of frames by varying InputSampleRate parameter for CreateSubmixVoice(). It looks like the maximum number of frames is 44100 / 100. I am not entirely sure there is no some multiplier somewhere which can also influence that, I am not familiar with this API.
The test shows another bug by the way. Windows allows creating xapo without supporting IXAPOParameters interface (like in my test), Wine xaudio does not.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #11 from Andrey Gusev andrey.goosev@gmail.com --- If I understand it right we are talking about XAUDIO2_QUANTUM_DENOMINATOR ?
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #12 from Paul Gofman gofmanp@gmail.com --- (In reply to Andrey Gusev from comment #11)
If I understand it right we are talking about XAUDIO2_QUANTUM_DENOMINATOR ?
I am not sure about quantum denominator, what I see so far Windows seem to reliably make the number of frames for XAPOs to be 1 / 10 of frequency, and the application is relying on that. I have no idea what the quantum is supposed to be theory, but FWIW the following definitions in xaudio2.dll look pretty consistent with what I get with the above test on Windows:
UINT32 XAUDIO2_QUANTUM_NUMERATOR = 1; UINT32 XAUDIO2_QUANTUM_DENOMINATOR = 100; const float XAUDIO2_QUANTUM_MS = (1000.0 * XAUDIO2_QUANTUM_NUMERATOR / XAUDIO2_QUANTUM_DENOMINATOR);
That is, XAUDIO2_QUANTUM_MS is 10ms, and number of samples in 10ms is freq[kHz] / 100.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #13 from Ethan Lee flibitijibibo@gmail.com --- That all sounds correct - where things get complicated is WASAPI shared mode's method of calculating the buffer size. It's actually a lot like XAudio2 itself where the sound server is running at a specific frequency, and then when you open the mastering voice with another frequency, it has to calculate the frames needed to fit the server's quantum size.
So, as an example, consider a server running at 48KHz. If the mastering voice is at 48KHz, it'll just be 480, a nice even number that makes complete sense:
48000 samples per second / 100 updates per second = 480 samples per block (SPB)
480 SPB * 48000 (server) / 48000 (master) = Still 480
However, if the master is at 44.1KHz, it gets weird:
480 SPB * 48000 (server) / 44100 (master) = ~522...
... but then WASAPI returns 528...? Maybe SIMD alignment or something? We use the full space in FAudio and it sounds okay, maybe they cheat and resample it just a teeny bit more than the application knows to keep all the memory aligned.
Point being, we need a little bit more information from the OS before this can be truly solved in FAudio. Here's the SDL issue I've been using to log my Audio API Mental Breakdown(TM):
https://bugzilla.libsdl.org/show_bug.cgi?id=4181
If the above SDL issue got fixed, this would be really easy to resolve on FAudio's end.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #14 from Paul Gofman gofmanp@gmail.com --- (In reply to Ethan Lee from comment #13)
That all sounds correct - where things get complicated is WASAPI shared mode's method of calculating the buffer size. It's actually a lot like XAudio2 itself where the sound server is running at a specific frequency, and then when you open the mastering voice with another frequency, it has to calculate the frames needed to fit the server's quantum size.
I might be very well missing some basics here, but are you sure it has anything to do with devices at all on Windows? What I observe in test is that the processing buffer length depends just on the frequency specified in CreateSubmixVoice() call. So if the device frequency is different Win xaudio it is probably doing some resample / rebuffering?
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #15 from Paul Gofman gofmanp@gmail.com --- (In reply to Paul Gofman from comment #14)
(In reply to Ethan Lee from comment #13)
That all sounds correct - where things get complicated is WASAPI shared mode's method of calculating the buffer size. It's actually a lot like XAudio2 itself where the sound server is running at a specific frequency, and then when you open the mastering voice with another frequency, it has to calculate the frames needed to fit the server's quantum size.
I might be very well missing some basics here, but are you sure it has anything to do with devices at all on Windows? What I observe in test is that the processing buffer length depends just on the frequency specified in CreateSubmixVoice() call.
To clarify, when testing on Windows, if I change freq of 44100 that is currently in the test I linked to some other value (say, 46500, chosen randomly), without changing anything else, I get the buffer length exactly of 465 (freq / 100) and not some combination with mastering volume or with some other frequency.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #16 from Ethan Lee flibitijibibo@gmail.com --- I have evidence of my idea locally, but I haven’t done a thorough test of the inputs and outputs (i.e. try every integer value as a frequency against all provided frequencies the driver supports on all the hardware at my disposal). Having that kind of thing in a table would be a good start in determining how this is calculated in the XAudio2 mastering voice creation.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #17 from Paul Gofman gofmanp@gmail.com --- (In reply to Ethan Lee from comment #13)
480 SPB * 48000 (server) / 48000 (master) = Still 480
However, if the master is at 44.1KHz, it gets weird:
480 SPB * 48000 (server) / 44100 (master) = ~522...
You are talking about mastering voice frequency vs device frequency here. If I read [1, 2] correctly, this does not have much to do with source and submix voices. If submix and mastering voices perform sample rate conversion anyway should device sample rate necessarily influence source and submix voices buffer sizes? Especially given that application can set any frequency on submix voice which does not have common denominators with master voice.
1. https://docs.microsoft.com/en-us/windows/win32/api/xaudio2/nf-xaudio2-ixaudi... - "The mastering voice performs a sample rate conversion from this input sample rate to the actual device output rate."
2. https://docs.microsoft.com/en-us/windows/win32/api/xaudio2/nf-xaudio2-ixaudi... - "A submix voice performs a sample rate conversion from the input sample rate to the input rate of its output voices in pSendList."
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #18 from Ethan Lee flibitijibibo@gmail.com --- The problem stacks, so if the buffer size on the master changes, all the other buffer sizes change along with it in order to ensure that we are asking for enough data from the application. You can see these calculations here:
Sources: https://github.com/FNA-XNA/FAudio/blob/master/src/FAudio.c#L502
Submixes: https://github.com/FNA-XNA/FAudio/blob/master/src/FAudio.c#L569
audio->updateSize refers to the mastering voice quantum length.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #19 from Paul Gofman gofmanp@gmail.com --- (In reply to Ethan Lee from comment #18)
The problem stacks, so if the buffer size on the master changes, all the other buffer sizes change along with it in order to ensure that we are asking for enough data from the application. You can see these calculations here:
Did you try running my test?
How it works for me on Windows, if I substitute some freq instead of 44100 here: (line 967): XA2CALL(CreateSubmixVoice, &sub, 2, 44100, 0, 0, NULL, NULL);
I get that freq / 100 instead of 441 here:
(line 995): ok(test_max_frame_count == 441, "Got unexpected frame count %u.\n", test_max_frame_count);
Do you observe anything different?
I am not immediately suggesting how to implement that, this of course might use some more testing and certainly requires some more insight into the problem.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #20 from Ethan Lee flibitijibibo@gmail.com --- I haven’t run it yet, it may be a while before I can - it sounds like the problem could manifest here as well?
https://github.com/FNA-XNA/FAudio/blob/master/src/FAudio.c#L1346
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #21 from Paul Gofman gofmanp@gmail.com --- (In reply to Ethan Lee from comment #20)
I haven’t run it yet, it may be a while before I can - it sounds like the problem could manifest here as well?
https://github.com/FNA-XNA/FAudio/blob/master/src/FAudio.c#L1346
I am not sure I understand. I was not trying to discuss implementation, I just tried to show what works differently on Windows in respect to the application problem. Once again, what I see in test is that can set whatever freq I want for submix voice and get the frame count in xapo on that voice of freq / 100.
I can only guess how it is implemented on Windows. My (rather wild) guess is that Windows does not try to control input buffer lengths. Maybe it sticks to that 10ms length (whatever number of samples there is for given frequency). Accurate resample can't be done on buffer per buffer basis anyway, there will be an edge effect (how would it interpolate the sample at the start of buffer without having enough some samples before the current buffer start?). So I can guess voices in graph might somehow manage the data in their input buffer independently on their input voices buffers.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #22 from Ethan Lee flibitijibibo@gmail.com --- Created attachment 66679 --> https://bugs.winehq.org/attachment.cgi?id=66679 FAudio Quantum Patch
Going to completely wing it for a second... attached is a patch that forcibly sets the quantum of the audio device to be exactly 10ms (or 21.33 per the spec for 1024_QUANTUM). This _should_ be the same as the earlier patch in theory. How it affects the stability of the native audio devices (WASAPI in particular scares me a lot) I have no idea, but if SDL's internal streaming works correctly this should sound okay and also give the MaxFrameCount that the game is expecting.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #23 from Paul Gofman gofmanp@gmail.com --- (In reply to Ethan Lee from comment #22)
Created attachment 66679 [details] FAudio Quantum Patch
Going to completely wing it for a second... attached is a patch that forcibly sets the quantum of the audio device to be exactly 10ms (or 21.33 per the spec for 1024_QUANTUM). This _should_ be the same as the earlier patch in theory. How it affects the stability of the native audio devices (WASAPI in particular scares me a lot) I have no idea, but if SDL's internal streaming works correctly this should sound okay and also give the MaxFrameCount that the game is expecting.
Yes, it works as expected and fixes the issue. I realize that it can potentially have some caveats. During the brief testing with the demo I could not hear obvious sound problems.
FWIW, I found what MS says about 10ms quantum [1]: "All audio processing takes place in a separate thread with a periodicity defined by the graph's quantum (currently 10 milliseconds on Microsoft Windows, and 5 1/3 milliseconds on Xbox 360). Every quantum milliseconds, the thread wakes up and disperses quantum milliseconds of audio data through the entire graph."
1. https://docs.microsoft.com/en-us/windows/win32/xaudio2/xaudio2-audio-graph
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #24 from Ethan Lee flibitijibibo@gmail.com --- Oh yeah, they have the separate threads for the graph and DMA buffer write... sometimes I forget how cursed it is in there.
In any case, I tested this on Xbox (the most cursed hardware of all) and it still works without any weird skips, so I've committed this to upstream:
https://github.com/FNA-XNA/FAudio/commit/a0f859c761bb80501f3c7501b12f344585a...
Would still be nice to have SDL_GetAudioDeviceSpec so the default isn't _always_ 48KHz, but that's someone else's problem for now.
https://bugs.winehq.org/show_bug.cgi?id=46725
Andrey Gusev andrey.goosev@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |RESOLVED Resolution|--- |NOTOURBUG
--- Comment #25 from Andrey Gusev andrey.goosev@gmail.com --- Also helps for Sniper Elite 4, Sniper Elite V2 Remastered and Zombie Army.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #26 from Ethan Lee flibitijibibo@gmail.com --- This may help #46918 as well. Basically anything that has a custom XAPO will want to try the latest FAudio revision.
https://bugs.winehq.org/show_bug.cgi?id=46725
--- Comment #27 from Andrey Gusev andrey.goosev@gmail.com --- (In reply to Ethan Lee from comment #26)
This may help #46918 as well. Basically anything that has a custom XAPO will want to try the latest FAudio revision.
No, in case of CoD it doesn't.