output: take a wlr_buffer in set_cursor

Instead of passing a wlr_texture to the backend, directly pass a
wlr_buffer. Use get_cursor_size and get_cursor_formats to create
a wlr_buffer that can be used as a cursor.

We don't want to pass a wlr_texture because we want to remove as
many rendering bits from the backend as possible.
This commit is contained in:
Simon Ser 2020-12-04 16:41:16 +01:00
parent 01e0f51fad
commit 2b0a1aeed5
10 changed files with 301 additions and 279 deletions

View file

@ -25,7 +25,9 @@
#include "backend/drm/iface.h" #include "backend/drm/iface.h"
#include "backend/drm/util.h" #include "backend/drm/util.h"
#include "render/pixel_format.h" #include "render/pixel_format.h"
#include "render/drm_format_set.h"
#include "render/swapchain.h" #include "render/swapchain.h"
#include "render/wlr_renderer.h"
#include "types/wlr_buffer.h" #include "types/wlr_buffer.h"
#include "util/signal.h" #include "util/signal.h"
@ -329,7 +331,7 @@ static bool drm_connector_attach_render(struct wlr_output *output,
static void drm_plane_set_committed(struct wlr_drm_plane *plane) { static void drm_plane_set_committed(struct wlr_drm_plane *plane) {
drm_fb_move(&plane->queued_fb, &plane->pending_fb); 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, wlr_swapchain_set_buffer_submitted(plane->surf.swapchain,
plane->queued_fb->wlr_buf); plane->queued_fb->wlr_buf);
} }
@ -834,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, static bool drm_connector_set_cursor(struct wlr_output *output,
struct wlr_texture *texture, float scale, struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) {
enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_backend *drm = conn->backend;
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
@ -850,90 +850,59 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return false; return false;
} }
if (!plane->surf.swapchain) { if (plane->cursor_hotspot_x != hotspot_x ||
int ret; plane->cursor_hotspot_y != hotspot_y) {
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, 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) {
// Update cursor hotspot // Update cursor hotspot
conn->cursor_x -= hotspot.x - plane->cursor_hotspot_x; conn->cursor_x -= hotspot_x - plane->cursor_hotspot_x;
conn->cursor_y -= hotspot.y - plane->cursor_hotspot_y; conn->cursor_y -= hotspot_y - plane->cursor_hotspot_y;
plane->cursor_hotspot_x = hotspot.x; plane->cursor_hotspot_x = hotspot_x;
plane->cursor_hotspot_y = hotspot.y; plane->cursor_hotspot_y = hotspot_y;
wlr_output_update_needs_frame(output); wlr_output_update_needs_frame(output);
} }
if (!update_texture) {
// Don't update cursor image
return true;
}
plane->cursor_enabled = false; plane->cursor_enabled = false;
if (texture != NULL) { if (buffer != NULL) {
int width = texture->width * output->scale / scale; if ((uint64_t)buffer->width != drm->cursor_width ||
int height = texture->height * output->scale / scale; (uint64_t)buffer->height != drm->cursor_height) {
wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch");
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; return false;
} }
if (!drm_surface_make_current(&plane->surf, NULL)) { 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; return false;
} }
struct wlr_renderer *rend = plane->surf.renderer->wlr_rend; bool ok = init_drm_surface(&plane->mgpu_surf, &drm->renderer,
buffer->width, buffer->height, format);
struct wlr_box cursor_box = { .width = width, .height = height }; free(format);
if (!ok) {
float output_matrix[9]; return false;
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);
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);
} }
float matrix[9]; local_buf = drm_surface_blit(&plane->mgpu_surf, buffer);
wlr_matrix_project_box(matrix, &cursor_box, transform, 0, if (local_buf == NULL) {
output_matrix); return false;
}
} else {
local_buf = wlr_buffer_lock(buffer);
}
wlr_renderer_begin(rend, plane->surf.width, plane->surf.height); bool ok = drm_fb_import(&plane->pending_fb, drm, local_buf,
wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 }); &plane->formats);
wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); wlr_buffer_unlock(local_buf);
wlr_renderer_end(rend); if (!ok) {
if (!drm_plane_lock_surface(plane, drm)) {
return false; return false;
} }
plane->cursor_enabled = true; plane->cursor_enabled = true;
plane->cursor_width = buffer->width;
plane->cursor_height = buffer->height;
} }
wlr_output_update_needs_frame(output); wlr_output_update_needs_frame(output);
@ -977,8 +946,8 @@ bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) {
return plane->cursor_enabled && return plane->cursor_enabled &&
conn->cursor_x < conn->output.width && conn->cursor_x < conn->output.width &&
conn->cursor_y < conn->output.height && conn->cursor_y < conn->output.height &&
conn->cursor_x + (int)plane->surf.width >= 0 && conn->cursor_x + plane->cursor_width >= 0 &&
conn->cursor_y + (int)plane->surf.height >= 0; conn->cursor_y + plane->cursor_height >= 0;
} }
static void dealloc_crtc(struct wlr_drm_connector *conn); static void dealloc_crtc(struct wlr_drm_connector *conn);

View file

@ -62,7 +62,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
gbm_device_destroy(renderer->gbm); 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, struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
const struct wlr_drm_format *drm_format) { const struct wlr_drm_format *drm_format) {
if (surf->width == width && surf->height == height) { 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; 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_buffer *buffer) {
struct wlr_renderer *renderer = surf->renderer->wlr_rend; struct wlr_renderer *renderer = surf->renderer->wlr_rend;
@ -191,7 +191,7 @@ static struct wlr_drm_format *create_linear_format(uint32_t format) {
return fmt; return fmt;
} }
static struct wlr_drm_format *drm_plane_pick_render_format( struct wlr_drm_format *drm_plane_pick_render_format(
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) { struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) {
const struct wlr_drm_format_set *render_formats = const struct wlr_drm_format_set *render_formats =
wlr_renderer_get_render_formats(renderer->wlr_rend); wlr_renderer_get_render_formats(renderer->wlr_rend);

View file

@ -406,28 +406,12 @@ static void output_rollback_render(struct wlr_output *wlr_output) {
} }
static bool output_set_cursor(struct wlr_output *wlr_output, static bool output_set_cursor(struct wlr_output *wlr_output,
struct wlr_texture *texture, float scale, struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) {
enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
struct wlr_wl_backend *backend = output->backend; 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 }; output->cursor.hotspot_x = hotspot_x;
wlr_box_transform(&hotspot, &hotspot, output->cursor.hotspot_y = hotspot_y;
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;
}
if (output->cursor.surface == NULL) { if (output->cursor.surface == NULL) {
output->cursor.surface = output->cursor.surface =
@ -435,58 +419,7 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
} }
struct wl_surface *surface = output->cursor.surface; struct wl_surface *surface = output->cursor.surface;
if (texture != NULL) { if (wlr_buffer != 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);
struct wlr_wl_buffer *buffer = struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer); get_or_create_wl_buffer(output->backend, wlr_buffer);
if (buffer == NULL) { if (buffer == NULL) {
@ -496,11 +429,6 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
wl_surface_attach(surface, buffer->wl_buffer, 0, 0); wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(surface); wl_surface_commit(surface);
wlr_buffer_unlock(wlr_buffer);
output->cursor.width = width;
output->cursor.height = height;
} else { } else {
wl_surface_attach(surface, NULL, 0, 0); wl_surface_attach(surface, NULL, 0, 0);
wl_surface_commit(surface); wl_surface_commit(surface);
@ -530,7 +458,6 @@ static void output_destroy(struct wlr_output *wlr_output) {
wl_list_remove(&output->link); wl_list_remove(&output->link);
wlr_swapchain_destroy(output->cursor.swapchain);
if (output->cursor.surface) { if (output->cursor.surface) {
wl_surface_destroy(output->cursor.surface); wl_surface_destroy(output->cursor.surface);
} }

View file

@ -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, static bool output_cursor_to_picture(struct wlr_x11_output *output,
struct wlr_texture *texture, enum wl_output_transform transform, struct wlr_buffer *buffer) {
int width, int height) {
struct wlr_x11_backend *x11 = output->x11; 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); struct wlr_renderer *renderer = wlr_backend_get_renderer(&x11->backend);
int depth = 32;
int stride = width * 4;
if (output->cursor.pic != XCB_NONE) { if (output->cursor.pic != XCB_NONE) {
xcb_render_free_picture(x11->xcb, output->cursor.pic); xcb_render_free_picture(x11->xcb, output->cursor.pic);
} }
output->cursor.pic = XCB_NONE; output->cursor.pic = XCB_NONE;
if (texture == NULL) { if (buffer == NULL) {
return true; return true;
} }
if (output->cursor.swapchain == NULL || int depth = 32;
output->cursor.swapchain->width != width || int stride = buffer->width * 4;
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 = if (!wlr_renderer_bind_buffer(renderer, buffer)) {
wlr_swapchain_acquire(output->cursor.swapchain, NULL);
if (wlr_buffer == NULL) {
return false; return false;
} }
if (!wlr_renderer_bind_buffer(renderer, wlr_buffer)) { uint8_t *data = malloc(buffer->height * stride);
return false;
}
uint8_t *data = malloc(width * height * 4);
if (data == NULL) { if (data == NULL) {
return false; 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( bool result = wlr_renderer_read_pixels(
renderer, DRM_FORMAT_ARGB8888, NULL, renderer, DRM_FORMAT_ARGB8888, NULL,
width * 4, width, height, 0, 0, 0, 0, stride, buffer->width, buffer->height, 0, 0, 0, 0,
data); data);
wlr_renderer_bind_buffer(renderer, NULL); wlr_renderer_bind_buffer(renderer, NULL);
wlr_buffer_unlock(wlr_buffer);
if (!result) { if (!result) {
free(data); free(data);
return false; 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_pixmap_t pix = xcb_generate_id(x11->xcb);
xcb_create_pixmap(x11->xcb, depth, pix, output->win, xcb_create_pixmap(x11->xcb, depth, pix, output->win,
width, height); buffer->width, buffer->height);
output->cursor.pic = xcb_generate_id(x11->xcb); output->cursor.pic = xcb_generate_id(x11->xcb);
xcb_render_create_picture(x11->xcb, output->cursor.pic, 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_create_gc(x11->xcb, gc, pix, 0, NULL);
xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP, xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP,
pix, gc, width, height, 0, 0, 0, depth, pix, gc, buffer->width, buffer->height, 0, 0, 0, depth,
stride * height * sizeof(uint8_t), stride * buffer->height * sizeof(uint8_t), data);
data);
free(data); free(data);
xcb_free_gc(x11->xcb, gc); xcb_free_gc(x11->xcb, gc);
xcb_free_pixmap(x11->xcb, pix); 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, static bool output_set_cursor(struct wlr_output *wlr_output,
struct wlr_texture *texture, float scale, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) {
enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11; struct wlr_x11_backend *x11 = output->x11;
int width = 0, height = 0;
if (x11->argb32 == XCB_NONE) { if (x11->argb32 == XCB_NONE) {
return false; return false;
} }
if (texture != NULL) { if (buffer != NULL) {
width = texture->width * wlr_output->scale / scale;
height = texture->height * wlr_output->scale / scale;
if (hotspot_x < 0) { if (hotspot_x < 0) {
hotspot_x = 0; hotspot_x = 0;
} }
if ((uint32_t)hotspot_x > texture->width) { if (hotspot_x > buffer->width) {
hotspot_x = texture->width; hotspot_x = buffer->width;
} }
if (hotspot_y < 0) { if (hotspot_y < 0) {
hotspot_y = 0; hotspot_y = 0;
} }
if ((uint32_t)hotspot_y > texture->height) { if (hotspot_y > buffer->height) {
hotspot_y = texture->height; hotspot_y = buffer->height;
} }
} }
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; bool success = output_cursor_to_picture(output, buffer);
wlr_box_transform(&hotspot, &hotspot,
wlr_output_transform_invert(wlr_output->transform),
width, height);
if (!update_texture) { update_x11_output_cursor(output, hotspot_x, hotspot_y);
// This means we previously had a failure of some sort.
if (texture != NULL && output->cursor.pic == XCB_NONE) {
return false;
}
// 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);
update_x11_output_cursor(output, hotspot.x, hotspot.y);
return success; return success;
} }

View file

@ -34,9 +34,10 @@ struct wlr_drm_plane {
struct wlr_drm_format_set formats; struct wlr_drm_format_set formats;
// Only used by cursor // Only used by cursor plane
bool cursor_enabled; bool cursor_enabled;
int32_t cursor_hotspot_x, cursor_hotspot_y; int cursor_width, cursor_height;
int cursor_hotspot_x, cursor_hotspot_y;
union wlr_drm_plane_props props; union wlr_drm_plane_props props;
}; };

View file

@ -43,6 +43,9 @@ bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer); struct wlr_drm_renderer *renderer);
void finish_drm_renderer(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); bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age);
void drm_surface_unset_current(struct wlr_drm_surface *surf); void drm_surface_unset_current(struct wlr_drm_surface *surf);
@ -53,8 +56,12 @@ void drm_fb_destroy(struct wlr_drm_fb *fb);
void drm_fb_clear(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); 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); 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, bool drm_plane_init_surface(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height, struct wlr_drm_backend *drm, int32_t width, uint32_t height,
bool with_modifiers); bool with_modifiers);

View file

@ -83,9 +83,7 @@ struct wlr_wl_output {
struct { struct {
struct wlr_wl_pointer *pointer; struct wlr_wl_pointer *pointer;
struct wl_surface *surface; struct wl_surface *surface;
struct wlr_swapchain *swapchain;
int32_t hotspot_x, hotspot_y; int32_t hotspot_x, hotspot_y;
int32_t width, height;
} cursor; } cursor;
}; };

View file

@ -24,22 +24,15 @@ struct wlr_output_impl {
/** /**
* Set the output cursor plane image. * Set the output cursor plane image.
* *
* The parameters describe the image texture, its scale and its transform. * If buffer is NULL, the cursor should be hidden.
* 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.
* *
* The hotspot indicates the offset that needs to be applied to the * 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 * 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). * words, the image should be displayed at (x - hotspot_x, y - hotspot_y).
* The hotspot is given in the texture's coordinate space. * 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, bool (*set_cursor)(struct wlr_output *output, struct wlr_buffer *buffer,
float scale, enum wl_output_transform transform, int hotspot_x, int hotspot_y);
int32_t hotspot_x, int32_t hotspot_y, bool update_texture);
/** /**
* Set the output cursor plane position. * Set the output cursor plane position.
* *

View file

@ -181,6 +181,8 @@ struct wlr_output {
struct wl_list cursors; // wlr_output_cursor::link struct wl_list cursors; // wlr_output_cursor::link
struct wlr_output_cursor *hardware_cursor; 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 int software_cursor_locks; // number of locks forcing software cursors
struct wl_listener display_destroy; struct wl_listener display_destroy;

View file

@ -16,6 +16,11 @@
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_surface.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/util/region.h> #include <wlr/util/region.h>
#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/global.h"
#include "util/signal.h" #include "util/signal.h"
@ -385,6 +390,9 @@ void wlr_output_destroy(struct wlr_output *output) {
wlr_output_cursor_destroy(cursor); wlr_output_cursor_destroy(cursor);
} }
wlr_swapchain_destroy(output->cursor_swapchain);
wlr_buffer_unlock(output->cursor_front_buffer);
if (output->idle_frame != NULL) { if (output->idle_frame != NULL) {
wl_event_source_remove(output->idle_frame); 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) { if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) {
assert(output->impl->set_cursor); assert(output->impl->set_cursor);
output->impl->set_cursor(output, NULL, 1, output->impl->set_cursor(output, NULL, 0, 0);
WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true);
output_cursor_damage_whole(output->hardware_cursor); output_cursor_damage_whole(output->hardware_cursor);
output->hardware_cursor = NULL; output->hardware_cursor = NULL;
} }
@ -1001,8 +1008,62 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) {
cursor->visible = visible; cursor->visible = visible;
} }
static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *output) {
float scale = cursor->output->scale; 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; enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
struct wlr_texture *texture = cursor->texture; struct wlr_texture *texture = cursor->texture;
if (cursor->surface != NULL) { if (cursor->surface != NULL) {
@ -1010,25 +1071,150 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
scale = cursor->surface->current.scale; scale = cursor->surface->current.scale;
transform = cursor->surface->current.transform; transform = cursor->surface->current.transform;
} }
if (texture == NULL) {
return NULL;
}
if (cursor->output->software_cursor_locks > 0) { 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);
}
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; return false;
} }
struct wlr_output_cursor *hwcur = cursor->output->hardware_cursor; struct wlr_output_cursor *hwcur = output->hardware_cursor;
if (cursor->output->impl->set_cursor && (hwcur == NULL || hwcur == cursor)) { if (hwcur != NULL && hwcur != cursor) {
return false;
}
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 // If the cursor was hidden or was a software cursor, the hardware
// cursor position is outdated // cursor position is outdated
assert(cursor->output->impl->move_cursor); output->impl->move_cursor(cursor->output,
cursor->output->impl->move_cursor(cursor->output,
(int)cursor->x, (int)cursor->y); (int)cursor->x, (int)cursor->y);
if (cursor->output->impl->set_cursor(cursor->output, texture,
scale, transform, cursor->hotspot_x, cursor->hotspot_y, true)) { struct wlr_buffer *buffer = NULL;
cursor->output->hardware_cursor = cursor; if (texture != NULL) {
return true; 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, 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) { if (cursor->output->hardware_cursor != cursor) {
output_cursor_damage_whole(cursor); output_cursor_damage_whole(cursor);
} else { } 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); assert(cursor->output->impl->set_cursor);
cursor->output->impl->set_cursor(cursor->output, NULL, cursor->output->impl->set_cursor(cursor->output,
1, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y, false); buffer, hotspot.x, hotspot.y);
} }
return; return;
} }
@ -1156,8 +1352,7 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor,
if (cursor->output->hardware_cursor == cursor) { if (cursor->output->hardware_cursor == cursor) {
assert(cursor->output->impl->set_cursor); assert(cursor->output->impl->set_cursor);
cursor->output->impl->set_cursor(cursor->output, NULL, 1, cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0);
WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true);
} }
} }
} }
@ -1219,8 +1414,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) {
if (cursor->output->hardware_cursor == cursor) { if (cursor->output->hardware_cursor == cursor) {
// If this cursor was the hardware cursor, disable it // If this cursor was the hardware cursor, disable it
if (cursor->output->impl->set_cursor) { if (cursor->output->impl->set_cursor) {
cursor->output->impl->set_cursor(cursor->output, NULL, 1, cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0);
WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true);
} }
cursor->output->hardware_cursor = NULL; cursor->output->hardware_cursor = NULL;
} }