backend/drm: Update to new cursor API

This commit is contained in:
Scott Anderson 2019-09-15 21:05:14 +12:00
parent ecb4faeed6
commit 2fcb2b95fc
5 changed files with 86 additions and 165 deletions

View file

@ -105,7 +105,7 @@ static void session_signal(struct wl_listener *listener, void *data) {
struct wlr_drm_plane *plane = conn->crtc->cursor;
drm->iface->crtc_set_cursor(drm, conn->crtc,
(plane && plane->cursor_enabled) ? plane->surf.back : NULL);
(plane && conn->cursor_enabled) ? plane->surf.back : NULL);
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y);

View file

@ -76,6 +76,12 @@ bool check_drm_features(struct wlr_drm_backend *drm) {
ret = drmGetCap(drm->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
drm->addfb2_modifiers = ret == 0 && cap == 1;
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &cap);
drm->cursor_width = ret == 0 ? cap : 64;
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &cap);
drm->cursor_height = ret == 0 ? cap : 64;
return true;
}
@ -395,6 +401,23 @@ static bool drm_connector_commit(struct wlr_output *output) {
return false;
}
/* TODO: Merge all of these functions into one */
if (conn->cursor_enabled && crtc->cursor) {
struct gbm_bo *bo = crtc->cursor->surf.front;
if (!drm->iface->crtc_set_cursor(drm, crtc, bo)) {
return false;
}
if (!drm->iface->crtc_move_cursor(drm, crtc,
conn->cursor_x, conn->cursor_y)) {
return false;
}
} else if (!drm->iface->crtc_set_cursor(drm, crtc, NULL)) {
return false;
}
if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) {
return false;
}
@ -595,7 +618,7 @@ bool drm_connector_set_mode(struct wlr_output *output,
conn->output.name, mode->width, mode->height, mode->refresh);
if (!init_drm_plane_surfaces(conn->crtc->primary, drm,
mode->width, mode->height, drm->renderer.gbm_format)) {
mode->width, mode->height, drm->renderer.gbm_format, 0)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer for plane");
return false;
}
@ -649,180 +672,78 @@ bool wlr_drm_connector_add_mode(struct wlr_output *output,
return true;
}
static bool drm_connector_set_cursor(struct wlr_output *output,
struct wlr_texture *texture, int32_t scale,
enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
static bool drm_connector_cursor_try_set_size(struct wlr_output *output,
int *width, int *height) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) {
/* Hardware cursors on secondary GPUs are not supported yet */
if (drm->parent) {
return false;
}
struct wlr_drm_plane *plane = crtc->cursor;
if (!plane) {
// We don't have a real cursor plane, so we make a fake one
plane = calloc(1, sizeof(*plane));
if (!plane) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return false;
}
crtc->cursor = plane;
struct wlr_drm_plane *cursor = conn->crtc->cursor;
/* We don't have a hardware cursor for this connector */
if (!cursor) {
return false;
}
if (!plane->surf.gbm) {
int ret;
uint64_t w, h;
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w);
w = ret ? 64 : w;
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h);
h = ret ? 64 : h;
/*
* Completely ignore the requested size. The only reliable value we can
* use is the one the kernel gives us.
*/
if (!drm->parent) {
if (!init_drm_surface(&plane->surf, &drm->renderer, w, h,
drm->renderer.gbm_format, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
return false;
}
} else {
if (!init_drm_surface(&plane->surf, &drm->parent->renderer, w, h,
drm->parent->renderer.gbm_format, GBM_BO_USE_LINEAR)) {
wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
return false;
}
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, w, h,
drm->renderer.gbm_format, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
return false;
}
}
if (cursor->surf.gbm) {
goto success;
}
wlr_matrix_projection(plane->matrix, plane->surf.width,
plane->surf.height, output->transform);
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
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;
if (!drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y)) {
return false;
}
wlr_output_update_needs_frame(output);
if (!init_drm_plane_surfaces(cursor, drm,
drm->cursor_width, drm->cursor_height,
DRM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR)) {
return false;
}
if (!update_texture) {
// Don't update cursor image
return true;
}
plane->cursor_enabled = false;
if (texture != NULL) {
int width, height;
wlr_texture_get_size(texture, &width, &height);
width = width * output->scale / scale;
height = height * output->scale / scale;
if (width > (int)plane->surf.width || height > (int)plane->surf.height) {
wlr_log(WLR_ERROR, "Cursor too large (max %dx%d)",
(int)plane->surf.width, (int)plane->surf.height);
return false;
}
make_drm_surface_current(&plane->surf, NULL);
struct wlr_renderer *rend = plane->surf.renderer->wlr_rend;
struct wlr_box cursor_box = { .width = width, .height = height };
float matrix[9];
wlr_matrix_project_box(matrix, &cursor_box, transform, 0, plane->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);
swap_drm_surface_buffers(&plane->surf, NULL);
plane->cursor_enabled = true;
}
if (!drm->session->active) {
return true; // will be committed when session is resumed
}
struct gbm_bo *bo = plane->cursor_enabled ? plane->surf.back : NULL;
if (bo && drm->parent) {
bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
}
if (bo) {
// workaround for nouveau
// Buffers created with GBM_BO_USER_LINEAR are placed in NOUVEAU_GEM_DOMAIN_GART.
// When the bo is attached to the cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM.
// However, this does not wait for the render operations to complete, leaving an empty surface.
// see https://bugs.freedesktop.org/show_bug.cgi?id=109631
// The render operations can be waited for using:
glFinish();
}
bool ok = drm->iface->crtc_set_cursor(drm, crtc, bo);
if (ok) {
wlr_output_update_needs_frame(output);
}
return ok;
success:
*width = drm->cursor_width;
*height = drm->cursor_height;
return true;
}
static bool drm_connector_move_cursor(struct wlr_output *output,
int x, int y) {
static bool drm_connector_cursor_attach_render(struct wlr_output *output,
int *buffer_age) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
if (!conn->crtc) {
return false;
}
struct wlr_drm_plane *plane = conn->crtc->cursor;
assert(conn->crtc);
struct wlr_drm_plane *cursor = conn->crtc->cursor;
assert(cursor && cursor->surf.gbm);
return make_drm_surface_current(&cursor->surf, buffer_age);
}
struct wlr_box box = { .x = x, .y = y };
static bool drm_connector_cursor_commit(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_crtc *crtc = conn->crtc;
struct wlr_output_cursor *cursor = &output->cursor_pending;
assert(crtc && crtc->cursor && crtc->cursor->surf.gbm);
int width, height;
wlr_output_transformed_resolution(output, &width, &height);
enum wl_output_transform transform =
wlr_output_transform_invert(output->transform);
wlr_box_transform(&box, &box, transform, width, height);
if (plane != NULL) {
box.x -= plane->cursor_hotspot_x;
box.y -= plane->cursor_hotspot_y;
if (cursor->committed & WLR_OUTPUT_CURSOR_BUFFER) {
if (!swap_drm_surface_buffers(&crtc->cursor->surf, NULL)) {
return false;
}
}
conn->cursor_x = box.x;
conn->cursor_y = box.y;
if (!drm->session->active) {
return true; // will be committed when session is resumed
if (cursor->committed & WLR_OUTPUT_CURSOR_ENABLE) {
conn->cursor_enabled = cursor->enabled;
}
bool ok = drm->iface->crtc_move_cursor(drm, conn->crtc, box.x, box.y);
if (ok) {
wlr_output_update_needs_frame(output);
if (cursor->committed & WLR_OUTPUT_CURSOR_POS) {
conn->cursor_x = cursor->x - cursor->hotspot_x;
conn->cursor_y = cursor->y - cursor->hotspot_y;
}
return ok;
/*
* The cursor is synchronized with the output; it will be changed
* in drm_connector_commit.
*/
return true;
}
static bool drm_connector_schedule_frame(struct wlr_output *output) {
@ -929,8 +850,9 @@ static void drm_connector_destroy(struct wlr_output *output) {
static const struct wlr_output_impl output_impl = {
.enable = enable_drm_connector,
.set_mode = drm_connector_set_mode,
.set_cursor = drm_connector_set_cursor,
.move_cursor = drm_connector_move_cursor,
.cursor_try_set_size = drm_connector_cursor_try_set_size,
.cursor_attach_render = drm_connector_cursor_attach_render,
.cursor_commit = drm_connector_cursor_commit,
.destroy = drm_connector_destroy,
.attach_render = drm_connector_attach_render,
.commit = drm_connector_commit,
@ -1098,7 +1020,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) {
struct wlr_output_mode *mode = conn->output.current_mode;
if (!init_drm_plane_surfaces(conn->crtc->primary, drm,
mode->width, mode->height, drm->renderer.gbm_format)) {
mode->width, mode->height, drm->renderer.gbm_format, 0)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer for plane");
drm_connector_cleanup(conn);
break;

View file

@ -261,10 +261,10 @@ struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format) {
uint32_t format, uint32_t flags) {
if (!drm->parent) {
return init_drm_surface(&plane->surf, &drm->renderer, width, height,
format, GBM_BO_USE_SCANOUT);
format, GBM_BO_USE_SCANOUT | flags);
}
if (!init_drm_surface(&plane->surf, &drm->parent->renderer,

View file

@ -28,11 +28,6 @@ struct wlr_drm_plane {
uint32_t drm_format; // ARGB8888 or XRGB8888
struct wlr_drm_format_set formats;
// Only used by cursor
float matrix[9];
bool cursor_enabled;
int32_t cursor_hotspot_x, cursor_hotspot_y;
union wlr_drm_plane_props props;
};
@ -72,6 +67,8 @@ struct wlr_drm_backend {
const struct wlr_drm_interface *iface;
clockid_t clock;
bool addfb2_modifiers;
int cursor_width;
int cursor_height;
int fd;
@ -119,7 +116,9 @@ struct wlr_drm_connector {
union wlr_drm_connector_props props;
uint32_t width, height;
int32_t cursor_x, cursor_y;
bool cursor_enabled;
int cursor_x, cursor_y;
drmModeCrtc *old_crtc;

View file

@ -44,7 +44,7 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format);
uint32_t format, uint32_t flags);
void finish_drm_surface(struct wlr_drm_surface *surf);
bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);