[PATCH 4/4] xwayland: Implement extended version of _NET_WM_SYNC_REQUEST protocol

Louis-Francis Ratté-Boulianne lfrb at collabora.com
Tue Jul 30 09:13:46 PDT 2013


"Extended synchronization provides a more general framework for redraw
coordination, including spontaneous application updates as well as resizing."

See documentation http://fishsoup.net/misc/wm-spec-synchronization.html
---
 src/compositor.c              |   3 +
 src/compositor.h              |   1 +
 src/xwayland/window-manager.c | 219 ++++++++++++++++++++++++++++++++++++++----
 src/xwayland/xwayland.h       |   2 +
 4 files changed, 206 insertions(+), 19 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index e9ba0fd..8138f71 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1323,6 +1323,8 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs)
 	if (output->dirty)
 		weston_output_update_matrix(output);
 
+	wl_signal_emit(&output->repaint_signal, msecs);
+
 	output->repaint(output, &output_damage);
 
 	pixman_region32_fini(&output_damage);
@@ -2741,6 +2743,7 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 
 	wl_signal_init(&output->frame_signal);
 	wl_signal_init(&output->destroy_signal);
+	wl_signal_init(&output->repaint_signal);
 	wl_list_init(&output->animation_list);
 	wl_list_init(&output->resource_list);
 
diff --git a/src/compositor.h b/src/compositor.h
index 57b206e..dccbbff 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -192,6 +192,7 @@ struct weston_output {
 	int dirty;
 	struct wl_signal frame_signal;
 	struct wl_signal destroy_signal;
+	struct wl_signal repaint_signal;
 	uint32_t frame_time;
 	int disable_planes;
 
diff --git a/src/xwayland/window-manager.c b/src/xwayland/window-manager.c
index 57a5d99..f083c8b 100644
--- a/src/xwayland/window-manager.c
+++ b/src/xwayland/window-manager.c
@@ -122,9 +122,15 @@ struct weston_wm_window {
 	int mapped;
 	xcb_sync_alarm_t sync_request_alarm;
 	xcb_sync_counter_t sync_request_counter;
+	int sync_request_counter_extended;
 	struct wl_event_source *sync_request_timer;
+	struct wl_listener frame_listener;
+	struct wl_listener repaint_listener;
 	int64_t sync_request_serial;
+	int64_t sync_request_wait_serial;
+	uint64_t frame_time;
 	int configure_pending;
+	int needs_frame_drawn;
 	int sync_disabled;
 	int wait_redraw;
 };
@@ -431,7 +437,12 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
 			break;
 		case XCB_SYNC_COUNTER:
 			counter = xcb_get_property_value(reply);
-			*(xcb_sync_counter_t *) p = *counter;
+			if (reply->value_len == 2) {
+				*(xcb_sync_counter_t *) p = counter[1];
+				window->sync_request_counter_extended = 1;
+			} else {
+				*(xcb_sync_counter_t *) p = *counter;
+			}
 			break;
 		case TYPE_WM_PROTOCOLS:
 			break;
@@ -746,12 +757,34 @@ weston_wm_window_set_net_wm_state(struct weston_wm_window *window)
 			    32, /* format */
 			    i, property);
 }
+ 
+static void
+weston_wm_window_update_frozen(struct weston_wm_window *window)
+{
+	int frozen = 0;
+
+	if (!window->sync_disabled) {
+		if (window->sync_request_counter_extended &&
+		    window->sync_request_serial % 2 == 1)
+			frozen = 1;
+
+		if (window->sync_request_serial < window->sync_request_wait_serial)
+			frozen = 1;
+
+		if (window->repaint_source)
+			frozen = 1;
+	}
+
+	window->wait_redraw = frozen;
+	/*if (window->surface)
+		window->surface->frozen = frozen;*/
+	weston_log("UPDATE FROZEN %i\n", frozen);
+}
 
 static void
 weston_wm_window_create_sync_alarm(struct weston_wm_window *window)
 {
 	struct weston_wm *wm = window->wm;
-	xcb_sync_int64_t value;
 	uint32_t mask;
 	xcb_sync_create_alarm_value_list_t value_list;
 
@@ -765,16 +798,29 @@ weston_wm_window_create_sync_alarm(struct weston_wm_window *window)
 		return;
 	}
 
-	value.hi = 0;
-	value.lo = 0;
-	window->sync_request_serial = 0;
-	xcb_sync_set_counter(wm->conn, window->sync_request_counter, value);
+	if (window->sync_request_counter_extended) {
+		xcb_sync_query_counter_cookie_t cookie;
+		xcb_sync_query_counter_reply_t *reply;
+
+		cookie = xcb_sync_query_counter(wm->conn, window->sync_request_counter);
+		reply = xcb_sync_query_counter_reply(wm->conn, cookie, NULL);
+		window->sync_request_serial = reply->counter_value.lo;
+		window->sync_request_serial += (int64_t) reply->counter_value.hi << 32;
+		free(reply);
+	} else {
+		xcb_sync_int64_t value;
+
+		value.hi = 0;
+		value.lo = 0;
+		window->sync_request_serial = 0;
+		xcb_sync_set_counter(wm->conn, window->sync_request_counter, value);
+	}
 
 	mask = (XCB_SYNC_CA_COUNTER | XCB_SYNC_CA_VALUE_TYPE |
 		XCB_SYNC_CA_VALUE | XCB_SYNC_CA_TEST_TYPE |
 		XCB_SYNC_CA_DELTA | XCB_SYNC_CA_EVENTS);
 	value_list.counter = window->sync_request_counter;
-	value_list.valueType = XCB_SYNC_VALUETYPE_ABSOLUTE;
+	value_list.valueType = XCB_SYNC_VALUETYPE_RELATIVE;
 	value_list.value.hi = 0;
 	value_list.value.lo = 1;
 	value_list.testType = XCB_SYNC_TESTTYPE_POSITIVE_COMPARISON;
@@ -798,7 +844,7 @@ sync_request_timeout(void *data)
 
 	weston_log("Sync request timed out. Temporarily disabling syncing.\n");
 	window->sync_disabled = 1;
-	window->wait_redraw = 0;
+	weston_wm_window_update_frozen(window);
 
 	if (window->configure_pending && !window->configure_source) {
 		window->configure_source =
@@ -814,8 +860,17 @@ weston_wm_window_send_sync_request(struct weston_wm_window *window)
 {
 	xcb_client_message_event_t client_message;
 	struct weston_wm *wm = window->wm;
-
-	window->sync_request_serial++;
+	int64_t wait_serial;
+
+	/* For the old style of _NET_WM_SYNC_REQUEST_COUNTER, we just have to
+	 * increase the value, but for the new "extended" style we need to
+	 * pick an even (unfrozen) value sufficiently ahead of the last serial
+	 * that we received from the client; the same code still works
+	 * for the old style. The increment of 240 is specified by the EWMH
+	 * and is (1 second) * (60fps) * (an increment of 4 per frame).
+	 */
+	wait_serial = window->sync_request_serial + 240;
+	window->sync_request_wait_serial = wait_serial;
 
 	client_message.response_type = XCB_CLIENT_MESSAGE;
 	client_message.format = 32;
@@ -823,15 +878,105 @@ weston_wm_window_send_sync_request(struct weston_wm_window *window)
 	client_message.type = wm->atom.wm_protocols;
 	client_message.data.data32[0] = wm->atom.net_wm_sync_request;
 	client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
-	client_message.data.data32[2] = window->sync_request_serial & 0xffffffff;
-	client_message.data.data32[3] = (window->sync_request_serial >> 32) & 0xffffffff;
+	client_message.data.data32[2] = wait_serial & 0xffffffff;
+	client_message.data.data32[3] = (wait_serial >> 32) & 0xffffffff;
+	client_message.data.data32[4] = window->sync_request_counter_extended;
 
-	weston_log("SEND SYNC REQUEST %llu\n", window->sync_request_serial);
+	weston_log("SEND SYNC REQUEST %llu\n", wait_serial);
 
 	xcb_send_event(wm->conn, 0, window->id, 0,
 		       (char *) &client_message);
 
 	wl_event_source_timer_update(window->sync_request_timer, 1000);
+	weston_wm_window_update_frozen(window);
+}
+
+static void
+weston_wm_window_send_frame_drawn(struct weston_wm_window *window, uint32_t msecs)
+{
+	xcb_client_message_event_t client_message;
+	struct weston_wm *wm = window->wm;
+	uint64_t usecs = msecs * 1000;
+
+	/* FIXME keep track of request serial for this frame */
+
+	client_message.response_type = XCB_CLIENT_MESSAGE;
+	client_message.format = 32;
+	client_message.window = window->id;
+	client_message.type = wm->atom.net_wm_frame_drawn;
+	client_message.data.data32[0] = window->sync_request_serial & 0xffffffff;
+	client_message.data.data32[1] = (window->sync_request_serial >> 32) & 0xffffffff;
+	client_message.data.data32[2] = usecs & 0xffffffff;
+	client_message.data.data32[3] = (usecs >> 32) & 0xffffffff;
+
+	weston_log("SEND FRAME DRAWN %llu %u\n", window->sync_request_serial, msecs);
+ 
+ 	xcb_send_event(wm->conn, 0, window->id, 0,
+ 		       (char *) &client_message);
+}
+
+static void
+weston_wm_window_send_frame_timings(struct weston_wm_window *window, uint32_t msecs)
+{
+	xcb_client_message_event_t client_message;
+	struct weston_wm *wm = window->wm;
+	int refresh_rate, refresh_interval;
+	uint64_t usecs = msecs * 1000;
+	uint32_t offset = 0;
+
+	if (usecs != 0) {
+		offset = usecs - window->frame_time;
+		if (offset == 0)
+			offset = 1;
+	}
+
+	refresh_rate = window->surface->output->current->refresh;
+	if (refresh_rate >= 1)
+		refresh_interval = 1000000000 / refresh_rate;
+	else
+		refresh_interval = 0;
+
+	client_message.response_type = XCB_CLIENT_MESSAGE;
+	client_message.format = 32;
+	client_message.window = window->id;
+	client_message.type = wm->atom.net_wm_frame_timings;
+	client_message.data.data32[0] = window->sync_request_serial & 0xffffffff;
+	client_message.data.data32[1] = (window->sync_request_serial >> 32) & 0xffffffff;
+	client_message.data.data32[2] = offset;
+	client_message.data.data32[3] = refresh_interval;
+	client_message.data.data32[4] = 0x80000000;
+
+	weston_log("SEND FRAME TIMINGS %llu %u\n", window->sync_request_serial, msecs);
+ 
+ 	xcb_send_event(wm->conn, 0, window->id, 0,
+ 		       (char *) &client_message);
+}
+
+static void
+weston_wm_window_repaint_notify(struct wl_listener *listener, void *data)
+{
+        struct weston_wm_window *window =
+                container_of(listener, struct weston_wm_window, repaint_listener);
+	struct weston_output *output = window->surface->output;
+	uint32_t msecs = (uint32_t) data;
+
+	window->frame_time = msecs * 1000;
+	window->needs_frame_drawn = 0;
+	wl_list_remove(&listener->link);
+	wl_signal_add(&output->frame_signal, &window->frame_listener);
+	weston_wm_window_send_frame_drawn(window, msecs);
+}
+
+static void
+weston_wm_window_frame_notify(struct wl_listener *listener, void *data)
+{
+        struct weston_wm_window *window =
+                container_of(listener, struct weston_wm_window, frame_listener);
+	struct weston_output *output = data;
+	uint32_t msecs = output->frame_time;
+
+	wl_list_remove(&listener->link);
+	weston_wm_window_send_frame_timings(window, msecs);
 }
 
 static void
@@ -1110,6 +1255,17 @@ weston_wm_window_schedule_repaint(struct weston_wm_window *window)
 }
 
 static void
+weston_wm_window_schedule_redraw(struct weston_wm_window *window)
+{
+	struct weston_output *output = window->surface->output;
+
+	window->needs_frame_drawn = 1;
+	wl_signal_add(&output->repaint_signal, &window->repaint_listener);
+	weston_surface_schedule_repaint(window->surface);
+}
+
+
+static void
 weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
 {
 	xcb_property_notify_event_t *property_notify =
@@ -1165,11 +1321,16 @@ weston_wm_window_create(struct weston_wm *wm,
 	window->map_notified = 0;
 	window->mapped = 0;
 	window->sync_request_counter = 0;
+	window->sync_request_counter_extended = 0;
 	window->sync_request_alarm = 0;
 	window->configure_pending = 0;
+	window->needs_frame_drawn = 0;
 	window->sync_disabled = 0;
 	window->wait_redraw = 0;
 
+	window->frame_listener.notify = weston_wm_window_frame_notify;
+	window->repaint_listener.notify = weston_wm_window_repaint_notify;
+
 	geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL);
 	/* technically we should use XRender and check the visual format's
 	alpha_mask, but checking depth is simpler and works in all known cases */
@@ -1394,6 +1555,8 @@ weston_wm_handle_sync_alarm_notify(struct weston_wm *wm,
 		(xcb_sync_alarm_notify_event_t *) event;
 	struct weston_wm_window *window;
 	int64_t counter_value = 0;
+	int no_delay_frame = 0;
+	int needs_frame_drawn = 0;
 
 	counter_value = alarm_event->counter_value.lo;
 	counter_value += (int64_t) alarm_event->counter_value.hi << 32;
@@ -1407,15 +1570,31 @@ weston_wm_handle_sync_alarm_notify(struct weston_wm *wm,
 		return;
 	}
 
-	wl_event_source_timer_update(window->sync_request_timer, 0);
-	window->sync_disabled = 0;
-	window->wait_redraw = 0;
+	if (window->sync_request_counter_extended && (counter_value % 2) == 0) {
+		needs_frame_drawn = 1;
+		no_delay_frame = (counter_value == window->sync_request_serial + 1);
+	}
 
-	if (window->configure_pending && !window->configure_source) {
+	window->sync_request_serial = counter_value;
+
+	if (window->sync_request_serial >= window->sync_request_wait_serial) {
+		wl_event_source_timer_update(window->sync_request_timer, 0);
+		window->sync_disabled = 0;
+	}
+
+	weston_wm_window_update_frozen(window);
+
+	if (window->configure_pending && !window->configure_source &&
+	    !window->wait_redraw) {
 		window->configure_source =
 			wl_event_loop_add_idle(wm->server->loop,
 					       weston_wm_window_configure, window);
 	}
+
+	if (no_delay_frame || (needs_frame_drawn &&
+	    counter_value >= window->sync_request_wait_serial)) {
+		weston_wm_window_schedule_redraw(window);
+	}
 }
 
 enum cursor_type {
@@ -1769,6 +1948,8 @@ weston_wm_get_resources(struct weston_wm *wm)
 
 		{ "_NET_WM_SYNC_REQUEST", F(atom.net_wm_sync_request) },
 		{ "_NET_WM_SYNC_REQUEST_COUNTER", F(atom.net_wm_sync_request_counter) },
+		{ "_NET_WM_FRAME_DRAWN", F(atom.net_wm_frame_drawn) },
+		{ "_NET_WM_FRAME_TIMINGS", F(atom.net_wm_frame_timings) },
 
 		{ "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
 		{ "_NET_SUPPORTING_WM_CHECK",
@@ -1930,7 +2111,7 @@ weston_wm_create(struct weston_xserver *wxs)
 	xcb_screen_iterator_t s;
 	uint32_t values[1];
 	int sv[2];
-	xcb_atom_t supported[5];
+	xcb_atom_t supported[6];
 
 	wm = malloc(sizeof *wm);
 	if (wm == NULL)
@@ -2002,6 +2183,7 @@ weston_wm_create(struct weston_xserver *wxs)
 	supported[2] = wm->atom.net_wm_state_fullscreen;
 	supported[3] = wm->atom.net_wm_sync_request;
 	supported[4] = wm->atom.net_wm_sync_request_counter;
+	supported[5] = wm->atom.net_wm_frame_drawn;
 	xcb_change_property(wm->conn,
 			    XCB_PROP_MODE_REPLACE,
 			    wm->screen->root,
@@ -2080,7 +2262,6 @@ weston_wm_window_configure(void *data)
 	int x, y, width, height;
 
 	if (window->sync_request_counter != 0 && !window->sync_disabled) {
-		window->wait_redraw = 1;
 		weston_wm_window_send_sync_request(window);
 	}
 
diff --git a/src/xwayland/xwayland.h b/src/xwayland/xwayland.h
index c609cf2..c07cfec 100644
--- a/src/xwayland/xwayland.h
+++ b/src/xwayland/xwayland.h
@@ -113,6 +113,8 @@ struct weston_wm {
 		xcb_atom_t		 net_wm_moveresize;
 		xcb_atom_t		 net_wm_sync_request;
 		xcb_atom_t		 net_wm_sync_request_counter;
+		xcb_atom_t		 net_wm_frame_drawn;
+		xcb_atom_t		 net_wm_frame_timings;
 		xcb_atom_t		 net_supporting_wm_check;
 		xcb_atom_t		 net_supported;
 		xcb_atom_t		 motif_wm_hints;
-- 
1.8.3.1



More information about the wayland-devel mailing list