The biggest problem is that there are _loads_ of entry points that aren't necessarily coupled with one another - the main ones are XAudio2Create, XACT3CreateEngine, CreateReverb, CreateVolumeMeter, and CreateFX, all of which are technically supposed to be decoupled from one another even though they all directly interact with each other and nothing outside of this subsystem of DirectX. The famous "we're totally decoupled except for the part where we're not" scenario. So in reality, you're not setting 1 allocator, you're setting up to 5:
https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b2411... https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b2411... https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b2411... https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b2411...
Not pictured is XAPO CreateFX, which I haven't started yet. In a typical application you would only set this once at startup, but since the COM wrapper (and Wine) don't know the call stream's order ahead of time, we have to shotgun blast it. Not the worst thing, just looks kind of goofy.
You might be thinking we could add the callback system to every function, but keep in mind that the allocators in some parts should be the same as in others, or malloc/free mismatches can occur. Admittedly it's "some" and not "all", but the fact that it's ambiguous is what makes it kind of dangerous to wield (IMO). Even worse, there's situations like AudioEngine_Initialize that takes in an optional XAudio2 parameter:
https://github.com/FNA-XNA/FAudio/blob/2fdfc193940a1aa9a89f84ce46bf7ff84280a...
So then some questions come up along the lines of "which allocator do we use, the XACT3 allocator all the time, or the XAudio2 allocator if we provide that (since the engine may have already allocated some stuff), or do we allow an XACT3 with allocator A and a custom XAudio2 with allocator B, and try to support both at the same time," and that's just the interaction between XACT and XAudio; there are potential conflicts with XAPO as well because the APO could be using allocator A but XAudio expects allocator B when reading RegistrationProprties, and that's not factoring in what could happen with custom XAudio2 voices you can set on XACT3 voices with custom XAPOs attached... you get the idea.
The global allocator definitely feels primitive and clunky, but I don't know if I have it in me to figure out the above...
-Ethan
On 10/19/18 18:35, Henri Verbeet wrote:
On Fri, 19 Oct 2018 at 22:26, Ethan Lee flibitijibibo@flibitijibibo.com wrote:
To test the waters, I added the global custom allocator func and tried it out with our existing COM wrapper, and aside from making sure that it's absolutely called before any API call, it's kind of nice! The commit is in a separate branch for now:
https://github.com/FNA-XNA/FAudio/commit/d36e46bf5609859e5796fed378c3d4b2411...
So that's pretty much what it would look like on the Wine side too; any function that initializes any portion of the XAudio/XACT APIs would need to set the functions before doing anything else. There's definitely redundancy and I probably shouldn't be using static functions in our COM wrapper, for example, but it works and manages to fix things in a way that allows our COM wrapper to work with the stock FAudio.dll on Windows, so that's nice!
This doesn't have to be the official API right away, of course, so I'm open to feedback on this.
I should note that I'm not all that familiar with the specifics of the XAudio API, so I may be missing the obvious here, but it's not clear to me why the allocation callbacks would need to be globals. (Reasons you don't want them to be globals include e.g. different parts of the same process loading the library, potentially with different allocators.)
For what it's worth, the kind of API I had in mind would look roughly like this:
HRESULT faudio_create(const struct faudio_create_info
*create_info, void **xaudio2) { ... }
HRESULT XAudio2Create(IXAudio2 **xaudio2, UINT32 flags,
XAUDIO2_PROCESSOR proc) { struct faudio_create_info create_info;
create_info.type = FAUDIO_STRUCTURE_TYPE_CREATE_INFO; create_info.next = NULL; create_info.pfn_malloc = SDL_malloc; create_info.pfn_realloc = SDL_realloc; create_info.pfn_free = SDL_free; create_info.iid = &IID_IXAudio28; create_info.flags = flags; create_info.proc = proc; return faudio_create(&create_info, (void **)xaudio2); }
Which (again, unsurprisingly) is fairly similar to the vkd3d API.