[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