mirror of https://github.com/bjornbytes/lovr.git
OpenXR Android stuff;
This commit is contained in:
parent
5d659d0ac2
commit
28144c8143
|
@ -36,3 +36,4 @@ bin
|
|||
deps/VrApi
|
||||
deps/pico
|
||||
deps/openxr
|
||||
deps/OpenXR-Oculus
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 69bb1508eae5b3c5be149d3518e772443db6a077
|
||||
Subproject commit e3a4e41d61544d8e2eba73f00da99b6818ec472b
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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>
|
Loading…
Reference in New Issue