diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 41a38f6dd..085a9fbb7 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -562,6 +562,7 @@ static void drm_connector_state_finish(struct wlr_drm_connector_state *state) { static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn, struct wlr_drm_connector_state *state) { + bool ok; struct wlr_drm_backend *drm = conn->backend; assert(state->base->committed & WLR_OUTPUT_STATE_BUFFER); @@ -571,34 +572,63 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn struct wlr_drm_plane *plane = crtc->primary; struct wlr_buffer *source_buf = state->base->buffer; + struct wlr_buffer *local_buf = wlr_buffer_lock(source_buf); - struct wlr_buffer *local_buf; - if (drm->parent) { + /* + * First try to import the buffer. We can have a decent degree of + * confidence this will work for a couple reasons: + * 1. Apps running on the dGPU in PRIME setups will be submitting + * buffers with linear modifiers, so that they can be imported + * on the primary GPU. This means they are directly imporatable + * here as well. This gives a nice FPS boost. + * 2. When the dGPU app supports reacting to dmabuf feedback it will + * be using dGPU modifiers, again meaning it can be imported into + * the dGPU directly for an additional nice perf boost. + * + * The fallback drm_surface_blit path will only be hit when the + * app is running fullscreen with dGPU (non-linear) modifiers and + * we start using rendered composition again. For a frame we will + * do the fallback before the app reallocs its buffers back to + * linear to be compatible with the primary GPU. + */ + ok = drm_fb_import(&state->primary_fb, drm, local_buf, + &crtc->primary->formats); + + /* + * If trying to import this buffer directly didn't work then try + * to perform a blit to a mgpu drm surface and import that instead. + */ + if (!ok && drm->parent) { struct wlr_drm_format format = {0}; if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to pick primary plane format"); - return false; + ok = false; + goto release_buf; } // TODO: fallback to modifier-less buffer allocation - bool ok = init_drm_surface(&plane->mgpu_surf, &drm->mgpu_renderer, + ok = init_drm_surface(&plane->mgpu_surf, &drm->mgpu_renderer, source_buf->width, source_buf->height, &format); wlr_drm_format_finish(&format); if (!ok) { - return false; + ok = false; + goto release_buf; } - local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf); - if (local_buf == NULL) { - return false; + struct wlr_buffer *drm_buf = drm_surface_blit(&plane->mgpu_surf, + &drm->parent->mgpu_renderer, source_buf); + if (drm_buf == NULL) { + ok = false; + goto release_buf; } - } else { - local_buf = wlr_buffer_lock(source_buf); + ok = drm_fb_import(&state->primary_fb, drm, drm_buf, + &plane->formats); + wlr_buffer_unlock(drm_buf); } - bool ok = drm_fb_import(&state->primary_fb, drm, local_buf, - &plane->formats); +release_buf: wlr_buffer_unlock(local_buf); + if (!ok) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to import buffer for scan-out"); @@ -1012,7 +1042,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, return false; } - local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); + local_buf = drm_surface_blit(&plane->mgpu_surf, &drm->parent->mgpu_renderer, buffer); if (local_buf == NULL) { return false; } diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index af62e8191..ace2d1559 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -75,7 +75,7 @@ bool init_drm_surface(struct wlr_drm_surface *surf, } struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, - struct wlr_buffer *buffer) { + struct wlr_drm_renderer *parent_renderer, struct wlr_buffer *buffer) { struct wlr_renderer *renderer = surf->renderer->wlr_rend; if (surf->swapchain->width != buffer->width || @@ -84,11 +84,23 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, return NULL; } - struct wlr_texture *tex = wlr_texture_from_buffer(renderer, buffer); - if (tex == NULL) { - wlr_log(WLR_ERROR, "Failed to import source buffer into multi-GPU renderer"); + struct wlr_texture_set *set = wlr_texture_set_create(renderer, NULL); + if (set == NULL) { + wlr_log(WLR_ERROR, "Failed to import source buffer multi-GPU texture set"); return NULL; } + /* Add the parent renderer so the texture set can use it for copies */ + wlr_texture_set_add_renderer(set, parent_renderer->wlr_rend, parent_renderer->allocator); + if (!wlr_texture_set_import_buffer(set, buffer)) { + wlr_log(WLR_ERROR, "Failed to import source buffer multi-GPU texture set"); + goto error_tex; + } + + struct wlr_texture *tex = wlr_texture_set_get_tex_for_renderer(set, renderer); + if (tex == NULL) { + wlr_log(WLR_ERROR, "Failed to export source buffer for multi-GPU renderer"); + goto error_tex; + } struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL); if (!dst) { @@ -111,14 +123,14 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, goto error_dst; } - wlr_texture_destroy(tex); + wlr_texture_set_destroy(set); return dst; error_dst: wlr_buffer_unlock(dst); error_tex: - wlr_texture_destroy(tex); + wlr_texture_set_destroy(set); return NULL; } diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index f53f720bc..04710c61e 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -32,7 +32,7 @@ bool init_drm_surface(struct wlr_drm_surface *surf, void finish_drm_surface(struct wlr_drm_surface *surf); struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, - struct wlr_buffer *buffer); + struct wlr_drm_renderer *parent_renderer, struct wlr_buffer *buffer); bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer); diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 6c399ba2b..d695acd04 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -1096,15 +1096,6 @@ static bool devid_from_fd(int fd, dev_t *devid) { return true; } -static bool is_secondary_drm_backend(struct wlr_backend *backend) { -#if WLR_HAS_DRM_BACKEND - return wlr_backend_is_drm(backend) && - wlr_drm_backend_get_parent(backend) != NULL; -#else - return false; -#endif -} - bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feedback_v1 *feedback, const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { assert(options->main_renderer != NULL); @@ -1147,8 +1138,7 @@ bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feed wlr_log(WLR_ERROR, "Failed to intersect renderer and scanout formats"); goto error; } - } else if (options->scanout_primary_output != NULL && - !is_secondary_drm_backend(options->scanout_primary_output->backend)) { + } else if (options->scanout_primary_output != NULL) { int backend_drm_fd = wlr_backend_get_drm_fd(options->scanout_primary_output->backend); if (backend_drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to get backend DRM FD"); @@ -1174,8 +1164,9 @@ bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feed tranche->target_device = backend_dev; tranche->flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT; - if (!wlr_drm_format_set_intersect(&tranche->formats, scanout_formats, renderer_formats)) { - wlr_log(WLR_ERROR, "Failed to intersect renderer and scanout formats"); + // Copy our scanout formats to the scanout tranche + if (!wlr_drm_format_set_copy(&tranche->formats, scanout_formats)) { + wlr_log(WLR_ERROR, "Failed to copy scanout formats"); goto error; } }