From 4bb5bc005f8ffea31fa104a7238b855c7c20cba6 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 29 Jan 2016 11:19:03 -0500 Subject: [PATCH] wayland: Don't call set_busy twice on the same surface If the compositor is using a shared memory buffer allocated by a client, then it's the client's responsibility to refrain from destroying the buffer until the compositor releases it. This is accomplished by taking a reference to the cairo surface assocatiated with the buffer after a frame, and dropping the reference when the compositor releases the buffer. In some cases though, the compositor doesn't release the buffer until a new buffer is set, so if we have staged drawing before the frame completes we can end up taking multiple references to the buffer and keeping it alive after it's released. This commit solves the problem by ensuring we only call _gdk_wayland_shm_surface_set_busy if isn't already busy. https://bugzilla.gnome.org/show_bug.cgi?id=761312 --- gdk/wayland/gdkdisplay-wayland.c | 3 +++ gdk/wayland/gdkwindow-wayland.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index f200800..a589756 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -1020,54 +1020,57 @@ _gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display, width*scale, height*scale, stride, WL_SHM_FORMAT_ARGB8888); wl_buffer_add_listener (data->buffer, &buffer_listener, surface); cairo_surface_set_user_data (surface, &gdk_wayland_cairo_key, data, gdk_wayland_cairo_surface_destroy); cairo_surface_set_device_scale (surface, scale, scale); status = cairo_surface_status (surface); if (status != CAIRO_STATUS_SUCCESS) { g_critical (G_STRLOC ": Unable to create Cairo image surface: %s", cairo_status_to_string (status)); } return surface; } struct wl_buffer * _gdk_wayland_shm_surface_get_wl_buffer (cairo_surface_t *surface) { GdkWaylandCairoSurfaceData *data = cairo_surface_get_user_data (surface, &gdk_wayland_cairo_key); return data->buffer; } void _gdk_wayland_shm_surface_set_busy (cairo_surface_t *surface) { GdkWaylandCairoSurfaceData *data = cairo_surface_get_user_data (surface, &gdk_wayland_cairo_key); + + g_assert (!data->busy); + data->busy = TRUE; cairo_surface_reference (surface); } gboolean _gdk_wayland_shm_surface_get_busy (cairo_surface_t *surface) { GdkWaylandCairoSurfaceData *data = cairo_surface_get_user_data (surface, &gdk_wayland_cairo_key); return data->busy; } gboolean _gdk_wayland_is_shm_surface (cairo_surface_t *surface) { return cairo_surface_get_user_data (surface, &gdk_wayland_cairo_key) != NULL; } GdkWaylandSelection * gdk_wayland_display_get_selection (GdkDisplay *display) { GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (display); return wayland_display->selection; } diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c index 8e74ef4..6dac820 100644 --- a/gdk/wayland/gdkwindow-wayland.c +++ b/gdk/wayland/gdkwindow-wayland.c @@ -420,61 +420,61 @@ on_frame_clock_before_paint (GdkFrameClock *clock, timings->predicted_presentation_time = presentation_time + refresh_interval; } else { /* As above, but we don't actually know the phase of the vblank, * so just assume that we're half way through a refresh cycle. */ timings->predicted_presentation_time = timings->frame_time + refresh_interval / 2 + refresh_interval; } } static void on_frame_clock_after_paint (GdkFrameClock *clock, GdkWindow *window) { GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl); struct wl_callback *callback; if (!impl->pending_commit) return; impl->pending_commit = FALSE; impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock); impl->awaiting_frame = TRUE; callback = wl_surface_frame (impl->surface); wl_callback_add_listener (callback, &frame_listener, window); _gdk_frame_clock_freeze (clock); wl_surface_commit (impl->surface); - if (_gdk_wayland_is_shm_surface (impl->cairo_surface)) + if (_gdk_wayland_is_shm_surface (impl->cairo_surface) && !_gdk_wayland_shm_surface_get_busy (impl->cairo_surface)) _gdk_wayland_shm_surface_set_busy (impl->cairo_surface); g_signal_emit (impl, signals[COMMITTED], 0); } static void window_update_scale (GdkWindow *window) { GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl); GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window)); guint32 scale; GSList *l; if (wayland_display->compositor_version < WL_SURFACE_HAS_BUFFER_SCALE) { /* We can't set the scale on this surface */ return; } scale = 1; for (l = impl->outputs; l != NULL; l = l->next) { guint32 output_scale = _gdk_wayland_screen_get_output_scale (wayland_display->screen, l->data); scale = MAX (scale, output_scale); } /* Notify app that scale changed */ gdk_wayland_window_configure (window, window->width, window->height, scale); } -- 2.7.0