From 5bddb5a909f9cf768cfcdbdd2bb943eb01484961 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 24 Sep 2019 15:33:15 +0300 Subject: [PATCH] backend/wayland: add support for direct scan-out Closes: https://github.com/swaywm/wlroots/issues/1830 --- backend/wayland/backend.c | 38 +++++++++++++++- backend/wayland/output.c | 94 +++++++++++++++++++++++++++++++++++++-- include/backend/wayland.h | 5 +++ protocol/meson.build | 1 + 4 files changed, 134 insertions(+), 4 deletions(-) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 7afd5e7e..21dc8ca7 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -5,6 +5,7 @@ #include +#include #include #include @@ -16,8 +17,9 @@ #include "backend/wayland.h" #include "util/signal.h" -#include "xdg-decoration-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" +#include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" #include "tablet-unstable-v2-client-protocol.h" @@ -59,6 +61,29 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { xdg_wm_base_handle_ping, }; +static void linux_dmabuf_v1_handle_format(void *data, + struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) { + // Note, this event is deprecated + struct wlr_wl_backend *wl = data; + + wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, + DRM_FORMAT_MOD_INVALID); +} + +static void linux_dmabuf_v1_handle_modifier(void *data, + struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format, + uint32_t modifier_hi, uint32_t modifier_lo) { + struct wlr_wl_backend *wl = data; + + uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo; + wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, modifier); +} + +static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { + .format = linux_dmabuf_v1_handle_format, + .modifier = linux_dmabuf_v1_handle_modifier, +}; + static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *iface, uint32_t version) { struct wlr_wl_backend *wl = data; @@ -85,6 +110,12 @@ static void registry_global(void *data, struct wl_registry *registry, } else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) { wl->tablet_manager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); + } else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 && + version >= 3) { + wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name, + &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1, + &linux_dmabuf_v1_listener, wl); } } @@ -153,6 +184,8 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_renderer_destroy(wl->renderer); wlr_egl_finish(&wl->egl); + wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats); + if (wl->pointer) { wl_pointer_destroy(wl->pointer); } @@ -165,6 +198,9 @@ static void backend_destroy(struct wlr_backend *backend) { if (wl->zwp_pointer_gestures_v1) { zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1); } + if (wl->zwp_linux_dmabuf_v1) { + zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1); + } xdg_wm_base_destroy(wl->xdg_wm_base); wl_compositor_destroy(wl->compositor); wl_registry_destroy(wl->registry); diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 1d697d9a..7a13e041 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -16,6 +16,7 @@ #include "backend/wayland.h" #include "util/signal.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -59,6 +60,59 @@ static bool output_attach_render(struct wlr_output *wlr_output, buffer_age); } +static bool output_attach_buffer(struct wlr_output *wlr_output, + struct wlr_buffer *buffer) { + struct wlr_wl_output *output = + get_wl_output_from_output(wlr_output); + struct wlr_wl_backend *wl = output->backend; + + struct wlr_dmabuf_attributes attribs; + if (!wlr_buffer_get_dmabuf(buffer, &attribs)) { + return false; + } + + if (attribs.width != wlr_output->width || + attribs.height != wlr_output->height) { + return false; + } + + if (!wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats, + attribs.format, attribs.modifier)) { + return false; + } + + uint32_t modifier_hi = attribs.modifier >> 32; + uint32_t modifier_lo = (uint32_t)attribs.modifier; + struct zwp_linux_buffer_params_v1 *params = + zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1); + for (int i = 0; i < attribs.n_planes; i++) { + zwp_linux_buffer_params_v1_add(params, attribs.fd[i], i, + attribs.offset[i], attribs.stride[i], modifier_hi, modifier_lo); + } + + uint32_t flags = 0; + if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) { + flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; + } + if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED) { + flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; + } + if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST) { + flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; + } + struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed( + params, attribs.width, attribs.height, attribs.format, flags); + // TODO: handle create() errors + + wl_surface_attach(output->surface, wl_buffer, 0, 0); + + if (output->pending_wl_buffer != NULL) { + wl_buffer_destroy(output->pending_wl_buffer); + } + output->pending_wl_buffer = wl_buffer; + return true; +} + static bool output_commit(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); @@ -76,13 +130,46 @@ static bool output_commit(struct wlr_output *wlr_output) { damage = &wlr_output->pending.damage; } - if (!wlr_egl_swap_buffers(&output->backend->egl, - output->egl_surface, damage)) { - return false; + wlr_buffer_unref(output->current_buffer); + output->current_buffer = NULL; + if (output->current_wl_buffer != NULL) { + wl_buffer_destroy(output->current_wl_buffer); + output->current_wl_buffer = NULL; + } + + assert(wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER); + switch (wlr_output->pending.buffer_type) { + case WLR_OUTPUT_STATE_BUFFER_RENDER: + if (!wlr_egl_swap_buffers(&output->backend->egl, + output->egl_surface, damage)) { + return false; + } + break; + case WLR_OUTPUT_STATE_BUFFER_SCANOUT: + if (damage == NULL) { + wl_surface_damage_buffer(output->surface, + 0, 0, INT32_MAX, INT32_MAX); + } else { + int rects_len; + pixman_box32_t *rects = + pixman_region32_rectangles(damage, &rects_len); + for (int i = 0; i < rects_len; i++) { + pixman_box32_t *r = &rects[i]; + wl_surface_damage_buffer(output->surface, r->x1, r->y1, + r->x2 - r->x1, r->y2 - r->y1); + } + } + wl_surface_commit(output->surface); + + output->current_buffer = wlr_buffer_ref(wlr_output->pending.buffer); + output->current_wl_buffer = output->pending_wl_buffer; + output->pending_wl_buffer = NULL; + break; } // TODO: if available, use the presentation-time protocol wlr_output_send_present(wlr_output, NULL); + return true; } @@ -222,6 +309,7 @@ static const struct wlr_output_impl output_impl = { .set_custom_mode = output_set_custom_mode, .destroy = output_destroy, .attach_render = output_attach_render, + .attach_buffer = output_attach_buffer, .commit = output_commit, .set_cursor = output_set_cursor, .move_cursor = output_move_cursor, diff --git a/include/backend/wayland.h b/include/backend/wayland.h index a237ced7..886aea9f 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -12,6 +12,7 @@ #include #include #include +#include struct wlr_wl_backend { struct wlr_backend backend; @@ -34,12 +35,14 @@ struct wlr_wl_backend { struct xdg_wm_base *xdg_wm_base; struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1; struct zwp_pointer_gestures_v1 *zwp_pointer_gestures_v1; + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1; struct wl_seat *seat; struct wl_pointer *pointer; struct wl_keyboard *keyboard; struct wlr_wl_pointer *current_pointer; struct zwp_tablet_manager_v2 *tablet_manager; char *seat_name; + struct wlr_drm_format_set linux_dmabuf_v1_formats; }; struct wlr_wl_output { @@ -55,6 +58,8 @@ struct wlr_wl_output { struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; struct wl_egl_window *egl_window; EGLSurface egl_surface; + struct wl_buffer *pending_wl_buffer, *current_wl_buffer; + struct wlr_buffer *current_buffer; uint32_t enter_serial; diff --git a/protocol/meson.build b/protocol/meson.build index 25694010..13dfaefb 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -43,6 +43,7 @@ protocols = [ client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], [wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],