[RFC Weston 10/10] window: prevent EGL sub-surface deadlock

Pekka Paalanen ppaalanen at gmail.com
Fri Feb 22 07:07:54 PST 2013


Mesa's eglSwapBuffers() waits for the frame event from the previous
swapBuffers, before it returns. Apparently this cannot be disabled with
eglSwapInterval().

When a sub-surface contains an EGL widget, and the commit mode is
parent-cached, the frame events will not be delivered to EGL until the
parent surface gets committed. Therefore rendering the EGL widget twice
would lead to a deadlock.

When the window is being resized, we need to force a repaint of the EGL
widget, too, to make the whole window consistent. For that, we need to
make sure the frame event from the previous eglSwapBuffers() actually
arrives.

This patch adds an extra wl_surface.commit(parent), when the window is
being resized, which should guarantee, that the previous eglSwapBuffers
gets its event.

To properly handle an EGL widget in a sub-surface, running in its own
thread, the EGL widget's automatic updates should be paused before
sending the extra wl_surface.commit(parent). A natural place for the
pause would be in the widget's resize hook. However, wl_surface.commit
cannot be called right after resize hooks, because it would commit
new, incomplete surface state.

Therefore this patch is not enough for threaded toytoolkit applications.
Luckily those do not exist yet.
---
 clients/window.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/clients/window.c b/clients/window.c
index ca6fb69..81c187b 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -3357,6 +3357,36 @@ surface_resize(struct surface *surface)
 }
 
 static void
+hack_prevent_EGL_sub_surface_deadlock(struct window *window)
+{
+	/*
+	 * This hack should be removed, when EGL respects
+	 * eglSwapInterval(0).
+	 *
+	 * If this window has sub-surfaces, especially a free-running
+	 * EGL-widget, we need to post the parent surface once with
+	 * all the old state to guarantee, that the EGL-widget will
+	 * receive its frame callback soon. Otherwise, a forced call
+	 * to eglSwapBuffers may end up blocking, waiting for a frame
+	 * event that will never come, because we will commit the parent
+	 * surface with all new state only after eglSwapBuffers returns.
+	 *
+	 * This assumes, that:
+	 * 1. When the EGL widget's resize hook is called, it pauses.
+	 * 2. When the EGL widget's redraw hook is called, it forces a
+	 *    repaint and a call to eglSwapBuffers(), and maybe resumes.
+	 * In a single threaded application condition 1 is a no-op.
+	 *
+	 * XXX: This should actually be after the surface_resize() calls,
+	 * but cannot, because then it would commit the incomplete state
+	 * accumulated from the widget resize hooks.
+	 */
+	if (window->subsurface_list.next != &window->main_surface->link ||
+	    window->subsurface_list.prev != &window->main_surface->link)
+		wl_surface_commit(window->main_surface->surface);
+}
+
+static void
 idle_resize(struct window *window)
 {
 	struct surface *surface;
@@ -3364,6 +3394,8 @@ idle_resize(struct window *window)
 	window->resize_needed = 0;
 	window->redraw_needed = 1;
 
+	hack_prevent_EGL_sub_surface_deadlock(window);
+
 	widget_set_allocation(window->main_surface->widget,
 			      window->pending_allocation.x,
 			      window->pending_allocation.y,
-- 
1.7.12.4



More information about the wayland-devel mailing list