Merge branch 'output_color_format' into 'master'

Draft: output: add color_format state field

See merge request wlroots/wlroots!5364
This commit is contained in:
Igor Deordiev 2026-06-11 09:17:41 +00:00
commit 66df6f3a4d
11 changed files with 135 additions and 7 deletions

View file

@ -401,6 +401,7 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo
state->vrr_enabled = vrr_enabled; state->vrr_enabled = vrr_enabled;
state->colorspace = colorspace; state->colorspace = colorspace;
state->hdr_output_metadata = hdr_output_metadata; state->hdr_output_metadata = hdr_output_metadata;
state->color_format = state->base->color_format;
return true; return true;
} }
@ -567,6 +568,10 @@ static void atomic_connector_add(struct atomic *atom,
if (modeset && active && conn->props.max_bpc != 0 && conn->max_bpc_bounds[1] != 0) { if (modeset && active && conn->props.max_bpc != 0 && conn->max_bpc_bounds[1] != 0) {
atomic_add(atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb)); atomic_add(atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb));
} }
if (modeset && conn->props.color_format != 0 &&
state->base->committed & WLR_OUTPUT_STATE_COLOR_FORMAT) {
atomic_add(atom, conn->id, conn->props.color_format, state->color_format);
}
if (conn->props.colorspace != 0) { if (conn->props.colorspace != 0) {
atomic_add(atom, conn->id, conn->props.colorspace, state->colorspace); atomic_add(atom, conn->id, conn->props.colorspace, state->colorspace);
} }

View file

@ -44,7 +44,8 @@ static const uint32_t COMMIT_OUTPUT_STATE =
WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE |
WLR_OUTPUT_STATE_COLOR_TRANSFORM | WLR_OUTPUT_STATE_COLOR_TRANSFORM |
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION | WLR_OUTPUT_STATE_IMAGE_DESCRIPTION |
WLR_OUTPUT_STATE_COLOR_REPRESENTATION; WLR_OUTPUT_STATE_COLOR_REPRESENTATION |
WLR_OUTPUT_STATE_COLOR_FORMAT;
static const uint32_t SUPPORTED_OUTPUT_STATE = static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE;

View file

@ -27,6 +27,7 @@ static const struct prop_info connector_info[] = {
{ "EDID", INDEX(edid) }, { "EDID", INDEX(edid) },
{ "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) }, { "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) },
{ "PATH", INDEX(path) }, { "PATH", INDEX(path) },
{ "color format", INDEX(color_format) },
{ "content type", INDEX(content_type) }, { "content type", INDEX(content_type) },
{ "link-status", INDEX(link_status) }, { "link-status", INDEX(link_status) },
{ "max bpc", INDEX(max_bpc) }, { "max bpc", INDEX(max_bpc) },

View file

@ -162,6 +162,7 @@ struct wlr_drm_connector_state {
uint32_t fb_damage_clips; uint32_t fb_damage_clips;
int primary_in_fence_fd; int primary_in_fence_fd;
bool vrr_enabled; bool vrr_enabled;
uint32_t color_format;
uint32_t colorspace; uint32_t colorspace;
uint32_t hdr_output_metadata; uint32_t hdr_output_metadata;
}; };

View file

@ -22,6 +22,7 @@ struct wlr_drm_connector_props {
uint32_t panel_orientation; // not guaranteed to exist uint32_t panel_orientation; // not guaranteed to exist
uint32_t content_type; // not guaranteed to exist uint32_t content_type; // not guaranteed to exist
uint32_t max_bpc; // not guaranteed to exist uint32_t max_bpc; // not guaranteed to exist
uint32_t color_format; // not guaranteed to exist
// atomic-modesetting only // atomic-modesetting only

View file

@ -78,6 +78,21 @@ enum wlr_output_state_field {
WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12,
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13,
WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14, WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14,
WLR_OUTPUT_STATE_COLOR_FORMAT = 1 << 15,
};
/**
* DRM color format for output connectors.
*
* These values match enum drm_connector_color_format in the kernel.
* AUTO (0) is the default and lets the driver pick.
*/
enum wlr_output_color_format {
WLR_OUTPUT_COLOR_FORMAT_AUTO = 0,
WLR_OUTPUT_COLOR_FORMAT_RGB444,
WLR_OUTPUT_COLOR_FORMAT_YCBCR444,
WLR_OUTPUT_COLOR_FORMAT_YCBCR422,
WLR_OUTPUT_COLOR_FORMAT_YCBCR420,
}; };
enum wlr_output_state_mode_type { enum wlr_output_state_mode_type {
@ -165,6 +180,8 @@ struct wlr_output_state {
struct wlr_color_transform *color_transform; struct wlr_color_transform *color_transform;
struct wlr_output_image_description *image_description; struct wlr_output_image_description *image_description;
uint32_t color_format; // enum wlr_output_color_format
}; };
struct wlr_output_impl; struct wlr_output_impl;
@ -210,6 +227,7 @@ struct wlr_output {
enum wl_output_transform transform; enum wl_output_transform transform;
enum wlr_output_adaptive_sync_status adaptive_sync_status; enum wlr_output_adaptive_sync_status adaptive_sync_status;
uint32_t render_format; uint32_t render_format;
enum wlr_output_color_format color_format;
enum wlr_color_encoding color_encoding; enum wlr_color_encoding color_encoding;
enum wlr_color_range color_range; enum wlr_color_range color_range;
const struct wlr_output_image_description *image_description; const struct wlr_output_image_description *image_description;
@ -641,6 +659,20 @@ void wlr_output_state_set_color_encoding_and_range(
struct wlr_output_state *state, struct wlr_output_state *state,
enum wlr_color_encoding encoding, enum wlr_color_range range); enum wlr_color_encoding encoding, enum wlr_color_range range);
/**
* Sets the color format for an output. The color format determines the color
* encoding sent to the display (RGB, YCbCr 4:4:4, YCbCr 4:2:2, YCbCr 4:2:0).
*
* The default value is WLR_OUTPUT_COLOR_FORMAT_AUTO.
*
* Not all connectors support all color formats. If the connector doesn't
* support the "color format" DRM property, this setting is ignored.
*
* This state will be applied once wlr_output_commit_state() is called.
*/
void wlr_output_state_set_color_format(struct wlr_output_state *state,
enum wlr_output_color_format color_format);
/** /**
* Copies the output state from src to dst. It is safe to then * Copies the output state from src to dst. It is safe to then
* wlr_output_state_finish() src and have dst still be valid. * wlr_output_state_finish() src and have dst still be valid.

View file

@ -59,6 +59,7 @@ struct wlr_output_head_v1_state {
enum wl_output_transform transform; enum wl_output_transform transform;
float scale; float scale;
bool adaptive_sync_enabled; bool adaptive_sync_enabled;
enum wlr_output_color_format color_format;
}; };
struct wlr_output_head_v1 { struct wlr_output_head_v1 {

View file

@ -39,7 +39,7 @@
interface version number is reset. interface version number is reset.
</description> </description>
<interface name="zwlr_output_manager_v1" version="4"> <interface name="zwlr_output_manager_v1" version="5">
<description summary="output device configuration manager"> <description summary="output device configuration manager">
This interface is a manager that allows reading and writing the current This interface is a manager that allows reading and writing the current
output device configuration. output device configuration.
@ -125,7 +125,7 @@
</event> </event>
</interface> </interface>
<interface name="zwlr_output_head_v1" version="4"> <interface name="zwlr_output_head_v1" version="5">
<description summary="output device"> <description summary="output device">
A head is an output device. The difference between a wl_output object and A head is an output device. The difference between a wl_output object and
a head is that heads are advertised even if they are turned off. A head a head is that heads are advertised even if they are turned off. A head
@ -364,6 +364,28 @@
</description> </description>
<arg name="state" type="uint" enum="adaptive_sync_state"/> <arg name="state" type="uint" enum="adaptive_sync_state"/>
</event> </event>
<!-- Version 5 additions -->
<enum name="color_format" since="5">
<description summary="color format for the output">
These values correspond to the DRM connector color format property.
The compositor may not support all formats on all connectors.
</description>
<entry name="auto" value="0" summary="compositor/driver picks"/>
<entry name="rgb" value="1" summary="RGB output"/>
<entry name="ycbcr444" value="2" summary="YCbCr 4:4:4"/>
<entry name="ycbcr422" value="3" summary="YCbCr 4:2:2"/>
<entry name="ycbcr420" value="4" summary="YCbCr 4:2:0"/>
</enum>
<event name="color_format" since="5">
<description summary="current color format">
This event describes the color format currently used by the head.
The color format determines the color encoding sent to the display.
</description>
<arg name="format" type="uint" enum="color_format"/>
</event>
</interface> </interface>
<interface name="zwlr_output_mode_v1" version="3"> <interface name="zwlr_output_mode_v1" version="3">
@ -421,7 +443,7 @@
</request> </request>
</interface> </interface>
<interface name="zwlr_output_configuration_v1" version="4"> <interface name="zwlr_output_configuration_v1" version="5">
<description summary="output configuration"> <description summary="output configuration">
This object is used by the client to describe a full output configuration. This object is used by the client to describe a full output configuration.
@ -539,7 +561,7 @@
</request> </request>
</interface> </interface>
<interface name="zwlr_output_configuration_head_v1" version="4"> <interface name="zwlr_output_configuration_head_v1" version="5">
<description summary="head configuration"> <description summary="head configuration">
This object is used by the client to update a single head's configuration. This object is used by the client to update a single head's configuration.
@ -554,6 +576,8 @@
<entry name="invalid_scale" value="5" summary="scale negative or zero"/> <entry name="invalid_scale" value="5" summary="scale negative or zero"/>
<entry name="invalid_adaptive_sync_state" value="6" since="4" <entry name="invalid_adaptive_sync_state" value="6" since="4"
summary="invalid enum value used in the set_adaptive_sync request"/> summary="invalid enum value used in the set_adaptive_sync request"/>
<entry name="invalid_color_format" value="7" since="5"
summary="invalid enum value used in the set_color_format request"/>
</enum> </enum>
<request name="set_mode"> <request name="set_mode">
@ -607,5 +631,19 @@
</description> </description>
<arg name="state" type="uint" enum="zwlr_output_head_v1.adaptive_sync_state"/> <arg name="state" type="uint" enum="zwlr_output_head_v1.adaptive_sync_state"/>
</request> </request>
<!-- Version 5 additions -->
<request name="set_color_format" since="5">
<description summary="set the color format">
This request sets the color format for the output. The color format
determines the color encoding sent to the display (RGB, YCbCr 4:4:4,
YCbCr 4:2:2, YCbCr 4:2:0).
If the connector does not support the color format property, the
compositor should ignore this request.
</description>
<arg name="format" type="uint" enum="zwlr_output_head_v1.color_format"/>
</request>
</interface> </interface>
</protocol> </protocol>

View file

@ -216,6 +216,10 @@ static void output_apply_state(struct wlr_output *output,
output->render_format = state->render_format; output->render_format = state->render_format;
} }
if (state->committed & WLR_OUTPUT_STATE_COLOR_FORMAT) {
output->color_format = state->color_format;
}
if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) {
output->subpixel = state->subpixel; output->subpixel = state->subpixel;
} }
@ -357,6 +361,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
.impl = impl, .impl = impl,
.event_loop = event_loop, .event_loop = event_loop,
.render_format = DRM_FORMAT_XRGB8888, .render_format = DRM_FORMAT_XRGB8888,
.color_format = WLR_OUTPUT_COLOR_FORMAT_AUTO,
.transform = WL_OUTPUT_TRANSFORM_NORMAL, .transform = WL_OUTPUT_TRANSFORM_NORMAL,
.scale = 1, .scale = 1,
.commit_seq = 0, .commit_seq = 0,
@ -590,6 +595,10 @@ static uint32_t output_compare_state(struct wlr_output *output,
output->color_range == state->color_range) { output->color_range == state->color_range) {
fields |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION; fields |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION;
} }
if ((state->committed & WLR_OUTPUT_STATE_COLOR_FORMAT) &&
output->color_format == state->color_format) {
fields |= WLR_OUTPUT_STATE_COLOR_FORMAT;
}
return fields; return fields;
} }

View file

@ -97,6 +97,12 @@ void wlr_output_state_set_layers(struct wlr_output_state *state,
state->layers_len = layers_len; state->layers_len = layers_len;
} }
void wlr_output_state_set_color_format(struct wlr_output_state *state,
enum wlr_output_color_format color_format) {
state->committed |= WLR_OUTPUT_STATE_COLOR_FORMAT;
state->color_format = color_format;
}
void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, void wlr_output_state_set_wait_timeline(struct wlr_output_state *state,
struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point) { struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point) {
state->committed |= WLR_OUTPUT_STATE_WAIT_TIMELINE; state->committed |= WLR_OUTPUT_STATE_WAIT_TIMELINE;

View file

@ -6,7 +6,7 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "wlr-output-management-unstable-v1-protocol.h" #include "wlr-output-management-unstable-v1-protocol.h"
#define OUTPUT_MANAGER_VERSION 4 #define OUTPUT_MANAGER_VERSION 5
enum { enum {
HEAD_STATE_ENABLED = 1 << 0, HEAD_STATE_ENABLED = 1 << 0,
@ -15,11 +15,12 @@ enum {
HEAD_STATE_TRANSFORM = 1 << 3, HEAD_STATE_TRANSFORM = 1 << 3,
HEAD_STATE_SCALE = 1 << 4, HEAD_STATE_SCALE = 1 << 4,
HEAD_STATE_ADAPTIVE_SYNC = 1 << 5, HEAD_STATE_ADAPTIVE_SYNC = 1 << 5,
HEAD_STATE_COLOR_FORMAT = 1 << 6,
}; };
static const uint32_t HEAD_STATE_ALL = HEAD_STATE_ENABLED | HEAD_STATE_MODE | static const uint32_t HEAD_STATE_ALL = HEAD_STATE_ENABLED | HEAD_STATE_MODE |
HEAD_STATE_POSITION | HEAD_STATE_TRANSFORM | HEAD_STATE_SCALE | HEAD_STATE_POSITION | HEAD_STATE_TRANSFORM | HEAD_STATE_SCALE |
HEAD_STATE_ADAPTIVE_SYNC; HEAD_STATE_ADAPTIVE_SYNC | HEAD_STATE_COLOR_FORMAT;
static const struct zwlr_output_head_v1_interface head_impl; static const struct zwlr_output_head_v1_interface head_impl;
@ -162,6 +163,7 @@ struct wlr_output_configuration_head_v1 *
config_head->state.scale = output->scale; config_head->state.scale = output->scale;
config_head->state.adaptive_sync_enabled = config_head->state.adaptive_sync_enabled =
output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED;
config_head->state.color_format = output->color_format;
return config_head; return config_head;
} }
@ -307,6 +309,24 @@ static void config_head_handle_set_adaptive_sync(struct wl_client *client,
} }
} }
static void config_head_handle_set_color_format(struct wl_client *client,
struct wl_resource *config_head_resource, uint32_t format) {
struct wlr_output_configuration_head_v1 *config_head =
config_head_from_resource(config_head_resource);
if (config_head == NULL) {
return;
}
if (format > ZWLR_OUTPUT_HEAD_V1_COLOR_FORMAT_YCBCR420) {
wl_resource_post_error(config_head_resource,
ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_COLOR_FORMAT,
"client requested invalid color format %u", format);
return;
}
config_head->state.color_format = format;
}
static const struct zwlr_output_configuration_head_v1_interface config_head_impl = { static const struct zwlr_output_configuration_head_v1_interface config_head_impl = {
.set_mode = config_head_handle_set_mode, .set_mode = config_head_handle_set_mode,
.set_custom_mode = config_head_handle_set_custom_mode, .set_custom_mode = config_head_handle_set_custom_mode,
@ -314,6 +334,7 @@ static const struct zwlr_output_configuration_head_v1_interface config_head_impl
.set_transform = config_head_handle_set_transform, .set_transform = config_head_handle_set_transform,
.set_scale = config_head_handle_set_scale, .set_scale = config_head_handle_set_scale,
.set_adaptive_sync = config_head_handle_set_adaptive_sync, .set_adaptive_sync = config_head_handle_set_adaptive_sync,
.set_color_format = config_head_handle_set_color_format,
}; };
static void config_head_handle_resource_destroy(struct wl_resource *resource) { static void config_head_handle_resource_destroy(struct wl_resource *resource) {
@ -815,6 +836,13 @@ static void head_send_state(struct wlr_output_head_v1 *head,
ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
} }
} }
if ((state & HEAD_STATE_COLOR_FORMAT) &&
wl_resource_get_version(head_resource) >=
ZWLR_OUTPUT_HEAD_V1_COLOR_FORMAT_SINCE_VERSION) {
zwlr_output_head_v1_send_color_format(head_resource,
head->state.color_format);
}
} }
static void head_handle_resource_destroy(struct wl_resource *resource) { static void head_handle_resource_destroy(struct wl_resource *resource) {
@ -911,6 +939,9 @@ static bool manager_update_head(struct wlr_output_manager_v1 *manager,
if (current->adaptive_sync_enabled != next->adaptive_sync_enabled) { if (current->adaptive_sync_enabled != next->adaptive_sync_enabled) {
state |= HEAD_STATE_ADAPTIVE_SYNC; state |= HEAD_STATE_ADAPTIVE_SYNC;
} }
if (current->color_format != next->color_format) {
state |= HEAD_STATE_COLOR_FORMAT;
}
// If a mode was added to wlr_output.modes we need to add the new mode // If a mode was added to wlr_output.modes we need to add the new mode
// to the wlr_output_head // to the wlr_output_head
@ -1032,6 +1063,8 @@ void wlr_output_head_v1_state_apply(
wlr_output_state_set_transform(output_state, head_state->transform); wlr_output_state_set_transform(output_state, head_state->transform);
wlr_output_state_set_adaptive_sync_enabled(output_state, wlr_output_state_set_adaptive_sync_enabled(output_state,
head_state->adaptive_sync_enabled); head_state->adaptive_sync_enabled);
wlr_output_state_set_color_format(output_state,
head_state->color_format);
} }
struct wlr_backend_output_state *wlr_output_configuration_v1_build_state( struct wlr_backend_output_state *wlr_output_configuration_v1_build_state(