summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorDudemanguy <random342@airmail.cc>2021-05-27 15:22:21 -0500
committerDudemanguy <random342@airmail.cc>2021-05-28 16:11:01 +0000
commitd7f3d1fff75a07bef91eef0edb9ae7db6d590637 (patch)
treeed5a9278b7b72a4b20c036e0cf4265581e34a93f /video/out
parent151b03987947542c01624fc1819851d0bc6c2b28 (diff)
downloadmpv-d7f3d1fff75a07bef91eef0edb9ae7db6d590637.tar.bz2
mpv-d7f3d1fff75a07bef91eef0edb9ae7db6d590637.tar.xz
wayland: refactor dispatching events
This was originally just a bugfix for a race condition, but the scope expanded a bit. Currently, the wayland code does a prepare_read -> dispatch_pending -> display_flush -> read_events -> dispatch_pending routine that's basically straight from the wayland client API documentation. This essentially just queues up all the wayland events mpv has and dispatches them to the compositor. We do this for blocking purposes on every frame we render. A very similar thing is done for wait_events from the VO. This code can pretty easily be unified and split off into a separate function, vo_wayland_dispatch_events. vo_wayland_wait_frame will call this function in a while loop (which will break either on timeout or if we receive frame callback from the compositor). wait_events needs to just check this in case we get some state change on the wakeup_pipe (i.e. waking up from the paused state). As for the actual bugfix part of this, it's a slight regression from c26d833. The toplevel config event always forced a redraw when a surface became activated again. This is for something like displaying cover art on a music file. If the window was originally out of view and then later brought back into focus, no picture would be rendered (i.e. the window is just black). That's because something like cover art is just 1 frame and the VO stops doing any other additional rendering. If you miss that 1 frame, nothing would show up ever again. The fix in this case is to always just force a redraw when the mpv window comes back into view. Well with the aforementioned commit, we stopped doing wl_display_roundtrip calls on every frame. That means we no longer do roundtrip blocking calls. We just be sure to queue up all of the events we have and then dispatch them. Because wayland is fundamentally an asynchronous protocol, there's no guarantee what order these events would be processed in. This meant that on occasion, a vo_wayland_wait_frame call (this could occur multiple times depending on the exact situation) would occur before the compositor would send back frame callback. That would result in the aforementioned bug of having just a black window. The fix, in this case, is to just do a vo_wayland_wait_frame call directly before we force the VO to do a redraw. Note that merely dispatching events isn't enough because we specifically need to wait for the compositor to give us frame callback before doing a new render. P.S. fix a typo too.
Diffstat (limited to 'video/out')
-rw-r--r--video/out/wayland_common.c80
1 files changed, 34 insertions, 46 deletions
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 3dcc9641e8..850c8d362d 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -1181,6 +1181,7 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel,
if (wl->activated) {
/* If the surface comes back into view, force a redraw. */
+ vo_wayland_wait_frame(wl);
wl->pending_vo_events |= VO_EVENT_EXPOSE;
}
}
@@ -1783,6 +1784,36 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
return VO_NOTIMPL;
}
+static void vo_wayland_dispatch_events(struct vo_wayland_state *wl, int nfds, int timeout)
+{
+ struct pollfd fds[2] = {
+ {.fd = wl->display_fd, .events = POLLIN },
+ {.fd = wl->wakeup_pipe[0], .events = POLLIN },
+ };
+
+ while (wl_display_prepare_read(wl->display) != 0)
+ wl_display_dispatch_pending(wl->display);
+ wl_display_flush(wl->display);
+
+ poll(fds, nfds, timeout);
+
+ if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ MP_FATAL(wl, "Error occurred on the display fd, closing\n");
+ wl_display_cancel_read(wl->display);
+ close(wl->display_fd);
+ wl->display_fd = -1;
+ mp_input_put_key(wl->vo->input_ctx, MP_KEY_CLOSE_WIN);
+ } else {
+ wl_display_read_events(wl->display);
+ }
+
+ if (fds[0].revents & POLLIN)
+ wl_display_dispatch_pending(wl->display);
+
+ if (fds[1].revents & POLLIN)
+ mp_flush_wakeup_pipe(wl->wakeup_pipe[0]);
+}
+
void vo_wayland_set_opaque_region(struct vo_wayland_state *wl, int alpha)
{
const int32_t width = wl->scaling * mp_rect_w(wl->geometry);
@@ -1868,10 +1899,6 @@ void vo_wayland_wakeup(struct vo *vo)
void vo_wayland_wait_frame(struct vo_wayland_state *wl)
{
int64_t vblank_time = 0;
- struct pollfd fds[1] = {
- {.fd = wl->display_fd, .events = POLLIN },
- };
-
/* We need some vblank interval to use for the timeout in
* this function. The order of preference of values to use is:
* 1. vsync duration from presentation time
@@ -1895,28 +1922,14 @@ void vo_wayland_wait_frame(struct vo_wayland_state *wl)
int64_t finish_time = mp_time_us() + vblank_time;
while (wl->frame_wait && finish_time > mp_time_us()) {
-
int poll_time = ceil((double)(finish_time - mp_time_us()) / 1000);
if (poll_time < 0) {
poll_time = 0;
}
-
- while (wl_display_prepare_read(wl->display) != 0)
- wl_display_dispatch_pending(wl->display);
- wl_display_flush(wl->display);
-
- poll(fds, 1, poll_time);
-
- if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
- wl_display_cancel_read(wl->display);
- } else {
- wl_display_read_events(wl->display);
- }
-
- wl_display_dispatch_pending(wl->display);
+ vo_wayland_dispatch_events(wl, 1, poll_time);
}
- /* If the compositor does not have presentatiom time, we cannot be sure
+ /* If the compositor does not have presentation time, we cannot be sure
* that this wait is accurate. Do a hacky block with wl_display_roundtrip. */
if (!wl->presentation && !wl_display_get_error(wl->display))
wl_display_roundtrip(wl->display);
@@ -1945,33 +1958,8 @@ void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us)
if (wl->display_fd == -1)
return;
- struct pollfd fds[2] = {
- {.fd = wl->display_fd, .events = POLLIN },
- {.fd = wl->wakeup_pipe[0], .events = POLLIN },
- };
-
int64_t wait_us = until_time_us - mp_time_us();
int timeout_ms = MPCLAMP((wait_us + 999) / 1000, 0, 10000);
- while (wl_display_prepare_read(wl->display) != 0)
- wl_display_dispatch_pending(wl->display);
- wl_display_flush(wl->display);
-
- poll(fds, 2, timeout_ms);
-
- if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
- MP_FATAL(wl, "Error occurred on the display fd, closing\n");
- wl_display_cancel_read(wl->display);
- close(wl->display_fd);
- wl->display_fd = -1;
- mp_input_put_key(vo->input_ctx, MP_KEY_CLOSE_WIN);
- } else {
- wl_display_read_events(wl->display);
- }
-
- if (fds[0].revents & POLLIN)
- wl_display_dispatch_pending(wl->display);
-
- if (fds[1].revents & POLLIN)
- mp_flush_wakeup_pipe(wl->wakeup_pipe[0]);
+ vo_wayland_dispatch_events(wl, 2, timeout_ms);
}