mirror of https://github.com/mpv-player/mpv
drm: rewrite based around vo_drm_state
A longstanding pain point of the drm VOs is the relative lack of state sharing. While drm_common does provide some sharing, it's far less than other platforms like x11 or wayland. What we do here is essentially copy them by creating a new vo_drm_state struct and using it in vo_drm and context_drm_egl. Much of the functionality that was essentially duplicated in both VOs/contexts is now reduced simple functions in drm_common. The usage of the term 'kms' was also mostly eliminated since this is libdrm nowadays from a userspace perspective.
This commit is contained in:
parent
8c617765fe
commit
92a6f2d687
|
@ -48,10 +48,6 @@
|
|||
#include "stream/stream.h"
|
||||
#include "demux/demux.h"
|
||||
|
||||
#if HAVE_DRM
|
||||
#include "video/out/drm_common.h"
|
||||
#endif
|
||||
|
||||
static void print_version(struct mp_log *log)
|
||||
{
|
||||
mp_print_version(log, true);
|
||||
|
@ -187,9 +183,6 @@ static const m_option_t mp_vo_opt_list[] = {
|
|||
#if HAVE_WIN32_DESKTOP
|
||||
{"vo-mmcss-profile", OPT_STRING(mmcss_profile)},
|
||||
#endif
|
||||
#if HAVE_DRM
|
||||
{"", OPT_SUBSTRUCT(drm_opts, drm_conf)},
|
||||
#endif
|
||||
#if HAVE_EGL_ANDROID
|
||||
{"android-surface-size", OPT_SIZE_BOX(android_surface_size)},
|
||||
#endif
|
||||
|
@ -829,6 +822,10 @@ static const m_option_t mp_opts[] = {
|
|||
{"", OPT_SUBSTRUCT(macos_opts, macos_conf)},
|
||||
#endif
|
||||
|
||||
#if HAVE_DRM
|
||||
{"", OPT_SUBSTRUCT(drm_opts, drm_conf)},
|
||||
#endif
|
||||
|
||||
#if HAVE_WAYLAND
|
||||
{"", OPT_SUBSTRUCT(wayland_opts, wayland_conf)},
|
||||
#endif
|
||||
|
|
|
@ -66,9 +66,6 @@ typedef struct mp_vo_opts {
|
|||
double timing_offset;
|
||||
int video_sync;
|
||||
|
||||
// vo_drm
|
||||
struct drm_opts *drm_opts;
|
||||
|
||||
struct m_geometry android_surface_size;
|
||||
|
||||
int swapchain_depth; // max number of images to render ahead
|
||||
|
@ -360,6 +357,7 @@ typedef struct MPOpts {
|
|||
struct d3d11va_opts *d3d11va_opts;
|
||||
struct cocoa_opts *cocoa_opts;
|
||||
struct macos_opts *macos_opts;
|
||||
struct drm_opts *drm_opts;
|
||||
struct wayland_opts *wayland_opts;
|
||||
struct dvd_opts *dvd_opts;
|
||||
struct vaapi_opts *vaapi_opts;
|
||||
|
|
|
@ -29,7 +29,7 @@ static struct AVBufferRef *drm_create_standalone(struct mpv_global *global,
|
|||
{
|
||||
void *tmp = talloc_new(NULL);
|
||||
struct drm_opts *drm_opts = mp_get_config_group(tmp, global, &drm_conf);
|
||||
const char *opt_path = drm_opts->drm_device_path;
|
||||
const char *opt_path = drm_opts->device_path;
|
||||
|
||||
const char *device_path = opt_path ? opt_path : "/dev/dri/renderD128";
|
||||
AVBufferRef* ref = NULL;
|
||||
|
|
|
@ -107,14 +107,14 @@ int drm_object_set_property(drmModeAtomicReq *request, struct drm_object *object
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct drm_object * drm_object_create(struct mp_log *log, int fd,
|
||||
uint32_t object_id, uint32_t type)
|
||||
struct drm_object *drm_object_create(struct mp_log *log, int fd,
|
||||
uint32_t object_id, uint32_t type)
|
||||
{
|
||||
struct drm_object *obj = NULL;
|
||||
obj = talloc_zero(NULL, struct drm_object);
|
||||
obj->fd = fd;
|
||||
obj->id = object_id;
|
||||
obj->type = type;
|
||||
obj->fd = fd;
|
||||
|
||||
if (drm_object_create_properties(log, fd, obj)) {
|
||||
talloc_free(obj);
|
||||
|
@ -195,7 +195,6 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd,
|
|||
if (connector->connector_id == connector_id)
|
||||
ctx->connector = drm_object_create(log, ctx->fd, connector->connector_id,
|
||||
DRM_MODE_OBJECT_CONNECTOR);
|
||||
|
||||
drmModeFreeConnector(connector);
|
||||
if (ctx->connector)
|
||||
break;
|
||||
|
@ -211,8 +210,7 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd,
|
|||
drmplane = NULL;
|
||||
|
||||
if (possible_crtcs & (1 << crtc_index)) {
|
||||
plane = drm_object_create(log, ctx->fd, plane_id,
|
||||
DRM_MODE_OBJECT_PLANE);
|
||||
plane = drm_object_create(log, ctx->fd, plane_id, DRM_MODE_OBJECT_PLANE);
|
||||
|
||||
if (!plane) {
|
||||
mp_err(log, "Failed to create Plane object from plane ID %d\n",
|
||||
|
|
|
@ -24,15 +24,11 @@
|
|||
#include <xf86drmMode.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "drm_common.h"
|
||||
|
||||
#define DRM_OPTS_PRIMARY_PLANE -1
|
||||
#define DRM_OPTS_OVERLAY_PLANE -2
|
||||
|
||||
struct drm_mode {
|
||||
drmModeModeInfo mode;
|
||||
uint32_t blob_id;
|
||||
};
|
||||
|
||||
struct drm_atomic_plane_state {
|
||||
uint64_t fb_id;
|
||||
uint64_t crtc_id;
|
||||
|
@ -83,13 +79,12 @@ struct drm_atomic_context {
|
|||
struct drm_atomic_state old_state;
|
||||
};
|
||||
|
||||
|
||||
int drm_object_create_properties(struct mp_log *log, int fd, struct drm_object *object);
|
||||
void drm_object_free_properties(struct drm_object *object);
|
||||
int drm_object_get_property(struct drm_object *object, char *name, uint64_t *value);
|
||||
int drm_object_set_property(drmModeAtomicReq *request, struct drm_object *object, char *name, uint64_t value);
|
||||
drmModePropertyBlobPtr drm_object_get_property_blob(struct drm_object *object, char *name);
|
||||
struct drm_object * drm_object_create(struct mp_log *log, int fd, uint32_t object_id, uint32_t type);
|
||||
struct drm_object *drm_object_create(struct mp_log *log, int fd, uint32_t object_id, uint32_t type);
|
||||
void drm_object_free(struct drm_object *object);
|
||||
void drm_object_print_info(struct mp_log *log, struct drm_object *object);
|
||||
struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, int crtc_id, int connector_id,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,24 +21,45 @@
|
|||
#include <stdbool.h>
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include "options/m_option.h"
|
||||
#include "drm_atomic.h"
|
||||
#include "vo.h"
|
||||
|
||||
#define DRM_OPTS_FORMAT_XRGB8888 0
|
||||
#define DRM_OPTS_FORMAT_XRGB2101010 1
|
||||
#define DRM_OPTS_FORMAT_XBGR8888 2
|
||||
#define DRM_OPTS_FORMAT_XBGR2101010 3
|
||||
|
||||
struct kms {
|
||||
struct mp_log *log;
|
||||
char *primary_node_path;
|
||||
struct framebuffer {
|
||||
int fd;
|
||||
drmModeConnector *connector;
|
||||
drmModeEncoder *encoder;
|
||||
struct drm_mode mode;
|
||||
uint32_t crtc_id;
|
||||
int card_no;
|
||||
struct drm_atomic_context *atomic_context;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t stride;
|
||||
uint32_t size;
|
||||
uint32_t handle;
|
||||
uint8_t *map;
|
||||
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;
|
||||
};
|
||||
|
||||
struct drm_opts {
|
||||
char *device_path;
|
||||
char *connector_spec;
|
||||
char *mode_spec;
|
||||
int drm_atomic;
|
||||
int draw_plane;
|
||||
int drmprime_video_plane;
|
||||
int drm_format;
|
||||
struct m_geometry draw_surface_size;
|
||||
int vrr_enabled;
|
||||
};
|
||||
|
||||
struct vt_switcher {
|
||||
|
@ -48,22 +69,34 @@ struct vt_switcher {
|
|||
void *handler_data[2];
|
||||
};
|
||||
|
||||
struct drm_opts {
|
||||
char *drm_device_path;
|
||||
char *drm_connector_spec;
|
||||
char *drm_mode_spec;
|
||||
int drm_atomic;
|
||||
int drm_draw_plane;
|
||||
int drm_drmprime_video_plane;
|
||||
int drm_format;
|
||||
struct m_geometry drm_draw_surface_size;
|
||||
int drm_vrr_enabled;
|
||||
};
|
||||
struct vo_drm_state {
|
||||
drmModeConnector *connector;
|
||||
drmModeEncoder *encoder;
|
||||
drmEventContext ev;
|
||||
|
||||
struct drm_vsync_tuple {
|
||||
uint64_t ust;
|
||||
unsigned int msc;
|
||||
unsigned int sbc;
|
||||
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 vo *vo;
|
||||
struct vt_switcher vt_switcher;
|
||||
struct vo_vsync_info vsync_info;
|
||||
|
||||
bool active;
|
||||
bool paused;
|
||||
bool still;
|
||||
bool vt_switcher_active;
|
||||
bool waiting_for_flip;
|
||||
|
||||
char *card_path;
|
||||
int card_no;
|
||||
int fd;
|
||||
|
||||
uint32_t crtc_id;
|
||||
uint32_t height;
|
||||
uint32_t width;
|
||||
};
|
||||
|
||||
struct drm_pflip_cb_closure {
|
||||
|
@ -74,26 +107,18 @@ struct drm_pflip_cb_closure {
|
|||
struct mp_log *log; // Needed to print error messages that shame bad drivers
|
||||
};
|
||||
|
||||
bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log);
|
||||
void vt_switcher_destroy(struct vt_switcher *s);
|
||||
void vt_switcher_poll(struct vt_switcher *s, int timeout_ms);
|
||||
void vt_switcher_interrupt_poll(struct vt_switcher *s);
|
||||
bool vo_drm_init(struct vo *vo);
|
||||
int vo_drm_control(struct vo *vo, int *events, int request, void *arg);
|
||||
|
||||
void vt_switcher_acquire(struct vt_switcher *s, void (*handler)(void*),
|
||||
void *user_data);
|
||||
void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*),
|
||||
void *user_data);
|
||||
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_us);
|
||||
void vo_drm_wait_on_flip(struct vo_drm_state *drm);
|
||||
void vo_drm_wakeup(struct vo *vo);
|
||||
|
||||
struct kms *kms_create(struct mp_log *log,
|
||||
const char *drm_device_path,
|
||||
const char *connector_spec,
|
||||
const char *mode_spec,
|
||||
int draw_plane, int drmprime_video_plane);
|
||||
void kms_destroy(struct kms *kms);
|
||||
double kms_get_display_fps(const struct kms *kms);
|
||||
|
||||
// DRM Page Flip callback
|
||||
void drm_pflip_cb(int fd, unsigned int msc, unsigned int sec,
|
||||
unsigned int usec, void *data);
|
||||
bool vo_drm_acquire_crtc(struct vo_drm_state *drm);
|
||||
void vo_drm_release_crtc(struct vo_drm_state *drm);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -93,7 +93,7 @@ static int init(struct ra_hwdec *hw)
|
|||
*/
|
||||
void *tmp = talloc_new(NULL);
|
||||
struct drm_opts *drm_opts = mp_get_config_group(tmp, hw->global, &drm_conf);
|
||||
const char *opt_path = drm_opts->drm_device_path;
|
||||
const char *opt_path = drm_opts->device_path;
|
||||
|
||||
const char *device_path = params && params->render_fd > -1 ?
|
||||
drmGetRenderDeviceNameFromFd(params->render_fd) :
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "common/msg.h"
|
||||
#include "options/m_config.h"
|
||||
#include "libmpv/render_gl.h"
|
||||
#include "video/out/drm_atomic.h"
|
||||
#include "video/out/drm_common.h"
|
||||
#include "video/out/drm_prime.h"
|
||||
#include "video/out/gpu/hwdec.h"
|
||||
|
@ -252,8 +253,8 @@ static int init(struct ra_hwdec *hw)
|
|||
|
||||
void *tmp = talloc_new(NULL);
|
||||
struct drm_opts *opts = mp_get_config_group(tmp, hw->global, &drm_conf);
|
||||
draw_plane = opts->drm_draw_plane;
|
||||
drmprime_video_plane = opts->drm_drmprime_video_plane;
|
||||
draw_plane = opts->draw_plane;
|
||||
drmprime_video_plane = opts->drmprime_video_plane;
|
||||
talloc_free(tmp);
|
||||
|
||||
struct mpv_opengl_drm_params_v2 *drm_params;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#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"
|
||||
|
@ -45,28 +46,19 @@
|
|||
#define EGL_PLATFORM_GBM_KHR 0x31D7
|
||||
#endif
|
||||
|
||||
struct framebuffer
|
||||
{
|
||||
int fd;
|
||||
uint32_t width, height;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
struct gbm_frame {
|
||||
struct gbm_bo *bo;
|
||||
struct drm_vsync_tuple vsync;
|
||||
};
|
||||
|
||||
struct gbm
|
||||
{
|
||||
struct gbm {
|
||||
struct gbm_surface *surface;
|
||||
struct gbm_device *device;
|
||||
struct gbm_frame **bo_queue;
|
||||
unsigned int num_bos;
|
||||
};
|
||||
|
||||
struct egl
|
||||
{
|
||||
struct egl {
|
||||
EGLDisplay display;
|
||||
EGLContext context;
|
||||
EGLSurface surface;
|
||||
|
@ -74,13 +66,9 @@ struct egl
|
|||
|
||||
struct priv {
|
||||
GL gl;
|
||||
struct kms *kms;
|
||||
|
||||
drmEventContext ev;
|
||||
|
||||
struct egl egl;
|
||||
struct gbm gbm;
|
||||
struct framebuffer *fb;
|
||||
|
||||
GLsync *vsync_fences;
|
||||
unsigned int num_vsync_fences;
|
||||
|
@ -89,18 +77,6 @@ struct priv {
|
|||
uint64_t *gbm_modifiers;
|
||||
unsigned int num_gbm_modifiers;
|
||||
|
||||
bool active;
|
||||
bool waiting_for_flip;
|
||||
|
||||
bool vt_switcher_active;
|
||||
struct vt_switcher vt_switcher;
|
||||
|
||||
bool still;
|
||||
bool paused;
|
||||
|
||||
struct drm_vsync_tuple vsync;
|
||||
struct vo_vsync_info vsync_info;
|
||||
|
||||
struct mpv_opengl_drm_params_v2 drm_params;
|
||||
struct mpv_opengl_drm_draw_surface_size draw_surface_size;
|
||||
};
|
||||
|
@ -245,8 +221,9 @@ static bool init_egl(struct ra_ctx *ctx)
|
|||
static bool init_gbm(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
MP_VERBOSE(ctx->vo, "Creating GBM device\n");
|
||||
p->gbm.device = gbm_create_device(p->kms->fd);
|
||||
p->gbm.device = gbm_create_device(drm->fd);
|
||||
if (!p->gbm.device) {
|
||||
MP_ERR(ctx->vo, "Failed to create GBM device.\n");
|
||||
return false;
|
||||
|
@ -288,14 +265,15 @@ static void framebuffer_destroy_callback(struct gbm_bo *bo, void *data)
|
|||
static void update_framebuffer_from_bo(struct ra_ctx *ctx, struct gbm_bo *bo)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
struct framebuffer *fb = gbm_bo_get_user_data(bo);
|
||||
if (fb) {
|
||||
p->fb = fb;
|
||||
drm->fb = fb;
|
||||
return;
|
||||
}
|
||||
|
||||
fb = talloc_zero(ctx, struct framebuffer);
|
||||
fb->fd = p->kms->fd;
|
||||
fb->fd = drm->fd;
|
||||
fb->width = gbm_bo_get_width(bo);
|
||||
fb->height = gbm_bo_get_height(bo);
|
||||
uint64_t modifier = gbm_bo_get_modifier(bo);
|
||||
|
@ -335,210 +313,50 @@ static void update_framebuffer_from_bo(struct ra_ctx *ctx, struct gbm_bo *bo)
|
|||
MP_ERR(ctx->vo, "Failed to create framebuffer: %s\n", mp_strerror(errno));
|
||||
}
|
||||
gbm_bo_set_user_data(bo, fb, framebuffer_destroy_callback);
|
||||
p->fb = fb;
|
||||
}
|
||||
|
||||
static bool crtc_setup(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
if (p->active)
|
||||
return true;
|
||||
p->active = true;
|
||||
|
||||
struct drm_atomic_context *atomic_ctx = p->kms->atomic_context;
|
||||
|
||||
if (!drm_atomic_save_old_state(atomic_ctx)) {
|
||||
MP_WARN(ctx->vo, "Failed to save old DRM atomic state\n");
|
||||
}
|
||||
|
||||
drmModeAtomicReqPtr request = drmModeAtomicAlloc();
|
||||
if (!request) {
|
||||
MP_ERR(ctx->vo, "Failed to allocate drm atomic request\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drm_object_set_property(request, atomic_ctx->connector, "CRTC_ID", p->kms->crtc_id) < 0) {
|
||||
MP_ERR(ctx->vo, "Could not set CRTC_ID on connector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!drm_mode_ensure_blob(p->kms->fd, &p->kms->mode)) {
|
||||
MP_ERR(ctx->vo, "Failed to create DRM mode blob\n");
|
||||
goto err;
|
||||
}
|
||||
if (drm_object_set_property(request, atomic_ctx->crtc, "MODE_ID", p->kms->mode.blob_id) < 0) {
|
||||
MP_ERR(ctx->vo, "Could not set MODE_ID on crtc\n");
|
||||
goto err;
|
||||
}
|
||||
if (drm_object_set_property(request, atomic_ctx->crtc, "ACTIVE", 1) < 0) {
|
||||
MP_ERR(ctx->vo, "Could not set ACTIVE on crtc\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* VRR related properties were added in kernel 5.0. We will not fail if we
|
||||
* cannot query or set the value, but we will log as appropriate.
|
||||
*/
|
||||
uint64_t vrr_capable = 0;
|
||||
drm_object_get_property(atomic_ctx->connector, "VRR_CAPABLE", &vrr_capable);
|
||||
MP_VERBOSE(ctx->vo, "crtc is%s VRR capable\n", vrr_capable ? "" : " not");
|
||||
|
||||
uint64_t vrr_requested = ctx->vo->opts->drm_opts->drm_vrr_enabled;
|
||||
if (vrr_requested == 1 || (vrr_capable && vrr_requested == -1)) {
|
||||
if (drm_object_set_property(request, atomic_ctx->crtc, "VRR_ENABLED", 1) < 0) {
|
||||
MP_WARN(ctx->vo, "Could not enable VRR on crtc\n");
|
||||
} else {
|
||||
MP_VERBOSE(ctx->vo, "Enabled VRR on crtc\n");
|
||||
}
|
||||
}
|
||||
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "FB_ID", p->fb->id);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_ID", p->kms->crtc_id);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_X", 0);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_Y", 0);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_W", p->draw_surface_size.width << 16);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_H", p->draw_surface_size.height << 16);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_X", 0);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_Y", 0);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_W", p->kms->mode.mode.hdisplay);
|
||||
drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_H", p->kms->mode.mode.vdisplay);
|
||||
|
||||
int ret = drmModeAtomicCommit(p->kms->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
||||
if (ret)
|
||||
MP_ERR(ctx->vo, "Failed to commit ModeSetting atomic request (%d)\n", ret);
|
||||
|
||||
drmModeAtomicFree(request);
|
||||
return ret == 0;
|
||||
|
||||
err:
|
||||
drmModeAtomicFree(request);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void crtc_release(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
if (!p->active)
|
||||
return;
|
||||
p->active = false;
|
||||
|
||||
if (!p->kms->atomic_context->old_state.saved)
|
||||
return;
|
||||
|
||||
bool success = true;
|
||||
struct drm_atomic_context *atomic_ctx = p->kms->atomic_context;
|
||||
drmModeAtomicReqPtr request = drmModeAtomicAlloc();
|
||||
if (!request) {
|
||||
MP_ERR(ctx->vo, "Failed to allocate drm atomic request\n");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (request && !drm_atomic_restore_old_state(request, atomic_ctx)) {
|
||||
MP_WARN(ctx->vo, "Got error while restoring old state\n");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (request) {
|
||||
int ret = drmModeAtomicCommit(p->kms->fd, request,
|
||||
DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
||||
success = ret == 0;
|
||||
if (!success)
|
||||
MP_WARN(ctx->vo, "Failed to commit ModeSetting atomic request (%d)\n", ret);
|
||||
}
|
||||
|
||||
if (request)
|
||||
drmModeAtomicFree(request);
|
||||
|
||||
if (!success)
|
||||
MP_ERR(ctx->vo, "Failed to restore previous mode\n");
|
||||
}
|
||||
|
||||
static void release_vt(void *data)
|
||||
{
|
||||
struct ra_ctx *ctx = data;
|
||||
MP_VERBOSE(ctx->vo, "Releasing VT\n");
|
||||
crtc_release(ctx);
|
||||
|
||||
const struct priv *p = ctx->priv;
|
||||
if (drmDropMaster(p->kms->fd)) {
|
||||
MP_WARN(ctx->vo, "Failed to drop DRM master: %s\n",
|
||||
mp_strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void acquire_vt(void *data)
|
||||
{
|
||||
struct ra_ctx *ctx = data;
|
||||
MP_VERBOSE(ctx->vo, "Acquiring VT\n");
|
||||
|
||||
const struct priv *p = ctx->priv;
|
||||
if (drmSetMaster(p->kms->fd)) {
|
||||
MP_WARN(ctx->vo, "Failed to acquire DRM master: %s\n",
|
||||
mp_strerror(errno));
|
||||
}
|
||||
|
||||
crtc_setup(ctx);
|
||||
drm->fb = fb;
|
||||
}
|
||||
|
||||
static void queue_flip(struct ra_ctx *ctx, struct gbm_frame *frame)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct drm_atomic_context *atomic_ctx = p->kms->atomic_context;
|
||||
int ret;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
|
||||
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 = &p->vsync;
|
||||
data->vsync_info = &p->vsync_info;
|
||||
data->waiting_for_flip = &p->waiting_for_flip;
|
||||
data->log = ctx->log;
|
||||
data->vsync = &drm->vsync;
|
||||
data->vsync_info = &drm->vsync_info;
|
||||
data->waiting_for_flip = &drm->waiting_for_flip;
|
||||
data->log = drm->log;
|
||||
|
||||
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", p->fb->id);
|
||||
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);
|
||||
|
||||
ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request,
|
||||
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, data);
|
||||
int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request,
|
||||
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, data);
|
||||
|
||||
if (ret) {
|
||||
MP_WARN(ctx->vo, "Failed to commit atomic request (%d)\n", ret);
|
||||
MP_WARN(ctx->vo, "Failed to commit atomic request: %s\n", mp_strerror(ret));
|
||||
talloc_free(data);
|
||||
}
|
||||
p->waiting_for_flip = !ret;
|
||||
drm->waiting_for_flip = !ret;
|
||||
|
||||
drmModeAtomicFree(atomic_ctx->request);
|
||||
atomic_ctx->request = drmModeAtomicAlloc();
|
||||
}
|
||||
|
||||
static void wait_on_flip(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
// poll page flip finish event
|
||||
while (p->waiting_for_flip) {
|
||||
const int timeout_ms = 3000;
|
||||
struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
|
||||
poll(fds, 1, timeout_ms);
|
||||
if (fds[0].revents & POLLIN) {
|
||||
const int ret = drmHandleEvent(p->kms->fd, &p->ev);
|
||||
if (ret != 0) {
|
||||
MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
p->vsync.sbc++;
|
||||
drm->vsync.sbc++;
|
||||
struct gbm_frame *new_frame = talloc(p, struct gbm_frame);
|
||||
new_frame->bo = bo;
|
||||
new_frame->vsync = p->vsync;
|
||||
new_frame->vsync = drm->vsync;
|
||||
MP_TARRAY_APPEND(p, p->gbm.bo_queue, p->gbm.num_bos, new_frame);
|
||||
}
|
||||
|
||||
|
@ -588,10 +406,11 @@ static bool drm_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
|
|||
{
|
||||
struct ra_ctx *ctx = sw->ctx;
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
|
||||
if (!p->kms->atomic_context->request) {
|
||||
p->kms->atomic_context->request = drmModeAtomicAlloc();
|
||||
p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request;
|
||||
if (!drm->atomic_context->request) {
|
||||
drm->atomic_context->request = drmModeAtomicAlloc();
|
||||
p->drm_params.atomic_request_ptr = &drm->atomic_context->request;
|
||||
}
|
||||
|
||||
return ra_gl_ctx_start_frame(sw, out_fbo);
|
||||
|
@ -600,9 +419,9 @@ static bool drm_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
|
|||
static bool drm_egl_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
|
||||
{
|
||||
struct ra_ctx *ctx = sw->ctx;
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
|
||||
p->still = frame->still;
|
||||
drm->still = frame->still;
|
||||
|
||||
return ra_gl_ctx_submit_frame(sw, frame);
|
||||
}
|
||||
|
@ -611,9 +430,10 @@ static void drm_egl_swap_buffers(struct ra_swapchain *sw)
|
|||
{
|
||||
struct ra_ctx *ctx = sw->ctx;
|
||||
struct priv *p = ctx->priv;
|
||||
const bool drain = p->paused || p->still; // True when we need to drain the swapchain
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
const bool drain = drm->paused || drm->still; // True when we need to drain the swapchain
|
||||
|
||||
if (!p->active)
|
||||
if (!drm->active)
|
||||
return;
|
||||
|
||||
wait_fence(ctx);
|
||||
|
@ -630,8 +450,8 @@ static void drm_egl_swap_buffers(struct ra_swapchain *sw)
|
|||
|
||||
while (drain || p->gbm.num_bos > ctx->vo->opts->swapchain_depth ||
|
||||
!gbm_surface_has_free_buffers(p->gbm.surface)) {
|
||||
if (p->waiting_for_flip) {
|
||||
wait_on_flip(ctx);
|
||||
if (drm->waiting_for_flip) {
|
||||
vo_drm_wait_on_flip(drm);
|
||||
swapchain_step(ctx);
|
||||
}
|
||||
if (p->gbm.num_bos <= 1)
|
||||
|
@ -654,19 +474,17 @@ static const struct ra_swapchain_fns drm_egl_swapchain = {
|
|||
static void drm_egl_uninit(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct drm_atomic_context *atomic_ctx = p->kms->atomic_context;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
struct drm_atomic_context *atomic_ctx = drm->atomic_context;
|
||||
|
||||
if (drmModeAtomicCommit(drm->fd, atomic_ctx->request, 0, NULL))
|
||||
MP_ERR(ctx->vo, "Failed to commit atomic request: %s\n", mp_strerror(errno));
|
||||
|
||||
int ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request, 0, NULL);
|
||||
if (ret)
|
||||
MP_ERR(ctx->vo, "Failed to commit atomic request (%d)\n", ret);
|
||||
drmModeAtomicFree(atomic_ctx->request);
|
||||
|
||||
vo_drm_uninit(ctx->vo);
|
||||
ra_gl_ctx_uninit(ctx);
|
||||
|
||||
crtc_release(ctx);
|
||||
if (p->vt_switcher_active)
|
||||
vt_switcher_destroy(&p->vt_switcher);
|
||||
|
||||
// According to GBM documentation all BO:s must be released before
|
||||
// gbm_surface_destroy can be called on the surface.
|
||||
while (p->gbm.num_bos) {
|
||||
|
@ -684,11 +502,6 @@ static void drm_egl_uninit(struct ra_ctx *ctx)
|
|||
eglDestroyContext(p->egl.display, p->egl.context);
|
||||
|
||||
close(p->drm_params.render_fd);
|
||||
|
||||
if (p->kms) {
|
||||
kms_destroy(p->kms);
|
||||
p->kms = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If the draw plane supports ARGB we want to use that, but if it doesn't we
|
||||
|
@ -699,9 +512,9 @@ static void drm_egl_uninit(struct ra_ctx *ctx)
|
|||
static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t xrgb_format)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
|
||||
drmModePlane *drmplane =
|
||||
drmModeGetPlane(p->kms->fd, p->kms->atomic_context->draw_plane->id);
|
||||
drmModePlane *drmplane = drmModeGetPlane(drm->fd, drm->atomic_context->draw_plane->id);
|
||||
bool have_argb = false;
|
||||
bool have_xrgb = false;
|
||||
bool result = false;
|
||||
|
@ -731,9 +544,10 @@ static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t
|
|||
static bool probe_gbm_modifiers(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
|
||||
drmModePropertyBlobPtr blob =
|
||||
drm_object_get_property_blob(p->kms->atomic_context->draw_plane, "IN_FORMATS");
|
||||
drmModePropertyBlobPtr blob = drm_object_get_property_blob(drm->atomic_context->draw_plane,
|
||||
"IN_FORMATS");
|
||||
if (!blob) {
|
||||
MP_VERBOSE(ctx->vo, "Failed to find IN_FORMATS property\n");
|
||||
return false;
|
||||
|
@ -769,47 +583,31 @@ static bool probe_gbm_modifiers(struct ra_ctx *ctx)
|
|||
|
||||
static void drm_egl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
*info = p->vsync_info;
|
||||
vo_drm_get_vsync(ctx->vo, info);
|
||||
}
|
||||
|
||||
static bool drm_egl_init(struct ra_ctx *ctx)
|
||||
{
|
||||
if (!vo_drm_init(ctx->vo))
|
||||
goto err;
|
||||
|
||||
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
||||
p->ev.version = DRM_EVENT_CONTEXT_VERSION;
|
||||
p->ev.page_flip_handler = &drm_pflip_cb;
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
|
||||
p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log);
|
||||
if (p->vt_switcher_active) {
|
||||
vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx);
|
||||
vt_switcher_release(&p->vt_switcher, release_vt, ctx);
|
||||
if (ctx->vo->drm->opts->draw_surface_size.wh_valid) {
|
||||
p->draw_surface_size.width = ctx->vo->drm->opts->draw_surface_size.w;
|
||||
p->draw_surface_size.height = ctx->vo->drm->opts->draw_surface_size.h;
|
||||
} else {
|
||||
MP_WARN(ctx, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
|
||||
p->draw_surface_size.width = drm->mode.mode.hdisplay;
|
||||
p->draw_surface_size.height = drm->mode.mode.vdisplay;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ctx, "Initializing KMS\n");
|
||||
p->kms = kms_create(ctx->log,
|
||||
ctx->vo->opts->drm_opts->drm_device_path,
|
||||
ctx->vo->opts->drm_opts->drm_connector_spec,
|
||||
ctx->vo->opts->drm_opts->drm_mode_spec,
|
||||
ctx->vo->opts->drm_opts->drm_draw_plane,
|
||||
ctx->vo->opts->drm_opts->drm_drmprime_video_plane);
|
||||
if (!p->kms) {
|
||||
MP_ERR(ctx, "Failed to create KMS.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx->vo->opts->drm_opts->drm_draw_surface_size.wh_valid) {
|
||||
p->draw_surface_size.width = ctx->vo->opts->drm_opts->drm_draw_surface_size.w;
|
||||
p->draw_surface_size.height = ctx->vo->opts->drm_opts->drm_draw_surface_size.h;
|
||||
} else {
|
||||
p->draw_surface_size.width = p->kms->mode.mode.hdisplay;
|
||||
p->draw_surface_size.height = p->kms->mode.mode.vdisplay;
|
||||
}
|
||||
drm->width = p->draw_surface_size.width;
|
||||
drm->height = p->draw_surface_size.height;
|
||||
|
||||
uint32_t argb_format;
|
||||
uint32_t xrgb_format;
|
||||
switch (ctx->vo->opts->drm_opts->drm_format) {
|
||||
switch (ctx->vo->drm->opts->drm_format) {
|
||||
case DRM_OPTS_FORMAT_XRGB2101010:
|
||||
argb_format = GBM_FORMAT_ARGB2101010;
|
||||
xrgb_format = GBM_FORMAT_XRGB2101010;
|
||||
|
@ -831,7 +629,7 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
if (!probe_gbm_format(ctx, argb_format, xrgb_format)) {
|
||||
MP_ERR(ctx->vo, "No suitable format found on draw plane (tried: %s and %s).\n",
|
||||
gbm_format_to_string(argb_format), gbm_format_to_string(xrgb_format));
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// It is not fatal if this fails. We'll just try without modifiers.
|
||||
|
@ -839,18 +637,18 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
|
||||
if (!init_gbm(ctx)) {
|
||||
MP_ERR(ctx->vo, "Failed to setup GBM.\n");
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!init_egl(ctx)) {
|
||||
MP_ERR(ctx->vo, "Failed to setup EGL.\n");
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface,
|
||||
p->egl.context)) {
|
||||
MP_ERR(ctx->vo, "Failed to make context current.\n");
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
mpegl_load_functions(&p->gl, ctx->vo->log);
|
||||
|
@ -861,27 +659,29 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
struct gbm_bo *new_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
|
||||
if (!new_bo) {
|
||||
MP_ERR(ctx, "Failed to lock GBM surface.\n");
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
enqueue_bo(ctx, new_bo);
|
||||
update_framebuffer_from_bo(ctx, new_bo);
|
||||
if (!p->fb || !p->fb->id) {
|
||||
if (!drm->fb || !drm->fb->id) {
|
||||
MP_ERR(ctx, "Failed to create framebuffer.\n");
|
||||
return false;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!crtc_setup(ctx)) {
|
||||
if (!vo_drm_acquire_crtc(ctx->vo->drm)) {
|
||||
MP_ERR(ctx, "Failed to set CRTC for connector %u: %s\n",
|
||||
p->kms->connector->connector_id, mp_strerror(errno));
|
||||
return false;
|
||||
drm->connector->connector_id, mp_strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->drm_params.fd = p->kms->fd;
|
||||
p->drm_params.crtc_id = p->kms->crtc_id;
|
||||
p->drm_params.connector_id = p->kms->connector->connector_id;
|
||||
p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request;
|
||||
char *rendernode_path = drmGetRenderDeviceNameFromFd(p->kms->fd);
|
||||
vo_drm_set_monitor_par(ctx->vo);
|
||||
|
||||
p->drm_params.fd = drm->fd;
|
||||
p->drm_params.crtc_id = drm->crtc_id;
|
||||
p->drm_params.connector_id = drm->connector->connector_id;
|
||||
p->drm_params.atomic_request_ptr = &drm->atomic_context->request;
|
||||
char *rendernode_path = drmGetRenderDeviceNameFromFd(drm->fd);
|
||||
if (rendernode_path) {
|
||||
MP_VERBOSE(ctx, "Opening render node \"%s\"\n", rendernode_path);
|
||||
p->drm_params.render_fd = open(rendernode_path, O_RDWR | O_CLOEXEC);
|
||||
|
@ -900,85 +700,42 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
.get_vsync = &drm_egl_get_vsync,
|
||||
};
|
||||
if (!ra_gl_ctx_init(ctx, &p->gl, params))
|
||||
return false;
|
||||
goto err;
|
||||
|
||||
ra_add_native_resource(ctx->ra, "drm_params_v2", &p->drm_params);
|
||||
ra_add_native_resource(ctx->ra, "drm_draw_surface_size", &p->draw_surface_size);
|
||||
|
||||
if (ctx->vo->opts->force_monitor_aspect != 0.0) {
|
||||
ctx->vo->monitor_par = p->fb->width / (double) p->fb->height /
|
||||
ctx->vo->opts->force_monitor_aspect;
|
||||
} else {
|
||||
ctx->vo->monitor_par = 1 / ctx->vo->opts->monitor_pixel_aspect;
|
||||
}
|
||||
|
||||
mp_verbose(ctx->vo->log, "Monitor pixel aspect: %g\n", ctx->vo->monitor_par);
|
||||
|
||||
p->vsync_info.vsync_duration = 0;
|
||||
p->vsync_info.skipped_vsyncs = -1;
|
||||
p->vsync_info.last_queue_display_time = -1;
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
drm_egl_uninit(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool drm_egl_reconfig(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
ctx->vo->dwidth = p->fb->width;
|
||||
ctx->vo->dheight = p->fb->height;
|
||||
ra_gl_ctx_resize(ctx->swapchain, p->fb->width, p->fb->height, 0);
|
||||
struct vo_drm_state *drm = ctx->vo->drm;
|
||||
ctx->vo->dwidth = drm->fb->width;
|
||||
ctx->vo->dheight = drm->fb->height;
|
||||
ra_gl_ctx_resize(ctx->swapchain, drm->fb->width, drm->fb->height, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int drm_egl_control(struct ra_ctx *ctx, int *events, int request,
|
||||
void *arg)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
switch (request) {
|
||||
case VOCTRL_GET_DISPLAY_FPS: {
|
||||
double fps = kms_get_display_fps(p->kms);
|
||||
if (fps <= 0)
|
||||
break;
|
||||
*(double*)arg = fps;
|
||||
return VO_TRUE;
|
||||
}
|
||||
case VOCTRL_GET_DISPLAY_RES: {
|
||||
((int *)arg)[0] = p->kms->mode.mode.hdisplay;
|
||||
((int *)arg)[1] = p->kms->mode.mode.vdisplay;
|
||||
return VO_TRUE;
|
||||
}
|
||||
case VOCTRL_PAUSE:
|
||||
ctx->vo->want_redraw = true;
|
||||
p->paused = true;
|
||||
return VO_TRUE;
|
||||
case VOCTRL_RESUME:
|
||||
p->paused = false;
|
||||
p->vsync_info.last_queue_display_time = -1;
|
||||
p->vsync_info.skipped_vsyncs = 0;
|
||||
p->vsync.ust = 0;
|
||||
p->vsync.msc = 0;
|
||||
return VO_TRUE;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
int ret = vo_drm_control(ctx->vo, events, request, arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wait_events(struct ra_ctx *ctx, int64_t until_time_us)
|
||||
static void drm_egl_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
if (p->vt_switcher_active) {
|
||||
int64_t wait_us = until_time_us - mp_time_us();
|
||||
int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
|
||||
vt_switcher_poll(&p->vt_switcher, timeout_ms);
|
||||
} else {
|
||||
vo_wait_default(ctx->vo, until_time_us);
|
||||
}
|
||||
vo_drm_wait_events(ctx->vo, until_time_us);
|
||||
}
|
||||
|
||||
static void wakeup(struct ra_ctx *ctx)
|
||||
static void drm_egl_wakeup(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
if (p->vt_switcher_active)
|
||||
vt_switcher_interrupt_poll(&p->vt_switcher);
|
||||
vo_drm_wakeup(ctx->vo);
|
||||
}
|
||||
|
||||
const struct ra_ctx_fns ra_ctx_drm_egl = {
|
||||
|
@ -988,6 +745,6 @@ const struct ra_ctx_fns ra_ctx_drm_egl = {
|
|||
.control = drm_egl_control,
|
||||
.init = drm_egl_init,
|
||||
.uninit = drm_egl_uninit,
|
||||
.wait_events = wait_events,
|
||||
.wakeup = wakeup,
|
||||
.wait_events = drm_egl_wait_events,
|
||||
.wakeup = drm_egl_wakeup,
|
||||
};
|
||||
|
|
|
@ -458,6 +458,7 @@ struct vo {
|
|||
struct vo_cocoa_state *cocoa;
|
||||
struct vo_wayland_state *wl;
|
||||
struct vo_android_state *android;
|
||||
struct vo_drm_state *drm;
|
||||
struct mp_hwdec_devices *hwdec_devs;
|
||||
struct input_ctx *input_ctx;
|
||||
struct osd_state *osd;
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
#include <drm_fourcc.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
#include "drm_common.h"
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "drm_atomic.h"
|
||||
#include "drm_common.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "sub/osd.h"
|
||||
#include "video/fmt-conversion.h"
|
||||
|
@ -46,50 +46,18 @@
|
|||
#define BYTES_PER_PIXEL 4
|
||||
#define BITS_PER_PIXEL 32
|
||||
|
||||
struct framebuffer {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t stride;
|
||||
uint32_t size;
|
||||
uint32_t handle;
|
||||
uint8_t *map;
|
||||
uint32_t fb;
|
||||
};
|
||||
|
||||
struct kms_frame {
|
||||
struct drm_frame {
|
||||
struct framebuffer *fb;
|
||||
struct drm_vsync_tuple vsync;
|
||||
};
|
||||
|
||||
struct priv {
|
||||
char *connector_spec;
|
||||
int mode_id;
|
||||
|
||||
struct kms *kms;
|
||||
drmModeCrtc *old_crtc;
|
||||
drmEventContext ev;
|
||||
|
||||
bool vt_switcher_active;
|
||||
struct vt_switcher vt_switcher;
|
||||
|
||||
int swapchain_depth;
|
||||
unsigned int buf_count;
|
||||
struct framebuffer *bufs;
|
||||
int front_buf;
|
||||
bool active;
|
||||
bool waiting_for_flip;
|
||||
bool still;
|
||||
bool paused;
|
||||
|
||||
struct kms_frame **fb_queue;
|
||||
struct drm_frame **fb_queue;
|
||||
unsigned int fb_queue_len;
|
||||
struct framebuffer *cur_fb;
|
||||
|
||||
uint32_t drm_format;
|
||||
enum mp_imgfmt imgfmt;
|
||||
|
||||
int32_t screen_w;
|
||||
int32_t screen_h;
|
||||
struct mp_image *last_input;
|
||||
struct mp_image *cur_frame;
|
||||
struct mp_image *cur_frame_cropped;
|
||||
|
@ -98,53 +66,73 @@ struct priv {
|
|||
struct mp_osd_res osd;
|
||||
struct mp_sws_context *sws;
|
||||
|
||||
struct drm_vsync_tuple vsync;
|
||||
struct vo_vsync_info vsync_info;
|
||||
struct framebuffer **bufs;
|
||||
int front_buf;
|
||||
int buf_count;
|
||||
};
|
||||
|
||||
static void fb_destroy(int fd, struct framebuffer *buf)
|
||||
static void destroy_framebuffer(int fd, struct framebuffer *fb)
|
||||
{
|
||||
if (buf->map) {
|
||||
munmap(buf->map, buf->size);
|
||||
if (!fb)
|
||||
return;
|
||||
|
||||
if (fb->map) {
|
||||
munmap(fb->map, fb->size);
|
||||
}
|
||||
if (buf->fb) {
|
||||
drmModeRmFB(fd, buf->fb);
|
||||
if (fb->id) {
|
||||
drmModeRmFB(fd, fb->id);
|
||||
}
|
||||
if (buf->handle) {
|
||||
if (fb->handle) {
|
||||
struct drm_mode_destroy_dumb dreq = {
|
||||
.handle = buf->handle,
|
||||
.handle = fb->handle,
|
||||
};
|
||||
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
|
||||
}
|
||||
}
|
||||
|
||||
static bool fb_setup_single(struct vo *vo, int fd, struct framebuffer *buf)
|
||||
static struct framebuffer *setup_framebuffer(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
|
||||
buf->handle = 0;
|
||||
struct framebuffer *fb = talloc_zero(drm, struct framebuffer);
|
||||
fb->width = drm->mode.mode.hdisplay;
|
||||
fb->height = drm->mode.mode.vdisplay;
|
||||
fb->fd = drm->fd;
|
||||
fb->handle = 0;
|
||||
|
||||
// create dumb buffer
|
||||
struct drm_mode_create_dumb creq = {
|
||||
.width = buf->width,
|
||||
.height = buf->height,
|
||||
.width = fb->width,
|
||||
.height = fb->height,
|
||||
.bpp = BITS_PER_PIXEL,
|
||||
};
|
||||
if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) < 0) {
|
||||
|
||||
if (drmIoctl(drm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) < 0) {
|
||||
MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
buf->stride = creq.pitch;
|
||||
buf->size = creq.size;
|
||||
buf->handle = creq.handle;
|
||||
|
||||
fb->stride = creq.pitch;
|
||||
fb->size = creq.size;
|
||||
fb->handle = creq.handle;
|
||||
|
||||
// select format
|
||||
if (drm->opts->drm_format == DRM_OPTS_FORMAT_XRGB2101010) {
|
||||
p->drm_format = DRM_FORMAT_XRGB2101010;
|
||||
p->imgfmt = IMGFMT_XRGB2101010;
|
||||
} else {
|
||||
p->drm_format = DRM_FORMAT_XRGB8888;;
|
||||
p->imgfmt = IMGFMT_XRGB8888;
|
||||
}
|
||||
|
||||
// create framebuffer object for the dumb-buffer
|
||||
int ret = drmModeAddFB2(fd, buf->width, buf->height,
|
||||
int ret = drmModeAddFB2(fb->fd, fb->width, fb->height,
|
||||
p->drm_format,
|
||||
(uint32_t[4]){buf->handle, 0, 0, 0},
|
||||
(uint32_t[4]){buf->stride, 0, 0, 0},
|
||||
(uint32_t[4]){fb->handle, 0, 0, 0},
|
||||
(uint32_t[4]){fb->stride, 0, 0, 0},
|
||||
(uint32_t[4]){0, 0, 0, 0},
|
||||
&buf->fb, 0);
|
||||
&fb->id, 0);
|
||||
if (ret) {
|
||||
MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno));
|
||||
goto err;
|
||||
|
@ -152,151 +140,36 @@ static bool fb_setup_single(struct vo *vo, int fd, struct framebuffer *buf)
|
|||
|
||||
// prepare buffer for memory mapping
|
||||
struct drm_mode_map_dumb mreq = {
|
||||
.handle = buf->handle,
|
||||
.handle = fb->handle,
|
||||
};
|
||||
if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq)) {
|
||||
if (drmIoctl(drm->fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq)) {
|
||||
MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
// perform actual memory mapping
|
||||
buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
fd, mreq.offset);
|
||||
if (buf->map == MAP_FAILED) {
|
||||
fb->map = mmap(0, fb->size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
drm->fd, mreq.offset);
|
||||
if (fb->map == MAP_FAILED) {
|
||||
MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(buf->map, 0, buf->size);
|
||||
return true;
|
||||
memset(fb->map, 0, fb->size);
|
||||
return fb;
|
||||
|
||||
err:
|
||||
fb_destroy(fd, buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool fb_setup_buffers(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
p->bufs = talloc_zero_array(p, struct framebuffer, p->buf_count);
|
||||
|
||||
p->front_buf = 0;
|
||||
for (unsigned int i = 0; i < p->buf_count; i++) {
|
||||
p->bufs[i].width = p->kms->mode.mode.hdisplay;
|
||||
p->bufs[i].height = p->kms->mode.mode.vdisplay;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < p->buf_count; i++) {
|
||||
if (!fb_setup_single(vo, p->kms->fd, &p->bufs[i])) {
|
||||
MP_ERR(vo, "Cannot create framebuffer\n");
|
||||
for (unsigned int j = 0; j < i; j++) {
|
||||
fb_destroy(p->kms->fd, &p->bufs[j]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
p->cur_fb = &p->bufs[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
*info = p->vsync_info;
|
||||
}
|
||||
|
||||
static bool crtc_setup(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
if (p->active)
|
||||
return true;
|
||||
p->old_crtc = drmModeGetCrtc(p->kms->fd, p->kms->crtc_id);
|
||||
int ret = drmModeSetCrtc(p->kms->fd, p->kms->crtc_id,
|
||||
p->cur_fb->fb,
|
||||
0, 0, &p->kms->connector->connector_id, 1,
|
||||
&p->kms->mode.mode);
|
||||
p->active = true;
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
static void crtc_release(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
if (!p->active)
|
||||
return;
|
||||
p->active = false;
|
||||
|
||||
// wait for current page flip
|
||||
while (p->waiting_for_flip) {
|
||||
int ret = drmHandleEvent(p->kms->fd, &p->ev);
|
||||
if (ret) {
|
||||
MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->old_crtc) {
|
||||
drmModeSetCrtc(p->kms->fd, p->old_crtc->crtc_id,
|
||||
p->old_crtc->buffer_id,
|
||||
p->old_crtc->x, p->old_crtc->y,
|
||||
&p->kms->connector->connector_id, 1,
|
||||
&p->old_crtc->mode);
|
||||
drmModeFreeCrtc(p->old_crtc);
|
||||
p->old_crtc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void release_vt(void *data)
|
||||
{
|
||||
struct vo *vo = data;
|
||||
crtc_release(vo);
|
||||
|
||||
const struct priv *p = vo->priv;
|
||||
if (drmDropMaster(p->kms->fd)) {
|
||||
MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void acquire_vt(void *data)
|
||||
{
|
||||
struct vo *vo = data;
|
||||
const struct priv *p = vo->priv;
|
||||
if (drmSetMaster(p->kms->fd)) {
|
||||
MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
|
||||
}
|
||||
|
||||
crtc_setup(vo);
|
||||
}
|
||||
|
||||
static void wait_events(struct vo *vo, int64_t until_time_us)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
if (p->vt_switcher_active) {
|
||||
int64_t wait_us = until_time_us - mp_time_us();
|
||||
int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
|
||||
vt_switcher_poll(&p->vt_switcher, timeout_ms);
|
||||
} else {
|
||||
vo_wait_default(vo, until_time_us);
|
||||
}
|
||||
}
|
||||
|
||||
static void wakeup(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
if (p->vt_switcher_active)
|
||||
vt_switcher_interrupt_poll(&p->vt_switcher);
|
||||
destroy_framebuffer(drm->fd, fb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int reconfig(struct vo *vo, struct mp_image_params *params)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
|
||||
vo->dwidth = p->screen_w;
|
||||
vo->dheight = p->screen_h;
|
||||
vo->dwidth =drm->fb->width;
|
||||
vo->dheight = drm->fb->height;
|
||||
vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
|
||||
|
||||
int w = p->dst.x1 - p->dst.x0;
|
||||
|
@ -312,10 +185,10 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
|
|||
};
|
||||
|
||||
talloc_free(p->cur_frame);
|
||||
p->cur_frame = mp_image_alloc(p->imgfmt, p->screen_w, p->screen_h);
|
||||
p->cur_frame = mp_image_alloc(p->imgfmt, drm->fb->width, drm->fb->height);
|
||||
mp_image_params_guess_csp(&p->sws->dst);
|
||||
mp_image_set_params(p->cur_frame, &p->sws->dst);
|
||||
mp_image_set_size(p->cur_frame, p->screen_w, p->screen_h);
|
||||
mp_image_set_size(p->cur_frame, drm->fb->width, drm->fb->height);
|
||||
|
||||
talloc_free(p->cur_frame_cropped);
|
||||
p->cur_frame_cropped = mp_image_new_dummy_ref(p->cur_frame);
|
||||
|
@ -327,33 +200,14 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
|
|||
if (mp_sws_reinit(p->sws) < 0)
|
||||
return -1;
|
||||
|
||||
p->vsync_info.vsync_duration = 0;
|
||||
p->vsync_info.skipped_vsyncs = -1;
|
||||
p->vsync_info.last_queue_display_time = -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;
|
||||
}
|
||||
|
||||
static void wait_on_flip(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
// poll page flip finish event
|
||||
while (p->waiting_for_flip) {
|
||||
const int timeout_ms = 3000;
|
||||
struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
|
||||
poll(fds, 1, timeout_ms);
|
||||
if (fds[0].revents & POLLIN) {
|
||||
const int ret = drmHandleEvent(p->kms->fd, &p->ev);
|
||||
if (ret != 0) {
|
||||
MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct framebuffer *get_new_fb(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
@ -361,14 +215,15 @@ static struct framebuffer *get_new_fb(struct vo *vo)
|
|||
p->front_buf++;
|
||||
p->front_buf %= p->buf_count;
|
||||
|
||||
return &p->bufs[p->front_buf];
|
||||
return p->bufs[p->front_buf];
|
||||
}
|
||||
|
||||
static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *front_buf)
|
||||
static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *buf)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
|
||||
if (p->active && front_buf != NULL) {
|
||||
if (drm->active && buf != NULL) {
|
||||
if (mpi) {
|
||||
struct mp_image src = *mpi;
|
||||
struct mp_rect src_rc = p->src;
|
||||
|
@ -396,12 +251,12 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *front
|
|||
const int g_padding = p->cur_frame->stride[0]/sizeof(uint16_t) - w;
|
||||
const int b_padding = p->cur_frame->stride[1]/sizeof(uint16_t) - w;
|
||||
const int r_padding = p->cur_frame->stride[2]/sizeof(uint16_t) - w;
|
||||
const int fbuf_padding = front_buf->stride/sizeof(uint32_t) - w;
|
||||
const int fbuf_padding = buf->stride/sizeof(uint32_t) - w;
|
||||
|
||||
uint16_t *g_ptr = (uint16_t*)p->cur_frame->planes[0];
|
||||
uint16_t *b_ptr = (uint16_t*)p->cur_frame->planes[1];
|
||||
uint16_t *r_ptr = (uint16_t*)p->cur_frame->planes[2];
|
||||
uint32_t *fbuf_ptr = (uint32_t*)front_buf->map;
|
||||
uint32_t *fbuf_ptr = (uint32_t*)buf->map;
|
||||
for (unsigned y = 0; y < h; ++y) {
|
||||
for (unsigned x = 0; x < w; ++x) {
|
||||
*fbuf_ptr++ = (*r_ptr++ << 20) | (*g_ptr++ << 10) | (*b_ptr++);
|
||||
|
@ -412,9 +267,9 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *front
|
|||
fbuf_ptr += fbuf_padding;
|
||||
}
|
||||
} else { // p->drm_format == DRM_FORMAT_XRGB8888
|
||||
memcpy_pic(front_buf->map, p->cur_frame->planes[0],
|
||||
memcpy_pic(buf->map, p->cur_frame->planes[0],
|
||||
p->cur_frame->w * BYTES_PER_PIXEL, p->cur_frame->h,
|
||||
front_buf->stride,
|
||||
buf->stride,
|
||||
p->cur_frame->stride[0]);
|
||||
}
|
||||
}
|
||||
|
@ -428,11 +283,12 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, struct framebuffer *front
|
|||
static void enqueue_frame(struct vo *vo, struct framebuffer *fb)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
|
||||
p->vsync.sbc++;
|
||||
struct kms_frame *new_frame = talloc(p, struct kms_frame);
|
||||
drm->vsync.sbc++;
|
||||
struct drm_frame *new_frame = talloc(p, struct drm_frame);
|
||||
new_frame->fb = fb;
|
||||
new_frame->vsync = p->vsync;
|
||||
new_frame->vsync = drm->vsync;
|
||||
MP_TARRAY_APPEND(p, p->fb_queue, p->fb_queue_len, new_frame);
|
||||
}
|
||||
|
||||
|
@ -455,17 +311,17 @@ static void swapchain_step(struct vo *vo)
|
|||
|
||||
static void draw_frame(struct vo *vo, struct vo_frame *frame)
|
||||
{
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
if (!p->active)
|
||||
if (!drm->active)
|
||||
return;
|
||||
|
||||
p->still = frame->still;
|
||||
drm->still = frame->still;
|
||||
|
||||
// we redraw the entire image when OSD needs to be redrawn
|
||||
struct framebuffer *fb = p->bufs[p->front_buf];
|
||||
const bool repeat = frame->repeat && !frame->redraw;
|
||||
|
||||
struct framebuffer *fb = &p->bufs[p->front_buf];
|
||||
if (!repeat) {
|
||||
fb = get_new_fb(vo);
|
||||
draw_image(vo, mp_image_new_ref(frame->current), fb);
|
||||
|
@ -474,42 +330,42 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
|
|||
enqueue_frame(vo, fb);
|
||||
}
|
||||
|
||||
static void queue_flip(struct vo *vo, struct kms_frame *frame)
|
||||
static void queue_flip(struct vo *vo, struct drm_frame *frame)
|
||||
{
|
||||
int ret = 0;
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
|
||||
p->cur_fb = frame->fb;
|
||||
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 = &p->vsync;
|
||||
data->vsync_info = &p->vsync_info;
|
||||
data->waiting_for_flip = &p->waiting_for_flip;
|
||||
data->vsync = &drm->vsync;
|
||||
data->vsync_info = &drm->vsync_info;
|
||||
data->waiting_for_flip = &drm->waiting_for_flip;
|
||||
data->log = vo->log;
|
||||
|
||||
ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id,
|
||||
p->cur_fb->fb,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, data);
|
||||
int ret = drmModePageFlip(drm->fd, drm->crtc_id,
|
||||
drm->fb->id, DRM_MODE_PAGE_FLIP_EVENT, data);
|
||||
if (ret) {
|
||||
MP_WARN(vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
|
||||
} else {
|
||||
p->waiting_for_flip = true;
|
||||
talloc_free(data);
|
||||
}
|
||||
drm->waiting_for_flip = !ret;
|
||||
}
|
||||
|
||||
static void flip_page(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
const bool drain = p->paused || p->still;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
const bool drain = drm->paused || drm->still;
|
||||
|
||||
if (!p->active)
|
||||
if (!drm->active)
|
||||
return;
|
||||
|
||||
while (drain || p->fb_queue_len > p->swapchain_depth) {
|
||||
if (p->waiting_for_flip) {
|
||||
wait_on_flip(vo);
|
||||
while (drain || p->fb_queue_len > vo->opts->swapchain_depth) {
|
||||
if (drm->waiting_for_flip) {
|
||||
vo_drm_wait_on_flip(vo->drm);
|
||||
swapchain_step(vo);
|
||||
}
|
||||
if (p->fb_queue_len <= 1)
|
||||
|
@ -526,22 +382,17 @@ static void flip_page(struct vo *vo)
|
|||
static void uninit(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
int fd = drm->fd;
|
||||
|
||||
crtc_release(vo);
|
||||
vo_drm_uninit(vo);
|
||||
|
||||
while (p->fb_queue_len > 0) {
|
||||
swapchain_step(vo);
|
||||
}
|
||||
|
||||
if (p->kms) {
|
||||
for (unsigned int i = 0; i < p->buf_count; i++)
|
||||
fb_destroy(p->kms->fd, &p->bufs[i]);
|
||||
kms_destroy(p->kms);
|
||||
p->kms = NULL;
|
||||
}
|
||||
|
||||
if (p->vt_switcher_active)
|
||||
vt_switcher_destroy(&p->vt_switcher);
|
||||
for (int i = 0; i < p->buf_count; ++i)
|
||||
destroy_framebuffer(fd, p->bufs[i]);
|
||||
|
||||
talloc_free(p->last_input);
|
||||
talloc_free(p->cur_frame);
|
||||
|
@ -551,73 +402,35 @@ static void uninit(struct vo *vo)
|
|||
static int preinit(struct vo *vo)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
if (!vo_drm_init(vo))
|
||||
goto err;
|
||||
|
||||
struct vo_drm_state *drm = vo->drm;
|
||||
p->buf_count = vo->opts->swapchain_depth + 1;
|
||||
p->bufs = talloc_zero_array(p, struct framebuffer *, p->buf_count);
|
||||
|
||||
p->front_buf = 0;
|
||||
for (int i = 0; i < p->buf_count; i++) {
|
||||
p->bufs[i] = setup_framebuffer(vo);
|
||||
if (!p->bufs[i])
|
||||
goto err;
|
||||
}
|
||||
drm->fb = p->bufs[0];
|
||||
|
||||
vo->drm->width = vo->drm->fb->width;
|
||||
vo->drm->height = vo->drm->fb->height;
|
||||
|
||||
if (!vo_drm_acquire_crtc(vo->drm)) {
|
||||
MP_ERR(vo, "Failed to set CRTC for connector %u: %s\n",
|
||||
vo->drm->connector->connector_id, mp_strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
vo_drm_set_monitor_par(vo);
|
||||
p->sws = mp_sws_alloc(vo);
|
||||
p->sws->log = vo->log;
|
||||
mp_sws_enable_cmdline_opts(p->sws, vo->global);
|
||||
p->ev.version = DRM_EVENT_CONTEXT_VERSION;
|
||||
p->ev.page_flip_handler = &drm_pflip_cb;
|
||||
|
||||
p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, vo->log);
|
||||
if (p->vt_switcher_active) {
|
||||
vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo);
|
||||
vt_switcher_release(&p->vt_switcher, release_vt, vo);
|
||||
} else {
|
||||
MP_WARN(vo, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
|
||||
}
|
||||
|
||||
p->kms = kms_create(vo->log,
|
||||
vo->opts->drm_opts->drm_device_path,
|
||||
vo->opts->drm_opts->drm_connector_spec,
|
||||
vo->opts->drm_opts->drm_mode_spec,
|
||||
0, 0);
|
||||
if (!p->kms) {
|
||||
MP_ERR(vo, "Failed to create KMS.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (vo->opts->drm_opts->drm_format == DRM_OPTS_FORMAT_XRGB2101010) {
|
||||
p->drm_format = DRM_FORMAT_XRGB2101010;
|
||||
p->imgfmt = IMGFMT_XRGB2101010;
|
||||
} else {
|
||||
p->drm_format = DRM_FORMAT_XRGB8888;;
|
||||
p->imgfmt = IMGFMT_XRGB8888;
|
||||
}
|
||||
|
||||
p->swapchain_depth = vo->opts->swapchain_depth;
|
||||
p->buf_count = p->swapchain_depth + 1;
|
||||
if (!fb_setup_buffers(vo)) {
|
||||
MP_ERR(vo, "Failed to set up buffers.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint64_t has_dumb = 0;
|
||||
if (drmGetCap(p->kms->fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0
|
||||
|| has_dumb == 0) {
|
||||
MP_ERR(vo, "Card \"%d\" does not support dumb buffers.\n",
|
||||
p->kms->card_no);
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->screen_w = p->bufs[0].width;
|
||||
p->screen_h = p->bufs[0].height;
|
||||
|
||||
if (!crtc_setup(vo)) {
|
||||
MP_ERR(vo, "Cannot set CRTC: %s\n", mp_strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (vo->opts->force_monitor_aspect != 0.0) {
|
||||
vo->monitor_par = p->screen_w / (double) p->screen_h /
|
||||
vo->opts->force_monitor_aspect;
|
||||
} else {
|
||||
vo->monitor_par = 1 / vo->opts->monitor_pixel_aspect;
|
||||
}
|
||||
mp_verbose(vo->log, "Monitor pixel aspect: %g\n", vo->monitor_par);
|
||||
|
||||
p->vsync_info.vsync_duration = 0;
|
||||
p->vsync_info.skipped_vsyncs = -1;
|
||||
p->vsync_info.last_queue_display_time = -1;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -633,6 +446,7 @@ static int query_format(struct vo *vo, int format)
|
|||
static int control(struct vo *vo, uint32_t request, void *arg)
|
||||
{
|
||||
struct priv *p = vo->priv;
|
||||
|
||||
switch (request) {
|
||||
case VOCTRL_SCREENSHOT_WIN:
|
||||
*(struct mp_image**)arg = mp_image_new_copy(p->cur_frame);
|
||||
|
@ -641,34 +455,13 @@ static int control(struct vo *vo, uint32_t request, void *arg)
|
|||
if (vo->config_ok)
|
||||
reconfig(vo, vo->params);
|
||||
return VO_TRUE;
|
||||
case VOCTRL_GET_DISPLAY_FPS: {
|
||||
double fps = kms_get_display_fps(p->kms);
|
||||
if (fps <= 0)
|
||||
break;
|
||||
*(double*)arg = fps;
|
||||
return VO_TRUE;
|
||||
}
|
||||
case VOCTRL_GET_DISPLAY_RES: {
|
||||
((int *)arg)[0] = p->kms->mode.mode.hdisplay;
|
||||
((int *)arg)[1] = p->kms->mode.mode.vdisplay;
|
||||
return VO_TRUE;
|
||||
}
|
||||
case VOCTRL_PAUSE:
|
||||
vo->want_redraw = true;
|
||||
p->paused = true;
|
||||
return VO_TRUE;
|
||||
case VOCTRL_RESUME:
|
||||
p->paused = false;
|
||||
p->vsync_info.last_queue_display_time = -1;
|
||||
p->vsync_info.skipped_vsyncs = 0;
|
||||
p->vsync.ust = 0;
|
||||
p->vsync.msc = 0;
|
||||
return VO_TRUE;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
int events = 0;
|
||||
int ret = vo_drm_control(vo, &events, request, arg);
|
||||
vo_event(vo, events);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct vo_driver video_out_drm = {
|
||||
.name = "drm",
|
||||
|
@ -679,9 +472,9 @@ const struct vo_driver video_out_drm = {
|
|||
.control = control,
|
||||
.draw_frame = draw_frame,
|
||||
.flip_page = flip_page,
|
||||
.get_vsync = get_vsync,
|
||||
.get_vsync = vo_drm_get_vsync,
|
||||
.uninit = uninit,
|
||||
.wait_events = wait_events,
|
||||
.wakeup = wakeup,
|
||||
.wait_events = vo_drm_wait_events,
|
||||
.wakeup = vo_drm_wakeup,
|
||||
.priv_size = sizeof(struct priv),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue