OpenXR Android stuff;

This commit is contained in:
bjorn 2020-08-29 19:45:52 -06:00
parent 5d659d0ac2
commit 28144c8143
7 changed files with 121 additions and 30 deletions

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ bin
deps/VrApi
deps/pico
deps/openxr
deps/OpenXR-Oculus

View File

@ -234,10 +234,19 @@ endif()
# OpenXR
# Currently, to use OpenXR, add the OpenXR SDK to the deps folder:
# git submodule add https://github.com/khronosgroup/openxr-sdk deps/openxr
# On Android, download the Oculus OpenXR loader and place the "OpenXR" folder at "deps/OpenXR-Oculus"
if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENXR)
include_directories(deps/openxr/include)
add_subdirectory(deps/openxr openxr)
set(LOVR_OPENXR openxr_loader)
if(ANDROID)
set(LOVR_OPENXR_OCULUS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps/OpenXR-Oculus" CACHE STRING "The path to the Oculus OpenXR loader")
add_library(openxr_loader SHARED IMPORTED)
include_directories("${LOVR_OPENXR_OCULUS_PATH}/Include")
set_target_properties(openxr_loader PROPERTIES IMPORTED_LOCATION "${LOVR_OPENXR_OCULUS_PATH}/Libs/Android/${ANDROID_ABI}/Release/libopenxr_loader.so")
set(LOVR_OPENXR openxr_loader)
else()
add_subdirectory(deps/openxr openxr)
set(LOVR_OPENXR openxr_loader)
endif()
endif()
# Oculus SDK -- expects Oculus SDK 1.26.0 or later
@ -614,15 +623,24 @@ elseif(ANDROID)
# - Figure out which Java class (Activity) and AndroidManifest.xml to use
# - Oculus uses the regular android os layer, pico implements its own in the headset backend
# - Some of the Pico SDK is in a jar that has to be added to the classpath and dx invocation
# TODO error (probably way earlier) if both USE_VRAPI and USE_PICO aren't defined
if(LOVR_USE_VRAPI)
set(ANDROID_FLAVOR "vrapi")
# TODO error (probably way earlier) if no headset API is defined, since everything will break
if(LOVR_USE_OPENXR)
set(MANIFEST "oculus")
set(ACTIVITY "openxr")
target_sources(lovr PRIVATE src/core/os_android.c)
get_target_property(OPENXR_LIB ${LOVR_OPENXR} IMPORTED_LOCATION)
file(COPY ${OPENXR_LIB} DESTINATION lib/${ANDROID_ABI})
set(ANDROID_CLASSPATH "${ANDROID_JAR}")
elseif(LOVR_USE_VRAPI)
set(MANIFEST "oculus")
set(ACTIVITY "vrapi")
target_sources(lovr PRIVATE src/core/os_android.c)
get_target_property(VRAPI_LIB ${LOVR_VRAPI} IMPORTED_LOCATION)
file(COPY ${VRAPI_LIB} DESTINATION lib/${ANDROID_ABI})
set(ANDROID_CLASSPATH "${ANDROID_JAR}")
elseif(LOVR_USE_PICO)
set(ANDROID_FLAVOR "pico")
set(MANIFEST "pico")
set(ACTIVITY "pico")
get_target_property(PICO_LIB ${LOVR_PICO} IMPORTED_LOCATION)
file(COPY ${PICO_LIB} DESTINATION lib/${ANDROID_ABI})
set(EXTRA_JAR "${LOVR_PICO_PATH}/classes.jar")
@ -633,7 +651,7 @@ elseif(ANDROID)
endif()
endif()
set(ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/AndroidManifest_${ANDROID_FLAVOR}.xml" CACHE STRING "The AndroidManifest.xml file to use")
set(ANDROID_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/AndroidManifest_${MANIFEST}.xml" CACHE STRING "The AndroidManifest.xml file to use")
# Make an apk
add_custom_command(
@ -642,7 +660,7 @@ elseif(ANDROID)
BYPRODUCTS lovr.apk
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy "${ANDROID_MANIFEST}" AndroidManifest.xml
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity_${ANDROID_FLAVOR}.java Activity.java
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Activity_${ACTIVITY}.java Activity.java
COMMAND ${Java_JAVAC_EXECUTABLE} -classpath "${ANDROID_CLASSPATH}" -d . Activity.java
COMMAND ${ANDROID_TOOLS}/dx --dex --output classes.dex ${EXTRA_JAR} org/lovr/app/Activity.class
COMMAND

View File

@ -182,6 +182,7 @@ ifeq ($(PLATFORM),android)
TOOLS = @(ANDROID_SDK)/sdk/build-tools/@(ANDROID_BUILD_TOOLS_VERSION)
ANDROID_JAR = @(ANDROID_SDK)/sdk/platforms/android-@(ANDROID_VERSION)/android.jar
GLUE = @(ANDROID_SDK)/sdk/ndk-bundle/sources/android/native_app_glue
OPENXR_LIB_PATH = $(DEPS)/OpenXR-Oculus/Libs/Android/arm64-v8a/Release
VRAPI_LIB_PATH = $(DEPS)/VrApi/Libs/Android/arm64-v8a/Release
PICO_LIB_PATH = $(DEPS)/pico/jni/arm64-v8a
CFLAGS += --target=aarch64-linux-android@(ANDROID_VERSION)
@ -195,11 +196,13 @@ ifeq ($(PLATFORM),android)
PREFIX = $(LIB)/lib
SUFFIX = .so
CFLAGS_@(GRAPHICS) += -DLOVR_GLES
ACTIVITY_@(OPENXR) = Activity_openxr
ACTIVITY_@(VRAPI) = Activity_vrapi
ACTIVITY_@(PICO) = Activity_pico
ifeq (@(ANDROID_MANIFEST),)
ANDROID_MANIFEST_@(VRAPI) = src/resources/AndroidManifest_vrapi.xml
ANDROID_MANIFEST_@(OPENXR) = src/resources/AndroidManifest_oculus.xml
ANDROID_MANIFEST_@(VRAPI) = src/resources/AndroidManifest_oculus.xml
ANDROID_MANIFEST_@(PICO) = src/resources/AndroidManifest_pico.xml
else
ANDROID_MANIFEST_y = @(ANDROID_MANIFEST)
@ -238,17 +241,20 @@ ifeq ($(PLATFORM),android)
CFLAGS_@(DATA) += -I$(DEPS)/msdfgen
CFLAGS_@(PHYSICS) += -I$(DEPS)/ode/include -I$(BUILD)/ode/include
CFLAGS_@(ENET) += -I$(DEPS)/enet/include
CFLAGS_@(OPENXR) += -I$(DEPS)/OpenXR-Oculus/Include
CFLAGS_@(VRAPI) += -I$(DEPS)/VrApi/Include
LDFLAGS_@(AUDIO) += -L$(BUILD)/$(LIB) -lopenal
LDFLAGS_@(DATA) += -L$(BUILD)/lib_msdfgen -lmsdfgen
LDFLAGS_@(PHYSICS) += -L$(BUILD)/$(LIB) -lode
LDFLAGS_@(ENET) += -L$(BUILD)/enet -lenet
LDFLAGS_@(OPENXR) += -L$(OPENXR_LIB_PATH) -lopenxr_loader
LDFLAGS_@(VRAPI) += -L$(VRAPI_LIB_PATH) -lvrapi
LDFLAGS_@(PICO) += -L$(PICO_LIB_PATH) -lPvr_NativeSDK
LIBS_@(AUDIO) += $(BUILD)/$(LIB)/libopenal.*so*
LIBS_@(PHYSICS) += $(BUILD)/$(LIB)/libode.so
LIBS_@(OPENXR) += $(OPENXR_LIB_PATH)/libopenxr_loader.so
LIBS_@(VRAPI) += $(VRAPI_LIB_PATH)/libvrapi.so
LIBS_@(PICO) += $(PICO_LIB_PATH)/libPvr_NativeSDK.so
endif

2
deps/openxr vendored

@ -1 +1 @@
Subproject commit 69bb1508eae5b3c5be149d3518e772443db6a077
Subproject commit e3a4e41d61544d8e2eba73f00da99b6818ec472b

View File

@ -13,6 +13,7 @@
#include <windows.h>
#elif defined(__ANDROID__)
#define XR_USE_PLATFORM_ANDROID
#include <android_native_app_glue.h>
#include <EGL/egl.h>
#include <jni.h>
#endif
@ -20,12 +21,15 @@
#define XR_USE_GRAPHICS_API_OPENGL
#define GRAPHICS_EXTENSION "XR_KHR_opengl_enable"
#elif defined(LOVR_GLES)
#define XR_USE_GRAPHICS_API_OPENGLES
#define XR_USE_GRAPHICS_API_OPENGL_ES
#define GRAPHICS_EXTENSION "XR_KHR_opengl_es_enable"
#endif
#define XR_NO_PROTOTYPES
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#ifdef __ANDROID__
#include <openxr/openxr_oculus.h>
#endif
#include "resources/openxr_actions.h"
#define XR(f) handleResult(f, __FILE__, __LINE__)
@ -38,9 +42,10 @@
HANDLE lovrPlatformGetWindow(void);
HGLRC lovrPlatformGetContext(void);
#elif defined(__ANDROID__)
EGLDisplay lovrPlatformGetEGLDisplay();
EGLContext lovrPlatformGetEGLContext();
EGLConfig lovrPlatformGetEGLConfig();
struct ANativeActivity* lovrPlatformGetActivity(void);
EGLDisplay lovrPlatformGetEGLDisplay(void);
EGLContext lovrPlatformGetEGLContext(void);
EGLConfig lovrPlatformGetEGLConfig(void);
#endif
#define XR_FOREACH(X)\
@ -146,6 +151,25 @@ static void openxr_destroy();
static bool openxr_init(float offset, uint32_t msaa) {
state.msaa = msaa;
#ifdef __ANDROID__
static PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
XR_LOAD(xrInitializeLoaderKHR);
if (!xrInitializeLoaderKHR) {
return false;
}
ANativeActivity* activity = lovrPlatformGetActivity();
XrLoaderInitInfoAndroidKHR loaderInfo = {
.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
.applicationVM = activity->vm,
.applicationContext = activity->clazz
};
if (XR_FAILED(xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*) &loaderInfo))) {
return false;
}
#endif
{ // Instance
uint32_t extensionCount;
xrEnumerateInstanceExtensionProperties(NULL, 0, &extensionCount, NULL);
@ -157,6 +181,10 @@ static bool openxr_init(float offset, uint32_t msaa) {
const char* enabledExtensionNames[4];
uint32_t enabledExtensionCount = 0;
#ifdef __ANDROID__
enabledExtensionNames[enabledExtensionCount++] = XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME;
#endif
enabledExtensionNames[enabledExtensionCount++] = GRAPHICS_EXTENSION;
if (hasExtension(extensions, extensionCount, XR_EXT_HAND_TRACKING_EXTENSION_NAME)) {
@ -173,6 +201,13 @@ static bool openxr_init(float offset, uint32_t msaa) {
XrInstanceCreateInfo info = {
.type = XR_TYPE_INSTANCE_CREATE_INFO,
#ifdef __ANDROID__
.next = &(XrInstanceCreateInfoAndroidKHR) {
.type = XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR,
.applicationVM = activity->vm,
.applicationActivity = activity->clazz
},
#endif
.applicationInfo.engineName = "LÖVR",
.applicationInfo.engineVersion = (LOVR_VERSION_MAJOR << 24) + (LOVR_VERSION_MINOR << 16) + LOVR_VERSION_PATCH,
.applicationInfo.applicationName = "LÖVR",
@ -272,13 +307,13 @@ static bool openxr_init(float offset, uint32_t msaa) {
{ // Session
#if defined(LOVR_GL)
XrGraphicsRequirementsOpenGLKHR requirements;
XrGraphicsRequirementsOpenGLKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR, NULL };
PFN_xrGetOpenGLGraphicsRequirementsKHR xrGetOpenGLGraphicsRequirementsKHR;
XR_LOAD(xrGetOpenGLGraphicsRequirementsKHR);
XR_INIT(xrGetOpenGLGraphicsRequirementsKHR(state.instance, state.system, &requirements));
// TODO validate OpenGL versions
#elif defined(LOVR_GLES)
XrGraphicsRequirementsOpenGLESKHR requirements;
XrGraphicsRequirementsOpenGLESKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR, NULL };
PFN_xrGetOpenGLESGraphicsRequirementsKHR xrGetOpenGLESGraphicsRequirementsKHR;
XR_LOAD(xrGetOpenGLESGraphicsRequirementsKHR);
XR_INIT(xrGetOpenGLESGraphicsRequirementsKHR(state.instance, state.system, &requirements));
@ -364,28 +399,43 @@ static bool openxr_init(float offset, uint32_t msaa) {
}
{ // Swapchain
#if defined(XR_USE_GRAPHICS_API_OPENGL)
TextureType textureType = TEXTURE_2D;
uint32_t width = state.width * 2;
uint32_t arraySize = 1;
XrSwapchainImageOpenGLKHR images[MAX_IMAGES];
for (uint32_t i = 0; i < MAX_IMAGES; i++) {
images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
images[i].next = NULL;
}
#elif defined(XR_USE_GRAPHICS_API_OPENGL_ES)
TextureType textureType = TEXTURE_ARRAY;
uint32_t width = state.width;
uint32_t arraySize = 2;
XrSwapchainImageOpenGLESKHR images[MAX_IMAGES];
for (uint32_t i = 0; i < MAX_IMAGES; i++) {
images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
images[i].next = NULL;
}
#endif
XrSwapchainCreateInfo info = {
.type = XR_TYPE_SWAPCHAIN_CREATE_INFO,
.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT,
.format = GL_SRGB8_ALPHA8,
.width = state.width * 2,
.width = width,
.height = state.height,
.sampleCount = 1,
.faceCount = 1,
.arraySize = 1,
.arraySize = arraySize,
.mipCount = 1
};
#if defined(XR_USE_GRAPHICS_API_OPENGL)
XrSwapchainImageOpenGLKHR images[MAX_IMAGES];
#elif defined(XR_USE_GRAPHICS_API_OPENGLES)
XrSwapchainImageOpenGLESKHR images[MAX_IMAGES];
#endif
XR_INIT(xrCreateSwapchain(state.session, &info, &state.swapchain));
XR_INIT(xrEnumerateSwapchainImages(state.swapchain, MAX_IMAGES, &state.imageCount, (XrSwapchainImageBaseHeader*) images));
for (uint32_t i = 0; i < state.imageCount; i++) {
state.textures[i] = lovrTextureCreateFromHandle(images[i].image, TEXTURE_2D, 1, state.msaa);
state.textures[i] = lovrTextureCreateFromHandle(images[i].image, textureType, arraySize, state.msaa);
}
// Pre-init composition layer
@ -402,14 +452,20 @@ static bool openxr_init(float offset, uint32_t msaa) {
.subImage = { state.swapchain, { { 0, 0 }, { state.width, state.height } }, 0 }
};
// Copy the left view to the right view and offset for side-by-side submission
// Copy the left view to the right view and offset either the viewport or array index
state.layerViews[1] = state.layerViews[0];
#if defined(XR_USE_GRAPHICS_API_OPENGL)
state.layerViews[1].subImage.imageRect.offset.x += state.width;
#elif defined(XR_USE_GRAPHICS_API_OPENGL_ES)
state.layerViews[1].subImage.imageArrayIndex = 1;
#endif
}
state.clipNear = .1f;
state.clipFar = 100.f;
state.frameState.type = XR_TYPE_FRAME_STATE;
return true;
}
@ -475,7 +531,7 @@ static void getViews(XrView views[2], uint32_t* count) {
.space = state.referenceSpace
};
XrViewState viewState;
XrViewState viewState = { .type = XR_TYPE_VIEW_STATE };
XR(xrLocateViews(state.session, &viewLocateInfo, &viewState, 2 * sizeof(XrView), count, views));
}
@ -725,7 +781,8 @@ static void openxr_renderTo(void (*callback)(void*), void* userdata) {
XrFrameEndInfo endInfo = {
.type = XR_TYPE_FRAME_END_INFO,
.displayTime = state.frameState.predictedDisplayTime,
.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE
.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
.layers = (const XrCompositionLayerBaseHeader*[1]) { (XrCompositionLayerBaseHeader*) &state.layers[0] }
};
XR(xrBeginFrame(state.session, &beginInfo));
@ -769,8 +826,6 @@ static void openxr_renderTo(void (*callback)(void*), void* userdata) {
callback(userdata);
lovrGraphicsSetCamera(NULL, false);
const XrCompositionLayerBaseHeader* layers[1] = { (XrCompositionLayerBaseHeader*) &state.layers[0] };
endInfo.layers = layers;
endInfo.layerCount = 1;
state.layerViews[0].pose = views[0].pose;
state.layerViews[0].fov = views[0].fov;
@ -822,7 +877,7 @@ static void openxr_update(float dt) {
bool wasFocused = state.sessionState == XR_SESSION_STATE_FOCUSED;
bool isFocused = event->state == XR_SESSION_STATE_FOCUSED;
if (wasFocused != isFocused) {
lovrEventPush((Event) { .type = EVENT_FOCUS, .data.boolean = isFocused });
lovrEventPush((Event) { .type = EVENT_FOCUS, .data.boolean.value = isFocused });
}
state.sessionState = event->state;

View File

@ -0,0 +1,10 @@
package org.lovr.app;
import android.app.NativeActivity;
public class Activity extends NativeActivity {
static {
System.loadLibrary("openxr_loader");
System.loadLibrary("lovr");
}
}

View File

@ -12,6 +12,7 @@
<meta-data android:name="com.oculus.vr.focusaware" android:value="true"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="com.oculus.intent.category.VR"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>