mirror of
https://github.com/lbgos/rift-s-linux.git
synced 2026-10-27 03:01:23 +01:00
1794 lines
48 KiB
C
1794 lines
48 KiB
C
// Copyright 2020-2023, Collabora, Ltd.
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
/*!
|
|
* @file
|
|
* @brief Handling functions called from generated dispatch function.
|
|
* @author Pete Black <pblack@collabora.com>
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
* @author Korcan Hussein <korcan.hussein@collabora.com>
|
|
* @ingroup ipc_server
|
|
*/
|
|
|
|
#include "util/u_misc.h"
|
|
#include "util/u_handles.h"
|
|
#include "util/u_pretty_print.h"
|
|
#include "util/u_visibility_mask.h"
|
|
#include "util/u_trace_marker.h"
|
|
|
|
#include "server/ipc_server.h"
|
|
#include "ipc_server_generated.h"
|
|
|
|
#ifdef XRT_GRAPHICS_SYNC_HANDLE_IS_FD
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
|
|
/*
|
|
*
|
|
* Helper functions.
|
|
*
|
|
*/
|
|
|
|
static xrt_result_t
|
|
validate_device_id(volatile struct ipc_client_state *ics, int64_t device_id, struct xrt_device **out_device)
|
|
{
|
|
if (device_id >= XRT_SYSTEM_MAX_DEVICES) {
|
|
IPC_ERROR(ics->server, "Invalid device ID (device_id >= XRT_SYSTEM_MAX_DEVICES)!");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
struct xrt_device *xdev = ics->server->idevs[device_id].xdev;
|
|
if (xdev == NULL) {
|
|
IPC_ERROR(ics->server, "Invalid device ID (xdev is NULL)!");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
*out_device = xdev;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
static xrt_result_t
|
|
validate_swapchain_state(volatile struct ipc_client_state *ics, uint32_t *out_index)
|
|
{
|
|
// Our handle is just the index for now.
|
|
uint32_t index = 0;
|
|
for (; index < IPC_MAX_CLIENT_SWAPCHAINS; index++) {
|
|
if (!ics->swapchain_data[index].active) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index >= IPC_MAX_CLIENT_SWAPCHAINS) {
|
|
IPC_ERROR(ics->server, "Too many swapchains!");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
*out_index = index;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
set_swapchain_info(volatile struct ipc_client_state *ics,
|
|
uint32_t index,
|
|
const struct xrt_swapchain_create_info *info,
|
|
struct xrt_swapchain *xsc)
|
|
{
|
|
ics->xscs[index] = xsc;
|
|
ics->swapchain_data[index].active = true;
|
|
ics->swapchain_data[index].width = info->width;
|
|
ics->swapchain_data[index].height = info->height;
|
|
ics->swapchain_data[index].format = info->format;
|
|
ics->swapchain_data[index].image_count = xsc->image_count;
|
|
}
|
|
|
|
static xrt_result_t
|
|
validate_reference_space_type(volatile struct ipc_client_state *ics, enum xrt_reference_space_type type)
|
|
{
|
|
if ((uint32_t)type >= XRT_SPACE_REFERENCE_TYPE_COUNT) {
|
|
IPC_ERROR(ics->server, "Invalid reference space type %u", type);
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
static xrt_result_t
|
|
validate_space_id(volatile struct ipc_client_state *ics, int64_t space_id, struct xrt_space **out_xspc)
|
|
{
|
|
if (space_id < 0) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
if (space_id >= IPC_MAX_CLIENT_SPACES) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
if (ics->xspcs[space_id] == NULL) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
*out_xspc = (struct xrt_space *)ics->xspcs[space_id];
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
static xrt_result_t
|
|
get_new_space_id(volatile struct ipc_client_state *ics, uint32_t *out_id)
|
|
{
|
|
// Our handle is just the index for now.
|
|
uint32_t index = 0;
|
|
for (; index < IPC_MAX_CLIENT_SPACES; index++) {
|
|
if (ics->xspcs[index] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index >= IPC_MAX_CLIENT_SPACES) {
|
|
IPC_ERROR(ics->server, "Too many spaces!");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
*out_id = index;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
static xrt_result_t
|
|
track_space(volatile struct ipc_client_state *ics, struct xrt_space *xs, uint32_t *out_id)
|
|
{
|
|
uint32_t id = UINT32_MAX;
|
|
xrt_result_t xret = get_new_space_id(ics, &id);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
// Remove volatile
|
|
struct xrt_space **xs_ptr = (struct xrt_space **)&ics->xspcs[id];
|
|
xrt_space_reference(xs_ptr, xs);
|
|
|
|
*out_id = id;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Handle functions.
|
|
*
|
|
*/
|
|
|
|
xrt_result_t
|
|
ipc_handle_instance_get_shm_fd(volatile struct ipc_client_state *ics,
|
|
uint32_t max_handle_capacity,
|
|
xrt_shmem_handle_t *out_handles,
|
|
uint32_t *out_handle_count)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
assert(max_handle_capacity >= 1);
|
|
|
|
out_handles[0] = ics->server->ism_handle;
|
|
*out_handle_count = 1;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_instance_describe_client(volatile struct ipc_client_state *ics,
|
|
const struct ipc_client_description *client_desc)
|
|
{
|
|
ics->client_state.info = client_desc->info;
|
|
ics->client_state.pid = client_desc->pid;
|
|
|
|
struct u_pp_sink_stack_only sink;
|
|
u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
|
|
|
|
#define P(...) u_pp(dg, __VA_ARGS__)
|
|
#define PNT(...) u_pp(dg, "\n\t" __VA_ARGS__)
|
|
#define PNTT(...) u_pp(dg, "\n\t\t" __VA_ARGS__)
|
|
#define EXT(NAME) PNTT(#NAME ": %s", client_desc->info.NAME ? "true" : "false")
|
|
|
|
P("Client info:");
|
|
PNT("id: %u", ics->client_state.id);
|
|
PNT("application_name: '%s'", client_desc->info.application_name);
|
|
PNT("pid: %i", client_desc->pid);
|
|
PNT("extensions:");
|
|
|
|
EXT(ext_hand_tracking_enabled);
|
|
EXT(ext_eye_gaze_interaction_enabled);
|
|
EXT(ext_hand_interaction_enabled);
|
|
|
|
#undef EXT
|
|
#undef PTT
|
|
#undef PT
|
|
#undef P
|
|
|
|
// Log the pretty message.
|
|
IPC_INFO(ics->server, "%s", sink.buffer);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_compositor_get_info(volatile struct ipc_client_state *ics,
|
|
struct xrt_system_compositor_info *out_info)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
*out_info = ics->server->xsysc->info;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_session_create(volatile struct ipc_client_state *ics,
|
|
const struct xrt_session_info *xsi,
|
|
bool create_native_compositor)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
struct xrt_session *xs = NULL;
|
|
struct xrt_compositor_native *xcn = NULL;
|
|
|
|
if (ics->xs != NULL) {
|
|
return XRT_ERROR_IPC_SESSION_ALREADY_CREATED;
|
|
}
|
|
|
|
if (!create_native_compositor) {
|
|
IPC_INFO(ics->server, "App asked for headless session, creating native compositor anyways");
|
|
}
|
|
|
|
xrt_result_t xret = xrt_system_create_session(ics->server->xsys, xsi, &xs, &xcn);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
ics->client_state.session_overlay = xsi->is_overlay;
|
|
ics->client_state.z_order = xsi->z_order;
|
|
|
|
ics->xs = xs;
|
|
ics->xc = &xcn->base;
|
|
|
|
xrt_syscomp_set_state(ics->server->xsysc, ics->xc, ics->client_state.session_visible,
|
|
ics->client_state.session_focused);
|
|
xrt_syscomp_set_z_order(ics->server->xsysc, ics->xc, ics->client_state.z_order);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_session_poll_events(volatile struct ipc_client_state *ics, union xrt_session_event *out_xse)
|
|
{
|
|
// Have we created the session?
|
|
if (ics->xs == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_session_poll_events(ics->xs, out_xse);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_session_begin(volatile struct ipc_client_state *ics)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
// Have we created the session?
|
|
if (ics->xs == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
// Need to check both because begin session is handled by compositor.
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_COMPOSITOR_NOT_CREATED;
|
|
}
|
|
|
|
//! @todo Pass the view type down.
|
|
const struct xrt_begin_session_info begin_session_info = {
|
|
.view_type = XRT_VIEW_TYPE_STEREO,
|
|
.ext_hand_tracking_enabled = ics->client_state.info.ext_hand_tracking_enabled,
|
|
.ext_eye_gaze_interaction_enabled = ics->client_state.info.ext_eye_gaze_interaction_enabled,
|
|
.ext_hand_interaction_enabled = ics->client_state.info.ext_hand_interaction_enabled,
|
|
};
|
|
|
|
return xrt_comp_begin_session(ics->xc, &begin_session_info);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_session_end(volatile struct ipc_client_state *ics)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
// Have we created the session?
|
|
if (ics->xs == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
// Need to check both because end session is handled by compositor.
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_COMPOSITOR_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_end_session(ics->xc);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_session_destroy(volatile struct ipc_client_state *ics)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
// Have we created the session?
|
|
if (ics->xs == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
ipc_server_client_destroy_session_and_compositor(ics);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_create_semantic_ids(volatile struct ipc_client_state *ics,
|
|
uint32_t *out_root_id,
|
|
uint32_t *out_view_id,
|
|
uint32_t *out_local_id,
|
|
uint32_t *out_local_floor_id,
|
|
uint32_t *out_stage_id,
|
|
uint32_t *out_unbounded_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
|
|
#define CREATE(NAME) \
|
|
do { \
|
|
*out_##NAME##_id = UINT32_MAX; \
|
|
if (xso->semantic.NAME == NULL) { \
|
|
break; \
|
|
} \
|
|
uint32_t id = 0; \
|
|
xrt_result_t xret = track_space(ics, xso->semantic.NAME, &id); \
|
|
if (xret != XRT_SUCCESS) { \
|
|
break; \
|
|
} \
|
|
*out_##NAME##_id = id; \
|
|
} while (false)
|
|
|
|
CREATE(root);
|
|
CREATE(view);
|
|
CREATE(local);
|
|
CREATE(local_floor);
|
|
CREATE(stage);
|
|
CREATE(unbounded);
|
|
|
|
#undef CREATE
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_create_offset(volatile struct ipc_client_state *ics,
|
|
uint32_t parent_id,
|
|
const struct xrt_pose *offset,
|
|
uint32_t *out_space_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
|
|
struct xrt_space *parent = NULL;
|
|
xrt_result_t xret = validate_space_id(ics, parent_id, &parent);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
|
|
struct xrt_space *xs = NULL;
|
|
xret = xrt_space_overseer_create_offset_space(xso, parent, offset, &xs);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
uint32_t space_id = UINT32_MAX;
|
|
xret = track_space(ics, xs, &space_id);
|
|
|
|
// Track space grabs a reference, or it errors and we don't want to keep it around.
|
|
xrt_space_reference(&xs, NULL);
|
|
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
*out_space_id = space_id;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_create_pose(volatile struct ipc_client_state *ics,
|
|
uint32_t xdev_id,
|
|
enum xrt_input_name name,
|
|
uint32_t *out_space_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
|
|
struct xrt_device *xdev = NULL;
|
|
xrt_result_t xret = validate_device_id(ics, xdev_id, &xdev);
|
|
if (xret != XRT_SUCCESS) {
|
|
U_LOG_E("Invalid device_id!");
|
|
return xret;
|
|
}
|
|
|
|
struct xrt_space *xs = NULL;
|
|
xret = xrt_space_overseer_create_pose_space(xso, xdev, name, &xs);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
uint32_t space_id = UINT32_MAX;
|
|
xret = track_space(ics, xs, &space_id);
|
|
|
|
// Track space grabs a reference, or it errors and we don't want to keep it around.
|
|
xrt_space_reference(&xs, NULL);
|
|
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
*out_space_id = space_id;
|
|
|
|
return xret;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_locate_space(volatile struct ipc_client_state *ics,
|
|
uint32_t base_space_id,
|
|
const struct xrt_pose *base_offset,
|
|
uint64_t at_timestamp,
|
|
uint32_t space_id,
|
|
const struct xrt_pose *offset,
|
|
struct xrt_space_relation *out_relation)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
struct xrt_space *base_space = NULL;
|
|
struct xrt_space *space = NULL;
|
|
xrt_result_t xret;
|
|
|
|
xret = validate_space_id(ics, base_space_id, &base_space);
|
|
if (xret != XRT_SUCCESS) {
|
|
U_LOG_E("Invalid base_space_id!");
|
|
return xret;
|
|
}
|
|
|
|
xret = validate_space_id(ics, space_id, &space);
|
|
if (xret != XRT_SUCCESS) {
|
|
U_LOG_E("Invalid space_id!");
|
|
return xret;
|
|
}
|
|
|
|
return xrt_space_overseer_locate_space( //
|
|
xso, //
|
|
base_space, //
|
|
base_offset, //
|
|
at_timestamp, //
|
|
space, //
|
|
offset, //
|
|
out_relation); //
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_locate_device(volatile struct ipc_client_state *ics,
|
|
uint32_t base_space_id,
|
|
const struct xrt_pose *base_offset,
|
|
uint64_t at_timestamp,
|
|
uint32_t xdev_id,
|
|
struct xrt_space_relation *out_relation)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
struct xrt_space *base_space = NULL;
|
|
struct xrt_device *xdev = NULL;
|
|
xrt_result_t xret;
|
|
|
|
xret = validate_space_id(ics, base_space_id, &base_space);
|
|
if (xret != XRT_SUCCESS) {
|
|
U_LOG_E("Invalid base_space_id!");
|
|
return xret;
|
|
}
|
|
|
|
xret = validate_device_id(ics, xdev_id, &xdev);
|
|
if (xret != XRT_SUCCESS) {
|
|
U_LOG_E("Invalid device_id!");
|
|
return xret;
|
|
}
|
|
|
|
return xrt_space_overseer_locate_device( //
|
|
xso, //
|
|
base_space, //
|
|
base_offset, //
|
|
at_timestamp, //
|
|
xdev, //
|
|
out_relation); //
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_destroy(volatile struct ipc_client_state *ics, uint32_t space_id)
|
|
{
|
|
struct xrt_space *xs = NULL;
|
|
xrt_result_t xret;
|
|
|
|
xret = validate_space_id(ics, space_id, &xs);
|
|
if (xret != XRT_SUCCESS) {
|
|
U_LOG_E("Invalid space_id!");
|
|
return xret;
|
|
}
|
|
|
|
assert(xs != NULL);
|
|
xs = NULL;
|
|
|
|
// Remove volatile
|
|
struct xrt_space **xs_ptr = (struct xrt_space **)&ics->xspcs[space_id];
|
|
xrt_space_reference(xs_ptr, NULL);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_mark_ref_space_in_use(volatile struct ipc_client_state *ics, enum xrt_reference_space_type type)
|
|
{
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
xrt_result_t xret;
|
|
|
|
xret = validate_reference_space_type(ics, type);
|
|
if (xret != XRT_SUCCESS) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
// Is this space already used?
|
|
if (ics->ref_space_used[type]) {
|
|
IPC_ERROR(ics->server, "Space '%u' already used!", type);
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
xret = xrt_space_overseer_ref_space_inc(xso, type);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(ics->server, "xrt_space_overseer_ref_space_inc failed");
|
|
return xret;
|
|
}
|
|
|
|
// Can now mark it as used.
|
|
ics->ref_space_used[type] = true;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_unmark_ref_space_in_use(volatile struct ipc_client_state *ics, enum xrt_reference_space_type type)
|
|
{
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
xrt_result_t xret;
|
|
|
|
xret = validate_reference_space_type(ics, type);
|
|
if (xret != XRT_SUCCESS) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
if (!ics->ref_space_used[type]) {
|
|
IPC_ERROR(ics->server, "Space '%u' not used!", type);
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
xret = xrt_space_overseer_ref_space_dec(xso, type);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(ics->server, "xrt_space_overseer_ref_space_dec failed");
|
|
return xret;
|
|
}
|
|
|
|
// Now we can mark it as not used.
|
|
ics->ref_space_used[type] = false;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_space_recenter_local_spaces(volatile struct ipc_client_state *ics)
|
|
{
|
|
struct xrt_space_overseer *xso = ics->server->xso;
|
|
|
|
return xrt_space_overseer_recenter_local_spaces(xso);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_get_info(volatile struct ipc_client_state *ics, struct xrt_compositor_info *out_info)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
*out_info = ics->xc->info;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_predict_frame(volatile struct ipc_client_state *ics,
|
|
int64_t *out_frame_id,
|
|
uint64_t *out_wake_up_time_ns,
|
|
uint64_t *out_predicted_display_time_ns,
|
|
uint64_t *out_predicted_display_period_ns)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
/*
|
|
* We use this to signal that the session has started, this is needed
|
|
* to make this client/session active/visible/focused.
|
|
*/
|
|
ipc_server_activate_session(ics);
|
|
|
|
uint64_t gpu_time_ns = 0;
|
|
return xrt_comp_predict_frame( //
|
|
ics->xc, //
|
|
out_frame_id, //
|
|
out_wake_up_time_ns, //
|
|
&gpu_time_ns, //
|
|
out_predicted_display_time_ns, //
|
|
out_predicted_display_period_ns); //
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_wait_woke(volatile struct ipc_client_state *ics, int64_t frame_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_mark_frame(ics->xc, frame_id, XRT_COMPOSITOR_FRAME_POINT_WOKE, os_monotonic_get_ns());
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_begin_frame(volatile struct ipc_client_state *ics, int64_t frame_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_begin_frame(ics->xc, frame_id);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_discard_frame(volatile struct ipc_client_state *ics, int64_t frame_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_discard_frame(ics->xc, frame_id);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_get_display_refresh_rate(volatile struct ipc_client_state *ics,
|
|
float *out_display_refresh_rate_hz)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_get_display_refresh_rate(ics->xc, out_display_refresh_rate_hz);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_request_display_refresh_rate(volatile struct ipc_client_state *ics, float display_refresh_rate_hz)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_request_display_refresh_rate(ics->xc, display_refresh_rate_hz);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_set_performance_level(volatile struct ipc_client_state *ics,
|
|
enum xrt_perf_domain domain,
|
|
enum xrt_perf_set_level level)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_COMPOSITOR_NOT_CREATED;
|
|
}
|
|
|
|
if (ics->xc->set_performance_level == NULL) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
return xrt_comp_set_performance_level(ics->xc, domain, level);
|
|
}
|
|
|
|
static bool
|
|
_update_projection_layer(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
// xdev
|
|
uint32_t device_id = layer->xdev_id;
|
|
// left
|
|
uint32_t lxsci = layer->swapchain_ids[0];
|
|
// right
|
|
uint32_t rxsci = layer->swapchain_ids[1];
|
|
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
struct xrt_swapchain *lxcs = ics->xscs[lxsci];
|
|
struct xrt_swapchain *rxcs = ics->xscs[rxsci];
|
|
|
|
if (lxcs == NULL || rxcs == NULL) {
|
|
U_LOG_E("Invalid swap chain for projection layer!");
|
|
return false;
|
|
}
|
|
|
|
if (xdev == NULL) {
|
|
U_LOG_E("Invalid xdev for projection layer!");
|
|
return false;
|
|
}
|
|
|
|
// Cast away volatile.
|
|
struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
|
|
|
|
xrt_comp_layer_stereo_projection(xc, xdev, lxcs, rxcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_projection_layer_depth(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
// xdev
|
|
uint32_t xdevi = layer->xdev_id;
|
|
// left
|
|
uint32_t l_xsci = layer->swapchain_ids[0];
|
|
// right
|
|
uint32_t r_xsci = layer->swapchain_ids[1];
|
|
// left
|
|
uint32_t l_d_xsci = layer->swapchain_ids[2];
|
|
// right
|
|
uint32_t r_d_xsci = layer->swapchain_ids[3];
|
|
|
|
struct xrt_device *xdev = get_xdev(ics, xdevi);
|
|
struct xrt_swapchain *l_xcs = ics->xscs[l_xsci];
|
|
struct xrt_swapchain *r_xcs = ics->xscs[r_xsci];
|
|
struct xrt_swapchain *l_d_xcs = ics->xscs[l_d_xsci];
|
|
struct xrt_swapchain *r_d_xcs = ics->xscs[r_d_xsci];
|
|
|
|
if (l_xcs == NULL || r_xcs == NULL || l_d_xcs == NULL || r_d_xcs == NULL) {
|
|
U_LOG_E("Invalid swap chain for projection layer #%u!", i);
|
|
return false;
|
|
}
|
|
|
|
if (xdev == NULL) {
|
|
U_LOG_E("Invalid xdev for projection layer #%u!", i);
|
|
return false;
|
|
}
|
|
|
|
// Cast away volatile.
|
|
struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
|
|
|
|
xrt_comp_layer_stereo_projection_depth(xc, xdev, l_xcs, r_xcs, l_d_xcs, r_d_xcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
do_single(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i,
|
|
const char *name,
|
|
struct xrt_device **out_xdev,
|
|
struct xrt_swapchain **out_xcs,
|
|
struct xrt_layer_data **out_data)
|
|
{
|
|
uint32_t device_id = layer->xdev_id;
|
|
uint32_t sci = layer->swapchain_ids[0];
|
|
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
struct xrt_swapchain *xcs = ics->xscs[sci];
|
|
|
|
if (xcs == NULL) {
|
|
U_LOG_E("Invalid swapchain for layer #%u, '%s'!", i, name);
|
|
return false;
|
|
}
|
|
|
|
if (xdev == NULL) {
|
|
U_LOG_E("Invalid xdev for layer #%u, '%s'!", i, name);
|
|
return false;
|
|
}
|
|
|
|
// Cast away volatile.
|
|
struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
|
|
|
|
*out_xdev = xdev;
|
|
*out_xcs = xcs;
|
|
*out_data = data;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_quad_layer(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
struct xrt_device *xdev;
|
|
struct xrt_swapchain *xcs;
|
|
struct xrt_layer_data *data;
|
|
|
|
if (!do_single(xc, ics, layer, i, "quad", &xdev, &xcs, &data)) {
|
|
return false;
|
|
}
|
|
|
|
xrt_comp_layer_quad(xc, xdev, xcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_cube_layer(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
struct xrt_device *xdev;
|
|
struct xrt_swapchain *xcs;
|
|
struct xrt_layer_data *data;
|
|
|
|
if (!do_single(xc, ics, layer, i, "cube", &xdev, &xcs, &data)) {
|
|
return false;
|
|
}
|
|
|
|
xrt_comp_layer_cube(xc, xdev, xcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_cylinder_layer(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
struct xrt_device *xdev;
|
|
struct xrt_swapchain *xcs;
|
|
struct xrt_layer_data *data;
|
|
|
|
if (!do_single(xc, ics, layer, i, "cylinder", &xdev, &xcs, &data)) {
|
|
return false;
|
|
}
|
|
|
|
xrt_comp_layer_cylinder(xc, xdev, xcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_equirect1_layer(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
struct xrt_device *xdev;
|
|
struct xrt_swapchain *xcs;
|
|
struct xrt_layer_data *data;
|
|
|
|
if (!do_single(xc, ics, layer, i, "equirect1", &xdev, &xcs, &data)) {
|
|
return false;
|
|
}
|
|
|
|
xrt_comp_layer_equirect1(xc, xdev, xcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_equirect2_layer(struct xrt_compositor *xc,
|
|
volatile struct ipc_client_state *ics,
|
|
volatile struct ipc_layer_entry *layer,
|
|
uint32_t i)
|
|
{
|
|
struct xrt_device *xdev;
|
|
struct xrt_swapchain *xcs;
|
|
struct xrt_layer_data *data;
|
|
|
|
if (!do_single(xc, ics, layer, i, "equirect2", &xdev, &xcs, &data)) {
|
|
return false;
|
|
}
|
|
|
|
xrt_comp_layer_equirect2(xc, xdev, xcs, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_update_layers(volatile struct ipc_client_state *ics, struct xrt_compositor *xc, struct ipc_layer_slot *slot)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
for (uint32_t i = 0; i < slot->layer_count; i++) {
|
|
volatile struct ipc_layer_entry *layer = &slot->layers[i];
|
|
|
|
switch (layer->data.type) {
|
|
case XRT_LAYER_STEREO_PROJECTION:
|
|
if (!_update_projection_layer(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case XRT_LAYER_STEREO_PROJECTION_DEPTH:
|
|
if (!_update_projection_layer_depth(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case XRT_LAYER_QUAD:
|
|
if (!_update_quad_layer(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case XRT_LAYER_CUBE:
|
|
if (!_update_cube_layer(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case XRT_LAYER_CYLINDER:
|
|
if (!_update_cylinder_layer(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case XRT_LAYER_EQUIRECT1:
|
|
if (!_update_equirect1_layer(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case XRT_LAYER_EQUIRECT2:
|
|
if (!_update_equirect2_layer(xc, ics, layer, i)) {
|
|
return false;
|
|
}
|
|
break;
|
|
default: U_LOG_E("Unhandled layer type '%i'!", layer->data.type); break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *ics,
|
|
uint32_t slot_id,
|
|
uint32_t *out_free_slot_id,
|
|
const xrt_graphics_sync_handle_t *handles,
|
|
const uint32_t handle_count)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
struct ipc_shared_memory *ism = ics->server->ism;
|
|
struct ipc_layer_slot *slot = &ism->slots[slot_id];
|
|
xrt_graphics_sync_handle_t sync_handle = XRT_GRAPHICS_SYNC_HANDLE_INVALID;
|
|
|
|
// If we have one or more save the first handle.
|
|
if (handle_count >= 1) {
|
|
sync_handle = handles[0];
|
|
}
|
|
|
|
// Free all sync handles after the first one.
|
|
for (uint32_t i = 1; i < handle_count; i++) {
|
|
// Checks for valid handle.
|
|
xrt_graphics_sync_handle_t tmp = handles[i];
|
|
u_graphics_sync_unref(&tmp);
|
|
}
|
|
|
|
// Copy current slot data.
|
|
struct ipc_layer_slot copy = *slot;
|
|
|
|
|
|
/*
|
|
* Transfer data to underlying compositor.
|
|
*/
|
|
|
|
xrt_comp_layer_begin(ics->xc, ©.data);
|
|
|
|
_update_layers(ics, ics->xc, ©);
|
|
|
|
xrt_comp_layer_commit(ics->xc, sync_handle);
|
|
|
|
|
|
/*
|
|
* Manage shared state.
|
|
*/
|
|
|
|
os_mutex_lock(&ics->server->global_state.lock);
|
|
|
|
*out_free_slot_id = (ics->server->current_slot_index + 1) % IPC_MAX_SLOTS;
|
|
ics->server->current_slot_index = *out_free_slot_id;
|
|
|
|
os_mutex_unlock(&ics->server->global_state.lock);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_layer_sync_with_semaphore(volatile struct ipc_client_state *ics,
|
|
uint32_t slot_id,
|
|
uint32_t semaphore_id,
|
|
uint64_t semaphore_value,
|
|
uint32_t *out_free_slot_id)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
if (semaphore_id >= IPC_MAX_CLIENT_SEMAPHORES) {
|
|
IPC_ERROR(ics->server, "Invalid semaphore_id");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
if (ics->xcsems[semaphore_id] == NULL) {
|
|
IPC_ERROR(ics->server, "Semaphore of id %u not created!", semaphore_id);
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
struct xrt_compositor_semaphore *xcsem = ics->xcsems[semaphore_id];
|
|
|
|
struct ipc_shared_memory *ism = ics->server->ism;
|
|
struct ipc_layer_slot *slot = &ism->slots[slot_id];
|
|
|
|
// Copy current slot data.
|
|
struct ipc_layer_slot copy = *slot;
|
|
|
|
|
|
|
|
/*
|
|
* Transfer data to underlying compositor.
|
|
*/
|
|
|
|
xrt_comp_layer_begin(ics->xc, ©.data);
|
|
|
|
_update_layers(ics, ics->xc, ©);
|
|
|
|
xrt_comp_layer_commit_with_semaphore(ics->xc, xcsem, semaphore_value);
|
|
|
|
|
|
/*
|
|
* Manage shared state.
|
|
*/
|
|
|
|
os_mutex_lock(&ics->server->global_state.lock);
|
|
|
|
*out_free_slot_id = (ics->server->current_slot_index + 1) % IPC_MAX_SLOTS;
|
|
ics->server->current_slot_index = *out_free_slot_id;
|
|
|
|
os_mutex_unlock(&ics->server->global_state.lock);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_set_thread_hint(volatile struct ipc_client_state *ics,
|
|
enum xrt_thread_hint hint,
|
|
uint32_t thread_id)
|
|
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_set_thread_hint(ics->xc, hint, thread_id);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_get_clients(volatile struct ipc_client_state *_ics, struct ipc_client_list *list)
|
|
{
|
|
struct ipc_server *s = _ics->server;
|
|
|
|
// Look client list.
|
|
os_mutex_lock(&s->global_state.lock);
|
|
|
|
uint32_t count = 0;
|
|
for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
|
|
|
|
volatile struct ipc_client_state *ics = &s->threads[i].ics;
|
|
|
|
// Is this thread running?
|
|
if (ics->server_thread_index < 0) {
|
|
continue;
|
|
}
|
|
|
|
list->ids[count++] = ics->client_state.id;
|
|
}
|
|
|
|
list->id_count = count;
|
|
|
|
// Unlock now.
|
|
os_mutex_unlock(&s->global_state.lock);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_get_properties(volatile struct ipc_client_state *_ics, struct xrt_system_properties *out_properties)
|
|
{
|
|
struct ipc_server *s = _ics->server;
|
|
|
|
return ipc_server_get_system_properties(s, out_properties);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_get_client_info(volatile struct ipc_client_state *_ics,
|
|
uint32_t client_id,
|
|
struct ipc_app_state *out_ias)
|
|
{
|
|
struct ipc_server *s = _ics->server;
|
|
|
|
return ipc_server_get_client_app_state(s, client_id, out_ias);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_set_primary_client(volatile struct ipc_client_state *_ics, uint32_t client_id)
|
|
{
|
|
struct ipc_server *s = _ics->server;
|
|
|
|
IPC_INFO(s, "System setting active client to %d.", client_id);
|
|
|
|
return ipc_server_set_active_client(s, client_id);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_set_focused_client(volatile struct ipc_client_state *ics, uint32_t client_id)
|
|
{
|
|
IPC_INFO(ics->server, "UNIMPLEMENTED: system setting focused client to %d.", client_id);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_toggle_io_client(volatile struct ipc_client_state *_ics, uint32_t client_id)
|
|
{
|
|
struct ipc_server *s = _ics->server;
|
|
|
|
IPC_INFO(s, "System toggling io for client %u.", client_id);
|
|
|
|
return ipc_server_toggle_io_client(s, client_id);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_toggle_io_device(volatile struct ipc_client_state *ics, uint32_t device_id)
|
|
{
|
|
if (device_id >= IPC_MAX_DEVICES) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
struct ipc_device *idev = &ics->server->idevs[device_id];
|
|
|
|
idev->io_active = !idev->io_active;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_get_properties(volatile struct ipc_client_state *ics,
|
|
const struct xrt_swapchain_create_info *info,
|
|
struct xrt_swapchain_create_properties *xsccp)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
return xrt_comp_get_swapchain_create_properties(ics->xc, info, xsccp);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_create(volatile struct ipc_client_state *ics,
|
|
const struct xrt_swapchain_create_info *info,
|
|
uint32_t *out_id,
|
|
uint32_t *out_image_count,
|
|
uint64_t *out_size,
|
|
bool *out_use_dedicated_allocation,
|
|
uint32_t max_handle_capacity,
|
|
xrt_graphics_buffer_handle_t *out_handles,
|
|
uint32_t *out_handle_count)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
xrt_result_t xret = XRT_SUCCESS;
|
|
uint32_t index = 0;
|
|
|
|
xret = validate_swapchain_state(ics, &index);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
// Create the swapchain
|
|
struct xrt_swapchain *xsc = NULL; // Has to be NULL.
|
|
xret = xrt_comp_create_swapchain(ics->xc, info, &xsc);
|
|
if (xret != XRT_SUCCESS) {
|
|
if (xret == XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED) {
|
|
IPC_WARN(ics->server,
|
|
"xrt_comp_create_swapchain: Attempted to create valid, but unsupported swapchain");
|
|
} else {
|
|
IPC_ERROR(ics->server, "Error xrt_comp_create_swapchain failed!");
|
|
}
|
|
return xret;
|
|
}
|
|
|
|
// It's now safe to increment the number of swapchains.
|
|
ics->swapchain_count++;
|
|
|
|
IPC_TRACE(ics->server, "Created swapchain %d.", index);
|
|
|
|
set_swapchain_info(ics, index, info, xsc);
|
|
|
|
// return our result to the caller.
|
|
struct xrt_swapchain_native *xscn = (struct xrt_swapchain_native *)xsc;
|
|
|
|
// Limit checking
|
|
assert(xsc->image_count <= XRT_MAX_SWAPCHAIN_IMAGES);
|
|
assert(xsc->image_count <= max_handle_capacity);
|
|
|
|
for (size_t i = 1; i < xsc->image_count; i++) {
|
|
assert(xscn->images[0].size == xscn->images[i].size);
|
|
assert(xscn->images[0].use_dedicated_allocation == xscn->images[i].use_dedicated_allocation);
|
|
}
|
|
|
|
// Assuming all images allocated in the same swapchain have the same allocation requirements.
|
|
*out_size = xscn->images[0].size;
|
|
*out_use_dedicated_allocation = xscn->images[0].use_dedicated_allocation;
|
|
*out_id = index;
|
|
*out_image_count = xsc->image_count;
|
|
|
|
// Setup the fds.
|
|
*out_handle_count = xsc->image_count;
|
|
for (size_t i = 0; i < xsc->image_count; i++) {
|
|
out_handles[i] = xscn->images[i].handle;
|
|
}
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_import(volatile struct ipc_client_state *ics,
|
|
const struct xrt_swapchain_create_info *info,
|
|
const struct ipc_arg_swapchain_from_native *args,
|
|
uint32_t *out_id,
|
|
const xrt_graphics_buffer_handle_t *handles,
|
|
uint32_t handle_count)
|
|
{
|
|
IPC_TRACE_MARKER();
|
|
|
|
xrt_result_t xret = XRT_SUCCESS;
|
|
uint32_t index = 0;
|
|
|
|
xret = validate_swapchain_state(ics, &index);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
struct xrt_image_native xins[XRT_MAX_SWAPCHAIN_IMAGES] = XRT_STRUCT_INIT;
|
|
for (uint32_t i = 0; i < handle_count; i++) {
|
|
xins[i].handle = handles[i];
|
|
xins[i].size = args->sizes[i];
|
|
#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_WIN32_HANDLE)
|
|
// DXGI handles need to be dealt with differently, they are identified
|
|
// by having their lower bit set to 1 during transfer
|
|
if ((size_t)xins[i].handle & 1) {
|
|
xins[i].handle = (HANDLE)((size_t)xins[i].handle - 1);
|
|
xins[i].is_dxgi_handle = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// create the swapchain
|
|
struct xrt_swapchain *xsc = NULL;
|
|
xret = xrt_comp_import_swapchain(ics->xc, info, xins, handle_count, &xsc);
|
|
if (xret != XRT_SUCCESS) {
|
|
return xret;
|
|
}
|
|
|
|
// It's now safe to increment the number of swapchains.
|
|
ics->swapchain_count++;
|
|
|
|
IPC_TRACE(ics->server, "Created swapchain %d.", index);
|
|
|
|
set_swapchain_info(ics, index, info, xsc);
|
|
*out_id = index;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_wait_image(volatile struct ipc_client_state *ics, uint32_t id, uint64_t timeout_ns, uint32_t index)
|
|
{
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
//! @todo Look up the index.
|
|
uint32_t sc_index = id;
|
|
struct xrt_swapchain *xsc = ics->xscs[sc_index];
|
|
|
|
return xrt_swapchain_wait_image(xsc, timeout_ns, index);
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_acquire_image(volatile struct ipc_client_state *ics, uint32_t id, uint32_t *out_index)
|
|
{
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
//! @todo Look up the index.
|
|
uint32_t sc_index = id;
|
|
struct xrt_swapchain *xsc = ics->xscs[sc_index];
|
|
|
|
xrt_swapchain_acquire_image(xsc, out_index);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_release_image(volatile struct ipc_client_state *ics, uint32_t id, uint32_t index)
|
|
{
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
//! @todo Look up the index.
|
|
uint32_t sc_index = id;
|
|
struct xrt_swapchain *xsc = ics->xscs[sc_index];
|
|
|
|
xrt_swapchain_release_image(xsc, index);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_swapchain_destroy(volatile struct ipc_client_state *ics, uint32_t id)
|
|
{
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
ics->swapchain_count--;
|
|
|
|
// Drop our reference, does NULL checking. Cast away volatile.
|
|
xrt_swapchain_reference((struct xrt_swapchain **)&ics->xscs[id], NULL);
|
|
ics->swapchain_data[id].active = false;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Compositor semaphore function..
|
|
*
|
|
*/
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_semaphore_create(volatile struct ipc_client_state *ics,
|
|
uint32_t *out_id,
|
|
uint32_t max_handle_count,
|
|
xrt_graphics_sync_handle_t *out_handles,
|
|
uint32_t *out_handle_count)
|
|
{
|
|
xrt_result_t xret;
|
|
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
int id = 0;
|
|
for (; id < IPC_MAX_CLIENT_SEMAPHORES; id++) {
|
|
if (ics->xcsems[id] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (id == IPC_MAX_CLIENT_SEMAPHORES) {
|
|
IPC_ERROR(ics->server, "Too many compositor semaphores alive!");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
struct xrt_compositor_semaphore *xcsem = NULL;
|
|
xrt_graphics_sync_handle_t handle = XRT_GRAPHICS_SYNC_HANDLE_INVALID;
|
|
|
|
xret = xrt_comp_create_semaphore(ics->xc, &handle, &xcsem);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(ics->server, "Failed to create compositor semaphore!");
|
|
return xret;
|
|
}
|
|
|
|
// Set it directly, no need to use reference here.
|
|
ics->xcsems[id] = xcsem;
|
|
|
|
// Set out parameters.
|
|
*out_id = id;
|
|
out_handles[0] = handle;
|
|
*out_handle_count = 1;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_compositor_semaphore_destroy(volatile struct ipc_client_state *ics, uint32_t id)
|
|
{
|
|
if (ics->xc == NULL) {
|
|
return XRT_ERROR_IPC_SESSION_NOT_CREATED;
|
|
}
|
|
|
|
if (ics->xcsems[id] == NULL) {
|
|
IPC_ERROR(ics->server, "Client tried to delete non-existent compositor semaphore!");
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
ics->compositor_semaphore_count--;
|
|
|
|
// Drop our reference, does NULL checking. Cast away volatile.
|
|
xrt_compositor_semaphore_reference((struct xrt_compositor_semaphore **)&ics->xcsems[id], NULL);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Device functions.
|
|
*
|
|
*/
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_update_input(volatile struct ipc_client_state *ics, uint32_t id)
|
|
{
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct ipc_shared_memory *ism = ics->server->ism;
|
|
struct ipc_device *idev = get_idev(ics, device_id);
|
|
struct xrt_device *xdev = idev->xdev;
|
|
struct ipc_shared_device *isdev = &ism->isdevs[device_id];
|
|
|
|
// Update inputs.
|
|
xrt_device_update_inputs(xdev);
|
|
|
|
// Copy data into the shared memory.
|
|
struct xrt_input *src = xdev->inputs;
|
|
struct xrt_input *dst = &ism->inputs[isdev->first_input_index];
|
|
size_t size = sizeof(struct xrt_input) * isdev->input_count;
|
|
|
|
bool io_active = ics->io_active && idev->io_active;
|
|
if (io_active) {
|
|
memcpy(dst, src, size);
|
|
} else {
|
|
memset(dst, 0, size);
|
|
|
|
for (uint32_t i = 0; i < isdev->input_count; i++) {
|
|
dst[i].name = src[i].name;
|
|
|
|
// Special case the rotation of the head.
|
|
if (dst[i].name == XRT_INPUT_GENERIC_HEAD_POSE) {
|
|
dst[i].active = src[i].active;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reply.
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
static struct xrt_input *
|
|
find_input(volatile struct ipc_client_state *ics, uint32_t device_id, enum xrt_input_name name)
|
|
{
|
|
struct ipc_shared_memory *ism = ics->server->ism;
|
|
struct ipc_shared_device *isdev = &ism->isdevs[device_id];
|
|
struct xrt_input *io = &ism->inputs[isdev->first_input_index];
|
|
|
|
for (uint32_t i = 0; i < isdev->input_count; i++) {
|
|
if (io[i].name == name) {
|
|
return &io[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_get_tracked_pose(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
enum xrt_input_name name,
|
|
uint64_t at_timestamp,
|
|
struct xrt_space_relation *out_relation)
|
|
{
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct ipc_device *isdev = &ics->server->idevs[device_id];
|
|
struct xrt_device *xdev = isdev->xdev;
|
|
|
|
// Find the input
|
|
struct xrt_input *input = find_input(ics, device_id, name);
|
|
if (input == NULL) {
|
|
return XRT_ERROR_IPC_FAILURE;
|
|
}
|
|
|
|
// Special case the headpose.
|
|
bool disabled = (!isdev->io_active || !ics->io_active) && name != XRT_INPUT_GENERIC_HEAD_POSE;
|
|
bool active_on_client = input->active;
|
|
|
|
// We have been disabled but the client hasn't called update.
|
|
if (disabled && active_on_client) {
|
|
U_ZERO(out_relation);
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
if (disabled || !active_on_client) {
|
|
return XRT_ERROR_POSE_NOT_ACTIVE;
|
|
}
|
|
|
|
// Get the pose.
|
|
xrt_device_get_tracked_pose(xdev, name, at_timestamp, out_relation);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_get_hand_tracking(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
enum xrt_input_name name,
|
|
uint64_t at_timestamp,
|
|
struct xrt_hand_joint_set *out_value,
|
|
uint64_t *out_timestamp)
|
|
{
|
|
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
|
|
// Get the pose.
|
|
xrt_device_get_hand_tracking(xdev, name, at_timestamp, out_value, out_timestamp);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_get_view_poses(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
const struct xrt_vec3 *fallback_eye_relation,
|
|
uint64_t at_timestamp_ns,
|
|
uint32_t view_count)
|
|
{
|
|
struct ipc_message_channel *imc = (struct ipc_message_channel *)&ics->imc;
|
|
struct ipc_device_get_view_poses_reply reply = XRT_STRUCT_INIT;
|
|
struct ipc_server *s = ics->server;
|
|
xrt_result_t xret;
|
|
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
|
|
|
|
if (view_count == 0 || view_count > IPC_MAX_RAW_VIEWS) {
|
|
IPC_ERROR(s, "Client asked for zero or too many views! (%u)", view_count);
|
|
|
|
reply.result = XRT_ERROR_IPC_FAILURE;
|
|
// Send the full reply, the client expects it.
|
|
return ipc_send(imc, &reply, sizeof(reply));
|
|
}
|
|
|
|
// Data to get.
|
|
struct xrt_fov fovs[IPC_MAX_RAW_VIEWS];
|
|
struct xrt_pose poses[IPC_MAX_RAW_VIEWS];
|
|
|
|
xrt_device_get_view_poses( //
|
|
xdev, //
|
|
fallback_eye_relation, //
|
|
at_timestamp_ns, //
|
|
view_count, //
|
|
&reply.head_relation, //
|
|
fovs, //
|
|
poses); //
|
|
|
|
/*
|
|
* Operation ok, head_relation has already been put in the reply
|
|
* struct, so we don't need to send that manually.
|
|
*/
|
|
reply.result = XRT_SUCCESS;
|
|
|
|
/*
|
|
* This isn't really needed, but demonstrates the server sending the
|
|
* length back in the reply, a common pattern for other functions.
|
|
*/
|
|
reply.view_count = view_count;
|
|
|
|
/*
|
|
* Send the reply first isn't required for functions in general, but it
|
|
* will need to match what the client expects. This demonstrates the
|
|
* server sending the length back in the reply, a common pattern for
|
|
* other functions.
|
|
*/
|
|
xret = ipc_send(imc, &reply, sizeof(reply));
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(s, "Failed to send reply!");
|
|
return xret;
|
|
}
|
|
|
|
// Send the fovs that we got.
|
|
xret = ipc_send(imc, fovs, sizeof(struct xrt_fov) * view_count);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(s, "Failed to send fovs!");
|
|
return xret;
|
|
}
|
|
|
|
// And finally the poses.
|
|
xret = ipc_send(imc, poses, sizeof(struct xrt_pose) * view_count);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(s, "Failed to send poses!");
|
|
return xret;
|
|
}
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_get_view_poses_2(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
const struct xrt_vec3 *default_eye_relation,
|
|
uint64_t at_timestamp_ns,
|
|
struct ipc_info_get_view_poses_2 *out_info)
|
|
{
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
|
|
xrt_device_get_view_poses( //
|
|
xdev, //
|
|
default_eye_relation, //
|
|
at_timestamp_ns, //
|
|
2, //
|
|
&out_info->head_relation, //
|
|
out_info->fovs, //
|
|
out_info->poses); //
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_compute_distortion(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
uint32_t view,
|
|
float u,
|
|
float v,
|
|
bool *out_ret,
|
|
struct xrt_uv_triplet *out_triplet)
|
|
{
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
|
|
bool ret = xrt_device_compute_distortion(xdev, view, u, v, out_triplet);
|
|
*out_ret = ret;
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_set_output(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
enum xrt_output_name name,
|
|
const union xrt_output_value *value)
|
|
{
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
|
|
// Set the output.
|
|
xrt_device_set_output(xdev, name, value);
|
|
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_get_visibility_mask(volatile struct ipc_client_state *ics,
|
|
uint32_t device_id,
|
|
enum xrt_visibility_mask_type type,
|
|
uint32_t view_index)
|
|
{
|
|
struct ipc_message_channel *imc = (struct ipc_message_channel *)&ics->imc;
|
|
struct ipc_device_get_visibility_mask_reply reply = XRT_STRUCT_INIT;
|
|
struct ipc_server *s = ics->server;
|
|
xrt_result_t xret;
|
|
|
|
// @todo verify
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
struct xrt_visibility_mask *mask = NULL;
|
|
if (xdev->get_visibility_mask) {
|
|
xret = xrt_device_get_visibility_mask(xdev, type, view_index, &mask);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(s, "Failed to get visibility mask");
|
|
return xret;
|
|
}
|
|
} else {
|
|
struct xrt_fov fov = xdev->hmd->distortion.fov[view_index];
|
|
u_visibility_mask_get_default(type, &fov, &mask);
|
|
}
|
|
|
|
if (mask == NULL) {
|
|
IPC_ERROR(s, "Failed to get visibility mask");
|
|
reply.mask_size = 0;
|
|
} else {
|
|
reply.mask_size = xrt_visibility_mask_get_size(mask);
|
|
}
|
|
|
|
xret = ipc_send(imc, &reply, sizeof(reply));
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(s, "Failed to send reply");
|
|
goto out_free;
|
|
}
|
|
|
|
xret = ipc_send(imc, mask, reply.mask_size);
|
|
if (xret != XRT_SUCCESS) {
|
|
IPC_ERROR(s, "Failed to send mask");
|
|
goto out_free;
|
|
}
|
|
|
|
out_free:
|
|
free(mask);
|
|
return xret;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_device_is_form_factor_available(volatile struct ipc_client_state *ics,
|
|
uint32_t id,
|
|
enum xrt_form_factor form_factor,
|
|
bool *out_available)
|
|
{
|
|
// To make the code a bit more readable.
|
|
uint32_t device_id = id;
|
|
struct xrt_device *xdev = get_xdev(ics, device_id);
|
|
*out_available = xrt_device_is_form_factor_available(xdev, form_factor);
|
|
return XRT_SUCCESS;
|
|
}
|
|
|
|
xrt_result_t
|
|
ipc_handle_system_devices_get_roles(volatile struct ipc_client_state *ics, struct xrt_system_roles *out_roles)
|
|
{
|
|
return xrt_system_devices_get_roles(ics->server->xsysd, out_roles);
|
|
}
|