diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 2caf76bd81..c7a72a3544 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -238,6 +239,35 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, goto error_event; } + if (drm->parent) { + // We'll perform a multi-GPU copy for all submitted buffers, we need + // to be able to texture from them + struct wlr_renderer *renderer = drm->renderer.wlr_rend; + const struct wlr_drm_format_set *texture_formats = + wlr_renderer_get_dmabuf_texture_formats(renderer); + if (texture_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to query renderer texture formats"); + goto error_event; + } + + for (size_t i = 0; i < texture_formats->len; i++) { + const struct wlr_drm_format *fmt = texture_formats->formats[i]; + if (fmt->len == 0) { + // Modifiers aren't supported. The implicit modifier changes + // from a GPU to the other, so we can only accept linear + // buffers + wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, + DRM_FORMAT_MOD_LINEAR); + continue; + } + + for (size_t j = 0; j < fmt->len; j++) { + wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, + fmt->modifiers[j]); + } + } + } + drm->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &drm->session_destroy); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 2c9560f1b2..2864979112 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -25,10 +25,20 @@ #include "backend/drm/iface.h" #include "backend/drm/util.h" #include "render/pixel_format.h" +#include "render/drm_format_set.h" #include "render/swapchain.h" +#include "render/wlr_renderer.h" +#include "types/wlr_buffer.h" #include "util/signal.h" bool check_drm_features(struct wlr_drm_backend *drm) { + if (drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &drm->cursor_width)) { + drm->cursor_width = 64; + } + if (drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &drm->cursor_height)) { + drm->cursor_height = 64; + } + uint64_t cap; if (drmGetCap(drm->fd, DRM_CAP_PRIME, &cap) || !(cap & DRM_PRIME_CAP_IMPORT)) { @@ -321,7 +331,7 @@ static bool drm_connector_attach_render(struct wlr_output *output, static void drm_plane_set_committed(struct wlr_drm_plane *plane) { drm_fb_move(&plane->queued_fb, &plane->pending_fb); - if (plane->queued_fb) { + if (plane->queued_fb && plane->surf.swapchain) { wlr_swapchain_set_buffer_submitted(plane->surf.swapchain, plane->queued_fb->wlr_buf); } @@ -668,10 +678,9 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, int width = mode.hdisplay; int height = mode.vdisplay; - uint32_t format = DRM_FORMAT_ARGB8888; bool modifiers = drm->addfb2_modifiers; - if (!drm_plane_init_surface(plane, drm, width, height, format, modifiers) || + if (!drm_plane_init_surface(plane, drm, width, height, modifiers) || !drm_connector_pageflip_renderer(conn, state)) { if (!modifiers) { wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer:" @@ -686,8 +695,7 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, "retrying without modifiers"); modifiers = false; - if (!drm_plane_init_surface(plane, drm, width, height, format, - modifiers)) { + if (!drm_plane_init_surface(plane, drm, width, height, modifiers)) { return false; } if (!drm_connector_pageflip_renderer(conn, state)) { @@ -828,9 +836,7 @@ struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output, } static bool drm_connector_set_cursor(struct wlr_output *output, - struct wlr_texture *texture, float scale, - enum wl_output_transform transform, - int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { + struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; @@ -844,91 +850,59 @@ static bool drm_connector_set_cursor(struct wlr_output *output, return false; } - if (!plane->surf.swapchain) { - 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 (!drm_plane_init_surface(plane, drm, w, h, - DRM_FORMAT_ARGB8888, true)) { - wlr_drm_conn_log(conn, WLR_ERROR, "Cannot allocate cursor resources"); - return false; - } - } - - struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; - wlr_box_transform(&hotspot, &hotspot, - wlr_output_transform_invert(output->transform), - plane->surf.width, plane->surf.height); - - if (plane->cursor_hotspot_x != hotspot.x || - plane->cursor_hotspot_y != hotspot.y) { + if (conn->cursor_hotspot_x != hotspot_x || + conn->cursor_hotspot_y != hotspot_y) { // Update cursor hotspot - conn->cursor_x -= hotspot.x - plane->cursor_hotspot_x; - conn->cursor_y -= hotspot.y - plane->cursor_hotspot_y; - plane->cursor_hotspot_x = hotspot.x; - plane->cursor_hotspot_y = hotspot.y; + conn->cursor_x -= hotspot_x - conn->cursor_hotspot_x; + conn->cursor_y -= hotspot_y - conn->cursor_hotspot_y; + conn->cursor_hotspot_x = hotspot_x; + conn->cursor_hotspot_y = hotspot_y; wlr_output_update_needs_frame(output); } - if (!update_texture) { - // Don't update cursor image - return true; - } - - plane->cursor_enabled = false; - if (texture != NULL) { - int width = texture->width * output->scale / scale; - int height = texture->height * output->scale / scale; - - if (width > (int)plane->surf.width || height > (int)plane->surf.height) { - wlr_drm_conn_log(conn, WLR_ERROR, "Cursor too large (max %dx%d)", - (int)plane->surf.width, (int)plane->surf.height); - return false; - } - - if (!drm_surface_make_current(&plane->surf, NULL)) { + conn->cursor_enabled = false; + if (buffer != NULL) { + if ((uint64_t)buffer->width != drm->cursor_width || + (uint64_t)buffer->height != drm->cursor_height) { + wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); return false; } - struct wlr_renderer *rend = plane->surf.renderer->wlr_rend; - - struct wlr_box cursor_box = { .width = width, .height = height }; + struct wlr_buffer *local_buf; + if (drm->parent) { + struct wlr_drm_format *format = + drm_plane_pick_render_format(plane, &drm->renderer); + if (format == NULL) { + wlr_log(WLR_ERROR, "Failed to pick cursor plane format"); + return false; + } - float output_matrix[9]; - wlr_matrix_identity(output_matrix); - if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { - struct wlr_box tr_size = { - .width = plane->surf.width, - .height = plane->surf.height, - }; - wlr_box_transform(&tr_size, &tr_size, output->transform, 0, 0); + bool ok = init_drm_surface(&plane->mgpu_surf, &drm->renderer, + buffer->width, buffer->height, format); + free(format); + if (!ok) { + return false; + } - wlr_matrix_translate(output_matrix, plane->surf.width / 2.0, - plane->surf.height / 2.0); - wlr_matrix_transform(output_matrix, output->transform); - wlr_matrix_translate(output_matrix, - tr_size.width / 2.0, - - tr_size.height / 2.0); + local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); + if (local_buf == NULL) { + return false; + } + } else { + local_buf = wlr_buffer_lock(buffer); } - float matrix[9]; - wlr_matrix_project_box(matrix, &cursor_box, transform, 0, - output_matrix); - - wlr_renderer_begin(rend, plane->surf.width, plane->surf.height); - wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 }); - wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); - wlr_renderer_end(rend); - - if (!drm_plane_lock_surface(plane, drm)) { + bool ok = drm_fb_import(&plane->pending_fb, drm, local_buf, + &plane->formats); + wlr_buffer_unlock(local_buf); + if (!ok) { return false; } - plane->cursor_enabled = true; + conn->cursor_enabled = true; + conn->cursor_width = buffer->width; + conn->cursor_height = buffer->height; } wlr_output_update_needs_frame(output); @@ -955,8 +929,8 @@ static bool drm_connector_move_cursor(struct wlr_output *output, wlr_output_transform_invert(output->transform); wlr_box_transform(&box, &box, transform, width, height); - box.x -= plane->cursor_hotspot_x; - box.y -= plane->cursor_hotspot_y; + box.x -= conn->cursor_hotspot_x; + box.y -= conn->cursor_hotspot_y; conn->cursor_x = box.x; conn->cursor_y = box.y; @@ -966,14 +940,11 @@ static bool drm_connector_move_cursor(struct wlr_output *output, } bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) { - assert(conn->crtc != NULL && conn->crtc->cursor != NULL); - struct wlr_drm_plane *plane = conn->crtc->cursor; - - return plane->cursor_enabled && + return conn->cursor_enabled && conn->cursor_x < conn->output.width && conn->cursor_y < conn->output.height && - conn->cursor_x + (int)plane->surf.width >= 0 && - conn->cursor_y + (int)plane->surf.height >= 0; + conn->cursor_x + conn->cursor_width >= 0 && + conn->cursor_y + conn->cursor_height >= 0; } static void dealloc_crtc(struct wlr_drm_connector *conn); @@ -1004,6 +975,32 @@ static void drm_connector_destroy_output(struct wlr_output *output) { memset(&conn->output, 0, sizeof(struct wlr_output)); } +static const struct wlr_drm_format_set *drm_connector_get_cursor_formats( + struct wlr_output *output, uint32_t buffer_caps) { + if (!(buffer_caps & WLR_BUFFER_CAP_DMABUF)) { + return NULL; + } + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + if (!conn->crtc) { + return false; + } + struct wlr_drm_plane *plane = conn->crtc->cursor; + if (!plane) { + return false; + } + if (conn->backend->parent) { + return &conn->backend->mgpu_formats; + } + return &plane->formats; +} + +static void drm_connector_get_cursor_size(struct wlr_output *output, + int *width, int *height) { + struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + *width = (int)drm->cursor_width; + *height = (int)drm->cursor_height; +} + static const struct wlr_output_impl output_impl = { .set_cursor = drm_connector_set_cursor, .move_cursor = drm_connector_move_cursor, @@ -1014,6 +1011,8 @@ static const struct wlr_output_impl output_impl = { .rollback_render = drm_connector_rollback_render, .get_gamma_size = drm_connector_get_gamma_size, .export_dmabuf = drm_connector_export_dmabuf, + .get_cursor_formats = drm_connector_get_cursor_formats, + .get_cursor_size = drm_connector_get_cursor_size, }; bool wlr_output_is_drm(struct wlr_output *output) { @@ -1093,10 +1092,8 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) { drm_plane_finish_surface(conn->crtc->primary); drm_plane_finish_surface(conn->crtc->cursor); - if (conn->crtc->cursor != NULL) { - conn->crtc->cursor->cursor_enabled = false; - } + conn->cursor_enabled = false; conn->crtc = NULL; } diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 5ca7ed46bb..e1e35e6346 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -62,7 +62,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { gbm_device_destroy(renderer->gbm); } -static bool init_drm_surface(struct wlr_drm_surface *surf, +bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, const struct wlr_drm_format *drm_format) { if (surf->width == width && surf->height == height) { @@ -126,7 +126,7 @@ void drm_surface_unset_current(struct wlr_drm_surface *surf) { surf->back_buffer = NULL; } -static struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, +struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, struct wlr_buffer *buffer) { struct wlr_renderer *renderer = surf->renderer->wlr_rend; @@ -191,86 +191,95 @@ static struct wlr_drm_format *create_linear_format(uint32_t format) { return fmt; } -bool drm_plane_init_surface(struct wlr_drm_plane *plane, - struct wlr_drm_backend *drm, int32_t width, uint32_t height, - uint32_t format, bool with_modifiers) { - if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) { - const struct wlr_pixel_format_info *info = - drm_get_pixel_format_info(format); - if (!info) { - wlr_log(WLR_ERROR, - "Failed to fallback on DRM opaque substitute for format " - "0x%"PRIX32, format); - return false; - } - format = info->opaque_substitute; - } - - const struct wlr_drm_format *plane_format = - wlr_drm_format_set_get(&plane->formats, format); - if (plane_format == NULL) { - wlr_log(WLR_ERROR, "Plane %"PRIu32" doesn't support format 0x%"PRIX32, - plane->id, format); - return false; - } - +struct wlr_drm_format *drm_plane_pick_render_format( + struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) { const struct wlr_drm_format_set *render_formats = - wlr_renderer_get_render_formats(drm->renderer.wlr_rend); + wlr_renderer_get_render_formats(renderer->wlr_rend); if (render_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get render formats"); - return false; + return NULL; + } + + const struct wlr_drm_format_set *plane_formats = &plane->formats; + + uint32_t fmt = DRM_FORMAT_ARGB8888; + if (!wlr_drm_format_set_has(&plane->formats, fmt, DRM_FORMAT_MOD_INVALID)) { + const struct wlr_pixel_format_info *format_info = + drm_get_pixel_format_info(fmt); + assert(format_info != NULL && + format_info->opaque_substitute != DRM_FORMAT_INVALID); + fmt = format_info->opaque_substitute; } + const struct wlr_drm_format *render_format = - wlr_drm_format_set_get(render_formats, format); + wlr_drm_format_set_get(render_formats, fmt); if (render_format == NULL) { - wlr_log(WLR_ERROR, "Renderer doesn't support format 0x%"PRIX32, - format); - return false; + wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); + return NULL; } - struct wlr_drm_format *format_implicit_modifier = NULL; - if (!with_modifiers) { - format_implicit_modifier = wlr_drm_format_create(format); - render_format = format_implicit_modifier; + const struct wlr_drm_format *plane_format = + wlr_drm_format_set_get(plane_formats, fmt); + if (plane_format == NULL) { + wlr_log(WLR_DEBUG, "Plane %"PRIu32" doesn't support format 0x%"PRIX32, + plane->id, fmt); + return NULL; } - struct wlr_drm_format *drm_format = + struct wlr_drm_format *format = wlr_drm_format_intersect(plane_format, render_format); - if (drm_format == NULL) { - wlr_log(WLR_ERROR, - "Failed to intersect plane and render formats 0x%"PRIX32, - format); - free(format_implicit_modifier); + if (format == NULL) { + wlr_log(WLR_DEBUG, "Failed to intersect plane and render " + "modifiers for format 0x%"PRIX32, fmt); + return NULL; + } + + return format; +} + +bool drm_plane_init_surface(struct wlr_drm_plane *plane, + struct wlr_drm_backend *drm, int32_t width, uint32_t height, + bool with_modifiers) { + struct wlr_drm_format *format = + drm_plane_pick_render_format(plane, &drm->renderer); + if (format == NULL) { + wlr_log(WLR_ERROR, "Failed to pick render format for plane %"PRIu32, + plane->id); return false; } + if (!with_modifiers) { + struct wlr_drm_format *format_implicit_modifier = + wlr_drm_format_create(format->format); + free(format); + format = format_implicit_modifier; + } + drm_plane_finish_surface(plane); bool ok = true; if (!drm->parent) { ok = init_drm_surface(&plane->surf, &drm->renderer, - width, height, drm_format); + width, height, format); } else { - struct wlr_drm_format *drm_format_linear = create_linear_format(format); - if (drm_format_linear == NULL) { - free(drm_format); - free(format_implicit_modifier); + struct wlr_drm_format *format_linear = create_linear_format(format->format); + if (format_linear == NULL) { + free(format); return false; } ok = init_drm_surface(&plane->surf, &drm->parent->renderer, - width, height, drm_format_linear); - free(drm_format_linear); + width, height, format_linear); + free(format_linear); if (ok && !init_drm_surface(&plane->mgpu_surf, &drm->renderer, - width, height, drm_format)) { + width, height, format)) { finish_drm_surface(&plane->surf); ok = false; } } - free(drm_format); - free(format_implicit_modifier); + free(format); return ok; } diff --git a/backend/wayland/output.c b/backend/wayland/output.c index ae2f942ebb..62e9b5afb5 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -20,6 +20,7 @@ #include "render/pixel_format.h" #include "render/swapchain.h" #include "render/wlr_renderer.h" +#include "types/wlr_buffer.h" #include "util/signal.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" @@ -405,28 +406,12 @@ static void output_rollback_render(struct wlr_output *wlr_output) { } static bool output_set_cursor(struct wlr_output *wlr_output, - struct wlr_texture *texture, float scale, - enum wl_output_transform transform, - int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { + struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); struct wlr_wl_backend *backend = output->backend; - struct wlr_renderer *renderer = wlr_backend_get_renderer(&backend->backend); - struct wlr_allocator *allocator = backend_get_allocator(&backend->backend); - - struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; - wlr_box_transform(&hotspot, &hotspot, - wlr_output_transform_invert(wlr_output->transform), - output->cursor.width, output->cursor.height); - // TODO: use output->wlr_output.transform to transform pixels and hotpot - output->cursor.hotspot_x = hotspot.x; - output->cursor.hotspot_y = hotspot.y; - - if (!update_texture) { - // Update hotspot without changing cursor image - update_wl_output_cursor(output); - return true; - } + output->cursor.hotspot_x = hotspot_x; + output->cursor.hotspot_y = hotspot_y; if (output->cursor.surface == NULL) { output->cursor.surface = @@ -434,58 +419,7 @@ static bool output_set_cursor(struct wlr_output *wlr_output, } struct wl_surface *surface = output->cursor.surface; - if (texture != NULL) { - int width = texture->width * wlr_output->scale / scale; - int height = texture->height * wlr_output->scale / scale; - - if (output->cursor.swapchain == NULL || - output->cursor.swapchain->width != width || - output->cursor.swapchain->height != height) { - wlr_swapchain_destroy(output->cursor.swapchain); - output->cursor.swapchain = wlr_swapchain_create(allocator, - width, height, output->backend->format); - if (output->cursor.swapchain == NULL) { - return false; - } - } - - struct wlr_buffer *wlr_buffer = - wlr_swapchain_acquire(output->cursor.swapchain, NULL); - if (wlr_buffer == NULL) { - return false; - } - - if (!wlr_renderer_bind_buffer(renderer, wlr_buffer)) { - return false; - } - - struct wlr_box cursor_box = { - .width = width, - .height = height, - }; - - float output_matrix[9]; - wlr_matrix_identity(output_matrix); - if (wlr_output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { - struct wlr_box tr_size = { .width = width, .height = height }; - wlr_box_transform(&tr_size, &tr_size, wlr_output->transform, 0, 0); - - wlr_matrix_translate(output_matrix, width / 2.0, height / 2.0); - wlr_matrix_transform(output_matrix, wlr_output->transform); - wlr_matrix_translate(output_matrix, - - tr_size.width / 2.0, - tr_size.height / 2.0); - } - - float matrix[9]; - wlr_matrix_project_box(matrix, &cursor_box, transform, 0, output_matrix); - - wlr_renderer_begin(renderer, width, height); - wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); - wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); - wlr_renderer_end(renderer); - - wlr_renderer_bind_buffer(renderer, NULL); - + if (wlr_buffer != NULL) { struct wlr_wl_buffer *buffer = get_or_create_wl_buffer(output->backend, wlr_buffer); if (buffer == NULL) { @@ -495,11 +429,6 @@ static bool output_set_cursor(struct wlr_output *wlr_output, wl_surface_attach(surface, buffer->wl_buffer, 0, 0); wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(surface); - - wlr_buffer_unlock(wlr_buffer); - - output->cursor.width = width; - output->cursor.height = height; } else { wl_surface_attach(surface, NULL, 0, 0); wl_surface_commit(surface); @@ -510,6 +439,17 @@ static bool output_set_cursor(struct wlr_output *wlr_output, return true; } +static const struct wlr_drm_format_set *output_get_cursor_formats( + struct wlr_output *wlr_output, uint32_t buffer_caps) { + struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); + if (buffer_caps & WLR_BUFFER_CAP_DMABUF) { + return &output->backend->linux_dmabuf_v1_formats; + } else if (buffer_caps & WLR_BUFFER_CAP_SHM) { + return &output->backend->shm_formats; + } + return NULL; +} + static void output_destroy(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (output == NULL) { @@ -518,7 +458,6 @@ static void output_destroy(struct wlr_output *wlr_output) { wl_list_remove(&output->link); - wlr_swapchain_destroy(output->cursor.swapchain); if (output->cursor.surface) { wl_surface_destroy(output->cursor.surface); } @@ -569,6 +508,7 @@ static const struct wlr_output_impl output_impl = { .rollback_render = output_rollback_render, .set_cursor = output_set_cursor, .move_cursor = output_move_cursor, + .get_cursor_formats = output_get_cursor_formats, }; bool wlr_output_is_wl(struct wlr_output *wlr_output) { diff --git a/backend/x11/output.c b/backend/x11/output.c index bba938d299..56161dee26 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -420,83 +420,38 @@ static void update_x11_output_cursor(struct wlr_x11_output *output, } static bool output_cursor_to_picture(struct wlr_x11_output *output, - struct wlr_texture *texture, enum wl_output_transform transform, - int width, int height) { + struct wlr_buffer *buffer) { struct wlr_x11_backend *x11 = output->x11; - struct wlr_allocator *allocator = backend_get_allocator(&x11->backend); struct wlr_renderer *renderer = wlr_backend_get_renderer(&x11->backend); - int depth = 32; - int stride = width * 4; if (output->cursor.pic != XCB_NONE) { xcb_render_free_picture(x11->xcb, output->cursor.pic); } output->cursor.pic = XCB_NONE; - if (texture == NULL) { + if (buffer == NULL) { return true; } - if (output->cursor.swapchain == NULL || - output->cursor.swapchain->width != width || - output->cursor.swapchain->height != height) { - wlr_swapchain_destroy(output->cursor.swapchain); - output->cursor.swapchain = wlr_swapchain_create(allocator, - width, height, x11->drm_format); - if (output->cursor.swapchain == NULL) { - return false; - } - } - - struct wlr_buffer *wlr_buffer = - wlr_swapchain_acquire(output->cursor.swapchain, NULL); - if (wlr_buffer == NULL) { - return false; - } + int depth = 32; + int stride = buffer->width * 4; - if (!wlr_renderer_bind_buffer(renderer, wlr_buffer)) { + if (!wlr_renderer_bind_buffer(renderer, buffer)) { return false; } - uint8_t *data = malloc(width * height * 4); + uint8_t *data = malloc(buffer->height * stride); if (data == NULL) { return false; } - struct wlr_box cursor_box = { - .width = width, - .height = height, - }; - - float output_matrix[9]; - wlr_matrix_identity(output_matrix); - if (output->wlr_output.transform != WL_OUTPUT_TRANSFORM_NORMAL) { - struct wlr_box tr_size = { .width = width, .height = height }; - wlr_box_transform(&tr_size, &tr_size, output->wlr_output.transform, 0, 0); - - wlr_matrix_translate(output_matrix, width / 2.0, height / 2.0); - wlr_matrix_transform(output_matrix, output->wlr_output.transform); - wlr_matrix_translate(output_matrix, - - tr_size.width / 2.0, - tr_size.height / 2.0); - } - - float matrix[9]; - wlr_matrix_project_box(matrix, &cursor_box, transform, 0, output_matrix); - - wlr_renderer_begin(renderer, width, height); - wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); - wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); - wlr_renderer_end(renderer); - bool result = wlr_renderer_read_pixels( renderer, DRM_FORMAT_ARGB8888, NULL, - width * 4, width, height, 0, 0, 0, 0, + stride, buffer->width, buffer->height, 0, 0, 0, 0, data); wlr_renderer_bind_buffer(renderer, NULL); - wlr_buffer_unlock(wlr_buffer); - if (!result) { free(data); return false; @@ -504,7 +459,7 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, xcb_pixmap_t pix = xcb_generate_id(x11->xcb); xcb_create_pixmap(x11->xcb, depth, pix, output->win, - width, height); + buffer->width, buffer->height); output->cursor.pic = xcb_generate_id(x11->xcb); xcb_render_create_picture(x11->xcb, output->cursor.pic, @@ -514,9 +469,8 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, xcb_create_gc(x11->xcb, gc, pix, 0, NULL); xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP, - pix, gc, width, height, 0, 0, 0, depth, - stride * height * sizeof(uint8_t), - data); + pix, gc, buffer->width, buffer->height, 0, 0, 0, depth, + stride * buffer->height * sizeof(uint8_t), data); free(data); xcb_free_gc(x11->xcb, gc); xcb_free_pixmap(x11->xcb, pix); @@ -525,55 +479,32 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, } static bool output_set_cursor(struct wlr_output *wlr_output, - struct wlr_texture *texture, float scale, - enum wl_output_transform transform, - int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { + struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; - int width = 0, height = 0; if (x11->argb32 == XCB_NONE) { return false; } - if (texture != NULL) { - width = texture->width * wlr_output->scale / scale; - height = texture->height * wlr_output->scale / scale; - + if (buffer != NULL) { if (hotspot_x < 0) { hotspot_x = 0; } - if ((uint32_t)hotspot_x > texture->width) { - hotspot_x = texture->width; + if (hotspot_x > buffer->width) { + hotspot_x = buffer->width; } if (hotspot_y < 0) { hotspot_y = 0; } - if ((uint32_t)hotspot_y > texture->height) { - hotspot_y = texture->height; - } - } - - struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; - wlr_box_transform(&hotspot, &hotspot, - wlr_output_transform_invert(wlr_output->transform), - width, height); - - if (!update_texture) { - // This means we previously had a failure of some sort. - if (texture != NULL && output->cursor.pic == XCB_NONE) { - return false; + if (hotspot_y > buffer->height) { + hotspot_y = buffer->height; } - - // Update hotspot without changing cursor image - update_x11_output_cursor(output, hotspot.x, hotspot.y); - return true; } - bool success = output_cursor_to_picture(output, texture, transform, - width, height); + bool success = output_cursor_to_picture(output, buffer); - update_x11_output_cursor(output, hotspot.x, hotspot.y); + update_x11_output_cursor(output, hotspot_x, hotspot_y); return success; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 13ccd672ce..378e9d20d9 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -34,10 +34,6 @@ struct wlr_drm_plane { struct wlr_drm_format_set formats; - // Only used by cursor - bool cursor_enabled; - int32_t cursor_hotspot_x, cursor_hotspot_y; - union wlr_drm_plane_props props; }; @@ -87,6 +83,10 @@ struct wlr_drm_backend { struct wlr_drm_renderer renderer; struct wlr_session *session; + + uint64_t cursor_width, cursor_height; + + struct wlr_drm_format_set mgpu_formats; }; enum wlr_drm_connector_state { @@ -118,7 +118,10 @@ struct wlr_drm_connector { union wlr_drm_connector_props props; - int32_t cursor_x, cursor_y; + bool cursor_enabled; + int cursor_x, cursor_y; + int cursor_width, cursor_height; + int cursor_hotspot_x, cursor_hotspot_y; drmModeCrtc *old_crtc; diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index 089c7923c1..468dd41e23 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -43,6 +43,9 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer); void finish_drm_renderer(struct wlr_drm_renderer *renderer); +bool init_drm_surface(struct wlr_drm_surface *surf, + struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, + const struct wlr_drm_format *drm_format); bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); void drm_surface_unset_current(struct wlr_drm_surface *surf); @@ -53,11 +56,15 @@ void drm_fb_destroy(struct wlr_drm_fb *fb); void drm_fb_clear(struct wlr_drm_fb **fb); void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); +struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, + struct wlr_buffer *buffer); bool drm_surface_render_black_frame(struct wlr_drm_surface *surf); +struct wlr_drm_format *drm_plane_pick_render_format( + struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer); bool drm_plane_init_surface(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm, int32_t width, uint32_t height, - uint32_t format, bool with_modifiers); + bool with_modifiers); void drm_plane_finish_surface(struct wlr_drm_plane *plane); bool drm_plane_lock_surface(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm); diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 59758ffd66..923bfe72e3 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -83,9 +83,7 @@ struct wlr_wl_output { struct { struct wlr_wl_pointer *pointer; struct wl_surface *surface; - struct wlr_swapchain *swapchain; int32_t hotspot_x, hotspot_y; - int32_t width, height; } cursor; }; diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 83f9716fc6..aef4136795 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -24,22 +24,15 @@ struct wlr_output_impl { /** * Set the output cursor plane image. * - * The parameters describe the image texture, its scale and its transform. - * If the scale and transform doesn't match the output's, the backend is - * responsible for scaling and transforming the texture appropriately. - * If texture is NULL, the cursor should be hidden. + * If buffer is NULL, the cursor should be hidden. * * The hotspot indicates the offset that needs to be applied to the * top-left corner of the image to match the cursor position. In other * words, the image should be displayed at (x - hotspot_x, y - hotspot_y). * The hotspot is given in the texture's coordinate space. - * - * If update_texture is true, all parameters need to be taken into account. - * If update_texture is false, only the hotspot is to be updated. */ - bool (*set_cursor)(struct wlr_output *output, struct wlr_texture *texture, - float scale, enum wl_output_transform transform, - int32_t hotspot_x, int32_t hotspot_y, bool update_texture); + bool (*set_cursor)(struct wlr_output *output, struct wlr_buffer *buffer, + int hotspot_x, int hotspot_y); /** * Set the output cursor plane position. * @@ -87,6 +80,20 @@ struct wlr_output_impl { */ bool (*export_dmabuf)(struct wlr_output *output, struct wlr_dmabuf_attributes *attribs); + /** + * Get the list of formats suitable for the cursor, assuming a buffer with + * the specified capabilities. + * + * If unimplemented, the cursor buffer has no format constraint. If NULL is + * returned, no format is suitable. + */ + const struct wlr_drm_format_set *(*get_cursor_formats)( + struct wlr_output *output, uint32_t buffer_caps); + /** + * Get the size suitable for the cursor buffer. Attempts to use a different + * size for the cursor may fail. + */ + void (*get_cursor_size)(struct wlr_output *output, int *width, int *height); }; /** diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 507bfa4755..dd5051c986 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -181,6 +181,8 @@ struct wlr_output { struct wl_list cursors; // wlr_output_cursor::link struct wlr_output_cursor *hardware_cursor; + struct wlr_swapchain *cursor_swapchain; + struct wlr_buffer *cursor_front_buffer; int software_cursor_locks; // number of locks forcing software cursors struct wl_listener display_destroy; diff --git a/types/wlr_output.c b/types/wlr_output.c index 8f83a00561..f625923769 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -16,6 +16,11 @@ #include #include #include +#include "backend/backend.h" +#include "render/allocator.h" +#include "render/drm_format_set.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" #include "util/global.h" #include "util/signal.h" @@ -385,6 +390,9 @@ void wlr_output_destroy(struct wlr_output *output) { wlr_output_cursor_destroy(cursor); } + wlr_swapchain_destroy(output->cursor_swapchain); + wlr_buffer_unlock(output->cursor_front_buffer); + if (output->idle_frame != NULL) { wl_event_source_remove(output->idle_frame); } @@ -829,8 +837,7 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) { assert(output->impl->set_cursor); - output->impl->set_cursor(output, NULL, 1, - WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true); + output->impl->set_cursor(output, NULL, 0, 0); output_cursor_damage_whole(output->hardware_cursor); output->hardware_cursor = NULL; } @@ -1001,8 +1008,62 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { cursor->visible = visible; } -static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { - float scale = cursor->output->scale; +static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *output) { + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_allocator *allocator = backend_get_allocator(output->backend); + assert(renderer != NULL && allocator != NULL); + + const struct wlr_drm_format_set *render_formats = + wlr_renderer_get_render_formats(renderer); + if (render_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get render formats"); + return NULL; + } + + const struct wlr_drm_format_set *display_formats; + if (output->impl->get_cursor_formats) { + display_formats = + output->impl->get_cursor_formats(output, allocator->buffer_caps); + if (display_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get display formats"); + return NULL; + } + } else { + // The backend can display any format + display_formats = render_formats; + } + + uint32_t fmt = DRM_FORMAT_ARGB8888; + + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, fmt); + if (render_format == NULL) { + wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); + return NULL; + } + + const struct wlr_drm_format *display_format = + wlr_drm_format_set_get(display_formats, fmt); + if (display_format == NULL) { + wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); + return NULL; + } + + struct wlr_drm_format *format = + wlr_drm_format_intersect(display_format, render_format); + if (format == NULL) { + wlr_log(WLR_DEBUG, "Failed to intersect display and render " + "modifiers for format 0x%"PRIX32, fmt); + return NULL; + } + + return format; +} + +static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) { + struct wlr_output *output = cursor->output; + + float scale = output->scale; enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; struct wlr_texture *texture = cursor->texture; if (cursor->surface != NULL) { @@ -1010,25 +1071,150 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { scale = cursor->surface->current.scale; transform = cursor->surface->current.transform; } + if (texture == NULL) { + return NULL; + } + + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + if (renderer == NULL) { + wlr_log(WLR_ERROR, "Failed to get backend renderer"); + return NULL; + } + + struct wlr_allocator *allocator = backend_get_allocator(output->backend); + if (allocator == NULL) { + wlr_log(WLR_ERROR, "Failed to get backend allocator"); + return NULL; + } + + int width = texture->width; + int height = texture->height; + if (output->impl->get_cursor_size) { + // Apply hardware limitations on buffer size + output->impl->get_cursor_size(cursor->output, &width, &height); + if ((int)texture->width > width || (int)texture->height > height) { + wlr_log(WLR_DEBUG, "Cursor texture too large (%dx%d), " + "exceeds hardware limitations (%dx%d)", texture->width, + texture->height, width, height); + return NULL; + } + } + + if (output->cursor_swapchain == NULL || + output->cursor_swapchain->width != width || + output->cursor_swapchain->height != height) { + struct wlr_drm_format *format = + output_pick_cursor_format(output); + if (format == NULL) { + wlr_log(WLR_ERROR, "Failed to pick cursor format"); + return NULL; + } + + wlr_swapchain_destroy(output->cursor_swapchain); + output->cursor_swapchain = wlr_swapchain_create(allocator, + width, height, format); + if (output->cursor_swapchain == NULL) { + wlr_log(WLR_ERROR, "Failed to create cursor swapchain"); + return NULL; + } + } + + struct wlr_buffer *buffer = + wlr_swapchain_acquire(output->cursor_swapchain, NULL); + if (buffer == NULL) { + return NULL; + } + + if (!wlr_renderer_bind_buffer(renderer, buffer)) { + wlr_buffer_unlock(buffer); + return NULL; + } + + struct wlr_box cursor_box = { + .width = texture->width * output->scale / scale, + .height = texture->height * output->scale / scale, + }; + + float output_matrix[9]; + wlr_matrix_identity(output_matrix); + if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { + struct wlr_box tr_size = { + .width = buffer->width, + .height = buffer->height, + }; + wlr_box_transform(&tr_size, &tr_size, output->transform, 0, 0); + + wlr_matrix_translate(output_matrix, buffer->width / 2.0, + buffer->height / 2.0); + wlr_matrix_transform(output_matrix, output->transform); + wlr_matrix_translate(output_matrix, - tr_size.width / 2.0, + - tr_size.height / 2.0); + } - if (cursor->output->software_cursor_locks > 0) { + float matrix[9]; + wlr_matrix_project_box(matrix, &cursor_box, transform, 0, output_matrix); + + wlr_renderer_begin(renderer, width, height); + wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); + wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); + wlr_renderer_end(renderer); + + wlr_renderer_bind_buffer(renderer, NULL); + + return buffer; +} + +static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { + struct wlr_output *output = cursor->output; + + if (!output->impl->set_cursor || + output->software_cursor_locks > 0) { + return false; + } + + struct wlr_output_cursor *hwcur = output->hardware_cursor; + if (hwcur != NULL && hwcur != cursor) { return false; } - struct wlr_output_cursor *hwcur = cursor->output->hardware_cursor; - if (cursor->output->impl->set_cursor && (hwcur == NULL || hwcur == cursor)) { - // If the cursor was hidden or was a software cursor, the hardware - // cursor position is outdated - assert(cursor->output->impl->move_cursor); - cursor->output->impl->move_cursor(cursor->output, - (int)cursor->x, (int)cursor->y); - if (cursor->output->impl->set_cursor(cursor->output, texture, - scale, transform, cursor->hotspot_x, cursor->hotspot_y, true)) { - cursor->output->hardware_cursor = cursor; - return true; + struct wlr_texture *texture = cursor->texture; + if (cursor->surface != NULL) { + // TODO: try using the surface buffer directly + texture = wlr_surface_get_texture(cursor->surface); + } + + // If the cursor was hidden or was a software cursor, the hardware + // cursor position is outdated + output->impl->move_cursor(cursor->output, + (int)cursor->x, (int)cursor->y); + + struct wlr_buffer *buffer = NULL; + if (texture != NULL) { + buffer = render_cursor_buffer(cursor); + if (buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to render cursor buffer"); + return false; } } - return false; + + struct wlr_box hotspot = { + .x = cursor->hotspot_x, + .y = cursor->hotspot_y, + }; + wlr_box_transform(&hotspot, &hotspot, + wlr_output_transform_invert(output->transform), + buffer ? buffer->width : 0, buffer ? buffer->height : 0); + + bool ok = output->impl->set_cursor(cursor->output, buffer, + hotspot.x, hotspot.y); + if (ok) { + wlr_buffer_unlock(output->cursor_front_buffer); + output->cursor_front_buffer = buffer; + output->hardware_cursor = cursor; + } else { + wlr_buffer_unlock(buffer); + } + return ok; } bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor, @@ -1130,9 +1316,19 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, if (cursor->output->hardware_cursor != cursor) { output_cursor_damage_whole(cursor); } else { + struct wlr_buffer *buffer = cursor->output->cursor_front_buffer; + + struct wlr_box hotspot = { + .x = cursor->hotspot_x, + .y = cursor->hotspot_y, + }; + wlr_box_transform(&hotspot, &hotspot, + wlr_output_transform_invert(cursor->output->transform), + buffer ? buffer->width : 0, buffer ? buffer->height : 0); + assert(cursor->output->impl->set_cursor); - cursor->output->impl->set_cursor(cursor->output, NULL, - 1, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y, false); + cursor->output->impl->set_cursor(cursor->output, + buffer, hotspot.x, hotspot.y); } return; } @@ -1156,8 +1352,7 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, if (cursor->output->hardware_cursor == cursor) { assert(cursor->output->impl->set_cursor); - cursor->output->impl->set_cursor(cursor->output, NULL, 1, - WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true); + cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); } } } @@ -1219,8 +1414,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { if (cursor->output->hardware_cursor == cursor) { // If this cursor was the hardware cursor, disable it if (cursor->output->impl->set_cursor) { - cursor->output->impl->set_cursor(cursor->output, NULL, 1, - WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true); + cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); } cursor->output->hardware_cursor = NULL; }