ipc: Use new frame timing code

This commit is contained in:
Jakob Bornecrantz 2020-06-23 21:30:18 +01:00
parent 83081f9cc1
commit e59b4a1cb1
8 changed files with 181 additions and 360 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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,

View file

@ -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": [