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)) { + struct wlr_buffer *buffer = output->pending.buffer; + if (!test_buffer(conn, output->pending.buffer)) { return false; } - - bo = import_gbm_bo(&drm->renderer, &attribs); - 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"); + 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); + if (!drm_crtc_page_flip(conn, NULL)) { + drm_fb_clear(&plane->pending_fb); 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; } @@ -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); + 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, 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; } - struct wlr_drm_plane *plane = crtc->primary; - 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); + // 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; + } + } + + 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 (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: + 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); + + /* 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); +void drm_plane_finish_surface(struct wlr_drm_plane *plane) { + if (!plane) { + return; + } - struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src); - assert(tex); + drm_fb_clear(&plane->pending_fb); + drm_fb_clear(&plane->queued_fb); + drm_fb_clear(&plane->current_fb); - 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); + 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"); } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index ffd3138f..0ab4ff21 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -25,11 +25,17 @@ struct wlr_drm_plane { struct wlr_drm_surface 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 struct wlr_drm_format_set formats; // Only used by cursor - float matrix[9]; bool cursor_enabled; int32_t cursor_hotspot_x, cursor_hotspot_y; @@ -124,16 +130,16 @@ struct wlr_drm_connector { drmModeCrtc *old_crtc; - bool pageflip_pending; struct wl_event_source *retry_pageflip; struct wl_list link; - // Buffer submitted to the kernel but not yet displayed - struct wlr_buffer *pending_buffer; - struct gbm_bo *pending_bo; - // Buffer currently being displayed - struct wlr_buffer *current_buffer; - struct gbm_bo *current_bo; + /* + * We've asked for a state change in the kernel, and yet to recieve a + * notification for its completion. Currently, the kernel only has a + * queue length of 1, and no way to modify your submissions after + * they're sent. + */ + bool pageflip_pending; }; 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, 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, struct wlr_drm_crtc *crtc, struct gbm_bo *bo); bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm, diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index 567cf95e..bfccf9d5 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -10,6 +10,7 @@ struct wlr_drm_backend; struct wlr_drm_plane; +struct wlr_buffer; struct wlr_drm_renderer { int fd; @@ -29,33 +30,48 @@ struct wlr_drm_surface { struct gbm_surface *gbm; EGLSurface egl; +}; - struct gbm_bo *front; - struct gbm_bo *back; +enum wlr_drm_fb_type { + 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, struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render); 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, - 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 drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); 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 diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index 2d9d11f4..15895ec6 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -14,8 +14,7 @@ void parse_edid(struct wlr_output *restrict output, size_t len, // Returns the string representation of a DRM output type const char *conn_get_name(uint32_t type_id); // Returns the DRM framebuffer id for a gbm_bo -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); // Part of match_obj enum {