vo_gpu_next: add --target-colorspace-hint-mode option
Useful to actually set target display paramters to colorspace hint.
This commit is contained in:
1 parent
5b1df3c5d9
commit
ce076de31f
3 files changed
+117
-32
No files matched your search
@@ -0,0 +1 @@
|
||||
add `--target-colorspace-hint-mode` option
|
||||
+82
-2
@@ -7023,6 +7023,84 @@ them.
|
||||
if the display signals support for HDR colorspace.
|
||||
Requires a supporting driver and ``--vo=gpu-next``. (Default: ``no``)
|
||||
|
||||
``--target-colorspace-hint-mode=<target|source>``
|
||||
Select which metadata to use for the ``--target-colorspace-hint``.
|
||||
(Only for ``--vo=gpu-next``)
|
||||
|
||||
target
|
||||
Uses metadata based on the target display's actual capabilities. This
|
||||
mode adapts the source content to the target display before output.
|
||||
Note: HDR primaries are not overridden by the ``--target-prim`` option
|
||||
this only affects the enclosing container for the colorspace.
|
||||
|
||||
source
|
||||
Uses the source content's metadata. This is the traditional
|
||||
"HDR passthrough" mode (SDR too), where it is assumed that the compositor
|
||||
and display will handle the colorspace directly and perform any necessary
|
||||
mappings.
|
||||
|
||||
Default is ``target``. If target display parameters are not available, this
|
||||
will fall back to ``source``. Note that this is done on individual properties
|
||||
basis, i.e. it will merge source params into target for unknown properties,
|
||||
though not the other way around.
|
||||
|
||||
``--target-*`` options override the metadata in both modes.
|
||||
|
||||
.. note::
|
||||
It is highly recommended to use ``--target-colorspace-hint=<auto|yes>``
|
||||
to ensure the output colorspace is set correctly. This is crucial for
|
||||
all non-sRGB content, even SDR, to allow the compositor, driver, and
|
||||
display to properly interpret the signal.
|
||||
|
||||
Unfortunately, it's not as easy as it sounds. While mpv performs
|
||||
high-quality color processing, we cannot guarantee what will happen
|
||||
after the signal leaves mpv. Therefore, you may need to adjust additional
|
||||
settings to ensure proper output. A one-size-fits-all default is not
|
||||
feasible.
|
||||
|
||||
Now with the backstory out of the way:
|
||||
|
||||
For HDR output the default of ``target`` should work fine, it will
|
||||
automatically infer the best target HDR parameters and surface format.
|
||||
For compatibility, displays are assumed to be in HDR mode, unless it's
|
||||
reported otherwise. (you can override this with ``--target-trc``).
|
||||
This way the HDR metadata is set and hopefully the compositor will
|
||||
handle the rest. If the input is SDR, it will be converted to PQ with
|
||||
primaries set to source values.
|
||||
|
||||
For SDR output, for targets where mpv cannot determine whether the target
|
||||
is HDR or SDR, you can use ``source`` mode. Metadata set will match the
|
||||
input colorspace. In case of HDR input, it will be passthrough as-is.
|
||||
Alternatively, you can use ``target`` mode and set ``--target-trc`` to
|
||||
a SDR transfer function. This way any input will be converted to SDR.
|
||||
|
||||
Use the stats display to verify the input and output colorspace settings.
|
||||
|
||||
TL;DR: Use ``--target-colorspace-hint=auto`` and adjust ``--target-*``
|
||||
parameters to match your target display capabilities, until it looks best
|
||||
for you. Use `Conditional auto profiles`_ for specific adjustments. Avoid
|
||||
using ``--target-colorspace-hint=no`` unless it's sRGB content, but even
|
||||
then it's better to set the colorspace metadata.
|
||||
|
||||
.. note::
|
||||
Additional chatter about the "HDR passthrough" mode: There is a belief
|
||||
that this mode should send the source HDR signal as-is to the display,
|
||||
and the display will magically handle it, no matter what. This is not
|
||||
always true. In some cases it is better to send the HDR signal
|
||||
tone-mapped to the target display's capabilities, and the best way to do
|
||||
this is within mpv itself.
|
||||
|
||||
This is generally handled by either the compositor or the GPU driver.
|
||||
You can probably find HDR "calibration" options somewhere in your system.
|
||||
|
||||
You can choose which metadata to send to the display and manually tweak
|
||||
it using the ``--target-*`` options. You can also try
|
||||
``--inverse-tone-mapping`` if you want to make everything appear more
|
||||
HDR-like.
|
||||
|
||||
Your mileage may vary, this highly depends on the target display, there
|
||||
is no single answer, but try experimenting, you may be surprised.
|
||||
|
||||
``--target-prim=<value>``
|
||||
Specifies the primaries of the display. Video colors will be adapted to
|
||||
this colorspace when ICC color management is not being used. Valid values
|
||||
@@ -7252,8 +7330,10 @@ them.
|
||||
Specifies the contrast (slope) at the knee point. Defaults to 1.0.
|
||||
|
||||
``--inverse-tone-mapping``
|
||||
If set, allows inverse tone mapping (expanding SDR to HDR). Not supported
|
||||
by all tone mapping curves. Use with caution. (``--vo=gpu-next`` only)
|
||||
If set, allows inverse tone mapping (expanding dynamic range). Can be used
|
||||
for upscaling SDR content to HDR, or for making HDR content brighter.
|
||||
Not supported by all tone mapping curves. Use with caution.
|
||||
(``--vo=gpu-next`` only)
|
||||
|
||||
``--tone-mapping-max-boost=<1.0..10.0>``
|
||||
Upper limit for how much the tone mapping algorithm is allowed to boost
|
||||
|
||||
+34
-30
@@ -174,6 +174,7 @@ struct gl_next_opts {
|
||||
struct user_lut image_lut;
|
||||
struct user_lut target_lut;
|
||||
int target_hint;
|
||||
int target_hint_mode;
|
||||
char **raw_opts;
|
||||
};
|
||||
|
||||
@@ -205,6 +206,7 @@ const struct m_sub_options gl_next_conf = {
|
||||
{"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
|
||||
{"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
|
||||
{"target-colorspace-hint", OPT_CHOICE(target_hint, {"auto", -1}, {"no", 0}, {"yes", 1})},
|
||||
{"target-colorspace-hint-mode", OPT_CHOICE(target_hint_mode, {"target", 0}, {"source", 1})},
|
||||
// No `target-lut-type` because we don't support non-RGB targets
|
||||
{"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
|
||||
{0},
|
||||
@@ -1003,12 +1005,15 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
|
||||
struct ra_swapchain *sw = p->ra_ctx->swapchain;
|
||||
|
||||
bool pass_colorspace = false;
|
||||
struct pl_color_space target_csp;
|
||||
// Assume HDR is supported, if query is not available
|
||||
struct pl_color_space target_csp = {0};
|
||||
// TODO: Implement this for all backends
|
||||
target_csp = sw->fns->target_csp
|
||||
? sw->fns->target_csp(sw)
|
||||
: (struct pl_color_space){ .transfer = PL_COLOR_TRC_PQ };
|
||||
if (sw->fns->target_csp)
|
||||
target_csp = sw->fns->target_csp(sw);
|
||||
// Assume HDR is supported, if query is not available
|
||||
if (target_csp.transfer == PL_COLOR_TRC_UNKNOWN) {
|
||||
target_csp = (struct pl_color_space){
|
||||
.transfer = opts->target_trc ? opts->target_trc : PL_COLOR_TRC_PQ };
|
||||
}
|
||||
if (!pl_color_transfer_is_hdr(target_csp.transfer)) {
|
||||
// Don't use reported display peak in SDR mode. Mostly because libplacebo
|
||||
// forcefully switches to PQ if hinting hdr metadata, ignoring the transfer
|
||||
@@ -1016,6 +1021,8 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
|
||||
// very specific usecase, needs proper calibration, users can set it manually.
|
||||
target_csp.hdr.max_luma = 0;
|
||||
target_csp.hdr.min_luma = 0;
|
||||
target_csp.hdr.max_cll = 0;
|
||||
target_csp.hdr.max_fall = 0;
|
||||
}
|
||||
|
||||
struct pl_color_space hint;
|
||||
@@ -1023,36 +1030,33 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
|
||||
(p->next_opts->target_hint == -1 &&
|
||||
pl_color_transfer_is_hdr(target_csp.transfer));
|
||||
if (target_hint && frame->current) {
|
||||
hint = frame->current->params.color;
|
||||
float target_peak = opts->target_peak ? opts->target_peak : target_csp.hdr.max_luma;
|
||||
float target_trc = opts->target_trc;
|
||||
if (!target_trc) {
|
||||
// If we are targeting HDR display, repack SDR to target trc. This way
|
||||
// we ensure that conversion is done by mpv and not by the compositor,
|
||||
// which is often worse in terms of quality. Do this only if we are sure
|
||||
// that display is in HDR mode, i.e. target_csp() reported HDR transfer.
|
||||
// libplacebo already does the same when peak > 203, but we want this
|
||||
// for any peak value.
|
||||
// Note: We don't have information if the compositor/driver is able to
|
||||
// reconfigure the display, currently target_csp() is only implemented
|
||||
// for D3D11 where we know automatic reconfiguration never happens.
|
||||
// Also I like to minimize possible reconfigurations, and outputting SDR
|
||||
// in PQ is fine.
|
||||
if (pl_color_transfer_is_hdr(target_csp.transfer) && target_csp.hdr.max_luma > 0 &&
|
||||
!pl_color_transfer_is_hdr(frame->current->params.color.transfer))
|
||||
{
|
||||
target_trc = target_trc ? target_trc : target_csp.transfer;
|
||||
}
|
||||
const struct pl_color_space *source = &frame->current->params.color;
|
||||
const struct pl_color_space *target = &target_csp;
|
||||
hint = *(p->next_opts->target_hint_mode == 0 ? target : source);
|
||||
if (pl_color_transfer_is_hdr(hint.transfer) && !pl_primaries_valid(&hint.hdr.prim))
|
||||
pl_color_space_merge(&hint, source);
|
||||
// Infer missing bits now. This is important so that we don't lose
|
||||
// information after user option overrides. For example, if the user
|
||||
// sets target_trc to PQ, but the hint(source) is SDR, we want to fill
|
||||
// in SDR luminance values instead of the default PQ range.
|
||||
pl_color_space_infer(&hint);
|
||||
// Always prefer target luminance and transfer for inverse tone mapping
|
||||
if (pl_color_transfer_is_hdr(target->transfer) && opts->tone_map.inverse) {
|
||||
hint.transfer = target->transfer;
|
||||
hint.hdr.max_luma = target->hdr.max_luma;
|
||||
hint.hdr.min_luma = target->hdr.min_luma;
|
||||
hint.hdr.max_cll = target->hdr.max_cll;
|
||||
hint.hdr.max_fall = target->hdr.max_fall;
|
||||
}
|
||||
if (p->ra_ctx->fns->pass_colorspace && p->ra_ctx->fns->pass_colorspace(p->ra_ctx))
|
||||
pass_colorspace = true;
|
||||
if (opts->target_prim)
|
||||
hint.primaries = opts->target_prim;
|
||||
if (target_trc)
|
||||
hint.transfer = target_trc;
|
||||
if (target_peak)
|
||||
hint.hdr.max_luma = target_peak;
|
||||
apply_target_contrast(p, &hint, target_csp.hdr.min_luma);
|
||||
if (opts->target_trc)
|
||||
hint.transfer = opts->target_trc;
|
||||
if (opts->target_peak)
|
||||
hint.hdr.max_luma = opts->target_peak;
|
||||
apply_target_contrast(p, &hint, hint.hdr.min_luma);
|
||||
if (!pass_colorspace)
|
||||
pl_swapchain_colorspace_hint(p->sw, &hint);
|
||||
} else if (!target_hint) {
|
||||
|
||||
Reference in new issue
Block a user