mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2024-12-29 11:06:18 +00:00
ipc: Add code to enable a service process
This enables out of process compositing.
This commit is contained in:
parent
5ba751a239
commit
7c8a8a3f87
|
@ -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_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_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_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.
|
# Most users won't touch these.
|
||||||
mark_as_advanced(BUILD_COMPOSITOR_MAIN BUILD_TARGET_OPENXR)
|
mark_as_advanced(BUILD_COMPOSITOR_MAIN BUILD_TARGET_OPENXR)
|
||||||
|
|
|
@ -50,6 +50,7 @@ build_docs = doxygen.found()
|
||||||
glslangValidator = find_program('glslangValidator')
|
glslangValidator = find_program('glslangValidator')
|
||||||
|
|
||||||
pthreads = cc.find_library('pthread', required: true)
|
pthreads = cc.find_library('pthread', required: true)
|
||||||
|
rt = cc.find_library('rt', required: true)
|
||||||
|
|
||||||
avcodec = dependency('libavcodec', required: false)
|
avcodec = dependency('libavcodec', required: false)
|
||||||
egl = dependency('egl', required: get_option('egl'))
|
egl = dependency('egl', required: get_option('egl'))
|
||||||
|
|
|
@ -28,6 +28,7 @@ fi
|
||||||
src/xrt/compositor \
|
src/xrt/compositor \
|
||||||
src/xrt/drivers \
|
src/xrt/drivers \
|
||||||
src/xrt/include \
|
src/xrt/include \
|
||||||
|
src/xrt/ipc \
|
||||||
src/xrt/state_trackers \
|
src/xrt/state_trackers \
|
||||||
src/xrt/targets \
|
src/xrt/targets \
|
||||||
\( -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) \
|
\( -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) \
|
||||||
|
|
|
@ -7,3 +7,7 @@ add_subdirectory(drivers)
|
||||||
add_subdirectory(compositor)
|
add_subdirectory(compositor)
|
||||||
add_subdirectory(state_trackers)
|
add_subdirectory(state_trackers)
|
||||||
add_subdirectory(targets)
|
add_subdirectory(targets)
|
||||||
|
|
||||||
|
if (XRT_BUILD_IPC)
|
||||||
|
add_subdirectory(ipc)
|
||||||
|
endif()
|
||||||
|
|
78
src/xrt/ipc/CMakeLists.txt
Normal file
78
src/xrt/ipc/CMakeLists.txt
Normal 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
122
src/xrt/ipc/ipc_client.h
Normal 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);
|
496
src/xrt/ipc/ipc_client_compositor.c
Normal file
496
src/xrt/ipc/ipc_client_compositor.c
Normal 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;
|
||||||
|
}
|
168
src/xrt/ipc/ipc_client_device.c
Normal file
168
src/xrt/ipc/ipc_client_device.c
Normal 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;
|
||||||
|
}
|
190
src/xrt/ipc/ipc_client_hmd.c
Normal file
190
src/xrt/ipc/ipc_client_hmd.c
Normal 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;
|
||||||
|
}
|
236
src/xrt/ipc/ipc_client_instance.c
Normal file
236
src/xrt/ipc/ipc_client_instance.c
Normal 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;
|
||||||
|
}
|
120
src/xrt/ipc/ipc_client_utils.c
Normal file
120
src/xrt/ipc/ipc_client_utils.c
Normal 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);
|
||||||
|
}
|
39
src/xrt/ipc/ipc_documentation.h
Normal file
39
src/xrt/ipc/ipc_documentation.h
Normal 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
192
src/xrt/ipc/ipc_protocol.h
Normal 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
188
src/xrt/ipc/ipc_server.h
Normal 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
|
447
src/xrt/ipc/ipc_server_client.c
Normal file
447
src/xrt/ipc/ipc_server_client.c
Normal 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;
|
||||||
|
}
|
490
src/xrt/ipc/ipc_server_process.c
Normal file
490
src/xrt/ipc/ipc_server_process.c
Normal 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;
|
||||||
|
}
|
85
src/xrt/ipc/ipc_server_utils.c
Normal file
85
src/xrt/ipc/ipc_server_utils.c
Normal 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;
|
||||||
|
}
|
18
src/xrt/ipc/ipc_server_utils.h
Normal file
18
src/xrt/ipc/ipc_server_utils.h
Normal 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
66
src/xrt/ipc/meson.build
Normal 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
116
src/xrt/ipc/proto.json
Normal 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
394
src/xrt/ipc/proto.py
Executable 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)
|
|
@ -8,4 +8,5 @@ subdir('auxiliary')
|
||||||
subdir('drivers')
|
subdir('drivers')
|
||||||
subdir('compositor')
|
subdir('compositor')
|
||||||
subdir('state_trackers')
|
subdir('state_trackers')
|
||||||
|
subdir('ipc')
|
||||||
subdir('targets')
|
subdir('targets')
|
||||||
|
|
Loading…
Reference in a new issue