-
Notifications
You must be signed in to change notification settings - Fork 3.2k
wayland: luminance improvements #17070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2146,13 +2146,32 @@ static void info_done(void *data, struct wp_image_description_info_v1 *image_des | |
| struct vo_wayland_state *wl = wd->wl; | ||
| wp_image_description_info_v1_destroy(image_description_info); | ||
| if (!wd->icc_file) { | ||
| wl->preferred_csp = wd->csp; | ||
| MP_VERBOSE(wl, "Preferred surface feedback received:\n"); | ||
| log_color_space(wl->log, wd); | ||
| if (wd->csp.hdr.max_luma > wd->ref_luma) { | ||
| MP_VERBOSE(wl, "Setting preferred transfer to PQ for HDR output.\n"); | ||
| wl->preferred_csp.transfer = PL_COLOR_TRC_PQ; | ||
| // Wayland luminances are always in reference to the reference luminance. That is, | ||
| // if max_luma == 2*ref_luma, then there is 2x headroom above paper white. On the | ||
| // other hand, libplacebo hardcodes PL_COLOR_SDR_WHITE as the reference luminance. | ||
| // We must scale all wayland values to correspond to the libplacebo scale, | ||
| // otherwise libplacebo will assume that there is too little or too much headroom | ||
| // when ref_luma != PL_COLOR_SDR_WHITE. | ||
| float a = wd->min_luma; | ||
| float b = (PL_COLOR_SDR_WHITE - a) / (wd->ref_luma - a); | ||
| wd->csp.hdr.min_luma = (wd->csp.hdr.min_luma - a) * b + a; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This value can realistically become negative and not all components might handle this. So this should probably be wrapped in MPMAX.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PS: |
||
| wd->csp.hdr.max_luma = (wd->csp.hdr.max_luma - a) * b + a; | ||
| if (wd->csp.hdr.max_cll != 0) | ||
| wd->csp.hdr.max_cll = (wd->csp.hdr.max_cll - a) * b + a; | ||
| if (wd->csp.hdr.max_fall != 0) | ||
| wd->csp.hdr.max_fall = (wd->csp.hdr.max_fall - a) * b + a; | ||
| // Since we want to do some exact comparisons of max_luma with PL_COLOR_SDR_WHITE, | ||
| // we need to round it. | ||
| if (fabsf(wd->csp.hdr.max_luma - PL_COLOR_SDR_WHITE) < 1e-2f) { | ||
| wd->csp.hdr.max_luma = PL_COLOR_SDR_WHITE; | ||
| if (wd->csp.hdr.max_cll != 0) | ||
| wd->csp.hdr.max_cll = MPMIN(wd->csp.hdr.max_cll, wd->csp.hdr.max_luma); | ||
| if (wd->csp.hdr.max_fall != 0) | ||
| wd->csp.hdr.max_fall = MPMIN(wd->csp.hdr.max_fall, wd->csp.hdr.max_luma); | ||
| } | ||
| wl->preferred_csp = wd->csp; | ||
| } else { | ||
| if (wl->icc_size) { | ||
| munmap(wl->icc_file, wl->icc_size); | ||
|
|
@@ -3959,10 +3978,59 @@ bool vo_wayland_check_visible(struct vo *vo) | |
| return render; | ||
| } | ||
|
|
||
| struct pl_color_space vo_wayland_preferred_csp(struct vo *vo) | ||
| struct pl_color_space vo_wayland_preferred_csp(struct vo *vo, float source_max_luma) | ||
| { | ||
| struct vo_wayland_state *wl = vo->wl; | ||
| return wl->preferred_csp; | ||
| struct pl_color_space csp = wl->preferred_csp; | ||
| if (!pl_color_transfer_is_hdr(csp.transfer)) { | ||
| // For transfer functions for which pl_color_transfer_is_hdr returns false, mpv | ||
| // discards the HDR metadata and assumes a max_luma of PL_COLOR_SDR_WHITE. | ||
| // Similarly, mesa discards the HDR metadata for such transfer functions. | ||
| // Therefore, if there is any reason for us to preserve the HDR metadata, we need | ||
| // to switch to a transfer function for which pl_color_transfer_is_hdr returns | ||
| // true. PL_COLOR_TRC_PQ is the most widely supported transfer function of that | ||
| // kind. If VK_COLOR_SPACE_HDR10_ST2084_EXT is not available, then libplacebo will | ||
| // still try to fall back to another HDR transfer function so that mesa passes the | ||
| // metadata on to the compositor. (Therefore, using any HDR transfer function here | ||
| // would do since we cannot actually know which transfer function the compositor | ||
| // prefers.) | ||
|
|
||
| // The default assumption by all components in the pipeline is that the maximum | ||
| // luminance for non-HDR transfer functions is paper white. Therefore, we don't | ||
| // have to handle this case. | ||
| if (csp.hdr.max_luma != PL_COLOR_SDR_WHITE) { | ||
| // Otherwise, if we are doing inverse tone mapping, which is indicated by | ||
| // source_max_luma == INFINITY, then libplacebo always needs to know the exact | ||
| // max_luma of the display since that is the target we want to hit. | ||
| if (source_max_luma == INFINITY) | ||
| csp.transfer = PL_COLOR_TRC_PQ; | ||
| // Otherwise, if the max_luma of the source is brighter than paper white, then | ||
| // libplacebo will always perform tone mapping. To do this correctly, it will | ||
| // need to know the exact max_luma of the display. | ||
| if (source_max_luma > PL_COLOR_SDR_WHITE) | ||
| csp.transfer = PL_COLOR_TRC_PQ; | ||
| // Otherwise, if max_luma of the display is less than paper white, there are | ||
| // two cases: | ||
| // 1. source_max_luma > csp.hdr.max_luma: In this case, we want libplacebo | ||
| // to tone map to the max_luma of the display. | ||
| // 2. source_max_luma <= csp.hdr.max_luma: In this case, we still need to | ||
| // tell the compositor that the max_luma of our images is | ||
| // < csp.hdr.max_luma since otherwise it will assume that the max_luma of | ||
| // or images is paper white and might do its own tone mapping down to | ||
| // csp.hdr.max_luma. | ||
| // So in both cases we need to pass the actual max_luma of the display through | ||
| // the pipeline. | ||
| if (csp.hdr.max_luma < PL_COLOR_SDR_WHITE) | ||
| csp.transfer = PL_COLOR_TRC_PQ; | ||
|
|
||
| // NOTE: If source_max_luma > csp.hdr.max_luma, which seems to be missing from | ||
| // the conditions above, then either source_max_luma > PL_COLOR_SDR_WHITE, in | ||
| // which case we hit the second branch above, or | ||
| // csp.hdr.max_luma < source_max_luma <= PL_COLOR_WHITE in which case we hit | ||
| // the third branch above. | ||
| } | ||
| } | ||
| return csp; | ||
| } | ||
|
|
||
| int vo_wayland_control(struct vo *vo, int *events, int request, void *arg) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value is base HDR10 value. And won't be in fact used by libplacebo if HDR10+/DV metadata is also available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do I get the correct value here?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something like this, but I'm still reluctant of using those values to infer any target parameters. Depending on the source this may or may not be valid/useful value. It may be transfer nominal value, which we have to avoid for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point libplacebo has to choose some value to determine if and how it needs to do tone mapping. That is the value I'm interested in. It is also ok if this value is too large since in that case the worst that should happen is that we de-optimize by using PQ instead of the preferred SDR transfer function.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code below again, we don't actually need the max_luma of the source. We only need to know if libplacebo is going to do tone mapping when targeting an SDR display, because that's the case in which it will need accurate metadata from the target.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the first commit it would have to be
See below for why not just
>.If the reference luminance is higher than the max target luminance, then the first commit will cause
wd->csp.hdr.max_luma < PL_COLOR_SDR_WHITE. In this case I don't understand why you don't want to tone-map to max_luma. This can obviously only happen in weird situations like when the user sets his brightness higher than 100% but it is easy to handle. Does libplacebo not handle tone mapping to below PL_COLOR_SDR_WHITE correctly?The first commit ensures that in that case
wd->csp.hdr.max_luma == PL_COLOR_SDR_WHITE.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason that I don't want to use this coarse hammer is that it means that mpv will always ignore the preferred transfer function when the user is using an HDR display even if mpv is playing an SDR video.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, if that is a piece of contention, then I can drop the second commit in favor of what you've suggested. The user-facing improvement is in the first commit.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually expected behavior. I prefer PQ output on HDR targets, even for SDR video. You can change it by using
target-mode=source.This is how
=targetmode works on Windows. Wheretarget_cspis on the wire format, hence whenever HDR mode is enabled, we output PQ. And the conversion to PQ is done inside mpv.I know Wayland is much smarter than that, and uses preferred transfer, which most likely is not related to the actual display, but if preferred transfer function is not compatible with mpv definition of HDR, I would prefer to keep it simple and avoid doing "complex" heuristics and fallback to PQ.
Feel free to correct my thinking, I don't have strong opinion how this should work. I just feel like the
target_cspshould not depend onsource. Well thehintcan depend on the source, so maybe I'm wrong here.My thinking is, if Wayland preferred function is "HDR-like" gamma2.2, then we should respect the HDR of it. I understand that in some cases, the definition may be compatible with mpv's SDR output and we can use gamma2.2 in this case, but this is slightly messy with all the heuristics. But I still don't see why it should depend on
sourceitself.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not wrong. The problem is
pl_color_transfer_is_hdrand the way it is used. As others have already pointed out, there are no HDR transfer functions as far as wayland is concerned. Ideally, all of the uses of that function should be removed from both mpv and libplacebo and the transfer function of the target hint should only be taken into account as late as possible: Once libplacebo knows everything there is to know about the image it is going to render, it decides if the preferred TF is suitable for that and otherwise switches to a different one.Getting there would be a much larger change and my thinking was that going the other way and passing the source into the windowing backend would get us roughly the same result with much less effort.