mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-06-14 14:32:57 -04:00
Merge branch 'hdr-metadata' into 'master'
output: expose EDID HDR static metadata See merge request wlroots/wlroots!5369
This commit is contained in:
commit
68138cf031
3 changed files with 138 additions and 14 deletions
|
|
@ -105,6 +105,43 @@ void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data)
|
||||||
output->supported_transfer_functions |= WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ;
|
output->supported_transfer_functions |= WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture HDR static metadata whenever the EDID provides any value, even if
|
||||||
|
// the Type 1 descriptor flag is unset: some displays only advertise EOTFs
|
||||||
|
// without explicitly flagging Type 1 support, but still expose useful
|
||||||
|
// desired-content luminance values.
|
||||||
|
if (hdr_static_metadata->type1 ||
|
||||||
|
hdr_static_metadata->desired_content_max_luminance > 0 ||
|
||||||
|
hdr_static_metadata->desired_content_max_frame_avg_luminance > 0 ||
|
||||||
|
hdr_static_metadata->desired_content_min_luminance > 0) {
|
||||||
|
output->hdr_metadata_value = (struct wlr_output_hdr_metadata){
|
||||||
|
.desired_content_min_luminance = hdr_static_metadata->desired_content_min_luminance,
|
||||||
|
};
|
||||||
|
if (hdr_static_metadata->desired_content_max_luminance > 0) {
|
||||||
|
output->hdr_metadata_value.has_desired_content_max_luminance = true;
|
||||||
|
output->hdr_metadata_value.desired_content_max_luminance =
|
||||||
|
hdr_static_metadata->desired_content_max_luminance;
|
||||||
|
}
|
||||||
|
if (hdr_static_metadata->desired_content_max_frame_avg_luminance > 0) {
|
||||||
|
output->hdr_metadata_value.has_desired_content_max_frame_average_luminance = true;
|
||||||
|
output->hdr_metadata_value.desired_content_max_frame_average_luminance =
|
||||||
|
hdr_static_metadata->desired_content_max_frame_avg_luminance;
|
||||||
|
}
|
||||||
|
output->hdr_metadata = &output->hdr_metadata_value;
|
||||||
|
wlr_log(WLR_DEBUG,
|
||||||
|
"EDID HDR static metadata: type1=%d pq=%d hlg=%d hdr_gamma=%d sdr=%d, "
|
||||||
|
"min_lum=%.4f cd/m^2, max_lum=%.1f cd/m^2, max_fall=%.1f cd/m^2",
|
||||||
|
hdr_static_metadata->type1, hdr_static_metadata->pq,
|
||||||
|
hdr_static_metadata->hlg, hdr_static_metadata->traditional_hdr,
|
||||||
|
hdr_static_metadata->traditional_sdr,
|
||||||
|
hdr_static_metadata->desired_content_min_luminance,
|
||||||
|
hdr_static_metadata->desired_content_max_luminance,
|
||||||
|
hdr_static_metadata->desired_content_max_frame_avg_luminance);
|
||||||
|
} else {
|
||||||
|
wlr_log(WLR_DEBUG,
|
||||||
|
"EDID has no HDR static metadata (sdr_only=%d)",
|
||||||
|
hdr_static_metadata->traditional_sdr);
|
||||||
|
}
|
||||||
|
|
||||||
di_info_destroy(info);
|
di_info_destroy(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,22 @@ enum wlr_output_state_mode_type {
|
||||||
WLR_OUTPUT_STATE_MODE_CUSTOM,
|
WLR_OUTPUT_STATE_MODE_CUSTOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HDR static metadata block 1 values, as advertised by the display via EDID.
|
||||||
|
*
|
||||||
|
* Luminances are given in cd/m². desired_content_min_luminance is always
|
||||||
|
* available (defaults to 0 when the EDID block does not provide a value).
|
||||||
|
* desired_content_max_luminance and desired_content_max_frame_average_luminance
|
||||||
|
* are optional and only meaningful when their corresponding has_* flag is set.
|
||||||
|
*/
|
||||||
|
struct wlr_output_hdr_metadata {
|
||||||
|
double desired_content_min_luminance;
|
||||||
|
bool has_desired_content_max_luminance;
|
||||||
|
double desired_content_max_luminance;
|
||||||
|
bool has_desired_content_max_frame_average_luminance;
|
||||||
|
double desired_content_max_frame_average_luminance;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Colorimetric image description.
|
* Colorimetric image description.
|
||||||
*
|
*
|
||||||
|
|
@ -194,6 +210,8 @@ struct wlr_output {
|
||||||
char *make, *model, *serial; // may be NULL
|
char *make, *model, *serial; // may be NULL
|
||||||
int32_t phys_width, phys_height; // mm
|
int32_t phys_width, phys_height; // mm
|
||||||
const struct wlr_color_primaries *default_primaries; // may be NULL
|
const struct wlr_color_primaries *default_primaries; // may be NULL
|
||||||
|
// HDR static metadata block 1 values from the EDID, NULL if not advertised
|
||||||
|
const struct wlr_output_hdr_metadata *hdr_metadata;
|
||||||
|
|
||||||
// Note: some backends may have zero modes
|
// Note: some backends may have zero modes
|
||||||
struct wl_list modes; // wlr_output_mode.link
|
struct wl_list modes; // wlr_output_mode.link
|
||||||
|
|
@ -278,6 +296,7 @@ struct wlr_output {
|
||||||
struct wlr_output_image_description image_description_value;
|
struct wlr_output_image_description image_description_value;
|
||||||
struct wlr_color_transform *color_transform;
|
struct wlr_color_transform *color_transform;
|
||||||
struct wlr_color_primaries default_primaries_value;
|
struct wlr_color_primaries default_primaries_value;
|
||||||
|
struct wlr_output_hdr_metadata hdr_metadata_value;
|
||||||
} WLR_PRIVATE;
|
} WLR_PRIVATE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,16 +160,31 @@ static void image_desc_handle_get_information(struct wl_client *client,
|
||||||
wp_image_description_info_v1_send_luminances(resource,
|
wp_image_description_info_v1_send_luminances(resource,
|
||||||
round(luminances.min * 10000), round(luminances.max),
|
round(luminances.min * 10000), round(luminances.max),
|
||||||
round(luminances.reference));
|
round(luminances.reference));
|
||||||
// TODO: send mastering display primaries and luminances here when we add
|
const struct wlr_color_primaries *target_primaries = &primaries;
|
||||||
// support for features.set_mastering_display_primaries
|
if (image_desc->data.has_mastering_display_primaries) {
|
||||||
|
target_primaries = &image_desc->data.mastering_display_primaries;
|
||||||
|
}
|
||||||
wp_image_description_info_v1_send_target_primaries(resource,
|
wp_image_description_info_v1_send_target_primaries(resource,
|
||||||
encode_cie1931_coord(primaries.red.x), encode_cie1931_coord(primaries.red.y),
|
encode_cie1931_coord(target_primaries->red.x), encode_cie1931_coord(target_primaries->red.y),
|
||||||
encode_cie1931_coord(primaries.green.x), encode_cie1931_coord(primaries.green.y),
|
encode_cie1931_coord(target_primaries->green.x), encode_cie1931_coord(target_primaries->green.y),
|
||||||
encode_cie1931_coord(primaries.blue.x), encode_cie1931_coord(primaries.blue.y),
|
encode_cie1931_coord(target_primaries->blue.x), encode_cie1931_coord(target_primaries->blue.y),
|
||||||
encode_cie1931_coord(primaries.white.x), encode_cie1931_coord(primaries.white.y));
|
encode_cie1931_coord(target_primaries->white.x), encode_cie1931_coord(target_primaries->white.y));
|
||||||
|
double target_min = luminances.min;
|
||||||
|
double target_max = luminances.max;
|
||||||
|
if (image_desc->data.has_mastering_luminance) {
|
||||||
|
target_min = image_desc->data.mastering_luminance.min;
|
||||||
|
target_max = image_desc->data.mastering_luminance.max;
|
||||||
|
}
|
||||||
wp_image_description_info_v1_send_target_luminance(resource,
|
wp_image_description_info_v1_send_target_luminance(resource,
|
||||||
round(luminances.min * 10000), round(luminances.max));
|
round(target_min * 10000), round(target_max));
|
||||||
// TODO: send target_max_cll and target_max_fall
|
if (image_desc->data.max_cll > 0) {
|
||||||
|
wp_image_description_info_v1_send_target_max_cll(resource,
|
||||||
|
image_desc->data.max_cll);
|
||||||
|
}
|
||||||
|
if (image_desc->data.max_fall > 0) {
|
||||||
|
wp_image_description_info_v1_send_target_max_fall(resource,
|
||||||
|
image_desc->data.max_fall);
|
||||||
|
}
|
||||||
wp_image_description_info_v1_send_done(resource);
|
wp_image_description_info_v1_send_done(resource);
|
||||||
wl_resource_destroy(resource);
|
wl_resource_destroy(resource);
|
||||||
}
|
}
|
||||||
|
|
@ -266,10 +281,50 @@ static void cm_output_handle_get_image_description(struct wl_client *client,
|
||||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||||
};
|
};
|
||||||
const struct wlr_output_image_description *image_desc = cm_output->output->image_description;
|
const struct wlr_output *output = cm_output->output;
|
||||||
|
const struct wlr_output_image_description *image_desc = output->image_description;
|
||||||
if (image_desc != NULL) {
|
if (image_desc != NULL) {
|
||||||
data.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(image_desc->transfer_function);
|
data.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(image_desc->transfer_function);
|
||||||
data.primaries_named = wlr_color_manager_v1_primaries_from_wlr(image_desc->primaries);
|
data.primaries_named = wlr_color_manager_v1_primaries_from_wlr(image_desc->primaries);
|
||||||
|
if (image_desc->mastering_luminance.max > 0) {
|
||||||
|
data.has_mastering_luminance = true;
|
||||||
|
data.mastering_luminance.min = image_desc->mastering_luminance.min;
|
||||||
|
data.mastering_luminance.max = image_desc->mastering_luminance.max;
|
||||||
|
}
|
||||||
|
if (image_desc->max_cll > 0) {
|
||||||
|
data.max_cll = (uint32_t)round(image_desc->max_cll);
|
||||||
|
}
|
||||||
|
if (image_desc->max_fall > 0) {
|
||||||
|
data.max_fall = (uint32_t)round(image_desc->max_fall);
|
||||||
|
}
|
||||||
|
const struct wlr_color_primaries *p = &image_desc->mastering_display_primaries;
|
||||||
|
if (p->red.x != 0 || p->red.y != 0 || p->green.x != 0 || p->green.y != 0 ||
|
||||||
|
p->blue.x != 0 || p->blue.y != 0 || p->white.x != 0 || p->white.y != 0) {
|
||||||
|
data.has_mastering_display_primaries = true;
|
||||||
|
data.mastering_display_primaries = *p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to EDID-derived target metadata when the compositor didn't
|
||||||
|
// populate it on the output's image description.
|
||||||
|
if (!data.has_mastering_luminance && output->hdr_metadata != NULL) {
|
||||||
|
const struct wlr_output_hdr_metadata *hdr = output->hdr_metadata;
|
||||||
|
if (hdr->has_desired_content_max_luminance) {
|
||||||
|
data.has_mastering_luminance = true;
|
||||||
|
data.mastering_luminance.min = hdr->desired_content_min_luminance;
|
||||||
|
data.mastering_luminance.max = hdr->desired_content_max_luminance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.max_cll == 0 && output->hdr_metadata != NULL &&
|
||||||
|
output->hdr_metadata->has_desired_content_max_luminance) {
|
||||||
|
data.max_cll = (uint32_t)round(output->hdr_metadata->desired_content_max_luminance);
|
||||||
|
}
|
||||||
|
if (data.max_fall == 0 && output->hdr_metadata != NULL &&
|
||||||
|
output->hdr_metadata->has_desired_content_max_frame_average_luminance) {
|
||||||
|
data.max_fall = (uint32_t)round(output->hdr_metadata->desired_content_max_frame_average_luminance);
|
||||||
|
}
|
||||||
|
if (!data.has_mastering_display_primaries && output->default_primaries != NULL) {
|
||||||
|
data.has_mastering_display_primaries = true;
|
||||||
|
data.mastering_display_primaries = *output->default_primaries;
|
||||||
}
|
}
|
||||||
image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true);
|
image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true);
|
||||||
}
|
}
|
||||||
|
|
@ -892,9 +947,20 @@ static void manager_handle_create_parametric_creator(struct wl_client *client,
|
||||||
|
|
||||||
static void manager_handle_create_windows_scrgb(struct wl_client *client,
|
static void manager_handle_create_windows_scrgb(struct wl_client *client,
|
||||||
struct wl_resource *manager_resource, uint32_t id) {
|
struct wl_resource *manager_resource, uint32_t id) {
|
||||||
wl_resource_post_error(manager_resource,
|
struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource);
|
||||||
WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE,
|
if (!manager->features.windows_scrgb) {
|
||||||
"get_windows_scrgb is not supported");
|
wl_resource_post_error(manager_resource,
|
||||||
|
WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE,
|
||||||
|
"create_windows_scrgb is not supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Windows-scRGB: sRGB (BT.709) primaries, extended-linear transfer
|
||||||
|
// characteristic. R=G=B=1.0 corresponds to 80 cd/m².
|
||||||
|
const struct wlr_image_description_v1_data data = {
|
||||||
|
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR,
|
||||||
|
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||||
|
};
|
||||||
|
image_desc_create_ready(manager, manager_resource, id, &data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wp_color_manager_v1_interface manager_impl = {
|
static const struct wp_color_manager_v1_interface manager_impl = {
|
||||||
|
|
@ -987,8 +1053,10 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp
|
||||||
assert(!options->features.set_primaries);
|
assert(!options->features.set_primaries);
|
||||||
assert(!options->features.set_tf_power);
|
assert(!options->features.set_tf_power);
|
||||||
assert(!options->features.set_luminances);
|
assert(!options->features.set_luminances);
|
||||||
assert(!options->features.extended_target_volume);
|
// extended_target_volume requires set_mastering_display_primaries
|
||||||
assert(!options->features.windows_scrgb);
|
if (options->features.extended_target_volume) {
|
||||||
|
assert(options->features.set_mastering_display_primaries);
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_color_manager_v1 *manager = calloc(1, sizeof(*manager));
|
struct wlr_color_manager_v1 *manager = calloc(1, sizeof(*manager));
|
||||||
if (manager == NULL) {
|
if (manager == NULL) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue