From 21fecd60153b0eb512ed16f562ca5f49ea4fd588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kr=C3=B6ner?= Date: Wed, 9 Jun 2021 22:35:35 +0200 Subject: [PATCH 1/5] Enable parsing of TILE info for DRM backend --- backend/drm/drm.c | 6 ++++++ backend/drm/properties.c | 1 + backend/drm/util.c | 35 ++++++++++++++++++++++++++++++++ include/backend/drm/properties.h | 3 ++- include/backend/drm/util.h | 3 +++ include/wlr/types/wlr_output.h | 12 +++++++++++ 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 3031d09b2..863861d6e 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1326,6 +1326,12 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, parse_edid(&wlr_conn->output, edid_len, edid); free(edid); + size_t tile_len = 0; + uint8_t *tile = get_drm_prop_blob(drm->fd, + wlr_conn->id, wlr_conn->props.tile, &tile_len); + parse_tile(&wlr_conn->output, tile_len, tile); + free(tile); + char *subconnector = NULL; if (wlr_conn->props.subconnector) { subconnector = get_drm_prop_enum(drm->fd, diff --git a/backend/drm/properties.c b/backend/drm/properties.c index f26965933..c4a97efaa 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -24,6 +24,7 @@ static const struct prop_info connector_info[] = { { "DPMS", INDEX(dpms) }, { "EDID", INDEX(edid) }, { "PATH", INDEX(path) }, + { "TILE", INDEX(tile) }, { "link-status", INDEX(link_status) }, { "non-desktop", INDEX(non_desktop) }, { "panel orientation", INDEX(panel_orientation) }, diff --git a/backend/drm/util.c b/backend/drm/util.c index 407c19a5e..b7ed7edaa 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -144,6 +144,41 @@ void parse_edid(struct wlr_output *restrict output, size_t len, const uint8_t *d } } +void parse_tile(struct wlr_output *restrict output, size_t len, const uint8_t *data) { + if (len > 0) { + int ret; + ret = sscanf((char*)data, "%d:%d:%d:%d:%d:%d:%d:%d", + &output->tile_info.group_id, + &output->tile_info.tile_is_single_monitor, + &output->tile_info.num_h_tile, + &output->tile_info.num_v_tile, + &output->tile_info.tile_h_loc, + &output->tile_info.tile_v_loc, + &output->tile_info.tile_h_size, + &output->tile_info.tile_v_size); + if(ret != 8) + wlr_log(WLR_ERROR, "Unable to understand tile information for " + "output %s", output->name); + else + wlr_log(WLR_INFO, "Output %s TILE information: " + "group ID %d, single monitor %d, total %d horizontal tiles, " + "total %d vertical tiles, horizontal tile %d, vertical tile " + "%d, width %d, height %d", + output->name, + output->tile_info.group_id, + output->tile_info.tile_is_single_monitor, + output->tile_info.num_h_tile, + output->tile_info.num_v_tile, + output->tile_info.tile_h_loc, + output->tile_info.tile_v_loc, + output->tile_info.tile_h_size, + output->tile_info.tile_v_size); + } + else + wlr_log(WLR_DEBUG, "No tile information available for output %s", + output->name); +} + const char *conn_get_name(uint32_t type_id) { switch (type_id) { case DRM_MODE_CONNECTOR_Unknown: return "Unknown"; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 10c1ccd09..0c5687e0e 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -20,12 +20,13 @@ union wlr_drm_connector_props { uint32_t subconnector; // not guaranteed to exist uint32_t non_desktop; uint32_t panel_orientation; // not guaranteed to exist + uint32_t tile; // atomic-modesetting only uint32_t crtc_id; }; - uint32_t props[4]; + uint32_t props[8]; }; union wlr_drm_crtc_props { diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index 6e97505c3..b17db0048 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -11,6 +11,9 @@ int32_t calculate_refresh_rate(const drmModeModeInfo *mode); // Populates the make/model/phys_{width,height} of output from the edid data void parse_edid(struct wlr_output *restrict output, size_t len, const uint8_t *data); +// Parses the TILE property +void parse_tile(struct wlr_output *restrict output, size_t len, + const uint8_t *data); // Returns the string representation of a DRM output type const char *conn_get_name(uint32_t type_id); diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 17f48311e..01d4a2fda 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -46,6 +46,17 @@ struct wlr_output_cursor { } events; }; +struct wlr_output_tile_info { + uint32_t group_id; + uint32_t tile_is_single_monitor; + uint32_t num_h_tile; + uint32_t num_v_tile; + uint32_t tile_h_loc; + uint32_t tile_v_loc; + uint32_t tile_h_size; + uint32_t tile_v_size; +}; + enum wlr_output_adaptive_sync_status { WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED, WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED, @@ -122,6 +133,7 @@ struct wlr_output { char model[16]; char serial[16]; int32_t phys_width, phys_height; // mm + struct wlr_output_tile_info tile_info; // Note: some backends may have zero modes struct wl_list modes; // wlr_output_mode::link From bdc33589227681b9ec23cf3b151c7be323eec915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kr=C3=B6ner?= Date: Sat, 26 Jun 2021 17:03:24 +0200 Subject: [PATCH 2/5] Add wlr_box to crop buffer for DRM output --- backend/drm/atomic.c | 32 +++++++++++++++++++++++++------- include/wlr/types/wlr_output.h | 13 +++++++++++++ types/output/output.c | 6 ++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 534879143..391db59ed 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -131,7 +131,7 @@ static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { atomic_add(atom, id, props->crtc_id, 0); } -static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, +static void set_plane_props(struct atomic *atom, struct wlr_box source_box, struct wlr_drm_plane *plane, uint32_t crtc_id, int32_t x, int32_t y) { uint32_t id = plane->id; const union wlr_drm_plane_props *props = &plane->props; @@ -141,12 +141,14 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, goto error; } - uint32_t width = fb->wlr_buf->width; - uint32_t height = fb->wlr_buf->height; + uint32_t width = source_box.width ? (uint32_t)source_box.width : + fb->wlr_buf->width; + uint32_t height = source_box.height ? (uint32_t)source_box.height : + fb->wlr_buf->height; // The src_* properties are in 16.16 fixed point - atomic_add(atom, id, props->src_x, 0); - atomic_add(atom, id, props->src_y, 0); + atomic_add(atom, id, props->src_x, (uint64_t)source_box.x << 16); + atomic_add(atom, id, props->src_y, (uint64_t)source_box.y << 16); atomic_add(atom, id, props->src_w, (uint64_t)width << 16); atomic_add(atom, id, props->src_h, (uint64_t)height << 16); atomic_add(atom, id, props->crtc_w, width); @@ -239,20 +241,36 @@ static bool atomic_crtc_commit(struct wlr_drm_connector *conn, atomic_add(&atom, crtc->id, crtc->props.mode_id, mode_id); atomic_add(&atom, crtc->id, crtc->props.active, active); if (active) { + struct wlr_box source_box; + if (state->committed & WLR_OUTPUT_STATE_SOURCE_BOX) { + /** + * Grab source box from output in order to crop the + * buffer. + */ + source_box = state->source_box; + } + else { + // Use dummy source box + source_box.x = source_box.y = source_box.width = + source_box.height = 0; + } if (crtc->props.gamma_lut != 0) { atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut); } if (crtc->props.vrr_enabled != 0) { atomic_add(&atom, crtc->id, crtc->props.vrr_enabled, vrr_enabled); } - set_plane_props(&atom, drm, crtc->primary, crtc->id, 0, 0); + set_plane_props(&atom, source_box, crtc->primary, crtc->id, 0, 0); if (crtc->primary->props.fb_damage_clips != 0) { atomic_add(&atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, fb_damage_clips); } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { - set_plane_props(&atom, drm, crtc->cursor, crtc->id, + // Ensure source_box is unset for cursor plane + source_box.x = source_box.y = source_box.width = + source_box.height = 0; + set_plane_props(&atom, source_box, crtc->cursor, crtc->id, conn->cursor_x, conn->cursor_y); } else { plane_disable(&atom, crtc->cursor); diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 01d4a2fda..316335493 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -16,6 +16,7 @@ #include #include #include +#include struct wlr_output_mode { int32_t width, height; @@ -72,6 +73,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_TRANSFORM = 1 << 5, WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6, WLR_OUTPUT_STATE_GAMMA_LUT = 1 << 7, + WLR_OUTPUT_STATE_SOURCE_BOX = 1 << 8, }; enum wlr_output_state_mode_type { @@ -89,6 +91,9 @@ struct wlr_output_state { float scale; enum wl_output_transform transform; bool adaptive_sync_enabled; + /* allow partial buffer scanout for tiling displays + * only valid if WLR_OUTPUT_STATE_SOURCE_BOX */ + struct wlr_box source_box; // source box for respective output // only valid if WLR_OUTPUT_STATE_BUFFER struct wlr_buffer *buffer; @@ -372,6 +377,14 @@ uint32_t wlr_output_preferred_read_format(struct wlr_output *output); */ void wlr_output_set_damage(struct wlr_output *output, pixman_region32_t *damage); +/** + * This can be used in case the output buffer is larger than the buffer that + * is supposed to be presented on the actual screen attached to the DRM + * connector. Current use case are hi-res tiling displays which use multiple + * DRM connectors to make up the full monitor. + */ +void wlr_output_set_source_box(struct wlr_output *output, + struct wlr_box source_box); /** * Test whether the pending output state would be accepted by the backend. If * this function returns true, `wlr_output_commit` can only fail due to a diff --git a/types/output/output.c b/types/output/output.c index 4e3f403ff..7d092abd9 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -714,6 +714,12 @@ void wlr_output_attach_buffer(struct wlr_output *output, output->pending.buffer = wlr_buffer_lock(buffer); } +void wlr_output_set_source_box(struct wlr_output *output, + struct wlr_box source_box) { + output->pending.source_box = source_box; + output->pending.committed |= WLR_OUTPUT_STATE_SOURCE_BOX; +} + void wlr_output_send_frame(struct wlr_output *output) { output->frame_pending = false; if (output->enabled) { From d1984032ab83d8a5dca35af8aa70a4d9e05dfde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kr=C3=B6ner?= Date: Fri, 9 Jul 2021 22:34:06 +0200 Subject: [PATCH 3/5] Change header to stable wlr_box interface --- include/wlr/types/wlr_output.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 316335493..78647cb97 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include struct wlr_output_mode { int32_t width, height; From 0c45125ac73ce3a55ba2d9d5ac4d529b5b5141ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kr=C3=B6ner?= Date: Sat, 13 Nov 2021 10:54:01 +0100 Subject: [PATCH 4/5] Fix errors from change in wlr_drm_connector_state --- backend/drm/atomic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 391db59ed..7c8ebf42d 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -142,9 +142,9 @@ static void set_plane_props(struct atomic *atom, struct wlr_box source_box, } uint32_t width = source_box.width ? (uint32_t)source_box.width : - fb->wlr_buf->width; + (uint32_t)fb->wlr_buf->width; uint32_t height = source_box.height ? (uint32_t)source_box.height : - fb->wlr_buf->height; + (uint32_t)fb->wlr_buf->height; // The src_* properties are in 16.16 fixed point atomic_add(atom, id, props->src_x, (uint64_t)source_box.x << 16); @@ -242,12 +242,12 @@ static bool atomic_crtc_commit(struct wlr_drm_connector *conn, atomic_add(&atom, crtc->id, crtc->props.active, active); if (active) { struct wlr_box source_box; - if (state->committed & WLR_OUTPUT_STATE_SOURCE_BOX) { + if (state->base->committed & WLR_OUTPUT_STATE_SOURCE_BOX) { /** * Grab source box from output in order to crop the * buffer. */ - source_box = state->source_box; + source_box = state->base->source_box; } else { // Use dummy source box From 52ef0b6a562a8d47fc99ae1a83900a6587cd99d4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 26 Jul 2021 14:22:44 +0200 Subject: [PATCH 5/5] output-group: new helper wlr_output_group allows compositors to group multiple outputs together and manage them as a single wlr_output. It's useful for output mirroring, and in the future for handling tiled displays. --- include/wlr/types/wlr_output_group.h | 32 +++++ types/meson.build | 1 + types/wlr_output_group.c | 169 +++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 include/wlr/types/wlr_output_group.h create mode 100644 types/wlr_output_group.c diff --git a/include/wlr/types/wlr_output_group.h b/include/wlr/types/wlr_output_group.h new file mode 100644 index 000000000..65e9221d1 --- /dev/null +++ b/include/wlr/types/wlr_output_group.h @@ -0,0 +1,32 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_OUTPUT_GROUP_H +#define WLR_TYPES_WLR_OUTPUT_GROUP_H + +#include + +struct wlr_output_group_child; + +struct wlr_output_group { + struct wlr_output base; + + // Private state + + struct wlr_output *main_output; + struct wl_list children; // wlr_output_group_child.link + + struct wl_listener main_output_destroy; +}; + +struct wlr_output_group *wlr_output_group_create(struct wlr_output *main_output); +struct wlr_output_group_child *wlr_output_group_add( + struct wlr_output_group *group, struct wlr_output *output); +void wlr_output_group_child_destroy(struct wlr_output_group_child *child); + +#endif diff --git a/types/meson.build b/types/meson.build index 89e834cec..6bf20a6e1 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,6 +44,7 @@ wlr_files += files( 'wlr_linux_dmabuf_v1.c', 'wlr_matrix.c', 'wlr_output_damage.c', + 'wlr_output_group.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', 'wlr_output_power_management_v1.c', diff --git a/types/wlr_output_group.c b/types/wlr_output_group.c new file mode 100644 index 000000000..b249fb09f --- /dev/null +++ b/types/wlr_output_group.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include + +struct wlr_output_group_child { + struct wlr_output *output; + struct wl_list link; // wlr_output_group.children + + struct wl_listener output_destroy; +}; + +static const struct wlr_output_impl output_impl; + +static struct wlr_output_group *group_from_output(struct wlr_output *output) { + assert(output->impl == &output_impl); + return (struct wlr_output_group *)output; +} + +static void group_destroy(struct wlr_output *output) { + struct wlr_output_group *group = group_from_output(output); + + wl_list_remove(&group->main_output_destroy.link); + + struct wlr_output_group_child *child, *child_tmp; + wl_list_for_each_safe(child, child_tmp, &group->children, link) { + wlr_output_group_child_destroy(child); + } + + free(group); +} + +static void output_apply(struct wlr_output *output, + struct wlr_output_state *state) { + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + wlr_output_attach_buffer(output, state->buffer); + } + // TODO: everything else +} + +static bool group_commit(struct wlr_output *output) { + struct wlr_output_group *group = group_from_output(output); + + output_apply(group->main_output, &output->pending); + + struct wlr_output_group_child *child; + wl_list_for_each(child, &group->children, link) { + output_apply(child->output, &output->pending); + } + + // TODO: perform a backend-wide commit if possible + if (!wlr_output_commit(group->main_output)) { + goto error; + } + + wl_list_for_each(child, &group->children, link) { + if (!wlr_output_commit(child->output)) { + goto error; + } + } + + // TODO: update our current state + + return true; + +error: + wlr_output_rollback(group->main_output); + + wl_list_for_each(child, &group->children, link) { + wlr_output_rollback(child->output); + } + + return false; +} + +static const struct wlr_drm_format_set *group_get_primary_formats( + struct wlr_output *output, uint32_t buffer_caps) { + struct wlr_output_group *group = group_from_output(output); + + // TODO: intersect primary formats from all children + return group->main_output->impl->get_primary_formats(group->main_output, buffer_caps); +} + +static const struct wlr_output_impl output_impl = { + .destroy = group_destroy, + .commit = group_commit, + .get_primary_formats = group_get_primary_formats, +}; + +static void group_handle_main_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_output_group *group = + wl_container_of(listener, group, main_output_destroy); + wlr_output_destroy(&group->base); +} + +struct wlr_output_group *wlr_output_group_create(struct wlr_output *main_output) { + struct wlr_output_group *group = calloc(1, sizeof(*group)); + if (group == NULL) { + return NULL; + } + + wlr_output_init(&group->base, main_output->backend, + &output_impl, main_output->display); + + wl_list_init(&group->children); + + group->main_output_destroy.notify = group_handle_main_output_destroy; + wl_signal_add(&main_output->events.destroy, &group->main_output_destroy); + + memcpy(&group->base.name, &main_output->name, sizeof(group->base.name)); + wlr_output_set_description(&group->base, main_output->description); + memcpy(&group->base.make, &main_output->make, sizeof(group->base.make)); + memcpy(&group->base.model, &main_output->model, sizeof(group->base.model)); + memcpy(&group->base.serial, &main_output->serial, sizeof(group->base.serial)); + group->base.phys_width = main_output->phys_width; + group->base.phys_height = main_output->phys_height; + group->base.modes = main_output->modes; + group->base.current_mode = main_output->current_mode; + group->base.width = main_output->width; + group->base.height = main_output->height; + group->base.refresh = main_output->refresh; + group->base.enabled = main_output->enabled; + group->base.scale = main_output->scale; + group->base.subpixel = main_output->subpixel; + group->base.transform = main_output->transform; + group->base.adaptive_sync_status = main_output->adaptive_sync_status; + + // TODO: listen to main output events and pass them through + + return group; +} + +static void child_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_output_group_child *child = + wl_container_of(listener, child, output_destroy); + wlr_output_group_child_destroy(child); +} + +struct wlr_output_group_child *wlr_output_group_add( + struct wlr_output_group *group, struct wlr_output *output) { + struct wlr_output_group_child *child; + wl_list_for_each(child, &group->children, link) { + if (child->output == output) { + return child; + } + } + + child = calloc(1, sizeof(*child)); + if (child == NULL) { + return NULL; + } + + child->output = output; + wl_list_insert(&group->children, &child->link); + + child->output_destroy.notify = child_handle_output_destroy; + wl_signal_add(&output->events.destroy, &child->output_destroy); + + return child; +} + +void wlr_output_group_child_destroy(struct wlr_output_group_child *child) { + wl_list_remove(&child->output_destroy.link); + wl_list_remove(&child->link); + free(child); +}