[Mesa-dev] [PATCH 1/2] eglut_wayland: Drive event loop by frame events

Daniel Stone daniels at collabora.com
Mon Jul 17 11:00:05 UTC 2017


The eglut_wayland event loop attempted to use frame events to drive the
redraw loop. However, the frame events were being requested after
eglSwapBuffers (i.e. wl_surface_commit()), which means they would not
actually activate until the next eglSwapBuffers.

This was being balanced out by 'redisplay' being set immediately by
es2gears, which would do an instant-return poll before calling
eglSwapBuffers, which would internally wait on its own frame event, and
as a side effect trigger the frame event that eglut_wayland had just
placed, causing redraw to be called again. The result would be a
stuttering 40fps.

Rewrite the event loop, so that:
  - frame events are placed before eglSwapBuffers is called
  - 'redisplay' no longer triggers an immediate return from poll
  - the frame event handler redraws if redisplay is requested, and
    exits if not
  - if redisplay is requested whilst we do not have a pending frame
    event, redraw instantly

Reported-by: Daniel van Vugt <daniel.van.vugt at canonical.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=101814
Signed-off-by: Daniel Stone <daniels at collabora.com>
---
 src/egl/eglut/eglut_wayland.c | 83 +++++++++++++++++++++++++------------------
 1 file changed, 48 insertions(+), 35 deletions(-)

diff --git a/src/egl/eglut/eglut_wayland.c b/src/egl/eglut/eglut_wayland.c
index 968b33fe..6376acc0 100644
--- a/src/egl/eglut/eglut_wayland.c
+++ b/src/egl/eglut/eglut_wayland.c
@@ -94,6 +94,7 @@ _eglutNativeInitDisplay(void)
    wl_registry_destroy(registry);
 
    _eglut->surface_type = EGL_WINDOW_BIT;
+   _eglut->redisplay = 1;
 }
 
 void
@@ -149,19 +150,27 @@ static const struct wl_callback_listener frame_listener = {
 
 static void
 draw(void *data, struct wl_callback *callback, uint32_t time)
-{	
+{
    struct window *window = (struct window *)data;
    struct eglut_window *win = _eglut->current;
 
+   if (callback) {
+      wl_callback_destroy(callback);
+      window->callback = NULL;
+   }
+
+   /* Our client doesn't want to push another frame; go back to sleep. */
+   if (!_eglut->redisplay)
+      return;
+   _eglut->redisplay = 0;
+
    if (win->display_cb)
       win->display_cb();
-   eglSwapBuffers(_eglut->dpy, win->surface);
-
-   if (callback)
-      wl_callback_destroy(callback);
 
    window->callback = wl_surface_frame(window->surface);
    wl_callback_add_listener(window->callback, &frame_listener, window);
+
+   eglSwapBuffers(_eglut->dpy, win->surface);
 }
 
 void
@@ -170,53 +179,57 @@ _eglutNativeEventLoop(void)
    struct pollfd pollfd;
    int ret;
 
-   draw(&window, NULL, 0);
-
    pollfd.fd = wl_display_get_fd(display.display);
    pollfd.events = POLLIN;
    pollfd.revents = 0;
 
    while (1) {
-      wl_display_dispatch_pending(display.display);
-
-      if (_eglut->idle_cb)
-         _eglut->idle_cb();
+      /* If we need to flush but can't, don't do anything at all which could
+       * push further events into the socket. */
+      if (!(pollfd.events & POLLOUT)) {
+         wl_display_dispatch_pending(display.display);
+
+         if (_eglut->idle_cb)
+            _eglut->idle_cb();
+
+         /* Client wants to redraw, but we have no frame event to trigger the
+          * redraw; kickstart it by redrawing immediately. */
+         if (_eglut->redisplay && !window.callback)
+            draw(&window, NULL, 0);
+      }
 
       ret = wl_display_flush(display.display);
-      if (ret < 0 && errno == EAGAIN)
-         pollfd.events |= POLLOUT;
-      else if (ret < 0)
-         break;
-
-      if (poll(&pollfd, 1, _eglut->redisplay ? 0 : -1) == -1)
+      if (ret < 0 && errno != EAGAIN)
+         break; /* fatal error; socket is broken */
+      else if (ret < 0 && errno == EAGAIN)
+         pollfd.events |= POLLOUT; /* need to wait until we can flush */
+      else
+         pollfd.events &= ~POLLOUT; /* successfully flushed */
+
+      if (poll(&pollfd, 1, -1) == -1)
          break;
 
       if (pollfd.revents & (POLLERR | POLLHUP))
          break;
 
+      if (pollfd.events & POLLOUT) {
+	 if (!(pollfd.revents & POLLOUT))
+            continue; /* block until we can flush */
+         pollfd.events &= ~POLLOUT;
+      }
+
       if (pollfd.revents & POLLIN) {
          ret = wl_display_dispatch(display.display);
          if (ret == -1)
             break;
       }
 
-      if (pollfd.revents & POLLOUT) {
-         ret = wl_display_flush(display.display);
-         if (ret == 0)
-            pollfd.events &= ~POLLOUT;
-         else if (ret == -1 && errno != EAGAIN)
-            break;
-      }
-
-      if (_eglut->redisplay) {
-         struct eglut_window *win = _eglut->current;
-
-         _eglut->redisplay = 0;
-
-         if (win->display_cb)
-            win->display_cb();
-
-         eglSwapBuffers(_eglut->dpy, win->surface);
-      }
+      ret = wl_display_flush(display.display);
+      if (ret < 0 && errno != EAGAIN)
+         break; /* fatal error; socket is broken */
+      else if (ret < 0 && errno == EAGAIN)
+         pollfd.events |= POLLOUT; /* need to wait until we can flush */
+      else
+         pollfd.events &= ~POLLOUT; /* successfully flushed */
    }
 }
-- 
2.13.3



More information about the mesa-dev mailing list