diff options
author | Daniel Stone <daniels@collabora.com> | 2017-07-17 11:44:35 +0100 |
---|---|---|
committer | Daniel Stone <daniels@collabora.com> | 2017-07-17 11:58:40 +0100 |
commit | c539078c5fa592134f6f8a95be51c8eb318f5c39 (patch) | |
tree | 669adced1b396816fa3581f137f7001ed87dc124 | |
parent | 6718fac2dca0bba91242452d4dbe03ba34c15361 (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.c | 83 |
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 */ } } |