context_drm_gl: add support for hdr metadata

It seems what we can do on this level is a bit limited but it's better
than nothing. Closes #8219.
This commit is contained in:
Dudemanguy 2024-10-15 16:45:18 -05:00
parent a61518db98
commit 2827c9f323
4 changed files with 134 additions and 15 deletions

View File

@ -299,6 +299,7 @@ void drm_atomic_destroy_context(struct drm_atomic_context *ctx)
drm_object_free(ctx->connector);
drm_object_free(ctx->draw_plane);
drm_object_free(ctx->drmprime_video_plane);
drmModeAtomicFree(ctx->request);
talloc_free(ctx);
}

View File

@ -59,6 +59,9 @@
#define ACQUIRE_SIGNAL SIGUSR2
#define MAX_CONNECTOR_NAME_LEN 20
#define DRM_PRIM_FACTOR 50000
#define DRM_MIN_LUMA_FACTOR 10000
static int vt_switcher_pipe[2];
static int drm_connector_opt_help(struct mp_log *log, const struct m_option *opt,
@ -135,6 +138,12 @@ static const char *connector_names[] = {
"USB", // DRM_MODE_CONNECTOR_USB
};
static int eotf_map[PL_COLOR_TRC_COUNT] = {
[PL_COLOR_TRC_BT_1886] = HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
[PL_COLOR_TRC_PQ] = HDMI_EOTF_SMPTE_ST2084,
[PL_COLOR_TRC_HLG] = HDMI_EOTF_BT_2100_HLG,
};
struct drm_mode_spec {
enum {
DRM_MODE_SPEC_BY_IDX, // Specified by idx
@ -443,6 +452,14 @@ void vo_drm_release_crtc(struct vo_drm_state *drm)
}
/* libdrm */
static void destroy_hdr_blob(struct vo_drm_state *drm)
{
if (drm->hdr.blob_id) {
drmModeDestroyPropertyBlob(drm->fd, drm->hdr.blob_id);
drm->hdr.blob_id = 0;
}
}
static void get_connector_name(const drmModeConnector *connector,
char ret[MAX_CONNECTOR_NAME_LEN])
{
@ -496,6 +513,18 @@ static drmModeConnector *get_first_connected_connector(const drmModeRes *res,
return NULL;
}
static void restore_sdr(struct vo_drm_state *drm)
{
struct drm_atomic_context *atomic_ctx = drm->atomic_context;
if (!atomic_ctx)
return;
vo_drm_set_hdr_metadata(drm->vo, true);
int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request, DRM_MODE_ATOMIC_ALLOW_MODESET, drm);
if (ret)
MP_VERBOSE(drm, "Failed to commit atomic request: %s\n", mp_strerror(ret));
}
static bool setup_connector(struct vo_drm_state *drm, const drmModeRes *res,
const char *connector_name)
{
@ -1047,6 +1076,9 @@ void vo_drm_uninit(struct vo *vo)
if (!drm)
return;
restore_sdr(drm);
destroy_hdr_blob(drm);
vo_drm_release_crtc(drm);
if (drm->vt_switcher_active)
vt_switcher_destroy(&drm->vt_switcher);
@ -1061,9 +1093,8 @@ void vo_drm_uninit(struct vo *vo)
drmModeFreeEncoder(drm->encoder);
drm->encoder = NULL;
}
if (drm->atomic_context) {
if (drm->atomic_context)
drm_atomic_destroy_context(drm->atomic_context);
}
close(drm->fd);
talloc_free(drm);
@ -1242,6 +1273,51 @@ double vo_drm_get_display_fps(struct vo_drm_state *drm)
return mode_get_Hz(&drm->mode.mode);
}
bool vo_drm_set_hdr_metadata(struct vo *vo, bool force_sdr)
{
struct vo_drm_state *drm = vo->drm;
struct mp_image_params target_params = vo_get_target_params(vo);
if (!force_sdr && (pl_color_space_equal(&target_params.color, &drm->target_params.color) ||
!target_params.w || !target_params.h))
return false;
destroy_hdr_blob(drm);
drm->target_params = target_params;
// For any HDR, the BT2020 drm colorspace is the only one that works in practice.
struct drm_atomic_context *atomic_ctx = drm->atomic_context;
int colorspace = !force_sdr && pl_color_space_is_hdr(&drm->target_params.color) ?
DRM_MODE_COLORIMETRY_BT2020_RGB : DRM_MODE_COLORIMETRY_DEFAULT;
drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "Colorspace", colorspace);
const struct pl_hdr_metadata *hdr = &target_params.color.hdr;
struct hdr_output_metadata metadata = {
.metadata_type = HDMI_STATIC_METADATA_TYPE1,
.hdmi_metadata_type1.metadata_type = HDMI_STATIC_METADATA_TYPE1,
.hdmi_metadata_type1.eotf = force_sdr ? HDMI_EOTF_TRADITIONAL_GAMMA_SDR : eotf_map[target_params.color.transfer],
.hdmi_metadata_type1.display_primaries[0].x = lrintf(hdr->prim.red.x * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.display_primaries[0].y = lrintf(hdr->prim.red.y * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.display_primaries[1].x = lrintf(hdr->prim.green.x * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.display_primaries[1].y = lrintf(hdr->prim.green.y * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.display_primaries[2].x = lrintf(hdr->prim.blue.x * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.display_primaries[2].y = lrintf(hdr->prim.blue.y * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.white_point.x = lrintf(hdr->prim.white.x * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.white_point.y = lrintf(hdr->prim.white.y * DRM_PRIM_FACTOR),
.hdmi_metadata_type1.min_display_mastering_luminance = lrintf(hdr->min_luma * DRM_MIN_LUMA_FACTOR),
.hdmi_metadata_type1.max_display_mastering_luminance = hdr->max_luma,
.hdmi_metadata_type1.max_cll = hdr->max_cll,
.hdmi_metadata_type1.max_fall = hdr->max_fall,
};
drmModeCreatePropertyBlob(drm->fd, &metadata, sizeof(metadata), &drm->hdr.blob_id);
drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "HDR_OUTPUT_METADATA", drm->hdr.blob_id);
return true;
}
void vo_drm_set_monitor_par(struct vo *vo)
{
struct vo_drm_state *drm = vo->drm;

View File

@ -21,6 +21,8 @@
#include <stdbool.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "video/mp_image.h"
#include "vo.h"
enum {
@ -31,6 +33,46 @@ enum {
DRM_OPTS_FORMAT_YUYV,
};
// Enum values based on include/linux/hdmi.h
// and https://docs.kernel.org/gpu/drm-uapi.html
// for interoperability with drm API.
enum drm_metadata_type {
HDMI_STATIC_METADATA_TYPE1 = 0,
};
// Enum values based on https://docs.kernel.org/gpu/drm-kms.html
// for interoperability with drm API.
enum drm_colorspace {
DRM_MODE_COLORIMETRY_DEFAULT = 0,
DRM_MODE_COLORIMETRY_NO_DATA = 0,
DRM_MODE_COLORIMETRY_SMPTE_170M_YCC = 1,
DRM_MODE_COLORIMETRY_BT709_YCC = 2,
DRM_MODE_COLORIMETRY_XVYCC_601 = 3,
DRM_MODE_COLORIMETRY_XVYCC_709 = 4,
DRM_MODE_COLORIMETRY_SYCC_601 = 5,
DRM_MODE_COLORIMETRY_OPYCC_601 = 6,
DRM_MODE_COLORIMETRY_OPRGB = 7,
DRM_MODE_COLORIMETRY_BT2020_CYCC = 8,
DRM_MODE_COLORIMETRY_BT2020_RGB = 9,
DRM_MODE_COLORIMETRY_BT2020_YCC = 10,
DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65 = 11,
DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER = 12,
DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED = 13,
DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT = 14,
DRM_MODE_COLORIMETRY_BT601_YCC = 15,
DRM_MODE_COLORIMETRY_COUNT,
};
// Enum values based on include/linux/hdmi.h
// and https://docs.kernel.org/gpu/drm-uapi.html
// for interoperability with drm API.
enum drm_eotf {
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
HDMI_EOTF_TRADITIONAL_GAMMA_HDR,
HDMI_EOTF_SMPTE_ST2084,
HDMI_EOTF_BT_2100_HLG,
};
struct framebuffer {
int fd;
uint32_t width;
@ -42,6 +84,11 @@ struct framebuffer {
uint32_t id;
};
struct drm_hdr {
struct hdr_output_metadata metadata;
uint32_t blob_id;
};
struct drm_mode {
drmModeModeInfo mode;
uint32_t blob_id;
@ -72,9 +119,11 @@ struct vo_drm_state {
drmEventContext ev;
struct drm_atomic_context *atomic_context;
struct drm_hdr hdr;
struct drm_mode mode;
struct drm_opts *opts;
struct framebuffer *fb;
struct mp_image_params target_params;
struct mp_log *log;
struct mp_present *present;
struct vo *vo;
@ -99,6 +148,7 @@ bool vo_drm_init(struct vo *vo);
int vo_drm_control(struct vo *vo, int *events, int request, void *arg);
double vo_drm_get_display_fps(struct vo_drm_state *drm);
bool vo_drm_set_hdr_metadata(struct vo *vo, bool force_sdr);
void vo_drm_set_monitor_par(struct vo *vo);
void vo_drm_uninit(struct vo *vo);
void vo_drm_wait_events(struct vo *vo, int64_t until_time_ns);

View File

@ -319,12 +319,15 @@ static void queue_flip(struct ra_ctx *ctx, struct gbm_frame *frame)
update_framebuffer_from_bo(ctx, frame->bo);
struct drm_atomic_context *atomic_ctx = drm->atomic_context;
int flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", drm->fb->id);
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "CRTC_ID", atomic_ctx->crtc->id);
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "ZPOS", 1);
int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request,
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, drm);
if (vo_drm_set_hdr_metadata(ctx->vo, false))
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request, flags, drm);
if (ret)
MP_WARN(ctx->vo, "Failed to commit atomic request: %s\n", mp_strerror(ret));
@ -457,17 +460,6 @@ static const struct ra_swapchain_fns drm_egl_swapchain = {
static void drm_egl_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
struct vo_drm_state *drm = ctx->vo->drm;
if (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));
drmModeAtomicFree(atomic_ctx->request);
}
ra_gl_ctx_uninit(ctx);
vo_drm_uninit(ctx->vo);