drm: use present_sync mechanism for presentation feedback

A ton of code and drm-specific abstractions can be dropped with this.
One important thing to note is that the usage of sbc is completely
dropped here. The utility of that is not particularly clear since the
sbc value was manually incremented before the flip and it's not as if
the drm page flip event gives it to us like it does with the msec and
ust. It can be reintroduced later if there is a need. For drm, we also
add a present_sync_clear_values helper since all presentation feedback
needs to be cleared on pause/resume for it.
This commit is contained in:
Dudemanguy 2023-11-02 15:54:01 -05:00
parent f629d7a2ff
commit a96d04f19d
6 changed files with 39 additions and 121 deletions

View File

@ -40,11 +40,12 @@
#include "common/common.h"
#include "common/msg.h"
#include "misc/ctype.h"
#include "options/m_config.h"
#include "osdep/io.h"
#include "osdep/poll_wrapper.h"
#include "osdep/timer.h"
#include "misc/ctype.h"
#include "present_sync.h"
#include "video/out/vo.h"
#define EVT_RELEASE 1
@ -912,55 +913,12 @@ err:
static void drm_pflip_cb(int fd, unsigned int msc, unsigned int sec,
unsigned int usec, void *data)
{
struct drm_pflip_cb_closure *closure = data;
struct vo_drm_state *drm = data;
struct drm_vsync_tuple *vsync = closure->vsync;
// frame_vsync->ust is the timestamp of the pageflip that happened just before this flip was queued
// frame_vsync->msc is the sequence number of the pageflip that happened just before this flip was queued
// frame_vsync->sbc is the sequence number for the frame that was just flipped to screen
struct drm_vsync_tuple *frame_vsync = closure->frame_vsync;
struct vo_vsync_info *vsync_info = closure->vsync_info;
const bool ready =
(vsync->msc != 0) &&
(frame_vsync->ust != 0) && (frame_vsync->msc != 0);
const uint64_t ust = (sec * 1000000LL) + usec;
const unsigned int msc_since_last_flip = msc - vsync->msc;
if (ready && msc == vsync->msc) {
// Seems like some drivers only increment msc every other page flip when
// running in interlaced mode (I'm looking at you nouveau). Obviously we
// can't work with this, so shame the driver and bail.
mp_err(closure->log,
"Got the same msc value twice: (msc: %u, vsync->msc: %u). This shouldn't happen. Possibly broken driver/interlaced mode?\n",
msc, vsync->msc);
goto fail;
}
vsync->ust = ust;
vsync->msc = msc;
if (ready) {
// Convert to mp_time
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
goto fail;
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 - vsync->ust * 1000);
const uint64_t ust_since_enqueue = vsync->ust - frame_vsync->ust;
const unsigned int msc_since_enqueue = vsync->msc - frame_vsync->msc;
const unsigned int sbc_since_enqueue = vsync->sbc - frame_vsync->sbc;
vsync_info->vsync_duration = ust_since_enqueue * 1000 / msc_since_enqueue;
vsync_info->skipped_vsyncs = msc_since_last_flip - 1; // Valid iff swap_buffers is called every vsync
vsync_info->last_queue_display_time = ust_mp_time + (sbc_since_enqueue * vsync_info->vsync_duration);
}
fail:
*closure->waiting_for_flip = false;
talloc_free(closure);
int64_t ust = MP_TIME_S_TO_NS(sec) + MP_TIME_US_TO_NS(usec);
present_sync_update_values(drm->present, ust, msc);
present_sync_swap(drm->present);
drm->waiting_for_flip = false;
}
int vo_drm_control(struct vo *vo, int *events, int request, void *arg)
@ -985,10 +943,7 @@ int vo_drm_control(struct vo *vo, int *events, int request, void *arg)
return VO_TRUE;
case VOCTRL_RESUME:
drm->paused = false;
drm->vsync_info.last_queue_display_time = -1;
drm->vsync_info.skipped_vsyncs = 0;
drm->vsync.ust = 0;
drm->vsync.msc = 0;
present_sync_clear_values(drm->present);
return VO_TRUE;
}
return VO_NOTIMPL;
@ -1075,10 +1030,7 @@ bool vo_drm_init(struct vo *vo)
drm->ev.version = DRM_EVENT_CONTEXT_VERSION;
drm->ev.page_flip_handler = &drm_pflip_cb;
drm->vsync_info.vsync_duration = 0;
drm->vsync_info.skipped_vsyncs = -1;
drm->vsync_info.last_queue_display_time = -1;
drm->present = mp_present_initialize(drm, 8); // max swapchain depth allowed
return true;
@ -1289,12 +1241,6 @@ double vo_drm_get_display_fps(struct vo_drm_state *drm)
return mode_get_Hz(&drm->mode.mode);
}
void vo_drm_get_vsync(struct vo *vo, struct vo_vsync_info *info)
{
struct vo_drm_state *drm = vo->drm;
*info = drm->vsync_info;
}
void vo_drm_set_monitor_par(struct vo *vo)
{
struct vo_drm_state *drm = vo->drm;

View File

@ -39,12 +39,6 @@ struct framebuffer {
uint32_t id;
};
struct drm_vsync_tuple {
uint64_t ust;
unsigned int msc;
unsigned int sbc;
};
struct drm_mode {
drmModeModeInfo mode;
uint32_t blob_id;
@ -77,12 +71,11 @@ struct vo_drm_state {
struct drm_atomic_context *atomic_context;
struct drm_mode mode;
struct drm_opts *opts;
struct drm_vsync_tuple vsync;
struct framebuffer *fb;
struct mp_log *log;
struct mp_present *present;
struct vo *vo;
struct vt_switcher vt_switcher;
struct vo_vsync_info vsync_info;
bool active;
bool paused;
@ -99,19 +92,10 @@ struct vo_drm_state {
uint32_t width;
};
struct drm_pflip_cb_closure {
struct drm_vsync_tuple *frame_vsync; // vsync tuple when the frame that just flipped was queued
struct drm_vsync_tuple *vsync; // vsync tuple of the latest page flip. drm_pflip_cb updates this
struct vo_vsync_info *vsync_info; // where the drm_pflip_cb routine writes its output
bool *waiting_for_flip; // drm_pflip_cb writes false here before returning
struct mp_log *log; // Needed to print error messages that shame bad drivers
};
bool vo_drm_init(struct vo *vo);
int vo_drm_control(struct vo *vo, int *events, int request, void *arg);
double vo_drm_get_display_fps(struct vo_drm_state *drm);
void vo_drm_get_vsync(struct vo *vo, struct vo_vsync_info *info);
void vo_drm_set_monitor_par(struct vo *vo);
void vo_drm_uninit(struct vo *vo);
void vo_drm_wait_events(struct vo *vo, int64_t until_time_ns);

View File

@ -29,10 +29,11 @@
#include <drm_fourcc.h>
#include "libmpv/render_gl.h"
#include "video/out/drm_atomic.h"
#include "video/out/drm_common.h"
#include "common/common.h"
#include "osdep/timer.h"
#include "video/out/drm_atomic.h"
#include "video/out/drm_common.h"
#include "video/out/present_sync.h"
#include "egl_helpers.h"
#include "common.h"
@ -48,7 +49,6 @@
struct gbm_frame {
struct gbm_bo *bo;
struct drm_vsync_tuple vsync;
};
struct gbm {
@ -322,26 +322,16 @@ static void queue_flip(struct ra_ctx *ctx, struct gbm_frame *frame)
update_framebuffer_from_bo(ctx, frame->bo);
// Alloc and fill the data struct for the page flip callback
struct drm_pflip_cb_closure *data = talloc(ctx, struct drm_pflip_cb_closure);
data->frame_vsync = &frame->vsync;
data->vsync = &drm->vsync;
data->vsync_info = &drm->vsync_info;
data->waiting_for_flip = &drm->waiting_for_flip;
data->log = drm->log;
struct drm_atomic_context *atomic_ctx = drm->atomic_context;
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", drm->fb->id);
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "CRTC_ID", atomic_ctx->crtc->id);
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "ZPOS", 1);
int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request,
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, data);
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, drm);
if (ret) {
if (ret)
MP_WARN(ctx->vo, "Failed to commit atomic request: %s\n", mp_strerror(ret));
talloc_free(data);
}
drm->waiting_for_flip = !ret;
drmModeAtomicFree(atomic_ctx->request);
@ -351,12 +341,9 @@ static void queue_flip(struct ra_ctx *ctx, struct gbm_frame *frame)
static void enqueue_bo(struct ra_ctx *ctx, struct gbm_bo *bo)
{
struct priv *p = ctx->priv;
struct vo_drm_state *drm = ctx->vo->drm;
drm->vsync.sbc++;
struct gbm_frame *new_frame = talloc(p, struct gbm_frame);
new_frame->bo = bo;
new_frame->vsync = drm->vsync;
MP_TARRAY_APPEND(p, p->gbm.bo_queue, p->gbm.num_bos, new_frame);
}
@ -590,7 +577,8 @@ static bool probe_gbm_modifiers(struct ra_ctx *ctx)
static void drm_egl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
{
vo_drm_get_vsync(ctx->vo, info);
struct vo_drm_state *drm = ctx->vo->drm;
present_sync_get_info(drm->present, info);
}
static bool drm_egl_init(struct ra_ctx *ctx)

View File

@ -107,6 +107,15 @@ void present_sync_swap(struct mp_present *present)
cur->queue_display_time = ust_mp_time;
}
void present_sync_clear_values(struct mp_present *present)
{
struct mp_present_entry *cur = present->head;
while (cur) {
*cur = (struct mp_present_entry){0};
cur = cur->list_node.next;
}
}
void present_sync_update_values(struct mp_present *present, int64_t ust,
int64_t msc)
{

View File

@ -49,6 +49,9 @@ void present_sync_get_info(struct mp_present *present, struct vo_vsync_info *inf
// Called after every buffer swap to update presentation statistics.
void present_sync_swap(struct mp_present *present);
// Zero the entire list but keep the items.
void present_sync_clear_values(struct mp_present *present);
// Called anytime the backend delivers new ust/msc values.
void present_sync_update_values(struct mp_present *present, int64_t ust,
int64_t msc);

View File

@ -33,6 +33,7 @@
#include "sub/osd.h"
#include "video/fmt-conversion.h"
#include "video/mp_image.h"
#include "video/out/present_sync.h"
#include "video/sws_utils.h"
#include "vo.h"
@ -48,7 +49,6 @@
struct drm_frame {
struct framebuffer *fb;
struct drm_vsync_tuple vsync;
};
struct priv {
@ -200,10 +200,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
if (mp_sws_reinit(p->sws) < 0)
return -1;
drm->vsync_info.vsync_duration = 0;
drm->vsync_info.skipped_vsyncs = -1;
drm->vsync_info.last_queue_display_time = -1;
vo->want_redraw = true;
return 0;
}
@ -283,12 +279,9 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *buf)
static void enqueue_frame(struct vo *vo, struct framebuffer *fb)
{
struct priv *p = vo->priv;
struct vo_drm_state *drm = vo->drm;
drm->vsync.sbc++;
struct drm_frame *new_frame = talloc(p, struct drm_frame);
new_frame->fb = fb;
new_frame->vsync = drm->vsync;
MP_TARRAY_APPEND(p, p->fb_queue, p->fb_queue_len, new_frame);
}
@ -332,25 +325,14 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
static void queue_flip(struct vo *vo, struct drm_frame *frame)
{
struct priv *p = vo->priv;
struct vo_drm_state *drm = vo->drm;
drm->fb = frame->fb;
// Alloc and fill the data struct for the page flip callback
struct drm_pflip_cb_closure *data = talloc(p, struct drm_pflip_cb_closure);
data->frame_vsync = &frame->vsync;
data->vsync = &drm->vsync;
data->vsync_info = &drm->vsync_info;
data->waiting_for_flip = &drm->waiting_for_flip;
data->log = vo->log;
int ret = drmModePageFlip(drm->fd, drm->crtc_id,
drm->fb->id, DRM_MODE_PAGE_FLIP_EVENT, data);
if (ret) {
drm->fb->id, DRM_MODE_PAGE_FLIP_EVENT, drm);
if (ret)
MP_WARN(vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
talloc_free(data);
}
drm->waiting_for_flip = !ret;
}
@ -379,6 +361,12 @@ static void flip_page(struct vo *vo)
}
}
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
{
struct vo_drm_state *drm = vo->drm;
present_sync_get_info(drm->present, info);
}
static void uninit(struct vo *vo)
{
struct priv *p = vo->priv;
@ -462,7 +450,7 @@ const struct vo_driver video_out_drm = {
.control = control,
.draw_frame = draw_frame,
.flip_page = flip_page,
.get_vsync = vo_drm_get_vsync,
.get_vsync = get_vsync,
.uninit = uninit,
.wait_events = vo_drm_wait_events,
.wakeup = vo_drm_wakeup,