mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-01 12:46:12 +00:00
ipc: Use new frame timing code
This commit is contained in:
parent
83081f9cc1
commit
e59b4a1cb1
|
@ -61,7 +61,6 @@ add_library(ipc_server STATIC
|
|||
ipc_server_process.c
|
||||
ipc_server_utils.c
|
||||
ipc_server_utils.h
|
||||
ipc_server_wait.c
|
||||
)
|
||||
target_include_directories(ipc_server
|
||||
INTERFACE
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "util/u_misc.h"
|
||||
|
||||
#include "os/os_time.h"
|
||||
|
||||
#include "ipc_protocol.h"
|
||||
#include "ipc_client.h"
|
||||
#include "ipc_client_generated.h"
|
||||
|
@ -267,37 +269,6 @@ ipc_compositor_get_formats(struct xrt_compositor *xc,
|
|||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
wait_semaphore(struct ipc_client_compositor *icc, struct ipc_shared_memory *ism)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
|
||||
IPC_ERROR(icc->ipc_c, "Error getting CLOCK_REALTIME\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int s;
|
||||
ts.tv_sec += 2;
|
||||
|
||||
do {
|
||||
s = sem_timedwait(&ism->wait_frame.sem, &ts);
|
||||
} while (s < 0 && errno == EINTR);
|
||||
|
||||
/* Check what happened */
|
||||
|
||||
if (s < 0) {
|
||||
if (errno == ETIMEDOUT) {
|
||||
IPC_ERROR(icc->ipc_c,
|
||||
"Error sem_timedwait() timed out\n");
|
||||
} else {
|
||||
IPC_ERROR(icc->ipc_c,
|
||||
"Error sem_timedwait() error '%i'\n", errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static xrt_result_t
|
||||
ipc_compositor_wait_frame(struct xrt_compositor *xc,
|
||||
int64_t *out_frame_id,
|
||||
|
@ -306,15 +277,54 @@ ipc_compositor_wait_frame(struct xrt_compositor *xc,
|
|||
{
|
||||
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
|
||||
|
||||
IPC_CALL_CHK(ipc_call_compositor_wait_frame(icc->ipc_c));
|
||||
uint64_t wake_up_time_ns = 0;
|
||||
uint64_t min_display_period_ns = 0;
|
||||
|
||||
wait_semaphore(icc, icc->ipc_c->ism);
|
||||
IPC_CALL_CHK(ipc_call_compositor_wait_frame(
|
||||
icc->ipc_c, // Connection
|
||||
out_frame_id, // Frame id
|
||||
out_predicted_display_time, // Display time
|
||||
&wake_up_time_ns, // When we should wake up
|
||||
out_predicted_display_period, // Current period
|
||||
&min_display_period_ns)); // Minimum display period
|
||||
|
||||
*out_frame_id = 1;
|
||||
*out_predicted_display_time =
|
||||
icc->ipc_c->ism->wait_frame.predicted_display_time;
|
||||
*out_predicted_display_period =
|
||||
icc->ipc_c->ism->wait_frame.predicted_display_period;
|
||||
uint64_t now_ns = os_monotonic_get_ns();
|
||||
|
||||
// Lets hope its not to late.
|
||||
if (wake_up_time_ns <= now_ns) {
|
||||
res = ipc_call_compositor_wait_woke(icc->ipc_c, *out_frame_id);
|
||||
return res;
|
||||
}
|
||||
|
||||
const uint64_t _1ms_in_ns = 1000 * 1000;
|
||||
const uint64_t measured_scheduler_latency_ns = 50 * 1000;
|
||||
|
||||
// Within one ms, just release the app right now.
|
||||
if (wake_up_time_ns - _1ms_in_ns <= now_ns) {
|
||||
res = ipc_call_compositor_wait_woke(icc->ipc_c, *out_frame_id);
|
||||
return res;
|
||||
}
|
||||
|
||||
// This is how much we should sleep.
|
||||
uint64_t diff_ns = wake_up_time_ns - now_ns;
|
||||
|
||||
// A minor tweak that helps hit the time better.
|
||||
diff_ns -= measured_scheduler_latency_ns;
|
||||
|
||||
os_nanosleep(diff_ns);
|
||||
|
||||
res = ipc_call_compositor_wait_woke(icc->ipc_c, *out_frame_id);
|
||||
|
||||
#if 0
|
||||
uint64_t then_ns = now_ns;
|
||||
now_ns = os_monotonic_get_ns();
|
||||
|
||||
diff_ns = now_ns - then_ns;
|
||||
uint64_t ms100 = diff_ns / (1000 * 10);
|
||||
|
||||
fprintf(stderr, "%s: Slept %i.%02ims\n", __func__, (int)ms100 / 100,
|
||||
(int)ms100 % 100);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -324,7 +334,7 @@ ipc_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id)
|
|||
{
|
||||
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
|
||||
|
||||
IPC_CALL_CHK(ipc_call_compositor_begin_frame(icc->ipc_c));
|
||||
IPC_CALL_CHK(ipc_call_compositor_begin_frame(icc->ipc_c, frame_id));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -405,7 +415,7 @@ ipc_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id)
|
|||
slot->num_layers = icc->layers.num_layers;
|
||||
|
||||
IPC_CALL_CHK(ipc_call_compositor_layer_sync(
|
||||
icc->ipc_c, icc->layers.slot_id, &icc->layers.slot_id));
|
||||
icc->ipc_c, frame_id, icc->layers.slot_id, &icc->layers.slot_id));
|
||||
|
||||
// Reset.
|
||||
icc->layers.num_layers = 0;
|
||||
|
@ -418,7 +428,7 @@ ipc_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id)
|
|||
{
|
||||
struct ipc_client_compositor *icc = ipc_client_compositor(xc);
|
||||
|
||||
IPC_CALL_CHK(ipc_call_compositor_discard_frame(icc->ipc_c));
|
||||
IPC_CALL_CHK(ipc_call_compositor_discard_frame(icc->ipc_c, frame_id));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "xrt/xrt_compiler.h"
|
||||
|
||||
#include "util/u_render_timing.h"
|
||||
|
||||
#include "os/os_threading.h"
|
||||
|
||||
#include "ipc_protocol.h"
|
||||
|
@ -67,7 +69,6 @@ extern "C" {
|
|||
struct xrt_instance;
|
||||
struct xrt_compositor;
|
||||
struct xrt_compositor_fd;
|
||||
struct ipc_wait;
|
||||
|
||||
|
||||
/*!
|
||||
|
@ -159,11 +160,13 @@ struct ipc_server
|
|||
bool print_spew;
|
||||
|
||||
// Hack for now.
|
||||
struct ipc_wait *iw;
|
||||
struct os_thread thread;
|
||||
volatile bool thread_started;
|
||||
volatile bool thread_stopping;
|
||||
volatile struct ipc_client_state thread_state;
|
||||
|
||||
|
||||
struct u_rt_helper urth;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -182,45 +185,6 @@ ipc_server_main(int argc, char **argv);
|
|||
void *
|
||||
ipc_server_client_thread(void *_cs);
|
||||
|
||||
/*!
|
||||
* Create a single wait thread.
|
||||
*
|
||||
* @ingroup ipc_server
|
||||
* @public @memberof ipc_server
|
||||
* @relatesalso ipc_wait
|
||||
*/
|
||||
int
|
||||
ipc_server_wait_alloc(struct ipc_server *s, struct ipc_wait **out_iw);
|
||||
|
||||
/*!
|
||||
* Destroy a wait thread, checks for NULL and sets to NULL.
|
||||
*
|
||||
* @ingroup ipc_server
|
||||
* @public @memberof ipc_wait
|
||||
*/
|
||||
void
|
||||
ipc_server_wait_free(struct ipc_wait **out_iw);
|
||||
|
||||
/*!
|
||||
* Add a client to wait for wait frame, if need be start waiting for the next
|
||||
* wait frame.
|
||||
*
|
||||
* @ingroup ipc_server
|
||||
* @public @memberof ipc_wait
|
||||
*/
|
||||
void
|
||||
ipc_server_wait_add_frame(struct ipc_wait *iw,
|
||||
volatile struct ipc_client_state *cs);
|
||||
|
||||
/*!
|
||||
* Reset the wait state for wait frame, after the client disconnected
|
||||
*
|
||||
* @ingroup ipc_server
|
||||
* @public @memberof ipc_wait
|
||||
*/
|
||||
void
|
||||
ipc_server_wait_reset_client(struct ipc_wait *iw,
|
||||
volatile struct ipc_client_state *cs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -76,26 +76,50 @@ ipc_handle_compositor_get_formats(volatile struct ipc_client_state *cs,
|
|||
}
|
||||
|
||||
xrt_result_t
|
||||
ipc_handle_compositor_wait_frame(volatile struct ipc_client_state *cs)
|
||||
ipc_handle_compositor_wait_frame(volatile struct ipc_client_state *cs,
|
||||
int64_t *out_frame_id,
|
||||
uint64_t *predicted_display_time,
|
||||
uint64_t *wake_up_time,
|
||||
uint64_t *predicted_display_period,
|
||||
uint64_t *min_display_period)
|
||||
{
|
||||
ipc_server_wait_add_frame(cs->server->iw, cs);
|
||||
u_rt_helper_predict(&cs->server->urth, out_frame_id,
|
||||
predicted_display_time, wake_up_time,
|
||||
predicted_display_period, min_display_period);
|
||||
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
xrt_result_t
|
||||
ipc_handle_compositor_begin_frame(volatile struct ipc_client_state *cs)
|
||||
ipc_handle_compositor_wait_woke(volatile struct ipc_client_state *cs,
|
||||
int64_t frame_id)
|
||||
{
|
||||
u_rt_helper_mark_wait_woke(&cs->server->urth, frame_id);
|
||||
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
xrt_result_t
|
||||
ipc_handle_compositor_discard_frame(volatile struct ipc_client_state *cs)
|
||||
ipc_handle_compositor_begin_frame(volatile struct ipc_client_state *cs,
|
||||
int64_t frame_id)
|
||||
{
|
||||
u_rt_helper_mark_begin(&cs->server->urth, frame_id);
|
||||
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
xrt_result_t
|
||||
ipc_handle_compositor_discard_frame(volatile struct ipc_client_state *cs,
|
||||
int64_t frame_id)
|
||||
{
|
||||
u_rt_helper_mark_discarded(&cs->server->urth, frame_id);
|
||||
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
xrt_result_t
|
||||
ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *cs,
|
||||
int64_t frame_id,
|
||||
uint32_t slot_id,
|
||||
uint32_t *out_free_slot_id)
|
||||
{
|
||||
|
@ -108,6 +132,8 @@ ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *cs,
|
|||
|
||||
*out_free_slot_id = (slot_id + 1) % IPC_MAX_SLOTS;
|
||||
|
||||
u_rt_helper_mark_delivered(&cs->server->urth, frame_id);
|
||||
|
||||
return XRT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -423,6 +449,8 @@ client_loop(volatile struct ipc_client_state *cs)
|
|||
close(cs->ipc_socket_fd);
|
||||
cs->ipc_socket_fd = -1;
|
||||
|
||||
u_rt_helper_clear(&cs->server->urth);
|
||||
|
||||
cs->active = false;
|
||||
cs->num_swapchains = 0;
|
||||
|
||||
|
@ -458,8 +486,6 @@ client_loop(volatile struct ipc_client_state *cs)
|
|||
if (cs->server->exit_on_disconnect) {
|
||||
cs->server->running = false;
|
||||
}
|
||||
|
||||
ipc_server_wait_reset_client(cs->server->iw, cs);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
* @ingroup ipc_server
|
||||
*/
|
||||
|
||||
#include "ipc_server.h"
|
||||
#include "xrt/xrt_device.h"
|
||||
#include "xrt/xrt_instance.h"
|
||||
#include "xrt/xrt_compositor.h"
|
||||
|
@ -18,11 +17,9 @@
|
|||
#include "util/u_misc.h"
|
||||
#include "util/u_debug.h"
|
||||
|
||||
#include "ipc_server.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>
|
||||
|
@ -66,8 +63,6 @@ teardown_all(struct ipc_server *s)
|
|||
{
|
||||
u_var_remove_root(s);
|
||||
|
||||
ipc_server_wait_free(&s->iw);
|
||||
|
||||
xrt_comp_destroy(&s->xc);
|
||||
|
||||
for (size_t i = 0; i < IPC_SERVER_NUM_XDEVS; i++) {
|
||||
|
@ -432,12 +427,14 @@ init_all(struct ipc_server *s)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = ipc_server_wait_alloc(s, &s->iw);
|
||||
if (ret < 0) {
|
||||
teardown_all(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Init the frame helper.
|
||||
u_rt_helper_init(&s->urth);
|
||||
|
||||
// Easier to use.
|
||||
s->xc = &s->xcfd->base;
|
||||
|
||||
|
@ -515,94 +512,90 @@ check_epoll(struct ipc_server *vs)
|
|||
}
|
||||
|
||||
static bool
|
||||
_update_projection_layer(struct comp_compositor *c,
|
||||
volatile struct ipc_client_state *active_client,
|
||||
_update_projection_layer(struct xrt_compositor *xc,
|
||||
volatile struct ipc_client_state *ics,
|
||||
volatile struct ipc_layer_entry *layer,
|
||||
uint32_t i)
|
||||
{
|
||||
// xdev
|
||||
uint32_t xdevi = layer->xdev_id;
|
||||
// left
|
||||
uint32_t lsi = layer->swapchain_ids[0];
|
||||
uint32_t lxsci = layer->swapchain_ids[0];
|
||||
// right
|
||||
uint32_t rsi = layer->swapchain_ids[1];
|
||||
uint32_t rxsci = layer->swapchain_ids[1];
|
||||
|
||||
if (active_client->xscs[lsi] == NULL ||
|
||||
active_client->xscs[rsi] == NULL) {
|
||||
struct xrt_device *xdev = ics->server->xdevs[xdevi];
|
||||
struct xrt_swapchain *lxcs = ics->xscs[lxsci];
|
||||
struct xrt_swapchain *rxcs = ics->xscs[rxsci];
|
||||
|
||||
if (lxcs == NULL || rxcs == NULL) {
|
||||
fprintf(stderr,
|
||||
"ERROR: Invalid swap chain for projection layer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct comp_swapchain *cl = comp_swapchain(active_client->xscs[lsi]);
|
||||
struct comp_swapchain *cr = comp_swapchain(active_client->xscs[rsi]);
|
||||
|
||||
struct comp_swapchain_image *l = NULL;
|
||||
struct comp_swapchain_image *r = NULL;
|
||||
l = &cl->images[layer->data.stereo.l.sub.image_index];
|
||||
r = &cr->images[layer->data.stereo.r.sub.image_index];
|
||||
|
||||
|
||||
// Cast away volatile.
|
||||
struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
|
||||
|
||||
//! @todo we are ignoring subrect here!
|
||||
comp_renderer_set_projection_layer(c->r, i, l, r, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_update_quad_layer(struct comp_compositor *c,
|
||||
volatile struct ipc_client_state *active_client,
|
||||
volatile struct ipc_layer_entry *layer,
|
||||
uint32_t i)
|
||||
{
|
||||
uint32_t sci = layer->swapchain_ids[0];
|
||||
|
||||
if (active_client->xscs[sci] == NULL) {
|
||||
fprintf(stderr, "ERROR: Invalid swap chain for quad layer.\n");
|
||||
if (xdev == NULL) {
|
||||
fprintf(stderr, "ERROR: Invalid xdev for projection layer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct comp_swapchain *sc = comp_swapchain(active_client->xscs[sci]);
|
||||
struct comp_swapchain_image *image = NULL;
|
||||
image = &sc->images[layer->data.quad.sub.image_index];
|
||||
|
||||
// Cast away volatile.
|
||||
struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
|
||||
|
||||
//! @todo we are ignoring subrect here!
|
||||
comp_renderer_set_quad_layer(c->r, i, image, data);
|
||||
xrt_comp_layer_stereo_projection(xc, xdev, lxcs, rxcs, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_update_layers(struct comp_compositor *c,
|
||||
volatile struct ipc_client_state *active_client,
|
||||
uint32_t *num_layers)
|
||||
_update_quad_layer(struct xrt_compositor *xc,
|
||||
volatile struct ipc_client_state *ics,
|
||||
volatile struct ipc_layer_entry *layer,
|
||||
uint32_t i)
|
||||
{
|
||||
uint32_t xdevi = layer->xdev_id;
|
||||
uint32_t sci = layer->swapchain_ids[0];
|
||||
|
||||
struct xrt_device *xdev = ics->server->xdevs[xdevi];
|
||||
struct xrt_swapchain *xcs = ics->xscs[sci];
|
||||
|
||||
if (xcs == NULL) {
|
||||
fprintf(stderr, "ERROR: Invalid swapchain for quad layer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xdev == NULL) {
|
||||
fprintf(stderr, "ERROR: Invalid xdev for quad layer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cast away volatile.
|
||||
struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
|
||||
|
||||
xrt_comp_layer_quad(xc, xdev, xcs, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_update_layers(struct xrt_compositor *xc,
|
||||
volatile struct ipc_client_state *active_client)
|
||||
{
|
||||
volatile struct ipc_layer_slot *render_state =
|
||||
&active_client->render_state;
|
||||
|
||||
if (*num_layers != render_state->num_layers) {
|
||||
//! @todo Resizing here would be faster
|
||||
*num_layers = render_state->num_layers;
|
||||
comp_renderer_destroy_layers(c->r);
|
||||
comp_renderer_allocate_layers(c->r, render_state->num_layers);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < render_state->num_layers; i++) {
|
||||
volatile struct ipc_layer_entry *layer =
|
||||
&render_state->layers[i];
|
||||
switch (layer->data.type) {
|
||||
case XRT_LAYER_STEREO_PROJECTION: {
|
||||
if (!_update_projection_layer(c, active_client, layer,
|
||||
if (!_update_projection_layer(xc, active_client, layer,
|
||||
i))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case XRT_LAYER_QUAD: {
|
||||
if (!_update_quad_layer(c, active_client, layer, i))
|
||||
if (!_update_quad_layer(xc, active_client, layer, i))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
@ -615,15 +608,12 @@ 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;
|
||||
|
||||
uint32_t num_layers = 0;
|
||||
|
||||
while (vs->running) {
|
||||
|
||||
/*
|
||||
|
@ -640,36 +630,27 @@ main_loop(struct ipc_server *vs)
|
|||
active_client = &vs->thread_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Render the swapchains.
|
||||
*/
|
||||
int64_t frame_id;
|
||||
uint64_t predicted_display_time;
|
||||
uint64_t predicted_display_period;
|
||||
|
||||
if (active_client == NULL || !active_client->active ||
|
||||
active_client->num_swapchains == 0) {
|
||||
if (num_layers != 0) {
|
||||
COMP_DEBUG(c, "Destroying layers.");
|
||||
comp_renderer_destroy_layers(c->r);
|
||||
num_layers = 0;
|
||||
}
|
||||
} 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.
|
||||
if (active_client->rendering_state) {
|
||||
if (!_update_layers(c, active_client,
|
||||
&num_layers))
|
||||
continue;
|
||||
xrt_comp_wait_frame(xc, &frame_id, &predicted_display_time,
|
||||
&predicted_display_period);
|
||||
|
||||
// set our client state back to waiting.
|
||||
active_client->rendering_state = false;
|
||||
}
|
||||
uint64_t now = os_monotonic_get_ns();
|
||||
uint64_t diff = predicted_display_time - now;
|
||||
|
||||
u_rt_helper_new_sample(&vs->urth, predicted_display_time, diff,
|
||||
predicted_display_period);
|
||||
|
||||
xrt_comp_begin_frame(xc, frame_id);
|
||||
xrt_comp_layer_begin(xc, frame_id, 0);
|
||||
|
||||
if (active_client != NULL && active_client->active) {
|
||||
_update_layers(xc, active_client);
|
||||
}
|
||||
|
||||
comp_renderer_draw(c->r);
|
||||
|
||||
// Now is a good time to destroy objects.
|
||||
comp_compositor_garbage_collect(c);
|
||||
xrt_comp_layer_commit(xc, frame_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,180 +0,0 @@
|
|||
// Copyright 2020, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Threads for blocking and waiting on things.
|
||||
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||
* @ingroup ipc_server
|
||||
*/
|
||||
|
||||
#include "xrt/xrt_gfx_fd.h"
|
||||
|
||||
#include "os/os_threading.h"
|
||||
#include "xrt/xrt_compositor.h"
|
||||
#include "util/u_misc.h"
|
||||
|
||||
#include "ipc_server.h"
|
||||
#include "ipc_protocol.h"
|
||||
|
||||
|
||||
struct ipc_wait
|
||||
{
|
||||
// Owning server.
|
||||
struct ipc_server *s;
|
||||
|
||||
//! Thread and lock helper.
|
||||
struct os_thread_helper oth;
|
||||
|
||||
// Number of waiters.
|
||||
uint32_t num_waiters;
|
||||
|
||||
// Client states waiting on this waitframe.
|
||||
volatile struct ipc_client_state *cs[IPC_MAX_CLIENTS];
|
||||
};
|
||||
|
||||
static void *
|
||||
run(void *ptr)
|
||||
{
|
||||
struct ipc_wait *iw = (struct ipc_wait *)ptr;
|
||||
|
||||
os_thread_helper_lock(&iw->oth);
|
||||
|
||||
while (os_thread_helper_is_running_locked(&iw->oth)) {
|
||||
// No waiters, wait for waiters.
|
||||
if (iw->num_waiters <= 0) {
|
||||
os_thread_helper_wait_locked(&iw->oth);
|
||||
}
|
||||
|
||||
// Where we woken up to shut down?
|
||||
if (!os_thread_helper_is_running_locked(&iw->oth)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Just in case.
|
||||
if (iw->num_waiters <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unlock the mutex when we have waiting to do.
|
||||
os_thread_helper_unlock(&iw->oth);
|
||||
|
||||
int64_t frame_id;
|
||||
|
||||
// Do the waiting.
|
||||
uint64_t predicted_display_time, predicted_display_period;
|
||||
xrt_comp_wait_frame(iw->s->xc, &frame_id,
|
||||
&predicted_display_time,
|
||||
&predicted_display_period);
|
||||
|
||||
// Lock for broadcast.
|
||||
os_thread_helper_lock(&iw->oth);
|
||||
|
||||
for (size_t i = 0; i < IPC_MAX_CLIENTS; i++) {
|
||||
volatile struct ipc_client_state *cs = iw->cs[i];
|
||||
iw->cs[i] = NULL;
|
||||
|
||||
if (cs == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
volatile struct ipc_shared_memory *ism = iw->s->ism;
|
||||
|
||||
ism->wait_frame.predicted_display_time =
|
||||
predicted_display_time;
|
||||
ism->wait_frame.predicted_display_period =
|
||||
predicted_display_period;
|
||||
|
||||
// Wake the client up now.
|
||||
sem_post((sem_t *)&ism->wait_frame.sem);
|
||||
}
|
||||
|
||||
iw->num_waiters = 0;
|
||||
}
|
||||
|
||||
os_thread_helper_unlock(&iw->oth);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ipc_server_wait_add_frame(struct ipc_wait *iw,
|
||||
volatile struct ipc_client_state *cs)
|
||||
{
|
||||
os_thread_helper_lock(&iw->oth);
|
||||
|
||||
// Don't do anything if we have stopped.
|
||||
if (!os_thread_helper_is_running_locked(&iw->oth)) {
|
||||
os_thread_helper_unlock(&iw->oth);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the client to the list of waiters.
|
||||
iw->cs[iw->num_waiters++] = cs;
|
||||
|
||||
// Wake up the thread.
|
||||
os_thread_helper_signal_locked(&iw->oth);
|
||||
|
||||
os_thread_helper_unlock(&iw->oth);
|
||||
}
|
||||
|
||||
void
|
||||
ipc_server_wait_reset_client(struct ipc_wait *iw,
|
||||
volatile struct ipc_client_state *cs)
|
||||
{
|
||||
os_thread_helper_lock(&iw->oth);
|
||||
|
||||
/* ipc_server_wait_add_frame would overwrite dangling references,
|
||||
* but clean them up anyway to be less confusing. */
|
||||
for (int i = 0; i < IPC_MAX_CLIENTS; i++) {
|
||||
if (iw->cs[i] == cs) {
|
||||
iw->cs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
volatile struct ipc_shared_memory *ism = iw->s->ism;
|
||||
sem_init((sem_t *)&ism->wait_frame.sem, true, 0);
|
||||
ism->wait_frame.predicted_display_period = 0;
|
||||
ism->wait_frame.predicted_display_time = 0;
|
||||
|
||||
os_thread_helper_unlock(&iw->oth);
|
||||
}
|
||||
|
||||
void
|
||||
ipc_server_wait_free(struct ipc_wait **out_iw)
|
||||
{
|
||||
struct ipc_wait *iw = *out_iw;
|
||||
|
||||
// Already freed, nothing to do.
|
||||
if (iw == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy also stops the thread should it be running.
|
||||
os_thread_helper_destroy(&iw->oth);
|
||||
|
||||
*out_iw = NULL;
|
||||
free(iw);
|
||||
}
|
||||
|
||||
int
|
||||
ipc_server_wait_alloc(struct ipc_server *s, struct ipc_wait **out_iw)
|
||||
{
|
||||
struct ipc_wait *iw = U_TYPED_CALLOC(struct ipc_wait);
|
||||
iw->s = s;
|
||||
|
||||
int ret = os_thread_helper_init(&iw->oth);
|
||||
if (ret < 0) {
|
||||
free(iw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = os_thread_helper_start(&iw->oth, run, iw);
|
||||
if (ret < 0) {
|
||||
ipc_server_wait_free(&iw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*out_iw = iw;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -57,7 +57,6 @@ lib_ipc_server = static_library(
|
|||
'ipc_server_process.c',
|
||||
'ipc_server_utils.c',
|
||||
'ipc_server_utils.h',
|
||||
'ipc_server_wait.c',
|
||||
],
|
||||
include_directories: [
|
||||
xrt_include,
|
||||
|
|
|
@ -14,14 +14,36 @@
|
|||
},
|
||||
|
||||
"compositor_wait_frame": {
|
||||
"out": [
|
||||
{"name": "frame_id", "type": "int64_t"},
|
||||
{"name": "predicted_display_time", "type": "uint64_t"},
|
||||
{"name": "wake_up_time", "type": "uint64_t"},
|
||||
{"name": "predicted_display_period", "type": "uint64_t"},
|
||||
{"name": "min_display_period", "type": "uint64_t"}
|
||||
]
|
||||
},
|
||||
|
||||
"compositor_begin_frame": {},
|
||||
"compositor_wait_woke": {
|
||||
"in": [
|
||||
{"name": "frame_id", "type": "int64_t"}
|
||||
]
|
||||
},
|
||||
|
||||
"compositor_discard_frame": {},
|
||||
"compositor_begin_frame": {
|
||||
"in": [
|
||||
{"name": "frame_id", "type": "int64_t"}
|
||||
]
|
||||
},
|
||||
|
||||
"compositor_discard_frame": {
|
||||
"in": [
|
||||
{"name": "frame_id", "type": "int64_t"}
|
||||
]
|
||||
},
|
||||
|
||||
"compositor_layer_sync": {
|
||||
"in": [
|
||||
{"name": "frame_id", "type": "int64_t"},
|
||||
{"name": "slot_id", "type": "uint32_t"}
|
||||
],
|
||||
"out": [
|
||||
|
|
Loading…
Reference in a new issue