diff --git a/video/out/vo.c b/video/out/vo.c index f195568eaa..8e63be75e6 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -134,6 +134,8 @@ struct vo_internal { int64_t nominal_vsync_interval; + bool external_renderloop_drive; + int64_t vsync_interval; int64_t *vsync_samples; int num_vsync_samples; @@ -788,11 +790,12 @@ static void wait_until(struct vo *vo, int64_t target) pthread_mutex_unlock(&in->lock); } -static bool render_frame(struct vo *vo) +bool vo_render_frame_external(struct vo *vo) { struct vo_internal *in = vo->in; struct vo_frame *frame = NULL; bool got_frame = false; + bool flipped = false; update_display_fps(vo); @@ -854,6 +857,7 @@ static bool render_frame(struct vo *vo) if (in->dropped_frame) { in->drop_count += 1; } else { + flipped = true; in->rendering = true; in->hasframe_rendered = true; int64_t prev_drop_count = vo->in->drop_count; @@ -904,6 +908,8 @@ static bool render_frame(struct vo *vo) done: talloc_free(frame); pthread_mutex_unlock(&in->lock); + if (in->external_renderloop_drive) + return flipped; return got_frame || (in->frame_queued && in->frame_queued->display_synced); } @@ -946,6 +952,44 @@ static void do_redraw(struct vo *vo) talloc_free(frame); } +static void drop_unrendered_frame(struct vo *vo) +{ + struct vo_internal *in = vo->in; + + pthread_mutex_lock(&in->lock); + + if (!in->frame_queued) + goto end; + + if ((in->frame_queued->pts + in->frame_queued->duration) > mp_time_us()) + goto end; + + MP_VERBOSE(vo, "Dropping unrendered frame (pts %li)\n", in->frame_queued->pts); + + talloc_free(in->frame_queued); + in->frame_queued = NULL; + in->hasframe = false; + pthread_cond_broadcast(&in->wakeup); + wakeup_core(vo); + +end: + pthread_mutex_unlock(&in->lock); +} + +void vo_enable_external_renderloop(struct vo *vo) +{ + struct vo_internal *in = vo->in; + MP_VERBOSE(vo, "Enabling event driven renderloop!\n"); + in->external_renderloop_drive = true; +} + +void vo_disable_external_renderloop(struct vo *vo) +{ + struct vo_internal *in = vo->in; + MP_VERBOSE(vo, "Disabling event driven renderloop!\n"); + in->external_renderloop_drive = false; +} + static void *vo_thread(void *ptr) { struct vo *vo = ptr; @@ -967,7 +1011,11 @@ static void *vo_thread(void *ptr) if (in->terminate) break; vo->driver->control(vo, VOCTRL_CHECK_EVENTS, NULL); - bool working = render_frame(vo); + bool working = false; + if (!in->external_renderloop_drive || !in->hasframe_rendered) + working = vo_render_frame_external(vo); + else + drop_unrendered_frame(vo); int64_t now = mp_time_us(); int64_t wait_until = now + (working ? 0 : (int64_t)1e9); @@ -980,7 +1028,7 @@ static void *vo_thread(void *ptr) wakeup_core(vo); } } - if (vo->want_redraw && !in->want_redraw) { + if (vo->want_redraw) { vo->want_redraw = false; in->want_redraw = true; wakeup_core(vo); diff --git a/video/out/vo.h b/video/out/vo.h index fc6cf394e4..995d6b97f5 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -433,6 +433,9 @@ void vo_query_formats(struct vo *vo, uint8_t *list); void vo_event(struct vo *vo, int event); int vo_query_and_reset_events(struct vo *vo, int events); struct mp_image *vo_get_current_frame(struct vo *vo); +void vo_enable_external_renderloop(struct vo *vo); +void vo_disable_external_renderloop(struct vo *vo); +bool vo_render_frame_external(struct vo *vo); void vo_set_queue_params(struct vo *vo, int64_t offset_us, int num_req_frames); int vo_get_num_req_frames(struct vo *vo); int64_t vo_get_vsync_interval(struct vo *vo); diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c index 6a971dd94b..95318d36df 100644 --- a/video/out/vo_gpu.c +++ b/video/out/vo_gpu.c @@ -203,7 +203,7 @@ static int control(struct vo *vo, uint32_t request, void *data) case VOCTRL_PAUSE: if (gl_video_showing_interpolated_frame(p->renderer)) vo->want_redraw = true; - return true; + break; case VOCTRL_PERFORMANCE_DATA: gl_video_perfdata(p->renderer, (struct voctrl_performance_data *)data); return true; diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index a0e734703b..19adf01df0 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -767,6 +767,9 @@ static void frame_callback(void *data, struct wl_callback *callback, uint32_t ti wl->frame_callback = wl_surface_frame(wl->surface); wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); + + if (!vo_render_frame_external(wl->vo)) + wl_surface_commit(wl->surface); } static const struct wl_callback_listener frame_listener = { @@ -785,6 +788,7 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id wl->surface = wl_compositor_create_surface(wl->compositor); wl->cursor_surface = wl_compositor_create_surface(wl->compositor); wl_surface_add_listener(wl->surface, &surface_listener, wl); + vo_enable_external_renderloop(wl->vo); wl->frame_callback = wl_surface_frame(wl->surface); wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); } @@ -1295,6 +1299,17 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg) *(char ***)arg = get_displays_spanned(wl); return VO_TRUE; } + case VOCTRL_PAUSE: { + wl_callback_destroy(wl->frame_callback); + wl->frame_callback = NULL; + vo_disable_external_renderloop(wl->vo); + return VO_TRUE; + } + case VOCTRL_RESUME: { + vo_enable_external_renderloop(wl->vo); + frame_callback(wl, NULL, 0); + return VO_TRUE; + } case VOCTRL_GET_UNFS_WINDOW_SIZE: { int *s = arg; s[0] = mp_rect_w(wl->geometry)*wl->scaling;