"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 _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
