diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index 672f3c68f6f4c..3fb2567b7dcaa 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -488,10 +488,19 @@ Available video output drivers are: Mode ID to use (resolution and frame rate). (default: 0) - ``--drm-overlay=`` - Select the DRM overlay index to use. - Overlay index is zero based, and related to crtc. - (default: 0) + ``--drm-osd-plane-id=`` + Select the DRM planed index to use for OSD (or OSD and video). + Index is zero based, and related to crtc. + When using this option with drm_prime renderer, it will only affect + the OSD contents. Otherwise it will set OSD & video plane. + (default: primary plane) + + ``--drm-video-plane-id=`` + Select the DRM planed index to use for video layer. + Index is zero based, and related to crtc. + This option only has effect when using the drm_prime renderer (which + supports several layers) together with ``vo=gpu`` and ``gpu-context=drm``. + (default: first overlay plane) ``--drm-format=`` Select the DRM format to use (default: xrgb8888). This allows you to @@ -506,6 +515,13 @@ Available video output drivers are: This currently only has an effect when used together with the ``drm`` backend for the ``gpu`` VO. The ``drm`` VO always uses xrgb8888. + ``--drm-osd-size=<[WxH]>`` + Sets the OSD OpenGL size to the specified size. OSD will then be upscaled + to the current screen resolution. This option can be useful when using + several layers in high resolutions with a GPU which cannot handle it. + Note : this option is only available with DRM atomic support. + (default: display resolution) + ``mediacodec_embed`` (Android) Renders ``IMGFMT_MEDIACODEC`` frames directly to an ``android.view.Surface``. Requires ``--hwdec=mediacodec`` for hardware decoding, along with diff --git a/libmpv/opengl_cb.h b/libmpv/opengl_cb.h index dd9ab2cf0939c..682068133a506 100644 --- a/libmpv/opengl_cb.h +++ b/libmpv/opengl_cb.h @@ -177,6 +177,9 @@ struct mpv_opengl_cb_drm_params { // currently used crtc id int crtc_id; + // currently used connector id + int connector_id; + // pointer to the drmModeAtomicReq that is being used for the renderloop. // This atomic request pointer should be usually created at every renderloop. struct _drmModeAtomicReq *atomic_request; diff --git a/libmpv/render.h b/libmpv/render.h index 5a39d4e1c6ad5..253b2b43f9313 100644 --- a/libmpv/render.h +++ b/libmpv/render.h @@ -276,6 +276,18 @@ typedef enum mpv_render_param_type { * in the same way. */ MPV_RENDER_PARAM_SKIP_RENDERING = 13, + /** + * DRM display, contains drm display handles. + * Valid for mpv_render_context_create(). + * Type : struct mpv_opengl_drm_params* + */ + MPV_RENDER_PARAM_DRM_DISPLAY = 14, + /** + * DRM osd size, contains osd dimensions. + * Valid for mpv_render_context_create(). + * Type : struct mpv_opengl_drm_osd_size* + */ + MPV_RENDER_PARAM_DRM_OSD_SIZE = 15, } mpv_render_param_type; /** diff --git a/libmpv/render_gl.h b/libmpv/render_gl.h index a4872a066b78c..574be86ffd36f 100644 --- a/libmpv/render_gl.h +++ b/libmpv/render_gl.h @@ -147,6 +147,37 @@ typedef struct mpv_opengl_fbo { int internal_format; } mpv_opengl_fbo; +typedef struct mpv_opengl_drm_params { + /** + * DRM fd (int). set this to -1 if invalid. + */ + int fd; + + /** + * Currently used crtc id + */ + int crtc_id; + + /** + * Currently used connector id + */ + int connector_id; + + /** + * Pointer to a drmModeAtomicReq pointer that is being used for the renderloop. + * This pointer should hold a pointer to the atomic request pointer + * The atomic request pointer is usually changed at every renderloop. + */ + struct _drmModeAtomicReq **atomic_request_ptr; +} mpv_opengl_drm_params; + +typedef struct mpv_opengl_drm_osd_size { + /** + * size of the OSD in pixels. + */ + int width, height; +} mpv_opengl_drm_osd_size; + #ifdef __cplusplus } #endif diff --git a/video/out/drm_atomic.c b/video/out/drm_atomic.c index 7a55483675640..e6f7cdbd7e7cb 100644 --- a/video/out/drm_atomic.c +++ b/video/out/drm_atomic.c @@ -78,6 +78,17 @@ int drm_object_get_property(struct drm_object *object, char *name, uint64_t *val return -EINVAL; } +drmModePropertyBlobPtr drm_object_get_property_blob(struct drm_object *object, char *name) +{ + uint64_t blob_id; + + if (!drm_object_get_property(object, name, &blob_id)) { + return drmModeGetPropertyBlob(object->fd, blob_id); + } + + return NULL; +} + int drm_object_set_property(drmModeAtomicReq *request, struct drm_object *object, char *name, uint64_t value) { @@ -98,6 +109,7 @@ struct drm_object * drm_object_create(struct mp_log *log, int fd, obj = talloc_zero(NULL, struct drm_object); obj->id = object_id; obj->type = type; + obj->fd = fd; if (drm_object_create_properties(log, fd, obj)) { talloc_free(obj); @@ -125,8 +137,8 @@ void drm_object_print_info(struct mp_log *log, struct drm_object *object) (long long)object->props->prop_values[i]); } -struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, - int crtc_id, int overlay_id) +struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, int crtc_id, + int connector_id, int osd_plane_id, int video_plane_id) { drmModePlane *drmplane = NULL; drmModePlaneRes *plane_res = NULL; @@ -134,7 +146,10 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, struct drm_object *plane = NULL; struct drm_atomic_context *ctx; int crtc_index = -1; - int layercount = 0; + int layercount = -1; + int primary_id = 0; + int overlay_id = 0; + uint64_t value; res = drmModeGetResources(fd); @@ -169,6 +184,19 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, } } + for (int i = 0; i < res->count_connectors; i++) { + drmModeConnector *connector = drmModeGetConnector(fd, res->connectors[i]); + if (connector) { + 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; + } + } + for (unsigned int j = 0; j < plane_res->count_planes; j++) { drmplane = drmModeGetPlane (ctx->fd, plane_res->planes[j]); @@ -181,20 +209,29 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, mp_err(log, "Unable to retrieve type property from plane %d\n", j); goto fail; } else { - if ((value == DRM_PLANE_TYPE_OVERLAY) && - (layercount == overlay_id)) { - ctx->overlay_plane = plane; - } - else if (value == DRM_PLANE_TYPE_PRIMARY) { - ctx->primary_plane = plane; + if (value == DRM_PLANE_TYPE_CURSOR) // Skip cursor planes + continue; + + layercount++; + + if ((!primary_id) && (value == DRM_PLANE_TYPE_PRIMARY)) + primary_id = drmplane->plane_id; + + if ((!overlay_id) && (value == DRM_PLANE_TYPE_OVERLAY)) + overlay_id = drmplane->plane_id; + + if (layercount == osd_plane_id) { + ctx->osd_plane = plane; + continue; } - else { - drm_object_free(plane); - plane = NULL; + + if (layercount == video_plane_id) { + ctx->video_plane = plane; + continue; } - if (value == DRM_PLANE_TYPE_OVERLAY) - layercount++; + drm_object_free(plane); + plane = NULL; } } else { mp_err(log, "Failed to create Plane object from plane ID %d\n", @@ -206,18 +243,34 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, drmplane = NULL; } - if (!ctx->primary_plane) { - mp_err(log, "Failed to find primary plane\n"); - goto fail; + // default OSD plane to primary if unspecified + if (!ctx->osd_plane) { + if (primary_id) { + mp_verbose(log, "Using default plane %d for OSD\n", primary_id); + ctx->osd_plane = drm_object_create(log, ctx->fd, primary_id, DRM_MODE_OBJECT_PLANE); + } else { + mp_err(log, "Failed to find OSD plane with id=%d\n", osd_plane_id); + goto fail; + } + } else { + mp_verbose(log, "Found OSD plane with ID %d\n", ctx->osd_plane->id); } - if (!ctx->overlay_plane) { - mp_err(log, "Failed to find overlay plane with id=%d\n", overlay_id); - goto fail; + // default video plane to overlay if unspecified + if (!ctx->video_plane) { + if (overlay_id) { + mp_verbose(log, "Using default plane %d for video\n", overlay_id); + ctx->video_plane = drm_object_create(log, ctx->fd, overlay_id, DRM_MODE_OBJECT_PLANE); + } else { + mp_err(log, "Failed to find video plane with id=%d\n", video_plane_id); + goto fail; + } + } else { + mp_verbose(log, "Found video plane with ID %d\n", ctx->video_plane->id); } - mp_verbose(log, "Found Primary plane with ID %d, overlay with ID %d\n", - ctx->primary_plane->id, ctx->overlay_plane->id); + mp_verbose(log, "Found Video plane with ID %d, OSD with ID %d\n", + ctx->video_plane->id, ctx->osd_plane->id); drmModeFreePlaneResources(plane_res); drmModeFreeResources(res); @@ -239,7 +292,8 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, void drm_atomic_destroy_context(struct drm_atomic_context *ctx) { drm_object_free(ctx->crtc); - drm_object_free(ctx->primary_plane); - drm_object_free(ctx->overlay_plane); + drm_object_free(ctx->connector); + drm_object_free(ctx->osd_plane); + drm_object_free(ctx->video_plane); talloc_free(ctx); } diff --git a/video/out/drm_atomic.h b/video/out/drm_atomic.h index d0ebdb910eb4c..01246baf3622f 100644 --- a/video/out/drm_atomic.h +++ b/video/out/drm_atomic.h @@ -25,6 +25,7 @@ #include "common/msg.h" struct drm_object { + int fd; uint32_t id; uint32_t type; drmModeObjectProperties *props; @@ -35,8 +36,9 @@ struct drm_atomic_context { int fd; struct drm_object *crtc; - struct drm_object *primary_plane; - struct drm_object *overlay_plane; + struct drm_object *connector; + struct drm_object *osd_plane; + struct drm_object *video_plane; drmModeAtomicReq *request; }; @@ -46,10 +48,12 @@ int drm_object_create_properties(struct mp_log *log, int fd, struct drm_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); 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 overlay_id); +struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, int crtc_id, int connector_id, + int osd_plane_id, int video_plane_id); void drm_atomic_destroy_context(struct drm_atomic_context *ctx); #endif // MP_DRMATOMIC_H diff --git a/video/out/drm_common.c b/video/out/drm_common.c index 68c64d4298287..df06744fbe6bd 100644 --- a/video/out/drm_common.c +++ b/video/out/drm_common.c @@ -47,12 +47,18 @@ const struct m_sub_options drm_conf = { OPT_STRING_VALIDATE("drm-connector", drm_connector_spec, 0, drm_validate_connector_opt), OPT_INT("drm-mode", drm_mode_id, 0), - OPT_INT("drm-overlay", drm_overlay_id, 0), + OPT_INT("drm-osd-plane-id", drm_osd_plane_id, 0), + OPT_INT("drm-video-plane-id", drm_video_plane_id, 0), OPT_CHOICE("drm-format", drm_format, 0, ({"xrgb8888", DRM_OPTS_FORMAT_XRGB8888}, {"xrgb2101010", DRM_OPTS_FORMAT_XRGB2101010})), + OPT_SIZE_BOX("drm-osd-size", drm_osd_size, 0), {0}, }, + .defaults = &(const struct drm_opts) { + .drm_osd_plane_id = -1, + .drm_video_plane_id = -1, + }, .size = sizeof(struct drm_opts), }; @@ -167,6 +173,27 @@ static bool setup_connector(struct kms *kms, const drmModeRes *res, static bool setup_crtc(struct kms *kms, const drmModeRes *res) { + // First try to find currently connected encoder and its current CRTC + for (unsigned int i = 0; i < res->count_encoders; i++) { + drmModeEncoder *encoder = drmModeGetEncoder(kms->fd, res->encoders[i]); + if (!encoder) { + MP_WARN(kms, "Cannot retrieve encoder %u:%u: %s\n", + i, res->encoders[i], mp_strerror(errno)); + continue; + } + + if (encoder->encoder_id == kms->connector->encoder_id && encoder->crtc_id != 0) { + MP_VERBOSE(kms, "Connector %u currently connected to encoder %u\n", + kms->connector->connector_id, kms->connector->encoder_id); + kms->encoder = encoder; + kms->crtc_id = encoder->crtc_id; + goto success; + } + + drmModeFreeEncoder(encoder); + } + + // Otherwise pick first legal encoder and CRTC combo for the connector for (unsigned int i = 0; i < kms->connector->count_encoders; ++i) { drmModeEncoder *encoder = drmModeGetEncoder(kms->fd, kms->connector->encoders[i]); @@ -184,7 +211,7 @@ static bool setup_crtc(struct kms *kms, const drmModeRes *res) kms->encoder = encoder; kms->crtc_id = res->crtcs[j]; - return true; + goto success; } drmModeFreeEncoder(encoder); @@ -193,6 +220,11 @@ static bool setup_crtc(struct kms *kms, const drmModeRes *res) MP_ERR(kms, "Connector %u has no suitable CRTC\n", kms->connector->connector_id); return false; + + success: + MP_VERBOSE(kms, "Selected Encoder %u with CRTC %u\n", + kms->encoder->encoder_id, kms->crtc_id); + return true; } static bool setup_mode(struct kms *kms, int mode_id) @@ -237,7 +269,7 @@ static void parse_connector_spec(struct mp_log *log, struct kms *kms_create(struct mp_log *log, const char *connector_spec, - int mode_id, int overlay_id) + int mode_id, int osd_plane_id, int video_plane_id) { int card_no = -1; char *connector_name = NULL; @@ -284,7 +316,8 @@ struct kms *kms_create(struct mp_log *log, const char *connector_spec, mp_verbose(log, "No DRM Atomic support found\n"); } else { mp_verbose(log, "DRM Atomic support found\n"); - kms->atomic_context = drm_atomic_create_context(kms->log, kms->fd, kms->crtc_id, overlay_id); + kms->atomic_context = drm_atomic_create_context(kms->log, kms->fd, kms->crtc_id, + kms->connector->connector_id, osd_plane_id, video_plane_id); if (!kms->atomic_context) { mp_err(log, "Failed to create DRM atomic context\n"); goto err; diff --git a/video/out/drm_common.h b/video/out/drm_common.h index ba1209e43cbab..4d1e2a9e3f04d 100644 --- a/video/out/drm_common.h +++ b/video/out/drm_common.h @@ -48,8 +48,10 @@ struct vt_switcher { struct drm_opts { char *drm_connector_spec; int drm_mode_id; - int drm_overlay_id; + int drm_osd_plane_id; + int drm_video_plane_id; int drm_format; + struct m_geometry drm_osd_size; }; bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log); @@ -63,7 +65,7 @@ void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*), void *user_data); struct kms *kms_create(struct mp_log *log, const char *connector_spec, - int mode_id, int overlay_id); + int mode_id, int osd_plane_id, int video_plane_id); void kms_destroy(struct kms *kms); double kms_get_display_fps(const struct kms *kms); diff --git a/video/out/gpu/libmpv_gpu.c b/video/out/gpu/libmpv_gpu.c index f687c97b51629..fce2acfa4d059 100644 --- a/video/out/gpu/libmpv_gpu.c +++ b/video/out/gpu/libmpv_gpu.c @@ -1,6 +1,7 @@ #include "config.h" #include "hwdec.h" #include "libmpv_gpu.h" +#include "libmpv/render_gl.h" #include "video.h" #include "video/out/libmpv.h" @@ -31,6 +32,14 @@ static const struct native_resource_entry native_resource_map[] = { .name = "wl", .size = 0, }, + [MPV_RENDER_PARAM_DRM_DISPLAY] = { + .name = "drm_params", + .size = sizeof (mpv_opengl_drm_params), + }, + [MPV_RENDER_PARAM_DRM_OSD_SIZE] = { + .name = "drm_osd_size", + .size = sizeof (mpv_opengl_drm_osd_size), + }, }; static int init(struct render_backend *ctx, mpv_render_param *params) diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c index e56055bc97362..1ba7f5dd0504f 100644 --- a/video/out/opengl/context_drm_egl.c +++ b/video/out/opengl/context_drm_egl.c @@ -28,7 +28,7 @@ #include #include -#include "libmpv/opengl_cb.h" +#include "libmpv/render_gl.h" #include "video/out/drm_common.h" #include "common/common.h" #include "osdep/timer.h" @@ -80,7 +80,8 @@ struct priv { bool vt_switcher_active; struct vt_switcher vt_switcher; - struct mpv_opengl_cb_drm_params drm_params; + struct mpv_opengl_drm_params drm_params; + struct mpv_opengl_drm_osd_size osd_size; }; // Not general. Limited to only the formats being used in this module @@ -194,11 +195,11 @@ static bool init_gbm(struct ra_ctx *ctx) } MP_VERBOSE(ctx->vo, "Initializing GBM surface (%d x %d)\n", - p->kms->mode.hdisplay, p->kms->mode.vdisplay); + p->osd_size.width, p->osd_size.height); p->gbm.surface = gbm_surface_create( p->gbm.device, - p->kms->mode.hdisplay, - p->kms->mode.vdisplay, + p->osd_size.width, + p->osd_size.height, p->gbm_format, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!p->gbm.surface) { @@ -246,17 +247,113 @@ static void update_framebuffer_from_bo(struct ra_ctx *ctx, struct gbm_bo *bo) p->fb = fb; } +static bool crtc_setup_atomic(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; + + drmModeAtomicReqPtr request = drmModeAtomicAlloc(); + if (request) { + 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; + } + + uint32_t blob_id; + if (drmModeCreatePropertyBlob(p->kms->fd, &p->kms->mode, sizeof(drmModeModeInfo), + &blob_id) != 0) { + MP_ERR(ctx->vo, "Failed to create DRM mode blob\n"); + return false; + } + if (drm_object_set_property(request, atomic_ctx->crtc, "MODE_ID", blob_id) < 0) { + MP_ERR(ctx->vo, "Could not set MODE_ID on crtc\n"); + return false; + } + if (drm_object_set_property(request, atomic_ctx->crtc, "ACTIVE", 1) < 0) { + MP_ERR(ctx->vo, "Could not set ACTIVE on crtc\n"); + return false; + } + + drm_object_set_property(request, atomic_ctx->osd_plane, "FB_ID", p->fb->id); + drm_object_set_property(request, atomic_ctx->osd_plane, "CRTC_ID", p->kms->crtc_id); + drm_object_set_property(request, atomic_ctx->osd_plane, "SRC_X", 0); + drm_object_set_property(request, atomic_ctx->osd_plane, "SRC_Y", 0); + drm_object_set_property(request, atomic_ctx->osd_plane, "SRC_W", p->osd_size.width << 16); + drm_object_set_property(request, atomic_ctx->osd_plane, "SRC_H", p->osd_size.height << 16); + drm_object_set_property(request, atomic_ctx->osd_plane, "CRTC_X", 0); + drm_object_set_property(request, atomic_ctx->osd_plane, "CRTC_Y", 0); + drm_object_set_property(request, atomic_ctx->osd_plane, "CRTC_W", p->kms->mode.hdisplay); + drm_object_set_property(request, atomic_ctx->osd_plane, "CRTC_H", p->kms->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 false; + } + + drmModeAtomicFree(request); + + return ret == 0; + } else { + MP_ERR(ctx->vo, "Failed to allocate drm atomic request\n"); + } + + return false; +} + +static bool crtc_release_atomic(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + + struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; + drmModeAtomicReqPtr request = drmModeAtomicAlloc(); + if (request) { + drm_object_set_property(request, atomic_ctx->connector, "CRTC_ID", p->old_crtc->crtc_id); + + uint32_t blob_id; + if (drmModeCreatePropertyBlob(p->kms->fd, &p->old_crtc->mode, sizeof(drmModeModeInfo), + &blob_id) != 0) { + MP_ERR(ctx->vo, "Failed to create DRM mode blob\n"); + return false; + } + drm_object_set_property(request, atomic_ctx->crtc, "MODE_ID", blob_id); + drm_object_set_property(request, atomic_ctx->crtc, "ACTIVE", 1); + drm_object_set_property(request, atomic_ctx->osd_plane, "FB_ID", p->old_crtc->buffer_id); + + int ret = drmModeAtomicCommit(p->kms->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + if (ret) + MP_WARN(ctx->vo, "Failed to commit ModeSetting atomic request (%d)\n", ret); + + drmModeAtomicFree(request); + + return ret == 0; + } else { + MP_ERR(ctx->vo, "Failed to allocate drm atomic request\n"); + } + + return false; +} + static bool crtc_setup(struct ra_ctx *ctx) { struct priv *p = ctx->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->fb->id, - 0, 0, &p->kms->connector->connector_id, 1, - &p->kms->mode); - p->active = true; - return ret == 0; + + if (p->kms->atomic_context) { + int ret = crtc_setup_atomic(ctx); + p->active = true; + return ret; + } else { + int ret = drmModeSetCrtc(p->kms->fd, p->kms->crtc_id, p->fb->id, + 0, 0, &p->kms->connector->connector_id, 1, + &p->kms->mode); + p->active = true; + return ret == 0; + } } static void crtc_release(struct ra_ctx *ctx) @@ -277,11 +374,16 @@ static void crtc_release(struct ra_ctx *ctx) } 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); + if (p->kms->atomic_context) { + if (!crtc_release_atomic(ctx)) + MP_ERR(ctx->vo, "Failed to restore previous mode\n"); + } else { + 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; } @@ -325,7 +427,7 @@ static bool drm_atomic_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *o if (p->kms->atomic_context) { if (!p->kms->atomic_context->request) { p->kms->atomic_context->request = drmModeAtomicAlloc(); - p->drm_params.atomic_request = p->kms->atomic_context->request; + p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request; } return ra_gl_ctx_start_frame(sw, out_fbo); } @@ -351,9 +453,9 @@ static void drm_egl_swap_buffers(struct ra_ctx *ctx) update_framebuffer_from_bo(ctx, p->gbm.next_bo); if (atomic_ctx) { - drm_object_set_property(atomic_ctx->request, atomic_ctx->primary_plane, "FB_ID", p->fb->id); - drm_object_set_property(atomic_ctx->request, atomic_ctx->primary_plane, "CRTC_ID", atomic_ctx->crtc->id); - drm_object_set_property(atomic_ctx->request, atomic_ctx->primary_plane, "ZPOS", 1); + drm_object_set_property(atomic_ctx->request, atomic_ctx->osd_plane, "FB_ID", p->fb->id); + drm_object_set_property(atomic_ctx->request, atomic_ctx->osd_plane, "CRTC_ID", atomic_ctx->crtc->id); + drm_object_set_property(atomic_ctx->request, atomic_ctx->osd_plane, "ZPOS", 1); ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request, DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, NULL); @@ -383,7 +485,7 @@ static void drm_egl_swap_buffers(struct ra_ctx *ctx) if (atomic_ctx) { drmModeAtomicFree(atomic_ctx->request); - p->drm_params.atomic_request = atomic_ctx->request = drmModeAtomicAlloc(); + atomic_ctx->request = drmModeAtomicAlloc(); } gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo); @@ -424,24 +526,24 @@ static void drm_egl_uninit(struct ra_ctx *ctx) } } -// If primary plane supports ARGB we want to use that, but if it doesn't we fall +// If the OSD plane supports ARGB we want to use that, but if it doesn't we fall // back on XRGB. If the driver does not support atomic there is no particular // reason to be using ARGB (drmprime hwdec will not work without atomic, // anyway), so we fall back to XRGB (another reason is that we do not have the -// convenient atomic_ctx and its convenient primary_plane field). +// convenient atomic_ctx and its convenient plane fields). static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t xrgb_format) { struct priv *p = ctx->priv; if (!p->kms->atomic_context) { p->gbm_format = xrgb_format; - MP_VERBOSE(ctx->vo, "Not using DRM Atomic: Use %s for primary plane.\n", + MP_VERBOSE(ctx->vo, "Not using DRM Atomic: Use %s for OSD plane.\n", gbm_format_to_string(xrgb_format)); return true; } drmModePlane *drmplane = - drmModeGetPlane(p->kms->fd, p->kms->atomic_context->primary_plane->id); + drmModeGetPlane(p->kms->fd, p->kms->atomic_context->osd_plane->id); bool have_argb = false; bool have_xrgb = false; bool result = false; @@ -455,11 +557,11 @@ static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t if (have_argb) { p->gbm_format = argb_format; - MP_VERBOSE(ctx->vo, "%s supported by primary plane.\n", gbm_format_to_string(argb_format)); + MP_VERBOSE(ctx->vo, "%s supported by OSD plane.\n", gbm_format_to_string(argb_format)); result = true; } else if (have_xrgb) { p->gbm_format = xrgb_format; - MP_VERBOSE(ctx->vo, "%s not supported by primary plane: Falling back to %s.\n", + MP_VERBOSE(ctx->vo, "%s not supported by OSD plane: Falling back to %s.\n", gbm_format_to_string(argb_format), gbm_format_to_string(xrgb_format)); result = true; } @@ -489,12 +591,27 @@ static bool drm_egl_init(struct ra_ctx *ctx) MP_VERBOSE(ctx, "Initializing KMS\n"); p->kms = kms_create(ctx->log, ctx->vo->opts->drm_opts->drm_connector_spec, ctx->vo->opts->drm_opts->drm_mode_id, - ctx->vo->opts->drm_opts->drm_overlay_id); + ctx->vo->opts->drm_opts->drm_osd_plane_id, + ctx->vo->opts->drm_opts->drm_video_plane_id); if (!p->kms) { MP_ERR(ctx, "Failed to create KMS.\n"); return false; } + if (ctx->vo->opts->drm_opts->drm_osd_size.wh_valid) { + if (p->kms->atomic_context) { + p->osd_size.width = ctx->vo->opts->drm_opts->drm_osd_size.w; + p->osd_size.height = ctx->vo->opts->drm_opts->drm_osd_size.h; + } else { + p->osd_size.width = p->kms->mode.hdisplay; + p->osd_size.height = p->kms->mode.vdisplay; + MP_WARN(ctx, "Setting OSD size is only available with DRM atomic, defaulting to screen resolution\n"); + } + } else { + p->osd_size.width = p->kms->mode.hdisplay; + p->osd_size.height = p->kms->mode.vdisplay; + } + uint32_t argb_format; uint32_t xrgb_format; if (DRM_OPTS_FORMAT_XRGB2101010 == ctx->vo->opts->drm_opts->drm_format) { @@ -551,8 +668,9 @@ static bool drm_egl_init(struct ra_ctx *ctx) 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; if (p->kms->atomic_context) - p->drm_params.atomic_request = p->kms->atomic_context->request; + p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request; struct ra_gl_ctx_params params = { .swap_buffers = drm_egl_swap_buffers, .external_swapchain = p->kms->atomic_context ? &drm_atomic_swapchain : @@ -561,7 +679,8 @@ static bool drm_egl_init(struct ra_ctx *ctx) if (!ra_gl_ctx_init(ctx, &p->gl, params)) return false; - ra_add_native_resource(ctx->ra, "opengl-cb-drm-params", &p->drm_params); + ra_add_native_resource(ctx->ra, "drm_params", &p->drm_params); + ra_add_native_resource(ctx->ra, "drm_osd_size", &p->osd_size); return true; } diff --git a/video/out/opengl/hwdec_drmprime_drm.c b/video/out/opengl/hwdec_drmprime_drm.c index 422612287ce72..24753067d6025 100644 --- a/video/out/opengl/hwdec_drmprime_drm.c +++ b/video/out/opengl/hwdec_drmprime_drm.c @@ -29,7 +29,7 @@ #include "video/hwdec.h" #include "common/msg.h" #include "options/m_config.h" -#include "libmpv/opengl_cb.h" +#include "libmpv/render_gl.h" #include "video/out/drm_common.h" #include "video/out/drm_prime.h" #include "video/out/gpu/hwdec.h" @@ -108,6 +108,30 @@ static void scale_dst_rect(struct ra_hwdec *hw, int source_w, int source_h ,stru dst->y1 += offset_y; } +static void disable_video_plane(struct ra_hwdec *hw) +{ + struct priv *p = hw->priv; + if (!p->ctx) + return; + + // Disabling video plane is needed on some devices when using the + // primary plane for video. Primary buffer can't be active with no + // framebuffer associated. So we need this function to commit it + // right away as mpv will free all framebuffers on playback end. + drmModeAtomicReqPtr request = drmModeAtomicAlloc(); + if (request) { + drm_object_set_property(request, p->ctx->video_plane, "FB_ID", 0); + drm_object_set_property(request, p->ctx->video_plane, "CRTC_ID", 0); + + int ret = drmModeAtomicCommit(p->ctx->fd, request, + DRM_MODE_ATOMIC_NONBLOCK, NULL); + + if (ret) + MP_ERR(hw, "Failed to commit disable plane request (code %d)", ret); + drmModeAtomicFree(request); + } +} + static int overlay_frame(struct ra_hwdec *hw, struct mp_image *hw_image, struct mp_rect *src, struct mp_rect *dst, bool newframe) { @@ -117,25 +141,34 @@ static int overlay_frame(struct ra_hwdec *hw, struct mp_image *hw_image, struct drm_frame next_frame = {0}; int ret; + // grab atomic request from native resources + if (p->ctx) { + struct mpv_opengl_drm_params *drm_params; + drm_params = (mpv_opengl_drm_params *)ra_get_native_resource(hw->ra, "drm_params"); + if (!drm_params) { + MP_ERR(hw, "Failed to retrieve drm params from native resources\n"); + return -1; + } + if (drm_params->atomic_request_ptr) { + request = *drm_params->atomic_request_ptr; + } else { + MP_ERR(hw, "drm params pointer to atomic request is invalid"); + return -1; + } + } + if (hw_image) { - // grab opengl-cb windowing info to eventually upscale the overlay - // as egl windows could be upscaled to primary plane. - struct mpv_opengl_cb_window_pos *glparams = - ra_get_native_resource(hw->ra, "opengl-cb-window-pos"); - if (glparams) { - scale_dst_rect(hw, glparams->width, glparams->height, dst, &p->dst); + // grab osd windowing info to eventually upscale the overlay + // as egl windows could be upscaled to osd plane. + struct mpv_opengl_drm_osd_size *osd_size = ra_get_native_resource(hw->ra, "drm_osd_size"); + if (osd_size) { + scale_dst_rect(hw, osd_size->width, osd_size->height, dst, &p->dst); } else { p->dst = *dst; } p->src = *src; - // grab drm interop info - struct mpv_opengl_cb_drm_params *drmparams = - ra_get_native_resource(hw->ra, "opengl-cb-drm-params"); - if (drmparams) - request = (drmModeAtomicReq *)drmparams->atomic_request; - next_frame.image = hw_image; desc = (AVDRMFrameDescriptor *)hw_image->planes[0]; @@ -151,29 +184,31 @@ static int overlay_frame(struct ra_hwdec *hw, struct mp_image *hw_image, } if (request) { - drm_object_set_property(request, p->ctx->overlay_plane, "FB_ID", next_frame.fb.fb_id); - drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_ID", p->ctx->crtc->id); - drm_object_set_property(request, p->ctx->overlay_plane, "SRC_X", p->src.x0 << 16); - drm_object_set_property(request, p->ctx->overlay_plane, "SRC_Y", p->src.y0 << 16); - drm_object_set_property(request, p->ctx->overlay_plane, "SRC_W", srcw << 16); - drm_object_set_property(request, p->ctx->overlay_plane, "SRC_H", srch << 16); - drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_X", MP_ALIGN_DOWN(p->dst.x0, 2)); - drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_Y", MP_ALIGN_DOWN(p->dst.y0, 2)); - drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_W", dstw); - drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_H", dsth); - drm_object_set_property(request, p->ctx->overlay_plane, "ZPOS", 0); + drm_object_set_property(request, p->ctx->video_plane, "FB_ID", next_frame.fb.fb_id); + drm_object_set_property(request, p->ctx->video_plane, "CRTC_ID", p->ctx->crtc->id); + drm_object_set_property(request, p->ctx->video_plane, "SRC_X", p->src.x0 << 16); + drm_object_set_property(request, p->ctx->video_plane, "SRC_Y", p->src.y0 << 16); + drm_object_set_property(request, p->ctx->video_plane, "SRC_W", srcw << 16); + drm_object_set_property(request, p->ctx->video_plane, "SRC_H", srch << 16); + drm_object_set_property(request, p->ctx->video_plane, "CRTC_X", MP_ALIGN_DOWN(p->dst.x0, 2)); + drm_object_set_property(request, p->ctx->video_plane, "CRTC_Y", MP_ALIGN_DOWN(p->dst.y0, 2)); + drm_object_set_property(request, p->ctx->video_plane, "CRTC_W", dstw); + drm_object_set_property(request, p->ctx->video_plane, "CRTC_H", dsth); + drm_object_set_property(request, p->ctx->video_plane, "ZPOS", 0); } else { - ret = drmModeSetPlane(p->ctx->fd, p->ctx->overlay_plane->id, p->ctx->crtc->id, next_frame.fb.fb_id, 0, + ret = drmModeSetPlane(p->ctx->fd, p->ctx->video_plane->id, p->ctx->crtc->id, next_frame.fb.fb_id, 0, MP_ALIGN_DOWN(p->dst.x0, 2), MP_ALIGN_DOWN(p->dst.y0, 2), dstw, dsth, p->src.x0 << 16, p->src.y0 << 16 , srcw << 16, srch << 16); if (ret < 0) { - MP_ERR(hw, "Failed to set the plane %d (buffer %d).\n", p->ctx->overlay_plane->id, + MP_ERR(hw, "Failed to set the plane %d (buffer %d).\n", p->ctx->video_plane->id, next_frame.fb.fb_id); goto fail; } } } } else { + disable_video_plane(hw); + while (p->old_frame.fb.fb_id) set_current_frame(hw, NULL); } @@ -190,6 +225,7 @@ static void uninit(struct ra_hwdec *hw) { struct priv *p = hw->priv; + disable_video_plane(hw); set_current_frame(hw, NULL); if (p->ctx) { @@ -201,25 +237,22 @@ static void uninit(struct ra_hwdec *hw) static int init(struct ra_hwdec *hw) { struct priv *p = hw->priv; - int drm_overlay; + int osd_plane_id, video_plane_id; p->log = hw->log; void *tmp = talloc_new(NULL); struct drm_opts *opts = mp_get_config_group(tmp, hw->global, &drm_conf); - drm_overlay = opts->drm_overlay_id; + osd_plane_id = opts->drm_osd_plane_id; + video_plane_id = opts->drm_video_plane_id; talloc_free(tmp); - struct mpv_opengl_cb_drm_params *params = - ra_get_native_resource(hw->ra, "opengl-cb-drm-params"); - if (!params) { - MP_VERBOSE(hw, "Could not get drm interop info.\n"); - goto err; - } + struct mpv_opengl_drm_params *drm_params; - if (params->fd) { - p->ctx = drm_atomic_create_context(p->log, params->fd, params->crtc_id, - drm_overlay); + drm_params = ra_get_native_resource(hw->ra, "drm_params"); + if (drm_params) { + p->ctx = drm_atomic_create_context(p->log, drm_params->fd, drm_params->crtc_id, + drm_params->connector_id, osd_plane_id, video_plane_id); if (!p->ctx) { mp_err(p->log, "Failed to retrieve DRM atomic context.\n"); goto err; @@ -237,13 +270,13 @@ static int init(struct ra_hwdec *hw) drmModeFreeCrtc(crtc); } - uint64_t has_prime; if (drmGetCap(p->ctx->fd, DRM_CAP_PRIME, &has_prime) < 0) { MP_ERR(hw, "Card does not support prime handles.\n"); goto err; } + disable_video_plane(hw); return 0; err: diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c index b25f03625635e..ed41d2aab0e9c 100644 --- a/video/out/vo_drm.c +++ b/video/out/vo_drm.c @@ -420,7 +420,8 @@ static int preinit(struct vo *vo) p->kms = kms_create( vo->log, vo->opts->drm_opts->drm_connector_spec, vo->opts->drm_opts->drm_mode_id, - vo->opts->drm_opts->drm_overlay_id); + vo->opts->drm_opts->drm_osd_plane_id, + vo->opts->drm_opts->drm_video_plane_id); if (!p->kms) { MP_ERR(vo, "Failed to create KMS.\n"); goto err;