mpv
mpv copied to clipboard
context_drm_gl: add support for hdr metadata
Somebody please actually test since I have no actual hdr capable monitors. There's also a HDMI_EOTF_TRADITIONAL_GAMMA_HDR enum in there but I have no idea what on earth that was supposed to be so I ignored it.
Also not sure if any existing --drm-format options are capable enough here.
Download the artifacts for this pull request:
I added some sanity checking to this by using libdisplay-info. Only bothered with static metadata for now. In theory if the display doesn't support what the target_params specify it should not attempt to use it and fallback to sdr mode.
Of course it's totally possible I did the checks wrong so testing appreciated.
Edit: And of course they are wrong.
I tested this with a HDR (DisplayHDR 600) capable monitor and the monitor confirmed it went into HDR mode. It seems to work fine. I used the following config:
vo=gpu-next
drm-connector=HDMI-A-1
drm-format=xrgb2101010
target-colorspace-hint
target-prim=dci-p3
target-trc=pq
target-peak=600
Off the top of my head, I still should add a check to make sure the monitor supports bt 2020 to this. I'm not sure if there's anything else that would be good to check.
For TVs to playback bt.2020 correctly, the Colorspace prop must be set if the connector supports it in the colorimetry data block (EDID).
Can probably be used to detect support, too.
For TVs to playback bt.2020 correctly, the Colorspace prop must be set if the connector supports it in the colorimetry data block (EDID).
Made an attempt at this. There's quite a few colorspaces but it seems that mutter and kwin both only care about bt2020_rgb so that is what I copied as well. Basically if we get an hdr one, set the colorspace property to bt2020_rgb. If not, set it back to the default one. Not sure if this is strictly correct of course, but there doesn't seem to be much prior art on this.
Hmm, with these new changes, my monitor does not claim to go into HDR mode anymore, although it has BT2020RGB in its colorimetry data block:
$ edid-decode /sys/class/drm/card0-HDMI-A-1/edid
[...]
Block 1, CTA-861 Extension Block:
[...]
Colorimetry Data Block:
xvYCC601
xvYCC709
BT2020cYCC
BT2020YCC
BT2020RGB
Gamut Boundary Description Metadata Profile P0
[...]
HDR Static Metadata Data Block:
Electro optical transfer functions:
Traditional gamma - SDR luminance range
Traditional gamma - HDR luminance range
SMPTE ST2084
Supported static metadata descriptors:
Static metadata type 1
Desired content max luminance: 118 (644.196 cd/m^2)
Desired content max frame-average luminance: 96 (400.000 cd/m^2)
Desired content min luminance: 23 (0.052 cd/m^2)
Does it work again if you comment out these two lines?
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index 938bdfe84c..25e8cd9661 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -694,8 +694,8 @@ static bool target_params_supported_by_display(struct vo_drm_state *drm)
if (!pl_color_transfer_is_hdr(trc) && !hdr_static_metadata->eotfs->traditional_sdr)
return false;
- if (pl_color_transfer_is_hdr(trc) && !drm->colorimetry->bt2020_rgb)
- return false;
+// if (pl_color_transfer_is_hdr(trc) && !drm->colorimetry->bt2020_rgb)
+// return false;
if (trc == PL_COLOR_TRC_PQ && !hdr_static_metadata->eotfs->pq)
return false;
I had the same thought, unfortunately no. The only new message in the debug log I get is
[vo/gpu-next] Failed to commit atomic request: Error number 22 occurred
which points to
drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "COLORSPACE", colorspace);
and indead commenting out setting the colorspace makes it work again
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -1384,7 +1384,7 @@ bool vo_drm_set_hdr_metadata(struct vo *vo)
return false;
int colorspace = pl_color_space_is_hdr(&drm->target_params.color) ? DRM_COLORSPACE_BT2020_RGB : DRM_COLORSPACE_DEFAULT;
- drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "COLORSPACE", colorspace);
+ //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 = {
Thanks. Does it work again now?
Ok, so the problem seems to be, that in the newest kernel the enum for DRM_MODE_COLORIMETRY_BT2020_RGB is different, then what was defined in your patch, it should be 9 (see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/drm/drm_connector.h?h=v6.12-rc4#n544) but in here, it evaluated to 6. Using 9 makes things work:
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -1384,7 +1384,7 @@ bool vo_drm_set_hdr_metadata(struct vo *vo)
return false;
int colorspace = pl_color_space_is_hdr(&drm->target_params.color) ? DRM_COLORSPACE_BT2020_RGB : DRM_COLORSPACE_DEFAULT;
- drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "COLORSPACE", DRM_COLORSPACE_BT2020_RGB);
+ drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "COLORSPACE", 9);
const struct pl_hdr_metadata *hdr = &target_params.color.hdr;
struct hdr_output_metadata metadata = {
Thanks. Does it work again now?
No, with these revised patches, it does only print the Error number 22 a lot of times more. Setting the colorspace in vo_drm_set_hdr_metadata works, as soon as the definition of DRM_COLORSPACE_BT2020_RGB matches the definition of DRM_MODE_COLORIMETRY_BT2020_RGB = 9 as in the drm_colorspace enum. in drm_connector.h. But at least for my machine it does not seem to be neccessary to set this property at all but the driver seems to figure out what to do by itself.
Yeah the current enum is not correct, DRM_MODE_COLORIMETRY_BT2020_RGB is 9.
On my side changing to 9 works fine.
Ah thanks for that. I naively assumed the order they were in the drm database was the actual order of the enums in the kernel. Hopefully fixed now.
I'm not exactly sure how gpu-next behaves wrt. smaller gamuts in bt.2020 when someone specifies target-prim=display-p3 or a smaller gamut than bt.2020.
But in almost all cases displays expect to receive bt.2020 so that's the only relevant colorspace to set.
Though there are displays that can receive straight P3 too, but would need more complex colorimetry+Colorspace setting.'
Anyways it works fine on my TV, with the colorimetry passing correctly. Driver sends out YCbCr 4:4:4. Just have a bit of audio delay but it's probably unrelated.
I can't check the HDR10 metadata but AFAIK there's some work necessary to adapt it for target-peak.
At least for me, I have to set target-prim=dci-p3 to get the colours right, when not doing it, bt.709 is used by default. Using target-prim=bt.2020 gives wrong colours for the display in use. So gpu-next seems to do the right thing with respect to the gamut.
This is what was set (see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c#n7410) for a device connected via HDMI:
kernel: HDR SB:22 02 00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 54
kernel: HDR SB:3d 8e 44 58 02 00 00 00 00 00 00 00 00 00 00 00
This is for one connected via eDP:
kernel: HDR SB:01 1a 02 00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b
kernel: HDR SB:54 3d 8e 44 58 02 00 00 00 00 00 00 00 00 00 00
On my TV, using target-prim=display-p3 is oversaturated. It only displays properly when sending bt.2020.
The option I was looking for was target-gamut (https://github.com/mpv-player/mpv/issues/12777) but it doesn't seem to make any difference here.
Overriding the colorimetry on the TV also makes no difference.
On my P3 monitor, bt.2020 is undersaturated so I have to use display-p3.
AFAIK most monitors don't really want to receive bt.2020.
So I suppose a future direction would be figuring out what target-prim is right for the connector and then adding someway to communicate that to vo_gpu_next. That can probably be another PR I think.
Could you guys post the output of this patch applied to the PR real quick on your devices? If we're lucky, this will be enough to detect this.
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index 76a93b2bd3..1b37ba23d9 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -678,6 +678,9 @@ static void setup_edid(struct vo_drm_state *drm)
}
}
+ if (drm->colorimetry)
+ MP_WARN(drm, "Connector supports bt2020_rgb: %d and st2113: %d\n", drm->colorimetry->bt2020_rgb, drm->colorimetry->st2113_rgb);
+
done:
if (blob)
drmModeFreePropertyBlob(blob);
Consumer displays supporting anything other than bt.2020 are pretty rare.
Neither of mine support it, just both BT2020YCC and BT2020RGB.
I just know TVs have logic to be able to handle bt.2020 because content is actually always in those primaries. So AFAIK when they receive HDR, it's expected to be 2020. Monitors don't really have to do that.
Hmm that's unfortunate, I was hoping something that needed display-p3 would mark that boolean. So I guess the only assumption we could do in this case is TVs need bt.2020 and monitors need display-p3? That seems pretty fragile. Maybe we just have to let the user set it themselves.
Just tried this.
It does enable HDR mode, but the rendering is broken.
mpv screenshot
physical
Also worthy of note, the mode resets correctly after exiting mpv with vo=gpu-next, but it doesn't with vo=gpu (the greenish mask stays, it's like broken alpha or something).
I could be missing something obvious here of course. I'm not sure.
Also worthy of note, the mode resets correctly after exiting mpv with vo=gpu-next, but it doesn't with vo=gpu (the greenish mask stays, it's like broken alpha or something).
That should hopefully be fixed now. @quietvoid or @juw: can either of you test if this still works correctly for you? It should but maybe I did something wrong while refactoring things.
That should hopefully be fixed now.
It is.
Still works fine here.
Just tried this.
It does enable HDR mode, but the rendering is broken.
Earlier test was with LTS kernel (6.6.58). Tested again with latest (6.11.5). Same issue. So, it's not a kernel issue.
Mesa info for completeness:
Device: AMD Radeon RX 570 Series (radeonsi, polaris10, LLVM 18.1.8, DRM 3.54, 6.6.58-1-lts)
Version: 24.2.5
I tried my test HDR files with the TV's own OS, and they all rendered correctly in HDR mode. So it's supposedly not an output device issue either.
Still works fine here.
For me it also still works fine.
PR looks good, but I think we should fix gpu-next metadata before merging it. Doesn't have to made bug changes, we can hack it for now, but I don't feel comfortable discarding all HDR metadata and advertise some dummy values.
Sorry I know you've already explained this a gazillion times but I'm totally lost on whatever needs to be done to gpu-next.
Sorry I know you've already explained this a gazillion times but I'm totally lost on whatever needs to be done to gpu-next.
No worries, it is my fault to not explain clearly. I also not sure myself what is the best way to resolve this. It's all about how target_params are populated by gpu_next.
https://github.com/mpv-player/mpv/blob/466b5530c9ca140ce70cd4d41295a987e32992e7/video/out/vo_gpu_next.c#L1133-L1141 The important part is that target here is the swapchain format
https://github.com/mpv-player/mpv/blob/466b5530c9ca140ce70cd4d41295a987e32992e7/video/out/vo_gpu_next.c#L1027-L1028 and HDR metadata is only present if libplacebo successfully set those HDR metadata on said swapchain.
https://github.com/haasn/libplacebo/blob/118d8106640796d3f2ceb55f8634a32a58a47aa2/src/vulkan/swapchain.c#L314-L315
This value is used to initialize swapchain frame properites and later used by mpv as target_params, because this is what is real target.
Now in case of DRM / EGL there is no colorspace_hint implemented and libplacebo/gpu_next will always default to sRGB, stripping all HDR metadata in the process. Even if you set --target-trc the metadata is already lost and instead the default one of 10000 nits is used after apply_target_options.
In short gpu_next has to know that the swapchain can be configured to use 10bit surface and that we suppose to render HDR to it. While colorspace_hint will be done "externally".
You can test that easily with any HDR video that whatever you do, target_params will not contain correct metadata. Which is nominal case should be video metadata passthrough to display unless user sets target-peak to override peak values to better match display capabilities.
As to how to fix that, I'm not sure, some things like creating swapchain is on libplacebo side, but if we can make it create high bitdepth swapchain it is good enough for us. But I didn't look at the details, how to connect things together.
and HDR metadata is only present if libplacebo successfully set those HDR metadata on said swapchain.
Is there a way for mpv to know whether this is set successfully or not? IMO what gpu-next should do here is to first try to signal colorspace hint first through libplacebo, and if libplacebo can't do it, use the GPU context/RA's implementation, which will be a new member function of RA API to signal colorspace hint.