diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index daa8ba9bf..c7dc4bd55 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -401,6 +401,7 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo state->vrr_enabled = vrr_enabled; state->colorspace = colorspace; state->hdr_output_metadata = hdr_output_metadata; + state->color_format = state->base->color_format; 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) { 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) { atomic_add(atom, conn->id, conn->props.colorspace, state->colorspace); } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d2f75f71f..0e0c7b16f 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -44,7 +44,8 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_COLOR_TRANSFORM | 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 = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 4c6bdcb36..e16112870 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -27,6 +27,7 @@ static const struct prop_info connector_info[] = { { "EDID", INDEX(edid) }, { "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) }, { "PATH", INDEX(path) }, + { "color format", INDEX(color_format) }, { "content type", INDEX(content_type) }, { "link-status", INDEX(link_status) }, { "max bpc", INDEX(max_bpc) }, diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index a8c5e077a..826e9cf14 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -162,6 +162,7 @@ struct wlr_drm_connector_state { uint32_t fb_damage_clips; int primary_in_fence_fd; bool vrr_enabled; + uint32_t color_format; uint32_t colorspace; uint32_t hdr_output_metadata; }; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 20e0a5bff..388191515 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -22,6 +22,7 @@ struct wlr_drm_connector_props { uint32_t panel_orientation; // not guaranteed to exist uint32_t content_type; // not guaranteed to exist uint32_t max_bpc; // not guaranteed to exist + uint32_t color_format; // not guaranteed to exist // atomic-modesetting only diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c8e44b0e6..cfba2517b 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -78,6 +78,21 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, 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 { @@ -165,6 +180,8 @@ struct wlr_output_state { struct wlr_color_transform *color_transform; struct wlr_output_image_description *image_description; + + uint32_t color_format; // enum wlr_output_color_format }; struct wlr_output_impl; @@ -210,6 +227,7 @@ struct wlr_output { enum wl_output_transform transform; enum wlr_output_adaptive_sync_status adaptive_sync_status; uint32_t render_format; + enum wlr_output_color_format color_format; enum wlr_color_encoding color_encoding; enum wlr_color_range color_range; 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, 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 * wlr_output_state_finish() src and have dst still be valid. diff --git a/include/wlr/types/wlr_output_management_v1.h b/include/wlr/types/wlr_output_management_v1.h index 2835a5924..a0bb4a185 100644 --- a/include/wlr/types/wlr_output_management_v1.h +++ b/include/wlr/types/wlr_output_management_v1.h @@ -59,6 +59,7 @@ struct wlr_output_head_v1_state { enum wl_output_transform transform; float scale; bool adaptive_sync_enabled; + enum wlr_output_color_format color_format; }; struct wlr_output_head_v1 { diff --git a/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml index 541284a8c..95420f676 100644 --- a/protocol/wlr-output-management-unstable-v1.xml +++ b/protocol/wlr-output-management-unstable-v1.xml @@ -39,7 +39,7 @@ interface version number is reset. - + This interface is a manager that allows reading and writing the current output device configuration. @@ -125,7 +125,7 @@ - + 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 @@ -364,6 +364,28 @@ + + + + + + These values correspond to the DRM connector color format property. + The compositor may not support all formats on all connectors. + + + + + + + + + + + This event describes the color format currently used by the head. + The color format determines the color encoding sent to the display. + + + @@ -421,7 +443,7 @@ - + This object is used by the client to describe a full output configuration. @@ -539,7 +561,7 @@ - + This object is used by the client to update a single head's configuration. @@ -554,6 +576,8 @@ + @@ -607,5 +631,19 @@ + + + + + + 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. + + + diff --git a/types/output/output.c b/types/output/output.c index 46da1f425..acda0823b 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -216,6 +216,10 @@ static void output_apply_state(struct wlr_output *output, 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) { output->subpixel = state->subpixel; } @@ -357,6 +361,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, .impl = impl, .event_loop = event_loop, .render_format = DRM_FORMAT_XRGB8888, + .color_format = WLR_OUTPUT_COLOR_FORMAT_AUTO, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .scale = 1, .commit_seq = 0, @@ -590,6 +595,10 @@ static uint32_t output_compare_state(struct wlr_output *output, output->color_range == state->color_range) { 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; } diff --git a/types/output/state.c b/types/output/state.c index 5f44c18e8..04b48a9ee 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -97,6 +97,12 @@ void wlr_output_state_set_layers(struct wlr_output_state *state, 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, struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point) { state->committed |= WLR_OUTPUT_STATE_WAIT_TIMELINE; diff --git a/types/wlr_output_management_v1.c b/types/wlr_output_management_v1.c index 910bcbe5e..9baf3dfb5 100644 --- a/types/wlr_output_management_v1.c +++ b/types/wlr_output_management_v1.c @@ -6,7 +6,7 @@ #include #include "wlr-output-management-unstable-v1-protocol.h" -#define OUTPUT_MANAGER_VERSION 4 +#define OUTPUT_MANAGER_VERSION 5 enum { HEAD_STATE_ENABLED = 1 << 0, @@ -15,11 +15,12 @@ enum { HEAD_STATE_TRANSFORM = 1 << 3, HEAD_STATE_SCALE = 1 << 4, 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 | 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; @@ -162,6 +163,7 @@ struct wlr_output_configuration_head_v1 * config_head->state.scale = output->scale; config_head->state.adaptive_sync_enabled = output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; + config_head->state.color_format = output->color_format; 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 = { .set_mode = config_head_handle_set_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_scale = config_head_handle_set_scale, .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) { @@ -815,6 +836,13 @@ static void head_send_state(struct wlr_output_head_v1 *head, 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) { @@ -911,6 +939,9 @@ static bool manager_update_head(struct wlr_output_manager_v1 *manager, if (current->adaptive_sync_enabled != next->adaptive_sync_enabled) { 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 // 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_adaptive_sync_enabled(output_state, 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(