From 8da9d9679e4cad640b0f0a518ca1529c8cdecbe0 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Sat, 8 Feb 2020 19:02:31 +1300 Subject: 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 --- backend/drm/atomic.c | 2 +- backend/drm/backend.c | 9 +- backend/drm/drm.c | 255 ++++++++++++++++++--------------------- backend/drm/renderer.c | 321 ++++++++++++++++++++++++++++++++----------------- backend/drm/util.c | 8 +- 5 files changed, 342 insertions(+), 253 deletions(-) (limited to 'backend') diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 71b18f98..05dd9d1f 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -172,7 +172,7 @@ static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm, if (bo) { 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); } else { atomic_add(&atom, plane->id, plane->props.fb_id, 0); diff --git a/backend/drm/backend.c b/backend/drm/backend.c index a58af02c..7aeaa01e 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -105,8 +105,13 @@ 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); + struct gbm_bo *bo = 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, conn->cursor_y); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 9be67244..ed74c7d1 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -331,7 +331,37 @@ static struct wlr_drm_connector *get_drm_connector_from_output( static bool drm_connector_attach_render(struct wlr_output *output, int *buffer_age) { 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) { @@ -405,76 +435,31 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) { } 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); switch (output->pending.buffer_type) { case WLR_OUTPUT_STATE_BUFFER_RENDER: - bo = swap_drm_surface_buffers(&plane->surf, damage); - if (bo == NULL) { - 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"); + if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { + wlr_log(WLR_ERROR, "drm_fb_lock_surface failed"); return false; } break; case WLR_OUTPUT_STATE_BUFFER_SCANOUT:; - struct wlr_dmabuf_attributes attribs; - if (!wlr_buffer_get_dmabuf(output->pending.buffer, &attribs)) { - return false; - } - - bo = import_gbm_bo(&drm->renderer, &attribs); - if (bo == NULL) { - wlr_log(WLR_ERROR, "import_gbm_bo failed"); + struct wlr_buffer *buffer = output->pending.buffer; + if (!test_buffer(conn, output->pending.buffer)) { 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"); + if (!drm_fb_import_wlr(&plane->pending_fb, &drm->renderer, buffer, + &crtc->primary->formats)) { return false; } break; } - if (conn->pageflip_pending) { - wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name); - return false; - } - - if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) { + if (!drm_crtc_page_flip(conn, NULL)) { + drm_fb_clear(&plane->pending_fb); 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; } @@ -648,37 +633,54 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output, struct wlr_dmabuf_attributes *attribs) { 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 (!drm->session->active) { return false; } - struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } + struct wlr_drm_plane *plane = crtc->primary; - struct wlr_drm_surface *surf = &plane->surf; - return export_drm_bo(surf->back, attribs); + 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, 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; if (!crtc) { wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC", conn->output.name); return false; } + + // drm_crtc_page_flip expects a FB to be available struct wlr_drm_plane *plane = crtc->primary; + if (plane_get_next_fb(plane)->type == WLR_DRM_FB_TYPE_NONE) { + drm_surface_render_black_frame(&plane->surf); + if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { + return false; + } + } - struct gbm_bo *bo = get_drm_surface_front( - drm->parent ? &plane->mgpu_surf : &plane->surf); - uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); - return drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, &mode->drm_mode); + return drm_crtc_page_flip(conn, mode); } 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); struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode; - if (drm_connector_pageflip_renderer(conn, mode)) { - conn->pageflip_pending = true; - wlr_output_update_enabled(&conn->output, true); - } else { + if (!drm_connector_pageflip_renderer(conn, mode)) { wl_event_source_timer_update(conn->retry_pageflip, 1000000.0f / conn->output.current_mode->refresh); } @@ -731,7 +730,7 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, 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)) { if (!modifiers) { 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 // modifiers - finish_drm_surface(&plane->surf); - finish_drm_surface(&plane->mgpu_surf); wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, " "retrying without modifiers"); 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)) { return false; } 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) { 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) { 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); h = ret ? 64 : h; - if (!drm->parent) { - if (!init_drm_surface(&plane->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; - } - } 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; - } + if (!drm_plane_init_surface(plane, drm, w, h, + DRM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR, false)) { + 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); 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; } - 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_box cursor_box = { .width = width, .height = height }; 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_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); + if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { + return false; + } 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 } - struct gbm_bo *bo = plane->cursor_enabled ? plane->surf.back : NULL; - if (bo && drm->parent) { - bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo); - } + struct gbm_bo *bo = NULL; + + if (plane->cursor_enabled) { + bo = drm_fb_acquire(&plane->pending_fb, drm, &plane->mgpu_surf); + + wlr_log(WLR_DEBUG, "SET_CURSOR %p %p", plane->pending_fb.bo, 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: + /* 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://gitlab.freedesktop.org/xorg/driver/xf86-video-nouveau/issues/480 + * 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); + + if (!drm->iface->crtc_set_cursor(drm, crtc, bo)) { + return false; } - return ok; + + wlr_output_update_needs_frame(output); + return true; } 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); set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL); - finish_drm_surface(&conn->crtc->primary->surf); - finish_drm_surface(&conn->crtc->cursor->surf); + drm_plane_finish_surface(conn->crtc->primary); + drm_plane_finish_surface(conn->crtc->cursor); 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, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) { struct wlr_drm_backend *drm = data; + struct wlr_drm_connector *conn = NULL; struct wlr_drm_connector *search; - wl_list_for_each(search, &drm->outputs, link) { if (search->crtc && search->crtc->id == crtc_id) { conn = search; + break; } } - if (!conn) { wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id); return; @@ -1564,27 +1552,24 @@ static void page_flip_handler(int fd, unsigned seq, return; } - // Release the old buffer as it's not displayed anymore. The pending - // buffer becomes the current buffer. - wlr_buffer_unlock(conn->current_buffer); - conn->current_buffer = conn->pending_buffer; - conn->pending_buffer = NULL; - - if (conn->current_bo != NULL) { - gbm_bo_destroy(conn->current_bo); + struct wlr_drm_plane *plane = conn->crtc->primary; + if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + drm_fb_move(&plane->current_fb, &plane->queued_fb); + } + if (conn->crtc->cursor && + conn->crtc->cursor->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + drm_fb_move(&conn->crtc->cursor->current_fb, + &conn->crtc->cursor->queued_fb); } - conn->current_bo = conn->pending_bo; - conn->pending_bo = NULL; uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC | 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; - } else { - post_drm_surface(&conn->crtc->primary->surf); - if (drm->parent) { - post_drm_surface(&conn->crtc->primary->mgpu_surf); - } } 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.frame_pending = false; - wlr_buffer_unlock(conn->pending_buffer); - wlr_buffer_unlock(conn->current_buffer); - conn->pending_buffer = conn->current_buffer = NULL; - /* Fallthrough */ case WLR_DRM_CONN_NEEDS_MODESET: wlr_log(WLR_INFO, "Emitting destruction signal for '%s'", diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 8b295db1..4a16732c 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { 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, uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) { if (surf->width == width && surf->height == height) { @@ -73,14 +74,6 @@ bool init_drm_surface(struct wlr_drm_surface *surf, surf->height = height; 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); surf->gbm = NULL; } @@ -119,18 +112,11 @@ error_zero: 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) { 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); if (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)); } -bool make_drm_surface_current(struct wlr_drm_surface *surf, +bool drm_surface_make_current(struct wlr_drm_surface *surf, int *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) { 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; } -struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest, - struct gbm_bo *src) { - make_drm_surface_current(dest, NULL); - - 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); +void drm_plane_finish_surface(struct wlr_drm_plane *plane) { + if (!plane) { + return; + } - 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); + drm_fb_clear(&plane->pending_fb); + drm_fb_clear(&plane->queued_fb); + drm_fb_clear(&plane->current_fb); - return swap_drm_surface_buffers(dest, NULL); + finish_drm_surface(&plane->surf); + finish_drm_surface(&plane->mgpu_surf); } -bool init_drm_plane_surfaces(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, - uint32_t format, bool with_modifiers) { + uint32_t format, uint32_t flags, bool with_modifiers) { struct wlr_drm_format_set *format_set = with_modifiers ? &plane->formats : NULL; + drm_plane_finish_surface(plane); + if (!drm->parent) { 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, - width, height, format, NULL, GBM_BO_USE_LINEAR)) { + width, height, format, NULL, + flags | GBM_BO_USE_LINEAR)) { return false; } 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); return false; } 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; +} diff --git a/backend/drm/util.c b/backend/drm/util.c index f8a344d1..86d66be4 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -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, - bool with_modifiers) { +uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers) { uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo); if (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); uint32_t width = gbm_bo_get_width(bo); uint32_t height = gbm_bo_get_height(bo); + uint32_t format = gbm_bo_get_format(bo); uint32_t handles[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 (drmModeAddFB2WithModifiers(fd, width, height, drm_format, handles, + if (drmModeAddFB2WithModifiers(fd, width, height, format, handles, strides, offsets, modifiers, &id, DRM_MODE_FB_MODIFIERS)) { wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer"); } } else { - if (drmModeAddFB2(fd, width, height, drm_format, handles, strides, + if (drmModeAddFB2(fd, width, height, format, handles, strides, offsets, &id, 0)) { wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer"); } -- cgit v1.2.3