diff --git a/CMakeLists.txt b/CMakeLists.txt index 648efe47f2..58676f8484 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,8 +118,9 @@ add_definitions(-DASSETS_DIR="${CMAKE_INSTALL_FULL_DATADIR}/ppsspp/assets/") if(OPENXR) add_definitions(-DOPENXR) - include_directories(ext/openxr vr) - link_directories(quest/libs/arm64-v8a) + add_library(openxr SHARED IMPORTED) + include_directories(ext/openxr) + set_property(TARGET openxr PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ext/openxr/libs/arm64-v8a/libopenxr_loader.so") message("OpenXR enabled") endif() @@ -1066,9 +1067,12 @@ if(ANDROID) if (OPENXR) set(nativeExtra ${nativeExtra} - VR/VRFramebuffer.c + VR/VRBase.cpp + VR/VRBase.h + VR/VRFramebuffer.cpp VR/VRFramebuffer.h ) + set(nativeExtraLibs ${nativeExtraLibs} openxr) endif() # No target elseif(IOS) diff --git a/Core/Config.cpp b/Core/Config.cpp index 0baa43eb3c..c3124c695d 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -843,7 +843,11 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("CardboardXShift", &g_Config.iCardboardXShift, 0, true, true), ConfigSetting("CardboardYShift", &g_Config.iCardboardYShift, 0, true, true), ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true), +#ifdef OPENXR + g_Config.iGPUBackend = (int)GPUBackend::OPENGL, +#else ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, true, false), +#endif ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, ""), ConfigSetting("DisabledGraphicsBackends", &g_Config.sDisabledGPUBackends, ""), ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false), diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index aa08576467..6e31b6d999 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -96,6 +96,10 @@ using namespace std::placeholders; static AVIDump avi; #endif +#ifdef OPENXR +#include "VR/VRBase.h" +#endif + // TODO: Ugly! static bool frameStep_; static int lastNumFlips; @@ -189,6 +193,10 @@ EmuScreen::EmuScreen(const Path &filename) // Usually, we don't want focus movement enabled on this screen, so disable on start. // Only if you open chat or dev tools do we want it to start working. UI::EnableFocusMovement(false); + +#ifdef OPENXR + VR_EnterVR(VR_GetEngine()); +#endif } bool EmuScreen::bootAllowStorage(const Path &filename) { diff --git a/VR/VRBase.cpp b/VR/VRBase.cpp new file mode 100644 index 0000000000..bea29e4b13 --- /dev/null +++ b/VR/VRBase.cpp @@ -0,0 +1,176 @@ +#include "VRBase.h" + +#include +#include +#include +#include + +static engine_t vr_engine; +int vr_initialized = 0; + +const char* const requiredExtensionNames[] = { + XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME, + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME}; +const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + +void VR_Init( ovrJava java ) +{ + if (vr_initialized) + return; + + ovrApp_Clear(&vr_engine.appState); + + // Create the EGL Context + ovrEgl_CreateContext(&vr_engine.appState.Egl, NULL); + + XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR}; + instanceCreateInfoAndroid.applicationVM = java.Vm; + instanceCreateInfoAndroid.applicationActivity = java.ActivityObject; + + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid; + memset(&loaderInitializeInfoAndroid, 0, sizeof(loaderInitializeInfoAndroid)); + loaderInitializeInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR; + loaderInitializeInfoAndroid.next = NULL; + loaderInitializeInfoAndroid.applicationVM = java.Vm; + loaderInitializeInfoAndroid.applicationContext = java.ActivityObject; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo; + memset(&appInfo, 0, sizeof(appInfo)); + strcpy(appInfo.applicationName, "PPSSPP"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "PPSSPP"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_CURRENT_API_VERSION; + + XrInstanceCreateInfo instanceCreateInfo; + memset(&instanceCreateInfo, 0, sizeof(instanceCreateInfo)); + instanceCreateInfo.type = XR_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.next = (XrBaseInStructure*)&instanceCreateInfoAndroid; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &vr_engine.appState.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo; + instanceInfo.type = XR_TYPE_INSTANCE_PROPERTIES; + instanceInfo.next = NULL; + OXR(xrGetInstanceProperties(vr_engine.appState.Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo; + memset(&systemGetInfo, 0, sizeof(systemGetInfo)); + systemGetInfo.type = XR_TYPE_SYSTEM_GET_INFO; + systemGetInfo.next = NULL; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(vr_engine.appState.Instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to get system."); + exit(1); + } + + // Get the graphics requirements. + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + vr_engine.appState.Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = {}; + graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(vr_engine.appState.Instance, systemId, &graphicsRequirements)); + + vr_engine.appState.MainThreadTid = gettid(); + vr_engine.appState.SystemId = systemId; + + vr_engine.java = java; + vr_initialized = 1; +} + +void VR_Destroy( engine_t* engine ) +{ + if (engine == &vr_engine) { + xrDestroyInstance(engine->appState.Instance); + ovrEgl_DestroyContext(&engine->appState.Egl); + ovrApp_Destroy(&engine->appState); + } +} + +void VR_EnterVR( engine_t* engine ) { + + if (engine->appState.Session) { + ALOGE("VR_EnterVR called with existing session"); + return; + } + + // Create the OpenXR Session. + XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = {}; + graphicsBindingAndroidGLES.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR; + graphicsBindingAndroidGLES.next = NULL; + graphicsBindingAndroidGLES.display = engine->appState.Egl.Display; + graphicsBindingAndroidGLES.config = engine->appState.Egl.Config; + graphicsBindingAndroidGLES.context = engine->appState.Egl.Context; + + XrSessionCreateInfo sessionCreateInfo = {}; + memset(&sessionCreateInfo, 0, sizeof(sessionCreateInfo)); + sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO; + sessionCreateInfo.next = &graphicsBindingAndroidGLES; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = engine->appState.SystemId; + + XrResult initResult; + OXR(initResult = xrCreateSession(engine->appState.Instance, &sessionCreateInfo, &engine->appState.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {}; + spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.HeadSpace)); +} + +void VR_LeaveVR( engine_t* engine ) { + if (engine->appState.Session) { + OXR(xrDestroySpace(engine->appState.HeadSpace)); + // StageSpace is optional. + if (engine->appState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(engine->appState.StageSpace)); + } + OXR(xrDestroySpace(engine->appState.FakeStageSpace)); + engine->appState.CurrentSpace = XR_NULL_HANDLE; + OXR(xrDestroySession(engine->appState.Session)); + engine->appState.Session = NULL; + } +} + +engine_t* VR_GetEngine( void ) { + return &vr_engine; +} diff --git a/VR/VRBase.h b/VR/VRBase.h new file mode 100644 index 0000000000..5b66d24488 --- /dev/null +++ b/VR/VRBase.h @@ -0,0 +1,13 @@ +#ifndef __VR_BASE +#define __VR_BASE + +#include "VRFramebuffer.h" + +void VR_Init( ovrJava java ); +void VR_Destroy( engine_t* engine ); +void VR_EnterVR( engine_t* engine ); +void VR_LeaveVR( engine_t* engine ); + +engine_t* VR_GetEngine( void ); + +#endif diff --git a/VR/VRFramebuffer.c b/VR/VRFramebuffer.cpp similarity index 99% rename from VR/VRFramebuffer.c rename to VR/VRFramebuffer.cpp index 8298020eba..4276863355 100644 --- a/VR/VRFramebuffer.c +++ b/VR/VRFramebuffer.cpp @@ -17,6 +17,8 @@ Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rig #include #include #include +#include +#include /* ================================================================================ @@ -420,9 +422,9 @@ void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { } } -GLboolean ovrApp_HandleXrEvents(ovrApp* app) { +int ovrApp_HandleXrEvents(ovrApp* app) { XrEventDataBuffer eventDataBuffer = {}; - GLboolean recenter = GL_FALSE; + int recenter = 0; // Poll for events for (;;) { @@ -467,7 +469,7 @@ GLboolean ovrApp_HandleXrEvents(ovrApp* app) { ref_space_change_event->referenceSpaceType, (void*)ref_space_change_event->session, FromXrTime(ref_space_change_event->changeTime)); - recenter = GL_TRUE; + recenter = 1; } break; case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { const XrEventDataSessionStateChanged* session_state_changed_event = diff --git a/VR/VRFramebuffer.h b/VR/VRFramebuffer.h index 1efd558df7..780425e26e 100644 --- a/VR/VRFramebuffer.h +++ b/VR/VRFramebuffer.h @@ -6,8 +6,6 @@ #define XR_USE_PLATFORM_ANDROID 1 #include #include -#include -#include #include #include #include @@ -58,8 +56,8 @@ typedef struct { uint32_t TextureSwapChainIndex; ovrSwapChain ColorSwapChain; XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; - GLuint* DepthBuffers; - GLuint* FrameBuffers; + unsigned int* DepthBuffers; + unsigned int* FrameBuffers; } ovrFramebuffer; typedef struct { @@ -67,13 +65,13 @@ typedef struct { } ovrRenderer; typedef struct { - GLboolean Active; + int Active; XrPosef Pose; } ovrTrackedController; typedef struct { ovrEgl Egl; - GLboolean Focused; + int Focused; XrInstance Instance; XrSession Session; @@ -84,7 +82,7 @@ typedef struct { XrSpace StageSpace; XrSpace FakeStageSpace; XrSpace CurrentSpace; - GLboolean SessionActive; + int SessionActive; int SwapInterval; // These threads will be marked as performance threads. @@ -93,7 +91,7 @@ typedef struct { ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; int LayerCount; - GLboolean TouchPadDownLastFrame; + int TouchPadDownLastFrame; ovrRenderer Renderer; ovrTrackedController TrackedController[2]; } ovrApp; @@ -138,7 +136,7 @@ typedef struct { void ovrApp_Clear(ovrApp* app); void ovrApp_Destroy(ovrApp* app); -GLboolean ovrApp_HandleXrEvents(ovrApp* app); +int ovrApp_HandleXrEvents(ovrApp* app); void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl); void ovrEgl_DestroyContext(ovrEgl* egl); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index b707653ddf..059195c871 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -93,6 +93,10 @@ struct JNIEnv {}; #include "Common/Log.h" #include "UI/GameInfoCache.h" +#ifdef OPENXR +#include "VR/VRBase.h" +#endif + #include "app-android.h" bool useCPUThread = true; @@ -753,6 +757,13 @@ retry: INFO_LOG(SYSTEM, "NativeApp.init() - launching emu thread"); EmuThreadStart(); } + +#ifdef OPENXR + ovrJava java; + java.Vm = gJvm; + java.ActivityObject = nativeActivity; + VR_Init(java); +#endif } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_audioInit(JNIEnv *, jclass) { diff --git a/quest/libs/arm64-v8a/libopenxr_loader.so b/ext/openxr/libs/arm64-v8a/libopenxr_loader.so similarity index 100% rename from quest/libs/arm64-v8a/libopenxr_loader.so rename to ext/openxr/libs/arm64-v8a/libopenxr_loader.so diff --git a/quest/AndroidManifest.xml b/quest/AndroidManifest.xml index 2ae4d17046..eb2d6cb824 100644 --- a/quest/AndroidManifest.xml +++ b/quest/AndroidManifest.xml @@ -10,6 +10,7 @@ + @@ -50,7 +51,10 @@ android:banner="@drawable/tv_banner" android:requestLegacyExternalStorage="true" android:preserveLegacyExternalStorage="true"> - + + + +