Atlas - SDL_openxrdyn.c
Home / ext / SDL / src / gpu / xr Lines: 3 | Size: 15738 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#include "SDL_openxrdyn.h" 24 25#ifdef HAVE_GPU_OPENXR 26 27#include <SDL3/SDL_dlopennote.h> 28#include <SDL3/SDL_openxr.h> 29 30#if defined(SDL_PLATFORM_APPLE) 31static const char *openxr_library_names[] = { "libopenxr_loader.dylib", NULL }; 32#elif defined(SDL_PLATFORM_WINDOWS) 33static const char *openxr_library_names[] = { "openxr_loader.dll", NULL }; 34#elif defined(SDL_PLATFORM_ANDROID) 35/* On Android, use the Khronos OpenXR loader (libopenxr_loader.so) which properly 36 * exports xrGetInstanceProcAddr. This is bundled via the Gradle dependency: 37 * implementation 'org.khronos.openxr:openxr_loader_for_android:X.Y.Z' 38 * 39 * The Khronos loader handles runtime discovery internally via the Android broker 40 * pattern and properly supports all pre-instance global functions. 41 * 42 * Note: Do NOT use Meta's forwardloader (libopenxr_forwardloader.so) - it doesn't 43 * export xrGetInstanceProcAddr directly and the function obtained via runtime 44 * negotiation crashes on pre-instance calls (e.g., xrEnumerateApiLayerProperties). */ 45static const char *openxr_library_names[] = { "libopenxr_loader.so", NULL }; 46#else 47static const char *openxr_library_names[] = { "libopenxr_loader.so.1", "libopenxr_loader.so", NULL }; 48SDL_ELF_NOTE_DLOPEN( 49 "gpu-openxr", 50 "Support for OpenXR with SDL_GPU rendering", 51 SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, 52 "libopenxr_loader.so.1", "libopenxr_loader.so" 53) 54#endif 55 56#define DEBUG_DYNAMIC_OPENXR 0 57 58typedef struct 59{ 60 SDL_SharedObject *lib; 61} openxrdynlib; 62 63static openxrdynlib openxr_loader = { NULL }; 64 65#ifndef SDL_PLATFORM_ANDROID 66static void *OPENXR_GetSym(const char *fnname, bool *failed) 67{ 68 void *fn = SDL_LoadFunction(openxr_loader.lib, fnname); 69 70#if DEBUG_DYNAMIC_OPENXR 71 if (fn) { 72 SDL_Log("OPENXR: Found '%s' in %s (%p)\n", fnname, dynlib->libname, fn); 73 } else { 74 SDL_Log("OPENXR: Symbol '%s' NOT FOUND!\n", fnname); 75 } 76#endif 77 78 return fn; 79} 80#endif 81 82// Define all the function pointers and wrappers... 83#define SDL_OPENXR_SYM(name) PFN_##name OPENXR_##name = NULL; 84#include "SDL_openxrsym.h" 85 86static int openxr_load_refcount = 0; 87 88#ifdef SDL_PLATFORM_ANDROID 89#include <jni.h> 90#include "../../video/khronos/openxr/openxr_platform.h" 91 92/* On Android, we need to initialize the loader with JNI context before use */ 93static bool openxr_android_loader_initialized = false; 94 95static bool OPENXR_InitializeAndroidLoader(void) 96{ 97 XrResult result; 98 PFN_xrInitializeLoaderKHR initializeLoader = NULL; 99 PFN_xrGetInstanceProcAddr loaderGetProcAddr = NULL; 100 JNIEnv *env = NULL; 101 JavaVM *vm = NULL; 102 jobject activity = NULL; 103 104 if (openxr_android_loader_initialized) { 105 return true; 106 } 107 108 /* The Khronos OpenXR loader (libopenxr_loader.so) properly exports xrGetInstanceProcAddr. 109 * Get it directly from the library - this is the standard approach. */ 110 loaderGetProcAddr = (PFN_xrGetInstanceProcAddr)SDL_LoadFunction(openxr_loader.lib, "xrGetInstanceProcAddr"); 111 112 if (loaderGetProcAddr == NULL) { 113 SDL_SetError("Failed to get xrGetInstanceProcAddr from OpenXR loader. " 114 "Make sure you're using the Khronos loader (libopenxr_loader.so), " 115 "not Meta's forwardloader."); 116 return false; 117 } 118 119#if DEBUG_DYNAMIC_OPENXR 120 SDL_Log("SDL/OpenXR: Got xrGetInstanceProcAddr from loader: %p", (void*)loaderGetProcAddr); 121#endif 122 123 /* Get xrInitializeLoaderKHR via xrGetInstanceProcAddr */ 124 result = loaderGetProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&initializeLoader); 125 if (XR_FAILED(result) || initializeLoader == NULL) { 126 SDL_SetError("Failed to get xrInitializeLoaderKHR (result: %d)", (int)result); 127 return false; 128 } 129 130#if DEBUG_DYNAMIC_OPENXR 131 SDL_Log("SDL/OpenXR: Got xrInitializeLoaderKHR: %p", (void*)initializeLoader); 132#endif 133 134 /* Get Android environment info from SDL */ 135 env = (JNIEnv *)SDL_GetAndroidJNIEnv(); 136 if (!env) { 137 SDL_SetError("Failed to get Android JNI environment"); 138 return false; 139 } 140 141 if ((*env)->GetJavaVM(env, &vm) != 0) { 142 SDL_SetError("Failed to get JavaVM from JNIEnv"); 143 return false; 144 } 145 146 activity = (jobject)SDL_GetAndroidActivity(); 147 if (!activity) { 148 SDL_SetError("Failed to get Android activity"); 149 return false; 150 } 151 152 XrLoaderInitInfoAndroidKHR loaderInitInfo = { 153 .type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR, 154 .next = NULL, 155 .applicationVM = vm, 156 .applicationContext = activity 157 }; 158 159 result = initializeLoader((XrLoaderInitInfoBaseHeaderKHR *)&loaderInitInfo); 160 if (XR_FAILED(result)) { 161 SDL_SetError("xrInitializeLoaderKHR failed with result %d", (int)result); 162 return false; 163 } 164 165#if DEBUG_DYNAMIC_OPENXR 166 SDL_Log("SDL/OpenXR: xrInitializeLoaderKHR succeeded"); 167#endif 168 169 /* Store the xrGetInstanceProcAddr function - this one properly handles 170 * all pre-instance calls (unlike Meta's forwardloader runtime negotiation) */ 171 OPENXR_xrGetInstanceProcAddr = loaderGetProcAddr; 172 xrGetInstanceProcAddr = loaderGetProcAddr; 173 174 openxr_android_loader_initialized = true; 175 return true; 176} 177#endif /* SDL_PLATFORM_ANDROID */ 178 179SDL_DECLSPEC void SDLCALL SDL_OpenXR_UnloadLibrary(void) 180{ 181#if DEBUG_DYNAMIC_OPENXR 182 SDL_Log("SDL/OpenXR: UnloadLibrary called, current refcount=%d", openxr_load_refcount); 183#endif 184 185 // Don't actually unload if more than one module is using the libs... 186 if (openxr_load_refcount > 0) { 187 if (--openxr_load_refcount == 0) { 188 189#if DEBUG_DYNAMIC_OPENXR 190 SDL_Log("SDL/OpenXR: Refcount reached 0, unloading library"); 191#endif 192 193#ifdef SDL_PLATFORM_ANDROID 194 /* On Android/Quest, don't actually unload the library or reset the loader state. 195 * The Quest OpenXR runtime doesn't support being re-initialized after teardown. 196 * xrInitializeLoaderKHR and xrNegotiateLoaderRuntimeInterface must only be called once. 197 * We keep the library loaded and the loader initialized. 198 * 199 * IMPORTANT: We also keep xrGetInstanceProcAddr intact so we can reload other 200 * function pointers on the next LoadLibrary call. Only NULL out the other symbols. */ 201#if DEBUG_DYNAMIC_OPENXR 202 SDL_Log("SDL/OpenXR: Android - keeping library loaded and loader initialized"); 203#endif 204 205 // Only NULL out non-essential function pointers, keep xrGetInstanceProcAddr 206#define SDL_OPENXR_SYM(name) \ 207 if (SDL_strcmp(#name, "xrGetInstanceProcAddr") != 0) { \ 208 OPENXR_##name = NULL; \ 209 } 210#include "SDL_openxrsym.h" 211#else 212 // On non-Android, NULL everything and unload 213#define SDL_OPENXR_SYM(name) OPENXR_##name = NULL; 214#include "SDL_openxrsym.h" 215 216 SDL_UnloadObject(openxr_loader.lib); 217 openxr_loader.lib = NULL; 218#endif 219 } 220#if DEBUG_DYNAMIC_OPENXR 221 else { 222 SDL_Log("SDL/OpenXR: Refcount is now %d, not unloading", openxr_load_refcount); 223 } 224#endif 225 } 226} 227 228// returns non-zero if all needed symbols were loaded. 229SDL_DECLSPEC bool SDLCALL SDL_OpenXR_LoadLibrary(void) 230{ 231 bool result = true; 232 233#if DEBUG_DYNAMIC_OPENXR 234 SDL_Log("SDL/OpenXR: LoadLibrary called, current refcount=%d, lib=%p", openxr_load_refcount, (void*)openxr_loader.lib); 235#endif 236 237 // deal with multiple modules (gpu, openxr, etc) needing these symbols... 238 if (openxr_load_refcount++ == 0) { 239#ifdef SDL_PLATFORM_ANDROID 240 /* On Android, the library may already be loaded if this is a reload after 241 * unload (we don't actually unload on Android to preserve runtime state) */ 242 if (openxr_loader.lib == NULL) { 243#endif 244 const char *path_hint = SDL_GetHint(SDL_HINT_OPENXR_LIBRARY); 245 246 // If a hint was specified, try that first 247 if (path_hint && *path_hint) { 248 openxr_loader.lib = SDL_LoadObject(path_hint); 249 } 250 251 // If no hint or hint failed, try the default library names 252 if (!openxr_loader.lib) { 253 for (int i = 0; openxr_library_names[i] != NULL; i++) { 254 openxr_loader.lib = SDL_LoadObject(openxr_library_names[i]); 255 if (openxr_loader.lib) { 256 break; 257 } 258 } 259 } 260 261 if (!openxr_loader.lib) { 262 SDL_SetError("Failed to load OpenXR loader library. " 263 "On Windows, ensure openxr_loader.dll is in your application directory or PATH. " 264 "On Linux, install the OpenXR loader package (libopenxr-loader) or set LD_LIBRARY_PATH. " 265 "You can also use the SDL_HINT_OPENXR_LIBRARY hint to specify the loader path."); 266 openxr_load_refcount--; 267 return false; 268 } 269#if defined(SDL_PLATFORM_ANDROID) 270 } else { 271#if DEBUG_DYNAMIC_OPENXR 272 SDL_Log("SDL/OpenXR: Library already loaded (Android reload), skipping SDL_LoadObject"); 273#endif 274 } 275#endif 276 277#ifdef SDL_PLATFORM_ANDROID 278 /* On Android, we need to initialize the loader before other functions work. 279 * OPENXR_InitializeAndroidLoader() will return early if already initialized. */ 280 if (!OPENXR_InitializeAndroidLoader()) { 281 SDL_UnloadObject(openxr_loader.lib); 282 openxr_loader.lib = NULL; 283 openxr_load_refcount--; 284 return false; 285 } 286#endif 287 288 bool failed = false; 289 290#ifdef SDL_PLATFORM_ANDROID 291 /* On Android with Meta's forwardloader, we need special handling. 292 * After calling xrInitializeLoaderKHR, the global functions should be available 293 * either as direct exports from the forwardloader or via xrGetInstanceProcAddr(NULL, ...). 294 * 295 * Try getting functions directly from the forwardloader first since they'll go 296 * through the proper forwarding path. */ 297 XrResult xrResult; 298 299#if DEBUG_DYNAMIC_OPENXR 300 SDL_Log("SDL/OpenXR: Loading global functions..."); 301#endif 302 303 /* First try to get functions directly from the forwardloader library */ 304 OPENXR_xrEnumerateApiLayerProperties = (PFN_xrEnumerateApiLayerProperties)SDL_LoadFunction(openxr_loader.lib, "xrEnumerateApiLayerProperties"); 305 OPENXR_xrCreateInstance = (PFN_xrCreateInstance)SDL_LoadFunction(openxr_loader.lib, "xrCreateInstance"); 306 OPENXR_xrEnumerateInstanceExtensionProperties = (PFN_xrEnumerateInstanceExtensionProperties)SDL_LoadFunction(openxr_loader.lib, "xrEnumerateInstanceExtensionProperties"); 307 308#if DEBUG_DYNAMIC_OPENXR 309 SDL_Log("SDL/OpenXR: Direct symbols - xrEnumerateApiLayerProperties=%p, xrCreateInstance=%p, xrEnumerateInstanceExtensionProperties=%p", 310 (void*)OPENXR_xrEnumerateApiLayerProperties, 311 (void*)OPENXR_xrCreateInstance, 312 (void*)OPENXR_xrEnumerateInstanceExtensionProperties); 313#endif 314 315 /* If direct loading failed, fall back to xrGetInstanceProcAddr(NULL, ...) */ 316 if (OPENXR_xrEnumerateApiLayerProperties == NULL) { 317 xrResult = xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrEnumerateApiLayerProperties", (PFN_xrVoidFunction*)&OPENXR_xrEnumerateApiLayerProperties); 318 if (XR_FAILED(xrResult) || OPENXR_xrEnumerateApiLayerProperties == NULL) { 319#if DEBUG_DYNAMIC_OPENXR 320 SDL_Log("SDL/OpenXR: Failed to get xrEnumerateApiLayerProperties via xrGetInstanceProcAddr"); 321#endif 322 failed = true; 323 } 324 } 325 326 if (OPENXR_xrCreateInstance == NULL) { 327 xrResult = xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrCreateInstance", (PFN_xrVoidFunction*)&OPENXR_xrCreateInstance); 328 if (XR_FAILED(xrResult) || OPENXR_xrCreateInstance == NULL) { 329#if DEBUG_DYNAMIC_OPENXR 330 SDL_Log("SDL/OpenXR: Failed to get xrCreateInstance via xrGetInstanceProcAddr"); 331#endif 332 failed = true; 333 } 334 } 335 336 if (OPENXR_xrEnumerateInstanceExtensionProperties == NULL) { 337 xrResult = xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties", (PFN_xrVoidFunction*)&OPENXR_xrEnumerateInstanceExtensionProperties); 338 if (XR_FAILED(xrResult) || OPENXR_xrEnumerateInstanceExtensionProperties == NULL) { 339#if DEBUG_DYNAMIC_OPENXR 340 SDL_Log("SDL/OpenXR: Failed to get xrEnumerateInstanceExtensionProperties via xrGetInstanceProcAddr"); 341#endif 342 failed = true; 343 } 344 } 345 346#if DEBUG_DYNAMIC_OPENXR 347 SDL_Log("SDL/OpenXR: Final symbols - xrEnumerateApiLayerProperties=%p, xrCreateInstance=%p, xrEnumerateInstanceExtensionProperties=%p", 348 (void*)OPENXR_xrEnumerateApiLayerProperties, 349 (void*)OPENXR_xrCreateInstance, 350 (void*)OPENXR_xrEnumerateInstanceExtensionProperties); 351 352 SDL_Log("SDL/OpenXR: Global functions loading %s", failed ? "FAILED" : "succeeded"); 353#endif 354#else 355#define SDL_OPENXR_SYM(name) OPENXR_##name = (PFN_##name)OPENXR_GetSym(#name, &failed); 356#include "SDL_openxrsym.h" 357#endif 358 359 if (failed) { 360 // in case something got loaded... 361 SDL_OpenXR_UnloadLibrary(); 362 result = false; 363 } 364 } 365#if DEBUG_DYNAMIC_OPENXR 366 else { 367 SDL_Log("SDL/OpenXR: Library already loaded (refcount=%d), skipping", openxr_load_refcount); 368 } 369#endif 370 371 return result; 372} 373 374SDL_DECLSPEC PFN_xrGetInstanceProcAddr SDLCALL SDL_OpenXR_GetXrGetInstanceProcAddr(void) 375{ 376 if (xrGetInstanceProcAddr == NULL) { 377 SDL_SetError("The OpenXR loader has not been loaded"); 378 } 379 380 return xrGetInstanceProcAddr; 381} 382 383XrInstancePfns *SDL_OPENXR_LoadInstanceSymbols(XrInstance instance) 384{ 385 XrResult result; 386 387 XrInstancePfns *pfns = SDL_calloc(1, sizeof(XrInstancePfns)); 388 389#define SDL_OPENXR_INSTANCE_SYM(name) \ 390 result = xrGetInstanceProcAddr(instance, #name, (PFN_xrVoidFunction *)&pfns->name); \ 391 if (result != XR_SUCCESS) { \ 392 SDL_free(pfns); \ 393 return NULL; \ 394 } 395#include "SDL_openxrsym.h" 396 397 return pfns; 398} 399 400#else 401 402SDL_DECLSPEC bool SDLCALL SDL_OpenXR_LoadLibrary(void) 403{ 404 return SDL_SetError("OpenXR is not enabled in this build of SDL"); 405} 406 407SDL_DECLSPEC void SDLCALL SDL_OpenXR_UnloadLibrary(void) 408{ 409 SDL_SetError("OpenXR is not enabled in this build of SDL"); 410} 411 412SDL_DECLSPEC PFN_xrGetInstanceProcAddr SDLCALL SDL_OpenXR_GetXrGetInstanceProcAddr(void) 413{ 414 return (PFN_xrGetInstanceProcAddr)SDL_SetError("OpenXR is not enabled in this build of SDL"); 415} 416 417#endif // HAVE_GPU_OPENXR 418[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.