mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-21 06:46:46 -04:00
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:
commit
5e9f77f4ff
11 changed files with 333 additions and 8 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
include/wlr/types/wlr_output_group.h
Normal file
32
include/wlr/types/wlr_output_group.h
Normal 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
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
169
types/wlr_output_group.c
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue