backend/drm: introduce wlr_drm_fb

This is a type which manages gbm_surfaces and imported dmabufs in the
same place, and makes the lifetime management between the two shared. It
should lead to easier to understand code, and fewer special cases.

This also contains a fair bit of refactoring to start using this new
type.

Co-authored-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Scott Anderson 2020-02-08 19:02:31 +13:00 committed by Simon Ser
parent fa5d709fc3
commit 8da9d9679e
8 changed files with 397 additions and 285 deletions

View file

@ -172,7 +172,7 @@ static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm,
if (bo) { if (bo) {
uint32_t fb_id = uint32_t fb_id =
get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); get_fb_for_bo(bo, drm->addfb2_modifiers);
set_plane_props(&atom, plane, crtc->id, fb_id, false); set_plane_props(&atom, plane, crtc->id, fb_id, false);
} else { } else {
atomic_add(&atom, plane->id, plane->props.fb_id, 0); atomic_add(&atom, plane->id, plane->props.fb_id, 0);

View file

@ -105,8 +105,13 @@ static void session_signal(struct wl_listener *listener, void *data) {
} }
struct wlr_drm_plane *plane = conn->crtc->cursor; struct wlr_drm_plane *plane = conn->crtc->cursor;
drm->iface->crtc_set_cursor(drm, conn->crtc, struct gbm_bo *bo = NULL;
(plane && plane->cursor_enabled) ? plane->surf.back : NULL); if (plane->cursor_enabled) {
bo = drm_fb_acquire(&plane->current_fb, drm,
&plane->mgpu_surf);
}
drm->iface->crtc_set_cursor(drm, conn->crtc, bo);
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x, drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y); conn->cursor_y);

View file

@ -331,7 +331,37 @@ static struct wlr_drm_connector *get_drm_connector_from_output(
static bool drm_connector_attach_render(struct wlr_output *output, static bool drm_connector_attach_render(struct wlr_output *output,
int *buffer_age) { int *buffer_age) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
return make_drm_surface_current(&conn->crtc->primary->surf, buffer_age); return drm_surface_make_current(&conn->crtc->primary->surf, buffer_age);
}
static bool drm_crtc_page_flip(struct wlr_drm_connector *conn,
struct wlr_drm_mode *mode) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend);
struct wlr_drm_crtc *crtc = conn->crtc;
struct wlr_drm_plane *plane = crtc->primary;
struct gbm_bo *bo;
uint32_t fb_id;
drmModeModeInfo *drm_mode = mode ? &mode->drm_mode : NULL;
if (conn->pageflip_pending) {
wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name);
return false;
}
bo = drm_fb_acquire(&plane->queued_fb, drm, &plane->mgpu_surf);
if (!bo) {
return false;
}
fb_id = get_fb_for_bo(bo, drm->addfb2_modifiers);
if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, drm_mode)) {
return false;
}
conn->pageflip_pending = true;
drm_fb_move(&crtc->primary->queued_fb, &crtc->primary->pending_fb);
wlr_output_update_enabled(&conn->output, true);
return true;
} }
static uint32_t strip_alpha_channel(uint32_t format) { static uint32_t strip_alpha_channel(uint32_t format) {
@ -405,76 +435,31 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) {
} }
struct wlr_drm_plane *plane = crtc->primary; struct wlr_drm_plane *plane = crtc->primary;
pixman_region32_t *damage = NULL;
if (output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
damage = &output->pending.damage;
}
struct gbm_bo *bo;
uint32_t fb_id = 0;
assert(output->pending.committed & WLR_OUTPUT_STATE_BUFFER); assert(output->pending.committed & WLR_OUTPUT_STATE_BUFFER);
switch (output->pending.buffer_type) { switch (output->pending.buffer_type) {
case WLR_OUTPUT_STATE_BUFFER_RENDER: case WLR_OUTPUT_STATE_BUFFER_RENDER:
bo = swap_drm_surface_buffers(&plane->surf, damage); if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
if (bo == NULL) { wlr_log(WLR_ERROR, "drm_fb_lock_surface failed");
wlr_log(WLR_ERROR, "swap_drm_surface_buffers failed");
return false;
}
if (drm->parent) {
bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
if (bo == NULL) {
wlr_log(WLR_ERROR, "copy_drm_surface_mgpu failed");
return false;
}
}
fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers);
if (fb_id == 0) {
wlr_log(WLR_ERROR, "get_fb_for_bo failed");
return false; return false;
} }
break; break;
case WLR_OUTPUT_STATE_BUFFER_SCANOUT:; case WLR_OUTPUT_STATE_BUFFER_SCANOUT:;
struct wlr_dmabuf_attributes attribs; struct wlr_buffer *buffer = output->pending.buffer;
if (!wlr_buffer_get_dmabuf(output->pending.buffer, &attribs)) { if (!test_buffer(conn, output->pending.buffer)) {
return false; return false;
} }
if (!drm_fb_import_wlr(&plane->pending_fb, &drm->renderer, buffer,
bo = import_gbm_bo(&drm->renderer, &attribs); &crtc->primary->formats)) {
if (bo == NULL) {
wlr_log(WLR_ERROR, "import_gbm_bo failed");
return false;
}
if (conn->pending_bo != NULL) {
gbm_bo_destroy(conn->pending_bo);
}
conn->pending_bo = bo;
fb_id = get_fb_for_bo(bo, gbm_bo_get_format(bo), drm->addfb2_modifiers);
if (fb_id == 0) {
wlr_log(WLR_ERROR, "get_fb_for_bo failed");
return false; return false;
} }
break; break;
} }
if (conn->pageflip_pending) { if (!drm_crtc_page_flip(conn, NULL)) {
wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name); drm_fb_clear(&plane->pending_fb);
return false; return false;
} }
if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) {
return false;
}
conn->pageflip_pending = true;
if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) {
wlr_buffer_unlock(conn->pending_buffer);
conn->pending_buffer = wlr_buffer_lock(output->pending.buffer);
}
wlr_output_update_enabled(output, true);
return true; return true;
} }
@ -648,37 +633,54 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output,
struct wlr_dmabuf_attributes *attribs) { struct wlr_dmabuf_attributes *attribs) {
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 = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
struct wlr_drm_crtc *crtc = conn->crtc;
if (!drm->session->active) { if (!drm->session->active) {
return false; return false;
} }
struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
return false; return false;
} }
struct wlr_drm_plane *plane = crtc->primary;
struct wlr_drm_surface *surf = &plane->surf;
return export_drm_bo(surf->back, attribs); struct wlr_drm_plane *plane = crtc->primary;
if (plane->current_fb.type == WLR_DRM_FB_TYPE_NONE) {
return false;
}
return export_drm_bo(plane->current_fb.bo, attribs);
}
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) {
if (plane->pending_fb.type != WLR_DRM_FB_TYPE_NONE) {
return &plane->pending_fb;
}
if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
return &plane->queued_fb;
}
return &plane->current_fb;
} }
static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn, static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn,
struct wlr_drm_mode *mode) { struct wlr_drm_mode *mode) {
struct wlr_drm_backend *drm =
get_drm_backend_from_backend(conn->output.backend);
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC", wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC",
conn->output.name); conn->output.name);
return false; return false;
} }
struct wlr_drm_plane *plane = crtc->primary;
struct gbm_bo *bo = get_drm_surface_front( // drm_crtc_page_flip expects a FB to be available
drm->parent ? &plane->mgpu_surf : &plane->surf); struct wlr_drm_plane *plane = crtc->primary;
uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); if (plane_get_next_fb(plane)->type == WLR_DRM_FB_TYPE_NONE) {
return drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, &mode->drm_mode); drm_surface_render_black_frame(&plane->surf);
if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
return false;
}
}
return drm_crtc_page_flip(conn, mode);
} }
static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { static void drm_connector_start_renderer(struct wlr_drm_connector *conn) {
@ -689,10 +691,7 @@ static void drm_connector_start_renderer(struct wlr_drm_connector *conn) {
wlr_log(WLR_DEBUG, "Starting renderer on output '%s'", conn->output.name); wlr_log(WLR_DEBUG, "Starting renderer on output '%s'", conn->output.name);
struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode; struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode;
if (drm_connector_pageflip_renderer(conn, mode)) { if (!drm_connector_pageflip_renderer(conn, mode)) {
conn->pageflip_pending = true;
wlr_output_update_enabled(&conn->output, true);
} else {
wl_event_source_timer_update(conn->retry_pageflip, wl_event_source_timer_update(conn->retry_pageflip,
1000000.0f / conn->output.current_mode->refresh); 1000000.0f / conn->output.current_mode->refresh);
} }
@ -731,7 +730,7 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
modifiers = false; modifiers = false;
} }
if (!init_drm_plane_surfaces(plane, drm, width, height, format, modifiers) || if (!drm_plane_init_surface(plane, drm, width, height, format, 0, modifiers) ||
!drm_connector_pageflip_renderer(conn, mode)) { !drm_connector_pageflip_renderer(conn, mode)) {
if (!modifiers) { if (!modifiers) {
wlr_log(WLR_ERROR, "Failed to initialize renderer " wlr_log(WLR_ERROR, "Failed to initialize renderer "
@ -742,14 +741,12 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn,
// If page-flipping with modifiers enabled doesn't work, retry without // If page-flipping with modifiers enabled doesn't work, retry without
// modifiers // modifiers
finish_drm_surface(&plane->surf);
finish_drm_surface(&plane->mgpu_surf);
wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, " wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, "
"retrying without modifiers"); "retrying without modifiers");
modifiers = false; modifiers = false;
if (!init_drm_plane_surfaces(plane, drm, width, height, format, if (!drm_plane_init_surface(plane, drm, width, height, format,
modifiers)) { 0, modifiers)) {
return false; return false;
} }
if (!drm_connector_pageflip_renderer(conn, mode)) { if (!drm_connector_pageflip_renderer(conn, mode)) {
@ -908,8 +905,8 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { 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 = get_drm_backend_from_backend(output->backend); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_crtc *crtc = conn->crtc;
if (!crtc) { if (!crtc) {
return false; return false;
} }
@ -933,31 +930,15 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h); ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h);
h = ret ? 64 : h; h = ret ? 64 : h;
if (!drm->parent) { if (!drm_plane_init_surface(plane, drm, w, h,
if (!init_drm_surface(&plane->surf, &drm->renderer, w, h, DRM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR, false)) {
drm->renderer.gbm_format, NULL,
GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
wlr_log(WLR_ERROR, "Cannot allocate cursor resources"); wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
return false; return false;
} }
} else {
if (!init_drm_surface(&plane->surf, &drm->parent->renderer, w, h,
drm->parent->renderer.gbm_format, NULL,
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, NULL,
GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) {
wlr_log(WLR_ERROR, "Cannot allocate cursor resources");
return false;
}
}
} }
wlr_matrix_projection(plane->matrix, plane->surf.width, float hotspot_proj[9];
wlr_matrix_projection(hotspot_proj, plane->surf.width,
plane->surf.height, output->transform); plane->surf.height, output->transform);
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
@ -999,21 +980,23 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return false; return false;
} }
make_drm_surface_current(&plane->surf, NULL); drm_surface_make_current(&plane->surf, NULL);
struct wlr_renderer *rend = plane->surf.renderer->wlr_rend; struct wlr_renderer *rend = plane->surf.renderer->wlr_rend;
struct wlr_box cursor_box = { .width = width, .height = height }; struct wlr_box cursor_box = { .width = width, .height = height };
float matrix[9]; float matrix[9];
wlr_matrix_project_box(matrix, &cursor_box, transform, 0, plane->matrix); wlr_matrix_project_box(matrix, &cursor_box, transform, 0, hotspot_proj);
wlr_renderer_begin(rend, plane->surf.width, plane->surf.height); wlr_renderer_begin(rend, plane->surf.width, plane->surf.height);
wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 }); wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); wlr_render_texture_with_matrix(rend, texture, matrix, 1.0);
wlr_renderer_end(rend); wlr_renderer_end(rend);
swap_drm_surface_buffers(&plane->surf, NULL); if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) {
return false;
}
plane->cursor_enabled = true; plane->cursor_enabled = true;
} }
@ -1022,25 +1005,30 @@ static bool drm_connector_set_cursor(struct wlr_output *output,
return true; // will be committed when session is resumed return true; // will be committed when session is resumed
} }
struct gbm_bo *bo = plane->cursor_enabled ? plane->surf.back : NULL; struct gbm_bo *bo = NULL;
if (bo && drm->parent) {
bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
}
if (bo) { if (plane->cursor_enabled) {
// workaround for nouveau bo = drm_fb_acquire(&plane->pending_fb, drm, &plane->mgpu_surf);
// 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. wlr_log(WLR_DEBUG, "SET_CURSOR %p %p", plane->pending_fb.bo, bo);
// 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 /* Workaround for nouveau buffers created with GBM_BO_USER_LINEAR are
// The render operations can be waited for using: * 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://gitlab.freedesktop.org/xorg/driver/xf86-video-nouveau/issues/480
* The render operations can be waited for using:
*/
glFinish(); glFinish();
} }
bool ok = drm->iface->crtc_set_cursor(drm, crtc, bo);
if (ok) { if (!drm->iface->crtc_set_cursor(drm, crtc, bo)) {
wlr_output_update_needs_frame(output); return false;
} }
return ok;
wlr_output_update_needs_frame(output);
return true;
} }
static bool drm_connector_move_cursor(struct wlr_output *output, static bool drm_connector_move_cursor(struct wlr_output *output,
@ -1133,8 +1121,8 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) {
conn->crtc - drm->crtcs, conn->output.name); conn->crtc - drm->crtcs, conn->output.name);
set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL); set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
finish_drm_surface(&conn->crtc->primary->surf); drm_plane_finish_surface(conn->crtc->primary);
finish_drm_surface(&conn->crtc->cursor->surf); drm_plane_finish_surface(conn->crtc->cursor);
drm->iface->conn_enable(drm, conn, false); drm->iface->conn_enable(drm, conn, false);
@ -1544,15 +1532,15 @@ static int mhz_to_nsec(int mhz) {
static void page_flip_handler(int fd, unsigned seq, static void page_flip_handler(int fd, unsigned seq,
unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) { unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) {
struct wlr_drm_backend *drm = data; struct wlr_drm_backend *drm = data;
struct wlr_drm_connector *conn = NULL; struct wlr_drm_connector *conn = NULL;
struct wlr_drm_connector *search; struct wlr_drm_connector *search;
wl_list_for_each(search, &drm->outputs, link) { wl_list_for_each(search, &drm->outputs, link) {
if (search->crtc && search->crtc->id == crtc_id) { if (search->crtc && search->crtc->id == crtc_id) {
conn = search; conn = search;
break;
} }
} }
if (!conn) { if (!conn) {
wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id); wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id);
return; return;
@ -1564,27 +1552,24 @@ static void page_flip_handler(int fd, unsigned seq,
return; return;
} }
// Release the old buffer as it's not displayed anymore. The pending struct wlr_drm_plane *plane = conn->crtc->primary;
// buffer becomes the current buffer. if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
wlr_buffer_unlock(conn->current_buffer); drm_fb_move(&plane->current_fb, &plane->queued_fb);
conn->current_buffer = conn->pending_buffer; }
conn->pending_buffer = NULL; if (conn->crtc->cursor &&
conn->crtc->cursor->queued_fb.type != WLR_DRM_FB_TYPE_NONE) {
if (conn->current_bo != NULL) { drm_fb_move(&conn->crtc->cursor->current_fb,
gbm_bo_destroy(conn->current_bo); &conn->crtc->cursor->queued_fb);
} }
conn->current_bo = conn->pending_bo;
conn->pending_bo = NULL;
uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC | uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC |
WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION;
if (conn->current_buffer != NULL) { /* Don't report ZERO_COPY in multi-gpu situations, because we had to copy
* data between the GPUs, even if we were using the direct scanout
* interface.
*/
if (!drm->parent && plane->current_fb.type == WLR_DRM_FB_TYPE_WLR_BUFFER) {
present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
} else {
post_drm_surface(&conn->crtc->primary->surf);
if (drm->parent) {
post_drm_surface(&conn->crtc->primary->mgpu_surf);
}
} }
struct timespec present_time = { struct timespec present_time = {
@ -1687,10 +1672,6 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) {
conn->output.needs_frame = false; conn->output.needs_frame = false;
conn->output.frame_pending = false; conn->output.frame_pending = false;
wlr_buffer_unlock(conn->pending_buffer);
wlr_buffer_unlock(conn->current_buffer);
conn->pending_buffer = conn->current_buffer = NULL;
/* Fallthrough */ /* Fallthrough */
case WLR_DRM_CONN_NEEDS_MODESET: case WLR_DRM_CONN_NEEDS_MODESET:
wlr_log(WLR_INFO, "Emitting destruction signal for '%s'", wlr_log(WLR_INFO, "Emitting destruction signal for '%s'",

View file

@ -3,6 +3,7 @@
#include <gbm.h> #include <gbm.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-util.h> #include <wayland-util.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
@ -61,7 +62,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
gbm_device_destroy(renderer->gbm); gbm_device_destroy(renderer->gbm);
} }
bool init_drm_surface(struct wlr_drm_surface *surf, static 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,
uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) { uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) {
if (surf->width == width && surf->height == height) { if (surf->width == width && surf->height == height) {
@ -73,14 +74,6 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
surf->height = height; surf->height = height;
if (surf->gbm) { if (surf->gbm) {
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
surf->front = NULL;
}
if (surf->back) {
gbm_surface_release_buffer(surf->gbm, surf->back);
surf->back = NULL;
}
gbm_surface_destroy(surf->gbm); gbm_surface_destroy(surf->gbm);
surf->gbm = NULL; surf->gbm = NULL;
} }
@ -119,18 +112,11 @@ error_zero:
return false; return false;
} }
void finish_drm_surface(struct wlr_drm_surface *surf) { static void finish_drm_surface(struct wlr_drm_surface *surf) {
if (!surf || !surf->renderer) { if (!surf || !surf->renderer) {
return; return;
} }
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
}
if (surf->back) {
gbm_surface_release_buffer(surf->gbm, surf->back);
}
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
if (surf->gbm) { if (surf->gbm) {
gbm_surface_destroy(surf->gbm); gbm_surface_destroy(surf->gbm);
@ -139,82 +125,11 @@ void finish_drm_surface(struct wlr_drm_surface *surf) {
memset(surf, 0, sizeof(*surf)); memset(surf, 0, sizeof(*surf));
} }
bool make_drm_surface_current(struct wlr_drm_surface *surf, bool drm_surface_make_current(struct wlr_drm_surface *surf,
int *buffer_damage) { int *buffer_damage) {
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage); return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage);
} }
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
pixman_region32_t *damage) {
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
}
wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, damage);
surf->front = surf->back;
surf->back = gbm_surface_lock_front_buffer(surf->gbm);
return surf->back;
}
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf) {
if (surf->front) {
return surf->front;
}
make_drm_surface_current(surf, NULL);
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
wlr_renderer_begin(renderer, surf->width, surf->height);
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
wlr_renderer_end(renderer);
return swap_drm_surface_buffers(surf, NULL);
}
void post_drm_surface(struct wlr_drm_surface *surf) {
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
surf->front = NULL;
}
}
struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer,
struct wlr_dmabuf_attributes *attribs) {
if (attribs->modifier == DRM_FORMAT_MOD_INVALID && attribs->n_planes == 1
&& attribs->offset[0] == 0) {
struct gbm_import_fd_data data = {
.fd = attribs->fd[0],
.width = attribs->width,
.height = attribs->height,
.stride = attribs->stride[0],
.format = attribs->format,
};
return gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD,
&data, GBM_BO_USE_SCANOUT);
} else {
struct gbm_import_fd_modifier_data data = {
.width = attribs->width,
.height = attribs->height,
.format = attribs->format,
.num_fds = attribs->n_planes,
.modifier = attribs->modifier,
};
if ((size_t)attribs->n_planes > sizeof(data.fds) / sizeof(data.fds[0])) {
return NULL;
}
for (size_t i = 0; i < (size_t)attribs->n_planes; ++i) {
data.fds[i] = attribs->fd[i];
data.strides[i] = attribs->stride[i];
data.offsets[i] = attribs->offset[i];
}
return gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD_MODIFIER,
&data, GBM_BO_USE_SCANOUT);
}
}
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) { bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes)); memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
@ -268,46 +183,234 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
return tex; return tex;
} }
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest, void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
struct gbm_bo *src) { if (!plane) {
make_drm_surface_current(dest, NULL); return;
struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src);
assert(tex);
float mat[9];
wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
struct wlr_renderer *renderer = dest->renderer->wlr_rend;
wlr_renderer_begin(renderer, dest->width, dest->height);
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
wlr_renderer_end(renderer);
return swap_drm_surface_buffers(dest, NULL);
} }
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane, drm_fb_clear(&plane->pending_fb);
drm_fb_clear(&plane->queued_fb);
drm_fb_clear(&plane->current_fb);
finish_drm_surface(&plane->surf);
finish_drm_surface(&plane->mgpu_surf);
}
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,
uint32_t format, bool with_modifiers) { uint32_t format, uint32_t flags, bool with_modifiers) {
struct wlr_drm_format_set *format_set = struct wlr_drm_format_set *format_set =
with_modifiers ? &plane->formats : NULL; with_modifiers ? &plane->formats : NULL;
drm_plane_finish_surface(plane);
if (!drm->parent) { if (!drm->parent) {
return init_drm_surface(&plane->surf, &drm->renderer, width, height, return init_drm_surface(&plane->surf, &drm->renderer, width, height,
format, format_set, GBM_BO_USE_SCANOUT); format, format_set, flags | GBM_BO_USE_SCANOUT);
} }
if (!init_drm_surface(&plane->surf, &drm->parent->renderer, if (!init_drm_surface(&plane->surf, &drm->parent->renderer,
width, height, format, NULL, GBM_BO_USE_LINEAR)) { width, height, format, NULL,
flags | GBM_BO_USE_LINEAR)) {
return false; return false;
} }
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer,
width, height, format, format_set, GBM_BO_USE_SCANOUT)) { width, height, format, format_set,
flags | GBM_BO_USE_SCANOUT)) {
finish_drm_surface(&plane->surf); finish_drm_surface(&plane->surf);
return false; return false;
} }
return true; return true;
} }
void drm_fb_clear(struct wlr_drm_fb *fb) {
switch (fb->type) {
case WLR_DRM_FB_TYPE_NONE:
assert(!fb->bo);
break;
case WLR_DRM_FB_TYPE_SURFACE:
gbm_surface_release_buffer(fb->surf->gbm, fb->bo);
break;
case WLR_DRM_FB_TYPE_WLR_BUFFER:
gbm_bo_destroy(fb->bo);
wlr_buffer_unlock(fb->wlr_buf);
fb->wlr_buf = NULL;
break;
}
fb->type = WLR_DRM_FB_TYPE_NONE;
fb->bo = NULL;
if (fb->mgpu_bo) {
assert(fb->mgpu_surf);
gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo);
fb->mgpu_bo = NULL;
fb->mgpu_surf = NULL;
}
}
bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) {
drm_fb_clear(fb);
if (!wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, NULL)) {
wlr_log(WLR_ERROR, "Failed to swap buffers");
return false;
}
fb->bo = gbm_surface_lock_front_buffer(surf->gbm);
if (!fb->bo) {
wlr_log(WLR_ERROR, "Failed to lock front buffer");
return false;
}
fb->type = WLR_DRM_FB_TYPE_SURFACE;
fb->surf = surf;
return true;
}
static uint32_t strip_alpha_channel(uint32_t format) {
switch (format) {
case DRM_FORMAT_ARGB8888:
return DRM_FORMAT_XRGB8888;
default:
return DRM_FORMAT_INVALID;
}
}
bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
struct wlr_buffer *buf, struct wlr_drm_format_set *set) {
drm_fb_clear(fb);
struct wlr_dmabuf_attributes attribs;
if (!wlr_buffer_get_dmabuf(buf, &attribs)) {
return false;
}
if (!wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) {
// The format isn't supported by the plane. Try stripping the alpha
// channel, if any.
uint32_t format = strip_alpha_channel(attribs.format);
if (wlr_drm_format_set_has(set, format, attribs.modifier)) {
attribs.format = format;
} else {
return false;
}
}
if (attribs.modifier != DRM_FORMAT_MOD_INVALID ||
attribs.n_planes > 1 || attribs.offset[0] != 0) {
struct gbm_import_fd_modifier_data data = {
.width = attribs.width,
.height = attribs.height,
.format = attribs.format,
.num_fds = attribs.n_planes,
.modifier = attribs.modifier,
};
if ((size_t)attribs.n_planes > sizeof(data.fds) / sizeof(data.fds[0])) {
return false;
}
for (size_t i = 0; i < (size_t)attribs.n_planes; ++i) {
data.fds[i] = attribs.fd[i];
data.strides[i] = attribs.stride[i];
data.offsets[i] = attribs.offset[i];
}
fb->bo = gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD_MODIFIER,
&data, GBM_BO_USE_SCANOUT);
} else {
struct gbm_import_fd_data data = {
.fd = attribs.fd[0],
.width = attribs.width,
.height = attribs.height,
.stride = attribs.stride[0],
.format = attribs.format,
};
fb->bo = gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD,
&data, GBM_BO_USE_SCANOUT);
}
if (!fb->bo) {
return false;
}
fb->type = WLR_DRM_FB_TYPE_WLR_BUFFER;
fb->wlr_buf = wlr_buffer_lock(buf);
return true;
}
void drm_fb_move(struct wlr_drm_fb *new, struct wlr_drm_fb *old) {
drm_fb_clear(new);
*new = *old;
memset(old, 0, sizeof(*old));
}
bool drm_surface_render_black_frame(struct wlr_drm_surface *surf) {
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
if (!drm_surface_make_current(surf, NULL)) {
return false;
}
wlr_renderer_begin(renderer, surf->width, surf->height);
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
wlr_renderer_end(renderer);
return true;
}
struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm,
struct wlr_drm_surface *mgpu) {
if (!fb->bo) {
wlr_log(WLR_ERROR, "Tried to acquire an FB with a NULL BO");
return NULL;
}
if (!drm->parent) {
return fb->bo;
}
if (fb->mgpu_bo) {
return fb->mgpu_bo;
}
/* Perform copy across GPUs */
struct wlr_renderer *renderer = mgpu->renderer->wlr_rend;
if (!drm_surface_make_current(mgpu, NULL)) {
return NULL;
}
struct wlr_texture *tex = get_tex_for_bo(mgpu->renderer, fb->bo);
if (!tex) {
return NULL;
}
float mat[9];
wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
wlr_renderer_begin(renderer, mgpu->width, mgpu->height);
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
wlr_renderer_end(renderer);
if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) {
wlr_log(WLR_ERROR, "Failed to swap buffers");
return NULL;
}
fb->mgpu_bo = gbm_surface_lock_front_buffer(mgpu->gbm);
if (!fb->mgpu_bo) {
wlr_log(WLR_ERROR, "Failed to lock front buffer");
return NULL;
}
fb->mgpu_surf = mgpu;
return fb->mgpu_bo;
}

View file

@ -179,8 +179,7 @@ static void free_fb(struct gbm_bo *bo, void *data) {
} }
} }
uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format, uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers) {
bool with_modifiers) {
uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo); uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
if (id) { if (id) {
return id; return id;
@ -191,6 +190,7 @@ uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format,
int fd = gbm_device_get_fd(gbm); int fd = gbm_device_get_fd(gbm);
uint32_t width = gbm_bo_get_width(bo); uint32_t width = gbm_bo_get_width(bo);
uint32_t height = gbm_bo_get_height(bo); uint32_t height = gbm_bo_get_height(bo);
uint32_t format = gbm_bo_get_format(bo);
uint32_t handles[4] = {0}; uint32_t handles[4] = {0};
uint32_t strides[4] = {0}; uint32_t strides[4] = {0};
@ -205,12 +205,12 @@ uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format,
} }
if (with_modifiers && gbm_bo_get_modifier(bo) != DRM_FORMAT_MOD_INVALID) { if (with_modifiers && gbm_bo_get_modifier(bo) != DRM_FORMAT_MOD_INVALID) {
if (drmModeAddFB2WithModifiers(fd, width, height, drm_format, handles, if (drmModeAddFB2WithModifiers(fd, width, height, format, handles,
strides, offsets, modifiers, &id, DRM_MODE_FB_MODIFIERS)) { strides, offsets, modifiers, &id, DRM_MODE_FB_MODIFIERS)) {
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer"); wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
} }
} else { } else {
if (drmModeAddFB2(fd, width, height, drm_format, handles, strides, if (drmModeAddFB2(fd, width, height, format, handles, strides,
offsets, &id, 0)) { offsets, &id, 0)) {
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer"); wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
} }

View file

@ -25,11 +25,17 @@ struct wlr_drm_plane {
struct wlr_drm_surface surf; struct wlr_drm_surface surf;
struct wlr_drm_surface mgpu_surf; struct wlr_drm_surface mgpu_surf;
/* Buffer to be submitted to the kernel on the next page-flip */
struct wlr_drm_fb pending_fb;
/* Buffer submitted to the kernel, will be presented on next vblank */
struct wlr_drm_fb queued_fb;
/* Buffer currently displayed on screen */
struct wlr_drm_fb current_fb;
uint32_t drm_format; // ARGB8888 or XRGB8888 uint32_t drm_format; // ARGB8888 or XRGB8888
struct wlr_drm_format_set formats; struct wlr_drm_format_set formats;
// Only used by cursor // Only used by cursor
float matrix[9];
bool cursor_enabled; bool cursor_enabled;
int32_t cursor_hotspot_x, cursor_hotspot_y; int32_t cursor_hotspot_x, cursor_hotspot_y;
@ -124,16 +130,16 @@ struct wlr_drm_connector {
drmModeCrtc *old_crtc; drmModeCrtc *old_crtc;
bool pageflip_pending;
struct wl_event_source *retry_pageflip; struct wl_event_source *retry_pageflip;
struct wl_list link; struct wl_list link;
// Buffer submitted to the kernel but not yet displayed /*
struct wlr_buffer *pending_buffer; * We've asked for a state change in the kernel, and yet to recieve a
struct gbm_bo *pending_bo; * notification for its completion. Currently, the kernel only has a
// Buffer currently being displayed * queue length of 1, and no way to modify your submissions after
struct wlr_buffer *current_buffer; * they're sent.
struct gbm_bo *current_bo; */
bool pageflip_pending;
}; };
struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_drm_backend *get_drm_backend_from_backend(
@ -150,6 +156,8 @@ bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
bool drm_connector_set_mode(struct wlr_output *output, bool drm_connector_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode); struct wlr_output_mode *mode);
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane);
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm, bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo); struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm, bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,

View file

@ -10,6 +10,7 @@
struct wlr_drm_backend; struct wlr_drm_backend;
struct wlr_drm_plane; struct wlr_drm_plane;
struct wlr_buffer;
struct wlr_drm_renderer { struct wlr_drm_renderer {
int fd; int fd;
@ -29,33 +30,48 @@ struct wlr_drm_surface {
struct gbm_surface *gbm; struct gbm_surface *gbm;
EGLSurface egl; EGLSurface egl;
};
struct gbm_bo *front; enum wlr_drm_fb_type {
struct gbm_bo *back; WLR_DRM_FB_TYPE_NONE,
WLR_DRM_FB_TYPE_SURFACE,
WLR_DRM_FB_TYPE_WLR_BUFFER
};
struct wlr_drm_fb {
enum wlr_drm_fb_type type;
struct gbm_bo *bo;
struct wlr_drm_surface *mgpu_surf;
struct gbm_bo *mgpu_bo;
union {
struct wlr_drm_surface *surf;
struct wlr_buffer *wlr_buf;
};
}; };
bool init_drm_renderer(struct wlr_drm_backend *drm, bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render); struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render);
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, bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age);
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
uint32_t format, struct wlr_drm_format_set *set, uint32_t flags);
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format, bool with_modifiers);
void finish_drm_surface(struct wlr_drm_surface *surf);
bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
pixman_region32_t *damage);
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
void post_drm_surface(struct wlr_drm_surface *surf);
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
struct gbm_bo *src);
struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer,
struct wlr_dmabuf_attributes *attribs);
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs); bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
void drm_fb_clear(struct wlr_drm_fb *fb);
bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf);
bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
struct wlr_buffer *buf, struct wlr_drm_format_set *set);
void drm_fb_move(struct wlr_drm_fb *new, struct wlr_drm_fb *old);
bool drm_surface_render_black_frame(struct wlr_drm_surface *surf);
struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm,
struct wlr_drm_surface *mgpu);
bool drm_plane_init_surface(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format, uint32_t flags, bool with_modifiers);
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
#endif #endif

View file

@ -14,8 +14,7 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
// Returns the string representation of a DRM output type // Returns the string representation of a DRM output type
const char *conn_get_name(uint32_t type_id); const char *conn_get_name(uint32_t type_id);
// Returns the DRM framebuffer id for a gbm_bo // Returns the DRM framebuffer id for a gbm_bo
uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format, uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers);
bool with_modifiers);
// Part of match_obj // Part of match_obj
enum { enum {