diff --git a/src/xrt/ipc/CMakeLists.txt b/src/xrt/ipc/CMakeLists.txt index 1f8f3240f..0dc4bda2f 100644 --- a/src/xrt/ipc/CMakeLists.txt +++ b/src/xrt/ipc/CMakeLists.txt @@ -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 diff --git a/src/xrt/ipc/ipc_client_compositor.c b/src/xrt/ipc/ipc_client_compositor.c index 6a0cbe45d..8941dfc2c 100644 --- a/src/xrt/ipc/ipc_client_compositor.c +++ b/src/xrt/ipc/ipc_client_compositor.c @@ -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; } diff --git a/src/xrt/ipc/ipc_server.h b/src/xrt/ipc/ipc_server.h index 2b4bba8c1..b60f57321 100644 --- a/src/xrt/ipc/ipc_server.h +++ b/src/xrt/ipc/ipc_server.h @@ -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 } diff --git a/src/xrt/ipc/ipc_server_client.c b/src/xrt/ipc/ipc_server_client.c index 7fc7bd171..68ac6a8ca 100644 --- a/src/xrt/ipc/ipc_server_client.c +++ b/src/xrt/ipc/ipc_server_client.c @@ -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); } diff --git a/src/xrt/ipc/ipc_server_process.c b/src/xrt/ipc/ipc_server_process.c index 0a44f520c..298b4eb74 100644 --- a/src/xrt/ipc/ipc_server_process.c +++ b/src/xrt/ipc/ipc_server_process.c @@ -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 #include #include @@ -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; diff --git a/src/xrt/ipc/ipc_server_wait.c b/src/xrt/ipc/ipc_server_wait.c deleted file mode 100644 index 04ed65650..000000000 --- a/src/xrt/ipc/ipc_server_wait.c +++ /dev/null @@ -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 - * @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; -} diff --git a/src/xrt/ipc/meson.build b/src/xrt/ipc/meson.build index 51d2bebf1..9937e2f8b 100644 --- a/src/xrt/ipc/meson.build +++ b/src/xrt/ipc/meson.build @@ -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, diff --git a/src/xrt/ipc/proto.json b/src/xrt/ipc/proto.json index 6b4177b8b..a61031826 100644 --- a/src/xrt/ipc/proto.json +++ b/src/xrt/ipc/proto.json @@ -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": [