diff --git a/src/xrt/ipc/CMakeLists.txt b/src/xrt/ipc/CMakeLists.txt index 43aff14ee..f4098872b 100644 --- a/src/xrt/ipc/CMakeLists.txt +++ b/src/xrt/ipc/CMakeLists.txt @@ -62,6 +62,7 @@ add_library( client/ipc_client_device.c client/ipc_client_hmd.c client/ipc_client_instance.c + client/ipc_client_space_overseer.c ) target_include_directories( ipc_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/xrt/ipc/client/ipc_client.h b/src/xrt/ipc/client/ipc_client.h index 5129ac17f..6f2922ea5 100644 --- a/src/xrt/ipc/client/ipc_client.h +++ b/src/xrt/ipc/client/ipc_client.h @@ -115,3 +115,6 @@ ipc_client_hmd_create(struct ipc_connection *ipc_c, struct xrt_tracking_origin * struct xrt_device * ipc_client_device_create(struct ipc_connection *ipc_c, struct xrt_tracking_origin *xtrack, uint32_t device_id); + +struct xrt_space_overseer * +ipc_client_space_overseer_create(struct ipc_connection *ipc_c); diff --git a/src/xrt/ipc/client/ipc_client_space_overseer.c b/src/xrt/ipc/client/ipc_client_space_overseer.c new file mode 100644 index 000000000..390bab334 --- /dev/null +++ b/src/xrt/ipc/client/ipc_client_space_overseer.c @@ -0,0 +1,231 @@ +// Copyright 2023, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief IPC Client space overseer. + * @author Jakob Bornecrantz + * @ingroup ipc_client + */ + +#include "xrt/xrt_space.h" + +#include "ipc_client_generated.h" + + +struct ipc_client_space +{ + struct xrt_space base; + + struct ipc_connection *ipc_c; + + uint32_t id; +}; + +struct ipc_client_space_overseer +{ + struct xrt_space_overseer base; + + struct ipc_connection *ipc_c; +}; + + +/* + * + * Helpers + * + */ + +static inline struct ipc_client_space * +ipc_client_space(struct xrt_space *xs) +{ + return (struct ipc_client_space *)xs; +} + +static inline struct ipc_client_space_overseer * +ipc_client_space_overseer(struct xrt_space_overseer *xso) +{ + return (struct ipc_client_space_overseer *)xso; +} + + +/* + * + * Space member functions. + * + */ + +static void +space_destroy(struct xrt_space *xs) +{ + struct ipc_client_space *icsp = ipc_client_space(xs); + + ipc_call_space_destroy(icsp->ipc_c, icsp->id); + + free(xs); +} + +static void +alloc_space_with_id(struct ipc_client_space_overseer *icspo, uint32_t id, struct xrt_space **out_space) +{ + struct ipc_client_space *icsp = U_TYPED_CALLOC(struct ipc_client_space); + icsp->base.reference.count = 1; + icsp->base.destroy = space_destroy; + icsp->ipc_c = icspo->ipc_c; + icsp->id = id; + + *out_space = &icsp->base; +} + + +/* + * + * Overseer member functions. + * + */ + +static xrt_result_t +create_offset_space(struct xrt_space_overseer *xso, + struct xrt_space *parent, + const struct xrt_pose *offset, + struct xrt_space **out_space) +{ + struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); + xrt_result_t xret; + uint32_t parent_id = ipc_client_space(parent)->id; + uint32_t id = 0; + + xret = ipc_call_space_create_offset(icspo->ipc_c, parent_id, offset, &id); + if (xret != XRT_SUCCESS) { + return xret; + } + + alloc_space_with_id(icspo, id, out_space); + + return XRT_SUCCESS; +} + +static xrt_result_t +create_pose_space(struct xrt_space_overseer *xso, + struct xrt_device *xdev, + enum xrt_input_name name, + struct xrt_space **out_space) +{ + struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); + xrt_result_t xret; + uint32_t xdev_id = ipc_client_xdev(xdev)->device_id; + uint32_t id = 0; + + xret = ipc_call_space_create_pose(icspo->ipc_c, xdev_id, name, &id); + if (xret != XRT_SUCCESS) { + return xret; + } + + alloc_space_with_id(icspo, id, out_space); + + return XRT_SUCCESS; +} + +static xrt_result_t +locate_space(struct xrt_space_overseer *xso, + struct xrt_space *base_space, + const struct xrt_pose *base_offset, + uint64_t at_timestamp_ns, + struct xrt_space *space, + const struct xrt_pose *offset, + struct xrt_space_relation *out_relation) +{ + struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); + + struct ipc_client_space *icsp_base_space = ipc_client_space(base_space); + struct ipc_client_space *icsp_space = ipc_client_space(space); + + return ipc_call_space_locate_space( // + icspo->ipc_c, // + icsp_base_space->id, // + base_offset, // + at_timestamp_ns, // + icsp_space->id, // + offset, // + out_relation); // +} + +static xrt_result_t +locate_device(struct xrt_space_overseer *xso, + struct xrt_space *base_space, + const struct xrt_pose *base_offset, + uint64_t at_timestamp_ns, + struct xrt_device *xdev, + struct xrt_space_relation *out_relation) +{ + struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); + + struct ipc_client_space *icsp_base_space = ipc_client_space(base_space); + uint32_t xdev_id = ipc_client_xdev(xdev)->device_id; + + return ipc_call_space_locate_device( // + icspo->ipc_c, // + icsp_base_space->id, // + base_offset, // + at_timestamp_ns, // + xdev_id, // + out_relation); // +} + +static void +destroy(struct xrt_space_overseer *xso) +{ + struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); + + xrt_space_reference(&icspo->base.semantic.root, NULL); + xrt_space_reference(&icspo->base.semantic.view, NULL); + xrt_space_reference(&icspo->base.semantic.local, NULL); + xrt_space_reference(&icspo->base.semantic.stage, NULL); + xrt_space_reference(&icspo->base.semantic.unbounded, NULL); + + free(icspo); +} + + +/* + * + * 'Exported' functions. + * + */ + +struct xrt_space_overseer * +ipc_client_space_overseer_create(struct ipc_connection *ipc_c) +{ + struct ipc_client_space_overseer *icspo = U_TYPED_CALLOC(struct ipc_client_space_overseer); + icspo->base.create_offset_space = create_offset_space; + icspo->base.create_pose_space = create_pose_space; + icspo->base.locate_space = locate_space; + icspo->base.locate_device = locate_device; + icspo->base.destroy = destroy; + icspo->ipc_c = ipc_c; + + uint32_t root_id = UINT32_MAX; + uint32_t view_id = UINT32_MAX; + uint32_t local_id = UINT32_MAX; + uint32_t stage_id = UINT32_MAX; + uint32_t unbounded_id = UINT32_MAX; + + ipc_call_space_create_semantic_ids(icspo->ipc_c, &root_id, &view_id, &local_id, &stage_id, &unbounded_id); + +#define CREATE(NAME) \ + do { \ + if (NAME##_id == UINT32_MAX) { \ + break; \ + } \ + alloc_space_with_id(icspo, NAME##_id, &icspo->base.semantic.NAME); \ + } while (false) + + CREATE(root); + CREATE(view); + CREATE(local); + CREATE(stage); + CREATE(unbounded); + +#undef CREATE + + return &icspo->base; +} diff --git a/src/xrt/ipc/server/ipc_server.h b/src/xrt/ipc/server/ipc_server.h index b84c9050f..4e61295af 100644 --- a/src/xrt/ipc/server/ipc_server.h +++ b/src/xrt/ipc/server/ipc_server.h @@ -1,4 +1,4 @@ -// Copyright 2020-2021, Collabora, Ltd. +// Copyright 2020-2023, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -13,6 +13,7 @@ #include "xrt/xrt_compiler.h" #include "xrt/xrt_system.h" +#include "xrt/xrt_space.h" #include "util/u_logging.h" @@ -47,6 +48,7 @@ extern "C" { #define IPC_MAX_CLIENT_SEMAPHORES 8 #define IPC_MAX_CLIENT_SWAPCHAINS 32 +#define IPC_MAX_CLIENT_SPACES 128 //#define IPC_MAX_CLIENTS 8 struct xrt_instance; @@ -100,6 +102,20 @@ struct ipc_client_state //! Ptrs to the semaphores. struct xrt_compositor_semaphore *xcsems[IPC_MAX_CLIENT_SEMAPHORES]; + struct + { + uint32_t root; + uint32_t local; + uint32_t stage; + uint32_t unbounded; + } semantic_spaces; + + //! Number of spaces. + uint32_t space_count; + + //! Ptrs to the spaces. + struct xtr_space *xspcs[IPC_MAX_CLIENT_SPACES]; + //! Socket fd used for client comms struct ipc_message_channel imc; @@ -300,6 +316,9 @@ struct ipc_server //! System devices. struct xrt_system_devices *xsysd; + //! Space overseer. + struct xrt_space_overseer *xso; + //! System compositor. struct xrt_system_compositor *xsysc; diff --git a/src/xrt/ipc/server/ipc_server_handler.c b/src/xrt/ipc/server/ipc_server_handler.c index edca90ef2..0aaf2fc89 100644 --- a/src/xrt/ipc/server/ipc_server_handler.c +++ b/src/xrt/ipc/server/ipc_server_handler.c @@ -1,9 +1,10 @@ -// Copyright 2020-2021, Collabora, Ltd. +// Copyright 2020-2023, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Handling functions called from generated dispatch function. * @author Pete Black + * @author Jakob Bornecrantz * @ingroup ipc_server */ @@ -25,6 +26,25 @@ * */ +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) { @@ -60,6 +80,65 @@ set_swapchain_info(volatile struct ipc_client_state *ics, ics->swapchain_data[index].image_count = xsc->image_count; } +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; +} + /* * @@ -163,6 +242,215 @@ ipc_handle_session_destroy(volatile struct ipc_client_state *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_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(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_compositor_get_info(volatile struct ipc_client_state *ics, struct xrt_compositor_info *out_info) { diff --git a/src/xrt/ipc/server/ipc_server_per_client_thread.c b/src/xrt/ipc/server/ipc_server_per_client_thread.c index 93179f7f2..770251d04 100644 --- a/src/xrt/ipc/server/ipc_server_per_client_thread.c +++ b/src/xrt/ipc/server/ipc_server_per_client_thread.c @@ -139,6 +139,12 @@ client_loop(volatile struct ipc_client_state *ics) ipc_server_client_destroy_compositor(ics); + // Make sure undestroyed spaces are unreferenced + for (uint32_t i = 0; i < IPC_MAX_CLIENT_SPACES; i++) { + // Cast away volatile. + xrt_space_reference((struct xrt_space **)&ics->xspcs[i], NULL); + } + // Should we stop the server when a client disconnects? if (ics->server->exit_on_disconnect) { ics->server->running = false; @@ -200,6 +206,12 @@ client_loop(volatile struct ipc_client_state *ics) ipc_server_client_destroy_compositor(ics); + // Make sure undestroyed spaces are unreferenced + for (uint32_t i = 0; i < IPC_MAX_CLIENT_SPACES; i++) { + // Cast away volatile. + xrt_space_reference((struct xrt_space **)&ics->xspcs[i], NULL); + } + // Should we stop the server when a client disconnects? if (ics->server->exit_on_disconnect) { ics->server->running = false; diff --git a/src/xrt/ipc/shared/proto.json b/src/xrt/ipc/shared/proto.json index 7f944fd0f..b688f9a69 100644 --- a/src/xrt/ipc/shared/proto.json +++ b/src/xrt/ipc/shared/proto.json @@ -68,6 +68,67 @@ "session_destroy": {}, + "space_create_semantic_ids": { + "out": [ + {"name": "root_id", "type": "uint32_t"}, + {"name": "view_id", "type": "uint32_t"}, + {"name": "local_id", "type": "uint32_t"}, + {"name": "stage_id", "type": "uint32_t"}, + {"name": "unbounded_id", "type": "uint32_t"} + ] + }, + + "space_create_offset": { + "in": [ + {"name": "parent_id", "type": "uint32_t"}, + {"name": "offset", "type": "struct xrt_pose"} + ], + "out": [ + {"name": "space_id", "type": "uint32_t"} + ] + }, + + "space_create_pose": { + "in": [ + {"name": "xdev_id", "type": "uint32_t"}, + {"name": "name", "type": "enum xrt_input_name"} + ], + "out": [ + {"name": "space_id", "type": "uint32_t"} + ] + }, + + "space_locate_space": { + "in": [ + {"name": "base_space_id", "type": "uint32_t"}, + {"name": "base_offset", "type": "struct xrt_pose"}, + {"name": "at_timestamp", "type": "uint64_t"}, + {"name": "space_id", "type": "uint32_t"}, + {"name": "offset", "type": "struct xrt_pose"} + ], + "out": [ + {"name": "relation", "type": "struct xrt_space_relation"} + ] + }, + + "space_locate_device": { + "in": [ + {"name": "base_space_id", "type": "uint32_t"}, + {"name": "base_offset", "type": "struct xrt_pose"}, + {"name": "at_timestamp", "type": "uint64_t"}, + {"name": "xdev_id", "type": "uint32_t"} + ], + "out": [ + {"name": "relation", "type": "struct xrt_space_relation"} + ] + }, + + "space_destroy": { + "in": [ + {"name": "space_id", "type": "uint32_t"} + ] + }, + "compositor_get_info": { "out": [ {"name": "info", "type": "struct xrt_compositor_info"} diff --git a/src/xrt/ipc/shared/proto.json.license b/src/xrt/ipc/shared/proto.json.license index c52f4cb8b..237108040 100644 --- a/src/xrt/ipc/shared/proto.json.license +++ b/src/xrt/ipc/shared/proto.json.license @@ -1,3 +1,3 @@ -Copyright 2018-2020, Collabora, Ltd. +Copyright 2018-2023, Collabora, Ltd. -SPDX-License-Identifier: BSL-1.0 \ No newline at end of file +SPDX-License-Identifier: BSL-1.0