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.
|
if the display signals support for HDR colorspace.
|
||||||
Requires a supporting driver and ``--vo=gpu-next``. (Default: ``no``)
|
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>``
|
``--target-prim=<value>``
|
||||||
Specifies the primaries of the display. Video colors will be adapted to
|
Specifies the primaries of the display. Video colors will be adapted to
|
||||||
this colorspace when ICC color management is not being used. Valid values
|
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.
|
Specifies the contrast (slope) at the knee point. Defaults to 1.0.
|
||||||
|
|
||||||
``--inverse-tone-mapping``
|
``--inverse-tone-mapping``
|
||||||
If set, allows inverse tone mapping (expanding SDR to HDR). Not supported
|
If set, allows inverse tone mapping (expanding dynamic range). Can be used
|
||||||
by all tone mapping curves. Use with caution. (``--vo=gpu-next`` only)
|
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>``
|
``--tone-mapping-max-boost=<1.0..10.0>``
|
||||||
Upper limit for how much the tone mapping algorithm is allowed to boost
|
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 image_lut;
|
||||||
struct user_lut target_lut;
|
struct user_lut target_lut;
|
||||||
int target_hint;
|
int target_hint;
|
||||||
|
int target_hint_mode;
|
||||||
char **raw_opts;
|
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)},
|
{"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
|
||||||
{"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
|
{"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", 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
|
// No `target-lut-type` because we don't support non-RGB targets
|
||||||
{"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
|
{"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
|
||||||
{0},
|
{0},
|
||||||
@@ -1003,12 +1005,15 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
|
|||||||
struct ra_swapchain *sw = p->ra_ctx->swapchain;
|
struct ra_swapchain *sw = p->ra_ctx->swapchain;
|
||||||
|
|
||||||
bool pass_colorspace = false;
|
bool pass_colorspace = false;
|
||||||
struct pl_color_space target_csp;
|
struct pl_color_space target_csp = {0};
|
||||||
// Assume HDR is supported, if query is not available
|
|
||||||
// TODO: Implement this for all backends
|
// TODO: Implement this for all backends
|
||||||
target_csp = sw->fns->target_csp
|
if (sw->fns->target_csp)
|
||||||
? sw->fns->target_csp(sw)
|
target_csp = sw->fns->target_csp(sw);
|
||||||
: (struct pl_color_space){ .transfer = PL_COLOR_TRC_PQ };
|
// 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)) {
|
if (!pl_color_transfer_is_hdr(target_csp.transfer)) {
|
||||||
// Don't use reported display peak in SDR mode. Mostly because libplacebo
|
// Don't use reported display peak in SDR mode. Mostly because libplacebo
|
||||||
// forcefully switches to PQ if hinting hdr metadata, ignoring the transfer
|
// 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.
|
// very specific usecase, needs proper calibration, users can set it manually.
|
||||||
target_csp.hdr.max_luma = 0;
|
target_csp.hdr.max_luma = 0;
|
||||||
target_csp.hdr.min_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;
|
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 &&
|
(p->next_opts->target_hint == -1 &&
|
||||||
pl_color_transfer_is_hdr(target_csp.transfer));
|
pl_color_transfer_is_hdr(target_csp.transfer));
|
||||||
if (target_hint && frame->current) {
|
if (target_hint && frame->current) {
|
||||||
hint = frame->current->params.color;
|
const struct pl_color_space *source = &frame->current->params.color;
|
||||||
float target_peak = opts->target_peak ? opts->target_peak : target_csp.hdr.max_luma;
|
const struct pl_color_space *target = &target_csp;
|
||||||
float target_trc = opts->target_trc;
|
hint = *(p->next_opts->target_hint_mode == 0 ? target : source);
|
||||||
if (!target_trc) {
|
if (pl_color_transfer_is_hdr(hint.transfer) && !pl_primaries_valid(&hint.hdr.prim))
|
||||||
// If we are targeting HDR display, repack SDR to target trc. This way
|
pl_color_space_merge(&hint, source);
|
||||||
// we ensure that conversion is done by mpv and not by the compositor,
|
// Infer missing bits now. This is important so that we don't lose
|
||||||
// which is often worse in terms of quality. Do this only if we are sure
|
// information after user option overrides. For example, if the user
|
||||||
// that display is in HDR mode, i.e. target_csp() reported HDR transfer.
|
// sets target_trc to PQ, but the hint(source) is SDR, we want to fill
|
||||||
// libplacebo already does the same when peak > 203, but we want this
|
// in SDR luminance values instead of the default PQ range.
|
||||||
// for any peak value.
|
pl_color_space_infer(&hint);
|
||||||
// Note: We don't have information if the compositor/driver is able to
|
// Always prefer target luminance and transfer for inverse tone mapping
|
||||||
// reconfigure the display, currently target_csp() is only implemented
|
if (pl_color_transfer_is_hdr(target->transfer) && opts->tone_map.inverse) {
|
||||||
// for D3D11 where we know automatic reconfiguration never happens.
|
hint.transfer = target->transfer;
|
||||||
// Also I like to minimize possible reconfigurations, and outputting SDR
|
hint.hdr.max_luma = target->hdr.max_luma;
|
||||||
// in PQ is fine.
|
hint.hdr.min_luma = target->hdr.min_luma;
|
||||||
if (pl_color_transfer_is_hdr(target_csp.transfer) && target_csp.hdr.max_luma > 0 &&
|
hint.hdr.max_cll = target->hdr.max_cll;
|
||||||
!pl_color_transfer_is_hdr(frame->current->params.color.transfer))
|
hint.hdr.max_fall = target->hdr.max_fall;
|
||||||
{
|
|
||||||
target_trc = target_trc ? target_trc : target_csp.transfer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (p->ra_ctx->fns->pass_colorspace && p->ra_ctx->fns->pass_colorspace(p->ra_ctx))
|
if (p->ra_ctx->fns->pass_colorspace && p->ra_ctx->fns->pass_colorspace(p->ra_ctx))
|
||||||
pass_colorspace = true;
|
pass_colorspace = true;
|
||||||
if (opts->target_prim)
|
if (opts->target_prim)
|
||||||
hint.primaries = opts->target_prim;
|
hint.primaries = opts->target_prim;
|
||||||
if (target_trc)
|
if (opts->target_trc)
|
||||||
hint.transfer = target_trc;
|
hint.transfer = opts->target_trc;
|
||||||
if (target_peak)
|
if (opts->target_peak)
|
||||||
hint.hdr.max_luma = target_peak;
|
hint.hdr.max_luma = opts->target_peak;
|
||||||
apply_target_contrast(p, &hint, target_csp.hdr.min_luma);
|
apply_target_contrast(p, &hint, hint.hdr.min_luma);
|
||||||
if (!pass_colorspace)
|
if (!pass_colorspace)
|
||||||
pl_swapchain_colorspace_hint(p->sw, &hint);
|
pl_swapchain_colorspace_hint(p->sw, &hint);
|
||||||
} else if (!target_hint) {
|
} else if (!target_hint) {
|
||||||
|
|||||||
Reference in new issue
Block a user