From bcf9b62fc0df2693f8bfebc67211bf3393aeef5d Mon Sep 17 00:00:00 2001
From: Jakob Bornecrantz <jakob@collabora.com>
Date: Thu, 18 Mar 2021 23:17:42 +0000
Subject: [PATCH] ipc: Adopt to new multi client interface

---
 src/xrt/ipc/client/ipc_client_compositor.c    |  81 +-
 src/xrt/ipc/server/ipc_server.h               |  58 +-
 src/xrt/ipc/server/ipc_server_handler.c       | 428 ++++++++---
 .../ipc/server/ipc_server_per_client_thread.c |  41 +-
 src/xrt/ipc/server/ipc_server_process.c       | 709 +++++-------------
 src/xrt/ipc/shared/ipc_protocol.h             |   1 +
 src/xrt/ipc/shared/proto.json                 |   7 +-
 7 files changed, 617 insertions(+), 708 deletions(-)

diff --git a/src/xrt/ipc/client/ipc_client_compositor.c b/src/xrt/ipc/client/ipc_client_compositor.c
index c8edabc9c..16f18abaa 100644
--- a/src/xrt/ipc/client/ipc_client_compositor.c
+++ b/src/xrt/ipc/client/ipc_client_compositor.c
@@ -61,14 +61,10 @@ struct ipc_client_compositor
 
 	struct
 	{
-		uint64_t display_time_ns;
-
 		//! 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;
 
 	//! Has the native compositor been created, only supports one for now.
@@ -397,14 +393,12 @@ ipc_compositor_wait_frame(struct xrt_compositor *xc,
 	struct ipc_client_compositor *icc = ipc_client_compositor(xc);
 
 	uint64_t wake_up_time_ns = 0;
-	uint64_t min_display_period_ns = 0;
 
-	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
+	IPC_CALL_CHK(ipc_call_compositor_predict_frame(icc->ipc_c,                     // Connection
+	                                               out_frame_id,                   // Frame id
+	                                               &wake_up_time_ns,               // When we should wake up
+	                                               out_predicted_display_time,     // Display time
+	                                               out_predicted_display_period)); // Current period
 
 	uint64_t now_ns = os_monotonic_get_ns();
 
@@ -465,8 +459,11 @@ ipc_compositor_layer_begin(struct xrt_compositor *xc,
 {
 	struct ipc_client_compositor *icc = ipc_client_compositor(xc);
 
-	icc->layers.display_time_ns = display_time_ns;
-	icc->layers.env_blend_mode = env_blend_mode;
+	struct ipc_shared_memory *ism = icc->ipc_c->ism;
+	struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id];
+
+	slot->display_time_ns = display_time_ns;
+	slot->env_blend_mode = env_blend_mode;
 
 	return XRT_SUCCESS;
 }
@@ -666,6 +663,34 @@ ipc_compositor_destroy(struct xrt_compositor *xc)
 	icc->compositor_created = false;
 }
 
+static void
+ipc_compositor_init(struct ipc_client_compositor *icc, struct xrt_compositor_native **out_xcn)
+{
+	icc->base.base.create_swapchain = ipc_compositor_swapchain_create;
+	icc->base.base.import_swapchain = ipc_compositor_swapchain_import;
+	icc->base.base.begin_session = ipc_compositor_begin_session;
+	icc->base.base.end_session = ipc_compositor_end_session;
+	icc->base.base.wait_frame = ipc_compositor_wait_frame;
+	icc->base.base.begin_frame = ipc_compositor_begin_frame;
+	icc->base.base.discard_frame = ipc_compositor_discard_frame;
+	icc->base.base.layer_begin = ipc_compositor_layer_begin;
+	icc->base.base.layer_stereo_projection = ipc_compositor_layer_stereo_projection;
+	icc->base.base.layer_stereo_projection_depth = ipc_compositor_layer_stereo_projection_depth;
+	icc->base.base.layer_quad = ipc_compositor_layer_quad;
+	icc->base.base.layer_cube = ipc_compositor_layer_cube;
+	icc->base.base.layer_cylinder = ipc_compositor_layer_cylinder;
+	icc->base.base.layer_equirect1 = ipc_compositor_layer_equirect1;
+	icc->base.base.layer_equirect2 = ipc_compositor_layer_equirect2;
+	icc->base.base.layer_commit = ipc_compositor_layer_commit;
+	icc->base.base.destroy = ipc_compositor_destroy;
+	icc->base.base.poll_events = ipc_compositor_poll_events;
+
+	// Fetch info from the compositor, among it the format format list.
+	get_info(&(icc->base.base), &icc->base.base.info);
+
+	*out_xcn = &icc->base;
+}
+
 
 /*
  *
@@ -792,11 +817,14 @@ ipc_syscomp_create_native_compositor(struct xrt_system_compositor *xsc,
 		return XRT_ERROR_MULTI_SESSION_NOT_IMPLEMENTED;
 	}
 
-	icc->compositor_created = true;
-	*out_xcn = &icc->base;
-
+	// Needs to be done before init.
 	IPC_CALL_CHK(ipc_call_session_create(icc->ipc_c, xsi));
 
+	// Needs to be done after session create call.
+	ipc_compositor_init(icc, out_xcn);
+
+	icc->compositor_created = true;
+
 	return XRT_SUCCESS;
 }
 
@@ -829,24 +857,6 @@ ipc_client_create_system_compositor(struct ipc_connection *ipc_c,
 {
 	struct ipc_client_compositor *c = U_TYPED_CALLOC(struct ipc_client_compositor);
 
-	c->base.base.create_swapchain = ipc_compositor_swapchain_create;
-	c->base.base.import_swapchain = ipc_compositor_swapchain_import;
-	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;
-	c->base.base.layer_begin = ipc_compositor_layer_begin;
-	c->base.base.layer_stereo_projection = ipc_compositor_layer_stereo_projection;
-	c->base.base.layer_stereo_projection_depth = ipc_compositor_layer_stereo_projection_depth;
-	c->base.base.layer_quad = ipc_compositor_layer_quad;
-	c->base.base.layer_cube = ipc_compositor_layer_cube;
-	c->base.base.layer_cylinder = ipc_compositor_layer_cylinder;
-	c->base.base.layer_equirect1 = ipc_compositor_layer_equirect1;
-	c->base.base.layer_equirect2 = ipc_compositor_layer_equirect2;
-	c->base.base.layer_commit = ipc_compositor_layer_commit;
-	c->base.base.destroy = ipc_compositor_destroy;
-	c->base.base.poll_events = ipc_compositor_poll_events;
 	c->system.create_native_compositor = ipc_syscomp_create_native_compositor;
 	c->system.destroy = ipc_syscomp_destroy;
 	c->ipc_c = ipc_c;
@@ -863,9 +873,6 @@ ipc_client_create_system_compositor(struct ipc_connection *ipc_c,
 	}
 #endif
 
-	// Fetch info from the compositor, among it the format format list.
-	get_info(&(c->base.base), &c->base.base.info);
-
 	// Fetch info from the system compositor.
 	get_system_info(c, &c->system.info);
 
diff --git a/src/xrt/ipc/server/ipc_server.h b/src/xrt/ipc/server/ipc_server.h
index 9645ad2ac..b247d53c2 100644
--- a/src/xrt/ipc/server/ipc_server.h
+++ b/src/xrt/ipc/server/ipc_server.h
@@ -69,14 +69,6 @@ struct ipc_swapchain_data
 	bool active;
 };
 
-
-struct ipc_queued_event
-{
-	bool pending;
-	uint64_t timestamp;
-	union xrt_compositor_event event;
-};
-
 /*!
  * Holds the state for a single client.
  *
@@ -105,17 +97,7 @@ struct ipc_client_state
 	//! Socket fd used for client comms
 	struct ipc_message_channel imc;
 
-	//! State for rendering.
-	struct ipc_layer_slot render_state;
-
-	//! Whether we are currently rendering @ref render_state
-	bool rendering_state;
-
-	//! The frame timing state.
-	struct u_rt_helper urth;
-
 	struct ipc_app_state client_state;
-	struct ipc_queued_event queued_events[IPC_EVENT_QUEUE_SIZE];
 
 	int server_thread_index;
 };
@@ -294,8 +276,6 @@ struct ipc_server
 
 	//! System compositor.
 	struct xrt_system_compositor *xsysc;
-	//! Native compositor.
-	struct xrt_compositor_native *xcn;
 
 	struct ipc_device idevs[IPC_SERVER_NUM_XDEVS];
 	struct xrt_tracking_origin *xtracks[IPC_SERVER_NUM_XDEVS];
@@ -317,9 +297,13 @@ struct ipc_server
 
 	volatile uint32_t current_slot_index;
 
-	int active_client_index;
-	int last_active_client_index;
-	struct os_mutex global_state_lock;
+	struct
+	{
+		int active_client_index;
+		int last_active_client_index;
+
+		struct os_mutex lock;
+	} global_state;
 };
 
 
@@ -349,12 +333,36 @@ ipc_server_main_android(struct ipc_server **ps, void (*startup_complete_callback
 #endif
 
 /*!
- * Called by client threads to manage global state
+ * Set the new active client.
  *
  * @ingroup ipc_server
  */
 void
-update_server_state(struct ipc_server *vs);
+ipc_server_set_active_client(struct ipc_server *s, int active_client_index);
+
+/*!
+ * Called by client threads to set a session to active.
+ *
+ * @ingroup ipc_server
+ */
+void
+ipc_server_activate_session(volatile struct ipc_client_state *ics);
+
+/*!
+ * Called by client threads to set a session to deactivate.
+ *
+ * @ingroup ipc_server
+ */
+void
+ipc_server_deactivate_session(volatile struct ipc_client_state *ics);
+
+/*!
+ * Called by client threads to recalculate active client.
+ *
+ * @ingroup ipc_server
+ */
+void
+ipc_server_update_state(struct ipc_server *s);
 
 /*!
  * Thread function for the client side dispatching.
diff --git a/src/xrt/ipc/server/ipc_server_handler.c b/src/xrt/ipc/server/ipc_server_handler.c
index 3da04b0a7..4157a2d09 100644
--- a/src/xrt/ipc/server/ipc_server_handler.c
+++ b/src/xrt/ipc/server/ipc_server_handler.c
@@ -10,6 +10,7 @@
 #include "xrt/xrt_gfx_native.h"
 
 #include "util/u_misc.h"
+#include "util/u_handles.h"
 #include "util/u_trace_marker.h"
 
 #include "server/ipc_server.h"
@@ -93,33 +94,39 @@ ipc_handle_system_compositor_get_info(volatile struct ipc_client_state *ics,
 xrt_result_t
 ipc_handle_session_create(volatile struct ipc_client_state *ics, const struct xrt_session_info *xsi)
 {
-	ics->client_state.session_active = false;
-	ics->client_state.session_overlay = false;
-	ics->client_state.session_visible = false;
+	struct xrt_compositor_native *xcn = NULL;
 
-	if (xsi->is_overlay) {
-		ics->client_state.session_overlay = true;
-		ics->client_state.z_order = xsi->z_order;
+	xrt_result_t xret = xrt_syscomp_create_native_compositor(ics->server->xsysc, xsi, &xcn);
+	if (xret != XRT_SUCCESS) {
+		return xret;
 	}
 
-	update_server_state(ics->server);
+	ics->client_state.session_overlay = xsi->is_overlay;
+	ics->client_state.z_order = xsi->z_order;
+
+	ics->xc = &xcn->base;
+
+	xrt_syscomp_set_state(ics->server->xsysc, ics->xc, ics->client_state.session_visible,
+	                      ics->client_state.session_focused);
+	xrt_syscomp_set_z_order(ics->server->xsysc, ics->xc, ics->client_state.z_order);
+
 	return XRT_SUCCESS;
 }
 
 xrt_result_t
 ipc_handle_session_begin(volatile struct ipc_client_state *ics)
 {
-	// ics->client_state.session_active = true;
-	// update_server_state(ics->server);
-	return XRT_SUCCESS;
+	IPC_TRACE_MARKER();
+
+	return xrt_comp_begin_session(ics->xc, 0);
 }
 
 xrt_result_t
 ipc_handle_session_end(volatile struct ipc_client_state *ics)
 {
-	ics->client_state.session_active = false;
-	update_server_state(ics->server);
-	return XRT_SUCCESS;
+	IPC_TRACE_MARKER();
+
+	return xrt_comp_end_session(ics->xc);
 }
 
 xrt_result_t
@@ -131,62 +138,310 @@ ipc_handle_compositor_get_info(volatile struct ipc_client_state *ics, struct xrt
 }
 
 xrt_result_t
-ipc_handle_compositor_wait_frame(volatile struct ipc_client_state *ics,
-                                 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_handle_compositor_predict_frame(volatile struct ipc_client_state *ics,
+                                    int64_t *out_frame_id,
+                                    uint64_t *out_wake_up_time_ns,
+                                    uint64_t *out_predicted_display_time_ns,
+                                    uint64_t *out_predicted_display_period_ns)
 {
 	IPC_TRACE_MARKER();
 
-	os_mutex_lock(&ics->server->global_state_lock);
+	/*
+	 * We use this to signal that the session has started, this is needed
+	 * to make this client/session active/visible/focused.
+	 */
+	ipc_server_activate_session(ics);
 
-	u_rt_helper_predict((struct u_rt_helper *)&ics->urth, out_frame_id, predicted_display_time, wake_up_time,
-	                    predicted_display_period, min_display_period);
-
-	os_mutex_unlock(&ics->server->global_state_lock);
-
-	ics->client_state.session_active = true;
-	update_server_state(ics->server);
-
-	return XRT_SUCCESS;
+	uint64_t gpu_time_ns = 0;
+	return xrt_comp_predict_frame(        //
+	    ics->xc,                          //
+	    out_frame_id,                     //
+	    out_wake_up_time_ns,              //
+	    &gpu_time_ns,                     //
+	    out_predicted_display_time_ns,    //
+	    out_predicted_display_period_ns); //
 }
 
 xrt_result_t
 ipc_handle_compositor_wait_woke(volatile struct ipc_client_state *ics, int64_t frame_id)
 {
-	os_mutex_lock(&ics->server->global_state_lock);
+	IPC_TRACE_MARKER();
 
-	u_rt_helper_mark_wait_woke((struct u_rt_helper *)&ics->urth, frame_id);
-
-	os_mutex_unlock(&ics->server->global_state_lock);
-
-	return XRT_SUCCESS;
+	return xrt_comp_mark_frame(ics->xc, frame_id, XRT_COMPOSITOR_FRAME_POINT_WOKE, os_monotonic_get_ns());
 }
 
 xrt_result_t
 ipc_handle_compositor_begin_frame(volatile struct ipc_client_state *ics, int64_t frame_id)
 {
-	os_mutex_lock(&ics->server->global_state_lock);
+	IPC_TRACE_MARKER();
 
-	u_rt_helper_mark_begin((struct u_rt_helper *)&ics->urth, frame_id);
-
-	os_mutex_unlock(&ics->server->global_state_lock);
-
-	return XRT_SUCCESS;
+	return xrt_comp_begin_frame(ics->xc, frame_id);
 }
 
 xrt_result_t
 ipc_handle_compositor_discard_frame(volatile struct ipc_client_state *ics, int64_t frame_id)
 {
-	os_mutex_lock(&ics->server->global_state_lock);
+	IPC_TRACE_MARKER();
 
-	u_rt_helper_mark_discarded((struct u_rt_helper *)&ics->urth, frame_id);
+	return xrt_comp_discard_frame(ics->xc, frame_id);
+}
 
-	os_mutex_unlock(&ics->server->global_state_lock);
+static bool
+_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 device_id = layer->xdev_id;
+	// left
+	uint32_t lxsci = layer->swapchain_ids[0];
+	// right
+	uint32_t rxsci = layer->swapchain_ids[1];
 
-	return XRT_SUCCESS;
+	struct xrt_device *xdev = get_xdev(ics, device_id);
+	struct xrt_swapchain *lxcs = ics->xscs[lxsci];
+	struct xrt_swapchain *rxcs = ics->xscs[rxsci];
+
+	if (lxcs == NULL || rxcs == NULL) {
+		U_LOG_E("Invalid swap chain for projection layer!");
+		return false;
+	}
+
+	if (xdev == NULL) {
+		U_LOG_E("Invalid xdev for projection layer!");
+		return false;
+	}
+
+	// Cast away volatile.
+	struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
+
+	xrt_comp_layer_stereo_projection(xc, xdev, lxcs, rxcs, data);
+
+	return true;
+}
+
+static bool
+_update_projection_layer_depth(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 l_xsci = layer->swapchain_ids[0];
+	// right
+	uint32_t r_xsci = layer->swapchain_ids[1];
+	// left
+	uint32_t l_d_xsci = layer->swapchain_ids[2];
+	// right
+	uint32_t r_d_xsci = layer->swapchain_ids[3];
+
+	struct xrt_device *xdev = get_xdev(ics, xdevi);
+	struct xrt_swapchain *l_xcs = ics->xscs[l_xsci];
+	struct xrt_swapchain *r_xcs = ics->xscs[r_xsci];
+	struct xrt_swapchain *l_d_xcs = ics->xscs[l_d_xsci];
+	struct xrt_swapchain *r_d_xcs = ics->xscs[r_d_xsci];
+
+	if (l_xcs == NULL || r_xcs == NULL || l_d_xcs == NULL || r_d_xcs == NULL) {
+		U_LOG_E("Invalid swap chain for projection layer #%u!", i);
+		return false;
+	}
+
+	if (xdev == NULL) {
+		U_LOG_E("Invalid xdev for projection layer #%u!", i);
+		return false;
+	}
+
+	// Cast away volatile.
+	struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
+
+	xrt_comp_layer_stereo_projection_depth(xc, xdev, l_xcs, r_xcs, l_d_xcs, r_d_xcs, data);
+
+	return true;
+}
+
+static bool
+do_single(struct xrt_compositor *xc,
+          volatile struct ipc_client_state *ics,
+          volatile struct ipc_layer_entry *layer,
+          uint32_t i,
+          const char *name,
+          struct xrt_device **out_xdev,
+          struct xrt_swapchain **out_xcs,
+          struct xrt_layer_data **out_data)
+{
+	uint32_t device_id = layer->xdev_id;
+	uint32_t sci = layer->swapchain_ids[0];
+
+	struct xrt_device *xdev = get_xdev(ics, device_id);
+	struct xrt_swapchain *xcs = ics->xscs[sci];
+
+	if (xcs == NULL) {
+		U_LOG_E("Invalid swapchain for layer #%u, '%s'!", i, name);
+		return false;
+	}
+
+	if (xdev == NULL) {
+		U_LOG_E("Invalid xdev for layer #%u, '%s'!", i, name);
+		return false;
+	}
+
+	// Cast away volatile.
+	struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
+
+	*out_xdev = xdev;
+	*out_xcs = xcs;
+	*out_data = data;
+
+	return true;
+}
+
+static bool
+_update_quad_layer(struct xrt_compositor *xc,
+                   volatile struct ipc_client_state *ics,
+                   volatile struct ipc_layer_entry *layer,
+                   uint32_t i)
+{
+	struct xrt_device *xdev;
+	struct xrt_swapchain *xcs;
+	struct xrt_layer_data *data;
+
+	if (!do_single(xc, ics, layer, i, "quad", &xdev, &xcs, &data)) {
+		return false;
+	}
+
+	xrt_comp_layer_quad(xc, xdev, xcs, data);
+
+	return true;
+}
+
+static bool
+_update_cube_layer(struct xrt_compositor *xc,
+                   volatile struct ipc_client_state *ics,
+                   volatile struct ipc_layer_entry *layer,
+                   uint32_t i)
+{
+	struct xrt_device *xdev;
+	struct xrt_swapchain *xcs;
+	struct xrt_layer_data *data;
+
+	if (!do_single(xc, ics, layer, i, "cube", &xdev, &xcs, &data)) {
+		return false;
+	}
+
+	xrt_comp_layer_cube(xc, xdev, xcs, data);
+
+	return true;
+}
+
+static bool
+_update_cylinder_layer(struct xrt_compositor *xc,
+                       volatile struct ipc_client_state *ics,
+                       volatile struct ipc_layer_entry *layer,
+                       uint32_t i)
+{
+	struct xrt_device *xdev;
+	struct xrt_swapchain *xcs;
+	struct xrt_layer_data *data;
+
+	if (!do_single(xc, ics, layer, i, "cylinder", &xdev, &xcs, &data)) {
+		return false;
+	}
+
+	xrt_comp_layer_cylinder(xc, xdev, xcs, data);
+
+	return true;
+}
+
+static bool
+_update_equirect1_layer(struct xrt_compositor *xc,
+                        volatile struct ipc_client_state *ics,
+                        volatile struct ipc_layer_entry *layer,
+                        uint32_t i)
+{
+	struct xrt_device *xdev;
+	struct xrt_swapchain *xcs;
+	struct xrt_layer_data *data;
+
+	if (!do_single(xc, ics, layer, i, "equirect1", &xdev, &xcs, &data)) {
+		return false;
+	}
+
+	xrt_comp_layer_equirect1(xc, xdev, xcs, data);
+
+	return true;
+}
+
+static bool
+_update_equirect2_layer(struct xrt_compositor *xc,
+                        volatile struct ipc_client_state *ics,
+                        volatile struct ipc_layer_entry *layer,
+                        uint32_t i)
+{
+	struct xrt_device *xdev;
+	struct xrt_swapchain *xcs;
+	struct xrt_layer_data *data;
+
+	if (!do_single(xc, ics, layer, i, "equirect2", &xdev, &xcs, &data)) {
+		return false;
+	}
+
+	xrt_comp_layer_equirect2(xc, xdev, xcs, data);
+
+	return true;
+}
+
+static bool
+_update_layers(volatile struct ipc_client_state *ics, struct xrt_compositor *xc, struct ipc_layer_slot *slot)
+{
+	IPC_TRACE_MARKER();
+
+	for (uint32_t i = 0; i < slot->num_layers; i++) {
+		volatile struct ipc_layer_entry *layer = &slot->layers[i];
+
+		switch (layer->data.type) {
+		case XRT_LAYER_STEREO_PROJECTION:
+			if (!_update_projection_layer(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		case XRT_LAYER_STEREO_PROJECTION_DEPTH:
+			if (!_update_projection_layer_depth(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		case XRT_LAYER_QUAD:
+			if (!_update_quad_layer(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		case XRT_LAYER_CUBE:
+			if (!_update_cube_layer(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		case XRT_LAYER_CYLINDER:
+			if (!_update_cylinder_layer(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		case XRT_LAYER_EQUIRECT1:
+			if (!_update_equirect1_layer(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		case XRT_LAYER_EQUIRECT2:
+			if (!_update_equirect2_layer(xc, ics, layer, i)) {
+				return false;
+			}
+			break;
+		default: U_LOG_E("Unhandled layer type '%i'!", layer->data.type); break;
+		}
+	}
+
+	return true;
 }
 
 xrt_result_t
@@ -197,33 +452,49 @@ ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *ics,
                                  const xrt_graphics_sync_handle_t *handles,
                                  const uint32_t num_handles)
 {
+	IPC_TRACE_MARKER();
+
 	struct ipc_shared_memory *ism = ics->server->ism;
 	struct ipc_layer_slot *slot = &ism->slots[slot_id];
+	xrt_graphics_sync_handle_t sync_handle = XRT_GRAPHICS_SYNC_HANDLE_INVALID;
 
-	for (uint32_t i = 0; i < num_handles; i++) {
-		if (!xrt_graphics_sync_handle_is_valid(handles[i])) {
-			continue;
-		}
-#ifdef XRT_GRAPHICS_SYNC_HANDLE_IS_FD
-		close(handles[i]);
-#else
-#error "Need port to transport these graphics buffers"
-#endif
+	// If we have one or more save the first handle.
+	if (num_handles >= 1) {
+		sync_handle = handles[0];
 	}
 
-	// Copy current slot data to our state.
-	ics->render_state = *slot;
-	ics->rendering_state = true;
+	// Free all sync handles after the first one.
+	for (uint32_t i = 1; i < num_handles; i++) {
+		// Checks for valid handle.
+		xrt_graphics_sync_handle_t tmp = handles[i];
+		u_graphics_sync_unref(&tmp);
+	}
 
-	os_mutex_lock(&ics->server->global_state_lock);
+	// Copy current slot data.
+	struct ipc_layer_slot copy = *slot;
+
+
+	/*
+	 * Transfer data to underlying compositor.
+	 */
+
+	xrt_comp_layer_begin(ics->xc, frame_id, copy.display_time_ns, copy.env_blend_mode);
+
+	_update_layers(ics, ics->xc, &copy);
+
+	xrt_comp_layer_commit(ics->xc, frame_id, sync_handle);
+
+
+	/*
+	 * Manage shared state.
+	 */
+
+	os_mutex_lock(&ics->server->global_state.lock);
 
 	*out_free_slot_id = (ics->server->current_slot_index + 1) % IPC_MAX_SLOTS;
 	ics->server->current_slot_index = *out_free_slot_id;
 
-	// Also protected by the global lock.
-	u_rt_helper_mark_delivered((struct u_rt_helper *)&ics->urth, frame_id);
-
-	os_mutex_unlock(&ics->server->global_state_lock);
+	os_mutex_unlock(&ics->server->global_state.lock);
 
 	return XRT_SUCCESS;
 }
@@ -231,25 +502,9 @@ ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *ics,
 xrt_result_t
 ipc_handle_compositor_poll_events(volatile struct ipc_client_state *ics, union xrt_compositor_event *out_xce)
 {
-	uint64_t l_timestamp = UINT64_MAX;
-	volatile struct ipc_queued_event *event_to_send = NULL;
-	for (uint32_t i = 0; i < IPC_EVENT_QUEUE_SIZE; i++) {
-		volatile struct ipc_queued_event *e = &ics->queued_events[i];
-		if (e->pending == true && e->timestamp < l_timestamp) {
-			event_to_send = e;
-		}
-	}
+	IPC_TRACE_MARKER();
 
-	// We always return an event in response to this call -
-	// We signal no events with a special event type.
-	out_xce->type = XRT_COMPOSITOR_EVENT_NONE;
-
-	if (event_to_send) {
-		*out_xce = event_to_send->event;
-		event_to_send->pending = false;
-	}
-
-	return XRT_SUCCESS;
+	return xrt_comp_poll_events(ics->xc, out_xce);
 }
 
 xrt_result_t
@@ -271,7 +526,7 @@ ipc_handle_system_get_client_info(volatile struct ipc_client_state *_ics,
 
 	//@todo: track this data in the ipc_client_state struct
 	out_client_desc->primary_application = false;
-	if (ics->server->active_client_index == (int)id) {
+	if (ics->server->global_state.active_client_index == (int)id) {
 		out_client_desc->primary_application = true;
 	}
 
@@ -306,10 +561,10 @@ ipc_handle_system_get_clients(volatile struct ipc_client_state *_ics, struct ipc
 xrt_result_t
 ipc_handle_system_set_primary_client(volatile struct ipc_client_state *ics, uint32_t client_id)
 {
-
-	ics->server->active_client_index = client_id;
 	IPC_INFO(ics->server, "System setting active client to %d.", client_id);
-	update_server_state(ics->server);
+
+	ipc_server_set_active_client(ics->server, client_id);
+
 	return XRT_SUCCESS;
 }
 
@@ -365,6 +620,8 @@ ipc_handle_swapchain_create(volatile struct ipc_client_state *ics,
                             xrt_graphics_buffer_handle_t *out_handles,
                             uint32_t *out_num_handles)
 {
+	IPC_TRACE_MARKER();
+
 	xrt_result_t xret = XRT_SUCCESS;
 	uint32_t index = 0;
 
@@ -416,6 +673,8 @@ ipc_handle_swapchain_import(volatile struct ipc_client_state *ics,
                             const xrt_graphics_buffer_handle_t *handles,
                             uint32_t num_handles)
 {
+	IPC_TRACE_MARKER();
+
 	xrt_result_t xret = XRT_SUCCESS;
 	uint32_t index = 0;
 
@@ -558,7 +817,6 @@ ipc_handle_device_get_tracked_pose(volatile struct ipc_client_state *ics,
                                    uint64_t at_timestamp,
                                    struct xrt_space_relation *out_relation)
 {
-
 	// To make the code a bit more readable.
 	uint32_t device_id = id;
 	struct ipc_device *isdev = &ics->server->idevs[device_id];
diff --git a/src/xrt/ipc/server/ipc_server_per_client_thread.c b/src/xrt/ipc/server/ipc_server_per_client_thread.c
index c1afa1797..047641ed0 100644
--- a/src/xrt/ipc/server/ipc_server_per_client_thread.c
+++ b/src/xrt/ipc/server/ipc_server_per_client_thread.c
@@ -69,9 +69,6 @@ client_loop(volatile struct ipc_client_state *ics)
 {
 	IPC_INFO(ics->server, "Client connected");
 
-	// Make sure it's ready for the client.
-	u_rt_helper_client_clear((struct u_rt_helper *)&ics->urth);
-
 	// Claim the client fd.
 	int epoll_fd = setup_epoll(ics);
 	if (epoll_fd < 0) {
@@ -123,43 +120,16 @@ client_loop(volatile struct ipc_client_state *ics)
 	epoll_fd = -1;
 
 	// Multiple threads might be looking at these fields.
-	os_mutex_lock(&ics->server->global_state_lock);
+	os_mutex_lock(&ics->server->global_state.lock);
 
 	ipc_message_channel_close((struct ipc_message_channel *)&ics->imc);
 
-	// Reset the urth for the next client.
-	u_rt_helper_client_clear((struct u_rt_helper *)&ics->urth);
-
 	ics->num_swapchains = 0;
 
 	ics->server->threads[ics->server_thread_index].state = IPC_THREAD_STOPPING;
 	ics->server_thread_index = -1;
 	memset((void *)&ics->client_state, 0, sizeof(struct ipc_app_state));
 
-	// Make sure to reset the renderstate fully.
-	ics->rendering_state = false;
-	ics->render_state.num_layers = 0;
-	for (uint32_t i = 0; i < ARRAY_SIZE(ics->render_state.layers); ++i) {
-		volatile struct ipc_layer_entry *rl = &ics->render_state.layers[i];
-
-		rl->swapchain_ids[0] = 0;
-		rl->swapchain_ids[1] = 0;
-		rl->data.flip_y = false;
-		/*!
-		 * @todo this is redundant, we're setting both elements of a
-		 * union. Why? Can we just zero the whole render_state?
-		 */
-		rl->data.stereo.l.sub.image_index = 0;
-		rl->data.stereo.r.sub.image_index = 0;
-		rl->data.quad.sub.image_index = 0;
-		rl->data.cube.sub.image_index = 0;
-		rl->data.cylinder.sub.image_index = 0;
-		rl->data.equirect1.sub.image_index = 0;
-		rl->data.equirect2.sub.image_index = 0;
-
-		//! @todo set rects or array index?
-	}
-
 	// Destroy all swapchains now.
 	for (uint32_t j = 0; j < IPC_MAX_CLIENT_SWAPCHAINS; j++) {
 		// Drop our reference, does NULL checking. Cast away volatile.
@@ -168,12 +138,17 @@ client_loop(volatile struct ipc_client_state *ics)
 		IPC_TRACE(ics->server, "Destroyed swapchain %d.", j);
 	}
 
-	os_mutex_unlock(&ics->server->global_state_lock);
+	os_mutex_unlock(&ics->server->global_state.lock);
+
+	// Cast away volatile.
+	xrt_comp_destroy((struct xrt_compositor **)&ics->xc);
 
 	// Should we stop the server when a client disconnects?
 	if (ics->server->exit_on_disconnect) {
 		ics->server->running = false;
 	}
+
+	ipc_server_deactivate_session(ics);
 }
 
 
@@ -190,7 +165,5 @@ ipc_server_client_thread(void *_ics)
 
 	client_loop(ics);
 
-	update_server_state(ics->server);
-
 	return NULL;
 }
diff --git a/src/xrt/ipc/server/ipc_server_process.c b/src/xrt/ipc/server/ipc_server_process.c
index c550ce421..089cfc04c 100644
--- a/src/xrt/ipc/server/ipc_server_process.c
+++ b/src/xrt/ipc/server/ipc_server_process.c
@@ -50,6 +50,7 @@ extern void
 oxr_sdl2_hack_stop(void **hack_ptr);
 /* ---- HACK ---- */
 
+
 /*
  *
  * Defines and helpers.
@@ -59,11 +60,12 @@ oxr_sdl2_hack_stop(void **hack_ptr);
 DEBUG_GET_ONCE_BOOL_OPTION(exit_on_disconnect, "IPC_EXIT_ON_DISCONNECT", false)
 DEBUG_GET_ONCE_LOG_OPTION(ipc_log, "IPC_LOG", U_LOGGING_WARN)
 
-struct _z_sort_data
-{
-	int32_t index;
-	int32_t z_order;
-};
+
+/*
+ *
+ * Idev functions.
+ *
+ */
 
 static void
 init_idev(struct ipc_device *idev, struct xrt_device *xdev)
@@ -89,13 +91,12 @@ teardown_idev(struct ipc_device *idev)
  * Static functions.
  *
  */
+
 static void
 teardown_all(struct ipc_server *s)
 {
 	u_var_remove_root(s);
 
-	xrt_comp_native_destroy(&s->xcn);
-
 	xrt_syscomp_destroy(&s->xsysc);
 
 	for (size_t i = 0; i < IPC_SERVER_NUM_XDEVS; i++) {
@@ -106,7 +107,7 @@ teardown_all(struct ipc_server *s)
 
 	ipc_server_mainloop_deinit(&s->ml);
 
-	os_mutex_destroy(&s->global_state_lock);
+	os_mutex_destroy(&s->global_state.lock);
 }
 
 static int
@@ -333,7 +334,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
 	volatile struct ipc_client_state *ics = NULL;
 	int32_t cs_index = -1;
 
-	os_mutex_lock(&vs->global_state_lock);
+	os_mutex_lock(&vs->global_state.lock);
 
 	// find the next free thread in our array (server_thread_index is -1)
 	// and have it handle this connection
@@ -349,7 +350,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
 		close(fd);
 
 		// Unlock when we are done.
-		os_mutex_unlock(&vs->global_state_lock);
+		os_mutex_unlock(&vs->global_state.lock);
 
 		U_LOG_E("Max client count reached!");
 		return;
@@ -361,7 +362,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
 		close(fd);
 
 		// Unlock when we are done.
-		os_mutex_unlock(&vs->global_state_lock);
+		os_mutex_unlock(&vs->global_state.lock);
 
 		U_LOG_E("Client state management error!");
 		return;
@@ -381,7 +382,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
 	os_thread_start(&it->thread, ipc_server_client_thread, (void *)ics);
 
 	// Unlock when we are done.
-	os_mutex_unlock(&vs->global_state_lock);
+	os_mutex_unlock(&vs->global_state.lock);
 }
 
 static int
@@ -432,17 +433,6 @@ init_all(struct ipc_server *s)
 		return ret;
 	}
 
-	struct xrt_session_info xsi = {
-	    .is_overlay = false,
-	    .flags = 0,
-	    .z_order = 0,
-	};
-	ret = xrt_syscomp_create_native_compositor(s->xsysc, &xsi, &s->xcn);
-	if (ret < 0) {
-		teardown_all(s);
-		return ret;
-	}
-
 	ret = init_shm(s);
 	if (ret < 0) {
 		teardown_all(s);
@@ -455,12 +445,7 @@ init_all(struct ipc_server *s)
 		return ret;
 	}
 
-	// Init all of the render timing helpers.
-	for (size_t i = 0; i < ARRAY_SIZE(s->threads); i++) {
-		u_rt_helper_init((struct u_rt_helper *)&s->threads[i].ics.urth);
-	}
-
-	ret = os_mutex_init(&s->global_state_lock);
+	ret = os_mutex_init(&s->global_state.lock);
 	if (ret < 0) {
 		teardown_all(s);
 		return ret;
@@ -476,500 +461,31 @@ init_all(struct ipc_server *s)
 	return 0;
 }
 
-static uint32_t
-find_event_slot(volatile struct ipc_client_state *ics)
-{
-	uint64_t oldest_event_timestamp = UINT64_MAX;
-	uint32_t oldest_event_index = 0;
-	for (uint32_t i = 0; i < IPC_EVENT_QUEUE_SIZE; i++) {
-		if (ics->queued_events->timestamp < oldest_event_timestamp) {
-			oldest_event_index = i;
-		}
-		if (!ics->queued_events[i].pending) {
-			return i;
-		}
-	}
-
-	U_LOG_E("Event queue full - unconsumed event lost!");
-	return oldest_event_index;
-}
-
-static void
-transition_overlay_visibility(volatile struct ipc_client_state *ics, bool visible)
-{
-	uint32_t event_slot = find_event_slot(ics);
-	uint64_t timestamp = os_monotonic_get_ns();
-
-	volatile struct ipc_queued_event *qe = &ics->queued_events[event_slot];
-
-	qe->timestamp = timestamp;
-	qe->pending = true;
-	qe->event.type = XRT_COMPOSITOR_EVENT_OVERLAY_CHANGE;
-	qe->event.overlay.visible = visible;
-}
-
-static void
-send_client_state(volatile struct ipc_client_state *ics)
-{
-	uint32_t event_slot = find_event_slot(ics);
-	uint64_t timestamp = os_monotonic_get_ns();
-
-	volatile struct ipc_queued_event *qe = &ics->queued_events[event_slot];
-
-	qe->timestamp = timestamp;
-	qe->pending = true;
-	qe->event.type = XRT_COMPOSITOR_EVENT_STATE_CHANGE;
-	qe->event.state.visible = ics->client_state.session_visible;
-	qe->event.state.focused = ics->client_state.session_focused;
-}
-
-static bool
-_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 device_id = layer->xdev_id;
-	// left
-	uint32_t lxsci = layer->swapchain_ids[0];
-	// right
-	uint32_t rxsci = layer->swapchain_ids[1];
-
-	struct xrt_device *xdev = get_xdev(ics, device_id);
-	struct xrt_swapchain *lxcs = ics->xscs[lxsci];
-	struct xrt_swapchain *rxcs = ics->xscs[rxsci];
-
-	if (lxcs == NULL || rxcs == NULL) {
-		U_LOG_E("Invalid swap chain for projection layer!");
-		return false;
-	}
-
-	if (xdev == NULL) {
-		U_LOG_E("Invalid xdev for projection layer!");
-		return false;
-	}
-
-	// Cast away volatile.
-	struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
-
-	xrt_comp_layer_stereo_projection(xc, xdev, lxcs, rxcs, data);
-
-	return true;
-}
-
-static bool
-_update_projection_layer_depth(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 l_xsci = layer->swapchain_ids[0];
-	// right
-	uint32_t r_xsci = layer->swapchain_ids[1];
-	// left
-	uint32_t l_d_xsci = layer->swapchain_ids[2];
-	// right
-	uint32_t r_d_xsci = layer->swapchain_ids[3];
-
-	struct xrt_device *xdev = get_xdev(ics, xdevi);
-	struct xrt_swapchain *l_xcs = ics->xscs[l_xsci];
-	struct xrt_swapchain *r_xcs = ics->xscs[r_xsci];
-	struct xrt_swapchain *l_d_xcs = ics->xscs[l_d_xsci];
-	struct xrt_swapchain *r_d_xcs = ics->xscs[r_d_xsci];
-
-	if (l_xcs == NULL || r_xcs == NULL || l_d_xcs == NULL || r_d_xcs == NULL) {
-		U_LOG_E("Invalid swap chain for projection layer!");
-		return false;
-	}
-
-	if (xdev == NULL) {
-		U_LOG_E("Invalid xdev for projection layer!");
-		return false;
-	}
-
-	// Cast away volatile.
-	struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
-
-	xrt_comp_layer_stereo_projection_depth(xc, xdev, l_xcs, r_xcs, l_d_xcs, r_d_xcs, data);
-
-	return true;
-}
-
-static bool
-do_single(struct xrt_compositor *xc,
-          volatile struct ipc_client_state *ics,
-          volatile struct ipc_layer_entry *layer,
-          uint32_t i,
-          const char *name,
-          struct xrt_device **out_xdev,
-          struct xrt_swapchain **out_xcs,
-          struct xrt_layer_data **out_data)
-{
-	uint32_t device_id = layer->xdev_id;
-	uint32_t sci = layer->swapchain_ids[0];
-
-	struct xrt_device *xdev = get_xdev(ics, device_id);
-	struct xrt_swapchain *xcs = ics->xscs[sci];
-
-	if (xcs == NULL) {
-		U_LOG_E("Invalid swapchain for '%u' layer, '%s'!", i, name);
-		return false;
-	}
-
-	if (xdev == NULL) {
-		U_LOG_E("Invalid xdev for '%u' layer, '%s'!", i, name);
-		return false;
-	}
-
-	// Cast away volatile.
-	struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
-
-	*out_xdev = xdev;
-	*out_xcs = xcs;
-	*out_data = data;
-
-	return true;
-}
-
-static bool
-_update_quad_layer(struct xrt_compositor *xc,
-                   volatile struct ipc_client_state *ics,
-                   volatile struct ipc_layer_entry *layer,
-                   uint32_t i)
-{
-	struct xrt_device *xdev;
-	struct xrt_swapchain *xcs;
-	struct xrt_layer_data *data;
-
-	if (!do_single(xc, ics, layer, i, "quad", &xdev, &xcs, &data)) {
-		return false;
-	}
-
-	xrt_comp_layer_quad(xc, xdev, xcs, data);
-
-	return true;
-}
-
-static bool
-_update_cube_layer(struct xrt_compositor *xc,
-                   volatile struct ipc_client_state *ics,
-                   volatile struct ipc_layer_entry *layer,
-                   uint32_t i)
-{
-	struct xrt_device *xdev;
-	struct xrt_swapchain *xcs;
-	struct xrt_layer_data *data;
-
-	if (!do_single(xc, ics, layer, i, "cube", &xdev, &xcs, &data)) {
-		return false;
-	}
-
-	xrt_comp_layer_cube(xc, xdev, xcs, data);
-
-	return true;
-}
-
-static bool
-_update_cylinder_layer(struct xrt_compositor *xc,
-                       volatile struct ipc_client_state *ics,
-                       volatile struct ipc_layer_entry *layer,
-                       uint32_t i)
-{
-	struct xrt_device *xdev;
-	struct xrt_swapchain *xcs;
-	struct xrt_layer_data *data;
-
-	if (!do_single(xc, ics, layer, i, "cylinder", &xdev, &xcs, &data)) {
-		return false;
-	}
-
-	xrt_comp_layer_cylinder(xc, xdev, xcs, data);
-
-	return true;
-}
-
-static bool
-_update_equirect1_layer(struct xrt_compositor *xc,
-                        volatile struct ipc_client_state *ics,
-                        volatile struct ipc_layer_entry *layer,
-                        uint32_t i)
-{
-	struct xrt_device *xdev;
-	struct xrt_swapchain *xcs;
-	struct xrt_layer_data *data;
-
-	if (!do_single(xc, ics, layer, i, "equirect1", &xdev, &xcs, &data)) {
-		return false;
-	}
-
-	xrt_comp_layer_equirect1(xc, xdev, xcs, data);
-
-	return true;
-}
-
-static bool
-_update_equirect2_layer(struct xrt_compositor *xc,
-                        volatile struct ipc_client_state *ics,
-                        volatile struct ipc_layer_entry *layer,
-                        uint32_t i)
-{
-	struct xrt_device *xdev;
-	struct xrt_swapchain *xcs;
-	struct xrt_layer_data *data;
-
-	if (!do_single(xc, ics, layer, i, "equirect2", &xdev, &xcs, &data)) {
-		return false;
-	}
-
-	xrt_comp_layer_equirect2(xc, xdev, xcs, data);
-
-	return true;
-}
-
-static int
-_overlay_sort_func(const void *a, const void *b)
-{
-	struct _z_sort_data *oa = (struct _z_sort_data *)a;
-	struct _z_sort_data *ob = (struct _z_sort_data *)b;
-	if (oa->z_order < ob->z_order) {
-		return -1;
-	}
-	if (oa->z_order > ob->z_order) {
-		return 1;
-	}
-	return 0;
-}
-
-static bool
-_update_layers(struct ipc_server *s, struct xrt_compositor *xc)
-{
-	IPC_TRACE_MARKER();
-
-	struct _z_sort_data z_data[IPC_MAX_CLIENTS];
-
-	// initialise, and fill in overlay app data
-	for (int32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
-		volatile struct ipc_client_state *ics = &s->threads[i].ics;
-		z_data[i].index = -1;
-		z_data[i].z_order = -1;
-		// we need to create a list of overlay applications, sorted by z
-		if (ics->client_state.session_overlay) {
-			if (ics->client_state.session_active) {
-				z_data[i].index = i;
-				z_data[i].z_order = ics->client_state.z_order;
-			}
-		}
-	}
-
-	// ensure our primary application is enabled,
-	// and rendered first in the stack
-	if (s->active_client_index >= 0) {
-		z_data[s->active_client_index].index = s->active_client_index;
-		z_data[s->active_client_index].z_order = INT32_MIN;
-	}
-
-	// sort the stack array
-	qsort(z_data, IPC_MAX_CLIENTS, sizeof(struct _z_sort_data), _overlay_sort_func);
-
-	// render the layer stack
-	for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
-		struct _z_sort_data *zd = &z_data[i];
-		if (zd->index < 0) {
-			continue;
-		}
-
-		volatile struct ipc_client_state *ics = &s->threads[zd->index].ics;
-
-		for (uint32_t j = 0; j < ics->render_state.num_layers; j++) {
-			volatile struct ipc_layer_entry *layer = &ics->render_state.layers[j];
-
-			switch (layer->data.type) {
-			case XRT_LAYER_STEREO_PROJECTION:
-				if (!_update_projection_layer(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			case XRT_LAYER_STEREO_PROJECTION_DEPTH:
-				if (!_update_projection_layer_depth(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			case XRT_LAYER_QUAD:
-				if (!_update_quad_layer(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			case XRT_LAYER_CUBE:
-				if (!_update_cube_layer(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			case XRT_LAYER_CYLINDER:
-				if (!_update_cylinder_layer(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			case XRT_LAYER_EQUIRECT1:
-				if (!_update_equirect1_layer(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			case XRT_LAYER_EQUIRECT2:
-				if (!_update_equirect2_layer(xc, ics, layer, i)) {
-					return false;
-				}
-				break;
-			default: U_LOG_E("Unhandled layer type '%i'!", layer->data.type); break;
-			}
-		}
-	}
-
-	return true;
-}
-
-static void
-broadcast_timings(struct ipc_server *s,
-                  uint64_t predicted_display_time_ns,
-                  uint64_t predicted_display_period_ns,
-                  uint64_t diff_ns)
-{
-	IPC_TRACE_MARKER();
-
-	os_mutex_lock(&s->global_state_lock);
-
-	// Broadcast the new timing information to the helpers.
-	for (size_t i = 0; i < ARRAY_SIZE(s->threads); i++) {
-		struct u_rt_helper *urth = (struct u_rt_helper *)&s->threads[i].ics.urth;
-		u_rt_helper_new_sample(          //
-		    urth,                        //
-		    predicted_display_time_ns,   //
-		    predicted_display_period_ns, //
-		    diff_ns);                    //
-	}
-
-	os_mutex_unlock(&s->global_state_lock);
-}
-
 static int
 main_loop(struct ipc_server *s)
 {
-	struct xrt_compositor *xc = &s->xcn->base;
-
-	// make sure all our client connections have a handle to the
-	// compositor and consistent initial state
-
 	while (s->running) {
-		int64_t frame_id;
-		uint64_t predicted_display_time_ns;
-		uint64_t predicted_display_period_ns;
+		os_nanosleep(U_TIME_1S_IN_NS / 20);
 
-		xrt_comp_wait_frame(xc, &frame_id, &predicted_display_time_ns, &predicted_display_period_ns);
-
-		uint64_t now_ns = os_monotonic_get_ns();
-		uint64_t diff_ns = predicted_display_time_ns - now_ns;
-
-		broadcast_timings(s, predicted_display_time_ns, predicted_display_period_ns, diff_ns);
-
-		xrt_comp_begin_frame(xc, frame_id);
-		xrt_comp_layer_begin(xc, frame_id, 0, 0);
-
-		_update_layers(s, xc);
-
-		xrt_comp_layer_commit(xc, frame_id, XRT_GRAPHICS_SYNC_HANDLE_INVALID);
-
-		// Check polling last, so we know we have valid timing data.
+		// Check polling.
 		ipc_server_mainloop_poll(s, &s->ml);
 	}
 
 	return 0;
 }
 
-
 static void
-handle_overlay_client_events(volatile struct ipc_client_state *ics, int active_id, int prev_active_id)
-{
-	// this is an overlay session.
-	if (ics->client_state.session_overlay) {
-
-		// switch between main applications
-		if (active_id >= 0 && prev_active_id >= 0) {
-			transition_overlay_visibility(ics, false);
-			transition_overlay_visibility(ics, true);
-		}
-
-		// switch from idle to active application
-		if (active_id >= 0 && prev_active_id < 0) {
-			transition_overlay_visibility(ics, true);
-		}
-
-		// switch from active application to idle
-		if (active_id < 0 && prev_active_id >= 0) {
-			transition_overlay_visibility(ics, false);
-		}
-	}
-}
-
-static void
-handle_focused_client_events(volatile struct ipc_client_state *ics, int active_id, int prev_active_id)
-{
-
-	// if our prev active id is -1 and our cur active id is -1, we
-	// can bail out early
-
-	if (active_id == -1 && prev_active_id == -1) {
-		return;
-	}
-
-	// set visibility/focus to false on all applications
-	ics->client_state.session_focused = false;
-	ics->client_state.session_visible = false;
-
-	// do we have a primary application?
-	if (active_id >= 0) {
-
-		// if we are an overlay, we are always visible
-		// if we have a primary application
-		if (ics->client_state.session_overlay) {
-			ics->client_state.session_visible = true;
-		}
-
-		// set visible + focused if we are the primary
-		// application
-		if (ics->server_thread_index == active_id) {
-			ics->client_state.session_visible = true;
-			ics->client_state.session_focused = true;
-		}
-		send_client_state(ics);
-		return;
-	}
-
-	// no primary application, set all overlays to synchronised
-	// state
-	if (ics->client_state.session_overlay) {
-		ics->client_state.session_focused = false;
-		ics->client_state.session_visible = false;
-		send_client_state(ics);
-	}
-}
-
-void
 init_server_state(struct ipc_server *s)
 {
-
 	// set up initial state for global vars, and each client state
 
-	s->active_client_index = -1; // we start off with no active client.
-	s->last_active_client_index = -1;
+	s->global_state.active_client_index = -1; // we start off with no active client.
+	s->global_state.last_active_client_index = -1;
 	s->current_slot_index = 0;
 
 	for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
 		volatile struct ipc_client_state *ics = &s->threads[i].ics;
 		ics->server = s;
-		ics->xc = &s->xcn->base;
 		ics->server_thread_index = -1;
 	}
 }
@@ -977,26 +493,105 @@ init_server_state(struct ipc_server *s)
 
 /*
  *
- * Exported functions.
+ * Client management functions.
  *
  */
 
-void
-update_server_state(struct ipc_server *s)
+static void
+handle_overlay_client_events(volatile struct ipc_client_state *ics, int active_id, int prev_active_id)
 {
-	// multiple threads could call this at the same time.
-	os_mutex_lock(&s->global_state_lock);
+	// Is an overlay session?
+	if (!ics->client_state.session_overlay) {
+		return;
+	}
 
+	// Does this client have a compositor yet?
+	if (ics->xc) {
+		return;
+	}
+
+	// Switch between main applications
+	if (active_id >= 0 && prev_active_id >= 0) {
+		xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, false);
+		xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, true);
+	}
+
+	// Switch from idle to active application
+	if (active_id >= 0 && prev_active_id < 0) {
+		xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, true);
+	}
+
+	// Switch from active application to idle
+	if (active_id < 0 && prev_active_id >= 0) {
+		xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, false);
+	}
+}
+
+static void
+handle_focused_client_events(volatile struct ipc_client_state *ics, int active_id, int prev_active_id)
+{
+	// Set start z_order at the bottom.
+	int64_t z_order = INT64_MIN;
+
+	// Set visibility/focus to false on all applications.
+	bool focused = false;
+	bool visible = false;
+
+	// Set visible + focused if we are the primary application
+	if (ics->server_thread_index == active_id) {
+		visible = true;
+		focused = true;
+		z_order = INT64_MIN;
+	}
+
+	// Set all overlays to always active and focused.
+	if (ics->client_state.session_overlay) {
+		visible = true;
+		focused = true;
+		z_order = ics->client_state.z_order;
+	}
+
+	ics->client_state.session_visible = visible;
+	ics->client_state.session_focused = focused;
+	ics->client_state.z_order = z_order;
+
+	if (ics->xc != NULL) {
+		xrt_syscomp_set_state(ics->server->xsysc, ics->xc, visible, focused);
+		xrt_syscomp_set_z_order(ics->server->xsysc, ics->xc, z_order);
+	}
+}
+
+static void
+flush_state_to_all_clients_locked(struct ipc_server *s)
+{
+	for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
+		volatile struct ipc_client_state *ics = &s->threads[i].ics;
+
+		// Not running?
+		if (ics->server_thread_index < 0) {
+			continue;
+		}
+
+		handle_focused_client_events(ics, s->global_state.active_client_index,
+		                             s->global_state.last_active_client_index);
+		handle_overlay_client_events(ics, s->global_state.active_client_index,
+		                             s->global_state.last_active_client_index);
+	}
+}
+
+static void
+update_server_state_locked(struct ipc_server *s)
+{
 	// if our client that is set to active is still active,
 	// and it is the same as our last active client, we can
 	// early-out, as no events need to be sent
 
-	if (s->active_client_index >= 0) {
+	if (s->global_state.active_client_index >= 0) {
 
-		volatile struct ipc_client_state *ics = &s->threads[s->active_client_index].ics;
+		volatile struct ipc_client_state *ics = &s->threads[s->global_state.active_client_index].ics;
 
-		if (ics->client_state.session_active && s->active_client_index == s->last_active_client_index) {
-			os_mutex_unlock(&s->global_state_lock);
+		if (ics->client_state.session_active &&
+		    s->global_state.active_client_index == s->global_state.last_active_client_index) {
 			return;
 		}
 	}
@@ -1026,33 +621,101 @@ update_server_state(struct ipc_server *s)
 	// if our currently-set active primary application is not
 	// actually active/displayable, use the fallback application
 	// instead.
-	volatile struct ipc_client_state *ics = &s->threads[s->active_client_index].ics;
-	if (!(ics->client_state.session_overlay == false && s->active_client_index >= 0 &&
+	volatile struct ipc_client_state *ics = &s->threads[s->global_state.active_client_index].ics;
+	if (!(ics->client_state.session_overlay == false && s->global_state.active_client_index >= 0 &&
 	      ics->client_state.session_active)) {
-		s->active_client_index = fallback_active_application;
+		s->global_state.active_client_index = fallback_active_application;
 	}
 
 
 	// if we have no applications to fallback to, enable the idle
 	// wallpaper.
 	if (set_idle) {
-		s->active_client_index = -1;
+		s->global_state.active_client_index = -1;
 	}
 
-	for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
+	flush_state_to_all_clients_locked(s);
 
-		volatile struct ipc_client_state *ics = &s->threads[i].ics;
-		if (ics->server_thread_index >= 0) {
+	s->global_state.last_active_client_index = s->global_state.active_client_index;
+}
 
-			handle_focused_client_events(ics, s->active_client_index, s->last_active_client_index);
 
-			handle_overlay_client_events(ics, s->active_client_index, s->last_active_client_index);
-		}
+/*
+ *
+ * Exported functions.
+ *
+ */
+
+void
+ipc_server_set_active_client(struct ipc_server *s, int client_id)
+{
+	os_mutex_lock(&s->global_state.lock);
+
+	if (client_id == s->global_state.active_client_index) {
+		os_mutex_unlock(&s->global_state.lock);
+		return;
 	}
 
-	s->last_active_client_index = s->active_client_index;
 
-	os_mutex_unlock(&s->global_state_lock);
+
+	os_mutex_unlock(&s->global_state.lock);
+}
+
+void
+ipc_server_activate_session(volatile struct ipc_client_state *ics)
+{
+	struct ipc_server *s = ics->server;
+
+	// Already active, noop.
+	if (ics->client_state.session_active) {
+		return;
+	}
+
+	assert(ics->server_thread_index >= 0);
+
+	// Multiple threads could call this at the same time.
+	os_mutex_lock(&s->global_state.lock);
+
+	ics->client_state.session_active = true;
+
+	if (ics->client_state.session_overlay) {
+		// For new active overlay sessions only update this session.
+		handle_focused_client_events(ics, s->global_state.active_client_index,
+		                             s->global_state.last_active_client_index);
+		handle_overlay_client_events(ics, s->global_state.active_client_index,
+		                             s->global_state.last_active_client_index);
+	} else {
+		// For new active regular sessions update all clients.
+		update_server_state_locked(s);
+	}
+
+	os_mutex_unlock(&s->global_state.lock);
+}
+
+void
+ipc_server_deactivate_session(volatile struct ipc_client_state *ics)
+{
+	struct ipc_server *s = ics->server;
+
+	// Multiple threads could call this at the same time.
+	os_mutex_lock(&s->global_state.lock);
+
+	ics->client_state.session_active = false;
+
+	update_server_state_locked(s);
+
+	os_mutex_unlock(&s->global_state.lock);
+}
+
+void
+ipc_server_update_state(struct ipc_server *s)
+{
+	// Multiple threads could call this at the same time.
+	os_mutex_lock(&s->global_state.lock);
+
+	update_server_state_locked(s);
+
+	os_mutex_unlock(&s->global_state.lock);
 }
 
 #ifndef XRT_OS_ANDROID
diff --git a/src/xrt/ipc/shared/ipc_protocol.h b/src/xrt/ipc/shared/ipc_protocol.h
index 2da3add08..4625b697d 100644
--- a/src/xrt/ipc/shared/ipc_protocol.h
+++ b/src/xrt/ipc/shared/ipc_protocol.h
@@ -151,6 +151,7 @@ struct ipc_layer_entry
  */
 struct ipc_layer_slot
 {
+	uint64_t display_time_ns;
 	enum xrt_blend_mode env_blend_mode;
 	uint32_t num_layers;
 	struct ipc_layer_entry layers[IPC_MAX_LAYERS];
diff --git a/src/xrt/ipc/shared/proto.json b/src/xrt/ipc/shared/proto.json
index 9da7a32c3..cdd6dd936 100644
--- a/src/xrt/ipc/shared/proto.json
+++ b/src/xrt/ipc/shared/proto.json
@@ -72,13 +72,12 @@
 		]
 	},
 
-	"compositor_wait_frame": {
+	"compositor_predict_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"}
+			{"name": "predicted_display_time", "type": "uint64_t"},
+			{"name": "predicted_display_period", "type": "uint64_t"}
 		]
 	},