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:
parent
bcbd821fa9
commit
261f51b475
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user