Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] about loading "AL_EXT_direct_context" functions (posted here because openal.org mailing list is dead apparently) #982

Open
hypatia-of-sva opened this issue Mar 26, 2024 · 2 comments

Comments

@hypatia-of-sva
Copy link

Hello kcat,

I post this here because the official OpenAL mailing list seems dead, the link on your website (https://openal.org/mailman/listinfo/openal) seems to lead nowhere. Maybe you can ask what is going on there, if it moved or if is gone as of now.

My question:

I've seen that in late February you added the "AL_EXT_direct_context" extension to the public API of OpenAL soft (https://github.com/kcat/openal-soft/blob/master/include/AL/alext.h) also, and I was currently updating the alad function loader (https://github.com/hypatia-of-sva/alad/blob/main/alad.h), but unsure about how this extension should be handled.

Basically, these functions do carry the explicit context, and should therefore be loaded independently of the context; and since alGetProcAddress does depend on the current context, it seems not be applicable. Therefore, loading from the DLL might be better. On the other hand, the pointer might not be in the DLL directly, if it is in some extension DLL etc., hence the alGetProcAddress. might be necessary to be used, maybe with intermittendly setting the current context to NULL. Which of these ways seems more opportune to you? Or is there another way that I have not considered?

Since it's not yet in the stable release there is no rush to solve this, but I would be interested to hear how these functions should be loaded in the future.

With regards,
Hypatia of Sva.

@kcat
Copy link
Owner

kcat commented Mar 26, 2024

If you're using OpenAL Soft directly or through OpenAL Soft's router, it should be enough to get the Direct functions by calling alcGetProcAddress with an appropriate device handle (device handle not needed if not using the router). Any contexts then created from a device with support for the extension can be used with the same device's Direct functions. So for example:

ALCdevice *device = alcOpenDevice(NULL);
if(!alcIsExtensionSupported(device, "ALC_EXT_direct_context"))
    error("Direct contexts not supported");
alEnableDirect = alcGetProcAddress(device, "alEnableDirect");
alDisableDirect = alcGetProcAddress(device, "alDisableDirect");
... etc ...

ALCcontext *context = alcCreateContext(device, NULL);

alGenSourcesDirect(context, ...);

However, if you're using Creative's router, it needs to be done in two stages:

First, open a device normally with alcOpenDevice (from the router) and check that it supports the ALC_EXT_direct_context extension. Then, use alcGetProcAddress with the device handle to get the alcGetProcAddress2 function, and close the device with the router's alcCloseDevice. Then use alcGetProcAddress2 with the NULL device handle to load all the standard alcOpenDevice, alcCloseDevice, alcGetProcAddress, etc, functions to get the given device's driver-specific functions. E.g.:

ALCdevice *device = alcOpenDevice(NULL);
if(!alcIsExtensionSupported(device, "ALC_EXT_direct_context"))
    error("Direct contexts not supported");
alcGetProcAddress2 = alcGetProcAddress(device, "alcGetProcAddress2");
alcCloseDevice(device);

alcOpenDevice = alcGetProcAddress2(NULL, "alcOpenDevice");
alcCloseDevice = alcGetProcAddress2(NULL, "alcCloseDevice");
... etc ...

Then you can open the device again with the driver-specific alcOpenDevice function you just loaded, and treat it as if you're not using the router by using all the driver-specific functions directly.

This is necessary because Creative's router wraps the drivers' ALCdevice and ALCcontext handles, so even though you could get the Direct functions from the driver through the router, the driver's Direct functions won't recognize the router's ALCcontext handles the app has. Creative's router also has checks in alcGetProcAddress to always return its own functions when querying known functions, which necessitates the need for alcGetProcAddress2. This kind of bootstrapping is similar to how WGL works to bypass Microsoft's OpenGL layer; open the desired device from the system API, load in all the functions from the driver, then close the device with the system API and reopen it directly with the driver. This does unfortunately create an issue if you want to have multiple devices open from different drivers at the same time (you would need to maintain separate sets of function pointers for each device, and use the appropriate set for the given device/context), but I don't know of a way around it when using Creative's router.

@hypatia-of-sva
Copy link
Author

hypatia-of-sva commented May 4, 2024

Hello kcat,

it took me some time to get back to this, but I now decided on a way to deal with it. I was thinking about it, and in my opinion, no way of dealing with this with global function pointers is truly satisfying. Therefore, I settled on the following compromise: the old interface will simply not load the direct context functions at all, instead, you have to create a struct and load them yourself. For example, this is one way of replicating the later, more complicated result of yours:

aladDirectFunctions myDirectLoadingFunctions;
aladALFunctions alDirect;
aladALCFunctions alcDirect;
aladDirectFunctions direct;

ALCdevice *bakeDeviceForALCGetProcAddress = NULL;
ALCdevice *bakeDeviceForALCGetProcAddress2 = NULL;
aladFunction bakeALCGetProcAddress(const char *name) {
    return alcGetProcAddress(bakeDeviceForALCGetProcAddress, name);
}
aladFunction bakeALCGetProcAddress2(const char *name) {
    return myDirectLoadingFunctions.alcGetProcAddress2(bakeDeviceForALCGetProcAddress2 , name);
}

aladLoadAL();
ALCdevice *device = alcOpenDevice(NULL);
if(!alcIsExtensionSupported(device, "ALC_EXT_direct_context"))
    error("Direct contexts not supported");
bakeDeviceForALCGetProcAddress  = device;
aladLoadDirectExtension(&myDirectLoadingFunctions, bakeALCGetProcAddress);
alcCloseDevice(device);

bakeDeviceForALCGetProcAddress2  = NULL;
aladLoadALCoreMinimal(&alDirect, bakeALCGetProcAddress2);
aladLoadALCoreRest(&alDirect, bakeALCGetProcAddress2);
aladLoadEFX(&alDirect, bakeALCGetProcAddress2);
aladLoadALExtensions(&alDirect, bakeALCGetProcAddress2);
aladLoadALCCore(&alcDirect, bakeALCGetProcAddress2);
aladLoadALCExtensions(&alcDirect, bakeALCGetProcAddress2);
aladLoadDirectExtension(&direct, bakeALCGetProcAddress2);

/* now we can use the newly loaded alcOpenDevice als alcDirect.OpenDevice
   We can also still use myDirectLoadingFunctions function members to use direct
   functions loaded with the device before, if the function pointers are still valid
   and the same with the global alcOpenDevice, which is a shorthand for aladALC.OpenDevice. */

This is a bit more clunky, but also more flexible. It took me some time to sit with this to actually be comfortable with it, but I do think this is better than the alternative of focing some sort of scheme that may very well look wrong in the future.

Also, I wanted you to give a little heads up that you switched the order of alSourceRewindvDirect and alSourceRewindDirect in comparison to the pointer types LPALSOURCEREWINDVDIRECT and LPALSOURCEREWINDDIRECT, and the same with alSourcePausevDirect and alSourcePauseDirect in comparison to LPALSOURCEPAUSEVDIRECT and LPALSOURCEPAUSEDIRECT. Not something really important, but a little annoyance if you are trying to copy-paste these correctly into a struct with block selection cursors.

With regards,
Hypatia of Sva.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants