#include "VRFramebuffer.h" #include #include #include #include #include #include #include #include #include #include #include /* ================================================================================ 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_DEPTH24_STENCIL8, 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_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[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; 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; } } int ovrApp_HandleXrEvents(ovrApp* app) { XrEventDataBuffer eventDataBuffer = {}; int recenter = 0; // 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 = 1; } 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(); }