summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stone <daniels@collabora.com>2017-07-17 11:44:35 +0100
committerDaniel Stone <daniels@collabora.com>2017-07-17 11:58:40 +0100
commitc539078c5fa592134f6f8a95be51c8eb318f5c39 (patch)
tree669adced1b396816fa3581f137f7001ed87dc124
parent6718fac2dca0bba91242452d4dbe03ba34c15361 (diff)
eglut_wayland: Drive event loop by frame events
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@canonical.com> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=101814
-rw-r--r--src/egl/eglut/eglut_wayland.c83
1 files 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 */
}
}