Skip to content

Commit 28144c8

Browse files
committed
OpenXR Android stuff;
1 parent 5d659d0 commit 28144c8

File tree

7 files changed

+121
-30
lines changed

7 files changed

+121
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ bin
3636
deps/VrApi
3737
deps/pico
3838
deps/openxr
39+
deps/OpenXR-Oculus

CMakeLists.txt

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,19 @@ endif()
234234
# OpenXR
235235
# Currently, to use OpenXR, add the OpenXR SDK to the deps folder:
236236
# git submodule add https://github.com/khronosgroup/openxr-sdk deps/openxr
237+
# On Android, download the Oculus OpenXR loader and place the "OpenXR" folder at "deps/OpenXR-Oculus"
237238
if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENXR)
238239
include_directories(deps/openxr/include)
239-
add_subdirectory(deps/openxr openxr)
240-
set(LOVR_OPENXR openxr_loader)
240+
if(ANDROID)
241+
set(LOVR_OPENXR_OCULUS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/OpenXR-Oculus" CACHE STRING "The path to the Oculus OpenXR loader")
242+
add_library(openxr_loader SHARED IMPORTED)
243+
include_directories("${LOVR_OPENXR_OCULUS_PATH}/Include")
244+
set_target_properties(openxr_loader PROPERTIES IMPORTED_LOCATION "${LOVR_OPENXR_OCULUS_PATH}/Libs/Android/${ANDROID_ABI}/Release/libopenxr_loader.so")
245+
set(LOVR_OPENXR openxr_loader)
246+
else()
247+
add_subdirectory(deps/openxr openxr)
248+
set(LOVR_OPENXR openxr_loader)
249+
endif()
241250
endif()
242251

243252
# Oculus SDK -- expects Oculus SDK 1.26.0 or later
@@ -614,15 +623,24 @@ elseif(ANDROID)
614623
# - Figure out which Java class (Activity) and AndroidManifest.xml to use
615624
# - Oculus uses the regular android os layer, pico implements its own in the headset backend
616625
# - Some of the Pico SDK is in a jar that has to be added to the classpath and dx invocation
617-
# TODO error (probably way earlier) if both USE_VRAPI and USE_PICO aren't defined
618-
if(LOVR_USE_VRAPI)
619-
set(ANDROID_FLAVOR "vrapi")
626+
# TODO error (probably way earlier) if no headset API is defined, since everything will break
627+
if(LOVR_USE_OPENXR)
628+
set(MANIFEST "oculus")
629+
set(ACTIVITY "openxr")
630+
target_sources(lovr PRIVATE src/core/os_android.c)
631+
get_target_property(OPENXR_LIB ${LOVR_OPENXR} IMPORTED_LOCATION)
632+
file(COPY ${OPENXR_LIB} DESTINATION lib/${ANDROID_ABI})
633+
set(ANDROID_CLASSPATH "${ANDROID_JAR}")
634+
elseif(LOVR_USE_VRAPI)
635+
set(MANIFEST "oculus")
636+
set(ACTIVITY "vrapi")
620637
target_sources(lovr PRIVATE src/core/os_android.c)
621638
get_target_property(VRAPI_LIB ${LOVR_VRAPI} IMPORTED_LOCATION)
622639
file(COPY ${VRAPI_LIB} DESTINATION lib/${ANDROID_ABI})
623640
set(ANDROID_CLASSPATH "${ANDROID_JAR}")
624641
elseif(LOVR_USE_PICO)
625-
set(ANDROID_FLAVOR "pico")
642+
set(MANIFEST "pico")
643+
set(ACTIVITY "pico")
626644
get_target_property(PICO_LIB ${LOVR_PICO} IMPORTED_LOCATION)
627645
file(COPY ${PICO_LIB} DESTINATION lib/${ANDROID_ABI})
628646
set(EXTRA_JAR "${LOVR_PICO_PATH}/classes.jar")
@@ -633,7 +651,7 @@ elseif(ANDROID)
633651
endif()
634652
endif()
635653

636-
set(ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/AndroidManifest_${ANDROID_FLAVOR}.xml" CACHE STRING "The AndroidManifest.xml file to use")
654+
set(ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/AndroidManifest_${MANIFEST}.xml" CACHE STRING "The AndroidManifest.xml file to use")
637655

638656
# Make an apk
639657
add_custom_command(
@@ -642,7 +660,7 @@ elseif(ANDROID)
642660
BYPRODUCTS lovr.apk
643661
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
644662
COMMAND ${CMAKE_COMMAND} -E copy "${ANDROID_MANIFEST}" AndroidManifest.xml
645-
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity_${ANDROID_FLAVOR}.java Activity.java
663+
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity_${ACTIVITY}.java Activity.java
646664
COMMAND ${Java_JAVAC_EXECUTABLE} -classpath "${ANDROID_CLASSPATH}" -d . Activity.java
647665
COMMAND ${ANDROID_TOOLS}/dx --dex --output classes.dex ${EXTRA_JAR} org/lovr/app/Activity.class
648666
COMMAND

Tuprules.tup

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ ifeq ($(PLATFORM),android)
182182
TOOLS = @(ANDROID_SDK)/sdk/build-tools/@(ANDROID_BUILD_TOOLS_VERSION)
183183
ANDROID_JAR = @(ANDROID_SDK)/sdk/platforms/android-@(ANDROID_VERSION)/android.jar
184184
GLUE = @(ANDROID_SDK)/sdk/ndk-bundle/sources/android/native_app_glue
185+
OPENXR_LIB_PATH = $(DEPS)/OpenXR-Oculus/Libs/Android/arm64-v8a/Release
185186
VRAPI_LIB_PATH = $(DEPS)/VrApi/Libs/Android/arm64-v8a/Release
186187
PICO_LIB_PATH = $(DEPS)/pico/jni/arm64-v8a
187188
CFLAGS += --target=aarch64-linux-android@(ANDROID_VERSION)
@@ -195,11 +196,13 @@ ifeq ($(PLATFORM),android)
195196
PREFIX = $(LIB)/lib
196197
SUFFIX = .so
197198
CFLAGS_@(GRAPHICS) += -DLOVR_GLES
199+
ACTIVITY_@(OPENXR) = Activity_openxr
198200
ACTIVITY_@(VRAPI) = Activity_vrapi
199201
ACTIVITY_@(PICO) = Activity_pico
200202

201203
ifeq (@(ANDROID_MANIFEST),)
202-
ANDROID_MANIFEST_@(VRAPI) = src/resources/AndroidManifest_vrapi.xml
204+
ANDROID_MANIFEST_@(OPENXR) = src/resources/AndroidManifest_oculus.xml
205+
ANDROID_MANIFEST_@(VRAPI) = src/resources/AndroidManifest_oculus.xml
203206
ANDROID_MANIFEST_@(PICO) = src/resources/AndroidManifest_pico.xml
204207
else
205208
ANDROID_MANIFEST_y = @(ANDROID_MANIFEST)
@@ -238,17 +241,20 @@ ifeq ($(PLATFORM),android)
238241
CFLAGS_@(DATA) += -I$(DEPS)/msdfgen
239242
CFLAGS_@(PHYSICS) += -I$(DEPS)/ode/include -I$(BUILD)/ode/include
240243
CFLAGS_@(ENET) += -I$(DEPS)/enet/include
244+
CFLAGS_@(OPENXR) += -I$(DEPS)/OpenXR-Oculus/Include
241245
CFLAGS_@(VRAPI) += -I$(DEPS)/VrApi/Include
242246

243247
LDFLAGS_@(AUDIO) += -L$(BUILD)/$(LIB) -lopenal
244248
LDFLAGS_@(DATA) += -L$(BUILD)/lib_msdfgen -lmsdfgen
245249
LDFLAGS_@(PHYSICS) += -L$(BUILD)/$(LIB) -lode
246250
LDFLAGS_@(ENET) += -L$(BUILD)/enet -lenet
251+
LDFLAGS_@(OPENXR) += -L$(OPENXR_LIB_PATH) -lopenxr_loader
247252
LDFLAGS_@(VRAPI) += -L$(VRAPI_LIB_PATH) -lvrapi
248253
LDFLAGS_@(PICO) += -L$(PICO_LIB_PATH) -lPvr_NativeSDK
249254

250255
LIBS_@(AUDIO) += $(BUILD)/$(LIB)/libopenal.*so*
251256
LIBS_@(PHYSICS) += $(BUILD)/$(LIB)/libode.so
257+
LIBS_@(OPENXR) += $(OPENXR_LIB_PATH)/libopenxr_loader.so
252258
LIBS_@(VRAPI) += $(VRAPI_LIB_PATH)/libvrapi.so
253259
LIBS_@(PICO) += $(PICO_LIB_PATH)/libPvr_NativeSDK.so
254260
endif

deps/openxr

Submodule openxr updated 158 files

src/modules/headset/headset_openxr.c

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,23 @@
1313
#include <windows.h>
1414
#elif defined(__ANDROID__)
1515
#define XR_USE_PLATFORM_ANDROID
16+
#include <android_native_app_glue.h>
1617
#include <EGL/egl.h>
1718
#include <jni.h>
1819
#endif
1920
#if defined(LOVR_GL)
2021
#define XR_USE_GRAPHICS_API_OPENGL
2122
#define GRAPHICS_EXTENSION "XR_KHR_opengl_enable"
2223
#elif defined(LOVR_GLES)
23-
#define XR_USE_GRAPHICS_API_OPENGLES
24+
#define XR_USE_GRAPHICS_API_OPENGL_ES
2425
#define GRAPHICS_EXTENSION "XR_KHR_opengl_es_enable"
2526
#endif
2627
#define XR_NO_PROTOTYPES
2728
#include <openxr/openxr.h>
2829
#include <openxr/openxr_platform.h>
30+
#ifdef __ANDROID__
31+
#include <openxr/openxr_oculus.h>
32+
#endif
2933
#include "resources/openxr_actions.h"
3034

3135
#define XR(f) handleResult(f, __FILE__, __LINE__)
@@ -38,9 +42,10 @@
3842
HANDLE lovrPlatformGetWindow(void);
3943
HGLRC lovrPlatformGetContext(void);
4044
#elif defined(__ANDROID__)
41-
EGLDisplay lovrPlatformGetEGLDisplay();
42-
EGLContext lovrPlatformGetEGLContext();
43-
EGLConfig lovrPlatformGetEGLConfig();
45+
struct ANativeActivity* lovrPlatformGetActivity(void);
46+
EGLDisplay lovrPlatformGetEGLDisplay(void);
47+
EGLContext lovrPlatformGetEGLContext(void);
48+
EGLConfig lovrPlatformGetEGLConfig(void);
4449
#endif
4550

4651
#define XR_FOREACH(X)\
@@ -146,6 +151,25 @@ static void openxr_destroy();
146151
static bool openxr_init(float offset, uint32_t msaa) {
147152
state.msaa = msaa;
148153

154+
#ifdef __ANDROID__
155+
static PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
156+
XR_LOAD(xrInitializeLoaderKHR);
157+
if (!xrInitializeLoaderKHR) {
158+
return false;
159+
}
160+
161+
ANativeActivity* activity = lovrPlatformGetActivity();
162+
XrLoaderInitInfoAndroidKHR loaderInfo = {
163+
.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
164+
.applicationVM = activity->vm,
165+
.applicationContext = activity->clazz
166+
};
167+
168+
if (XR_FAILED(xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*) &loaderInfo))) {
169+
return false;
170+
}
171+
#endif
172+
149173
{ // Instance
150174
uint32_t extensionCount;
151175
xrEnumerateInstanceExtensionProperties(NULL, 0, &extensionCount, NULL);
@@ -157,6 +181,10 @@ static bool openxr_init(float offset, uint32_t msaa) {
157181
const char* enabledExtensionNames[4];
158182
uint32_t enabledExtensionCount = 0;
159183

184+
#ifdef __ANDROID__
185+
enabledExtensionNames[enabledExtensionCount++] = XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME;
186+
#endif
187+
160188
enabledExtensionNames[enabledExtensionCount++] = GRAPHICS_EXTENSION;
161189

162190
if (hasExtension(extensions, extensionCount, XR_EXT_HAND_TRACKING_EXTENSION_NAME)) {
@@ -173,6 +201,13 @@ static bool openxr_init(float offset, uint32_t msaa) {
173201

174202
XrInstanceCreateInfo info = {
175203
.type = XR_TYPE_INSTANCE_CREATE_INFO,
204+
#ifdef __ANDROID__
205+
.next = &(XrInstanceCreateInfoAndroidKHR) {
206+
.type = XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR,
207+
.applicationVM = activity->vm,
208+
.applicationActivity = activity->clazz
209+
},
210+
#endif
176211
.applicationInfo.engineName = "LÖVR",
177212
.applicationInfo.engineVersion = (LOVR_VERSION_MAJOR << 24) + (LOVR_VERSION_MINOR << 16) + LOVR_VERSION_PATCH,
178213
.applicationInfo.applicationName = "LÖVR",
@@ -272,13 +307,13 @@ static bool openxr_init(float offset, uint32_t msaa) {
272307

273308
{ // Session
274309
#if defined(LOVR_GL)
275-
XrGraphicsRequirementsOpenGLKHR requirements;
310+
XrGraphicsRequirementsOpenGLKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR, NULL };
276311
PFN_xrGetOpenGLGraphicsRequirementsKHR xrGetOpenGLGraphicsRequirementsKHR;
277312
XR_LOAD(xrGetOpenGLGraphicsRequirementsKHR);
278313
XR_INIT(xrGetOpenGLGraphicsRequirementsKHR(state.instance, state.system, &requirements));
279314
// TODO validate OpenGL versions
280315
#elif defined(LOVR_GLES)
281-
XrGraphicsRequirementsOpenGLESKHR requirements;
316+
XrGraphicsRequirementsOpenGLESKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR, NULL };
282317
PFN_xrGetOpenGLESGraphicsRequirementsKHR xrGetOpenGLESGraphicsRequirementsKHR;
283318
XR_LOAD(xrGetOpenGLESGraphicsRequirementsKHR);
284319
XR_INIT(xrGetOpenGLESGraphicsRequirementsKHR(state.instance, state.system, &requirements));
@@ -364,28 +399,43 @@ static bool openxr_init(float offset, uint32_t msaa) {
364399
}
365400

366401
{ // Swapchain
402+
#if defined(XR_USE_GRAPHICS_API_OPENGL)
403+
TextureType textureType = TEXTURE_2D;
404+
uint32_t width = state.width * 2;
405+
uint32_t arraySize = 1;
406+
XrSwapchainImageOpenGLKHR images[MAX_IMAGES];
407+
for (uint32_t i = 0; i < MAX_IMAGES; i++) {
408+
images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
409+
images[i].next = NULL;
410+
}
411+
#elif defined(XR_USE_GRAPHICS_API_OPENGL_ES)
412+
TextureType textureType = TEXTURE_ARRAY;
413+
uint32_t width = state.width;
414+
uint32_t arraySize = 2;
415+
XrSwapchainImageOpenGLESKHR images[MAX_IMAGES];
416+
for (uint32_t i = 0; i < MAX_IMAGES; i++) {
417+
images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
418+
images[i].next = NULL;
419+
}
420+
#endif
421+
367422
XrSwapchainCreateInfo info = {
368423
.type = XR_TYPE_SWAPCHAIN_CREATE_INFO,
369424
.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT,
370425
.format = GL_SRGB8_ALPHA8,
371-
.width = state.width * 2,
426+
.width = width,
372427
.height = state.height,
373428
.sampleCount = 1,
374429
.faceCount = 1,
375-
.arraySize = 1,
430+
.arraySize = arraySize,
376431
.mipCount = 1
377432
};
378433

379-
#if defined(XR_USE_GRAPHICS_API_OPENGL)
380-
XrSwapchainImageOpenGLKHR images[MAX_IMAGES];
381-
#elif defined(XR_USE_GRAPHICS_API_OPENGLES)
382-
XrSwapchainImageOpenGLESKHR images[MAX_IMAGES];
383-
#endif
384434
XR_INIT(xrCreateSwapchain(state.session, &info, &state.swapchain));
385435
XR_INIT(xrEnumerateSwapchainImages(state.swapchain, MAX_IMAGES, &state.imageCount, (XrSwapchainImageBaseHeader*) images));
386436

387437
for (uint32_t i = 0; i < state.imageCount; i++) {
388-
state.textures[i] = lovrTextureCreateFromHandle(images[i].image, TEXTURE_2D, 1, state.msaa);
438+
state.textures[i] = lovrTextureCreateFromHandle(images[i].image, textureType, arraySize, state.msaa);
389439
}
390440

391441
// Pre-init composition layer
@@ -402,14 +452,20 @@ static bool openxr_init(float offset, uint32_t msaa) {
402452
.subImage = { state.swapchain, { { 0, 0 }, { state.width, state.height } }, 0 }
403453
};
404454

405-
// Copy the left view to the right view and offset for side-by-side submission
455+
// Copy the left view to the right view and offset either the viewport or array index
406456
state.layerViews[1] = state.layerViews[0];
457+
#if defined(XR_USE_GRAPHICS_API_OPENGL)
407458
state.layerViews[1].subImage.imageRect.offset.x += state.width;
459+
#elif defined(XR_USE_GRAPHICS_API_OPENGL_ES)
460+
state.layerViews[1].subImage.imageArrayIndex = 1;
461+
#endif
408462
}
409463

410464
state.clipNear = .1f;
411465
state.clipFar = 100.f;
412466

467+
state.frameState.type = XR_TYPE_FRAME_STATE;
468+
413469
return true;
414470
}
415471

@@ -475,7 +531,7 @@ static void getViews(XrView views[2], uint32_t* count) {
475531
.space = state.referenceSpace
476532
};
477533

478-
XrViewState viewState;
534+
XrViewState viewState = { .type = XR_TYPE_VIEW_STATE };
479535
XR(xrLocateViews(state.session, &viewLocateInfo, &viewState, 2 * sizeof(XrView), count, views));
480536
}
481537

@@ -725,7 +781,8 @@ static void openxr_renderTo(void (*callback)(void*), void* userdata) {
725781
XrFrameEndInfo endInfo = {
726782
.type = XR_TYPE_FRAME_END_INFO,
727783
.displayTime = state.frameState.predictedDisplayTime,
728-
.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE
784+
.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
785+
.layers = (const XrCompositionLayerBaseHeader*[1]) { (XrCompositionLayerBaseHeader*) &state.layers[0] }
729786
};
730787

731788
XR(xrBeginFrame(state.session, &beginInfo));
@@ -769,8 +826,6 @@ static void openxr_renderTo(void (*callback)(void*), void* userdata) {
769826
callback(userdata);
770827
lovrGraphicsSetCamera(NULL, false);
771828

772-
const XrCompositionLayerBaseHeader* layers[1] = { (XrCompositionLayerBaseHeader*) &state.layers[0] };
773-
endInfo.layers = layers;
774829
endInfo.layerCount = 1;
775830
state.layerViews[0].pose = views[0].pose;
776831
state.layerViews[0].fov = views[0].fov;
@@ -822,7 +877,7 @@ static void openxr_update(float dt) {
822877
bool wasFocused = state.sessionState == XR_SESSION_STATE_FOCUSED;
823878
bool isFocused = event->state == XR_SESSION_STATE_FOCUSED;
824879
if (wasFocused != isFocused) {
825-
lovrEventPush((Event) { .type = EVENT_FOCUS, .data.boolean = isFocused });
880+
lovrEventPush((Event) { .type = EVENT_FOCUS, .data.boolean.value = isFocused });
826881
}
827882

828883
state.sessionState = event->state;

src/resources/Activity_openxr.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.lovr.app;
2+
3+
import android.app.NativeActivity;
4+
5+
public class Activity extends NativeActivity {
6+
static {
7+
System.loadLibrary("openxr_loader");
8+
System.loadLibrary("lovr");
9+
}
10+
}

src/resources/AndroidManifest_vrapi.xml renamed to src/resources/AndroidManifest_oculus.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<meta-data android:name="com.oculus.vr.focusaware" android:value="true"/>
1313
<intent-filter>
1414
<action android:name="android.intent.action.MAIN"/>
15+
<category android:name="com.oculus.intent.category.VR"/>
1516
<category android:name="android.intent.category.LAUNCHER"/>
1617
</intent-filter>
1718
</activity>

0 commit comments

Comments
 (0)