diff --git a/android/app/src/main/cpp/code/vr/vr_base.c b/android/app/src/main/cpp/code/vr/vr_base.c index 35c04543..89906a85 100644 --- a/android/app/src/main/cpp/code/vr/vr_base.c +++ b/android/app/src/main/cpp/code/vr/vr_base.c @@ -6,24 +6,19 @@ //#if __ANDROID__ #include +#include static engine_t vr_engine; const char* const requiredExtensionNames[] = { - XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, - XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, - XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, - XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME, - XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME, - XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME, - XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME, - XR_FB_COLOR_SPACE_EXTENSION_NAME, - XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME, - XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME, - XR_FB_FOVEATION_EXTENSION_NAME, - XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME}; + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, + XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME, + XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME, + XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME}; const uint32_t numRequiredExtensions = - sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); cvar_t *vr_worldscale = NULL; cvar_t *vr_hudDepth = NULL; @@ -50,7 +45,7 @@ cvar_t *vr_goreLevel = NULL; engine_t* VR_Init( ovrJava java ) { - memset(&vr_engine, 0, sizeof(vr_engine)); + ovrApp_Clear(&vr_engine.appState); PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; xrGetInstanceProcAddr( @@ -84,21 +79,52 @@ engine_t* VR_Init( ovrJava java ) instanceCreateInfo.enabledApiLayerNames = NULL; instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; - if (xrCreateInstance(&instanceCreateInfo, &vr_engine.instance) != XR_SUCCESS) { - Com_Printf("xrCreateInstance failed"); + + 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; - if (xrGetSystem(vr_engine.instance, &systemGetInfo, &vr_engine.systemId) != XR_SUCCESS) { - Com_Printf("xrGetSystem failed"); + + 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; return &vr_engine; } @@ -224,27 +250,18 @@ void VR_InitCvars( void ) void VR_Destroy( engine_t* engine ) { if (engine == &vr_engine) { - xrDestroyInstance(engine->instance); + xrDestroyInstance(engine->appState.Instance); + ovrApp_Destroy(&engine->appState); } } void VR_EnterVR( engine_t* engine, ovrJava java ) { - if (engine->session) { + if (engine->appState.Session) { Com_Printf("VR_EnterVR called with existing session"); return; } - // Get the graphics requirements. - PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; - xrGetInstanceProcAddr( - engine->instance, - "xrGetOpenGLESGraphicsRequirementsKHR", - (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR)); - XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = {}; - graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR; - pfnGetOpenGLESGraphicsRequirementsKHR(engine->instance, engine->systemId, &graphicsRequirements); - // Create the OpenXR Session. XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = {}; graphicsBindingAndroidGLES.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR; @@ -258,17 +275,29 @@ void VR_EnterVR( engine_t* engine, ovrJava java ) { sessionCreateInfo.type = XR_TYPE_SESSION_CREATE_INFO; sessionCreateInfo.next = &graphicsBindingAndroidGLES; sessionCreateInfo.createFlags = 0; - sessionCreateInfo.systemId = engine->systemId; - if (xrCreateSession(engine->instance, &sessionCreateInfo, &engine->session) != XR_SUCCESS) { - Com_Printf("xrCreateSession failed"); + 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); } } void VR_LeaveVR( engine_t* engine ) { - if (engine->session) { - xrDestroySession(engine->session); - engine->session = NULL; + if (engine->appState.Session) { + OXR(xrDestroySpace(engine->appState.HeadSpace)); + OXR(xrDestroySpace(engine->appState.LocalSpace)); + // 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)); + OXR(xrDestroyInstance(engine->appState.Instance)); + engine->appState.Session = NULL; } } diff --git a/android/app/src/main/cpp/code/vr/vr_renderer.c b/android/app/src/main/cpp/code/vr/vr_renderer.c index fd93d3c0..3bf4e967 100644 --- a/android/app/src/main/cpp/code/vr/vr_renderer.c +++ b/android/app/src/main/cpp/code/vr/vr_renderer.c @@ -18,10 +18,28 @@ #include #endif -#define SUPER_SAMPLE 1.15f - extern vr_clientinfo_t vr; +XrView* projections; +GLboolean stageSupported = GL_FALSE; + +void VR_UpdateStageBounds(ovrApp* pappState) { + XrExtent2Df stageBounds = {}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGV("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + + pappState->CurrentSpace = pappState->FakeStageSpace; + } + + ALOGV("Stage bounds: width = %f, depth %f", stageBounds.width, stageBounds.height); +} + void APIENTRY VR_GLDebugLog(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) @@ -60,14 +78,84 @@ void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight) if (engine) { - //TODO: - /* - *pWidth = width = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH) * SUPER_SAMPLE; - *pHeight = height = vrapi_GetSystemPropertyInt(&engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT) * SUPER_SAMPLE; + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + engine->appState.Instance, engine->appState.SystemId, 0, &viewportConfigTypeCount, NULL)); - vr.fov_x = vrapi_GetSystemPropertyInt( &engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X); - vr.fov_y = vrapi_GetSystemPropertyInt( &engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y); - */ + XrViewConfigurationType* viewportConfigurationTypes = + (XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType)); + + OXR(xrEnumerateViewConfigurations( + engine->appState.Instance, + engine->appState.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig; + viewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + OXR(xrGetViewConfigurationProperties( + engine->appState.Instance, engine->appState.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + engine->appState.Instance, engine->appState.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + XrViewConfigurationView* elements = + (XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView)); + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + engine->appState.Instance, + engine->appState.SystemId, + viewportConfigType, + viewCount, + &viewCount, + elements)); + + // Cache the view config properties for the selected config type. + if (viewportConfigType == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) { + assert(viewCount == ovrMaxNumEyes); + for (uint32_t e = 0; e < viewCount; e++) { + engine->appState.ViewConfigurationView[e] = elements[e]; + } + } + + free(elements); + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + free(viewportConfigurationTypes); + + *pWidth = width = engine->appState.ViewConfigurationView[0].recommendedImageRectWidth; + *pHeight = height = engine->appState.ViewConfigurationView[0].recommendedImageRectHeight; + //TODO: + /* + vr.fov_x = vrapi_GetSystemPropertyInt( &engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X); + vr.fov_y = vrapi_GetSystemPropertyInt( &engine->java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y); + */ } else { @@ -75,10 +163,6 @@ void VR_GetResolution(engine_t* engine, int *pWidth, int *pHeight) *pWidth = width; *pHeight = height; } - - //TODO:remove hardcoded values - *pWidth = 3664 / 2; - *pHeight = 1920; } void VR_InitRenderer( engine_t* engine ) { @@ -89,33 +173,113 @@ void VR_InitRenderer( engine_t* engine ) { int eyeW, eyeH; VR_GetResolution(engine, &eyeW, &eyeH); - ovrRenderer_Create(engine->session, &engine->renderer, eyeW, eyeH); - ovrRenderer_SetFoveation( - &engine->instance, - &engine->session, - &engine->renderer, - XR_FOVEATION_LEVEL_HIGH_FB, - 0, - XR_FOVEATION_DYNAMIC_DISABLED_FB); - XrReferenceSpaceCreateInfo spaceCreateInfo = {}; - spaceCreateInfo.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO; - spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; - spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; - spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; - xrCreateReferenceSpace(engine->session, &spaceCreateInfo, &engine->stageSpace); + // Get the viewport configuration info for the chosen viewport configuration type. + engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + engine->appState.Instance, engine->appState.SystemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, &engine->appState.ViewportConfig)); + + // Get the supported display refresh rates for the system. + { + PFN_xrEnumerateDisplayRefreshRatesFB pfnxrEnumerateDisplayRefreshRatesFB = NULL; + OXR(xrGetInstanceProcAddr( + engine->appState.Instance, + "xrEnumerateDisplayRefreshRatesFB", + (PFN_xrVoidFunction*)(&pfnxrEnumerateDisplayRefreshRatesFB))); + + OXR(pfnxrEnumerateDisplayRefreshRatesFB( + engine->appState.Session, 0, &engine->appState.NumSupportedDisplayRefreshRates, NULL)); + + engine->appState.SupportedDisplayRefreshRates = + (float*)malloc(engine->appState.NumSupportedDisplayRefreshRates * sizeof(float)); + OXR(pfnxrEnumerateDisplayRefreshRatesFB( + engine->appState.Session, + engine->appState.NumSupportedDisplayRefreshRates, + &engine->appState.NumSupportedDisplayRefreshRates, + engine->appState.SupportedDisplayRefreshRates)); + ALOGV("Supported Refresh Rates:"); + for (uint32_t i = 0; i < engine->appState.NumSupportedDisplayRefreshRates; i++) { + ALOGV("%d:%f", i, engine->appState.SupportedDisplayRefreshRates[i]); + } + + OXR(xrGetInstanceProcAddr( + engine->appState.Instance, + "xrGetDisplayRefreshRateFB", + (PFN_xrVoidFunction*)(&engine->appState.pfnGetDisplayRefreshRate))); + + float currentDisplayRefreshRate = 0.0f; + OXR(engine->appState.pfnGetDisplayRefreshRate(engine->appState.Session, ¤tDisplayRefreshRate)); + ALOGV("Current System Display Refresh Rate: %f", currentDisplayRefreshRate); + + OXR(xrGetInstanceProcAddr( + engine->appState.Instance, + "xrRequestDisplayRefreshRateFB", + (PFN_xrVoidFunction*)(&engine->appState.pfnRequestDisplayRefreshRate))); + + // Test requesting the system default. + OXR(engine->appState.pfnRequestDisplayRefreshRate(engine->appState.Session, 0.0f)); + ALOGV("Requesting system default display refresh rate"); + } + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(engine->appState.Session, 0, &numOutputSpaces, NULL)); + + XrReferenceSpaceType* referenceSpaces = + (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); + + OXR(xrEnumerateReferenceSpaces( + engine->appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = GL_TRUE; + break; + } + } + + free(referenceSpaces); + + // 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)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.LocalSpace)); + + // Create a default stage space to use if SPACE_TYPE_STAGE is not + // supported, or calls to xrGetReferenceSpaceBoundsRect fail. + { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f; + OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.FakeStageSpace)); + ALOGV("Created fake stage space from local space with offset"); + engine->appState.CurrentSpace = engine->appState.FakeStageSpace; + } + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(engine->appState.Session, &spaceCreateInfo, &engine->appState.StageSpace)); + ALOGV("Created stage space"); + engine->appState.CurrentSpace = engine->appState.StageSpace; + } + + projections = (XrView*)(malloc(ovrMaxNumEyes * sizeof(XrView))); + + ovrRenderer_Create( + engine->appState.Session, + &engine->appState.Renderer, + engine->appState.ViewConfigurationView[0].recommendedImageRectWidth, + engine->appState.ViewConfigurationView[0].recommendedImageRectHeight); } void VR_DestroyRenderer( engine_t* engine ) { - xrDestroySpace(engine->stageSpace); - ovrRenderer_Destroy(&engine->renderer); -} - - -void VR_ReInitRenderer() -{ - VR_DestroyRenderer( VR_GetEngine() ); - VR_InitRenderer( VR_GetEngine() ); + ovrRenderer_Destroy(&engine->appState.Renderer); + free(projections); } @@ -202,88 +366,7 @@ ovrLayerCylinder2 BuildCylinderLayer(engine_t* engine, const int textureWidth, c } */ -void VR_ClearFrameBuffer( int width, int height ) -{ - - glEnable( GL_SCISSOR_TEST ); - glViewport( 0, 0, width, height ); - - if (Cvar_VariableIntegerValue("vr_thirdPersonSpectator")) - { - //Blood red.. ish - glClearColor( 0.12f, 0.0f, 0.05f, 1.0f ); - } - else - { - //Black - glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); - } - - glScissor( 0, 0, width, height ); - glClear( GL_COLOR_BUFFER_BIT ); - - glScissor( 0, 0, 0, 0 ); - glDisable( GL_SCISSOR_TEST ); -} - void VR_DrawFrame( engine_t* engine ) { - XrEventDataBuffer eventDataBuffer = {}; - - // Poll for events - for (;;) { - XrEventDataBaseHeader *baseEventHeader = (XrEventDataBaseHeader * )(&eventDataBuffer); - baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; - baseEventHeader->next = NULL; - if (xrPollEvent(engine->instance, &eventDataBuffer) != XR_SUCCESS) { - break; - } - if (baseEventHeader->type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) { - const XrEventDataSessionStateChanged* session_state_changed_event = - (XrEventDataSessionStateChanged*)(baseEventHeader); - switch (session_state_changed_event->state) { - case XR_SESSION_STATE_READY: - if (!engine->sessionActive) { - XrSessionBeginInfo sessionBeginInfo; - memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo)); - sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO; - sessionBeginInfo.next = NULL; - sessionBeginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - if (xrBeginSession(engine->session, &sessionBeginInfo) != XR_SUCCESS) { - Com_Printf("xrBeginSession failed"); - exit(1); - } - engine->sessionActive = GL_TRUE; - } - break; - case XR_SESSION_STATE_STOPPING: - if (engine->sessionActive) { - xrEndSession(engine->session); - engine->sessionActive = GL_FALSE; - } - break; - } - } - } - - if (!engine->sessionActive) { - return; - } - - // NOTE: OpenXR does not use the concept of frame indices. Instead, - // XrWaitFrame returns the predicted display time. - XrFrameWaitInfo waitFrameInfo = {}; - waitFrameInfo.type = XR_TYPE_FRAME_WAIT_INFO; - waitFrameInfo.next = NULL; - XrFrameState frameState = {}; - frameState.type = XR_TYPE_FRAME_STATE; - frameState.next = NULL; - xrWaitFrame(engine->session, &waitFrameInfo, &frameState); - - XrFrameBeginInfo beginFrameDesc = {}; - beginFrameDesc.type = XR_TYPE_FRAME_BEGIN_INFO; - beginFrameDesc.next = NULL; - xrBeginFrame(engine->session, &beginFrameDesc); - float fov_y = 90; //TODO: float fov_x = 90; //TODO: @@ -302,87 +385,180 @@ void VR_DrawFrame( engine_t* engine ) { const ovrMatrix4f projectionMatrix = ovrMatrix4f_CreateProjectionFov( fov_x / vr.weapon_zoomLevel, fov_y / vr.weapon_zoomLevel, 0.0f, 0.0f, 1.0f, 0.0f ); re.SetVRHeadsetParms(projectionMatrix.M, - engine->renderer.FrameBuffer[0].FrameBuffers[engine->renderer.FrameBuffer[0].TextureSwapChainIndex], - engine->renderer.FrameBuffer[1].FrameBuffers[engine->renderer.FrameBuffer[1].TextureSwapChainIndex]); + engine->appState.Renderer.FrameBuffer[0].FrameBuffers[engine->appState.Renderer.FrameBuffer[0].TextureSwapChainIndex], + engine->appState.Renderer.FrameBuffer[1].FrameBuffers[engine->appState.Renderer.FrameBuffer[1].TextureSwapChainIndex]); - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { - ovrFramebuffer* frameBuffer = &engine->renderer.FrameBuffer[eye]; - ovrFramebuffer_Acquire(frameBuffer); - ovrFramebuffer_SetCurrent(frameBuffer); + GLboolean stageBoundsDirty = GL_TRUE; + ovrApp_HandleXrEvents(&engine->appState); + if (engine->appState.SessionActive == GL_FALSE) { + return; + } - VR_ClearFrameBuffer(frameBuffer->Width, frameBuffer->Height); - Com_Frame(); + if (stageBoundsDirty) { + VR_UpdateStageBounds(&engine->appState); + stageBoundsDirty = GL_FALSE; + } - ovrFramebuffer_Resolve(frameBuffer); - ovrFramebuffer_Release(frameBuffer); - } - ovrFramebuffer_SetNone(); + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {}; + waitFrameInfo.type = XR_TYPE_FRAME_WAIT_INFO; + waitFrameInfo.next = NULL; - // Compose the layers for this frame. - XrCompositionLayerProjectionView projection_layer_elements[XR_EYES_COUNT] = {}; - XrCompositionLayerProjection projection_layer = {}; - projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; - projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; - projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; - projection_layer.space = engine->stageSpace; - projection_layer.viewCount = XR_EYES_COUNT; - projection_layer.views = projection_layer_elements; + XrFrameState frameState = {}; + frameState.type = XR_TYPE_FRAME_STATE; + frameState.next = NULL; - XrPosef viewTransform[2]; - ovrSceneMatrices sceneMatrices; - XrView* projections = (XrView*)(malloc(XR_EYES_COUNT * sizeof(XrView))); - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { - XrPosef xfHeadFromEye = projections[eye].pose; - //XrPosef xfStageFromEye = XrPosef_Multiply(xfStageFromHead, xfHeadFromEye); - viewTransform[eye] = XrPosef_Inverse(xfHeadFromEye); //TODO:there should be xfStageFromEye as parameter + OXR(xrWaitFrame(engine->appState.Session, &waitFrameInfo, &frameState)); - sceneMatrices.ViewMatrix[eye] = - XrMatrix4x4f_CreateFromRigidTransform(&viewTransform[eye]); - const XrFovf fov = projections[eye].fov; - XrMatrix4x4f_CreateProjectionFov( - &sceneMatrices.ProjectionMatrix[eye], - fov.angleLeft, - fov.angleRight, - fov.angleUp, - fov.angleDown, - 0.1f, - 0.0f); - } + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {}; + beginFrameDesc.type = XR_TYPE_FRAME_BEGIN_INFO; + beginFrameDesc.next = NULL; + OXR(xrBeginFrame(engine->appState.Session, &beginFrameDesc)); - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { - ovrFramebuffer* frameBuffer = &engine->renderer.FrameBuffer[eye]; + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + OXR(xrLocateSpace( + engine->appState.HeadSpace, engine->appState.CurrentSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfStageFromHead = loc.pose; + OXR(xrLocateSpace( + engine->appState.HeadSpace, engine->appState.LocalSpace, frameState.predictedDisplayTime, &loc)); - memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); - projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + XrViewLocateInfo projectionInfo = {}; + projectionInfo.type = XR_TYPE_VIEW_LOCATE_INFO; + projectionInfo.viewConfigurationType = engine->appState.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = engine->appState.HeadSpace; - projection_layer_elements[eye].pose = XrPosef_Inverse(viewTransform[eye]); - projection_layer_elements[eye].fov = projections[eye].fov; + XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL}; - memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); - projection_layer_elements[eye].subImage.swapchain = frameBuffer->ColorSwapChain.Handle; - projection_layer_elements[eye].subImage.imageRect.offset.x = 0; - projection_layer_elements[eye].subImage.imageRect.offset.y = 0; - projection_layer_elements[eye].subImage.imageRect.extent.width = - frameBuffer->ColorSwapChain.Width; - projection_layer_elements[eye].subImage.imageRect.extent.height = - frameBuffer->ColorSwapChain.Height; - projection_layer_elements[eye].subImage.imageArrayIndex = 0; - } + uint32_t projectionCapacityInput = ovrMaxNumEyes; + uint32_t projectionCountOutput = projectionCapacityInput; + OXR(xrLocateViews( + engine->appState.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + // - // Compose the layers for this frame. - const XrCompositionLayerBaseHeader* layers[1] = {}; - layers[0] = (const XrCompositionLayerBaseHeader*)&projection_layer; + ovrSceneMatrices sceneMatrices; + XrPosef viewTransform[2]; - XrFrameEndInfo endFrameInfo = {}; - endFrameInfo.type = XR_TYPE_FRAME_END_INFO; - endFrameInfo.displayTime = frameState.predictedDisplayTime; - endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; - endFrameInfo.layerCount = 1; - endFrameInfo.layers = layers; + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef xfStageFromEye = XrPosef_Multiply(xfStageFromHead, xfHeadFromEye); + viewTransform[eye] = XrPosef_Inverse(xfStageFromEye); - xrEndFrame(engine->session, &endFrameInfo); - free(projections); + sceneMatrices.ViewMatrix[eye] = + XrMatrix4x4f_CreateFromRigidTransform(&viewTransform[eye]); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f_CreateProjectionFov( + &sceneMatrices.ProjectionMatrix[eye], + fov.angleLeft, + fov.angleRight, + fov.angleUp, + fov.angleDown, + 0.1f, + 0.0f); + } + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView projection_layer_elements[2] = {}; + + engine->appState.LayerCount = 0; + memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + GLboolean shouldRenderWorldLayer = GL_TRUE; + + // Render the world-view layer + if (shouldRenderWorldLayer) { + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; + + ovrFramebuffer_Acquire(frameBuffer); + + // Set the current framebuffer. + ovrFramebuffer_SetCurrent(frameBuffer); + + if (Cvar_VariableIntegerValue("vr_thirdPersonSpectator")) + { + //Blood red.. ish + glClearColor( 0.12f, 0.0f, 0.05f, 1.0f ); + } + else + { + //Black + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + } + glClear( GL_COLOR_BUFFER_BIT ); + + Com_Frame(); + + ovrFramebuffer_Resolve(frameBuffer); + + ovrFramebuffer_Release(frameBuffer); + } + + ovrFramebuffer_SetNone(); + + XrCompositionLayerProjection projection_layer = {}; + projection_layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + projection_layer.space = engine->appState.CurrentSpace; + projection_layer.viewCount = ovrMaxNumEyes; + projection_layer.views = projection_layer_elements; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; + + memset( + &projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + + projection_layer_elements[eye].pose = XrPosef_Inverse(viewTransform[eye]); + projection_layer_elements[eye].fov = projections[eye].fov; + + memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); + projection_layer_elements[eye].subImage.swapchain = + frameBuffer->ColorSwapChain.Handle; + projection_layer_elements[eye].subImage.imageRect.offset.x = 0; + projection_layer_elements[eye].subImage.imageRect.offset.y = 0; + projection_layer_elements[eye].subImage.imageRect.extent.width = + frameBuffer->ColorSwapChain.Width; + projection_layer_elements[eye].subImage.imageRect.extent.height = + frameBuffer->ColorSwapChain.Height; + projection_layer_elements[eye].subImage.imageArrayIndex = 0; + } + + engine->appState.Layers[engine->appState.LayerCount++].Projection = projection_layer; + } + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {}; + for (int i = 0; i < engine->appState.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&engine->appState.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {}; + endFrameInfo.type = XR_TYPE_FRAME_END_INFO; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = engine->appState.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(engine->appState.Session, &endFrameInfo)); //TODO /* diff --git a/android/app/src/main/cpp/code/vr/vr_types.c b/android/app/src/main/cpp/code/vr/vr_types.c index 2bfd8451..44a1fd58 100644 --- a/android/app/src/main/cpp/code/vr/vr_types.c +++ b/android/app/src/main/cpp/code/vr/vr_types.c @@ -14,134 +14,12 @@ Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rig #include #include #include -#include #include #include -#include #include -//ID engines common -#include "../qcommon/q_shared.h" -#include "../qcommon/qcommon.h" - -#if !defined(EGL_OPENGL_ES3_BIT_KHR) -#define EGL_OPENGL_ES3_BIT_KHR 0x0040 -#endif - -// EXT_texture_border_clamp -#ifndef GL_CLAMP_TO_BORDER -#define GL_CLAMP_TO_BORDER 0x812D -#endif - -#ifndef GL_TEXTURE_BORDER_COLOR -#define GL_TEXTURE_BORDER_COLOR 0x1004 -#endif - -#if !defined(GL_EXT_multisampled_render_to_texture) -typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( -GLenum target, - GLsizei samples, -GLenum internalformat, - GLsizei width, -GLsizei height); -typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( -GLenum target, - GLenum attachment, -GLenum textarget, - GLuint texture, -GLint level, - GLsizei samples); -#endif - -// GL_EXT_texture_cube_map_array -#if !defined(GL_TEXTURE_CUBE_MAP_ARRAY) -#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 -#endif - - -#define MATH_PI 3.14159265358979323846f - -#define GL(func) func; -#define OXR(func) func; -#define OVR_LOG_TAG "XrCompositor" - -#define ALOGE(...) Com_Printf(__VA_ARGS__) -#define ALOGV(...) Com_Printf(__VA_ARGS__) - -const int CPU_LEVEL = 2; -const int GPU_LEVEL = 3; const int NUM_MULTI_SAMPLES = 4; -typedef union { - XrCompositionLayerProjection Projection; - XrCompositionLayerQuad Quad; - XrCompositionLayerCylinderKHR Cylinder; - XrCompositionLayerCubeKHR Cube; - XrCompositionLayerEquirect2KHR Equirect2; -} ovrCompositorLayer_Union; - -/* -================================================================================ - -OpenGL-ES Utility Functions - -================================================================================ -*/ - -const char* EglErrorString(const EGLint error) { - switch (error) { - case EGL_SUCCESS: - return "EGL_SUCCESS"; - case EGL_NOT_INITIALIZED: - return "EGL_NOT_INITIALIZED"; - case EGL_BAD_ACCESS: - return "EGL_BAD_ACCESS"; - case EGL_BAD_ALLOC: - return "EGL_BAD_ALLOC"; - case EGL_BAD_ATTRIBUTE: - return "EGL_BAD_ATTRIBUTE"; - case EGL_BAD_CONTEXT: - return "EGL_BAD_CONTEXT"; - case EGL_BAD_CONFIG: - return "EGL_BAD_CONFIG"; - case EGL_BAD_CURRENT_SURFACE: - return "EGL_BAD_CURRENT_SURFACE"; - case EGL_BAD_DISPLAY: - return "EGL_BAD_DISPLAY"; - case EGL_BAD_SURFACE: - return "EGL_BAD_SURFACE"; - case EGL_BAD_MATCH: - return "EGL_BAD_MATCH"; - case EGL_BAD_PARAMETER: - return "EGL_BAD_PARAMETER"; - case EGL_BAD_NATIVE_PIXMAP: - return "EGL_BAD_NATIVE_PIXMAP"; - case EGL_BAD_NATIVE_WINDOW: - return "EGL_BAD_NATIVE_WINDOW"; - case EGL_CONTEXT_LOST: - return "EGL_CONTEXT_LOST"; - default: - return "unknown"; - } -} - -const char* GlFrameBufferStatusString(GLenum status) { - switch (status) { - case GL_FRAMEBUFFER_UNDEFINED: - return "GL_FRAMEBUFFER_UNDEFINED"; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; - case GL_FRAMEBUFFER_UNSUPPORTED: - return "GL_FRAMEBUFFER_UNSUPPORTED"; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; - default: - return "unknown"; - } -} - /* ================================================================================ @@ -150,6 +28,7 @@ ovrFramebuffer ================================================================================ */ + void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->Width = 0; frameBuffer->Height = 0; @@ -164,7 +43,7 @@ void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->FrameBuffers = NULL; } -GLboolean ovrFramebuffer_Create( +bool ovrFramebuffer_Create( XrSession session, ovrFramebuffer* frameBuffer, const GLenum colorFormat, @@ -300,8 +179,7 @@ GLboolean ovrFramebuffer_Create( GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { ALOGE( - "Incomplete frame buffer object: %s", - GlFrameBufferStatusString(renderFramebufferStatus)); + "Incomplete frame buffer object: %d", renderFramebufferStatus); return false; } } else { @@ -325,8 +203,7 @@ GLboolean ovrFramebuffer_Create( GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { ALOGE( - "Incomplete frame buffer object: %s", - GlFrameBufferStatusString(renderFramebufferStatus)); + "Incomplete frame buffer object: %d", renderFramebufferStatus); return false; } } @@ -360,8 +237,6 @@ 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); - - // We now let the resolve happen implicitly. } void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { @@ -400,7 +275,7 @@ ovrRenderer */ void ovrRenderer_Clear(ovrRenderer* renderer) { - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]); } } @@ -411,7 +286,7 @@ void ovrRenderer_Create( int suggestedEyeTextureWidth, int suggestedEyeTextureHeight) { // Create the frame buffers. - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { ovrFramebuffer_Create( session, &renderer->FrameBuffer[eye], @@ -423,64 +298,200 @@ void ovrRenderer_Create( } void ovrRenderer_Destroy(ovrRenderer* renderer) { - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); } } -void ovrRenderer_SetFoveation( - XrInstance* instance, - XrSession* session, - ovrRenderer* renderer, - XrFoveationLevelFB level, - float verticalOffset, - XrFoveationDynamicFB dynamic) { - PFN_xrCreateFoveationProfileFB pfnCreateFoveationProfileFB; - OXR(xrGetInstanceProcAddr( - *instance, - "xrCreateFoveationProfileFB", - (PFN_xrVoidFunction*)(&pfnCreateFoveationProfileFB))); +/* +================================================================================ - PFN_xrDestroyFoveationProfileFB pfnDestroyFoveationProfileFB; - OXR(xrGetInstanceProcAddr( - *instance, - "xrDestroyFoveationProfileFB", - (PFN_xrVoidFunction*)(&pfnDestroyFoveationProfileFB))); +ovrApp - PFN_xrUpdateSwapchainFB pfnUpdateSwapchainFB; - OXR(xrGetInstanceProcAddr( - *instance, "xrUpdateSwapchainFB", (PFN_xrVoidFunction*)(&pfnUpdateSwapchainFB))); +================================================================================ +*/ - for (int eye = 0; eye < XR_EYES_COUNT; eye++) { - XrFoveationLevelProfileCreateInfoFB levelProfileCreateInfo; - memset(&levelProfileCreateInfo, 0, sizeof(levelProfileCreateInfo)); - levelProfileCreateInfo.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB; - levelProfileCreateInfo.level = level; - levelProfileCreateInfo.verticalOffset = verticalOffset; - levelProfileCreateInfo.dynamic = dynamic; +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->LocalSpace = XR_NULL_HANDLE; + app->StageSpace = XR_NULL_HANDLE; + app->FakeStageSpace = XR_NULL_HANDLE; + app->CurrentSpace = XR_NULL_HANDLE; + app->SessionActive = false; + app->SupportedDisplayRefreshRates = NULL; + app->RequestedDisplayRefreshRateIndex = 0; + app->NumSupportedDisplayRefreshRates = 0; + app->pfnGetDisplayRefreshRate = NULL; + app->pfnRequestDisplayRefreshRate = NULL; + app->SwapInterval = 1; + memset(app->Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + app->LayerCount = 0; + app->MainThreadTid = 0; + app->RenderThreadTid = 0; + app->TouchPadDownLastFrame = false; - XrFoveationProfileCreateInfoFB profileCreateInfo; - memset(&profileCreateInfo, 0, sizeof(profileCreateInfo)); - profileCreateInfo.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB; - profileCreateInfo.next = &levelProfileCreateInfo; + ovrRenderer_Clear(&app->Renderer); +} - XrFoveationProfileFB foveationProfile; +void ovrApp_Destroy(ovrApp* app) { + if (app->SupportedDisplayRefreshRates != NULL) { + free(app->SupportedDisplayRefreshRates); + } - pfnCreateFoveationProfileFB(*session, &profileCreateInfo, &foveationProfile); + ovrApp_Clear(app); +} - XrSwapchainStateFoveationFB foveationUpdateState; - memset(&foveationUpdateState, 0, sizeof(foveationUpdateState)); - foveationUpdateState.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB; - foveationUpdateState.profile = foveationProfile; +void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { + assert(app->SessionActive == false); - pfnUpdateSwapchainFB( - renderer->FrameBuffer[eye].ColorSwapChain.Handle, - (XrSwapchainStateBaseHeaderFB*)(&foveationUpdateState)); + XrSessionBeginInfo sessionBeginInfo; + memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo)); + sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO; + sessionBeginInfo.next = NULL; + sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType; - pfnDestroyFoveationProfileFB(foveationProfile); + XrResult result; + OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); + + app->SessionActive = (result == XR_SUCCESS); + + // Set session state once we have entered VR mode and have a valid session object. + if (app->SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid)); + } + } else if (state == XR_SESSION_STATE_STOPPING) { + assert(app->SessionActive); + + OXR(xrEndSession(app->Session)); + app->SessionActive = false; } } +void ovrApp_HandleXrEvents(ovrApp* app) { + XrEventDataBuffer eventDataBuffer = {}; + + // 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_DISPLAY_REFRESH_RATE_CHANGED_FB: { + const XrEventDataDisplayRefreshRateChangedFB* refresh_rate_changed_event = + (XrEventDataDisplayRefreshRateChangedFB*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB event: fromRate %f -> toRate %f", + refresh_rate_changed_event->fromDisplayRefreshRate, + refresh_rate_changed_event->toDisplayRefreshRate); + } 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)); + } 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; + } + } +} + +/* +================================================================================ + +ovrMatrix4f + +================================================================================ +*/ + + ovrMatrix4f ovrMatrix4f_CreateProjection( const float minX, const float maxX, diff --git a/android/app/src/main/cpp/code/vr/vr_types.h b/android/app/src/main/cpp/code/vr/vr_types.h index caa61749..8670d425 100644 --- a/android/app/src/main/cpp/code/vr/vr_types.h +++ b/android/app/src/main/cpp/code/vr/vr_types.h @@ -23,6 +23,35 @@ #include #include +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( +GLenum target, +GLsizei samples, +GLenum internalformat, +GLsizei width, +GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( +GLenum target, +GLenum attachment, +GLenum textarget, +GLuint texture, +GLint level, +GLsizei samples); +#endif + +#define ALOGE(...) printf(__VA_ARGS__) +#define ALOGV(...) printf(__VA_ARGS__) + +typedef union { + XrCompositionLayerProjection Projection; +} ovrCompositorLayer_Union; + +enum { ovrMaxLayerCount = 16 }; +enum { ovrMaxNumEyes = 2 }; + +#define GL(func) func; +#define OXR(func) func; + typedef struct { JavaVM* Vm; jobject ActivityObject; @@ -30,45 +59,73 @@ typedef struct { } ovrJava; typedef struct { - XrSwapchain Handle; - uint32_t Width; - uint32_t Height; + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; } ovrSwapChain; typedef struct { - int Width; - int Height; - int Multisamples; - uint32_t TextureSwapChainLength; - uint32_t TextureSwapChainIndex; - ovrSwapChain ColorSwapChain; - XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; - GLuint* DepthBuffers; - GLuint* FrameBuffers; + int Width; + int Height; + int Multisamples; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + ovrSwapChain ColorSwapChain; + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; + GLuint* DepthBuffers; + GLuint* FrameBuffers; } ovrFramebuffer; typedef struct { - ovrFramebuffer FrameBuffer[XR_EYES_COUNT]; + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; } ovrRenderer; typedef struct { - XrMatrix4x4f ViewMatrix[XR_EYES_COUNT]; - XrMatrix4x4f ProjectionMatrix[XR_EYES_COUNT]; + XrMatrix4x4f ViewMatrix[ovrMaxNumEyes]; + XrMatrix4x4f ProjectionMatrix[ovrMaxNumEyes]; } ovrSceneMatrices; +typedef struct { + GLboolean Focused; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace LocalSpace; + XrSpace StageSpace; + XrSpace FakeStageSpace; + XrSpace CurrentSpace; + GLboolean SessionActive; + + float* SupportedDisplayRefreshRates; + uint32_t RequestedDisplayRefreshRateIndex; + uint32_t NumSupportedDisplayRefreshRates; + PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRate; + PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRate; + + int SwapInterval; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + + GLboolean TouchPadDownLastFrame; + ovrRenderer Renderer; +} ovrApp; + + typedef struct ovrMatrix4f_ { float M[4][4]; } ovrMatrix4f; typedef struct { uint64_t frameIndex; + ovrApp appState; ovrJava java; - ovrRenderer renderer; - XrInstance instance; - XrSession session; - XrSystemId systemId; - XrSpace stageSpace; - GLboolean sessionActive; } engine_t; typedef enum { @@ -87,46 +144,16 @@ typedef enum { VRFM_QUERY = 99 //Used to query which mode is active } vrFollowMode_t; -//ovrFramebuffer -void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer); -GLboolean ovrFramebuffer_Create( - XrSession session, - ovrFramebuffer* frameBuffer, - const GLenum colorFormat, - const int width, - const int height, - const int multisamples); -void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer); +void ovrApp_Clear(ovrApp* app); +void ovrApp_Destroy(ovrApp* app); +void ovrApp_HandleXrEvents(ovrApp* app); + +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer); void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer); void ovrFramebuffer_SetNone(); -void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer); -void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer); -void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer); -//ovrRenderer -void ovrRenderer_Clear(ovrRenderer* renderer); -void ovrRenderer_Create( - XrSession session, - ovrRenderer* renderer, - int suggestedEyeTextureWidth, - int suggestedEyeTextureHeight); -void ovrRenderer_Destroy(ovrRenderer* renderer); -void ovrRenderer_SetFoveation( - XrInstance* instance, - XrSession* session, - ovrRenderer* renderer, - XrFoveationLevelFB level, - float verticalOffset, - XrFoveationDynamicFB dynamic); - -//ovrMatrix4f -ovrMatrix4f ovrMatrix4f_CreateProjection( - const float minX, - const float maxX, - float const minY, - const float maxY, - const float nearZ, - const float farZ); ovrMatrix4f ovrMatrix4f_CreateProjectionFov( const float fovDegreesX, const float fovDegreesY, @@ -135,4 +162,11 @@ ovrMatrix4f ovrMatrix4f_CreateProjectionFov( const float nearZ, const float farZ); +void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight); +void ovrRenderer_Destroy(ovrRenderer* renderer); + #endif