diff options
author | Simon Ser <contact@emersion.fr> | 2020-11-18 19:21:03 +0100 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2020-12-13 12:16:52 +0100 |
commit | d79a00bf020866905ab41f3a3d3b6db667ca11ba (patch) | |
tree | 39f0c95a13ea8e10f1dbf80fbca8a396ba841da7 /backend | |
parent | 16a51bbab27ba5fa9348f21cfabff3085e25fc99 (diff) |
backend/x11: switch to wlr_swapchain
Diffstat (limited to 'backend')
-rw-r--r-- | backend/x11/backend.c | 156 | ||||
-rw-r--r-- | backend/x11/meson.build | 4 | ||||
-rw-r--r-- | backend/x11/output.c | 258 |
3 files changed, 385 insertions, 33 deletions
diff --git a/backend/x11/backend.c b/backend/x11/backend.c index f2b09a25..980bc14c 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -6,12 +6,16 @@ #include <stdio.h> #include <stdlib.h> #include <time.h> +#include <unistd.h> #include <wlr/config.h> +#include <drm_fourcc.h> #include <X11/Xlib-xcb.h> #include <wayland-server-core.h> #include <xcb/xcb.h> +#include <xcb/dri3.h> +#include <xcb/present.h> #include <xcb/xfixes.h> #include <xcb/xinput.h> @@ -25,8 +29,26 @@ #include <wlr/util/log.h> #include "backend/x11.h" +#include "render/drm_format_set.h" +#include "render/gbm_allocator.h" +#include "render/wlr_renderer.h" #include "util/signal.h" +// See dri2_format_for_depth in mesa +const struct wlr_x11_format formats[] = { + { .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 }, + { .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 }, +}; + +static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) { + for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { + if (formats[i].depth == depth) { + return &formats[i]; + } + } + return NULL; +} + struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_backend *x11, xcb_window_t window) { struct wlr_x11_output *output; @@ -82,6 +104,8 @@ static void handle_x11_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event; if (ev->extension == x11->xinput_opcode) { handle_x11_xinput_event(x11, ev); + } else if (ev->extension == x11->present_opcode) { + handle_x11_present_event(x11, ev); } else { handle_x11_unknown_event(x11, event); } @@ -157,6 +181,7 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_renderer_destroy(x11->renderer); wlr_egl_finish(&x11->egl); + free(x11->drm_format); #if WLR_HAS_XCB_ERRORS xcb_errors_context_free(x11->errors_context); @@ -190,6 +215,27 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { backend_destroy(&x11->backend); } +static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) { + xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); + while (iter.rem > 0) { + if (iter.data->depth == depth) { + return iter.data; + } + xcb_depth_next(&iter); + } + return NULL; +} + +static xcb_visualid_t pick_visualid(xcb_depth_t *depth) { + xcb_visualtype_t *visuals = xcb_depth_visuals(depth); + for (int i = 0; i < xcb_depth_visuals_length(depth); i++) { + if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) { + return visuals[i].visual_id; + } + } + return 0; +} + struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display, wlr_renderer_create_func_t create_renderer_func) { @@ -247,6 +293,47 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const xcb_query_extension_reply_t *ext; + // DRI3 extension + + ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id); + if (!ext || !ext->present) { + wlr_log(WLR_ERROR, "X11 does not support DRI3 extension"); + goto error_display; + } + + xcb_dri3_query_version_cookie_t dri3_cookie = + xcb_dri3_query_version(x11->xcb, 1, 2); + xcb_dri3_query_version_reply_t *dri3_reply = + xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL); + if (!dri3_reply || dri3_reply->major_version < 1 || + dri3_reply->minor_version < 2) { + wlr_log(WLR_ERROR, "X11 does not support required DRI3 version"); + goto error_display; + } + free(dri3_reply); + + // Present extension + + ext = xcb_get_extension_data(x11->xcb, &xcb_present_id); + if (!ext || !ext->present) { + wlr_log(WLR_ERROR, "X11 does not support Present extension"); + goto error_display; + } + x11->present_opcode = ext->major_opcode; + + xcb_present_query_version_cookie_t present_cookie = + xcb_present_query_version(x11->xcb, 1, 2); + xcb_present_query_version_reply_t *present_reply = + xcb_present_query_version_reply(x11->xcb, present_cookie, NULL); + if (!present_reply || present_reply->major_version < 1) { + wlr_log(WLR_ERROR, "X11 does not support required Present version"); + free(present_reply); + goto error_display; + } + free(present_reply); + + // Xfixes extension + ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xfixes extension"); @@ -265,6 +352,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } free(fixes_reply); + // Xinput extension + ext = xcb_get_extension_data(x11->xcb, &xcb_input_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xinput extension"); @@ -295,6 +384,32 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, wl_event_source_check(x11->event_source); x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data; + if (!x11->screen) { + wlr_log(WLR_ERROR, "Failed to get X11 screen"); + goto error_display; + } + + x11->depth = get_depth(x11->screen, 32); + if (!x11->depth) { + wlr_log(WLR_ERROR, "Failed to get 32-bit depth for X11 screen"); + goto error_display; + } + + x11->visualid = pick_visualid(x11->depth); + if (!x11->visualid) { + wlr_log(WLR_ERROR, "Failed to pick X11 visual"); + goto error_display; + } + + x11->x11_format = x11_format_from_depth(x11->depth->depth); + if (!x11->x11_format) { + wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth); + goto error_display; + } + + x11->colormap = xcb_generate_id(x11->xcb); + xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap, + x11->screen->root, x11->visualid); if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; @@ -306,18 +421,53 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 0, + EGL_ALPHA_SIZE, 1, EGL_NONE, }; x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR, x11->xlib_conn, config_attribs, x11->screen->root_visual); - if (x11->renderer == NULL) { wlr_log(WLR_ERROR, "Failed to create renderer"); goto error_event; } + // TODO: we can use DRI3Open instead + int drm_fd = wlr_renderer_get_drm_fd(x11->renderer); + if (fd < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM device FD from renderer"); + return false; + } + + drm_fd = dup(drm_fd); + if (fd < 0) { + wlr_log_errno(WLR_ERROR, "dup failed"); + return false; + } + + struct wlr_gbm_allocator *alloc = wlr_gbm_allocator_create(drm_fd); + if (alloc == NULL) { + wlr_log(WLR_ERROR, "Failed to create GBM allocator"); + return false; + } + x11->allocator = &alloc->base; + + const struct wlr_drm_format_set *formats = + wlr_renderer_get_dmabuf_render_formats(x11->renderer); + if (formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer"); + return false; + } + const struct wlr_drm_format *format = + wlr_drm_format_set_get(formats, x11->x11_format->drm); + if (format == NULL) { + wlr_log(WLR_ERROR, "Renderer doesn't support DRM format 0x%"PRIX32, + x11->x11_format->drm); + return false; + } + // TODO intersect modifiers with DRI3GetSupportedModifiers + x11->drm_format = wlr_drm_format_dup(format); + #if WLR_HAS_XCB_ERRORS if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) { wlr_log(WLR_ERROR, "Failed to create error context"); @@ -325,6 +475,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } #endif + x11->present_event_id = xcb_generate_id(x11->xcb); + wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD, &input_device_impl, "X11 keyboard", 0, 0); wlr_keyboard_init(&x11->keyboard, &keyboard_impl); diff --git a/backend/x11/meson.build b/backend/x11/meson.build index 40530bb0..51c6dd04 100644 --- a/backend/x11/meson.build +++ b/backend/x11/meson.build @@ -2,8 +2,10 @@ x11_libs = [] x11_required = [ 'x11-xcb', 'xcb', - 'xcb-xinput', + 'xcb-dri3', + 'xcb-present', 'xcb-xfixes', + 'xcb-xinput', ] msg = [] diff --git a/backend/x11/output.c b/backend/x11/output.c index 07374b61..0e81179d 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -4,6 +4,9 @@ #include <stdlib.h> #include <string.h> +#include <drm_fourcc.h> +#include <xcb/dri3.h> +#include <xcb/present.h> #include <xcb/xcb.h> #include <xcb/xinput.h> @@ -13,6 +16,8 @@ #include <wlr/util/log.h> #include "backend/x11.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" #include "util/signal.h" static int signal_frame(void *data) { @@ -85,7 +90,8 @@ static void output_destroy(struct wlr_output *wlr_output) { wl_list_remove(&output->link); wl_event_source_remove(output->frame_timer); - wlr_egl_destroy_surface(&x11->egl, output->surf); + wlr_buffer_unlock(output->back_buffer); + wlr_swapchain_destroy(output->swapchain); xcb_destroy_window(x11->xcb, output->win); xcb_flush(x11->xcb); free(output); @@ -96,7 +102,20 @@ static bool output_attach_render(struct wlr_output *wlr_output, struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; - return wlr_egl_make_current(&x11->egl, output->surf, buffer_age); + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); + if (!output->back_buffer) { + return false; + } + + if (!wlr_egl_make_current(&x11->egl, EGL_NO_SURFACE, NULL)) { + return false; + } + if (!wlr_renderer_bind_buffer(x11->renderer, output->back_buffer)) { + return false; + } + + return true; } static bool output_test(struct wlr_output *wlr_output) { @@ -112,6 +131,125 @@ static bool output_test(struct wlr_output *wlr_output) { return true; } +static struct wlr_x11_buffer *import_x11_buffer(struct wlr_x11_output *output, + struct wlr_buffer *wlr_buffer) { + struct wlr_x11_backend *x11 = output->x11; + + struct wlr_dmabuf_attributes attrs = {0}; + if (!wlr_buffer_get_dmabuf(wlr_buffer, &attrs)) { + return NULL; + } + + if (attrs.format != x11->x11_format->drm) { + // The pixmap's depth must match the window's depth, otherwise Present + // will throw a Match error + return NULL; + } + + // xcb closes the FDs after sending them, so we need to dup them here + struct wlr_dmabuf_attributes dup_attrs = {0}; + if (!wlr_dmabuf_attributes_copy(&dup_attrs, &attrs)) { + return NULL; + } + + const struct wlr_x11_format *x11_fmt = x11->x11_format; + xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb); + xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win, + attrs.n_planes, attrs.width, attrs.height, attrs.stride[0], + attrs.offset[0], attrs.stride[1], attrs.offset[1], attrs.stride[2], + attrs.offset[2], attrs.stride[3], attrs.offset[3], x11_fmt->depth, + x11_fmt->bpp, attrs.modifier, dup_attrs.fd); + + struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer)); + if (!buffer) { + xcb_free_pixmap(x11->xcb, pixmap); + return NULL; + } + buffer->buffer = wlr_buffer_lock(wlr_buffer); + buffer->pixmap = pixmap; + buffer->x11 = x11; + wl_list_insert(&output->buffers, &buffer->link); + return buffer; +} + +static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) { + if (!buffer) { + return; + } + wl_list_remove(&buffer->link); + xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap); + wlr_buffer_unlock(buffer->buffer); + free(buffer); +} + +static bool output_commit_buffer(struct wlr_x11_output *output) { + struct wlr_x11_backend *x11 = output->x11; + + assert(output->back_buffer != NULL); + + wlr_renderer_bind_buffer(x11->renderer, NULL); + wlr_egl_unset_current(&x11->egl); + + struct wlr_x11_buffer *x11_buffer = + import_x11_buffer(output, output->back_buffer); + if (!x11_buffer) { + goto error; + } + + xcb_xfixes_region_t region = XCB_NONE; + if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) { + pixman_region32_t *damage = &output->wlr_output.pending.damage; + + int rects_len = 0; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len); + + xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); + if (!xcb_rects) { + goto error; + } + + for (int i = 0; i < rects_len; i++) { + pixman_box32_t *box = &rects[i]; + xcb_rects[i] = (struct xcb_rectangle_t){ + .x = box->x1, + .y = box->y1, + .width = box->x2 - box->x1, + .height = box->y2 - box->y1, + }; + } + + xcb_xfixes_region_t region = xcb_generate_id(x11->xcb); + xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects); + + free(xcb_rects); + } + + uint32_t serial = output->wlr_output.commit_seq; + uint32_t options = 0; + xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial, + 0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, 0, 0, 0, + 0, NULL); + + if (region != XCB_NONE) { + xcb_xfixes_destroy_region(x11->xcb, region); + } + + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = NULL; + + wlr_swapchain_set_buffer_submitted(output->swapchain, x11_buffer->buffer); + + wlr_output_send_present(&output->wlr_output, NULL); + + return true; + +error: + destroy_x11_buffer(x11_buffer); + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = NULL; + return false; +} + static bool output_commit(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; @@ -145,24 +283,21 @@ static bool output_commit(struct wlr_output *wlr_output) { } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { - pixman_region32_t *damage = NULL; - if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { - damage = &wlr_output->pending.damage; - } - - if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) { + if (!output_commit_buffer(output)) { return false; } - - wlr_output_send_present(wlr_output, NULL); } + xcb_flush(x11->xcb); + return true; } static void output_rollback_render(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); - wlr_egl_unset_current(&output->x11->egl); + struct wlr_x11_backend *x11 = output->x11; + wlr_renderer_bind_buffer(x11->renderer, NULL); + wlr_egl_unset_current(&x11->egl); } static const struct wlr_output_impl output_impl = { @@ -186,6 +321,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { return NULL; } output->x11 = x11; + wl_list_init(&output->buffers); struct wlr_output *wlr_output = &output->wlr_output; wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display); @@ -193,6 +329,14 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output->width = 1024; wlr_output->height = 768; + output->swapchain = wlr_swapchain_create(x11->allocator, + wlr_output->width, wlr_output->height, x11->drm_format); + if (!output->swapchain) { + wlr_log(WLR_ERROR, "Failed to create swapchain"); + free(output); + return NULL; + } + output_set_refresh(&output->wlr_output, 0); snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", @@ -204,14 +348,18 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { "X11 output %zd", x11->last_output_num); wlr_output_set_description(wlr_output, description); - uint32_t mask = XCB_CW_EVENT_MASK; + // The X11 protocol requires us to set a colormap and border pixel if the + // depth doesn't match the root window's + uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; uint32_t values[] = { - XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY + 0, + XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, + x11->colormap, }; output->win = xcb_generate_id(x11->xcb); - xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win, - x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1, - XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values); + xcb_create_window(x11->xcb, x11->depth->depth, output->win, + x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values); struct { xcb_input_event_mask_t head; @@ -227,16 +375,14 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { XCB_INPUT_XI_EVENT_MASK_LEAVE | XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN | XCB_INPUT_XI_EVENT_MASK_TOUCH_END | - XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE, + XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE | + XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY, }; xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head); - output->surf = wlr_egl_create_surface(&x11->egl, &output->win); - if (!output->surf) { - wlr_log(WLR_ERROR, "Failed to create EGL surface"); - free(output); - return NULL; - } + uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY; + xcb_present_select_input(x11->xcb, x11->present_event_id, output->win, + present_mask); xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win, x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1, @@ -278,17 +424,30 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *ev) { // ignore events that set an invalid size: - if (ev->width > 0 && ev->height > 0) { - wlr_output_update_custom_mode(&output->wlr_output, ev->width, - ev->height, output->wlr_output.refresh); - - // Move the pointer to its new location - update_x11_pointer_position(output, output->x11->time); - } else { + if (ev->width == 0 || ev->height == 0) { wlr_log(WLR_DEBUG, "Ignoring X11 configure event for height=%d, width=%d", ev->width, ev->height); + return; + } + + if (output->swapchain->width != ev->width || + output->swapchain->height != ev->height) { + struct wlr_swapchain *swapchain = wlr_swapchain_create( + output->x11->allocator, ev->width, ev->height, + output->x11->drm_format); + if (!swapchain) { + return; + } + wlr_swapchain_destroy(output->swapchain); + output->swapchain = swapchain; } + + wlr_output_update_custom_mode(&output->wlr_output, ev->width, + ev->height, output->wlr_output.refresh); + + // Move the pointer to its new location + update_x11_pointer_position(output, output->x11->time); } bool wlr_output_is_x11(struct wlr_output *wlr_output) { @@ -310,3 +469,42 @@ void wlr_x11_output_set_title(struct wlr_output *output, const char *title) { x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8, strlen(title), title); } + +static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output, + xcb_pixmap_t pixmap) { + struct wlr_x11_buffer *buffer; + wl_list_for_each(buffer, &output->buffers, link) { + if (buffer->pixmap == pixmap) { + return buffer; + } + } + return NULL; +} + +void handle_x11_present_event(struct wlr_x11_backend *x11, + xcb_ge_generic_event_t *event) { + switch (event->event_type) { + case XCB_PRESENT_EVENT_IDLE_NOTIFY:; + xcb_present_idle_notify_event_t *idle_notify = + (xcb_present_idle_notify_event_t *)event; + + struct wlr_x11_output *output = + get_x11_output_from_window_id(x11, idle_notify->window); + if (!output) { + wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window"); + return; + } + + struct wlr_x11_buffer *buffer = + get_x11_buffer(output, idle_notify->pixmap); + if (!buffer) { + wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer"); + return; + } + + destroy_x11_buffer(buffer); + break; + default: + wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type); + } +} |