1
0
mirror of https://github.com/mpv-player/mpv synced 2025-01-19 05:41:16 +00:00

present_sync: rewrite around linked list

When this was originally written, the queuing/list approach was
deliberately removed since it adds more complication and xorg/wayland
don't really use it anyway. In practice, you only really have one frame
in flight with presentation timestamps. However, one slight annoyance is
that the drm code has its own thing which is almost exactly the same and
does its own calculations. Ideally, we'd port drm to this instead, but
the implementation there takes into account N-frames in flight which
probably does actually work. So we need to make present_sync smarter and
be able to handle this.

mpv does actually have its own linked list implementation already which
is a good fit for this. mp_present becomes the list and each
mp_present_entry has its own set of timestamps. During initialization,
we create all the entries we need and then simply treat it like a queue
during the lifecycle of the VO. When an entry is fully used
(present_sync_get_info), then we remove it from the list, zero it out,
and append it to the end for future use. This avoids needing to allocate
memory on every frame (which is what drm currently does) and allows for
a reasonable number of in flight frames at the same time as this should
never grow to some obscene number. The nice thing is that current users
of present_sync don't need to change anything besides the initialization
step.
This commit is contained in:
Dudemanguy 2023-11-02 13:41:32 -05:00
parent bcbd821fa9
commit 261f51b475
4 changed files with 81 additions and 33 deletions

View File

@ -17,6 +17,7 @@
#include <time.h>
#include "misc/linked_list.h"
#include "mpv_talloc.h"
#include "osdep/timer.h"
#include "present_sync.h"
@ -37,34 +38,65 @@
void present_sync_get_info(struct mp_present *present, struct vo_vsync_info *info)
{
info->vsync_duration = present->vsync_duration;
info->skipped_vsyncs = present->last_skipped_vsyncs;
info->last_queue_display_time = present->last_queue_display_time;
struct mp_present_entry *cur = present->head;
while (cur) {
if (cur->queue_display_time)
break;
cur = cur->list_node.next;
}
if (!cur)
return;
info->vsync_duration = cur->vsync_duration;
info->skipped_vsyncs = cur->skipped_vsyncs;
info->last_queue_display_time = cur->queue_display_time;
// Remove from the list, zero out everything, and append at the end
LL_REMOVE(list_node, present, cur);
*cur = (struct mp_present_entry){0};
LL_APPEND(list_node, present, cur);
}
struct mp_present *mp_present_initialize(void *talloc_ctx, int entries)
{
struct mp_present *present = talloc_zero(talloc_ctx, struct mp_present);
for (int i = 0; i < entries; i++) {
struct mp_present_entry *entry = talloc_zero(present, struct mp_present_entry);
LL_APPEND(list_node, present, entry);
}
return present;
}
void present_sync_swap(struct mp_present *present)
{
int64_t ust = present->current_ust;
int64_t msc = present->current_msc;
struct mp_present_entry *cur = present->head;
while (cur) {
if (!cur->queue_display_time)
break;
cur = cur->list_node.next;
}
if (!cur)
return;
int64_t ust = cur->ust;
int64_t msc = cur->msc;
int64_t last_ust = cur->list_node.prev ? cur->list_node.prev->ust : 0;
int64_t last_msc = cur->list_node.prev ? cur->list_node.prev->msc : 0;
// Avoid attempting to use any presentation statistics if the ust is 0 or has
// not actually updated (i.e. the last_ust is equal to current_ust).
if (!ust || ust == present->last_ust) {
present->last_skipped_vsyncs = -1;
present->vsync_duration = -1;
present->last_queue_display_time = -1;
// not actually updated (i.e. the last_ust is equal to ust).
if (!ust || ust == last_ust) {
cur->skipped_vsyncs = -1;
cur->vsync_duration = -1;
cur->queue_display_time = -1;
return;
}
present->last_skipped_vsyncs = 0;
int64_t ust_passed = ust ? ust - present->last_ust: 0;
present->last_ust = ust;
int64_t msc_passed = msc ? msc - present->last_msc: 0;
present->last_msc = msc;
cur->skipped_vsyncs = 0;
int64_t ust_passed = ust ? ust - last_ust: 0;
int64_t msc_passed = msc ? msc - last_msc: 0;
if (msc_passed && ust_passed)
present->vsync_duration = ust_passed / msc_passed;
cur->vsync_duration = ust_passed / msc_passed;
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
@ -72,13 +104,21 @@ void present_sync_swap(struct mp_present *present)
int64_t now_monotonic = MP_TIME_S_TO_NS(ts.tv_sec) + ts.tv_nsec;
int64_t ust_mp_time = mp_time_ns() - (now_monotonic - ust);
present->last_queue_display_time = ust_mp_time;
cur->queue_display_time = ust_mp_time;
}
void present_update_sync_values(struct mp_present *present, int64_t ust,
int64_t msc)
{
present->current_ust = ust;
present->current_msc = msc;
struct mp_present_entry *cur = present->head;
while (cur) {
if (!cur->ust)
break;
cur = cur->list_node.next;
}
if (!cur)
return;
cur->ust = ust;
cur->msc = msc;
}

View File

@ -25,16 +25,24 @@
/* Generic helpers for obtaining presentation feedback from
* backend APIs. This requires ust/msc values. */
struct mp_present {
int64_t current_ust;
int64_t current_msc;
int64_t last_ust;
int64_t last_msc;
struct mp_present_entry {
int64_t ust;
int64_t msc;
int64_t vsync_duration;
int64_t last_skipped_vsyncs;
int64_t last_queue_display_time;
int64_t skipped_vsyncs;
int64_t queue_display_time;
struct {
struct mp_present_entry *next, *prev;
} list_node;
};
struct mp_present {
struct mp_present_entry *head, *tail;
};
struct mp_present *mp_present_initialize(void *talloc_ctx, int entries);
// Used during the get_vsync call to deliver the presentation statistics to the VO.
void present_sync_get_info(struct mp_present *present, struct vo_vsync_info *info);

View File

@ -2282,7 +2282,7 @@ bool vo_wayland_init(struct vo *vo)
wl->fback_pool->len = 8; // max swapchain depth allowed
wl->fback_pool->fback = talloc_zero_array(wl->fback_pool, struct wp_presentation_feedback *,
wl->fback_pool->len);
wl->present = talloc_zero(wl, struct mp_present);
wl->present = mp_present_initialize(wl, 8); // max swapchain depth allowed
} else {
MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
wp_presentation_interface.name);
@ -2560,8 +2560,8 @@ void vo_wayland_wait_frame(struct vo_wayland_state *wl)
* 3. refresh rate of the output reported by the compositor
* 4. make up crap if vblank_time is still <= 0 (better than nothing) */
if (wl->use_present)
vblank_time = wl->present->vsync_duration;
if (wl->use_present && wl->present->head)
vblank_time = wl->present->head->vsync_duration;
if (vblank_time <= 0 && wl->refresh_interval > 0)
vblank_time = wl->refresh_interval;

View File

@ -626,7 +626,7 @@ bool vo_x11_init(struct vo *vo)
x11_error_output = x11->log;
XSetErrorHandler(x11_errorhandler);
x11->present = talloc_zero(x11, struct mp_present);
x11->present = mp_present_initialize(x11, 8); // max swapchain depth allowed
dispName = XDisplayName(NULL);