Skip to content

Commit

Permalink
Add WinlatorXR by lvonasek (https://github.com/lvonasek)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunodev85 committed Mar 28, 2024
1 parent 8f79ebc commit 3af161c
Show file tree
Hide file tree
Showing 21 changed files with 2,584 additions and 8 deletions.
3 changes: 3 additions & 0 deletions app/.gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/main/cpp/OpenXR-SDK"]
path = src/main/cpp/OpenXR-SDK
url = https://github.com/KhronosGroup/OpenXR-SDK.git
24 changes: 24 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-feature
android:name="android.hardware.vr.headtracking"
android:required="false" />
<uses-feature
android:name="com.oculus.feature.PASSTHROUGH"
android:required="false" />
<uses-feature
android:name="oculus.software.overlay_keyboard"
android:required="false" />

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Expand Down Expand Up @@ -37,6 +46,21 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|density|navigation"
android:screenOrientation="sensorLandscape" />

<activity
android:name="com.winlator.XrActivity"
android:configChanges="density|orientation|screenSize|keyboard|keyboardHidden|uiMode"
android:exported="true"
android:launchMode="singleTask"
android:resizeableActivity="false"
android:screenOrientation="landscape"
android:process=":vr_process"
android:theme="@style/AppThemeFullscreen">
<intent-filter>
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name="com.winlator.ControlsEditorActivity"
android:exported="false"
android:theme="@style/AppThemeFullscreen"
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
cmake_minimum_required(VERSION 3.22.1)

Project(Winlator)

set(CMAKE_C_FLAGS "-O2 -Wno-unused-function -Wimplicit-function-declaration")

add_subdirectory(proot)
add_subdirectory(virglrenderer)
add_subdirectory(OpenXR-SDK)

add_library(winlator SHARED
xr/engine.c
xr/framebuffer.c
xr/input.c
xr/main.c
xr/math.c
xr/renderer.c
drawable.c
gpu_image.c
sysvshared_memory.c
Expand All @@ -15,6 +24,7 @@ target_link_libraries(winlator
log
android
jnigraphics
openxr_loader
EGL
GLESv2
GLESv3)
1 change: 1 addition & 0 deletions app/src/main/cpp/OpenXR-SDK
Submodule OpenXR-SDK added at 288d3a
207 changes: 207 additions & 0 deletions app/src/main/cpp/xr/engine.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#include "engine.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void XrEngineInit(struct XrEngine* engine, void* system, const char* name, int version)
{
if (engine->Initialized)
return;
memset(engine, 0, sizeof(engine));

#ifdef ANDROID
PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR",
(PFN_xrVoidFunction*)&xrInitializeLoaderKHR);
if (xrInitializeLoaderKHR != NULL)
{
xrJava* java = (xrJava*)system;
XrLoaderInitInfoAndroidKHR loader_info;
memset(&loader_info, 0, sizeof(loader_info));
loader_info.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
loader_info.next = NULL;
loader_info.applicationVM = java->vm;
loader_info.applicationContext = java->activity;
xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loader_info);
}
#endif

int count = 0;
const char* extensions[32];
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
extensions[count++] = XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME;
#endif
#ifdef ANDROID
if (engine->PlatformFlag[PLATFORM_EXTENSION_INSTANCE])
{
extensions[count++] = XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME;
}
if (engine->PlatformFlag[PLATFORM_EXTENSION_PASSTHROUGH])
{
extensions[count++] = XR_FB_PASSTHROUGH_EXTENSION_NAME;
}
if (engine->PlatformFlag[PLATFORM_EXTENSION_PERFORMANCE])
{
extensions[count++] = XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME;
extensions[count++] = XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME;
}
#endif

// Create the OpenXR instance.
XrApplicationInfo app_info;
memset(&app_info, 0, sizeof(app_info));
strcpy(app_info.applicationName, name);
strcpy(app_info.engineName, name);
app_info.applicationVersion = version;
app_info.engineVersion = version;
app_info.apiVersion = XR_CURRENT_API_VERSION;

XrInstanceCreateInfo instance_info;
memset(&instance_info, 0, sizeof(instance_info));
instance_info.type = XR_TYPE_INSTANCE_CREATE_INFO;
instance_info.next = NULL;
instance_info.createFlags = 0;
instance_info.applicationInfo = app_info;
instance_info.enabledApiLayerCount = 0;
instance_info.enabledApiLayerNames = NULL;
instance_info.enabledExtensionCount = count;
instance_info.enabledExtensionNames = extensions;

#ifdef ANDROID
XrInstanceCreateInfoAndroidKHR instance_info_android = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR};
if (engine->PlatformFlag[PLATFORM_EXTENSION_INSTANCE])
{
xrJava* java = (xrJava*)system;
instance_info_android.applicationVM = java->vm;
instance_info_android.applicationActivity = java->activity;
instance_info.next = (XrBaseInStructure*)&instance_info_android;
}
#endif

XrResult result;
OXR(result = xrCreateInstance(&instance_info, &engine->Instance));
if (result != XR_SUCCESS)
{
ALOGE("Failed to create XR instance: %d", (int)result);
exit(1);
}

XrInstanceProperties instance_properties;
instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES;
instance_properties.next = NULL;
OXR(xrGetInstanceProperties(engine->Instance, &instance_properties));
ALOGV("Runtime %s: Version : %d.%d.%d", instance_properties.runtimeName,
XR_VERSION_MAJOR(instance_properties.runtimeVersion),
XR_VERSION_MINOR(instance_properties.runtimeVersion),
XR_VERSION_PATCH(instance_properties.runtimeVersion));

XrSystemGetInfo system_info;
memset(&system_info, 0, sizeof(system_info));
system_info.type = XR_TYPE_SYSTEM_GET_INFO;
system_info.next = NULL;
system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;

OXR(result = xrGetSystem(engine->Instance, &system_info, &engine->SystemId));
if (result != XR_SUCCESS)
{
ALOGE("Failed to get system");
exit(1);
}

// Get the graphics requirements.
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL;
OXR(xrGetInstanceProcAddr(engine->Instance, "xrGetOpenGLESGraphicsRequirementsKHR",
(PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR)));

XrGraphicsRequirementsOpenGLESKHR graphics_requirements = {};
graphics_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR;
OXR(pfnGetOpenGLESGraphicsRequirementsKHR(engine->Instance, engine->SystemId, &graphics_requirements));
#endif

#ifdef ANDROID
engine->MainThreadId = gettid();
#endif
engine->Initialized = true;
}

void XrEngineDestroy(struct XrEngine* engine)
{
if (engine->Initialized)
{
xrDestroyInstance(engine->Instance);
engine->Initialized = false;
}
}

void XrEngineEnter(struct XrEngine* engine)
{
if (engine->Session)
{
ALOGE("EnterXR called with existing session");
return;
}

// Create the OpenXR Session.
XrSessionCreateInfo session_info;
memset(&session_info, 0, sizeof(session_info));
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
XrGraphicsBindingOpenGLESAndroidKHR graphics_binding_gl = {};
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
graphics_binding_gl.next = NULL;
graphics_binding_gl.display = eglGetCurrentDisplay();
graphics_binding_gl.config = NULL;
graphics_binding_gl.context = eglGetCurrentContext();
session_info.next = &graphics_binding_gl;
#endif
session_info.type = XR_TYPE_SESSION_CREATE_INFO;
session_info.createFlags = 0;
session_info.systemId = engine->SystemId;

XrResult result;
OXR(result = xrCreateSession(engine->Instance, &session_info, &engine->Session));
if (result != XR_SUCCESS)
{
ALOGE("Failed to create XR session: %d", (int)result);
exit(1);
}

// Create a space to the first path
XrReferenceSpaceCreateInfo space_info = {};
space_info.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO;
space_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
space_info.poseInReferenceSpace.orientation.w = 1.0f;
OXR(xrCreateReferenceSpace(engine->Session, &space_info, &engine->HeadSpace));
}

void XrEngineLeave(struct XrEngine* engine)
{
if (engine->Session)
{
OXR(xrDestroySpace(engine->HeadSpace));
// StageSpace is optional.
if (engine->StageSpace != XR_NULL_HANDLE)
{
OXR(xrDestroySpace(engine->StageSpace));
}
OXR(xrDestroySpace(engine->FakeSpace));
engine->CurrentSpace = XR_NULL_HANDLE;
OXR(xrDestroySession(engine->Session));
engine->Session = XR_NULL_HANDLE;
}
}

void XrEngineWaitForFrame(struct XrEngine* engine)
{
XrFrameWaitInfo wait_frame_info = {};
wait_frame_info.type = XR_TYPE_FRAME_WAIT_INFO;
wait_frame_info.next = NULL;

XrFrameState frame_state = {};
frame_state.type = XR_TYPE_FRAME_STATE;
frame_state.next = NULL;

OXR(xrWaitFrame(engine->Session, &wait_frame_info, &frame_state));
engine->PredictedDisplayTime = frame_state.predictedDisplayTime;
}
99 changes: 99 additions & 0 deletions app/src/main/cpp/xr/engine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#pragma once

#include <stdbool.h>

//#define _DEBUG

#ifndef ANDROID
#define ANDROID 1
#endif

#if defined(_DEBUG)
void GLCheckErrors(const char* file, int line);
void OXRCheckErrors(XrResult result, const char* file, int line);

#define GL(func) func; GLCheckErrors(__FILE__ , __LINE__);
#define OXR(func) OXRCheckErrors(func, __FILE__ , __LINE__);
#else
#define GL(func) func;
#define OXR(func) func;
#endif

#ifdef ANDROID
#include <android/log.h>
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "OpenXR", __VA_ARGS__);
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "OpenXR", __VA_ARGS__);

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <jni.h>
#define XR_USE_PLATFORM_ANDROID 1
#define XR_USE_GRAPHICS_API_OPENGL_ES 1
#else
#include <cstdio>
#define ALOGE(...) printf(__VA_ARGS__)
#define ALOGV(...) printf(__VA_ARGS__)
#endif

#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>

enum
{
XrMaxLayerCount = 2
};
enum
{
XrMaxNumEyes = 2
};

enum XrPlatformFlag
{
PLATFORM_CONTROLLER_PICO,
PLATFORM_CONTROLLER_QUEST,
PLATFORM_EXTENSION_INSTANCE,
PLATFORM_EXTENSION_PASSTHROUGH,
PLATFORM_EXTENSION_PERFORMANCE,
PLATFORM_TRACKING_FLOOR,
PLATFORM_MAX
};

typedef union
{
XrCompositionLayerProjection projection;
XrCompositionLayerQuad quad;
} XrCompositorLayer;

#ifdef ANDROID
typedef struct
{
jobject activity;
JNIEnv* env;
JavaVM* vm;
} xrJava;
#endif

struct XrEngine {
XrInstance Instance;
XrSession Session;
XrSystemId SystemId;

XrSpace CurrentSpace;
XrSpace FakeSpace;
XrSpace HeadSpace;
XrSpace StageSpace;

XrTime PredictedDisplayTime;

int MainThreadId;
int RenderThreadId;

bool PlatformFlag[PLATFORM_MAX];
bool Initialized;
};

void XrEngineInit(struct XrEngine* engine, void* system, const char* name, int version);
void XrEngineDestroy(struct XrEngine* engine);
void XrEngineEnter(struct XrEngine* engine);
void XrEngineLeave(struct XrEngine* engine);
void XrEngineWaitForFrame(struct XrEngine* engine);
Loading

3 comments on commit 3af161c

@iamnotkurtcobain
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your hard work!

Love testing Winlator.

Xinput is still kinda wonky and not 100% reliable.

@iamnotkurtcobain
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Xinput stops completely working after playing a game for a while.

You have to restart the game.

Maybe you can find the cause of this.

@IAMLXM0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey I'm trying to run this on my S24 (Exynos) phone and it crashes right after I run the container (not getting into windows mode). Any ideas or is it something that should be developed later? (I've tried VirGL, doesn't help)

Please sign in to comment.