aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2017-08-07 08:01:32 -0400
committerGitHub <noreply@github.com>2017-08-07 08:01:32 -0400
commitab063c3936ff8dd0ee34388cbd260dccc59661df (patch)
treed7ad82aa3e5b3dbdfa494a7b7751b4dbd7c7ad9c
parentf95c02eebe54785a4f64332f7e574dddff7e3669 (diff)
parentd09ca20a4dccd7696423fc9f17acc88b7b3a7986 (diff)
Merge pull request #44 from ascent12/drm
DRM plane support, refactoring, and other changes
-rw-r--r--backend/drm/backend.c99
-rw-r--r--backend/drm/drm-properties.c143
-rw-r--r--backend/drm/drm-util.c267
-rw-r--r--backend/drm/drm.c1011
-rw-r--r--backend/egl.c2
-rw-r--r--backend/meson.build2
-rw-r--r--examples/shared.c2
-rw-r--r--include/backend/drm-properties.h62
-rw-r--r--include/backend/drm-util.h37
-rw-r--r--include/backend/drm.h108
-rw-r--r--include/wlr/render/matrix.h6
-rw-r--r--render/matrix.c60
-rw-r--r--types/wlr_output.c63
13 files changed, 1319 insertions, 543 deletions
diff --git a/backend/drm/backend.c b/backend/drm/backend.c
index a2c71317..68eae890 100644
--- a/backend/drm/backend.c
+++ b/backend/drm/backend.c
@@ -15,24 +15,27 @@
#include "backend/udev.h"
#include "backend/drm.h"
-static bool wlr_drm_backend_init(struct wlr_backend_state *state) {
- wlr_drm_scan_connectors(state);
+static bool wlr_drm_backend_init(struct wlr_backend_state *drm) {
+ wlr_drm_scan_connectors(drm);
return true;
}
-static void wlr_drm_backend_destroy(struct wlr_backend_state *state) {
- if (!state) {
+static void wlr_drm_backend_destroy(struct wlr_backend_state *drm) {
+ if (!drm) {
return;
}
- for (size_t i = 0; state->outputs && i < state->outputs->length; ++i) {
- struct wlr_output_state *output = state->outputs->items[i];
- wlr_output_destroy(output->wlr_output);
+
+ for (size_t i = 0; drm->outputs && i < drm->outputs->length; ++i) {
+ struct wlr_output_state *output = drm->outputs->items[i];
+ wlr_output_destroy(output->base);
}
- wlr_udev_signal_remove(state->udev, &state->drm_invalidated);
- wlr_drm_renderer_free(&state->renderer);
- wlr_session_close_file(state->session, state->fd);
- wl_event_source_remove(state->drm_event);
- free(state);
+
+ wlr_udev_signal_remove(drm->udev, &drm->drm_invalidated);
+ wlr_drm_renderer_free(&drm->renderer);
+ wlr_drm_resources_free(drm);
+ wlr_session_close_file(drm->session, drm->fd);
+ wl_event_source_remove(drm->drm_event);
+ free(drm);
}
static struct wlr_backend_impl backend_impl = {
@@ -50,14 +53,10 @@ static void session_signal(struct wl_listener *listener, void *data) {
for (size_t i = 0; i < drm->outputs->length; ++i) {
struct wlr_output_state *output = drm->outputs->items[i];
wlr_drm_output_start_renderer(output);
+ wlr_drm_crtc_set_cursor(drm, output->crtc);
}
} else {
wlr_log(L_INFO, "DRM fd paused");
-
- for (size_t i = 0; i < drm->outputs->length; ++i) {
- struct wlr_output_state *output = drm->outputs->items[i];
- wlr_drm_output_pause_renderer(output);
- }
}
}
@@ -76,64 +75,68 @@ static void drm_invalidated(struct wl_listener *listener, void *data) {
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
struct wlr_session *session, struct wlr_udev *udev, int gpu_fd) {
- assert(display && session && gpu_fd > 0);
+ assert(display && session && gpu_fd >= 0);
char *name = drmGetDeviceNameFromFd2(gpu_fd);
drmVersion *version = drmGetVersion(gpu_fd);
-
wlr_log(L_INFO, "Initalizing DRM backend for %s (%s)", name, version->name);
-
free(name);
drmFreeVersion(version);
- struct wlr_backend_state *state = calloc(1, sizeof(struct wlr_backend_state));
- if (!state) {
- wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno));
+ struct wlr_backend_state *drm = calloc(1, sizeof(*drm));
+ if (!drm) {
+ wlr_log_errno(L_ERROR, "Allocation failed");
return NULL;
}
- struct wlr_backend *backend = wlr_backend_create(&backend_impl, state);
+ struct wlr_backend *backend = wlr_backend_create(&backend_impl, drm);
if (!backend) {
- wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno));
+ wlr_log_errno(L_ERROR, "Allocation failed");
return NULL;
}
- state->backend = backend;
- state->session = session;
- state->udev = udev;
- state->outputs = list_create();
- if (!state->outputs) {
+ drm->base = backend;
+ drm->session = session;
+ drm->udev = udev;
+ drm->outputs = list_create();
+ if (!drm->outputs) {
wlr_log(L_ERROR, "Failed to allocate list");
goto error_backend;
}
- state->fd = gpu_fd;
+ drm->fd = gpu_fd;
struct stat st;
- if (fstat(state->fd, &st) < 0) {
- wlr_log(L_ERROR, "Stat failed: %s", strerror(errno));
+ if (fstat(drm->fd, &st) < 0) {
+ wlr_log_errno(L_ERROR, "Stat failed");
}
- state->dev = st.st_rdev;
+ drm->dev = st.st_rdev;
- state->drm_invalidated.notify = drm_invalidated;
- wlr_udev_signal_add(udev, state->dev, &state->drm_invalidated);
+ drm->drm_invalidated.notify = drm_invalidated;
+ wlr_udev_signal_add(udev, drm->dev, &drm->drm_invalidated);
- state->display = display;
+ drm->display = display;
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
- state->drm_event = wl_event_loop_add_fd(event_loop, state->fd,
+ drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd,
WL_EVENT_READABLE, wlr_drm_event, NULL);
- if (!state->drm_event) {
+ if (!drm->drm_event) {
wlr_log(L_ERROR, "Failed to create DRM event source");
goto error_fd;
}
- state->session_signal.notify = session_signal;
- wl_signal_add(&session->session_signal, &state->session_signal);
+ drm->session_signal.notify = session_signal;
+ wl_signal_add(&session->session_signal, &drm->session_signal);
+
+ if (!wlr_drm_check_features(drm)) {
+ goto error_event;
+ }
+
+ if (!wlr_drm_resources_init(drm)) {
+ goto error_event;
+ }
- // TODO: what is the difference between the per-output renderer and this
- // one?
- if (!wlr_drm_renderer_init(&state->renderer, state->fd)) {
+ if (!wlr_drm_renderer_init(&drm->renderer, drm->fd)) {
wlr_log(L_ERROR, "Failed to initialize renderer");
goto error_event;
}
@@ -141,12 +144,12 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
return backend;
error_event:
- wl_event_source_remove(state->drm_event);
+ wl_event_source_remove(drm->drm_event);
error_fd:
- wlr_session_close_file(state->session, state->fd);
- list_free(state->outputs);
+ wlr_session_close_file(drm->session, drm->fd);
+ list_free(drm->outputs);
error_backend:
- free(state);
+ free(drm);
free(backend);
return NULL;
}
diff --git a/backend/drm/drm-properties.c b/backend/drm/drm-properties.c
new file mode 100644
index 00000000..1b6e745f
--- /dev/null
+++ b/backend/drm/drm-properties.c
@@ -0,0 +1,143 @@
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <wlr/util/log.h>
+#include "backend/drm-properties.h"
+
+/*
+ * Creates a mapping between property names and an array index where to store
+ * the ids. The prop_info arrays must be sorted by name, as bsearch is used to
+ * search them.
+ */
+struct prop_info {
+ const char *name;
+ size_t index;
+};
+
+static const struct prop_info connector_info[] = {
+#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
+ { "CRTC_ID", INDEX(crtc_id) },
+ { "DPMS", INDEX(dpms) },
+ { "EDID", INDEX(edid) },
+#undef INDEX
+};
+
+static const struct prop_info crtc_info[] = {
+#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
+ { "rotation", INDEX(rotation) },
+ { "scaling mode", INDEX(scaling_mode) },
+#undef INDEX
+};
+
+static const struct prop_info plane_info[] = {
+#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t))
+ { "CRTC_H", INDEX(crtc_h) },
+ { "CRTC_ID", INDEX(crtc_id) },
+ { "CRTC_W", INDEX(crtc_w) },
+ { "CRTC_X", INDEX(crtc_x) },
+ { "CRTC_Y", INDEX(crtc_y) },
+ { "FB_ID", INDEX(fb_id) },
+ { "SRC_H", INDEX(src_h) },
+ { "SRC_W", INDEX(src_w) },
+ { "SRC_X", INDEX(src_x) },
+ { "SRC_Y", INDEX(src_y) },
+ { "type", INDEX(type) },
+#undef INDEX
+};
+
+static int cmp_prop_info(const void *arg1, const void *arg2) {
+ const char *key = arg1;
+ const struct prop_info *elem = arg2;
+
+ return strcmp(key, elem->name);
+}
+
+static bool scan_properties(int fd, uint32_t id, uint32_t type, uint32_t *result,
+ const struct prop_info *info, size_t info_len) {
+ drmModeObjectProperties *props = drmModeObjectGetProperties(fd, id, type);
+ if (!props) {
+ wlr_log_errno(L_ERROR, "Failed to get DRM object properties");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < props->count_props; ++i) {
+ drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]);
+ if (!prop) {
+ wlr_log_errno(L_ERROR, "Failed to get DRM object property");
+ continue;
+ }
+
+ const struct prop_info *p =
+ bsearch(prop->name, info, info_len, sizeof(info[0]), cmp_prop_info);
+ if (p) {
+ result[p->index] = prop->prop_id;
+ }
+
+ drmModeFreeProperty(prop);
+ }
+
+ drmModeFreeObjectProperties(props);
+ return true;
+}
+
+bool wlr_drm_get_connector_props(int fd, uint32_t id, union wlr_drm_connector_props *out) {
+ return scan_properties(fd, id, DRM_MODE_OBJECT_CONNECTOR, out->props,
+ connector_info, sizeof(connector_info) / sizeof(connector_info[0]));
+}
+
+bool wlr_drm_get_crtc_props(int fd, uint32_t id, union wlr_drm_crtc_props *out) {
+ return scan_properties(fd, id, DRM_MODE_OBJECT_CRTC, out->props,
+ crtc_info, sizeof(crtc_info) / sizeof(crtc_info[0]));
+}
+
+bool wlr_drm_get_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out) {
+ return scan_properties(fd, id, DRM_MODE_OBJECT_PLANE, out->props,
+ plane_info, sizeof(plane_info) / sizeof(plane_info[0]));
+}
+
+bool wlr_drm_get_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret) {
+ drmModeObjectProperties *props = drmModeObjectGetProperties(fd, obj, DRM_MODE_OBJECT_ANY);
+ if (!props) {
+ return false;
+ }
+
+ bool found = false;
+
+ for (uint32_t i = 0; i < props->count_props; ++i) {
+ if (props->props[i] == prop) {
+ *ret = props->prop_values[i];
+ found = true;
+ break;
+ }
+ }
+
+ drmModeFreeObjectProperties(props);
+ return found;
+}
+
+void *wlr_drm_get_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len) {
+ uint64_t blob_id;
+ if (!wlr_drm_get_prop(fd, obj, prop, &blob_id)) {
+ return NULL;
+ }
+
+ drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id);
+ if (!blob) {
+ return NULL;
+ }
+
+ void *ptr = malloc(blob->length);
+ if (!ptr) {
+ drmModeFreePropertyBlob(blob);
+ return NULL;
+ }
+
+ memcpy(ptr, blob->data, blob->length);
+ *ret_len = blob->length;
+
+ drmModeFreePropertyBlob(blob);
+ return ptr;
+}
diff --git a/backend/drm/drm-util.c b/backend/drm/drm-util.c
new file mode 100644
index 00000000..6159b5de
--- /dev/null
+++ b/backend/drm/drm-util.c
@@ -0,0 +1,267 @@
+#include <stdio.h>
+#include <string.h>
+#include <drm.h>
+#include <drm_mode.h>
+#include "backend/drm-util.h"
+
+int32_t calculate_refresh_rate(drmModeModeInfo *mode) {
+ int32_t refresh = (mode->clock * 1000000LL / mode->htotal +
+ mode->vtotal / 2) / mode->vtotal;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ refresh *= 2;
+ }
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+ refresh /= 2;
+ }
+
+ if (mode->vscan > 1) {
+ refresh /= mode->vscan;
+ }
+
+ return refresh;
+}
+
+// Constructed from http://edid.tv/manufacturer
+static const char *get_manufacturer(uint16_t id) {
+#define ID(a, b, c) ((a & 0x1f) << 10) | ((b & 0x1f) << 5) | (c & 0x1f)
+ switch (id) {
+ case ID('A', 'A', 'A'): return "Avolites Ltd";
+ case ID('A', 'C', 'I'): return "Ancor Communications Inc";
+ case ID('A', 'C', 'R'): return "Acer Technologies";
+ case ID('A', 'P', 'P'): return "Apple Computer Inc";
+ case ID('B', 'N', 'O'): return "Bang & Olufsen";
+ case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
+ case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
+ case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
+ case ID('D', 'E', 'L'): return "Dell Inc.";
+ case ID('D', 'O', 'N'): return "DENON, Ltd.";
+ case ID('E', 'N', 'C'): return "Eizo Nanao Corporation";
+ case ID('E', 'P', 'H'): return "Epiphan Systems Inc.";
+ case ID('F', 'U', 'S'): return "Fujitsu Siemens Computers GmbH";
+ case ID('G', 'S', 'M'): return "Goldstar Company Ltd";
+ case ID('H', 'I', 'Q'): return "Kaohsiung Opto Electronics Americas, Inc.";
+ case ID('H', 'S', 'D'): return "HannStar Display Corp";
+ case ID('H', 'W', 'P'): return "Hewlett Packard";
+ case ID('I', 'N', 'T'): return "Interphase Corporation";
+ case ID('I', 'V', 'M'): return "Iiyama North America";
+ case ID('L', 'E', 'N'): return "Lenovo Group Limited";
+ case ID('M', 'A', 'X'): return "Rogen Tech Distribution Inc";
+ case ID('M', 'E', 'G'): return "Abeam Tech Ltd";
+ case ID('M', 'E', 'I'): return "Panasonic Industry Company";
+ case ID('M', 'T', 'C'): return "Mars-Tech Corporation";
+ case ID('M', 'T', 'X'): return "Matrox";
+ case ID('N', 'E', 'C'): return "NEC Corporation";
+ case ID('O', 'N', 'K'): return "ONKYO Corporation";
+ case ID('O', 'R', 'N'): return "ORION ELECTRIC CO., LTD.";
+ case ID('O', 'T', 'M'): return "Optoma Corporation";
+ case ID('O', 'V', 'R'): return "Oculus VR, Inc.";
+ case ID('P', 'H', 'L'): return "Philips Consumer Electronics Company";
+ case ID('P', 'I', 'O'): return "Pioneer Electronic Corporation";
+ case ID('P', 'N', 'R'): return "Planar Systems, Inc.";
+ case ID('Q', 'D', 'S'): return "Quanta Display Inc.";
+ case ID('S', 'A', 'M'): return "Samsung Electric Company";
+ case ID('S', 'E', 'C'): return "Seiko Epson Corporation";
+ case ID('S', 'H', 'P'): return "Sharp Corporation";
+ case ID('S', 'I', 'I'): return "Silicon Image, Inc.";
+ case ID('S', 'N', 'Y'): return "Sony";
+ case ID('T', 'O', 'P'): return "Orion Communications Co., Ltd.";
+ case ID('T', 'S', 'B'): return "Toshiba America Info Systems Inc";
+ case ID('T', 'S', 'T'): return "Transtream Inc";
+ case ID('U', 'N', 'K'): return "Unknown";
+ case ID('V', 'I', 'Z'): return "VIZIO, Inc";
+ case ID('V', 'S', 'C'): return "ViewSonic Corporation";
+ case ID('Y', 'M', 'H'): return "Yamaha Corporation";
+ default: return "Unknown";
+ }
+#undef ID
+}
+
+/* See https://en.wikipedia.org/wiki/Extended_Display_Identification_Data for layout of EDID data.
+ * We don't parse the EDID properly. We just expect to receive valid data.
+ */
+void parse_edid(struct wlr_output *restrict output, size_t len, const uint8_t *data) {
+ if (!data || len < 128) {
+ snprintf(output->make, sizeof(output->make), "<Unknown>");
+ snprintf(output->model, sizeof(output->model), "<Unknown>");
+ return;
+ }
+
+ uint16_t id = (data[8] << 8) | data[9];
+ snprintf(output->make, sizeof(output->make), "%s", get_manufacturer(id));
+
+ output->phys_width = ((data[68] & 0xf0) << 4) | data[66];
+ output->phys_height = ((data[68] & 0x0f) << 8) | data[67];
+
+ for (size_t i = 72; i <= 108; i += 18) {
+ uint16_t flag = (data[i] << 8) | data[i + 1];
+ if (flag == 0 && data[i + 3] == 0xFC) {
+ sprintf(output->model, "%.13s", &data[i + 5]);
+
+ // Monitor names are terminated by newline if they're too short
+ char *nl = strchr(output->model, '\n');
+ if (nl) {
+ *nl = '\0';
+ }
+
+ break;
+ }
+ }
+}
+
+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";
+ default: return "Unknown";
+ }
+}
+
+static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
+ for (size_t i = 0; i < n; ++i) {
+ if (arr[i] == key) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Store all of the non-recursive state in a struct, so we aren't literally
+ * passing 12 arguments to a function.
+ */
+struct match_state {
+ const size_t num_objs;
+ const uint32_t *restrict objs;
+ const size_t num_res;
+ size_t score;
+ size_t replaced;
+ uint32_t *restrict res;
+ uint32_t *restrict best;
+ const uint32_t *restrict orig;
+ bool exit_early;
+};
+
+/*
+ * skips: The number of SKIP elements encountered so far.
+ * score: The number of resources we've matched so far.
+ * replaced: The number of changes from the original solution.
+ * i: The index of the current element.
+ *
+ * This tries to match a solution as close to st->orig as it can.
+ *
+ * Returns whether we've set a new best element with this solution.
+ */
+static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_t replaced, size_t i) {
+ // Finished
+ if (i >= st->num_res) {
+ if (score > st->score || (score == st->score && replaced < st->replaced)) {
+ st->score = score;
+ st->replaced = replaced;
+ memcpy(st->best, st->res, sizeof st->best[0] * st->num_res);
+
+ if (st->score == st->num_objs && st->replaced == 0) {
+ st->exit_early = true;
+ }
+ st->exit_early = (st->score == st->num_res - skips
+ || st->score == st->num_objs)
+ && st->replaced == 0;
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (st->orig[i] == SKIP) {
+ st->res[i] = SKIP;
+ return match_obj_(st, skips + 1, score, replaced, i + 1);
+ }
+
+ /*
+ * Attempt to use the current solution first, to try and avoid
+ * recalculating everything
+ */
+
+ if (st->orig[i] != UNMATCHED && !is_taken(i, st->res, st->orig[i])) {
+ st->res[i] = st->orig[i];
+ if (match_obj_(st, skips, score + 1, replaced, i + 1)) {
+ return true;
+ }
+ }
+
+ if (st->orig[i] != UNMATCHED) {
+ ++replaced;
+ }
+
+ bool is_best = false;
+ for (st->res[i] = 0; st->res[i] < st->num_objs; ++st->res[i]) {
+ // We tried this earlier
+ if (st->res[i] == st->orig[i]) {
+ continue;
+ }
+
+ // Not compatable
+ if (!(st->objs[st->res[i]] & (1 << i))) {
+ continue;
+ }
+
+ // Already taken
+ if (is_taken(i, st->res, st->res[i])) {
+ continue;
+ }
+
+ if (match_obj_(st, skips, score + 1, replaced, i + 1)) {
+ is_best = true;
+ }
+
+ if (st->exit_early) {
+ return true;
+ }
+ }
+
+ if (is_best) {
+ return true;
+ }
+
+ // Maybe this resource can't be matched
+ st->res[i] = UNMATCHED;
+ return match_obj_(st, skips, score, replaced, i + 1);
+}
+
+size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs],
+ size_t num_res, const uint32_t res[static restrict num_res],
+ uint32_t out[static restrict num_res]) {
+ uint32_t solution[num_res];
+
+ struct match_state st = {
+ .num_objs = num_objs,
+ .num_res = num_res,
+ .score = 0,
+ .replaced = SIZE_MAX,
+ .objs = objs,
+ .res = solution,
+ .best = out,
+ .orig = res,
+ .exit_early = false,
+ };
+
+ match_obj_(&st, 0, 0, 0, 0);
+ return st.score;
+}
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index 6e969788..700eb554 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -10,32 +10,149 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gbm.h>
-#include <GLES3/gl3.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
#include <wayland-server.h>
#include <wlr/backend/interface.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
+#include <wlr/render/matrix.h>
+#include <wlr/render/gles2.h>
+#include <wlr/render.h>
#include "backend/drm.h"
+#include "backend/drm-util.h"
-static const char *conn_name[] = {
- [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
- [DRM_MODE_CONNECTOR_VGA] = "VGA",
- [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
- [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
- [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
- [DRM_MODE_CONNECTOR_Composite] = "Composite",
- [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
- [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
- [DRM_MODE_CONNECTOR_Component] = "Component",
- [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
- [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
- [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
- [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
- [DRM_MODE_CONNECTOR_TV] = "TV",
- [DRM_MODE_CONNECTOR_eDP] = "eDP",
- [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
- [DRM_MODE_CONNECTOR_DSI] = "DSI",
-};
+bool wlr_drm_check_features(struct wlr_backend_state *drm) {
+ if (drmSetClientCap(drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
+ wlr_log(L_INFO, "DRM universal planes unsupported");
+ return false;
+ }
+
+ if (drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
+ wlr_log(L_INFO, "Atomic modesetting unsupported");
+ }
+
+ return true;
+}
+
+static int cmp_plane(const void *arg1, const void *arg2)
+{
+ const struct wlr_drm_plane *a = arg1;
+ const struct wlr_drm_plane *b = arg2;
+
+ return (int)a->type - (int)b->type;
+}
+
+static bool init_planes(struct wlr_backend_state *drm)
+{
+ drmModePlaneRes *plane_res = drmModeGetPlaneResources(drm->fd);
+ if (!plane_res) {
+ wlr_log_errno(L_ERROR, "Failed to get DRM plane resources");
+ return false;
+ }
+
+ wlr_log(L_INFO, "Found %"PRIu32" DRM planes", plane_res->count_planes);
+
+ if (plane_res->count_planes == 0) {
+ drmModeFreePlaneResources(plane_res);
+ return true;
+ }
+
+ drm->num_planes = plane_res->count_planes;
+ drm->planes = calloc(drm->num_planes, sizeof(*drm->planes));
+ if (!drm->planes) {
+ wlr_log_errno(L_ERROR, "Allocation failed");
+ goto error_res;
+ }
+
+ for (size_t i = 0; i < drm->num_planes; ++i) {
+ struct wlr_drm_plane *p = &drm->planes[i];
+
+ drmModePlane *plane = drmModeGetPlane(drm->fd, plane_res->planes[i]);
+ if (!plane) {
+ wlr_log_errno(L_ERROR, "Failed to get DRM plane");
+ goto error_planes;
+ }
+
+ p->id = plane->plane_id;
+ p->possible_crtcs = plane->possible_crtcs;
+ uint64_t type;
+
+ if (!wlr_drm_get_plane_props(drm->fd, p->id, &p->props) ||
+ !wlr_drm_get_prop(drm->fd, p->id, p->props.type, &type)) {
+ drmModeFreePlane(plane);
+ goto error_planes;
+ }
+
+ p->type = type;
+ drm->num_type_planes[type]++;
+
+ drmModeFreePlane(plane);
+ }
+
+ wlr_log(L_INFO, "(%zu overlay, %zu primary, %zu cursor)",
+ drm->num_overlay_planes, drm->num_primary_planes, drm->num_cursor_planes);
+
+ qsort(drm->planes, drm->num_planes, sizeof(*drm->planes), cmp_plane);
+
+ drm->overlay_planes = drm->planes;
+ drm->primary_planes = drm->overlay_planes + drm->num_overlay_planes;
+ drm->cursor_planes = drm->primary_planes + drm->num_primary_planes;
+
+ return true;
+
+error_planes:
+ free(drm->planes);
+error_res:
+ drmModeFreePlaneResources(plane_res);
+ return false;
+}
+
+bool wlr_drm_resources_init(struct wlr_backend_state *drm) {
+ drmModeRes *res = drmModeGetResources(drm->fd);
+ if (!res) {
+ wlr_log_errno(L_ERROR, "Failed to get DRM resources");
+ return false;
+ }
+
+ wlr_log(L_INFO, "Found %d DRM CRTCs", res->count_crtcs);
+
+ drm->num_crtcs = res->count_crtcs;
+ drm->crtcs = calloc(drm->num_crtcs, sizeof(drm->crtcs[0]));
+ if (!drm->crtcs) {
+ wlr_log_errno(L_ERROR, "Allocation failed");
+ goto error_res;
+ }
+
+ for (size_t i = 0; i < drm->num_crtcs; ++i) {
+ struct wlr_drm_crtc *crtc = &drm->crtcs[i];
+ crtc->id = res->crtcs[i];
+ wlr_drm_get_crtc_props(drm->fd, crtc->id, &crtc->props);
+ }
+
+ if (!init_planes(drm)) {
+ goto error_crtcs;
+ }
+
+ drmModeFreeResources(res);
+
+ return true;
+
+error_crtcs:
+ free(drm->crtcs);
+error_res:
+ drmModeFreeResources(res);
+ return false;
+}
+
+void wlr_drm_resources_free(struct wlr_backend_state *drm) {
+ if (!drm) {
+ return;
+ }
+
+ free(drm->crtcs);
+ free(drm->planes);
+}
bool wlr_drm_renderer_init(struct wlr_drm_renderer *renderer, int fd) {
renderer->gbm = gbm_create_device(fd);
@@ -57,318 +174,493 @@ void wlr_drm_renderer_free(struct wlr_drm_renderer *renderer) {
if (!renderer) {
return;
}
+
wlr_egl_free(&renderer->egl);
gbm_device_destroy(renderer->gbm);
}
-static void free_fb(struct gbm_bo *bo, void *data) {
- uint32_t *id = data;
+static bool wlr_drm_plane_renderer_init(struct wlr_drm_renderer *renderer,
+ struct wlr_drm_plane *plane, uint32_t width, uint32_t height, uint32_t flags) {
+ if (plane->width == width && plane->height == height) {
+ return true;
+ }
+
+ plane->width = width;
+ plane->height = height;
- if (id && *id) {
- drmModeRmFB(gbm_bo_get_fd(bo), *id);
+ plane->gbm = gbm_surface_create(renderer->gbm, width, height,
+ GBM_FORMAT_ARGB8888, GBM_BO_USE_RENDERING | flags);
+ if (!plane->gbm) {
+ wlr_log_errno(L_ERROR, "Failed to create GBM surface for plane");
+ return false;
+ }
+
+ plane->egl = wlr_egl_create_surface(&renderer->egl, plane->gbm);
+ if (plane->egl == EGL_NO_SURFACE) {
+ wlr_log(L_ERROR, "Failed to create EGL surface for plane");
+ return false;
}
- free(id);
+ return true;
+}
+
+static void wlr_drm_plane_renderer_free(struct wlr_drm_renderer *renderer,
+ struct wlr_drm_plane *plane) {
+ if (!renderer || !plane) {
+ return;
+ }
+
+ if (plane->front) {
+ gbm_surface_release_buffer(plane->gbm, plane->front);
+ }
+ if (plane->back) {
+ gbm_surface_release_buffer(plane->gbm, plane->back);
+ }
+
+ if (plane->egl) {
+ eglDestroySurface(renderer->egl.display, plane->egl);
+ }
+ if (plane->gbm) {
+ gbm_surface_destroy(plane->gbm);
+ }
+
+ if (plane->wlr_surf) {
+ wlr_surface_destroy(plane->wlr_surf);
+ }
+ if (plane->wlr_rend) {
+ wlr_renderer_destroy(plane->wlr_rend);
+ }
+ if (plane->cursor_bo) {
+ gbm_bo_destroy(plane->cursor_bo);
+ }
+
+ plane->width = 0;
+ plane->height = 0;
+ plane->egl = EGL_NO_SURFACE;
+ plane->gbm = NULL;
+ plane->front = NULL;
+ plane->back = NULL;
+ plane->wlr_rend = NULL;
+ plane->wlr_surf = NULL;
+ plane->cursor_bo = NULL;
}
-static uint32_t get_fb_for_bo(int fd, struct gbm_bo *bo) {
- uint32_t *id = gbm_bo_get_user_data(bo);
+static void free_fb(struct gbm_bo *bo, void *data) {
+ uint32_t id = (uintptr_t)data;
if (id) {
- return *id;
+ struct gbm_device *gbm = gbm_bo_get_device(bo);
+ drmModeRmFB(gbm_device_get_fd(gbm), id);
}
+}
- id = calloc(1, sizeof *id);
- if (!id) {
- wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno));
- return 0;
+static uint32_t get_fb_for_bo(struct gbm_bo *bo) {
+ uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
+ if (id) {
+ return id;
}
- drmModeAddFB(fd, gbm_bo_get_width(bo), gbm_bo_get_height(bo), 24, 32,
- gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, id);
+ struct gbm_device *gbm = gbm_bo_get_device(bo);
+ drmModeAddFB(gbm_device_get_fd(gbm), gbm_bo_get_width(bo), gbm_bo_get_height(bo),
+ 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &id);
+
+ gbm_bo_set_user_data(bo, (void *)(uintptr_t)id, free_fb);
+
+ return id;
+}
+
+static void wlr_drm_plane_make_current(struct wlr_drm_renderer *renderer,
+ struct wlr_drm_plane *plane) {
+ eglMakeCurrent(renderer->egl.display, plane->egl, plane->egl,
+ renderer->egl.context);
+}
+
+static void wlr_drm_plane_swap_buffers(struct wlr_drm_renderer *renderer,
+ struct wlr_drm_plane *plane) {
+ if (plane->front) {
+ gbm_surface_release_buffer(plane->gbm, plane->front);
+ }
- gbm_bo_set_user_data(bo, id, free_fb);
+ eglSwapBuffers(renderer->egl.display, plane->egl);
- return *id;
+ plane->front = plane->back;
+ plane->back = gbm_surface_lock_front_buffer(plane->gbm);
}
static void wlr_drm_output_make_current(struct wlr_output_state *output) {
- struct wlr_drm_renderer *renderer = output->renderer;
- eglMakeCurrent(renderer->egl.display, output->egl,
- output->egl, renderer->egl.context);
+ wlr_drm_plane_make_current(output->renderer, output->crtc->primary);
}
static void wlr_drm_output_swap_buffers(struct wlr_output_state *output) {
struct wlr_drm_renderer *renderer = output->renderer;
+ struct wlr_drm_crtc *crtc = output->crtc;
+ struct wlr_drm_plane *plane = crtc->primary;
+
+ wlr_drm_plane_swap_buffers(renderer, plane);
+ uint32_t fb_id = get_fb_for_bo(plane->back);
+
+ drmModePageFlip(renderer->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, output);
+ output->pageflip_pending = true;
+}
- if (!eglSwapBuffers(renderer->egl.display, output->egl)) {
+void wlr_drm_output_start_renderer(struct wlr_output_state *output) {
+ if (output->state != WLR_DRM_OUTPUT_CONNECTED) {
return;
}
- struct gbm_bo *bo = gbm_surface_lock_front_buffer(output->gbm);
+
+ struct wlr_drm_renderer *renderer = output->renderer;
+ struct wlr_output_mode *mode = output->base->current_mode;
+ struct wlr_drm_crtc *crtc = output->crtc;
+ struct wlr_drm_plane *plane = crtc->primary;
+
+ struct gbm_bo *bo = plane->front;
if (!bo) {
- return;
+ // Render a black frame to start the rendering loop
+ wlr_drm_plane_make_current(renderer, plane);
+ glViewport(0, 0, plane->width, plane->height);
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ wlr_drm_plane_swap_buffers(renderer, plane);
+
+ bo = plane->back;
}
- uint32_t fb_id = get_fb_for_bo(renderer->fd, bo);
- drmModePageFlip(renderer->fd, output->crtc, fb_id, DRM_MODE_PAGE_FLIP_EVENT, output);
- output->pageflip_pending = true;
- output->bo[1] = output->bo[0];
- output->bo[0] = bo;
+ uint32_t fb_id = get_fb_for_bo(bo);
+ drmModeSetCrtc(renderer->fd, crtc->id, fb_id, 0, 0,
+ &output->connector, 1, &mode->state->mode);
+ drmModePageFlip(renderer->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, output);
+ output->pageflip_pending = true;
}
-void wlr_drm_output_pause_renderer(struct wlr_output_state *output) {
- if (output->state != DRM_OUTPUT_CONNECTED) {
+static void wlr_drm_output_enable(struct wlr_output_state *output, bool enable) {
+ struct wlr_backend_state *state =
+ wl_container_of(output->renderer, state, renderer);
+ if (output->state != WLR_DRM_OUTPUT_CONNECTED) {
return;
}
- if (output->bo[1]) {
- gbm_surface_release_buffer(output->gbm, output->bo[1]);
- output->bo[1] = NULL;
- }
- if (output->bo[0]) {
- gbm_surface_release_buffer(output->gbm, output->bo[0]);
- output->bo[0] = NULL;
+ if (enable) {
+ drmModeConnectorSetProperty(state->fd, output->connector, output->props.dpms,
+ DRM_MODE_DPMS_ON);
+
+ wlr_drm_output_start_renderer(output);
+ } else {
+ drmModeConnectorSetProperty(state->fd, output->connector, output->props.dpms,
+ DRM_MODE_DPMS_OFF);
}
}
-void wlr_drm_output_start_renderer(struct wlr_output_state *output) {
- if (output->state != DRM_OUTPUT_CONNECTED) {
- return;
- }
+static void realloc_planes(struct wlr_backend_state *drm, const uint32_t *crtc_in) {
+ // overlay, primary, cursor
+ for (int type = 0; type < 3; ++type) {
+ if (drm->num_type_planes[type] == 0) {
+ continue;
+ }
- struct wlr_drm_renderer *renderer = output->renderer;
- struct wlr_output_mode *mode = output->wlr_output->current_mode;
+ uint32_t possible[drm->num_type_planes[type]];
+ uint32_t crtc[drm->num_crtcs];
+ uint32_t crtc_res[drm->num_crtcs];
- // Render black frame
- eglMakeCurrent(renderer->egl.display, output->egl, output->egl, renderer->egl.context);
+ for (size_t i = 0; i < drm->num_type_planes[type]; ++i) {
+ possible[i] = drm->type_planes[type][i].possible_crtcs;
+ }
- glViewport(0, 0, output->width, output->height);
- glClearColor(0.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
+ for (size_t i = 0; i < drm->num_crtcs; ++i) {
+ if (crtc_in[i] == UNMATCHED) {
+ crtc[i] = SKIP;
+ } else if (drm->crtcs[i].planes[type]) {
+ crtc[i] = drm->crtcs[i].planes[type] - drm->type_planes[type];
+ } else {
+ crtc[i] = UNMATCHED;
+ }
+ }
- eglSwapBuffers(renderer->egl.display, output->egl);
+ match_obj(drm->num_type_planes[type], possible, drm->num_crtcs, crtc, crtc_res);
- struct gbm_bo *bo = gbm_surface_lock_front_buffer(output->gbm);
- uint32_t fb_id = get_fb_for_bo(renderer->fd, bo);
+ for (size_t i = 0; i < drm->num_crtcs; ++i) {
+ if (crtc_res[i] == UNMATCHED || crtc_res[i] == SKIP) {
+ continue;
+ }
- drmModeSetCrtc(renderer->fd, output->crtc, fb_id, 0, 0,
- &output->connector, 1, &mode->state->mode);
- drmModePageFlip(renderer->fd, output->crtc, fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, output);
+ struct wlr_drm_crtc *c = &drm->crtcs[i];
+ struct wlr_drm_plane **old = &c->planes[type];
+ struct wlr_drm_plane *new = &drm->type_planes[type][crtc_res[i]];
- output->bo[1] = NULL;
- output->bo[0] = bo;
+ if (*old != new) {
+ wlr_drm_plane_renderer_free(&drm->renderer, *old);
+ wlr_drm_plane_renderer_free(&drm->renderer, new);
+ *old = new;
+ }
+ }
+ }
}
-static bool display_init_renderer(struct wlr_drm_renderer *renderer,
- struct wlr_output_state *output) {
- struct wlr_output_mode *mode = output->wlr_output->current_mode;
- output->renderer = renderer;
- output->gbm = gbm_surface_create(renderer->gbm, mode->width,
- mode->height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
- if (!output->gbm) {
- wlr_log(L_ERROR, "Failed to create GBM surface for %s: %s", output->wlr_output->name,
- strerror(errno));
- return false;
- }
+static void realloc_crtcs(struct wlr_backend_state *drm, struct wlr_output_state *output) {
+ uint32_t crtc[drm->num_crtcs];
+ uint32_t crtc_res[drm->num_crtcs];
+ uint32_t possible_crtc[drm->outputs->length];
- output->egl = wlr_egl_create_surface(&renderer->egl, output->gbm);
- if (output->egl == EGL_NO_SURFACE) {
- wlr_log(L_ERROR, "Failed to create EGL surface for %s", output->wlr_output->name);
- return false;
+ for (size_t i = 0; i < drm->num_crtcs; ++i) {
+ crtc[i] = UNMATCHED;
}
- wlr_drm_output_start_renderer(output);
- return true;
-}
+ memset(possible_crtc, 0, sizeof(possible_crtc));
-static int find_id(const void *item, const void *cmp_to) {
- const struct wlr_output_state *output = item;
- const uint32_t *id = cmp_to;
+ size_t index;
+ for (size_t i = 0; i < drm->outputs->length; ++i) {
+ struct wlr_output_state *o = drm->outputs->items[i];
+ if (o == output) {
+ index = i;
+ }
- if (output->connector < *id) {
- return -1;
- } else if (output->connector > *id) {
- return 1;
- } else {
- return 0;
+ if (o->state != WLR_DRM_OUTPUT_CONNECTED) {
+ continue;
+ }
+
+ possible_crtc[i] = o->possible_crtc;
+ crtc[o->crtc - drm->crtcs] = i;
}
-}
-static void wlr_drm_output_enable(struct wlr_output_state *output, bool enable) {
- struct wlr_backend_state *state =
- wl_container_of(output->renderer, state, renderer);
- if (output->state != DRM_OUTPUT_CONNECTED) {
+ possible_crtc[index] = output->possible_crtc;
+ match_obj(drm->outputs->length, possible_crtc, drm->num_crtcs, crtc, crtc_res);
+
+ bool matched = false;
+ for (size_t i = 0; i < drm->num_crtcs; ++i) {
+ // We don't want any of the current monitors to be deactivated.
+ if (crtc[i] != UNMATCHED && crtc_res[i] == UNMATCHED) {
+ return;
+ }
+ if (crtc_res[i] == index) {
+ matched = true;
+ }
+ }
+
+ // There is no point doing anything if this monitor doesn't get activated
+ if (!matched) {
return;
}
- if (enable) {
- drmModeConnectorSetProperty(state->fd, output->connector, output->props.dpms,
- DRM_MODE_DPMS_ON);
+ for (size_t i = 0; i < drm->num_crtcs; ++i) {
+ if (crtc_res[i] == UNMATCHED) {
+ continue;
+ }
- // Start rendering loop again by drawing a black frame
- wlr_drm_output_make_current(output);
- glClearColor(0.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
- wlr_drm_output_swap_buffers(output);
- } else {
- drmModeConnectorSetProperty(state->fd, output->connector, output->props.dpms,
- DRM_MODE_DPMS_STANDBY);
+ if (crtc_res[i] != crtc[i]) {
+ struct wlr_output_state *o = drm->outputs->items[crtc_res[i]];
+ o->crtc = &drm->crtcs[i];
+ }
}
+
+ realloc_planes(drm, crtc_res);
}
static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
struct wlr_output_mode *mode) {
- struct wlr_backend_state *state =
- wl_container_of(output->renderer, state, renderer);
+ struct wlr_backend_state *drm = wl_container_of(output->renderer, drm, renderer);
- wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", output->wlr_output->name,
+ wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", output->base->name,
mode->width, mode->height, mode->refresh);
- drmModeConnector *conn = drmModeGetConnector(state->fd, output->connector);
+ drmModeConnector *conn = drmModeGetConnector(drm->fd, output->connector);
if (!conn) {
- wlr_log(L_ERROR, "Failed to get DRM connector");
- goto error;
+ wlr_log_errno(L_ERROR, "Failed to get DRM connector");
+ goto error_output;
}
if (conn->connection != DRM_MODE_CONNECTED || conn->count_modes == 0) {
- wlr_log(L_ERROR, "%s is not connected", output->wlr_output->name);
- goto error;
+ wlr_log(L_ERROR, "%s is not connected", output->base->name);
+ goto error_output;
}
- drmModeRes *res = drmModeGetResources(state->fd);
- if (!res) {
- wlr_log(L_ERROR, "Failed to get DRM resources");
- goto error;
+ drmModeEncoder *enc = NULL;
+ for (int i = 0; !enc && i < conn->count_encoders; ++i) {
+ enc = drmModeGetEncoder(drm->fd, conn->encoders[i]);
}
- bool success = false;
- for (int i = 0; !success && i < conn->count_encoders; ++i) {
- drmModeEncoder *enc = drmModeGetEncoder(state->fd, conn->encoders[i]);
- if (!enc) {
- continue;
- }
+ if (!enc) {
+ wlr_log(L_ERROR, "Failed to get DRM encoder");
+ goto error_conn;
+ }
- for (int j = 0; j < res->count_crtcs; ++j) {
- if ((enc->possible_crtcs & (1 << j)) == 0) {
- continue;
- }
+ output->possible_crtc = enc->possible_crtcs;
+ realloc_crtcs(drm, output);
- if ((state->taken_crtcs & (1 << j)) == 0) {
- state->taken_crtcs |= 1 << j;
- output->crtc = res->crtcs[j];
- success = true;
- break;
- }
- }
- drmModeFreeEncoder(enc);
+ if (!output->crtc) {
+ wlr_log(L_ERROR, "Unable to match %s with a CRTC", output->base->name);
+ goto error_enc;
}
- drmModeFreeResources(res);
+ struct wlr_drm_crtc *crtc = output->crtc;
+ wlr_log(L_DEBUG, "%s: crtc=%ju ovr=%jd pri=%jd cur=%jd", output->base->name,
+ crtc - drm->crtcs,
+ crtc->overlay ? crtc->overlay - drm->overlay_planes : -1,
+ crtc->primary ? crtc->primary - drm->primary_planes : -1,
+ crtc->cursor ? crtc->cursor - drm->cursor_planes : -1);
- if (!success) {
- wlr_log(L_ERROR, "Failed to find CRTC for %s", output->wlr_output->name);
- goto error;
- }
+ output->state = WLR_DRM_OUTPUT_CONNECTED;
+ output->width = output->base->width = mode->width;
+ output->height = output->base->height = mode->height;
+ output->base->current_mode = mode;
+ wl_signal_emit(&output->base->events.resolution, output->base);
+
+ // Since realloc_crtcs can deallocate planes on OTHER outputs,
+ // we actually need to reinitalise all of them
+ for (size_t i = 0; i < drm->outputs->length; ++i) {
+ struct wlr_output_state *output = drm->outputs->items[i];
+ struct wlr_output_mode *mode = output->base->current_mode;
+ struct wlr_drm_crtc *crtc = output->crtc;
+
+ if (output->state != WLR_DRM_OUTPUT_CONNECTED) {
+ continue;
+ }
- output->state = DRM_OUTPUT_CONNECTED;
- output->width = output->wlr_output->width = mode->width;
- output->height = output->wlr_output->height = mode->height;
- output->wlr_output->current_mode = mode;
- wl_signal_emit(&output->wlr_output->events.resolution, output->wlr_output);
+ if (!wlr_drm_plane_renderer_init(&drm->renderer, crtc->primary,
+ mode->width, mode->height, GBM_BO_USE_SCANOUT)) {
+ wlr_log(L_ERROR, "Failed to initalise renderer for plane");
+ goto error_enc;
+ }
- if (!display_init_renderer(&state->renderer, output)) {
- wlr_log(L_ERROR, "Failed to initalise renderer for %s", output->wlr_output->name);
- goto error;
+ wlr_drm_output_start_renderer(output);
}
+ drmModeFreeEncoder(enc);
drmModeFreeConnector(conn);
return true;
-error:
- wlr_drm_output_cleanup(output, false);
+error_enc:
+ drmModeFreeEncoder(enc);
+error_conn:
drmModeFreeConnector(conn);
+error_output:
+ wlr_drm_output_cleanup(output, false);
return false;
}
static void wlr_drm_output_transform(struct wlr_output_state *output,
enum wl_output_transform transform) {
- output->wlr_output->transform = transform;
+ output->base->transform = transform;
}
-static bool wlr_drm_output_set_cursor(struct wlr_output_state *output,
- const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height) {
- struct wlr_backend_state *state = wl_container_of(output->renderer, state, renderer);
- if (!buf) {
- drmModeSetCursor(state->fd, output->crtc, 0, 0, 0);
+bool wlr_drm_crtc_set_cursor(struct wlr_backend_state *drm, struct wlr_drm_crtc *crtc) {
+ if (!crtc || !crtc->cursor || !crtc->cursor->gbm) {
return true;
}
- if (!gbm_device_is_format_supported(state->renderer.gbm,
- GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
- wlr_log(L_ERROR, "Failed to create cursor bo: ARGB8888 pixel format is "
- "unsupported on this device");
+ struct wlr_drm_plane *plane = crtc->cursor;
+
+ uint32_t bo_handle = gbm_bo_get_handle(plane->cursor_bo).u32;
+ if (drmModeSetCursor(drm->fd, crtc->id, bo_handle, plane->width, plane->height)) {
+ wlr_log_errno(L_ERROR, "Failed to set hardware cursor");
return false;
}
- uint64_t bo_width, bo_height;
- int ret;
+ return true;
+}
- ret = drmGetCap(state->fd, DRM_CAP_CURSOR_WIDTH, &bo_width);
- bo_width = ret ? 64 : bo_width;
- ret = drmGetCap(state->fd, DRM_CAP_CURSOR_HEIGHT, &bo_height);
- bo_height = ret ? 64 : bo_height;
+static bool wlr_drm_output_set_cursor(struct wlr_output_state *output,
+ const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height) {
+ struct wlr_backend_state *drm = wl_container_of(output->renderer, drm, renderer);
+ struct wlr_drm_renderer *renderer = output->renderer;
+ struct wlr_drm_crtc *crtc = output->crtc;
+ struct wlr_drm_plane *plane = crtc->cursor;
- if (width > bo_width || height > bo_width) {
- wlr_log(L_INFO, "Cursor too large (max %dx%d)", (int)bo_width, (int)bo_height);
- return false;
+ if (!buf) {
+ drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0);
+ return true;
}
- for (int i = 0; i < 2; ++i) {
- if (output->cursor_bo[i]) {
- continue;
+ if (!plane) {
+ plane = calloc(1, sizeof(*plane));
+ if (!plane) {
+ wlr_log_errno(L_ERROR, "Allocation failed");
+ return false;
}
+ crtc->cursor = plane;
+ }
- output->cursor_bo[i] = gbm_bo_create(state->renderer.gbm, bo_width, bo_height,
- GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+ if (!plane->gbm) {
+ int ret;
+ uint64_t w, h;
+ ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w);
+ w = ret ? 64 : w;
+ ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h);
+ h = ret ? 64 : h;
- if (!output->cursor_bo[i]) {
- wlr_log(L_ERROR, "Failed to create cursor bo");
+ if (width > w || height > h) {
+ wlr_log(L_INFO, "Cursor too large (max %dx%d)", (int)w, (int)h);
return false;
}
- }
- struct gbm_bo *bo;
- output->current_cursor ^= 1;
- bo = output->cursor_bo[output->current_cursor];
+ if (!wlr_drm_plane_renderer_init(renderer, plane, w, h, 0)) {
+ wlr_log(L_ERROR, "Cannot allocate cursor resources");
+ return false;
+ }
- uint32_t bo_stride = gbm_bo_get_stride(bo);
- uint8_t tmp[bo_stride * height];
- memset(tmp, 0, sizeof(tmp));
+ plane->cursor_bo = gbm_bo_create(renderer->gbm, w, h, GBM_FORMAT_ARGB8888,
+ GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+ if (!plane->cursor_bo) {
+ wlr_log_errno(L_ERROR, "Failed to create cursor bo");
+ return false;
+ }
- for (size_t i = 0; i < height; ++i) {
- memcpy(tmp + i * bo_stride, buf + i * stride * 4, width * 4);
- }
+ // OpenGL will read the pixels out upside down,
+ // so we need to flip the image vertically
+ wlr_matrix_surface(plane->matrix, plane->width, plane->height,
+ output->base->transform ^ WL_OUTPUT_TRANSFORM_FLIPPED_180);
- if (gbm_bo_write(bo, tmp, sizeof(tmp)) < 0) {
- wlr_log(L_ERROR, "Failed to write cursor to bo");
- return false;
+ plane->wlr_rend = wlr_gles2_renderer_init();
+ if (!plane->wlr_rend) {
+ return false;
+ }
+
+ plane->wlr_surf = wlr_render_surface_init(plane->wlr_rend);
+ if (!plane->wlr_surf) {
+ return false;
+ }
}
- uint32_t bo_handle = gbm_bo_get_handle(bo).u32;
- if (drmModeSetCursor(state->fd, output->crtc, bo_handle, bo_width, bo_height)) {
- wlr_log_errno(L_INFO, "Failed to set hardware cursor");
+ struct gbm_bo *bo = plane->cursor_bo;
+ uint32_t bo_width = gbm_bo_get_width(bo);
+ uint32_t bo_height = gbm_bo_get_height(bo);
+ uint32_t bo_stride;
+ void *bo_data;
+
+ if (!gbm_bo_map(bo, 0, 0, bo_width, bo_height,
+ GBM_BO_TRANSFER_WRITE, &bo_stride, &bo_data)) {
+ wlr_log_errno(L_ERROR, "Unable to map buffer");
return false;
}
- return true;
+ wlr_drm_plane_make_current(renderer, plane);
+
+ wlr_surface_attach_pixels(plane->wlr_surf, WL_SHM_FORMAT_ARGB8888,
+ stride, width, height, buf);
+
+ glViewport(0, 0, plane->width, plane->height);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ float matrix[16];
+ wlr_surface_get_matrix(plane->wlr_surf, &matrix, &plane->matrix, 0, 0);
+ wlr_render_with_matrix(plane->wlr_rend, plane->wlr_surf, &matrix);
+
+ glFinish();
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, bo_stride);
+ glReadPixels(0, 0, plane->width, plane->height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, bo_data);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
+
+ wlr_drm_plane_swap_buffers(renderer, plane);
+
+ gbm_bo_unmap(bo, bo_data);
+
+ return wlr_drm_crtc_set_cursor(drm, crtc);
}
static bool wlr_drm_output_move_cursor(struct wlr_output_state *output,
int x, int y) {
- struct wlr_backend_state *state =
- wl_container_of(output->renderer, state, renderer);
- return !drmModeMoveCursor(state->fd, output->crtc, x, y);
+ struct wlr_backend_state *drm =
+ wl_container_of(output->renderer, drm, renderer);
+ return !drmModeMoveCursor(drm->fd, output->crtc->id, x, y);
}
static void wlr_drm_output_destroy(struct wlr_output_state *output) {
@@ -387,216 +679,100 @@ static struct wlr_output_impl output_impl = {
.swap_buffers = wlr_drm_output_swap_buffers,
};
-static int32_t calculate_refresh_rate(drmModeModeInfo *mode) {
- int32_t refresh = (mode->clock * 1000000LL / mode->htotal +
- mode->vtotal / 2) / mode->vtotal;
-
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- refresh *= 2;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
- refresh /= 2;
- if (mode->vscan > 1)
- refresh /= mode->vscan;
-
- return refresh;
-}
-
-// Constructed from http://edid.tv/manufacturer
-const char *get_manufacturer(uint16_t id) {
-#define ID(a, b, c) ((a & 0x1f) << 10) | ((b & 0x1f) << 5) | (c & 0x1f)
- switch (id) {
- case ID('A', 'A', 'A'): return "Avolites Ltd";
- case ID('A', 'C', 'I'): return "Ancor Communications Inc";
- case ID('A', 'C', 'R'): return "Acer Technologies";
- case ID('A', 'P', 'P'): return "Apple Computer Inc";
- case ID('B', 'N', 'O'): return "Bang & Olufsen";
- case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
- case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
- case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
- case ID('D', 'E', 'L'): return "Dell Inc.";
- case ID('D', 'O', 'N'): return "DENON, Ltd.";
- case ID('E', 'N', 'C'): return "Eizo Nanao Corporation";
- case ID('E', 'P', 'H'): return "Epiphan Systems Inc.";
- case ID('F', 'U', 'S'): return "Fujitsu Siemens Computers GmbH";
- case ID('G', 'S', 'M'): return "Goldstar Company Ltd";
- case ID('H', 'I', 'Q'): return "Kaohsiung Opto Electronics Americas, Inc.";
- case ID('H', 'S', 'D'): return "HannStar Display Corp";
- case ID('H', 'W', 'P'): return "Hewlett Packard";
- case ID('I', 'N', 'T'): return "Interphase Corporation";
- case ID('I', 'V', 'M'): return "Iiyama North America";
- case ID('L', 'E', 'N'): return "Lenovo Group Limited";
- case ID('M', 'A', 'X'): return "Rogen Tech Distribution Inc";
- case ID('M', 'E', 'G'): return "Abeam Tech Ltd";
- case ID('M', 'E', 'I'): return "Panasonic Industry Company";
- case ID('M', 'T', 'C'): return "Mars-Tech Corporation";
- case ID('M', 'T', 'X'): return "Matrox";
- case ID('N', 'E', 'C'): return "NEC Corporation";
- case ID('O', 'N', 'K'): return "ONKYO Corporation";
- case ID('O', 'R', 'N'): return "ORION ELECTRIC CO., LTD.";
- case ID('O', 'T', 'M'): return "Optoma Corporation";
- case ID('O', 'V', 'R'): return "Oculus VR, Inc.";
- case ID('P', 'H', 'L'): return "Philips Consumer Electronics Company";
- case ID('P', 'I', 'O'): return "Pioneer Electronic Corporation";
- case ID('P', 'N', 'R'): return "Planar Systems, Inc.";
- case ID('Q', 'D', 'S'): return "Quanta Display Inc.";
- case ID('S', 'A', 'M'): return "Samsung Electric Company";
- case ID('S', 'E', 'C'): return "Seiko Epson Corporation";
- case ID('S', 'H', 'P'): return "Sharp Corporation";
- case ID('S', 'I', 'I'): return "Silicon Image, Inc.";
- case ID('S', 'N', 'Y'): return "Sony";
- case ID('T', 'O', 'P'): return "Orion Communications Co., Ltd.";
- case ID('T', 'S', 'B'): return "Toshiba America Info Systems Inc";
- case ID('T', 'S', 'T'): return "Transtream Inc";
- case ID('U', 'N', 'K'): return "Unknown";
- case ID('V', 'I', 'Z'): return "VIZIO, Inc";
- case ID('V', 'S', 'C'): return "ViewSonic Corporation";
- case ID('Y', 'M', 'H'): return "Yamaha Corporation";
- default: return "Unknown";
- }
-#undef ID
-}
-
-/* See https://en.wikipedia.org/wiki/Extended_Display_Identification_Data for layout of EDID data.
- * We don't parse the EDID properly. We just expect to receive valid data.
- */
-static void parse_edid(struct wlr_output *restrict output, const uint8_t *restrict data) {
- uint32_t id = (data[8] << 8) | data[9];
- snprintf(output->make, sizeof(output->make), "%s", get_manufacturer(id));
-
- output->phys_width = ((data[68] & 0xf0) << 4) | data[66];
- output->phys_height = ((data[68] & 0x0f) << 8) | data[67];
-
- for (size_t i = 72; i <= 108; i += 18) {
- uint16_t flag = (data[i] << 8) | data[i + 1];
- if (flag == 0 && data[i + 3] == 0xFC) {
- sprintf(output->model, "%.13s", &data[i + 5]);
-
- // Monitor names are terminated by newline if they're too short
- char *nl = strchr(output->model, '\n');
- if (nl) {
- *nl = '\0';
- }
+static int find_id(const void *item, const void *cmp_to) {
+ const struct wlr_output_state *output = item;
+ const uint32_t *id = cmp_to;
- break;
- }
+ if (output->connector < *id) {
+ return -1;
+ } else if (output->connector > *id) {
+ return 1;
+ } else {
+ return 0;
}
}
-static void scan_property_ids(int fd, drmModeConnector *conn,
- struct wlr_output_state *output) {
- for (int i = 0; i < conn->count_props; ++i) {
- drmModePropertyRes *prop = drmModeGetProperty(fd, conn->props[i]);
- if (!prop) {
- continue;
- }
-
- if (strcmp(prop->name, "DPMS") == 0) {
- output->props.dpms = prop->prop_id;
- } else if (strcmp(prop->name, "EDID") == 0) {
- drmModePropertyBlobRes *edid = drmModeGetPropertyBlob(fd, conn->prop_values[i]);
- if (!edid) {
- drmModeFreeProperty(prop);
- continue;
- }
-
- parse_edid(output->wlr_output, edid->data);
-
- drmModeFreePropertyBlob(edid);
- }
-
- drmModeFreeProperty(prop);
- }
-}
+static const int32_t subpixel_map[] = {
+ [DRM_MODE_SUBPIXEL_UNKNOWN] = WL_OUTPUT_SUBPIXEL_UNKNOWN,
+ [DRM_MODE_SUBPIXEL_HORIZONTAL_RGB] = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB,
+ [DRM_MODE_SUBPIXEL_HORIZONTAL_BGR] = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR,
+ [DRM_MODE_SUBPIXEL_VERTICAL_RGB] = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB,
+ [DRM_MODE_SUBPIXEL_VERTICAL_BGR] = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR,
+ [DRM_MODE_SUBPIXEL_NONE] = WL_OUTPUT_SUBPIXEL_NONE,
+};
-void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
+void wlr_drm_scan_connectors(struct wlr_backend_state *drm) {
wlr_log(L_INFO, "Scanning DRM connectors");
- drmModeRes *res = drmModeGetResources(state->fd);
+ drmModeRes *res = drmModeGetResources(drm->fd);
if (!res) {
- wlr_log(L_ERROR, "Failed to get DRM resources");
+ wlr_log_errno(L_ERROR, "Failed to get DRM resources");
return;
}
for (int i = 0; i < res->count_connectors; ++i) {
- uint32_t id = res->connectors[i];
-
- drmModeConnector *conn = drmModeGetConnector(state->fd, id);
+ drmModeConnector *conn = drmModeGetConnector(drm->fd,
+ res->connectors[i]);
if (!conn) {
- wlr_log(L_ERROR, "Failed to get DRM connector");
+ wlr_log_errno(L_ERROR, "Failed to get DRM connector");
continue;
}
struct wlr_output_state *output;
- struct wlr_output *wlr_output;
- int index = list_seq_find(state->outputs, find_id, &id);
+ int index = list_seq_find(drm->outputs, find_id, &conn->connector_id);
if (index == -1) {
- output = calloc(1, sizeof(struct wlr_output_state));
- if (!state) {
+ output = calloc(1, sizeof(*output));
+ if (!output) {
+ wlr_log_errno(L_ERROR, "Allocation failed");
drmModeFreeConnector(conn);
- wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno));
- return;
+ continue;
}
- wlr_output = output->wlr_output = wlr_output_create(&output_impl, output);
- if (!wlr_output) {
+
+ output->base = wlr_output_create(&output_impl, output);
+ if (!output->base) {
+ wlr_log_errno(L_ERROR, "Allocation failed");
drmModeFreeConnector(conn);
- wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno));
- return;
+ free(output);
+ continue;
}
- output->renderer = &state->renderer;
- output->state = DRM_OUTPUT_DISCONNECTED;
- output->connector = id;
- // TODO: Populate more wlr_output fields
- // TODO: Move this to wlr_output->name
- snprintf(wlr_output->name, sizeof(wlr_output->name), "%s-%"PRIu32,
- conn_name[conn->connector_type],
- conn->connector_type_id);
- wlr_output->phys_width = conn->mmWidth;
- wlr_output->phys_height = conn->mmHeight;
- switch (conn->subpixel) {
- case DRM_MODE_SUBPIXEL_UNKNOWN:
- wlr_output->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
- break;
- case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
- wlr_output->subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
- break;
- case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
- wlr_output->subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
- break;
- case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
- wlr_output->subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
- break;
- case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
- wlr_output->subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
- break;
- case DRM_MODE_SUBPIXEL_NONE:
- default:
- wlr_output->subpixel = WL_OUTPUT_SUBPIXEL_NONE;
- break;
- }
+ output->renderer = &drm->renderer;
+ output->state = WLR_DRM_OUTPUT_DISCONNECTED;
+ output->connector = conn->connector_id;
- drmModeEncoder *curr_enc = drmModeGetEncoder(state->fd, conn->encoder_id);
+ drmModeEncoder *curr_enc = drmModeGetEncoder(drm->fd, conn->encoder_id);
if (curr_enc) {
- output->old_crtc = drmModeGetCrtc(state->fd, curr_enc->crtc_id);
- free(curr_enc);
+ output->old_crtc = drmModeGetCrtc(drm->fd, curr_enc->crtc_id);
+ drmModeFreeEncoder(curr_enc);
}
- scan_property_ids(state->fd, conn, output);
+ output->base->phys_width = conn->mmWidth;
+ output->base->phys_height = conn->mmHeight;
+ output->base->subpixel = subpixel_map[conn->subpixel];
+ snprintf(output->base->name, sizeof(output->base->name), "%s-%"PRIu32,
+ conn_get_name(conn->connector_type),
+ conn->connector_type_id);
- wlr_output_create_global(wlr_output, state->display);
- list_add(state->outputs, output);
- wlr_log(L_INFO, "Found display '%s'", wlr_output->name);
+ wlr_drm_get_connector_props(drm->fd, output->connector, &output->props);
+
+ size_t edid_len = 0;
+ uint8_t *edid = wlr_drm_get_prop_blob(drm->fd, output->connector,
+ output->props.edid, &edid_len);
+ parse_edid(output->base, edid_len, edid);
+ free(edid);
+
+ wlr_output_create_global(output->base, drm->display);
+ list_add(drm->outputs, output);
+ wlr_log(L_INFO, "Found display '%s'", output->base->name);
} else {
- output = state->outputs->items[index];
- wlr_output = output->wlr_output;
+ output = drm->outputs->items[index];
}
- // TODO: move state into wlr_output
- if (output->state == DRM_OUTPUT_DISCONNECTED &&
- conn->connection == DRM_MODE_CONNECTED) {
+ if (output->state == WLR_DRM_OUTPUT_DISCONNECTED &&
+ conn->connection == DRM_MODE_CONNECTED) {
- wlr_log(L_INFO, "'%s' connected", output->wlr_output->name);
+ wlr_log(L_INFO, "'%s' connected", output->base->name);
wlr_log(L_INFO, "Detected modes:");
for (int i = 0; i < conn->count_modes; ++i) {
@@ -613,16 +789,16 @@ void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
wlr_log(L_INFO, " %"PRId32"@%"PRId32"@%"PRId32,
mode->width, mode->height, mode->refresh);
- list_add(wlr_output->modes, mode);
+ list_add(output->base->modes, mode);
}
- output->state = DRM_OUTPUT_NEEDS_MODESET;
- wlr_log(L_INFO, "Sending modesetting signal for '%s'", output->wlr_output->name);
- wl_signal_emit(&state->backend->events.output_add, wlr_output);
- } else if (output->state == DRM_OUTPUT_CONNECTED &&
- conn->connection != DRM_MODE_CONNECTED) {
+ output->state = WLR_DRM_OUTPUT_NEEDS_MODESET;
+ wlr_log(L_INFO, "Sending modesetting signal for '%s'", output->base->name);
+ wl_signal_emit(&drm->base->events.output_add, output->base);
+ } else if (output->state == WLR_DRM_OUTPUT_CONNECTED &&
+ conn->connection != DRM_MODE_CONNECTED) {
- wlr_log(L_INFO, "'%s' disconnected", output->wlr_output->name);
+ wlr_log(L_INFO, "'%s' disconnected", output->base->name);
wlr_drm_output_cleanup(output, false);
}
@@ -635,17 +811,18 @@ void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
static void page_flip_handler(int fd, unsigned seq,
unsigned tv_sec, unsigned tv_usec, void *user) {
struct wlr_output_state *output = user;
- struct wlr_backend_state *state =
- wl_container_of(output->renderer, state, renderer);
+ struct wlr_backend_state *drm =
+ wl_container_of(output->renderer, drm, renderer);
- if (output->gbm && output->bo[1]) {
- gbm_surface_release_buffer(output->gbm, output->bo[1]);
- output->bo[1] = NULL;
+ struct wlr_drm_plane *plane = output->crtc->primary;
+ if (plane->front) {
+ gbm_surface_release_buffer(plane->gbm, plane->front);
+ plane->front = NULL;
}
output->pageflip_pending = false;
- if (output->state == DRM_OUTPUT_CONNECTED && state->session->active) {
- wl_signal_emit(&output->wlr_output->events.frame, output->wlr_output);
+ if (output->state == WLR_DRM_OUTPUT_CONNECTED && drm->session->active) {
+ wl_signal_emit(&output->base->events.frame, output->base);
}
}
@@ -681,29 +858,37 @@ void wlr_drm_output_cleanup(struct wlr_output_state *output, bool restore) {
}
struct wlr_drm_renderer *renderer = output->renderer;
- struct wlr_backend_state *state = wl_container_of(renderer, state, renderer);
+ struct wlr_backend_state *drm = wl_container_of(renderer, drm, renderer);
switch (output->state) {
- case DRM_OUTPUT_CONNECTED:
- output->state = DRM_OUTPUT_DISCONNECTED;
+ case WLR_DRM_OUTPUT_CONNECTED:
+ output->state = WLR_DRM_OUTPUT_DISCONNECTED;
if (restore) {
restore_output(output, renderer->fd);
restore = false;
}
- eglDestroySurface(renderer->egl.display, output->egl);
- gbm_surface_destroy(output->gbm);
- output->egl = EGL_NO_SURFACE;
- output->gbm = NULL;
+
+ struct wlr_drm_crtc *crtc = output->crtc;
+ for (int i = 0; i < 3; ++i) {
+ wlr_drm_plane_renderer_free(renderer, crtc->planes[i]);
+ if (crtc->planes[i] && crtc->planes[i]->id == 0) {
+ free(crtc->planes[i]);
+ crtc->planes[i] = NULL;
+ }
+ }
+
+ output->crtc = NULL;
+ output->possible_crtc = 0;
/* Fallthrough */
- case DRM_OUTPUT_NEEDS_MODESET:
- output->state = DRM_OUTPUT_DISCONNECTED;
+ case WLR_DRM_OUTPUT_NEEDS_MODESET:
+ output->state = WLR_DRM_OUTPUT_DISCONNECTED;
if (restore) {
restore_output(output, renderer->fd);
}
- wlr_log(L_INFO, "Emmiting destruction signal for '%s'", output->wlr_output->name);
- wl_signal_emit(&state->backend->events.output_remove, output->wlr_output);
+ wlr_log(L_INFO, "Emmiting destruction signal for '%s'", output->base->name);
+ wl_signal_emit(&drm->base->events.output_remove, output->base);
break;
- case DRM_OUTPUT_DISCONNECTED:
+ case WLR_DRM_OUTPUT_DISCONNECTED:
break;
}
}
diff --git a/backend/egl.c b/backend/egl.c
index 8bb05f6f..2ac1e77c 100644
--- a/backend/egl.c
+++ b/backend/egl.c
@@ -99,7 +99,7 @@ static bool egl_get_config(EGLDisplay disp, EGLConfig *out, EGLenum platform) {
continue;
}
- if (gbm_format == GBM_FORMAT_XRGB8888) {
+ if (gbm_format == GBM_FORMAT_ARGB8888) {
*out = configs[i];
return true;
}
diff --git a/backend/meson.build b/backend/meson.build
index dc5a3d66..a5bf5058 100644
--- a/backend/meson.build
+++ b/backend/meson.build
@@ -7,6 +7,8 @@ wlr_files += files(
'session/session.c',
'drm/backend.c',
'drm/drm.c',
+ 'drm/drm-properties.c',
+ 'drm/drm-util.c',
'libinput/backend.c',
'libinput/events.c',
'libinput/keyboard.c',
diff --git a/examples/shared.c b/examples/shared.c
index c66b8a23..cebdb785 100644
--- a/examples/shared.c
+++ b/examples/shared.c
@@ -9,7 +9,7 @@
#include <wayland-server-protocol.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
-#include <wlr/backend/multi.h>
+#include <wlr/backend/multi.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/util/log.h>
diff --git a/include/backend/drm-properties.h b/include/backend/drm-properties.h
new file mode 100644
index 00000000..287fc1e0
--- /dev/null
+++ b/include/backend/drm-properties.h
@@ -0,0 +1,62 @@
+#ifndef DRM_PROPERTIES_H
+#define DRM_PROPERTIES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * These types contain the property ids for several DRM objects.
+ * See https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html#kms-properties
+ * for more details.
+ */
+
+union wlr_drm_connector_props {
+ struct {
+ uint32_t edid;
+ uint32_t dpms;
+
+ // atomic-modesetting only
+
+ uint32_t crtc_id;
+ };
+ uint32_t props[3];
+};
+
+union wlr_drm_crtc_props {
+ struct {
+ // Neither of these are guranteed to exist
+ uint32_t rotation;
+ uint32_t scaling_mode;
+ };
+ uint32_t props[2];
+};
+
+union wlr_drm_plane_props {
+ struct {
+ uint32_t type;
+ uint32_t rotation; // Not guranteed to exist
+
+ // atomic-modesetting only
+
+ uint32_t src_x;
+ uint32_t src_y;
+ uint32_t src_w;
+ uint32_t src_h;
+ uint32_t crtc_x;
+ uint32_t crtc_y;
+ uint32_t crtc_w;
+ uint32_t crtc_h;
+ uint32_t fb_id;
+ uint32_t crtc_id;
+ };
+ uint32_t props[12];
+};
+
+bool wlr_drm_get_connector_props(int fd, uint32_t id, union wlr_drm_connector_props *out);
+bool wlr_drm_get_crtc_props(int fd, uint32_t id, union wlr_drm_crtc_props *out);
+bool wlr_drm_get_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out);
+
+bool wlr_drm_get_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret);
+void *wlr_drm_get_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len);
+
+#endif
diff --git a/include/backend/drm-util.h b/include/backend/drm-util.h
new file mode 100644
index 00000000..759bdb48
--- /dev/null
+++ b/include/backend/drm-util.h
@@ -0,0 +1,37 @@
+#ifndef WLR_DRM_UTIL_H
+#define WLR_DRM_UTIL_H
+
+#include <stdint.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <wlr/types/wlr_output.h>
+
+// Calculates a more accurate refresh rate (mHz) than what mode itself provides
+int32_t calculate_refresh_rate(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);
+// Returns the string representation of a DRM output type
+const char *conn_get_name(uint32_t type_id);
+
+// Part of match_obj
+enum {
+ UNMATCHED = (uint32_t)-1,
+ SKIP = (uint32_t)-2,
+};
+
+/*
+ * Tries to match some DRM objects with some other DRM resource.
+ * e.g. Match CRTCs with Encoders, CRTCs with Planes.
+ *
+ * objs contains a bit array which resources it can be matched with.
+ * e.g. Bit 0 set means can be matched with res[0]
+ *
+ * res contains an index of which objs it is matched with or UNMATCHED.
+ *
+ * This solution is left in out.
+ * Returns the total number of matched solutions.
+ */
+size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs],
+ size_t num_res, const uint32_t res[static restrict num_res],
+ uint32_t out[static restrict num_res]);
+#endif
diff --git a/include/backend/drm.h b/include/backend/drm.h
index ecdd945b..0d1bc80d 100644
--- a/include/backend/drm.h
+++ b/include/backend/drm.h
@@ -14,8 +14,58 @@
#include <wlr/backend/drm.h>
#include <wlr/util/list.h>
-#include "backend/egl.h"
-#include "backend/udev.h"
+#include <backend/egl.h>
+#include <backend/udev.h>
+#include "drm-properties.h"
+
+struct wlr_drm_plane {
+ uint32_t type;
+ uint32_t id;
+
+ uint32_t possible_crtcs;
+
+ uint32_t width, height;
+
+ struct gbm_surface *gbm;
+ EGLSurface egl;
+
+ struct gbm_bo *front;
+ struct gbm_bo *back;
+
+ // Only used by cursor
+ float matrix[16];
+ struct wlr_renderer *wlr_rend;
+ struct wlr_surface *wlr_surf;
+ struct gbm_bo *cursor_bo;
+
+ union wlr_drm_plane_props props;
+};
+
+struct wlr_drm_crtc {
+ uint32_t id;
+ union {
+ struct {
+ struct wlr_drm_plane *overlay;
+ struct wlr_drm_plane *primary;
+ struct wlr_drm_plane *cursor;
+ };
+ struct wlr_drm_plane *planes[3];
+ };
+
+ union wlr_drm_crtc_props props;
+
+ struct wl_list connectors;
+};
+
+struct wlr_drm_connector {
+ struct wlr_output *base;
+ uint32_t id;
+ struct wlr_drm_crtc *crtc;
+
+ union wlr_drm_connector_props props;
+
+ struct wl_list link;
+};
struct wlr_drm_renderer {
int fd;
@@ -27,10 +77,34 @@ bool wlr_drm_renderer_init(struct wlr_drm_renderer *renderer, int fd);
void wlr_drm_renderer_free(struct wlr_drm_renderer *renderer);
struct wlr_backend_state {
+ struct wlr_backend *base;
+
int fd;
dev_t dev;
- struct wlr_backend *backend;
+ size_t num_crtcs;
+ struct wlr_drm_crtc *crtcs;
+ size_t num_planes;
+ struct wlr_drm_plane *planes;
+
+ union {
+ struct {
+ size_t num_overlay_planes;
+ size_t num_primary_planes;
+ size_t num_cursor_planes;
+ };
+ size_t num_type_planes[3];
+ };
+
+ union {
+ struct {
+ struct wlr_drm_plane *overlay_planes;
+ struct wlr_drm_plane *primary_planes;
+ struct wlr_drm_plane *cursor_planes;
+ };
+ struct wlr_drm_plane *type_planes[3];
+ };
+
struct wl_display *display;
struct wl_event_source *drm_event;
@@ -46,9 +120,9 @@ struct wlr_backend_state {
};
enum wlr_drm_output_state {
- DRM_OUTPUT_DISCONNECTED,
- DRM_OUTPUT_NEEDS_MODESET,
- DRM_OUTPUT_CONNECTED,
+ WLR_DRM_OUTPUT_DISCONNECTED,
+ WLR_DRM_OUTPUT_NEEDS_MODESET,
+ WLR_DRM_OUTPUT_CONNECTED,
};
struct wlr_output_mode_state {
@@ -57,38 +131,34 @@ struct wlr_output_mode_state {
};
struct wlr_output_state {
- struct wlr_output *wlr_output;
+ struct wlr_output *base;
enum wlr_drm_output_state state;
uint32_t connector;
- struct {
- uint32_t dpms;
- } props;
+ struct wlr_drm_crtc *crtc;
+ uint32_t possible_crtc;
+
+ union wlr_drm_connector_props props;
uint32_t width;
uint32_t height;
- uint32_t crtc;
drmModeCrtc *old_crtc;
struct wlr_drm_renderer *renderer;
- EGLSurface *egl;
- struct gbm_surface *gbm;
- struct gbm_bo *bo[2];
- struct gbm_bo *cursor_bo[2];
- int current_cursor;
- uint32_t cursor_width, cursor_height;
bool pageflip_pending;
- bool cleanup;
};
+bool wlr_drm_check_features(struct wlr_backend_state *drm);
+bool wlr_drm_resources_init(struct wlr_backend_state *drm);
+void wlr_drm_resources_free(struct wlr_backend_state *drm);
void wlr_drm_output_cleanup(struct wlr_output_state *output, bool restore);
void wlr_drm_scan_connectors(struct wlr_backend_state *state);
int wlr_drm_event(int fd, uint32_t mask, void *data);
void wlr_drm_output_start_renderer(struct wlr_output_state *output);
-void wlr_drm_output_pause_renderer(struct wlr_output_state *output);
+bool wlr_drm_crtc_set_cursor(struct wlr_backend_state *drm, struct wlr_drm_crtc *crtc);
#endif
diff --git a/include/wlr/render/matrix.h b/include/wlr/render/matrix.h
index 954207da..be6a947d 100644
--- a/include/wlr/render/matrix.h
+++ b/include/wlr/render/matrix.h
@@ -1,10 +1,16 @@
#ifndef _WLR_RENDER_MATRIX_H
#define _WLR_RENDER_MATRIX_H
+#include <stdint.h>
+
void wlr_matrix_identity(float (*output)[16]);
void wlr_matrix_translate(float (*output)[16], float x, float y, float z);
void wlr_matrix_scale(float (*output)[16], float x, float y, float z);
void wlr_matrix_rotate(float (*output)[16], float radians);
void wlr_matrix_mul(const float (*x)[16], const float (*y)[16], float (*product)[16]);
+enum wl_output_transform;
+void wlr_matrix_surface(float mat[static 16], int32_t width, int32_t height,
+ enum wl_output_transform transform);
+
#endif
diff --git a/render/matrix.c b/render/matrix.c
index 4de20e5d..c7b11efe 100644
--- a/render/matrix.c
+++ b/render/matrix.c
@@ -1,5 +1,6 @@
#include <string.h>
#include <math.h>
+#include <wayland-server-protocol.h>
#include <wlr/render/matrix.h>
/* Obtains the index for the given row/column */
@@ -81,3 +82,62 @@ void wlr_matrix_mul(const float (*x)[16], const float (*y)[16], float (*product)
};
memcpy(*product, _product, sizeof(_product));
}
+
+static const float transforms[][4] = {
+ [WL_OUTPUT_TRANSFORM_NORMAL] = {
+ 1.0f, 0.0f,
+ 0.0f, -1.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_90] = {
+ 0.0f, -1.0f,
+ -1.0f, 0.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_180] = {
+ -1.0f, 0.0f,
+ 0.0f, 1.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_270] = {
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_FLIPPED] = {
+ -1.0f, 0.0f,
+ 0.0f, -1.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_FLIPPED_90] = {
+ 0.0f, 1.0f,
+ -1.0f, 0.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_FLIPPED_180] = {
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ },
+ [WL_OUTPUT_TRANSFORM_FLIPPED_270] = {
+ 0.0f, -1.0f,
+ 1.0f, 0.0f,
+ },
+};
+
+// Equivilent to glOrtho(0, width, 0, height, 1, -1) with the transform applied
+void wlr_matrix_surface(float mat[static 16], int32_t width, int32_t height,
+ enum wl_output_transform transform) {
+ memset(mat, 0, sizeof(*mat) * 16);
+
+ const float *t = transforms[transform];
+ float x = 2.0f / width;
+ float y = 2.0f / height;
+
+ // Rotation + relection
+ mat[0] = x * t[0];
+ mat[1] = x * t[1];
+ mat[4] = y * t[2];
+ mat[5] = y * t[3];
+
+ // Translation
+ mat[3] = -copysign(1.0f, mat[0] + mat[1]);
+ mat[7] = -copysign(1.0f, mat[4] + mat[5]);
+
+ // Identity
+ mat[10] = 1.0f;
+ mat[15] = 1.0f;
+}
diff --git a/types/wlr_output.c b/types/wlr_output.c
index fecfe5de..7d3cdbed 100644
--- a/types/wlr_output.c
+++ b/types/wlr_output.c
@@ -88,67 +88,8 @@ struct wl_global *wlr_output_create_global(
return wl_global;
}
-static const float transforms[][4] = {
- [WL_OUTPUT_TRANSFORM_NORMAL] = {
- 1.0f, 0.0f,
- 0.0f, -1.0f,
- },
- [WL_OUTPUT_TRANSFORM_90] = {
- 0.0f, -1.0f,
- -1.0f, 0.0f,
- },
- [WL_OUTPUT_TRANSFORM_180] = {
- -1.0f, 0.0f,
- 0.0f, 1.0f,
- },
- [WL_OUTPUT_TRANSFORM_270] = {
- 0.0f, 1.0f,
- 1.0f, 0.0f,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED] = {
- -1.0f, 0.0f,
- 0.0f, -1.0f,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED_90] = {
- 0.0f, 1.0f,
- -1.0f, 0.0f,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED_180] = {
- 1.0f, 0.0f,
- 0.0f, 1.0f,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED_270] = {
- 0.0f, -1.0f,
- 1.0f, 0.0f,
- },
-};
-
-// Equivilent to glOrtho(0, width, 0, height, 1, -1) with the transform applied
-static void set_matrix(float mat[static 16], int32_t width, int32_t height,
- enum wl_output_transform transform) {
- memset(mat, 0, sizeof(*mat) * 16);
-
- const float *t = transforms[transform];
- float x = 2.0f / width;
- float y = 2.0f / height;
-
- // Rotation + relection
- mat[0] = x * t[0];
- mat[1] = x * t[1];
- mat[4] = y * t[2];
- mat[5] = y * t[3];
-
- // Translation
- mat[3] = -copysign(1.0f, mat[0] + mat[1]);
- mat[7] = -copysign(1.0f, mat[4] + mat[5]);
-
- // Identity
- mat[10] = 1.0f;
- mat[15] = 1.0f;
-}
-
void wlr_output_update_matrix(struct wlr_output *output) {
- set_matrix(output->transform_matrix, output->width, output->height, output->transform);
+ wlr_matrix_surface(output->transform_matrix, output->width, output->height, output->transform);
}
struct wlr_output *wlr_output_create(struct wlr_output_impl *impl,
@@ -206,7 +147,7 @@ bool wlr_output_set_cursor(struct wlr_output *output,
output->cursor.texture = wlr_render_surface_init(output->cursor.renderer);
}
- wlr_surface_attach_pixels(output->cursor.texture, WL_SHM_FORMAT_ABGR8888,
+ wlr_surface_attach_pixels(output->cursor.texture, WL_SHM_FORMAT_ARGB8888,
stride, width, height, buf);
return true;