From 9a91d7236a13e8770af4cdb7a880a24de397fdc2 Mon Sep 17 00:00:00 2001
From: Aaron Benjamin <[EMAIL REDACTED]>
Date: Fri, 30 Jan 2026 17:18:51 -0500
Subject: [PATCH] GPU: OpenXR integration (#14837)
Based on Beyley's initial draft in #11601.
Co-authored-by: Beyley Cardellio <ep1cm1n10n123@gmail.com>
Co-authored-by: Ethan Lee <flibitijibibo@gmail.com>
---
Android.mk | 1 +
CMakeLists.txt | 6 +
VisualC-GDK/SDL/SDL.vcxproj | 6 +
VisualC-GDK/SDL/SDL.vcxproj.filters | 3 +
VisualC/SDL/SDL.vcxproj | 4 +
VisualC/SDL/SDL.vcxproj.filters | 29 +
Xcode/SDL/SDL.xcodeproj/project.pbxproj | 22 +
include/SDL3/SDL_gpu.h | 14 +
include/SDL3/SDL_hints.h | 12 +
include/SDL3/SDL_openxr.h | 207 +
include/build_config/SDL_build_config.h.cmake | 1 +
.../build_config/SDL_build_config_android.h | 1 +
.../build_config/SDL_build_config_windows.h | 1 +
src/SDL_internal.h | 1 +
src/dynapi/SDL_dynapi.c | 2 +
src/dynapi/SDL_dynapi.sym | 7 +
src/dynapi/SDL_dynapi_overrides.h | 7 +
src/dynapi/SDL_dynapi_procs.h | 7 +
src/gpu/SDL_gpu.c | 47 +
src/gpu/SDL_sysgpu.h | 29 +
src/gpu/d3d12/SDL_gpu_d3d12.c | 577 +-
src/gpu/metal/SDL_gpu_metal.m | 49 +
src/gpu/vulkan/SDL_gpu_vulkan.c | 815 +-
src/gpu/xr/SDL_gpu_openxr.c | 199 +
src/gpu/xr/SDL_gpu_openxr.h | 30 +
src/gpu/xr/SDL_openxr_internal.h | 52 +
src/gpu/xr/SDL_openxrdyn.c | 414 +
src/gpu/xr/SDL_openxrdyn.h | 55 +
src/gpu/xr/SDL_openxrsym.h | 49 +
src/video/khronos/openxr/openxr.h | 8583 +++++++++++++++++
.../openxr/openxr_loader_negotiation.h | 141 +
src/video/khronos/openxr/openxr_platform.h | 776 ++
.../khronos/openxr/openxr_platform_defines.h | 114 +
src/video/khronos/openxr/openxr_reflection.h | 7226 ++++++++++++++
.../openxr/openxr_reflection_parent_structs.h | 330 +
test/testsymbols.c | 2 +
36 files changed, 19720 insertions(+), 99 deletions(-)
create mode 100644 include/SDL3/SDL_openxr.h
create mode 100644 src/gpu/xr/SDL_gpu_openxr.c
create mode 100644 src/gpu/xr/SDL_gpu_openxr.h
create mode 100644 src/gpu/xr/SDL_openxr_internal.h
create mode 100644 src/gpu/xr/SDL_openxrdyn.c
create mode 100644 src/gpu/xr/SDL_openxrdyn.h
create mode 100644 src/gpu/xr/SDL_openxrsym.h
create mode 100644 src/video/khronos/openxr/openxr.h
create mode 100644 src/video/khronos/openxr/openxr_loader_negotiation.h
create mode 100644 src/video/khronos/openxr/openxr_platform.h
create mode 100644 src/video/khronos/openxr/openxr_platform_defines.h
create mode 100644 src/video/khronos/openxr/openxr_reflection.h
create mode 100644 src/video/khronos/openxr/openxr_reflection_parent_structs.h
diff --git a/Android.mk b/Android.mk
index f4600bfa2757e..2e3b11483c75e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -39,6 +39,7 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/io/generic/*.c) \
$(wildcard $(LOCAL_PATH)/src/gpu/*.c) \
$(wildcard $(LOCAL_PATH)/src/gpu/vulkan/*.c) \
+ $(wildcard $(LOCAL_PATH)/src/gpu/xr/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/dummy/*.c) \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e03bae7ee5a3d..1211d81acc63c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -391,6 +391,7 @@ set_option(SDL_LIBUDEV "Enable libudev support" ON)
set_option(SDL_ASAN "Use AddressSanitizer to detect memory errors" OFF)
set_option(SDL_CCACHE "Use Ccache to speed up build" OFF)
set_option(SDL_CLANG_TIDY "Run clang-tidy static analysis" OFF)
+dep_option(SDL_GPU_OPENXR "Build SDL_GPU with OpenXR support" ON "SDL_GPU;NOT RISCOS" OFF)
set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION")
@@ -1272,6 +1273,8 @@ sdl_glob_sources(
"${SDL3_SOURCE_DIR}/src/filesystem/*.h"
"${SDL3_SOURCE_DIR}/src/gpu/*.c"
"${SDL3_SOURCE_DIR}/src/gpu/*.h"
+ "${SDL3_SOURCE_DIR}/src/gpu/xr/*.c"
+ "${SDL3_SOURCE_DIR}/src/gpu/xr/*.h"
"${SDL3_SOURCE_DIR}/src/joystick/*.c"
"${SDL3_SOURCE_DIR}/src/joystick/*.h"
"${SDL3_SOURCE_DIR}/src/haptic/*.c"
@@ -3550,6 +3553,9 @@ if(SDL_GPU)
set(SDL_VIDEO_RENDER_GPU 1)
set(HAVE_RENDER_GPU TRUE)
endif()
+ if(SDL_GPU_OPENXR)
+ set(HAVE_GPU_OPENXR 1)
+ endif()
endif()
# Dummies
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index a1356ab17f8f5..c92e3b4f8328c 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -927,6 +927,12 @@
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.XboxOne.x64'">CompileAsCpp</CompileAs>
</ClCompile>
<ClCompile Include="..\..\src\gpu\vulkan\SDL_gpu_vulkan.c" />
+ <ClCompile Include="..\..\src\gpu\xr\SDL_gpu_openxr.c" />
+ <ClCompile Include="..\..\src\gpu\xr\SDL_openxrdyn.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\gpu\xr\SDL_gpu_openxr_c.h" />
+ <ClInclude Include="..\..\src\gpu\xr\SDL_openxr_internal.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\src\core\windows\version.rc" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index a2f431abf1942..06bcb2460c467 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -52,6 +52,9 @@
<ClCompile Include="..\..\src\gpu\SDL_gpu.c" />
<ClCompile Include="..\..\src\gpu\d3d12\SDL_gpu_d3d12.c" />
<ClCompile Include="..\..\src\gpu\vulkan\SDL_gpu_vulkan.c" />
+ <ClCompile Include="..\..\src\gpu\xr\SDL_gpu_openxr.c" />
+ <ClCompile Include="..\..\src\gpu\xr\SDL_openxrdyn.c" />
+ <ClInclude Include="..\..\src\gpu\xr\SDL_openxr_internal.h" />
<ClCompile Include="..\..\src\haptic\dummy\SDL_syshaptic.c" />
<ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 588f17693ba42..4ce70c0261b72 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -475,6 +475,8 @@
<ClInclude Include="..\..\src\gpu\d3d12\D3D12_Blit.h" />
<ClInclude Include="..\..\src\gpu\SDL_sysgpu.h" />
<ClInclude Include="..\..\src\gpu\vulkan\SDL_gpu_vulkan_vkfuncs.h" />
+ <ClInclude Include="..\..\src\gpu\xr\SDL_gpu_openxr_c.h" />
+ <ClInclude Include="..\..\src\gpu\xr\SDL_openxr_internal.h" />
<ClInclude Include="..\..\src\hidapi\SDL_hidapi_windows.h" />
<ClInclude Include="..\..\src\hidapi\windows\hidapi_cfgmgr32.h" />
<ClInclude Include="..\..\src\hidapi\windows\hidapi_descriptor_reconstruct.h" />
@@ -629,6 +631,8 @@
<ClCompile Include="..\..\src\gpu\SDL_gpu.c" />
<ClCompile Include="..\..\src\gpu\d3d12\SDL_gpu_d3d12.c" />
<ClCompile Include="..\..\src\gpu\vulkan\SDL_gpu_vulkan.c" />
+ <ClCompile Include="..\..\src\gpu\xr\SDL_gpu_openxr.c" />
+ <ClCompile Include="..\..\src\gpu\xr\SDL_openxrdyn.c" />
<ClCompile Include="..\..\src\io\generic\SDL_asyncio_generic.c" />
<ClCompile Include="..\..\src\io\SDL_asyncio.c" />
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index c97143a205015..3c2dab84a9410 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -981,6 +981,15 @@
<ClInclude Include="..\..\src\gpu\SDL_sysgpu.h">
<Filter>gpu</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\gpu\vulkan\SDL_gpu_vulkan_vkfuncs.h">
+ <Filter>gpu</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\gpu\xr\SDL_gpu_openxr_c.h">
+ <Filter>gpu</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\gpu\xr\SDL_openxr_internal.h">
+ <Filter>gpu</Filter>
+ </ClInclude>
<ClInclude Include="..\..\include\SDL3\SDL_storage.h" />
<ClInclude Include="..\..\include\SDL3\SDL_time.h" />
<ClInclude Include="..\..\src\core\SDL_core_unsupported.h" />
@@ -1953,6 +1962,26 @@
<ClCompile Include="..\..\src\gpu\SDL_gpu.c">
<Filter>gpu</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\gpu\d3d12\SDL_gpu_d3d12.c">
+ <Filter>gpu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\gpu\vulkan\SDL_gpu_vulkan.c">
+ <Filter>gpu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\gpu\xr\SDL_gpu_openxr.c">
+ <Filter>gpu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\gpu\xr\SDL_openxrdyn.c">
+ <Filter>gpu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\process\SDL_process.c" />
+ <ClCompile Include="..\..\src\process\windows\SDL_windowsprocess.c" />
+ <ClCompile Include="..\..\src\render\gpu\SDL_pipeline_gpu.c" />
+ <ClCompile Include="..\..\src\render\gpu\SDL_render_gpu.c" />
+ <ClCompile Include="..\..\src\render\gpu\SDL_shaders_gpu.c" />
+ <ClCompile Include="..\..\src\storage\generic\SDL_genericstorage.c" />
+ <ClCompile Include="..\..\src\storage\steam\SDL_steamstorage.c" />
+ <ClCompile Include="..\..\src\storage\SDL_storage.c" />
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_steam_triton.c">
<Filter>joystick\hidapi</Filter>
</ClCompile>
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index 46a6edc2993e6..a7c4f3d01ab86 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -366,6 +366,10 @@
E4F257952C81903800FCEAFC /* SDL_gpu_vulkan.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F257832C81903800FCEAFC /* SDL_gpu_vulkan.c */; };
E4F257962C81903800FCEAFC /* SDL_gpu.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F257852C81903800FCEAFC /* SDL_gpu.c */; };
E4F257972C81903800FCEAFC /* SDL_sysgpu.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F257862C81903800FCEAFC /* SDL_sysgpu.h */; };
+ E4F257982C81903800FCEAFC /* SDL_gpu_openxr.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F257882C81903800FCEAFC /* SDL_gpu_openxr.c */; };
+ E4F257992C81903800FCEAFC /* SDL_openxrdyn.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F257892C81903800FCEAFC /* SDL_openxrdyn.c */; };
+ E4F2579A2C81903800FCEAFC /* SDL_gpu_openxr_c.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F2578A2C81903800FCEAFC /* SDL_gpu_openxr_c.h */; };
+ E4F2579B2C81903800FCEAFC /* SDL_openxr_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F2578C2C81903800FCEAFC /* SDL_openxr_internal.h */; };
E4F7981A2AD8D84800669F54 /* SDL_core_unsupported.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F798192AD8D84800669F54 /* SDL_core_unsupported.c */; };
E4F7981C2AD8D85500669F54 /* SDL_dynapi_unsupported.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F7981B2AD8D85500669F54 /* SDL_dynapi_unsupported.h */; };
E4F7981E2AD8D86A00669F54 /* SDL_render_unsupported.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F7981D2AD8D86A00669F54 /* SDL_render_unsupported.c */; };
@@ -937,6 +941,10 @@
E4F257832C81903800FCEAFC /* SDL_gpu_vulkan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gpu_vulkan.c; sourceTree = "<group>"; };
E4F257852C81903800FCEAFC /* SDL_gpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gpu.c; sourceTree = "<group>"; };
E4F257862C81903800FCEAFC /* SDL_sysgpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysgpu.h; sourceTree = "<group>"; };
+ E4F257882C81903800FCEAFC /* SDL_gpu_openxr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gpu_openxr.c; sourceTree = "<group>"; };
+ E4F257892C81903800FCEAFC /* SDL_openxrdyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_openxrdyn.c; sourceTree = "<group>"; };
+ E4F2578A2C81903800FCEAFC /* SDL_gpu_openxr_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gpu_openxr_c.h; sourceTree = "<group>"; };
+ E4F2578C2C81903800FCEAFC /* SDL_openxr_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_openxr_internal.h; sourceTree = "<group>"; };
E4F798192AD8D84800669F54 /* SDL_core_unsupported.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_core_unsupported.c; sourceTree = "<group>"; };
E4F7981B2AD8D85500669F54 /* SDL_dynapi_unsupported.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi_unsupported.h; sourceTree = "<group>"; };
E4F7981D2AD8D86A00669F54 /* SDL_render_unsupported.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_unsupported.c; sourceTree = "<group>"; };
@@ -2354,11 +2362,23 @@
path = vulkan;
sourceTree = "<group>";
};
+ E4F2578B2C81903800FCEAFC /* xr */ = {
+ isa = PBXGroup;
+ children = (
+ E4F257882C81903800FCEAFC /* SDL_gpu_openxr.c */,
+ E4F257892C81903800FCEAFC /* SDL_openxrdyn.c */,
+ E4F2578A2C81903800FCEAFC /* SDL_gpu_openxr_c.h */,
+ E4F2578C2C81903800FCEAFC /* SDL_openxr_internal.h */,
+ );
+ path = xr;
+ sourceTree = "<group>";
+ };
E4F257872C81903800FCEAFC /* gpu */ = {
isa = PBXGroup;
children = (
E4F257812C81903800FCEAFC /* metal */,
E4F257842C81903800FCEAFC /* vulkan */,
+ E4F2578B2C81903800FCEAFC /* xr */,
E4F257852C81903800FCEAFC /* SDL_gpu.c */,
E4F257862C81903800FCEAFC /* SDL_sysgpu.h */,
);
@@ -2946,6 +2966,8 @@
A7D8A95723E2514000DCD162 /* SDL_atomic.c in Sources */,
A75FDBCE23EA380300529352 /* SDL_hidapi_rumble.c in Sources */,
E4F257952C81903800FCEAFC /* SDL_gpu_vulkan.c in Sources */,
+ E4F257982C81903800FCEAFC /* SDL_gpu_openxr.c in Sources */,
+ E4F257992C81903800FCEAFC /* SDL_openxrdyn.c in Sources */,
A7D8BB2723E2514500DCD162 /* SDL_displayevents.c in Sources */,
A7D8AB2523E2514100DCD162 /* SDL_log.c in Sources */,
A7D8AE8823E2514100DCD162 /* SDL_cocoaopengl.m in Sources */,
diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index 2fe19429df8ac..cfcad7464bc46 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -2364,6 +2364,20 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDeviceWithProperties(
#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_OPTIONS_POINTER "SDL.gpu.device.create.vulkan.options"
#define SDL_PROP_GPU_DEVICE_CREATE_METAL_ALLOW_MACFAMILY1_BOOLEAN "SDL.gpu.device.create.metal.allowmacfamily1"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_ENABLE_BOOLEAN "SDL.gpu.device.create.xr.enable"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_INSTANCE_POINTER "SDL.gpu.device.create.xr.instance_out"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_SYSTEM_ID_POINTER "SDL.gpu.device.create.xr.system_id_out"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_VERSION_NUMBER "SDL.gpu.device.create.xr.version"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_FORM_FACTOR_NUMBER "SDL.gpu.device.create.xr.form_factor"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_EXTENSION_COUNT_NUMBER "SDL.gpu.device.create.xr.extensions.count"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_EXTENSION_NAMES_POINTER "SDL.gpu.device.create.xr.extensions.names"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_LAYER_COUNT_NUMBER "SDL.gpu.device.create.xr.layers.count"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_LAYER_NAMES_POINTER "SDL.gpu.device.create.xr.layers.names"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_APPLICATION_NAME_STRING "SDL.gpu.device.create.xr.application.name"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_APPLICATION_VERSION_NUMBER "SDL.gpu.device.create.xr.application.version"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_ENGINE_NAME_STRING "SDL.gpu.device.create.xr.engine.name"
+#define SDL_PROP_GPU_DEVICE_CREATE_XR_ENGINE_VERSION_NUMBER "SDL.gpu.device.create.xr.engine.version"
+
/**
* A structure specifying additional options when using Vulkan.
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index ce9b711f7f3e4..d7841e398b89a 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1145,6 +1145,18 @@ extern "C" {
*/
#define SDL_HINT_GPU_DRIVER "SDL_GPU_DRIVER"
+/**
+ * A variable that specifies the library name to use when loading the OpenXR loader.
+ *
+ * By default, SDL will try the system default name, but on some platforms like Windows,
+ * debug builds of the OpenXR loader have a different name, and are not always directly compatible with release applications.
+ * Setting this hint allows you to compensate for this difference in your app when applicable.
+ *
+ * This hint should be set before the OpenXR loader is loaded.
+ * For example, creating an OpenXR GPU device will load the OpenXR loader.
+ */
+#define SDL_HINT_OPENXR_LIBRARY "SDL_OPENXR_LIBRARY"
+
/**
* A variable to control whether SDL_hid_enumerate() enumerates all HID
* devices or only controllers.
diff --git a/include/SDL3/SDL_openxr.h b/include/SDL3/SDL_openxr.h
new file mode 100644
index 0000000000000..e8d9c06f83b20
--- /dev/null
+++ b/include/SDL3/SDL_openxr.h
@@ -0,0 +1,207 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/**
+ * # CategoryOpenXR
+ *
+ * Functions for creating OpenXR handles for SDL_gpu contexts.
+ *
+ * For the most part, OpenXR operates independent of SDL, but
+ * the graphics initialization depends on direct support from SDL_gpu.
+ *
+ */
+
+#ifndef SDL_openxr_h_
+#define SDL_openxr_h_
+
+#include <SDL3/SDL_stdinc.h>
+#include <SDL3/SDL_gpu.h>
+
+#include <SDL3/SDL_begin_code.h>
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(OPENXR_H_)
+#define NO_SDL_OPENXR_TYPEDEFS 1
+#endif /* OPENXR_H_ */
+
+#if !defined(NO_SDL_OPENXR_TYPEDEFS)
+#define XR_NULL_HANDLE 0
+
+#if !defined(XR_DEFINE_HANDLE)
+ #define XR_DEFINE_HANDLE(object) typedef Uint64 object;
+#endif /* XR_DEFINE_HANDLE */
+
+typedef enum XrStructureType {
+ XR_TYPE_SESSION_CREATE_INFO = 8,
+ XR_TYPE_SWAPCHAIN_CREATE_INFO = 9,
+} XrStructureType;
+
+XR_DEFINE_HANDLE(XrInstance)
+XR_DEFINE_HANDLE(XrSystemId)
+XR_DEFINE_HANDLE(XrSession)
+XR_DEFINE_HANDLE(XrSwapchain)
+
+typedef struct {
+ XrStructureType type;
+ const void* next;
+} XrSessionCreateInfo;
+typedef struct {
+ XrStructureType type;
+ const void* next;
+} XrSwapchainCreateInfo;
+
+typedef enum XrResult {
+ XR_ERROR_FUNCTION_UNSUPPORTED = -7,
+ XR_ERROR_HANDLE_INVALID = -12,
+} XrResult;
+
+#define PFN_xrGetInstanceProcAddr SDL_FunctionPointer
+#endif /* NO_SDL_OPENXR_TYPEDEFS */
+
+/**
+ * Creates an OpenXR session. The OpenXR system ID is pulled from the passed GPU context.
+ *
+ * \param device a GPU context.
+ * \param createinfo the create info for the OpenXR session, sans the system ID.
+ * \param session a pointer filled in with an OpenXR session created for the given device.
+ * \returns the result of the call.
+ *
+ * \sa SDL_CreateGPUDeviceWithProperties
+ */
+extern SDL_DECLSPEC XrResult SDLCALL SDL_CreateGPUXRSession(SDL_GPUDevice *device, const XrSessionCreateInfo *createinfo, XrSession *session);
+
+/**
+ * Queries the GPU device for supported XR swapchain image formats.
+ *
+ * The returned pointer should be allocated with SDL_malloc() and will be
+ * passed to SDL_free().
+ *
+ * \param device a GPU context.
+ * \param session an OpenXR session created for the given device.
+ * \param num_formats a pointer filled with the number of supported XR swapchain formats.
+ * \returns a 0 terminated array of supported formats or NULL on failure;
+ * call SDL_GetError() for more information. This should be freed
+ * with SDL_free() when it is no longer needed.
+ *
+ * \sa SDL_CreateGPUXRSwapchain
+ */
+extern SDL_DECLSPEC SDL_GPUTextureFormat * SDLCALL SDL_GetGPUXRSwapchainFormats(SDL_GPUDevice *device, XrSession session, int *num_formats);
+
+/**
+ * Creates an OpenXR swapchain.
+ *
+ * The array returned via `textures` is sized according to
+ *`xrEnumerateSwapchainImages`, and thus should only be accessed via index
+ * values returned from `xrAcquireSwapchainImage`.
+ *
+ * Applications are still allowed to call `xrEnumerateSwapchainImages` on the
+ * returned XrSwapchain if they need to get the exact size of the array.
+ *
+ * \param device a GPU context.
+ * \param session an OpenXR session created for the given device.
+ * \param createinfo the create info for the OpenXR swapchain, sans the format.
+ * \param format a supported format for the OpenXR swapchain.
+ * \param swapchain a pointer filled in with the created OpenXR swapchain.
+ * \param textures a pointer filled in with the array of created swapchain images.
+ * \returns the result of the call.
+ *
+ * \sa SDL_CreateGPUDeviceWithProperties
+ * \sa SDL_CreateGPUXRSession
+ * \sa SDL_GetGPUXRSwapchainFormats
+ * \sa SDL_DestroyGPUXRSwapchain
+ */
+extern SDL_DECLSPEC XrResult SDLCALL SDL_CreateGPUXRSwapchain(
+ SDL_GPUDevice *device,
+ XrSession session,
+ const XrSwapchainCreateInfo *createinfo,
+ SDL_GPUTextureFormat format,
+ XrSwapchain *swapchain,
+ SDL_GPUTexture ***textures);
+
+/**
+ * Destroys and OpenXR swapchain previously returned by SDL_CreateGPUXRSwapchain.
+ *
+ * \param device a GPU context.
+ * \param swapchain a swapchain previously returned by SDL_CreateGPUXRSwapchain.
+ * \param swapchainImages an array of swapchain images returned by the same call to SDL_CreateGPUXRSwapchain.
+ * \returns the result of the call.
+ *
+ * \sa SDL_CreateGPUDeviceWithProperties
+ * \sa SDL_CreateGPUXRSession
+ * \sa SDL_CreateGPUXRSwapchain
+ */
+extern SDL_DECLSPEC XrResult SDLCALL SDL_DestroyGPUXRSwapchain(SDL_GPUDevice *device, XrSwapchain swapchain, SDL_GPUTexture **swapchainImages);
+
+/**
+ * Dynamically load the OpenXR loader. This can be called at any time.
+ *
+ * SDL keeps a reference count of the OpenXR loader, calling this function multiple
+ * times will increment that count, rather than loading the library multiple times.
+ *
+ * If not called, this will be implicitly called when creating a GPU device with OpenXR.
+ *
+ * This function will use the platform default OpenXR loader name,
+ * unless the `SDL_HINT_OPENXR_LIBRARY` hint is set.
+ *
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \threadsafety This function is not thread safe.
+ *
+ * \sa SDL_HINT_OPENXR_LIBRARY
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_OpenXR_LoadLibrary(void);
+
+/**
+ * Unload the OpenXR loader previously loaded by SDL_OpenXR_LoadLibrary.
+ *
+ * SDL keeps a reference count of the OpenXR loader, calling this function will decrement that count.
+ * Once the reference count reaches zero, the library is unloaded.
+ *
+ * \threadsafety This function is not thread safe.
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_OpenXR_UnloadLibrary(void);
+
+/**
+ * Get the address of the `xrGetInstanceProcAddr` function.
+ *
+ * This should be called after either calling SDL_OpenXR_LoadLibrary() or
+ * creating an OpenXR SDL_GPUDevice.
+ *
+ * The actual type of the returned function pointer is PFN_xrGetInstanceProcAddr,
+ * but that isn't always available. You should include the OpenXR headers before this header,
+ * or cast the return value of this function to the correct type.
+ *
+ * \returns the function pointer for `xrGetInstanceProcAddr` or NULL on
+ * failure; call SDL_GetError() for more information.
+ */
+extern SDL_DECLSPEC PFN_xrGetInstanceProcAddr SDLCALL SDL_OpenXR_GetXrGetInstanceProcAddr(void);
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include <SDL3/SDL_close_code.h>
+
+#endif /* SDL_openxr_h_ */
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 5d4e0717efa3e..3350723aa2eb7 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -488,6 +488,7 @@
#cmakedefine SDL_GPU_D3D12 1
#cmakedefine SDL_GPU_VULKAN 1
#cmakedefine SDL_GPU_METAL 1
+#cmakedefine HAVE_GPU_OPENXR 1
#cmakedefine SDL_GPU_PRIVATE 1
diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h
index ce6f3e20ae4a2..e709ec9ee04e4 100644
--- a/include/build_config/SDL_build_config_android.h
+++ b/include/build_config/SDL_build_config_android.h
@@ -193,6 +193,7 @@
#define SDL_VIDEO_VULKAN 1
#define SDL_VIDEO_RENDER_VULKAN 1
#define SDL_GPU_VULKAN 1
+#define HAVE_GPU_OPENXR 1
#define SDL_VIDEO_RENDER_GPU 1
#endif
diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h
index 07eecb4d3fb6f..eb7ff81312e4e 100644
--- a/include/build_config/SDL_build_config_windows.h
+++ b/include/build_config/SDL_build_config_windows.h
@@ -289,6 +289,7 @@ typedef unsigned int uintptr_t;
#endif
#define SDL_GPU_D3D12 1
#define SDL_GPU_VULKAN 1
+#define HAVE_GPU_OPENXR 1
#define SDL_VIDEO_RENDER_GPU 1
/* Enable system power support */
diff --git a/src/SDL_internal.h b/src/SDL_internal.h
index 3fa50c30f06e1..3e903be1c34f0 100644
--- a/src/SDL_internal.h
+++ b/src/SDL_internal.h
@@ -233,6 +233,7 @@
#undef SDL_GPU_D3D12
#undef SDL_GPU_METAL
#undef SDL_GPU_VULKAN
+#undef HAVE_GPU_OPENXR
#undef SDL_VIDEO_RENDER_GPU
#endif // SDL_GPU_DISABLED
diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c
index 495a8e52c4412..e88b24f19137b 100644
--- a/src/dynapi/SDL_dynapi.c
+++ b/src/dynapi/SDL_dynapi.c
@@ -38,6 +38,7 @@
#endif
#include <SDL3/SDL.h>
+#include <SDL3/SDL_openxr.h>
#define SDL_MAIN_NOIMPL // don't drag in header-only implementation of SDL_main
#include <SDL3/SDL_main.h>
#include "../core/SDL_core_unsupported.h"
@@ -597,6 +598,7 @@ static void SDL_InitDynamicAPI(void)
#else // SDL_DYNAMIC_API
#include <SDL3/SDL.h>
+#include <SDL3/SDL_openxr.h>
Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize);
Sint32 SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 2252d31d13c89..2f2ff3998bbc8 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1272,6 +1272,13 @@ SDL3_0.0.0 {
SDL_LoadSurface;
SDL_SetWindowFillDocument;
SDL_TryLockJoysticks;
+ SDL_CreateGPUXRSession;
+ SDL_GetGPUXRSwapchainFormats;
+ SDL_CreateGPUXRSwapchain;
+ SDL_DestroyGPUXRSwapchain;
+ SDL_OpenXR_LoadLibrary;
+ SDL_OpenXR_UnloadLibrary;
+ SDL_OpenXR_GetXrGetInstanceProcAddr;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index d95d3754e7992..fd7627cf325da 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1298,3 +1298,10 @@
#define SDL_LoadSurface SDL_LoadSurface_REAL
#define SDL_SetWindowFillDocument SDL_SetWindowFillDocument_REAL
#define SDL_TryLockJoysticks SDL_TryLockJoysticks_REAL
+#define SDL_CreateGPUXRSession SDL_CreateGPUXRSession_REAL
+#define SDL_GetGPUXRSwapchainFormats SDL_GetGPUXRSwapchainFormats_REAL
+#define SDL_CreateGPUXRSwapchain SDL_CreateGPUXRSwapchain_REAL
+#define SDL_DestroyGPUXRSwapchain SDL_DestroyGPUXRSwapchain_REAL
+#define SDL_OpenXR_LoadLibrary SDL_OpenXR_LoadLibrary_REAL
+#define SDL_OpenXR_UnloadLibrary SDL_OpenXR_UnloadLibrary_REAL
+#define SDL_OpenXR_GetXrGetInstanceProcAddr SDL_OpenXR_GetXrGetInstanceProcAddr_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index ee60a4227ddb4..bd44244021075 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1306,3 +1306,10 @@ SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadSurface_IO,(SDL_IOStream *a,bool b),(a,b),r
SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadSurface,(const char *a),(a),return)
SDL_DYNAPI_PROC(bool,SDL_SetWindowFillDocument,(SDL_Window *a,bool b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_TryLockJoysticks,(void),(),return)
+SDL_DYNAPI_PROC(XrResult,SDL_CreateGPUXRSession,(SDL_GPUDevice *a,const XrSessionCreateInfo *b,XrSession *c),(a,b,c),return)
+SDL_DYNAPI_PROC(SDL_GPUTextureFormat*,SDL_GetGPUXRSwapchainFormats,(SDL_GPUDevice *a,XrSession b,int *c),(a,b,c),return)
+SDL_DYNAPI_PROC(XrResult,SDL_CreateGPUXRSwapchain,(SDL_GPUDevice *a,XrSession b,const XrSwapchainCreateInfo *c,SDL_GPUTextureFormat d,XrSwapchain *e,SDL_GPUTexture ***f),(a,b,c,d,e,f),return)
+SDL_DYNAPI_PROC(XrResult,SDL_DestroyGPUXRSwapchain,(SDL_GPUDevice *a,XrSwapchain b,SDL_GPUTexture **c),(a,b,c),return)
+SDL_DYNAPI_PROC(bool,SDL_OpenXR_LoadLibrary,(void),(),return)
+SDL_DYNAPI_PROC(void,SDL_OpenXR_UnloadLibrary,(void),(),)
+SDL_DYNAPI_PROC(PFN_xrGetInstanceProcAddr,SDL_OpenXR_GetXrGetInstanceProcAddr,(void),(),return)
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index bec9bf4df5fa1..11b0b1210093b 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -607,6 +607,13 @@ static const SDL_GPUBootstrap * SDL_GPUSelectBackend(SDL_PropertiesID props)
return NULL;
}
+#ifndef HAVE_GPU_OPENXR
+ if (SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_XR_ENABLE_BOOLEAN, false)) {
+ SDL_SetError("OpenXR is not enabled in this build of SDL");
+ return NULL;
+ }
+#endif
+
gpudriver = SDL_GetHint(SDL_HINT_GPU_DRIVER);
if (gpudriver == NULL) {
gpudriver = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, NULL);
@@ -752,6 +759,13 @@ void SDL_DestroyGPUDevice(SDL_GPUDevice *device)
device->DestroyDevice(device);
}
+XrResult SDL_DestroyGPUXRSwapchain(SDL_GPUDevice *device, XrSwapchain swapchain, SDL_GPUTexture **swapchainImages)
+{
+ CHECK_DEVICE_MAGIC(device, XR_ERROR_HANDLE_INVALID);
+
+ return device->DestroyXRSwapchain(device->dri
(Patch may be truncated, please check the link at the top of this post.)