From dc977a2eed89733444a32adf21e1a442ccf95e4f Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 7 Jul 2022 19:30:06 +0200 Subject: [PATCH] OpenXR - VR framebuffer class added --- CMakeLists.txt | 9 +- VR/VRFramebuffer.c | 692 +++++++++++++++++++++++++++++++++++++++++++++ VR/VRFramebuffer.h | 290 +++++++++++++++++++ 3 files changed, 990 insertions(+), 1 deletion(-) create mode 100644 VR/VRFramebuffer.c create mode 100644 VR/VRFramebuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c9e47538c5..648efe47f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,7 @@ add_definitions(-DASSETS_DIR="${CMAKE_INSTALL_FULL_DATADIR}/ppsspp/assets/") if(OPENXR) add_definitions(-DOPENXR) - include_directories(ext/openxr) + include_directories(ext/openxr vr) link_directories(quest/libs/arm64-v8a) message("OpenXR enabled") endif() @@ -1063,6 +1063,13 @@ if(ANDROID) android/jni/OpenSLContext.cpp android/jni/OpenSLContext.h ) + + if (OPENXR) + set(nativeExtra ${nativeExtra} + VR/VRFramebuffer.c + VR/VRFramebuffer.h + ) + endif() # No target elseif(IOS) set(nativeExtra ${nativeExtra} diff --git a/VR/VRFramebuffer.c b/VR/VRFramebuffer.c new file mode 100644 index 0000000000..8298020eba --- /dev/null +++ b/VR/VRFramebuffer.c @@ -0,0 +1,692 @@ +#include "VRFramebuffer.h" + +/************************************************************************************ + +Original file name : XrCompositor_NativeActivity.c + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +================================================================================ + +ovrEgl + +================================================================================ +*/ + +void ovrEgl_Clear(ovrEgl* egl) { + egl->MajorVersion = 0; + egl->MinorVersion = 0; + egl->Display = 0; + egl->Config = 0; + egl->TinySurface = EGL_NO_SURFACE; + egl->MainSurface = EGL_NO_SURFACE; + egl->Context = EGL_NO_CONTEXT; +} + +void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl) { + if (egl->Display != 0) { + return; + } + + egl->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(egl->Display, &egl->MajorVersion, &egl->MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + const int MAX_CONFIGS = 1024; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(egl->Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %d", eglGetError()); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + egl->Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(egl->Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(egl->Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + egl->Config = configs[i]; + break; + } + } + if (egl->Config == 0) { + ALOGE(" eglChooseConfig() failed: %d", eglGetError()); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + egl->Context = eglCreateContext( + egl->Display, + egl->Config, + (shareEgl != NULL) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (egl->Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %d", eglGetError()); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + egl->TinySurface = eglCreatePbufferSurface(egl->Display, egl->Config, surfaceAttribs); + if (egl->TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %d", eglGetError()); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(egl->Display, egl->TinySurface, egl->TinySurface, egl->Context) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %d", eglGetError()); + eglDestroySurface(egl->Display, egl->TinySurface); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } +} + +void ovrEgl_DestroyContext(ovrEgl* egl) { + if (egl->Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %d", eglGetError()); + } + } + if (egl->Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(egl->Display, egl->Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %d", eglGetError()); + } + egl->Context = EGL_NO_CONTEXT; + } + if (egl->TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(egl->Display, egl->TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %d", eglGetError()); + } + egl->TinySurface = EGL_NO_SURFACE; + } + if (egl->Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(egl->Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %d", eglGetError()); + } + egl->Display = 0; + } +} + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + + +void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { + frameBuffer->Width = 0; + frameBuffer->Height = 0; + frameBuffer->TextureSwapChainLength = 0; + frameBuffer->TextureSwapChainIndex = 0; + frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->ColorSwapChain.Width = 0; + frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthBuffers = NULL; + frameBuffer->FrameBuffers = NULL; +} + +bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const int width, + const int height) { + + frameBuffer->Width = width; + frameBuffer->Height = height; + + XrSwapchainCreateInfo swapChainCreateInfo; + memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo)); + swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = GL_RGBA8; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + // Allocate the swapchain images array. + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->ColorSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + + frameBuffer->DepthBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + frameBuffer->FrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Create the color buffer texture. + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + GLenum colorTextureTarget = GL_TEXTURE_2D; + GL(glBindTexture(colorTextureTarget, colorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + // Create depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferRenderbuffer( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %d", renderFramebufferStatus); + return false; + } + } + + return true; +} + +void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { + GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); + GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); + OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + free(frameBuffer->ColorSwapChainImage); + + free(frameBuffer->DepthBuffers); + free(frameBuffer->FrameBuffers); + + ovrFramebuffer_Clear(frameBuffer); +} + +void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) { + GL(glBindFramebuffer( + GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); +} + +void ovrFramebuffer_SetNone() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); +} + +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { + // Acquire the swapchain image + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage( + frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + + XrSwapchainImageWaitInfo waitInfo; + waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO; + waitInfo.next = NULL; + waitInfo.timeout = 1000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + int i = 0; + while (res != XR_SUCCESS) { + res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f micro seconds)", + i, + waitInfo.timeout * (1E-9)); + } +} + +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); +} + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + +void ovrRenderer_Clear(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]); + } +} + +void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight) { + // Create the frame buffers. + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Create( + session, + &renderer->FrameBuffer[eye], + suggestedEyeTextureWidth, + suggestedEyeTextureHeight); + } +} + +void ovrRenderer_Destroy(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); + } +} + +/* +================================================================================ + +ovrApp + +================================================================================ +*/ + +void ovrApp_Clear(ovrApp* app) { + app->Focused = false; + app->Instance = XR_NULL_HANDLE; + app->Session = XR_NULL_HANDLE; + memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties)); + memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView)); + app->SystemId = XR_NULL_SYSTEM_ID; + app->HeadSpace = XR_NULL_HANDLE; + app->StageSpace = XR_NULL_HANDLE; + app->FakeStageSpace = XR_NULL_HANDLE; + app->CurrentSpace = XR_NULL_HANDLE; + app->SessionActive = false; + app->SwapInterval = 1; + memset(app->Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + app->LayerCount = 0; + app->MainThreadTid = 0; + app->RenderThreadTid = 0; + app->TouchPadDownLastFrame = false; + + ovrEgl_Clear(&app->Egl); + ovrRenderer_Clear(&app->Renderer); +} + +void ovrApp_Destroy(ovrApp* app) { + ovrApp_Clear(app); +} + +void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { + assert(app->SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo; + memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo)); + sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO; + sessionBeginInfo.next = NULL; + sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); + + app->SessionActive = (result == XR_SUCCESS); + } else if (state == XR_SESSION_STATE_STOPPING) { + assert(app->SessionActive); + + OXR(xrEndSession(app->Session)); + app->SessionActive = false; + } +} + +GLboolean ovrApp_HandleXrEvents(ovrApp* app) { + XrEventDataBuffer eventDataBuffer = {}; + GLboolean recenter = GL_FALSE; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(app->Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { + const XrEventDataInstanceLossPending* instance_loss_pending_event = + (XrEventDataInstanceLossPending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f", + FromXrTime(instance_loss_pending_event->lossTime)); + } break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { + XrEventDataReferenceSpaceChangePending* ref_space_change_event = + (XrEventDataReferenceSpaceChangePending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f", + ref_space_change_event->referenceSpaceType, + (void*)ref_space_change_event->session, + FromXrTime(ref_space_change_event->changeTime)); + recenter = GL_TRUE; + } break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + app->Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + app->Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state); + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } + return recenter; +} + +/* +================================================================================ + +ovrMatrix4f + +================================================================================ +*/ + +ovrMatrix4f ovrMatrix4f_CreateProjectionFov( + const float angleLeft, + const float angleRight, + const float angleUp, + const float angleDown, + const float nearZ, + const float farZ) { + + const float tanAngleLeft = tanf(angleLeft); + const float tanAngleRight = tanf(angleRight); + + const float tanAngleDown = tanf(angleDown); + const float tanAngleUp = tanf(angleUp); + + const float tanAngleWidth = tanAngleRight - tanAngleLeft; + + // Set to tanAngleDown - tanAngleUp for a clip space with positive Y + // down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with + // positive Y up (OpenGL / D3D / Metal). + const float tanAngleHeight = tanAngleUp - tanAngleDown; + + // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). + // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). + const float offsetZ = nearZ; + + ovrMatrix4f result; + if (farZ <= nearZ) { + // place the far plane at infinity + result.M[0][0] = 2 / tanAngleWidth; + result.M[0][1] = 0; + result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result.M[0][3] = 0; + + result.M[1][0] = 0; + result.M[1][1] = 2 / tanAngleHeight; + result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result.M[1][3] = 0; + + result.M[2][0] = 0; + result.M[2][1] = 0; + result.M[2][2] = -1; + result.M[2][3] = -(nearZ + offsetZ); + + result.M[3][0] = 0; + result.M[3][1] = 0; + result.M[3][2] = -1; + result.M[3][3] = 0; + } else { + // normal projection + result.M[0][0] = 2 / tanAngleWidth; + result.M[0][1] = 0; + result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result.M[0][3] = 0; + + result.M[1][0] = 0; + result.M[1][1] = 2 / tanAngleHeight; + result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result.M[1][3] = 0; + + result.M[2][0] = 0; + result.M[2][1] = 0; + result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ); + result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ); + + result.M[3][0] = 0; + result.M[3][1] = 0; + result.M[3][2] = -1; + result.M[3][3] = 0; + } + return result; +} + +ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) { + const float ww = q->w * q->w; + const float xx = q->x * q->x; + const float yy = q->y * q->y; + const float zz = q->z * q->z; + + ovrMatrix4f out; + out.M[0][0] = ww + xx - yy - zz; + out.M[0][1] = 2 * (q->x * q->y - q->w * q->z); + out.M[0][2] = 2 * (q->x * q->z + q->w * q->y); + out.M[0][3] = 0; + + out.M[1][0] = 2 * (q->x * q->y + q->w * q->z); + out.M[1][1] = ww - xx + yy - zz; + out.M[1][2] = 2 * (q->y * q->z - q->w * q->x); + out.M[1][3] = 0; + + out.M[2][0] = 2 * (q->x * q->z - q->w * q->y); + out.M[2][1] = 2 * (q->y * q->z + q->w * q->x); + out.M[2][2] = ww - xx - yy + zz; + out.M[2][3] = 0; + + out.M[3][0] = 0; + out.M[3][1] = 0; + out.M[3][2] = 0; + out.M[3][3] = 1; + return out; +} + + +/// Use left-multiplication to accumulate transformations. +ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) { + ovrMatrix4f out; + out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] + + a->M[0][3] * b->M[3][0]; + out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] + + a->M[1][3] * b->M[3][0]; + out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] + + a->M[2][3] * b->M[3][0]; + out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] + + a->M[3][3] * b->M[3][0]; + + out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] + + a->M[0][3] * b->M[3][1]; + out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] + + a->M[1][3] * b->M[3][1]; + out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] + + a->M[2][3] * b->M[3][1]; + out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] + + a->M[3][3] * b->M[3][1]; + + out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] + + a->M[0][3] * b->M[3][2]; + out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] + + a->M[1][3] * b->M[3][2]; + out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] + + a->M[2][3] * b->M[3][2]; + out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] + + a->M[3][3] * b->M[3][2]; + + out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] + + a->M[0][3] * b->M[3][3]; + out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] + + a->M[1][3] * b->M[3][3]; + out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] + + a->M[2][3] * b->M[3][3]; + out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] + + a->M[3][3] * b->M[3][3]; + return out; +} + +ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) { + const float sinX = sinf(radiansX); + const float cosX = cosf(radiansX); + const ovrMatrix4f rotationX = { + {{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}}; + const float sinY = sinf(radiansY); + const float cosY = cosf(radiansY); + const ovrMatrix4f rotationY = { + {{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}}; + const float sinZ = sinf(radiansZ); + const float cosZ = cosf(radiansZ); + const ovrMatrix4f rotationZ = { + {{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}}; + const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX); + return ovrMatrix4f_Multiply(&rotationZ, &rotationXY); +} + +XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) { + XrVector4f out; + out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w; + out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w; + out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w; + out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w; + return out; +} + +/* +================================================================================ + +ovrTrackedController + +================================================================================ +*/ + +void ovrTrackedController_Clear(ovrTrackedController* controller) { + controller->Active = false; + controller->Pose = XrPosef_Identity(); +} diff --git a/VR/VRFramebuffer.h b/VR/VRFramebuffer.h new file mode 100644 index 0000000000..1efd558df7 --- /dev/null +++ b/VR/VRFramebuffer.h @@ -0,0 +1,290 @@ +#ifndef __VR_FRAMEBUFFER +#define __VR_FRAMEBUFFER + +//OpenXR +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#include +#include +#include +#include +#include +#include +#include +#include + +#define MATH_PI 3.14159265358979323846f + +#define ALOGE(...) printf(__VA_ARGS__) +#define ALOGV(...) printf(__VA_ARGS__) + +typedef union { + XrCompositionLayerProjection Projection; + XrCompositionLayerCylinderKHR Cylinder; +} ovrCompositorLayer_Union; + +enum { ovrMaxLayerCount = 1 }; +enum { ovrMaxNumEyes = 2 }; + +#define GL(func) func; +#define OXR(func) func; + +typedef struct { + JavaVM* Vm; + jobject ActivityObject; + JNIEnv* Env; +} ovrJava; + +typedef struct { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +} ovrSwapChain; + +typedef struct { + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +} ovrEgl; + +typedef struct { + int Width; + int Height; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + ovrSwapChain ColorSwapChain; + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; + GLuint* DepthBuffers; + GLuint* FrameBuffers; +} ovrFramebuffer; + +typedef struct { + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; +} ovrRenderer; + +typedef struct { + GLboolean Active; + XrPosef Pose; +} ovrTrackedController; + +typedef struct { + ovrEgl Egl; + GLboolean Focused; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace StageSpace; + XrSpace FakeStageSpace; + XrSpace CurrentSpace; + GLboolean SessionActive; + + int SwapInterval; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + + GLboolean TouchPadDownLastFrame; + ovrRenderer Renderer; + ovrTrackedController TrackedController[2]; +} ovrApp; + + +typedef struct { + float M[4][4]; +} ovrMatrix4f; + +typedef enum ovrButton_ { + ovrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers + ovrButton_B = 0x00000002, + ovrButton_RThumb = 0x00000004, + ovrButton_RShoulder = 0x00000008, + + ovrButton_X = 0x00000100, + ovrButton_Y = 0x00000200, + ovrButton_LThumb = 0x00000400, + ovrButton_LShoulder = 0x00000800, + + ovrButton_Up = 0x00010000, + ovrButton_Down = 0x00020000, + ovrButton_Left = 0x00040000, + ovrButton_Right = 0x00080000, + ovrButton_Enter = 0x00100000, //< Set for touchpad click on the Go Controller, menu + // button on Left Quest Controller + ovrButton_Back = 0x00200000, //< Back button on the Go Controller (only set when + // a short press comes up) + ovrButton_GripTrigger = 0x04000000, //< grip trigger engaged + ovrButton_Trigger = 0x20000000, //< Index Trigger engaged + ovrButton_Joystick = 0x80000000, //< Click of the Joystick + + ovrButton_EnumSize = 0x7fffffff +} ovrButton; + +typedef struct { + uint64_t frameIndex; + ovrApp appState; + ovrJava java; + float predictedDisplayTime; +} engine_t; + +void ovrApp_Clear(ovrApp* app); +void ovrApp_Destroy(ovrApp* app); +GLboolean ovrApp_HandleXrEvents(ovrApp* app); + +void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl); +void ovrEgl_DestroyContext(ovrEgl* egl); + +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_SetNone(); + +void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight); +void ovrRenderer_Destroy(ovrRenderer* renderer); + +void ovrTrackedController_Clear(ovrTrackedController* controller); + +ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b); +ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ); +ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q); +ovrMatrix4f ovrMatrix4f_CreateProjectionFov( + const float fovDegreesX, + const float fovDegreesY, + const float offsetX, + const float offsetY, + const float nearZ, + const float farZ); + +XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v); + + +/// THESE METHODS HAVE ORIGIN IN openxr_oculus_helpers.h + +static inline double FromXrTime(const XrTime time) { + return (time * 1e-9); +} + +static inline XrTime ToXrTime(const double timeInSeconds) { + return (timeInSeconds * 1e9); +} + +static inline XrPosef XrPosef_Identity() { + XrPosef r; + r.orientation.x = 0; + r.orientation.y = 0; + r.orientation.z = 0; + r.orientation.w = 1; + r.position.x = 0; + r.position.y = 0; + r.position.z = 0; + return r; +} + +static inline float XrVector3f_LengthSquared(const XrVector3f v) { + return v.x * v.x + v.y * v.y + v.z * v.z;; +} + +static inline float XrVector3f_Length(const XrVector3f v) { + return sqrtf(XrVector3f_LengthSquared(v)); +} + +static inline XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale) { + XrVector3f u; + u.x = v.x * scale; + u.y = v.y * scale; + u.z = v.z * scale; + return u; +} + +static inline XrVector3f XrVector3f_Normalized(const XrVector3f v) { + float rcpLen = 1.0f / XrVector3f_Length(v); + return XrVector3f_ScalarMultiply(v, rcpLen); +} + +static inline XrQuaternionf XrQuaternionf_CreateFromVectorAngle( + const XrVector3f axis, + const float angle) { + XrQuaternionf r; + if (XrVector3f_LengthSquared(axis) == 0.0f) { + r.x = 0; + r.y = 0; + r.z = 0; + r.w = 1; + return r; + } + + XrVector3f unitAxis = XrVector3f_Normalized(axis); + float sinHalfAngle = sinf(angle * 0.5f); + + r.w = cosf(angle * 0.5f); + r.x = unitAxis.x * sinHalfAngle; + r.y = unitAxis.y * sinHalfAngle; + r.z = unitAxis.z * sinHalfAngle; + return r; +} + +static inline XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b) { + XrQuaternionf c; + c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; + c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; + c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; + c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + return c; +} + +static inline XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q) { + XrQuaternionf r; + r.x = -q.x; + r.y = -q.y; + r.z = -q.z; + r.w = q.w; + return r; +} + +static inline XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v) { + XrVector3f r; + XrQuaternionf q = {v.x, v.y, v.z, 0.0f}; + XrQuaternionf aq = XrQuaternionf_Multiply(a, q); + XrQuaternionf aInv = XrQuaternionf_Inverse(a); + XrQuaternionf aqaInv = XrQuaternionf_Multiply(aq, aInv); + r.x = aqaInv.x; + r.y = aqaInv.y; + r.z = aqaInv.z; + return r; +} + +static inline XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v) { + XrVector3f w; + w.x = u.x + v.x; + w.y = u.y + v.y; + w.z = u.z + v.z; + return w; +} + +static inline XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v) { + XrVector3f r0 = XrQuaternionf_Rotate(a.orientation, v); + return XrVector3f_Add(r0, a.position); +} + +static inline XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b) { + XrPosef c; + c.orientation = XrQuaternionf_Multiply(a.orientation, b.orientation); + c.position = XrPosef_Transform(a, b.position); + return c; +} + +#endif