ipc: Add code to enable a service process

This enables out of process compositing.
This commit is contained in:
Jakob Bornecrantz 2020-04-11 01:28:35 +01:00
parent 5ba751a239
commit 7c8a8a3f87
22 changed files with 3454 additions and 1 deletions

View file

@ -71,7 +71,8 @@ cmake_dependent_option(BUILD_WITH_OPENGL "Enable OpenGL Graphics API support" ON
cmake_dependent_option(BUILD_WITH_EGL "Enable OpenGL on EGL Graphics API support" ON "BUILD_WITH_OPENGL AND EGL_FOUND" OFF)
cmake_dependent_option(BUILD_WITH_DBUS "Enable dbus support (for BLE support)" ON "DBUS_FOUND" OFF)
cmake_dependent_option(BUILD_COMPOSITOR_MAIN "Build main compositor host" ON "BUILD_WITH_WAYLAND OR BUILD_WITH_XCB" OFF)
cmake_dependent_option(BUILD_TARGET_OPENXR "Build OpenXR runtime target" ON "BUILD_COMPOSITOR_MAIN" OFF)
cmake_dependent_option(XRT_BUILD_IPC "Build OpenXR runtime target" ON "BUILD_COMPOSITOR_MAIN" OFF)
cmake_dependent_option(BUILD_TARGET_OPENXR "Build OpenXR runtime target" ON "BUILD_COMPOSITOR_MAIN OR XRT_BUILD_IPC" OFF)
# Most users won't touch these.
mark_as_advanced(BUILD_COMPOSITOR_MAIN BUILD_TARGET_OPENXR)

View file

@ -50,6 +50,7 @@ build_docs = doxygen.found()
glslangValidator = find_program('glslangValidator')
pthreads = cc.find_library('pthread', required: true)
rt = cc.find_library('rt', required: true)
avcodec = dependency('libavcodec', required: false)
egl = dependency('egl', required: get_option('egl'))

View file

@ -28,6 +28,7 @@ fi
src/xrt/compositor \
src/xrt/drivers \
src/xrt/include \
src/xrt/ipc \
src/xrt/state_trackers \
src/xrt/targets \
\( -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) \

View file

@ -7,3 +7,7 @@ add_subdirectory(drivers)
add_subdirectory(compositor)
add_subdirectory(state_trackers)
add_subdirectory(targets)
if (XRT_BUILD_IPC)
add_subdirectory(ipc)
endif()

View file

@ -0,0 +1,78 @@
# Copyright 2020, Collabora, Ltd.
# SPDX-License-Identifier: BSL-1.0
###
# Generator
function(proto_gen output)
add_custom_command(OUTPUT ${output}
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/proto.py
${CMAKE_CURRENT_SOURCE_DIR}/proto.json
${output}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/proto.py
${CMAKE_CURRENT_SOURCE_DIR}/proto.json
)
endfunction(proto_gen)
proto_gen(${CMAKE_CURRENT_BINARY_DIR}/ipc_protocol_generated.h)
proto_gen(${CMAKE_CURRENT_BINARY_DIR}/ipc_client_generated.h)
proto_gen(${CMAKE_CURRENT_BINARY_DIR}/ipc_client_generated.c)
proto_gen(${CMAKE_CURRENT_BINARY_DIR}/ipc_server_generated.h)
proto_gen(${CMAKE_CURRENT_BINARY_DIR}/ipc_server_generated.c)
###
# Client
add_library(ipc_client STATIC
${CMAKE_CURRENT_BINARY_DIR}/ipc_protocol_generated.h
${CMAKE_CURRENT_BINARY_DIR}/ipc_client_generated.c
${CMAKE_CURRENT_BINARY_DIR}/ipc_client_generated.h
ipc_client.h
ipc_client_compositor.c
ipc_client_device.c
ipc_client_hmd.c
ipc_client_instance.c
ipc_client_utils.c
)
target_include_directories(ipc_client INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(ipc_client PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(ipc_client PRIVATE
aux_util
rt
)
###
# Server
add_library(ipc_server STATIC
${CMAKE_CURRENT_BINARY_DIR}/ipc_protocol_generated.h
${CMAKE_CURRENT_BINARY_DIR}/ipc_server_generated.c
${CMAKE_CURRENT_BINARY_DIR}/ipc_server_generated.h
ipc_server.h
ipc_server_client.c
ipc_server_process.c
ipc_server_utils.c
ipc_server_utils.h
)
target_include_directories(ipc_server
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(ipc_server PRIVATE
aux_util
${CMAKE_CURRENT_SOURCE_DIR}/../compositor
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(ipc_server PRIVATE
aux_util
rt
)

122
src/xrt/ipc/ipc_client.h Normal file
View file

@ -0,0 +1,122 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Common client side code.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_client
*/
#pragma once
#include "xrt/xrt_compiler.h"
#include "ipc_protocol.h"
#include <stdio.h>
/*
*
* Logging
*
*/
/*!
* Spew level logging.
*/
#define IPC_SPEW(c, ...) \
do { \
if ((c)->print_spew) { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (false)
/*!
* Debug level logging.
*/
#define IPC_DEBUG(c, ...) \
do { \
if ((c)->print_debug) { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (false)
/*!
* Error level logging.
*/
#define IPC_ERROR(c, ...) \
do { \
(void)(c)->print_debug; \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while (false)
/*
*
* Structs
*
*/
struct xrt_compositor_fd;
/*!
* Connection.
*/
typedef struct ipc_connection
{
int socket_fd;
struct ipc_shared_memory *ism;
int ism_fd;
bool print_debug; // TODO: link to settings
bool print_spew; // TODO: link to settings
} ipc_connection_t;
ipc_result_t
ipc_client_send_message(ipc_connection_t *ipc_c, void *message, size_t size);
ipc_result_t
ipc_client_send_and_get_reply(struct ipc_connection *ipc_c,
void *msg_ptr,
size_t msg_size,
void *reply_ptr,
size_t reply_size);
ipc_result_t
ipc_client_send_and_get_reply_fds(ipc_connection_t *ipc_c,
void *msg_ptr,
size_t msg_size,
void *reply_ptr,
size_t reply_size,
int *fds,
size_t num_fds);
/*
*
* Internal functions.
*
*/
int
ipc_client_compositor_create(ipc_connection_t *ipc_c,
struct xrt_device *xdev,
bool flip_y,
struct xrt_compositor_fd **out_xcfd);
struct xrt_device *
ipc_client_hmd_create(ipc_connection_t *ipc_c, uint32_t device_id);
struct xrt_device *
ipc_client_device_create(ipc_connection_t *ipc_c, uint32_t device_id);

View file

@ -0,0 +1,496 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Client side wrapper of compositor.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_client
*/
#include "xrt/xrt_device.h"
#include "xrt/xrt_compositor.h"
#include "util/u_misc.h"
#include "ipc_protocol.h"
#include "ipc_client.h"
#include "ipc_client_generated.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <assert.h>
/*
*
* Internal structs and helpers.
*
*/
struct ipc_client_compositor
{
struct xrt_compositor_fd base;
ipc_connection_t *ipc_c;
struct
{
//! Id that we are currently using for submitting layers.
uint32_t slot_id;
uint32_t num_layers;
enum xrt_blend_mode env_blend_mode;
} layers;
};
struct ipc_client_swapchain
{
struct xrt_swapchain_fd base;
struct ipc_client_compositor *icc;
uint32_t id;
};
static inline struct ipc_client_compositor *
ipc_client_compositor(struct xrt_compositor *xc)
{
return (struct ipc_client_compositor *)xc;
}
static inline struct ipc_client_swapchain *
ipc_client_swapchain(struct xrt_swapchain *xs)
{
return (struct ipc_client_swapchain *)xs;
}
/*
*
* Misc functions
*
*/
void
compositor_disconnect(ipc_connection_t *ipc_c)
{
if (ipc_c->socket_fd < 0) {
return;
}
close(ipc_c->socket_fd);
ipc_c->socket_fd = -1;
}
#define CALL_CHK(call) \
if ((call) != IPC_SUCCESS) { \
IPC_DEBUG(icc->ipc_c, "IPC: %s call error!", __func__); \
}
/*
*
* Swapchain.
*
*/
static void
ipc_compositor_swapchain_destroy(struct xrt_swapchain *xsc)
{
struct ipc_client_swapchain *ics = ipc_client_swapchain(xsc);
struct ipc_client_compositor *icc = ics->icc;
CALL_CHK(ipc_call_swapchain_destroy(icc->ipc_c, ics->id));
free(xsc);
}
static bool
ipc_compositor_swapchain_wait_image(struct xrt_swapchain *xsc,
uint64_t timeout,
uint32_t index)
{
struct ipc_client_swapchain *ics = ipc_client_swapchain(xsc);
struct ipc_client_compositor *icc = ics->icc;
CALL_CHK(
ipc_call_swapchain_wait_image(icc->ipc_c, ics->id, timeout, index));
return true;
}
static bool
ipc_compositor_swapchain_acquire_image(struct xrt_swapchain *xsc,
uint32_t *out_index)
{
struct ipc_client_swapchain *ics = ipc_client_swapchain(xsc);
struct ipc_client_compositor *icc = ics->icc;
CALL_CHK(
ipc_call_swapchain_acquire_image(icc->ipc_c, ics->id, out_index));
return true;
}
static bool
ipc_compositor_swapchain_release_image(struct xrt_swapchain *xsc,
uint32_t index)
{
struct ipc_client_swapchain *ics = ipc_client_swapchain(xsc);
struct ipc_client_compositor *icc = ics->icc;
CALL_CHK(ipc_call_swapchain_release_image(icc->ipc_c, ics->id, index));
return true;
}
/*
*
* Compositor functions.
*
*/
static struct xrt_swapchain *
ipc_compositor_swapchain_create(struct xrt_compositor *xc,
enum xrt_swapchain_create_flags create,
enum xrt_swapchain_usage_bits bits,
int64_t format,
uint32_t sample_count,
uint32_t width,
uint32_t height,
uint32_t face_count,
uint32_t array_size,
uint32_t mip_count)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
int remote_fds[IPC_MAX_SWAPCHAIN_FDS] = {0};
ipc_result_t r = 0;
uint32_t handle;
uint32_t num_images;
uint64_t size;
r = ipc_call_swapchain_create(icc->ipc_c, // connection
width, // in
height, // in
format, // in
&handle, // out
&num_images, // out
&size, // out
remote_fds, // fds
IPC_MAX_SWAPCHAIN_FDS); // fds
if (r != IPC_SUCCESS) {
return NULL;
}
struct ipc_client_swapchain *ics =
U_TYPED_CALLOC(struct ipc_client_swapchain);
ics->base.base.array_size = 1;
ics->base.base.num_images = num_images;
ics->base.base.wait_image = ipc_compositor_swapchain_wait_image;
ics->base.base.acquire_image = ipc_compositor_swapchain_acquire_image;
ics->base.base.release_image = ipc_compositor_swapchain_release_image;
ics->base.base.destroy = ipc_compositor_swapchain_destroy;
ics->icc = icc;
ics->id = handle;
for (uint32_t i = 0; i < num_images; i++) {
ics->base.images[i].fd = remote_fds[i];
ics->base.images[i].size = size;
}
return &ics->base.base;
}
static void
ipc_compositor_begin_session(struct xrt_compositor *xc,
enum xrt_view_type view_type)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
IPC_SPEW(icc->ipc_c, "IPC: compositor begin session");
CALL_CHK(ipc_call_session_begin(icc->ipc_c));
}
static void
ipc_compositor_end_session(struct xrt_compositor *xc)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
IPC_SPEW(icc->ipc_c, "IPC: compositor end session");
CALL_CHK(ipc_call_session_end(icc->ipc_c));
}
static void
ipc_compositor_get_formats(struct xrt_compositor *xc,
uint32_t *num_formats,
int64_t *formats)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
IPC_SPEW(icc->ipc_c, "IPC: compositor get_formats");
struct ipc_formats_info info;
CALL_CHK(ipc_call_compositor_get_formats(icc->ipc_c, &info));
*num_formats = info.num_formats;
memcpy(formats, info.formats, sizeof(int64_t) * (*num_formats));
}
static void
ipc_compositor_wait_frame(struct xrt_compositor *xc,
uint64_t *predicted_display_time,
uint64_t *predicted_display_period)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
CALL_CHK(ipc_call_compositor_wait_frame(
icc->ipc_c, predicted_display_period, predicted_display_time));
}
static void
ipc_compositor_begin_frame(struct xrt_compositor *xc)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
CALL_CHK(ipc_call_compositor_begin_frame(icc->ipc_c));
}
#if 0 /* LAYERS */
static void
ipc_compositor_layer_begin(struct xrt_compositor *xc,
enum xrt_blend_mode env_blend_mode)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
icc->layers.env_blend_mode = env_blend_mode;
}
static void
ipc_compositor_layer_stereo_projection(
struct xrt_compositor *xc,
uint64_t timestamp,
struct xrt_device *xdev,
enum xrt_input_name name,
enum xrt_layer_composition_flags layer_flags,
struct xrt_swapchain *l_sc,
uint32_t l_image_index,
struct xrt_rect *l_rect,
uint32_t l_array_index,
struct xrt_fov *l_fov,
struct xrt_pose *l_pose,
struct xrt_swapchain *r_sc,
uint32_t r_image_index,
struct xrt_rect *r_rect,
uint32_t r_array_index,
struct xrt_fov *r_fov,
struct xrt_pose *r_pose)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
struct ipc_shared_memory *ism = icc->ipc_c->ism;
struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id];
struct ipc_layer_entry *layer = &slot->layers[icc->layers.num_layers];
struct ipc_layer_stereo_projection *stereo = &layer->stereo;
struct ipc_client_swapchain *l = ipc_client_swapchain(l_sc);
struct ipc_client_swapchain *r = ipc_client_swapchain(r_sc);
stereo->timestamp = timestamp;
stereo->xdev_id = 0; //! @todo Real id.
stereo->name = name;
stereo->layer_flags = layer_flags;
stereo->l.swapchain_id = l->id;
stereo->l.image_index = l_image_index;
stereo->l.rect = *l_rect;
stereo->l.array_index = l_array_index;
stereo->l.fov = *l_fov;
stereo->l.pose = *l_pose;
stereo->r.swapchain_id = r->id;
stereo->r.image_index = r_image_index;
stereo->r.rect = *r_rect;
stereo->r.array_index = r_array_index;
stereo->r.fov = *r_fov;
stereo->r.pose = *r_pose;
// Increment the number of layers.
icc->layers.num_layers++;
}
static void
ipc_compositor_layer_quad(struct xrt_compositor *xc,
uint64_t timestamp,
struct xrt_device *xdev,
enum xrt_input_name name,
enum xrt_layer_composition_flags layer_flags,
enum xrt_layer_eye_visibility visibility,
struct xrt_swapchain *sc,
uint32_t image_index,
struct xrt_rect *rect,
uint32_t array_index,
struct xrt_pose *pose,
struct xrt_vec2 *size)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
struct ipc_shared_memory *ism = icc->ipc_c->ism;
struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id];
struct ipc_layer_entry *layer = &slot->layers[icc->layers.num_layers];
struct ipc_layer_quad *quad = &layer->quad;
struct ipc_client_swapchain *ics = ipc_client_swapchain(sc);
quad->timestamp = timestamp;
quad->xdev_id = 0; //! @todo Real id.
quad->name = name;
quad->layer_flags = layer_flags;
quad->swapchain_id = ics->id;
quad->image_index = image_index;
quad->rect = *rect;
quad->array_index = array_index;
quad->pose = *pose;
quad->size = *size;
// Increment the number of layers.
icc->layers.num_layers++;
}
static void
ipc_compositor_layer_commit(struct xrt_compositor *xc)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
struct ipc_shared_memory *ism = icc->ipc_c->ism;
struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id];
// Last bit of data to put in the shared memory area.
slot->num_layers = icc->layers.num_layers;
CALL_CHK(ipc_call_compositor_layer_sync(icc->ipc_c, icc->layers.slot_id,
&icc->layers.slot_id));
// Reset.
icc->layers.num_layers = 0;
}
#else
static void
ipc_compositor_end_frame(struct xrt_compositor *xc,
enum xrt_blend_mode blend_mode,
struct xrt_swapchain **xscs,
const uint32_t *image_index,
uint32_t *layers,
uint32_t num_swapchains)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
struct ipc_shared_memory *ism = icc->ipc_c->ism;
struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id];
struct ipc_layer_entry *layer = &slot->layers[icc->layers.num_layers];
struct ipc_layer_stereo_projection *stereo = &layer->stereo;
struct ipc_client_swapchain *l = ipc_client_swapchain(xscs[0]);
struct ipc_client_swapchain *r = ipc_client_swapchain(xscs[0]);
// stereo->timestamp = timestamp;
// stereo->xdev_id = 0; //! @todo Real id.
// stereo->name = name;
// stereo->layer_flags = layer_flags;
stereo->l.swapchain_id = l->id;
stereo->l.image_index = image_index[0];
// stereo->l.rect = *l_rect;
stereo->l.array_index = layers[0];
// stereo->l.fov = *l_fov;
// stereo->l.pose = *l_pose;
stereo->r.swapchain_id = r->id;
stereo->r.image_index = image_index[1];
// stereo->r.rect = *r_rect;
stereo->r.array_index = layers[1];
// stereo->r.fov = *r_fov;
// stereo->r.pose = *r_pose;
// Last bit of data to put in the shared memory area.
slot->num_layers = icc->layers.num_layers;
CALL_CHK(ipc_call_compositor_layer_sync(icc->ipc_c, icc->layers.slot_id,
&icc->layers.slot_id));
// Reset.
icc->layers.num_layers = 0;
}
#endif
static void
ipc_compositor_discard_frame(struct xrt_compositor *xc)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
CALL_CHK(ipc_call_compositor_discard_frame(icc->ipc_c));
}
static void
ipc_compositor_destroy(struct xrt_compositor *xc)
{
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
IPC_SPEW(icc->ipc_c, "IPC: NOT IMPLEMENTED compositor destroy");
}
/*
*
* 'Exported' functions.
*
*/
int
ipc_client_compositor_create(ipc_connection_t *ipc_c,
struct xrt_device *xdev,
bool flip_y,
struct xrt_compositor_fd **out_xcfd)
{
struct ipc_client_compositor *c =
U_TYPED_CALLOC(struct ipc_client_compositor);
c->base.base.create_swapchain = ipc_compositor_swapchain_create;
c->base.base.begin_session = ipc_compositor_begin_session;
c->base.base.end_session = ipc_compositor_end_session;
c->base.base.wait_frame = ipc_compositor_wait_frame;
c->base.base.begin_frame = ipc_compositor_begin_frame;
c->base.base.discard_frame = ipc_compositor_discard_frame;
#if 0 /* LAYERS */
c->base.base.layer_begin = ipc_compositor_layer_begin;
c->base.base.layer_stereo_projection =
ipc_compositor_layer_stereo_projection;
c->base.base.layer_quad = ipc_compositor_layer_quad;
c->base.base.layer_commit = ipc_compositor_layer_commit;
#else
c->base.base.end_frame = ipc_compositor_end_frame;
#endif
c->base.base.destroy = ipc_compositor_destroy;
c->ipc_c = ipc_c;
// fetch our format list on client compositor construction
int64_t formats[IPC_MAX_FORMATS] = {0};
uint32_t num_formats = 0;
ipc_compositor_get_formats(&(c->base.base), &num_formats, formats);
// TODO: client compositor format count is hardcoded
c->base.base.num_formats = 0;
for (uint32_t i = 0; i < 8; i++) {
if (i < num_formats) {
c->base.base.formats[i] = formats[i];
c->base.base.num_formats++;
}
}
*out_xcfd = &c->base;
return 0;
}

View file

@ -0,0 +1,168 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief IPC Client device.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_client
*/
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "xrt/xrt_device.h"
#include "os/os_time.h"
#include "math/m_api.h"
#include "util/u_var.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_device.h"
#include "ipc_client.h"
#include "ipc_client_generated.h"
/*
*
* Structs and defines.
*
*/
struct ipc_client_device
{
struct xrt_device base;
ipc_connection_t *ipc_c;
uint32_t device_id;
};
/*
*
* Functions
*
*/
static inline struct ipc_client_device *
ipc_client_device(struct xrt_device *xdev)
{
return (struct ipc_client_device *)xdev;
}
static void
ipc_client_device_destroy(struct xrt_device *xdev)
{
struct ipc_client_device *icd = ipc_client_device(xdev);
// Remove the variable tracking.
u_var_remove_root(icd);
// We do not own these, so don't free them.
icd->base.inputs = NULL;
icd->base.outputs = NULL;
// Free this device with the helper.
u_device_free(&icd->base);
}
static void
ipc_client_device_update_inputs(struct xrt_device *xdev)
{
struct ipc_client_device *icd = ipc_client_device(xdev);
ipc_result_t r =
ipc_call_device_update_input(icd->ipc_c, icd->device_id);
if (r != IPC_SUCCESS) {
IPC_DEBUG(icd->ipc_c, "IPC: Error sending input update!");
}
}
static void
ipc_client_device_get_tracked_pose(struct xrt_device *xdev,
enum xrt_input_name name,
uint64_t at_timestamp_ns,
uint64_t *out_relation_timestamp_ns,
struct xrt_space_relation *out_relation)
{
struct ipc_client_device *icd = ipc_client_device(xdev);
ipc_result_t r = ipc_call_device_get_tracked_pose(
icd->ipc_c, icd->device_id, name, at_timestamp_ns,
out_relation_timestamp_ns, out_relation);
if (r != IPC_SUCCESS) {
IPC_DEBUG(icd->ipc_c, "IPC: Error sending input update!");
}
}
static void
ipc_client_device_get_view_pose(struct xrt_device *xdev,
struct xrt_vec3 *eye_relation,
uint32_t view_index,
struct xrt_pose *out_pose)
{
// Empty
}
static void
ipc_client_device_set_output(struct xrt_device *xdev,
enum xrt_output_name name,
union xrt_output_value *value)
{
struct ipc_client_device *icd = ipc_client_device(xdev);
ipc_result_t r =
ipc_call_device_set_output(icd->ipc_c, icd->device_id, name, value);
if (r != IPC_SUCCESS) {
IPC_DEBUG(icd->ipc_c, "IPC: Error sending set output!");
}
}
struct xrt_device *
ipc_client_device_create(ipc_connection_t *ipc_c, uint32_t device_id)
{
// Helpers.
struct ipc_shared_memory *ism = ipc_c->ism;
struct ipc_shared_device *idev = &ism->idevs[device_id];
// Allocate and setup the basics.
enum u_device_alloc_flags flags = (enum u_device_alloc_flags)(
U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE);
struct ipc_client_device *icd =
U_DEVICE_ALLOCATE(struct ipc_client_device, flags, 0, 0);
icd->ipc_c = ipc_c;
icd->base.update_inputs = ipc_client_device_update_inputs;
icd->base.get_tracked_pose = ipc_client_device_get_tracked_pose;
icd->base.get_view_pose = ipc_client_device_get_view_pose;
icd->base.set_output = ipc_client_device_set_output;
icd->base.destroy = ipc_client_device_destroy;
// Start copying the information from the idev.
icd->base.name = idev->name;
icd->device_id = device_id;
// Print name.
snprintf(icd->base.str, XRT_DEVICE_NAME_LEN, "%s", idev->str);
// Setup inputs, by pointing directly to the shared memory.
assert(idev->num_inputs > 0);
icd->base.inputs = &ism->inputs[idev->first_input_index];
icd->base.num_inputs = idev->num_inputs;
assert(idev->num_outputs > 0);
icd->base.outputs = &ism->outputs[idev->first_output_index];
icd->base.num_outputs = idev->num_outputs;
// Setup variable tracker.
u_var_add_root(icd, icd->base.str, true);
u_var_add_ro_u32(icd, &icd->device_id, "device_id");
return &icd->base;
}

View file

@ -0,0 +1,190 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief IPC Client HMD device.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_client
*/
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "xrt/xrt_device.h"
#include "os/os_time.h"
#include "math/m_api.h"
#include "util/u_var.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_device.h"
#include "ipc_client.h"
#include "ipc_client_generated.h"
/*
*
* Structs and defines.
*
*/
struct ipc_client_hmd
{
struct xrt_device base;
ipc_connection_t *ipc_c;
uint32_t device_id;
};
/*
*
* Functions
*
*/
static inline struct ipc_client_hmd *
ipc_client_hmd(struct xrt_device *xdev)
{
return (struct ipc_client_hmd *)xdev;
}
static void
ipc_client_hmd_destroy(struct xrt_device *xdev)
{
struct ipc_client_hmd *ich = ipc_client_hmd(xdev);
// Remove the variable tracking.
u_var_remove_root(ich);
// We do not own these, so don't free them.
ich->base.inputs = NULL;
ich->base.outputs = NULL;
// Free this device with the helper.
u_device_free(&ich->base);
}
static void
ipc_client_hmd_update_inputs(struct xrt_device *xdev)
{
struct ipc_client_hmd *ich = ipc_client_hmd(xdev);
ipc_result_t r =
ipc_call_device_update_input(ich->ipc_c, ich->device_id);
if (r != IPC_SUCCESS) {
IPC_DEBUG(ich->ipc_c, "IPC: Error calling input update!");
}
}
static void
ipc_client_hmd_get_tracked_pose(struct xrt_device *xdev,
enum xrt_input_name name,
uint64_t at_timestamp_ns,
uint64_t *out_relation_timestamp_ns,
struct xrt_space_relation *out_relation)
{
struct ipc_client_hmd *ich = ipc_client_hmd(xdev);
ipc_result_t r = ipc_call_device_get_tracked_pose(
ich->ipc_c, ich->device_id, name, at_timestamp_ns,
out_relation_timestamp_ns, out_relation);
if (r != IPC_SUCCESS) {
IPC_DEBUG(ich->ipc_c, "IPC: Error calling tracked pose!");
}
}
static void
ipc_client_hmd_get_view_pose(struct xrt_device *xdev,
struct xrt_vec3 *eye_relation,
uint32_t view_index,
struct xrt_pose *out_pose)
{
struct ipc_client_hmd *ich = ipc_client_hmd(xdev);
ipc_result_t r = ipc_call_device_get_view_pose(
ich->ipc_c, ich->device_id, eye_relation, view_index, out_pose);
if (r != IPC_SUCCESS) {
IPC_DEBUG(ich->ipc_c, "IPC: Error calling view pose!");
}
}
struct xrt_device *
ipc_client_hmd_create(ipc_connection_t *ipc_c, uint32_t device_id)
{
struct ipc_shared_memory *ism = ipc_c->ism;
struct ipc_shared_device *idev = &ism->idevs[device_id];
enum u_device_alloc_flags flags = (enum u_device_alloc_flags)(
U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE);
struct ipc_client_hmd *ich =
U_DEVICE_ALLOCATE(struct ipc_client_hmd, flags, 0, 0);
ich->ipc_c = ipc_c;
ich->device_id = device_id;
ich->base.update_inputs = ipc_client_hmd_update_inputs;
ich->base.get_tracked_pose = ipc_client_hmd_get_tracked_pose;
ich->base.get_view_pose = ipc_client_hmd_get_view_pose;
ich->base.destroy = ipc_client_hmd_destroy;
// Start copying the information from the idev.
ich->base.name = idev->name;
ich->device_id = device_id;
// Print name.
snprintf(ich->base.str, XRT_DEVICE_NAME_LEN, "%s", idev->str);
// Setup inputs, by pointing directly to the shared memory.
assert(idev->num_inputs > 0);
ich->base.inputs = &ism->inputs[idev->first_input_index];
ich->base.num_inputs = idev->num_inputs;
#if 0
// Setup info.
struct u_device_simple_info info;
info.display.w_pixels = 1920;
info.display.h_pixels = 1080;
info.display.w_meters = 0.13f;
info.display.h_meters = 0.07f;
info.lens_horizontal_separation_meters = 0.13f / 2.0f;
info.lens_vertical_position_meters = 0.07f / 2.0f;
info.views[0].fov = 85.0f * (M_PI / 180.0f);
info.views[1].fov = 85.0f * (M_PI / 180.0f);
if (!u_device_setup_split_side_by_side(&ich->base, &info)) {
IPC_ERROR(ich->ipc_c, "Failed to setup basic device info");
ipc_client_hmd_destroy(&ich->base);
return NULL;
}
#endif
// clang-foramt off
ich->base.hmd->blend_mode = XRT_BLEND_MODE_OPAQUE;
ich->base.hmd->distortion.models = XRT_DISTORTION_MODEL_NONE;
ich->base.hmd->distortion.preferred = XRT_DISTORTION_MODEL_NONE;
ich->base.hmd->views[0].display.w_pixels =
ipc_c->ism->hmd.views[0].display.w_pixels;
ich->base.hmd->views[0].display.h_pixels =
ipc_c->ism->hmd.views[0].display.h_pixels;
ich->base.hmd->views[0].fov = ipc_c->ism->hmd.views[0].fov;
ich->base.hmd->views[1].display.w_pixels =
ipc_c->ism->hmd.views[1].display.w_pixels;
ich->base.hmd->views[1].display.h_pixels =
ipc_c->ism->hmd.views[1].display.h_pixels;
ich->base.hmd->views[1].fov = ipc_c->ism->hmd.views[1].fov;
// clang-foramt on
// Setup variable tracker.
u_var_add_root(ich, ich->base.str, true);
u_var_add_ro_u32(ich, &ich->device_id, "device_id");
return &ich->base;
}

View file

@ -0,0 +1,236 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Client side wrapper of instance.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_client
*/
#include "xrt/xrt_instance.h"
#include "xrt/xrt_gfx_fd.h"
#include "util/u_misc.h"
#include "ipc_protocol.h"
#include "ipc_client.h"
#include "ipc_client_generated.h"
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/un.h>
#include <fcntl.h>
#include <unistd.h>
/*
*
* Struct and helpers.
*
*/
struct ipc_client_instance
{
struct xrt_instance base;
ipc_connection_t ipc_c;
struct xrt_device *xdevs[8];
size_t num_xdevs;
};
static inline struct ipc_client_instance *
ipc_client_instance(struct xrt_instance *xinst)
{
return (struct ipc_client_instance *)xinst;
}
static bool
ipc_connect(ipc_connection_t *ipc_c)
{
struct sockaddr_un addr;
int ret;
ipc_c->print_spew = false; // TODO: hardcoded - fetch from settings
ipc_c->print_debug = false; // TODO: hardcoded - fetch from settings
// create our IPC socket
ret = socket(PF_UNIX, SOCK_STREAM, 0);
if (ret < 0) {
IPC_DEBUG(ipc_c, "Socket Create Error!");
return false;
}
int socket = ret;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, IPC_MSG_SOCK_FILE);
ret = connect(socket, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
IPC_DEBUG(ipc_c, "Socket Connect error!");
close(socket);
return false;
}
ipc_c->socket_fd = socket;
return true;
}
/*
*
* Member functions.
*
*/
static int
ipc_client_instance_select(struct xrt_instance *xinst,
struct xrt_device **xdevs,
size_t num_xdevs)
{
struct ipc_client_instance *ii = ipc_client_instance(xinst);
if (num_xdevs < 1) {
return -1;
}
// @todo What about ownership?
for (size_t i = 0; i < num_xdevs && i < ii->num_xdevs; i++) {
xdevs[i] = ii->xdevs[i];
}
return 0;
}
static int
ipc_client_instance_create_fd_compositor(struct xrt_instance *xinst,
struct xrt_device *xdev,
bool flip_y,
struct xrt_compositor_fd **out_xcfd)
{
struct ipc_client_instance *ii = ipc_client_instance(xinst);
struct xrt_compositor_fd *xcfd = NULL;
int ret = ipc_client_compositor_create(&ii->ipc_c, xdev, flip_y, &xcfd);
if (ret < 0 || xcfd == NULL) {
return -1;
}
*out_xcfd = xcfd;
return 0;
}
static int
ipc_client_instance_get_prober(struct xrt_instance *xinst,
struct xrt_prober **out_xp)
{
*out_xp = NULL;
return -1;
}
static void
ipc_client_instance_destroy(struct xrt_instance *xinst)
{
struct ipc_client_instance *ii = ipc_client_instance(xinst);
free(ii);
}
/*
*
* Exported function(s).
*
*/
int
ipc_instance_create(struct xrt_instance **out_xinst)
{
struct ipc_client_instance *ii =
U_TYPED_CALLOC(struct ipc_client_instance);
ii->base.select = ipc_client_instance_select;
ii->base.create_fd_compositor =
ipc_client_instance_create_fd_compositor;
ii->base.get_prober = ipc_client_instance_get_prober;
ii->base.destroy = ipc_client_instance_destroy;
// FDs needs to be set to something negative.
ii->ipc_c.socket_fd = -1;
ii->ipc_c.ism_fd = -1;
if (!ipc_connect(&ii->ipc_c)) {
IPC_ERROR(
&ii->ipc_c,
"Failed to connect to monado service process\n\n"
"###\n"
"#\n"
"# Please make sure that the service procss is running\n"
"#\n"
"# It is called \"monado-service\"\n"
"# For builds it's located "
"\"build-dir/src/xrt/targets/service/monado-service\"\n"
"#\n"
"###\n");
free(ii);
return -1;
}
// get our xdev shm from the server and mmap it
ipc_result_t r =
ipc_call_instance_get_shm_fd(&ii->ipc_c, &ii->ipc_c.ism_fd, 1);
if (r != IPC_SUCCESS) {
IPC_ERROR(&ii->ipc_c, "Failed to retrieve shm fd");
free(ii);
return -1;
}
const int flags = MAP_SHARED;
const int access = PROT_READ | PROT_WRITE;
const size_t size = sizeof(struct ipc_shared_memory);
ii->ipc_c.ism = mmap(NULL, size, access, flags, ii->ipc_c.ism_fd, 0);
if (ii->ipc_c.ism == NULL) {
IPC_ERROR(&ii->ipc_c, "Failed to mmap shm ");
free(ii);
return -1;
}
struct ipc_shared_memory *ism = ii->ipc_c.ism;
uint32_t count = 0;
// Query the server for how many devices it has.
for (uint32_t i = 0; i < ism->num_idevs; i++) {
if (ism->idevs[i].name == XRT_DEVICE_GENERIC_HMD) {
ii->xdevs[count++] =
ipc_client_hmd_create(&ii->ipc_c, i);
} else {
ii->xdevs[count++] =
ipc_client_device_create(&ii->ipc_c, i);
}
}
ii->num_xdevs = count;
*out_xinst = &ii->base;
return 0;
}
int
xrt_prober_create(void **hack)
{
return -1;
}

View file

@ -0,0 +1,120 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Common client side code.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_client
*/
#include "ipc_client.h"
#include "util/u_misc.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
ipc_result_t
ipc_client_send_and_get_reply(struct ipc_connection *ipc_c,
void *msg_ptr,
size_t msg_size,
void *reply_ptr,
size_t reply_size)
{
if (ipc_c->socket_fd < 0) {
IPC_DEBUG(ipc_c, "Error sending - not connected!");
return IPC_FAILURE;
}
ssize_t len = send(ipc_c->socket_fd, msg_ptr, msg_size, 0);
if ((size_t)len != msg_size) {
IPC_DEBUG(ipc_c, "Error sending - cannot continue!");
return IPC_FAILURE;
}
// wait for the response
struct iovec iov = {0};
struct msghdr msg = {0};
iov.iov_base = reply_ptr;
iov.iov_len = reply_size;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
len = recvmsg(ipc_c->socket_fd, &msg, 0);
if (len < 0) {
IPC_DEBUG(ipc_c, "recvmsg failed with error: %s",
strerror(errno));
return IPC_FAILURE;
}
if ((size_t)len != reply_size) {
IPC_DEBUG(ipc_c, "recvmsg failed with error: no data");
return IPC_FAILURE;
}
return IPC_SUCCESS;
}
ipc_result_t
ipc_client_send_and_get_reply_fds(ipc_connection_t *ipc_c,
void *msg_ptr,
size_t msg_size,
void *reply_ptr,
size_t reply_size,
int *fds,
size_t num_fds)
{
if (send(ipc_c->socket_fd, msg_ptr, msg_size, 0) == -1) {
IPC_DEBUG(ipc_c, "Error sending - cannot continue!");
return IPC_FAILURE;
}
const size_t fds_size = sizeof(int) * num_fds;
char buf[CMSG_SPACE(fds_size)];
memset(buf, 0, sizeof(buf));
struct iovec iov = {0};
iov.iov_base = reply_ptr;
iov.iov_len = reply_size;
struct msghdr msg = {0};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
ssize_t len = recvmsg(ipc_c->socket_fd, &msg, 0);
if (len < 0) {
IPC_DEBUG(ipc_c, "recvmsg failed with error: %s",
strerror(errno));
return -1;
}
if (len == 0) {
IPC_DEBUG(ipc_c, "recvmsg failed with error: no data");
return -1;
}
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
memcpy(fds, (int *)CMSG_DATA(cmsg), fds_size);
return IPC_SUCCESS;
}
ipc_result_t
ipc_client_send_message(ipc_connection_t *ipc_c, void *message, size_t size)
{
return ipc_client_send_and_get_reply(ipc_c, message, size, message,
size);
}

View file

@ -0,0 +1,39 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief IPC documentation.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc
*/
#pragma once
/*!
* @defgroup ipc Inter-Process Communication
* @ingroup xrt
*
* @brief Inter-Process Communication layer for Monado.
*/
/*!
* @defgroup ipc_client Client IPC
* @ingroup ipc
*
* @brief Client side IPC code.
*/
/*!
* @defgroup ipc_server Server IPC
* @ingroup ipc
*
* @brief Server side IPC code.
*/
/*!
* @dir xrt/ipc
* @ingroup xrt
*
* @brief Inter-Process Communication layer
*/

192
src/xrt/ipc/ipc_protocol.h Normal file
View file

@ -0,0 +1,192 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Common protocol definition.
* @author Pete Black <pblack@collabora.com>
* @ingroup ipc
*/
#pragma once
#include "xrt/xrt_device.h"
#include "xrt/xrt_compiler.h"
#define IPC_MSG_SOCK_FILE "/tmp/monado_comp_ipc"
#define IPC_MAX_SWAPCHAIN_FDS 8
#define IPC_CRED_SIZE 1 // auth not implemented
#define IPC_BUF_SIZE 512 // must be >= largest message length in bytes
#define IPC_MAX_VIEWS 8 // max views we will return configs for
#define IPC_MAX_FORMATS 32 // max formats our server-side compositor supports
#define IPC_MAX_DEVICES 8 // max number of devices we will map via shared mem
#define IPC_MAX_LAYERS 16
#define IPC_MAX_SLOTS 3
#define IPC_SHARED_MAX_DEVICES 8
#define IPC_SHARED_MAX_INPUTS 1024
#define IPC_SHARED_MAX_OUTPUTS 128
/*
*
* Shared memory structs.
*
*/
struct ipc_shared_device
{
//! Enum identifier of the device.
enum xrt_device_name name;
//! A string describing the device.
char str[XRT_DEVICE_NAME_LEN];
//! Number of inputs.
uint32_t num_inputs;
//! 'Offset' into the array of inputs where this devices inputs starts.
uint32_t first_input_index;
//! Number of outputs.
uint32_t num_outputs;
//! 'Offset' into the array of outputs where this devices outputs
//! starts.
uint32_t first_output_index;
};
struct ipc_layer_stereo_projection
{
uint64_t timestamp;
uint32_t xdev_id;
enum xrt_input_name name;
#if 0 /* LAYERS */
enum xrt_layer_composition_flags layer_flags;
#endif
struct
{
uint32_t swapchain_id;
uint32_t image_index;
#if 0 /* LAYERS */
struct xrt_rect rect;
#endif
uint32_t array_index;
struct xrt_fov fov;
struct xrt_pose pose;
} l, r;
};
struct ipc_layer_quad
{
uint64_t timestamp;
uint32_t xdev_id;
enum xrt_input_name name;
#if 0 /* LAYERS */
enum xrt_layer_composition_flags layer_flags;
#endif
uint32_t swapchain_id;
uint32_t image_index;
#if 0 /* LAYERS */
struct xrt_rect rect;
#endif
uint32_t array_index;
struct xrt_pose pose;
struct xrt_vec2 size;
};
enum ipc_layer_type
{
IPC_LAYER_STEREO_PROJECTION,
IPC_LAYER_QUAD,
};
struct ipc_layer_entry
{
enum ipc_layer_type type;
union {
struct ipc_layer_quad quad;
struct ipc_layer_stereo_projection stereo;
};
};
struct ipc_layer_slot
{
enum xrt_blend_mode env_blend_mode;
uint32_t num_layers;
struct ipc_layer_entry layers[IPC_MAX_LAYERS];
};
/*!
* A big struct that contains all data that is shared to a client, no pointers
* allowed in this. To get the inputs of a device you go:
*
* ```C++
* struct xrt_input *
* helper(struct ipc_shared_memory *ism, uin32_t device_id, size_t input)
* {
* size_t index = ism->idevs[device_id]->first_input_index + input;
* return &ism->inputs[index];
* }
* ```
*/
struct ipc_shared_memory
{
size_t num_idevs;
struct ipc_shared_device idevs[IPC_SHARED_MAX_DEVICES];
struct
{
struct
{
/*!
* Pixel properties of this display, not in absolute
* screen coordinates that the compositor sees. So
* before any rotation is applied by xrt_view::rot.
*
* The xrt_view::display::w_pixels &
* xrt_view::display::h_pixels become the recommdnded
* image size for this view.
*/
struct
{
uint32_t w_pixels;
uint32_t h_pixels;
} display;
/*!
* Fov expressed in OpenXR.
*/
struct xrt_fov fov;
} views[2];
} hmd;
struct xrt_input inputs[IPC_SHARED_MAX_INPUTS];
struct xrt_output outputs[IPC_SHARED_MAX_OUTPUTS];
struct ipc_layer_slot slots[IPC_MAX_SLOTS];
};
/*
*
* Enums
*
*/
typedef enum ipc_result
{
IPC_SUCCESS = 0,
IPC_FAILURE,
} ipc_result_t;
/*
*
* Reset of protocol is generated.
*
*/
#include "ipc_protocol_generated.h"

188
src/xrt/ipc/ipc_server.h Normal file
View file

@ -0,0 +1,188 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Common server side code.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_server
*/
#pragma once
#include "xrt/xrt_compiler.h"
#include "os/os_threading.h"
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
*
* Logging
*
*/
/*!
* Spew level logging.
*/
#define IPC_SPEW(c, ...) \
do { \
if (c->print_spew) { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (false)
/*!
* Debug level logging.
*/
#define IPC_DEBUG(c, ...) \
do { \
if (c->print_debug) { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (false)
/*
*
* Structs
*
*/
#define IPC_SERVER_NUM_XDEVS 8
#define IPC_MAX_CLIENT_SWAPCHAINS 8
#define IPC_MAX_CLIENTS 8
struct xrt_instance;
struct xrt_compositor;
struct xrt_compositor_fd;
/*!
* Information about a single swapchain.
*
* @ingroup ipc_server
*/
struct ipc_swapchain_data
{
uint32_t width;
uint32_t height;
uint64_t format;
uint32_t num_images;
};
/*!
* Render state for a client.
*
* @ingroup ipc_server
*/
struct ipc_render_state
{
bool rendering;
uint32_t l_render_index;
uint32_t r_render_index;
};
/*!
* Holds the state for a single client.
*
* @ingroup ipc_server
*/
struct ipc_client_state
{
//! Link back to the main server.
struct ipc_server *server;
//! Compositor for this client.
struct xrt_compositor *xc;
//! Number of swapchains in use by client
uint32_t num_swapchains;
//! Handles for dealing with swapchains via ipc
uint32_t swapchain_handles[IPC_MAX_CLIENT_SWAPCHAINS];
//! Ptrs to the swapchains
struct xrt_swapchain *xscs[IPC_MAX_CLIENT_SWAPCHAINS];
//! Data for the swapchains.
struct ipc_swapchain_data swapchain_data[IPC_MAX_CLIENT_SWAPCHAINS];
//! Socket fd used for client comms
int ipc_socket_fd;
//! State for rendering.
struct ipc_render_state render_state;
bool active;
};
/*!
* Main IPC object for the server.
*
* @ingroup ipc_server
*/
struct ipc_server
{
struct xrt_instance *xinst;
struct xrt_compositor *xc;
struct xrt_compositor_fd *xcfd;
struct xrt_device *xdevs[IPC_SERVER_NUM_XDEVS];
struct ipc_shared_memory *ism;
int ism_fd;
//! Socket that we accept connections on.
int listen_socket;
//! For waiting on various events in the main thread.
int epoll_fd;
// Is the mainloop supposed to run.
volatile bool running;
// Should we exit when a client disconnects.
bool exit_on_disconnect;
bool print_debug;
bool print_spew;
// Hack for now.
struct os_thread thread;
volatile bool thread_started;
volatile bool thread_stopping;
volatile struct ipc_client_state thread_state;
};
/*!
* Main entrypoint to the compositor process.
*
* @ingroup ipc_server
*/
int
ipc_server_main(int argc, char **argv);
/*!
* Thread function for the client side dispatching.
*
* @ingroup ipc_server
*/
void *
ipc_server_client_thread(void *_cs);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,447 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Common server side code.
* @author Pete Black <pblack@collabora.com>
* @ingroup ipc_server
*/
#include "xrt/xrt_gfx_fd.h"
#include "util/u_misc.h"
#include "ipc_server.h"
#include "ipc_server_utils.h"
#include "ipc_server_generated.h"
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <sys/epoll.h>
/*
*
* Handle functions.
*
*/
ipc_result_t
ipc_handle_instance_get_shm_fd(volatile struct ipc_client_state *cs,
size_t max_num_fds,
int *out_fds,
size_t *out_num_fds)
{
assert(max_num_fds >= 1);
out_fds[0] = cs->server->ism_fd;
*out_num_fds = 1;
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_session_begin(volatile struct ipc_client_state *cs)
{
cs->active = true;
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_session_end(volatile struct ipc_client_state *cs)
{
cs->active = false;
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_compositor_get_formats(volatile struct ipc_client_state *cs,
struct ipc_formats_info *out_info)
{
out_info->num_formats = cs->xc->num_formats;
for (size_t i = 0; i < cs->xc->num_formats; i++) {
out_info->formats[i] = cs->xc->formats[i];
}
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_compositor_wait_frame(volatile struct ipc_client_state *cs,
uint64_t *out_predicted_display_time,
uint64_t *out_predicted_display_period)
{
xrt_comp_wait_frame(cs->xc, out_predicted_display_time,
out_predicted_display_period);
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_compositor_begin_frame(volatile struct ipc_client_state *cs)
{
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_compositor_discard_frame(volatile struct ipc_client_state *cs)
{
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *cs,
uint32_t slot_id,
uint32_t *out_free_slot_id)
{
struct ipc_shared_memory *ism = cs->server->ism;
struct ipc_layer_slot *slot = &ism->slots[slot_id];
struct ipc_layer_stereo_projection *stereo = &slot->layers[0].stereo;
// TODO: this is hardcoded for now
cs->render_state.l_render_index = stereo->l.image_index;
// TODO: this is hardcoded for now
cs->render_state.r_render_index = stereo->l.image_index;
// printf("SERVER: sending compositor end frame response\n");
cs->render_state.rendering = true;
*out_free_slot_id = (slot_id + 1) % IPC_MAX_SLOTS;
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_swapchain_create(volatile struct ipc_client_state *cs,
uint32_t width,
uint32_t height,
uint64_t format,
uint32_t *out_id,
uint32_t *out_num_images,
uint64_t *out_size,
size_t max_num_fds,
int *out_fds,
size_t *out_num_fds)
{
enum xrt_swapchain_create_flags flags =
XRT_SWAPCHAIN_CREATE_STATIC_IMAGE;
enum xrt_swapchain_usage_bits usage =
XRT_SWAPCHAIN_CREATE_STATIC_IMAGE | XRT_SWAPCHAIN_USAGE_COLOR;
uint32_t sample_count = 1;
uint32_t face_count = 1;
uint32_t array_size = 1;
uint32_t mip_count = 1;
// create the swapchain
struct xrt_swapchain *xsc =
xrt_comp_create_swapchain(cs->xc, // Compositor
flags, // Flags
usage, // Usage
format, // Format
sample_count, // Sample count
width, // Width
height, // Height
face_count, // Face count
array_size, // Array size
mip_count); // Mip count
uint32_t index = cs->num_swapchains;
uint32_t num_images = xsc->num_images;
// Our handle is just the index for now
uint32_t handle = index;
cs->xscs[index] = xsc;
cs->swapchain_data[index].width = width;
cs->swapchain_data[index].height = height;
cs->swapchain_data[index].format = format;
cs->swapchain_data[index].num_images = num_images;
cs->swapchain_handles[index] = handle;
// It's now safe to increment the number of swapchains.
cs->num_swapchains++;
// return our result to the caller.
struct xrt_swapchain_fd *xcsfd = (struct xrt_swapchain_fd *)xsc;
// Sanity checking.
assert(num_images <= IPC_MAX_SWAPCHAIN_FDS);
assert(num_images <= max_num_fds);
*out_id = handle;
*out_size = xcsfd->images[0].size;
*out_num_images = num_images;
// Setup the fds.
*out_num_fds = num_images;
for (size_t i = 0; i < num_images; i++) {
out_fds[i] = xcsfd->images[i].fd;
}
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_swapchain_wait_image(volatile struct ipc_client_state *cs,
uint32_t id,
uint64_t timeout,
uint32_t index)
{
//! @todo Look up the index.
uint32_t sc_index = id;
struct xrt_swapchain *xsc = cs->xscs[sc_index];
xrt_swapchain_wait_image(xsc, timeout, index);
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_swapchain_acquire_image(volatile struct ipc_client_state *cs,
uint32_t id,
uint32_t *out_index)
{
//! @todo Look up the index.
uint32_t sc_index = id;
struct xrt_swapchain *xsc = cs->xscs[sc_index];
xrt_swapchain_acquire_image(xsc, out_index);
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_swapchain_release_image(volatile struct ipc_client_state *cs,
uint32_t id,
uint32_t index)
{
//! @todo Look up the index.
uint32_t sc_index = id;
struct xrt_swapchain *xsc = cs->xscs[sc_index];
xrt_swapchain_release_image(xsc, index);
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_swapchain_destroy(volatile struct ipc_client_state *cs, uint32_t id)
{
//! @todo Implement destroy swapchain.
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_device_update_input(volatile struct ipc_client_state *cs,
uint32_t id)
{
// To make the code a bit more readable.
uint32_t device_id = id;
struct ipc_shared_memory *ism = cs->server->ism;
struct xrt_device *xdev = cs->server->xdevs[device_id];
struct ipc_shared_device *idev = &ism->idevs[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[idev->first_input_index];
memcpy(dst, src, sizeof(struct xrt_input) * idev->num_inputs);
// Reply.
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_device_get_tracked_pose(volatile struct ipc_client_state *cs,
uint32_t id,
enum xrt_input_name name,
uint64_t at_timestamp,
uint64_t *out_timestamp,
struct xrt_space_relation *out_relation)
{
// To make the code a bit more readable.
uint32_t device_id = id;
struct xrt_device *xdev = cs->server->xdevs[device_id];
// Get the pose.
xrt_device_get_tracked_pose(xdev, name, at_timestamp, out_timestamp,
out_relation);
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_device_get_view_pose(volatile struct ipc_client_state *cs,
uint32_t id,
struct xrt_vec3 *eye_relation,
uint32_t view_index,
struct xrt_pose *out_pose)
{
// To make the code a bit more readable.
uint32_t device_id = id;
struct xrt_device *xdev = cs->server->xdevs[device_id];
// Get the pose.
xrt_device_get_view_pose(xdev, eye_relation, view_index, out_pose);
return IPC_SUCCESS;
}
ipc_result_t
ipc_handle_device_set_output(volatile struct ipc_client_state *cs,
uint32_t id,
enum xrt_output_name name,
union xrt_output_value *value)
{
// To make the code a bit more readable.
uint32_t device_id = id;
struct xrt_device *xdev = cs->server->xdevs[device_id];
// Set the output.
xrt_device_set_output(xdev, name, value);
return IPC_SUCCESS;
}
/*
*
* Helper functions.
*
*/
static int
setup_epoll(int listen_socket)
{
int ret = epoll_create1(EPOLL_CLOEXEC);
if (ret < 0) {
return ret;
}
int epoll_fd = ret;
struct epoll_event ev = {0};
ev.events = EPOLLIN;
ev.data.fd = listen_socket;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket, &ev);
if (ret < 0) {
fprintf(stderr, "ERROR: epoll_ctl(listen_socket) failed '%i'\n",
ret);
return ret;
}
return epoll_fd;
}
/*
*
* Client loop.
*
*/
static void
client_loop(volatile struct ipc_client_state *cs)
{
fprintf(stderr, "SERVER: Client connected\n");
// Claim the client fd.
int epoll_fd = setup_epoll(cs->ipc_socket_fd);
if (epoll_fd < 0) {
return;
}
uint8_t buf[IPC_BUF_SIZE];
while (cs->server->running) {
const int half_a_second_ms = 500;
struct epoll_event event = {0};
// We use epoll here to be able to timeout.
int ret = epoll_wait(epoll_fd, &event, 1, half_a_second_ms);
if (ret < 0) {
fprintf(stderr, "ERROR: Failed epoll_wait '%i'\n", ret);
break;
}
// Timed out, loop again.
if (ret == 0) {
continue;
}
// Detect clients disconnecting gracefully.
if (ret > 0 && (event.events & EPOLLHUP) != 0) {
break;
}
// Finally get the data that is waiting for us.
ssize_t len = recv(cs->ipc_socket_fd, &buf, IPC_BUF_SIZE, 0);
if (len < 4) {
fprintf(stderr, "ERROR: Invalid packet received\n");
break;
}
// Check the first 4 bytes of the message and dispatch.
ipc_command_t *ipc_command = (uint32_t *)buf;
ret = ipc_dispatch(cs, ipc_command);
if (ret < 0) {
fprintf(stderr, "ERROR: Failed to dispatch packet\n");
break;
}
}
fprintf(stderr, "SERVER: Client disconnected\n");
close(epoll_fd);
epoll_fd = -1;
close(cs->ipc_socket_fd);
cs->ipc_socket_fd = -1;
cs->active = false;
cs->num_swapchains = 0;
for (uint32_t j = 0; j < IPC_MAX_CLIENT_SWAPCHAINS; j++) {
xrt_swapchain_destroy((struct xrt_swapchain **)&cs->xscs[j]);
cs->swapchain_handles[j] = -1;
}
// Should we stop the server when a client disconnects?
if (cs->server->exit_on_disconnect) {
cs->server->running = false;
}
}
/*
*
* Entry point.
*
*/
void *
ipc_server_client_thread(void *_cs)
{
volatile struct ipc_client_state *cs = _cs;
client_loop(cs);
cs->server->thread_stopping = true;
cs->server->thread_started = false;
return NULL;
}

View file

@ -0,0 +1,490 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Server process functions.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_server
*/
#include "ipc_server.h"
#include "xrt/xrt_device.h"
#include "xrt/xrt_instance.h"
#include "xrt/xrt_compositor.h"
#include "util/u_var.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "ipc_server_utils.h"
#include "main/comp_compositor.h"
#include "main/comp_renderer.h"
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
/*
*
* Defines and helpers.
*
*/
#define IPC_MAX_CLIENTS 8
DEBUG_GET_ONCE_BOOL_OPTION(exit_on_disconnect, "IPC_EXIT_ON_DISCONNECT", false)
/*
*
* Static functions.
*
*/
static void
teardown_all(struct ipc_server *s)
{
u_var_remove_root(s);
xrt_comp_destroy(&s->xc);
for (size_t i = 0; i < IPC_SERVER_NUM_XDEVS; i++) {
xrt_device_destroy(&s->xdevs[i]);
}
xrt_instance_destroy(&s->xinst);
if (s->listen_socket > 0) {
close(s->listen_socket);
s->listen_socket = -1;
}
}
static int
init_shm(struct ipc_server *s)
{
const size_t size = sizeof(struct ipc_shared_memory);
int fd = shm_open("/monado_shm", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
return -1;
}
if (ftruncate(fd, size) < 0) {
close(fd);
return -1;
}
const int access = PROT_READ | PROT_WRITE;
const int flags = MAP_SHARED;
s->ism = mmap(NULL, size, access, flags, fd, 0);
if (s->ism == NULL) {
close(fd);
return -1;
}
// we have a filehandle, we will pass this to
// our client rather than access via filesystem
shm_unlink("/monado_shm");
s->ism_fd = fd;
/*
*
* Setup the shared memory state.
*
*/
uint32_t input_index = 0;
uint32_t output_index = 0;
uint32_t count = 0;
struct ipc_shared_memory *ism = s->ism;
for (size_t i = 0; i < IPC_SERVER_NUM_XDEVS; i++) {
struct xrt_device *xdev = s->xdevs[i];
if (xdev == NULL) {
continue;
}
struct ipc_shared_device *idev = &ism->idevs[count++];
idev->name = xdev->name;
memcpy(idev->str, xdev->str, sizeof(idev->str));
// Is this a HMD?
if (xdev->hmd != NULL) {
ism->hmd.views[0].display.w_pixels =
xdev->hmd->views[0].display.w_pixels;
ism->hmd.views[0].display.h_pixels =
xdev->hmd->views[0].display.h_pixels;
ism->hmd.views[0].fov = xdev->hmd->views[0].fov;
ism->hmd.views[1].display.w_pixels =
xdev->hmd->views[1].display.w_pixels;
ism->hmd.views[1].display.h_pixels =
xdev->hmd->views[1].display.h_pixels;
ism->hmd.views[1].fov = xdev->hmd->views[1].fov;
}
// Initial update.
xrt_device_update_inputs(xdev);
// Copy the initial state and also count the number in inputs.
size_t input_start = input_index;
for (size_t k = 0; k < xdev->num_inputs; k++) {
ism->inputs[input_index++] = xdev->inputs[k];
}
// Setup the 'offsets' and number of inputs.
if (input_start != input_index) {
idev->num_inputs = input_index - input_start;
idev->first_input_index = input_start;
}
// Copy the initial state and also count the number in outputs.
size_t output_start = output_index;
for (size_t k = 0; k < xdev->num_outputs; k++) {
ism->outputs[output_index++] = xdev->outputs[k];
}
// Setup the 'offsets' and number of outputs.
if (output_start != output_index) {
idev->num_outputs = output_index - output_start;
idev->first_output_index = output_start;
}
}
// Finally tell the client how many devices we have.
s->ism->num_idevs = count;
return 0;
}
static int
init_listen_socket(struct ipc_server *s)
{
struct sockaddr_un addr;
int fd, ret;
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr, "Message Socket Create Error!\n");
return fd;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, IPC_MSG_SOCK_FILE);
unlink(IPC_MSG_SOCK_FILE);
ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
close(fd);
return ret;
}
ret = listen(fd, IPC_MAX_CLIENTS);
if (ret < 0) {
close(fd);
return ret;
}
// All ok!
s->listen_socket = fd;
return fd;
}
static int
init_epoll(struct ipc_server *s)
{
int ret = epoll_create1(EPOLL_CLOEXEC);
if (ret < 0) {
return ret;
}
s->epoll_fd = ret;
struct epoll_event ev = {0};
ev.events = EPOLLIN;
ev.data.fd = 0; // stdin
ret = epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, 0, &ev);
if (ret < 0) {
fprintf(stderr, "ERROR: epoll_ctl(stdin) failed '%i'\n", ret);
return ret;
}
ev.events = EPOLLIN;
ev.data.fd = s->listen_socket;
ret = epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->listen_socket, &ev);
if (ret < 0) {
fprintf(stderr, "ERROR: epoll_ctl(listen_socket) failed '%i'\n",
ret);
return ret;
}
return 0;
}
static int
init_all(struct ipc_server *s)
{
// Yes we should be running.
s->running = true;
s->exit_on_disconnect = debug_get_bool_option_exit_on_disconnect();
int ret = xrt_instance_create(&s->xinst);
if (ret < 0) {
teardown_all(s);
return ret;
}
ret = xrt_instance_select(s->xinst, s->xdevs, IPC_SERVER_NUM_XDEVS);
if (ret < 0) {
teardown_all(s);
return ret;
}
if (s->xdevs[0] == NULL) {
teardown_all(s);
return -1;
}
ret = xrt_instance_create_fd_compositor(s->xinst, s->xdevs[0], false,
&s->xcfd);
if (ret < 0) {
teardown_all(s);
return ret;
}
ret = init_shm(s);
if (ret < 0) {
teardown_all(s);
return ret;
}
ret = init_listen_socket(s);
if (ret < 0) {
teardown_all(s);
return ret;
}
ret = init_epoll(s);
if (ret < 0) {
teardown_all(s);
return ret;
}
// Easier to use.
s->xc = &s->xcfd->base;
u_var_add_root(s, "IPC Server", false);
u_var_add_bool(s, &s->print_debug, "print.debug");
u_var_add_bool(s, &s->print_spew, "print.spew");
u_var_add_bool(s, &s->exit_on_disconnect, "exit_on_disconnect");
u_var_add_bool(s, (void *)&s->running, "running");
return 0;
}
static void
handle_listen(struct ipc_server *vs)
{
int ret = accept(vs->listen_socket, NULL, NULL);
if (ret < 0) {
fprintf(stderr, "ERROR: accept '%i'\n", ret);
vs->running = false;
}
volatile struct ipc_client_state *cs = &vs->thread_state;
// The return is the new fd;
int fd = ret;
if (vs->thread_started && !vs->thread_stopping) {
fprintf(stderr, "ERROR: Client already connected!\n");
close(fd);
return;
}
if (vs->thread_stopping) {
os_thread_join((struct os_thread *)&vs->thread);
os_thread_destroy((struct os_thread *)&vs->thread);
vs->thread_stopping = false;
}
vs->thread_started = true;
cs->ipc_socket_fd = fd;
os_thread_start((struct os_thread *)&vs->thread,
ipc_server_client_thread, (void *)cs);
}
#define NUM_POLL_EVENTS 8
#define NO_SLEEP 0
static void
check_epoll(struct ipc_server *vs)
{
int epoll_fd = vs->epoll_fd;
struct epoll_event events[NUM_POLL_EVENTS] = {0};
// No sleeping, returns immediately.
int ret = epoll_wait(epoll_fd, events, NUM_POLL_EVENTS, NO_SLEEP);
if (ret < 0) {
fprintf(stderr, "EPOLL ERROR! \"%i\"\n", ret);
vs->running = false;
return;
}
for (int i = 0; i < ret; i++) {
// If we get data on stdin, stop.
if (events[i].data.fd == 0) {
vs->running = false;
return;
}
// Somebody new at the door.
if (events[i].data.fd == vs->listen_socket) {
handle_listen(vs);
}
}
}
static int
main_loop(struct ipc_server *vs)
{
struct xrt_compositor *xc = vs->xc;
struct comp_compositor *c = comp_compositor(xc);
// make sure all our client connections have a handle to the compositor
// and consistent initial state
vs->thread_state.server = vs;
vs->thread_state.xc = xc;
struct comp_swapchain_image *last_l = NULL;
struct comp_swapchain_image *last_r = NULL;
bool using_idle_images = true;
while (vs->running) {
/*
* Check polling.
*/
check_epoll(vs);
/*
* Update active client.
*/
volatile struct ipc_client_state *active_client = NULL;
if (vs->thread_state.active) {
active_client = &vs->thread_state;
}
/*
* Render the swapchains.
*/
struct comp_swapchain_image *l = NULL;
struct comp_swapchain_image *r = NULL;
if (active_client == NULL || !active_client->active ||
active_client->num_swapchains == 0) {
if (!using_idle_images) {
COMP_DEBUG(c, "Resetting to idle images.");
comp_renderer_set_idle_images(c->r);
using_idle_images = true;
last_r = NULL;
last_l = NULL;
}
} else {
// our ipc server thread will fill in l & r
// swapchain indices and toggle wait to false
// when the client calls end_frame, signalling
// us to render.
volatile struct ipc_render_state *render_state =
&active_client->render_state;
if (render_state->rendering) {
struct comp_swapchain *cl =
comp_swapchain(active_client->xscs[0]);
struct comp_swapchain *cr =
comp_swapchain(active_client->xscs[1]);
l = &cl->images[render_state->l_render_index];
r = &cr->images[render_state->r_render_index];
// set our client state back to waiting.
render_state->rendering = false;
comp_compositor_garbage_collect(c);
using_idle_images = false;
}
}
// Rendering idle images
if (r == NULL || l == NULL) {
comp_renderer_frame_cached(c->r);
comp_compositor_garbage_collect(c);
continue;
}
// Rebuild command buffers if we are showing new buffers.
if (last_r != r || last_l != l) {
comp_renderer_reset(c->r);
}
last_r = r;
last_l = l;
comp_renderer_frame(c->r, r, 0, l, 0);
// Now is a good time to destroy objects.
comp_compositor_garbage_collect(c);
}
return 0;
}
/*
*
* Exported functions.
*
*/
int
ipc_server_main(int argc, char **argv)
{
struct ipc_server *s = U_TYPED_CALLOC(struct ipc_server);
int ret = init_all(s);
if (ret < 0) {
free(s);
return ret;
}
ret = main_loop(s);
teardown_all(s);
free(s);
return ret;
}

View file

@ -0,0 +1,85 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Server helper functions.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_server
*/
#include "ipc_server_utils.h"
#include "util/u_misc.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
/*
*
* Actual senders
*
*/
int
ipc_reply(int socket, void *data, size_t len)
{
struct msghdr msg = {0};
struct iovec iov = {0};
iov.iov_base = data;
iov.iov_len = len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
ssize_t ret = sendmsg(socket, &msg, 0);
if (ret < 0) {
printf(
"sending plain message on socket %d failed with error: "
"%s\n",
socket, strerror(errno));
}
return ret;
}
int
ipc_reply_fds(int socket, void *data, size_t size, int *fds, uint32_t num_fds)
{
uint8_t cmsgbuf[CMSG_SPACE(sizeof(int) * num_fds)];
struct iovec iov = {0};
iov.iov_base = data;
iov.iov_len = size;
struct msghdr msg = {0};
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_LEN(sizeof(int) * num_fds);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
memcpy(CMSG_DATA(cmsg), fds, num_fds * sizeof(int));
ssize_t ret = sendmsg(socket, &msg, 0);
if (ret < 0) {
printf("sending %d FDs on socket %d failed with error: %s\n",
num_fds, socket, strerror(errno));
}
return ret;
}

View file

@ -0,0 +1,18 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Server util helpers.
* @author Pete Black <pblack@collabora.com>
* @ingroup ipc_server
*/
#pragma once
#include "ipc_protocol.h"
int
ipc_reply(int socket, void *data, size_t len);
int
ipc_reply_fds(int socket, void *data, size_t size, int *fds, uint32_t num_fds);

66
src/xrt/ipc/meson.build Normal file
View file

@ -0,0 +1,66 @@
# Copyright 2020, Collabora, Ltd.
# SPDX-License-Identifier: BSL-1.0
###
# Generated
prog_python = import('python').find_installation('python3')
generated = custom_target('protocol code',
command: [prog_python, '@INPUT@', '@OUTPUT@'],
input: ['proto.py', 'proto.json'],
output: [
'ipc_protocol_generated.h',
'ipc_client_generated.c',
'ipc_client_generated.h',
'ipc_server_generated.c',
'ipc_server_generated.h',
]
)
###
# Client
lib_ipc_client = static_library(
'ipc_client',
[
generated[0],
generated[1],
generated[2],
'ipc_client.h',
'ipc_client_compositor.c',
'ipc_client_device.c',
'ipc_client_hmd.c',
'ipc_client_instance.c',
'ipc_client_utils.c',
],
include_directories: [
xrt_include,
],
dependencies: [aux]
)
###
# Server
lib_ipc_server = static_library(
'ipc_server',
[
generated[0],
generated[3],
generated[4],
'ipc_server.h',
'ipc_server_client.c',
'ipc_server_process.c',
'ipc_server_utils.c',
'ipc_server_utils.h',
],
include_directories: [
xrt_include,
comp_include,
],
dependencies: [aux_util, rt, aux_vk, aux_ogl]
)

116
src/xrt/ipc/proto.json Normal file
View file

@ -0,0 +1,116 @@
{
"instance_get_shm_fd": {
"out_fds": true
},
"session_begin": {},
"session_end": {},
"compositor_get_formats": {
"out": [
{"name": "info", "type": "struct ipc_formats_info"}
]
},
"compositor_wait_frame": {
"out": [
{"name": "predicted_display_time", "type": "uint64_t"},
{"name": "predicted_display_period", "type": "uint64_t"}
]
},
"compositor_begin_frame": {},
"compositor_discard_frame": {},
"compositor_layer_sync": {
"in": [
{"name": "slot_id", "type": "uint32_t"}
],
"out": [
{"name": "free_slot_id", "type": "uint32_t"}
]
},
"swapchain_create": {
"in": [
{"name": "width", "type": "uint32_t"},
{"name": "height", "type": "uint32_t"},
{"name": "format", "type": "uint64_t"}
],
"out": [
{"name": "id", "type": "uint32_t"},
{"name": "num_images", "type": "uint32_t"},
{"name": "size", "type": "uint64_t"}
],
"out_fds": true
},
"swapchain_wait_image": {
"in": [
{"name": "id", "type": "uint32_t"},
{"name": "timeout", "type": "uint64_t"},
{"name": "index", "type": "uint32_t"}
]
},
"swapchain_acquire_image": {
"in": [
{"name": "id", "type": "uint32_t"}
],
"out": [
{"name": "index", "type": "uint32_t"}
]
},
"swapchain_release_image": {
"in": [
{"name": "id", "type": "uint32_t"},
{"name": "index", "type": "uint32_t"}
]
},
"swapchain_destroy": {
"in": [
{"name": "id", "type": "uint32_t"}
]
},
"device_update_input": {
"in": [
{"name": "id", "type": "uint32_t"}
]
},
"device_get_tracked_pose": {
"in": [
{"name": "id", "type": "uint32_t"},
{"name": "name", "type": "enum xrt_input_name"},
{"name": "at_timestamp", "type": "uint64_t"}
],
"out": [
{"name": "timestamp", "type": "uint64_t"},
{"name": "relation", "type": "struct xrt_space_relation"}
]
},
"device_get_view_pose": {
"in": [
{"name": "id", "type": "uint32_t"},
{"name": "eye_relation", "type": "struct xrt_vec3"},
{"name": "view_index", "type": "uint32_t"}
],
"out": [
{"name": "pose", "type": "struct xrt_pose"}
]
},
"device_set_output": {
"in": [
{"name": "id", "type": "uint32_t"},
{"name": "name", "type": "enum xrt_output_name"},
{"name": "value", "type": "union xrt_output_value"}
]
}
}

394
src/xrt/ipc/proto.py Executable file
View file

@ -0,0 +1,394 @@
#!/usr/bin/env python3
# Copyright 2020, Collabora, Ltd.
# SPDX-License-Identifier: BSL-1.0
import json
import argparse
class Arg:
isAggregate = False
name = ''
type = ''
def parseArray(array):
ret = []
for elm in array:
ret.append(Arg(elm))
return ret
def getFuncArgumentIn(self):
if self.isAggregate:
return self.type + " *" + self.name
else:
return self.type + " " + self.name
def getFuncArgumentOut(self):
return self.type + " *out_" + self.name
def getStructField(self):
return self.type + " " + self.name
def dump(self):
print("\t\t" + self.type + ": " + self.name)
def __init__(self, data):
self.name = data['name']
self.type = data['type']
if self.type.find("struct ") == 0:
self.isAggregate = True
if self.type.find("union ") == 0:
self.isAggregate = True
class Call:
id = None
name = ''
inArgs = []
outArgs = []
outFds = False
def dump(self):
print("Call " + self.name)
if self.inArgs:
print("\tIn:")
for arg in self.inArgs:
arg.dump()
if self.outArgs:
print("\tOut:")
for arg in self.outArgs:
arg.dump()
def writeCallDecl(self, f):
f.write("\nipc_result_t\n")
start = "ipc_call_" + self.name + "("
pad = ""
for c in start:
pad = pad + " "
f.write(start + "struct ipc_connection *ipc_c")
for arg in self.inArgs:
f.write(",\n" + pad + arg.getFuncArgumentIn())
for arg in self.outArgs:
f.write(",\n" + pad + arg.getFuncArgumentOut())
if self.outFds:
f.write(",\n" + pad + "int *fds")
f.write(",\n" + pad + "size_t num_fds")
f.write(")")
def writeHandleDecl(self, f):
f.write("\nipc_result_t\n")
start = "ipc_handle_" + self.name + "("
pad = ""
for c in start:
pad = pad + " "
f.write(start + "volatile struct ipc_client_state *cs")
for arg in self.inArgs:
f.write(",\n" + pad + arg.getFuncArgumentIn())
for arg in self.outArgs:
f.write(",\n" + pad + arg.getFuncArgumentOut())
if self.outFds:
f.write(",\n" + pad + "size_t max_num_fds")
f.write(",\n" + pad + "int *out_fds")
f.write(",\n" + pad + "size_t *out_num_fds")
f.write(")")
def __init__(self, name, data):
self.name = name
for key in data:
if key == 'id':
self.id = data[key]
if key == 'in':
self.inArgs = Arg.parseArray(data[key])
if key == 'out':
self.outArgs = Arg.parseArray(data[key])
if key == 'out_fds':
self.outFds = data[key]
if not self.id:
self.id = "IPC_" + name.upper()
class Proto:
calls = []
def parse(data):
return Proto(data)
def loadAndParse(file):
with open(file) as infile:
return Proto.parse(json.loads(infile.read()))
def dump(self):
for call in self.calls:
call.dump()
def addCall(self, name, data):
self.calls.append(Call(name, data))
def __init__(self, data):
for name in data:
call = data[name]
self.addCall(name, call)
header = '''// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief {brief}.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc{suffix}
*/
'''
def doH(file):
f = open(file, "w")
f.write(header.format(brief='Generated IPC protocol header', suffix=''))
f.write('''
#pragma once
// clang-format off
struct ipc_connection;
''')
f.write('''
typedef enum ipc_command
{
\tIPC_ERR = 0,''')
for call in p.calls:
f.write("\n\t" + call.id + ",")
f.write("\n} ipc_command_t;\n")
f.write('''
struct ipc_command_msg
{
\tenum ipc_command cmd;
};
struct ipc_result_reply
{
\tipc_result_t result;
};
struct ipc_formats_info
{
\tuint64_t formats[IPC_MAX_FORMATS];
\tuint32_t num_formats;
};
''')
f.write('''
static inline const char *
ipc_cmd_to_str(ipc_command_t id)
{
\tswitch (id) {
\tcase IPC_ERR: return "IPC_ERR";''')
for call in p.calls:
f.write("\n\tcase " + call.id + ": return \"" + call.id + "\";")
f.write("\n\tdefault: return \"IPC_UNKNOWN\";")
f.write("\n\t}\n}\n")
for call in p.calls:
# Should we emit a msg struct.
if call.inArgs:
f.write("\nstruct ipc_" + call.name + "_msg\n")
f.write("{\n")
f.write("\tenum ipc_command cmd;\n")
for arg in call.inArgs:
f.write("\t" + arg.getStructField() + ";\n")
f.write("};\n")
# Should we emit a reply struct.
if call.outArgs:
f.write("\nstruct ipc_" + call.name + "_reply\n")
f.write("{\n")
f.write("\tipc_result_t result;\n")
for arg in call.outArgs:
f.write("\t" + arg.getStructField() + ";\n")
f.write("};\n")
f.write("\n// clang-format on\n")
f.close()
def doClientC(file):
f = open(file, "w")
f.write(header.format(brief='Generated IPC client code', suffix='_client'))
f.write('''
#include "ipc_client.h"
// clang-format off
\n''')
# Loop over all of the calls.
for call in p.calls:
call.writeCallDecl(f)
f.write("\n{\n")
# Message struct
if call.inArgs:
f.write("\tstruct ipc_" + call.name + "_msg _msg = {\n")
else:
f.write("\tstruct ipc_command_msg _msg = {\n")
f.write("\t .cmd = " + str(call.id) + ",\n")
for arg in call.inArgs:
if arg.isAggregate:
f.write("\t ." + arg.name + " = *" + arg.name + ",\n")
else:
f.write("\t ." + arg.name + " = " + arg.name + ",\n")
f.write("\t};\n")
# Reply struct
if call.outArgs:
f.write("\tstruct ipc_" + call.name + "_reply _reply;\n")
else:
f.write("\tstruct ipc_result_reply _reply = {0};\n")
f.write('''
\tipc_result_t ret = ipc_client_send_and_get_reply''')
if call.outFds:
f.write('''_fds(
\t ipc_c, &_msg, sizeof(_msg), &_reply, sizeof(_reply), fds, num_fds);''')
else:
f.write('''(
\t ipc_c, &_msg, sizeof(_msg), &_reply, sizeof(_reply));''')
f.write('''
\tif (ret != IPC_SUCCESS) {
\t\treturn ret;
\t}
\n''')
for arg in call.outArgs:
f.write("\t*out_" + arg.name + " = _reply." + arg.name + ";\n")
f.write("\n\treturn _reply.result;\n}\n")
f.write("\n// clang-format off\n")
f.close()
def doClientH(file):
f = open(file, "w")
f.write(header.format(brief='Generated IPC client code', suffix='_client'))
f.write('''
#pragma once
#include "ipc_protocol.h"
#include "ipc_client.h"
// clang-format off
''')
for call in p.calls:
call.writeCallDecl(f)
f.write(";\n")
f.write("\n// clang-format on\n")
f.close()
def doServerC(file):
f = open(file, "w")
f.write(header.format(brief='Generated IPC server code', suffix='_server'))
f.write('''
#include "ipc_protocol.h"
#include "ipc_server.h"
#include "ipc_server_utils.h"
#include "ipc_server_generated.h"
// clang-format off
#define MAX_FDS 16
''')
f.write('''
int
ipc_dispatch(volatile struct ipc_client_state *cs, ipc_command_t *ipc_command)
{
\tswitch (*ipc_command) {
''')
for call in p.calls:
f.write("\tcase " + call.id + ": {\n")
if call.inArgs:
f.write("\t\tstruct ipc_" + call.name + "_msg *msg =\n")
f.write("\t\t (struct ipc_" + call.name + "_msg *)ipc_command;\n")
if call.outArgs:
f.write("\t\tstruct ipc_" + call.name + "_reply reply = {0};\n")
else:
f.write("\t\tstruct ipc_result_reply reply = {0};\n")
if call.outFds:
f.write("\t\tint fds[MAX_FDS] = {0};\n")
f.write("\t\tsize_t num_fds = {0};\n")
f.write("\n")
start = "reply.result = ipc_handle_" + call.name + "("
pad = ""
for c in start:
pad = pad + " "
f.write("\t\t" + start + "cs")
for arg in call.inArgs:
if arg.isAggregate:
f.write(",\n\t\t" + pad + "&msg->" + arg.name)
else:
f.write(",\n\t\t" + pad + "msg->" + arg.name)
for arg in call.outArgs:
f.write(",\n\t\t" + pad + "&reply." + arg.name)
if call.outFds:
f.write(",\n\t\t" + pad + "MAX_FDS")
f.write(",\n\t\t" + pad + "fds")
f.write(",\n\t\t" + pad + "&num_fds")
f.write(");\n")
if call.outFds:
f.write("\t\treturn ipc_reply_fds(cs->ipc_socket_fd, &reply, sizeof(reply), fds, num_fds);\n")
else:
f.write("\t\treturn ipc_reply(cs->ipc_socket_fd, &reply, sizeof(reply));\n")
f.write("\t}\n")
f.write('''\tdefault:
\t\tprintf("UNHANDLED IPC MESSAGE! %d\\n", *ipc_command);
\t\treturn -1;
\t}
}
// clang-format on
''')
f.close()
def doServerH(file):
f = open(file, "w")
f.write(header.format(brief='Generated IPC server code', suffix='_server'))
f.write('''
#pragma once
#include "ipc_protocol.h"
#include "ipc_server.h"
// clang-format off
int
ipc_dispatch(volatile struct ipc_client_state *cs, ipc_command_t *ipc_command);
''')
for call in p.calls:
call.writeHandleDecl(f)
f.write(";\n")
f.write("\n// clang-format on\n")
f.close()
parser = argparse.ArgumentParser(description='Protocol generator.')
parser.add_argument('proto', help='Protocol file to use')
parser.add_argument('output', type=str, nargs='+', help='Output file, uses the ending to figure out what file it should generate')
args = parser.parse_args()
p = Proto.loadAndParse(args.proto)
for output in args.output:
if output.endswith("ipc_protocol_generated.h"):
doH(output)
if output.endswith("ipc_client_generated.c"):
doClientC(output)
if output.endswith("ipc_client_generated.h"):
doClientH(output)
if output.endswith("ipc_server_generated.c"):
doServerC(output)
if output.endswith("ipc_server_generated.h"):
doServerH(output)

View file

@ -8,4 +8,5 @@ subdir('auxiliary')
subdir('drivers')
subdir('compositor')
subdir('state_trackers')
subdir('ipc')
subdir('targets')