monado/src/xrt/state_trackers/oxr/oxr_session.c
2019-03-24 13:25:30 -07:00

519 lines
14 KiB
C

// Copyright 2018-2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Holds session related functions.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup oxr_main
*/
#include <stdio.h>
#include <stdlib.h>
#include "util/u_debug.h"
#include "util/u_misc.h"
#include "math/m_api.h"
#include "util/u_time.h"
#include "xrt/xrt_device.h"
#include "xrt/xrt_gfx_xlib.h"
#include "xrt/xrt_gfx_vk.h"
#include "oxr_objects.h"
#include "oxr_logger.h"
#include "oxr_two_call.h"
DEBUG_GET_ONCE_BOOL_OPTION(views, "OXR_DEBUG_VIEWS", false)
DEBUG_GET_ONCE_NUM_OPTION(ipd, "OXR_DEBUG_IPD_MM", 63)
DEBUG_GET_ONCE_NUM_OPTION(prediction_ms, "OXR_DEBUG_PREDICTION_MS", 11)
static bool
is_running(XrSessionState state)
{
switch (state) {
case XR_SESSION_STATE_RUNNING: return true;
case XR_SESSION_STATE_VISIBLE: return true;
case XR_SESSION_STATE_FOCUSED: return true;
default: return false;
}
}
XrResult
oxr_session_enumerate_formats(struct oxr_logger *log,
struct oxr_session *sess,
uint32_t formatCapacityInput,
uint32_t *formatCountOutput,
int64_t *formats)
{
struct xrt_compositor *xc = sess->compositor;
if (xc == NULL) {
*formatCountOutput = 0;
return XR_SUCCESS;
}
OXR_TWO_CALL_HELPER(log, formatCapacityInput, formatCountOutput,
formats, xc->num_formats, xc->formats);
}
XrResult
oxr_session_begin(struct oxr_logger *log,
struct oxr_session *sess,
const XrSessionBeginInfo *beginInfo)
{
if (is_running(sess->state)) {
return oxr_error(log, XR_ERROR_SESSION_RUNNING,
" session is already running");
}
struct xrt_compositor *xc = sess->compositor;
if (xc != NULL) {
xc->begin_session(xc, (enum xrt_view_type)beginInfo
->primaryViewConfigurationType);
}
oxr_event_push_XrEventDataSessionStateChanged(
log, sess, XR_SESSION_STATE_RUNNING, 0);
oxr_event_push_XrEventDataSessionStateChanged(
log, sess, XR_SESSION_STATE_VISIBLE, 0);
oxr_event_push_XrEventDataSessionStateChanged(
log, sess, XR_SESSION_STATE_FOCUSED, 0);
sess->state = XR_SESSION_STATE_FOCUSED;
return XR_SUCCESS;
}
XrResult
oxr_session_end(struct oxr_logger *log, struct oxr_session *sess)
{
struct xrt_compositor *xc = sess->compositor;
if (!is_running(sess->state)) {
return oxr_error(log, XR_ERROR_SESSION_NOT_RUNNING,
" session is not running");
}
if (xc != NULL) {
if (sess->frame_started) {
xc->discard_frame(xc);
sess->frame_started = false;
}
xc->end_session(xc);
}
oxr_event_push_XrEventDataSessionStateChanged(
log, sess, XR_SESSION_STATE_STOPPING, 0);
oxr_event_push_XrEventDataSessionStateChanged(log, sess,
XR_SESSION_STATE_IDLE, 0);
oxr_event_push_XrEventDataSessionStateChanged(
log, sess, XR_SESSION_STATE_READY, 0);
sess->state = XR_SESSION_STATE_READY;
return XR_SUCCESS;
}
XrResult
oxr_session_get_view_pose_at(struct oxr_logger *log,
struct oxr_session *sess,
XrTime at_time,
struct xrt_pose *pose)
{
// @todo This function needs to be massively expanded to support all
// use cases this drive. The main use of this function is to get
// either the predicted position of the headset device. Right now
// it only returns the current position. But it must also deal
// with past values are allowed by the spec. See displayTime
// argument on the xrLocateViews function. It will also drive
// the function xrLocateSpace view using the view space.
// @todo If using orientation tracking only implement a neck model to
// get at least a slightly better position.
struct xrt_device *xdev = sess->sys->device;
struct xrt_space_relation relation;
xdev->get_tracked_pose(xdev, &relation);
if ((relation.relation_flags &
XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0) {
pose->orientation = relation.pose.orientation;
} else {
pose->orientation.x = 0;
pose->orientation.y = 0;
pose->orientation.z = 0;
pose->orientation.w = 1;
}
if ((relation.relation_flags & XRT_SPACE_RELATION_POSITION_VALID_BIT) !=
0) {
pose->position = relation.pose.position;
} else {
// "nominal height" 1.6m
pose->position.x = 0.0f;
pose->position.y = 1.60f;
pose->position.z = 0.0f;
}
if ((relation.relation_flags &
XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT) != 0) {
//! @todo Forcing a fixed amount of prediction for now since
//! devices don't tell us timestamps yet.
struct xrt_quat predicted;
math_quat_integrate_velocity(
&pose->orientation, &relation.angular_velocity,
sess->static_prediction_s, &predicted);
if (debug_get_bool_option_views()) {
fprintf(
stderr,
"\toriginal quat = {%f, %f, %f, %f} predicted = "
"{%f, %f, %f, %f}\n",
pose->orientation.x, pose->orientation.y,
pose->orientation.z, pose->orientation.w,
predicted.x, predicted.y, predicted.z, predicted.w);
}
pose->orientation = predicted;
}
return XR_SUCCESS;
}
void
print_view_fov(uint32_t index, const struct xrt_fov *fov)
{
if (!debug_get_bool_option_views()) {
return;
}
fprintf(stderr, "\tviews[%i].fov = {%f, %f, %f, %f}\n", index,
fov->angle_left, fov->angle_right, fov->angle_up,
fov->angle_down);
}
void
print_view_pose(uint32_t index, const struct xrt_pose *pose)
{
if (!debug_get_bool_option_views()) {
return;
}
fprintf(stderr, "\tviews[%i].pose = {{%f, %f, %f, %f}, {%f, %f, %f}}\n",
index, pose->orientation.x, pose->orientation.y,
pose->orientation.z, pose->orientation.w, pose->position.x,
pose->position.y, pose->position.z);
}
XrResult
oxr_session_views(struct oxr_logger *log,
struct oxr_session *sess,
const XrViewLocateInfo *viewLocateInfo,
XrViewState *viewState,
uint32_t viewCapacityInput,
uint32_t *viewCountOutput,
XrView *views)
{
struct xrt_device *xdev = sess->sys->device;
struct oxr_space *baseSpc = (struct oxr_space *)viewLocateInfo->space;
uint32_t num_views = 2;
// Does this apply for all calls?
if (!baseSpc->is_reference) {
viewState->viewStateFlags = 0;
return XR_SUCCESS;
}
// Start two call handling.
if (viewCountOutput != NULL) {
*viewCountOutput = num_views;
}
if (viewCapacityInput == 0) {
return XR_SUCCESS;
}
if (viewCapacityInput < num_views) {
return oxr_error(log, XR_ERROR_SIZE_INSUFFICIENT,
"(viewCapacityInput == %u) need %u",
viewCapacityInput, num_views);
}
// End two call handling.
if (debug_get_bool_option_views()) {
fprintf(stderr, "%s\n", __func__);
fprintf(stderr, "\tviewLocateInfo->displayTime %lu\n",
viewLocateInfo->displayTime);
}
// Get the viewLocateInfo->space to view space relation.
struct xrt_space_relation pure_relation;
oxr_space_ref_relation(log, sess, XR_REFERENCE_SPACE_TYPE_VIEW,
baseSpc->type, viewLocateInfo->displayTime,
&pure_relation);
struct xrt_pose pure = pure_relation.pose;
// @todo the fov information that we get from xdev->views[i].fov is not
// properly filled out in oh_device.c, fix before wasting time on
// debugging weird rendering when adding stuff here.
for (uint32_t i = 0; i < num_views; i++) {
//! @todo Do not hardcode IPD.
struct xrt_vec3 eye_relation = {
sess->ipd_meters,
0.0f,
0.0f,
};
struct xrt_pose view_pose;
// Get the per view pose from the device.
xdev->get_view_pose(xdev, &eye_relation, i, &view_pose);
// Do the magical space relation dance here.
math_pose_openxr_locate(&view_pose, &pure, &baseSpc->pose,
(struct xrt_pose *)&views[i].pose);
// Copy the fov information directly from the device.
views[i].fov = *(XrFovf *)&xdev->views[i].fov;
print_view_fov(i, (struct xrt_fov *)&views[i].fov);
print_view_pose(i, (struct xrt_pose *)&views[i].pose);
}
// @todo Add tracking bit once we have them.
viewState->viewStateFlags = 0;
viewState->viewStateFlags |= XR_VIEW_STATE_POSITION_VALID_BIT;
viewState->viewStateFlags |= XR_VIEW_STATE_ORIENTATION_VALID_BIT;
return XR_SUCCESS;
}
XrResult
oxr_session_frame_wait(struct oxr_logger *log,
struct oxr_session *sess,
XrFrameState *frameState)
{
if (!is_running(sess->state)) {
return oxr_error(log, XR_ERROR_SESSION_NOT_RUNNING,
" session is not running");
}
// OK to update this here because xrWaitFrame must be externally
// synchronized by the app.
timepoint_ns now =
time_state_get_now_and_update(sess->sys->inst->timekeeping);
// Set defaults - may be overridden by compositor.
frameState->predictedDisplayPeriod = sess->nominal_frame_interval_ns;
frameState->predictedDisplayTime =
now + frameState->predictedDisplayPeriod;
struct xrt_compositor *xc = sess->compositor;
xc->wait_frame(xc, &frameState->predictedDisplayTime,
&frameState->predictedDisplayPeriod);
return XR_SUCCESS;
}
XrResult
oxr_session_frame_begin(struct oxr_logger *log, struct oxr_session *sess)
{
if (!is_running(sess->state)) {
return oxr_error(log, XR_ERROR_SESSION_NOT_RUNNING,
" session is not running");
}
struct xrt_compositor *xc = sess->compositor;
XrResult ret;
if (sess->frame_started) {
ret = XR_FRAME_DISCARDED;
xc->discard_frame(xc);
} else {
ret = XR_SUCCESS;
sess->frame_started = true;
}
xc->begin_frame(xc);
return ret;
}
static enum xrt_blend_mode
oxr_blend_mode_to_xrt(XrEnvironmentBlendMode blend_mode)
{
// clang-format off
switch (blend_mode) {
case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: return XRT_BLEND_MODE_OPAQUE;
case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: return XRT_BLEND_MODE_ADDITIVE;
case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: return XRT_BLEND_MODE_ALPHA_BLEND;
default: return (enum xrt_blend_mode)0;
}
// clang-format on
}
XrResult
oxr_session_frame_end(struct oxr_logger *log,
struct oxr_session *sess,
const XrFrameEndInfo *frameEndInfo)
{
/*
* Session state and call order.
*/
if (!is_running(sess->state)) {
return oxr_error(log, XR_ERROR_SESSION_NOT_RUNNING,
" session is not running");
}
if (!sess->frame_started) {
return oxr_error(log, XR_ERROR_CALL_ORDER_INVALID,
" frame not begun with xrBeginFrame");
}
struct xrt_compositor *xc = sess->compositor;
/*
* Early out for discarded frame if layer count is 0,
* since then blend mode, etc. doesn't matter.
*/
if (frameEndInfo->layerCount == 0) {
xc->discard_frame(xc);
sess->frame_started = false;
return XR_SUCCESS;
}
/*
* Blend mode.
*/
enum xrt_blend_mode blend_mode =
oxr_blend_mode_to_xrt(frameEndInfo->environmentBlendMode);
if (blend_mode == 0) {
return oxr_error(log, XR_ERROR_VALIDATION_FAILURE,
"(frameEndInfo->environmentBlendMode) "
"unknown environment blend mode");
}
if ((blend_mode & sess->sys->device->blend_mode) == 0) {
return oxr_error(log,
XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED,
"(frameEndInfo->environmentBlendMode) "
"is not supported");
}
/*
* Layers.
*/
if (frameEndInfo->layers == NULL) {
return oxr_error(log, XR_ERROR_VALIDATION_FAILURE,
"(frameEndInfo->layers == NULL)");
}
if (frameEndInfo->layers[0]->type !=
XR_TYPE_COMPOSITION_LAYER_PROJECTION) {
return oxr_error(log, XR_ERROR_VALIDATION_FAILURE,
"(frameEndInfo->layers[0]->type)");
}
XrCompositionLayerProjection *proj =
(XrCompositionLayerProjection *)frameEndInfo->layers[0];
if (proj->viewCount != 2) {
return oxr_error(log, XR_ERROR_VALIDATION_FAILURE,
"(frameEndInfo->layers[0]->viewCount == %u)"
" must be 2",
proj->viewCount);
}
/*
* Doing the real work.
*/
struct xrt_swapchain *chains[2];
uint32_t acquired_index[2];
uint32_t num_chains = ARRAY_SIZE(chains);
for (uint32_t i = 0; i < num_chains; i++) {
//! @todo Validate this above.
struct oxr_swapchain *sc =
(struct oxr_swapchain *)proj->views[i].subImage.swapchain;
chains[i] = sc->swapchain;
acquired_index[i] = proj->views[i].subImage.imageArrayIndex;
}
xc->end_frame(xc, blend_mode, chains, acquired_index, num_chains);
sess->frame_started = false;
return XR_SUCCESS;
}
XrResult
oxr_session_create(struct oxr_logger *log,
struct oxr_system *sys,
XrStructureType *next,
struct oxr_session **out_session)
{
struct oxr_session *sess;
XrResult ret;
if (sys->inst->headless && next == NULL) {
ret = XR_SUCCESS;
sess = U_TYPED_CALLOC(struct oxr_session);
sess->debug = OXR_XR_DEBUG_SESSION;
sess->sys = sys;
sess->compositor = NULL;
sess->create_swapchain = NULL;
} else
#ifdef XR_USE_PLATFORM_XLIB
if (*next == XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR) {
ret = oxr_session_create_gl_xlib(
log, sys, (XrGraphicsBindingOpenGLXlibKHR *)next, &sess);
} else
#endif
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (*next == XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR) {
ret = oxr_session_create_vk(
log, sys, (XrGraphicsBindingVulkanKHR *)next, &sess);
} else
#endif
{
ret = oxr_error(log, XR_ERROR_VALIDATION_FAILURE,
"(createInfo->next->type)");
}
if (ret != XR_SUCCESS) {
return ret;
}
sess->ipd_meters = debug_get_num_option_ipd() / 1000.0f;
//! @todo hard-coding 90Hz
sess->nominal_frame_interval_ns = 11111111;
sess->static_prediction_s =
debug_get_num_option_prediction_ms() / 1000.0f;
oxr_event_push_XrEventDataSessionStateChanged(log, sess,
XR_SESSION_STATE_IDLE, 0);
oxr_event_push_XrEventDataSessionStateChanged(
log, sess, XR_SESSION_STATE_READY, 0);
sess->state = XR_SESSION_STATE_READY;
*out_session = sess;
return ret;
}
XrResult
oxr_session_destroy(struct oxr_logger *log, struct oxr_session *sess)
{
if (sess->compositor != NULL) {
sess->compositor->destroy(sess->compositor);
}
free(sess);
return XR_SUCCESS;
}