Merge branch 'display_tiling_clean_new' into 'master'

Draft: Display Tiling for DRM backend

See merge request wlroots/wlroots!3344
This commit is contained in:
Christian Kröner 2022-10-13 20:09:09 +00:00
commit 5e9f77f4ff
11 changed files with 333 additions and 8 deletions

View file

@ -170,7 +170,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;
@ -180,12 +180,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 :
(uint32_t)fb->wlr_buf->width;
uint32_t height = source_box.height ? (uint32_t)source_box.height :
(uint32_t)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);
@ -292,20 +294,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->base->committed & WLR_OUTPUT_STATE_SOURCE_BOX) {
/**
* Grab source box from output in order to crop the
* buffer.
*/
source_box = state->base->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);

View file

@ -1272,6 +1272,12 @@ void scan_drm_connectors(struct wlr_drm_backend *drm,
parse_edid(wlr_conn, 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, tile_len, tile);
free(tile);
char *subconnector = NULL;
if (wlr_conn->props.subconnector) {
subconnector = get_drm_prop_enum(drm->fd,

View file

@ -25,6 +25,7 @@ static const struct prop_info connector_info[] = {
{ "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) },
{ "PATH", INDEX(path) },
{ "TILE", INDEX(tile) },
{ "content type", INDEX(content_type) },
{ "link-status", INDEX(link_status) },
{ "max bpc", INDEX(max_bpc) },

View file

@ -134,6 +134,70 @@ void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data)
}
}
void parse_tile(struct wlr_drm_connector *conn, size_t len, const uint8_t *data) {
struct wlr_output *output = &conn->output;
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";
case DRM_MODE_CONNECTOR_VGA: return "VGA";
case DRM_MODE_CONNECTOR_DVII: return "DVI-I";
case DRM_MODE_CONNECTOR_DVID: return "DVI-D";
case DRM_MODE_CONNECTOR_DVIA: return "DVI-A";
case DRM_MODE_CONNECTOR_Composite: return "Composite";
case DRM_MODE_CONNECTOR_SVIDEO: return "SVIDEO";
case DRM_MODE_CONNECTOR_LVDS: return "LVDS";
case DRM_MODE_CONNECTOR_Component: return "Component";
case DRM_MODE_CONNECTOR_9PinDIN: return "DIN";
case DRM_MODE_CONNECTOR_DisplayPort: return "DP";
case DRM_MODE_CONNECTOR_HDMIA: return "HDMI-A";
case DRM_MODE_CONNECTOR_HDMIB: return "HDMI-B";
case DRM_MODE_CONNECTOR_TV: return "TV";
case DRM_MODE_CONNECTOR_eDP: return "eDP";
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
case DRM_MODE_CONNECTOR_DSI: return "DSI";
case DRM_MODE_CONNECTOR_DPI: return "DPI";
case DRM_MODE_CONNECTOR_WRITEBACK: return "Writeback";
#ifdef DRM_MODE_CONNECTOR_SPI
case DRM_MODE_CONNECTOR_SPI: return "SPI";
#endif
#ifdef DRM_MODE_CONNECTOR_USB
case DRM_MODE_CONNECTOR_USB: return "USB";
#endif
default: return "Unknown";
output->model = strdup(model_str);
if (serial_str[0] != '\0') {
output->serial = strdup(serial_str);

View file

@ -20,6 +20,7 @@ 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;
uint32_t content_type; // not guaranteed to exist
uint32_t max_bpc; // not guaranteed to exist
@ -27,7 +28,7 @@ union wlr_drm_connector_props {
uint32_t crtc_id;
};
uint32_t props[4];
uint32_t props[8];
};
union wlr_drm_crtc_props {

View file

@ -12,6 +12,8 @@ int32_t calculate_refresh_rate(const drmModeModeInfo *mode);
enum wlr_output_mode_aspect_ratio get_picture_aspect_ratio(const drmModeModeInfo *mode);
// Populates the make/model/phys_{width,height} of output from the edid data
void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data);
// Parses the TILE property
void parse_tile(struct wlr_drm_connector *conn, size_t len, const uint8_t *data);
// Part of match_obj
enum {

View file

@ -16,6 +16,7 @@
#include <wayland-util.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/addon.h>
#include <wlr/util/box.h>
enum wlr_output_mode_aspect_ratio {
WLR_OUTPUT_MODE_ASPECT_RATIO_NONE,
@ -51,6 +52,17 @@ struct wlr_output_cursor {
struct wl_listener surface_destroy;
};
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,
@ -66,6 +78,7 @@ enum wlr_output_state_field {
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6,
WLR_OUTPUT_STATE_GAMMA_LUT = 1 << 7,
WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8,
WLR_OUTPUT_STATE_SOURCE_BOX = 1 << 9,
WLR_OUTPUT_STATE_SUBPIXEL = 1 << 9,
};
@ -87,6 +100,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
uint32_t render_format;
enum wl_output_subpixel subpixel;
@ -131,6 +147,7 @@ struct wlr_output {
char *description; // may be NULL
char *make, *model, *serial; // may be NULL
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
@ -416,6 +433,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

View file

@ -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 <wlr/types/wlr_output.h>
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

View file

@ -54,6 +54,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',

View file

@ -873,6 +873,12 @@ void wlr_output_attach_buffer(struct wlr_output *output,
output_state_attach_buffer(&output->pending, 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) {

169
types/wlr_output_group.c Normal file
View file

@ -0,0 +1,169 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/types/wlr_output_group.h>
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);
}