diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index d3a91efb..16b08f7a 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -275,9 +275,9 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo } int in_fence_fd = -1; - if (state->base->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { - in_fence_fd = wlr_drm_syncobj_timeline_export_sync_file(state->base->wait_timeline, - state->base->wait_point); + if (state->wait_timeline != NULL) { + in_fence_fd = wlr_drm_syncobj_timeline_export_sync_file(state->wait_timeline, + state->wait_point); if (in_fence_fd < 0) { return false; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 1ffa34d5..c81517a4 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -693,6 +694,7 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, static void drm_connector_state_finish(struct wlr_drm_connector_state *state) { drm_fb_clear(&state->primary_fb); drm_fb_clear(&state->cursor_fb); + wlr_drm_syncobj_timeline_unref(state->wait_timeline); } static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn, @@ -707,6 +709,14 @@ 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_drm_syncobj_timeline *wait_timeline = NULL; + uint64_t wait_point = 0; + if (state->base->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + wait_timeline = state->base->wait_timeline; + wait_point = state->base->wait_point; + } + assert(state->wait_timeline == NULL); + struct wlr_buffer *local_buf; if (drm->parent) { struct wlr_drm_format format = {0}; @@ -723,12 +733,23 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn return false; } - local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf); + local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf, + wait_timeline, wait_point); if (local_buf == NULL) { return false; } + + if (plane->mgpu_surf.timeline != NULL) { + state->wait_timeline = wlr_drm_syncobj_timeline_ref(plane->mgpu_surf.timeline); + state->wait_point = plane->mgpu_surf.point; + } } else { local_buf = wlr_buffer_lock(source_buf); + + if (wait_timeline != NULL) { + state->wait_timeline = wlr_drm_syncobj_timeline_ref(wait_timeline); + state->wait_point = wait_point; + } } bool ok = drm_fb_import(&state->primary_fb, drm, local_buf, @@ -1088,7 +1109,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, buffer, NULL, 0); if (local_buf == NULL) { return false; } @@ -1625,9 +1646,11 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, output->non_desktop = non_desktop; } - // TODO: support sync timelines in multi-GPU mode // TODO: support sync timelines with libliftoff - output->timeline = drm->parent == NULL && drm->iface == &atomic_iface; + output->timeline = drm->iface == &atomic_iface; + if (drm->parent) { + output->timeline = output->timeline && drm->mgpu_renderer.wlr_rend->features.timeline; + } memset(wlr_conn->max_bpc_bounds, 0, sizeof(wlr_conn->max_bpc_bounds)); if (wlr_conn->props.max_bpc != 0) { diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 37569c18..60acc57b 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ void finish_drm_surface(struct wlr_drm_surface *surf) { return; } + wlr_drm_syncobj_timeline_unref(surf->timeline); wlr_swapchain_destroy(surf->swapchain); *surf = (struct wlr_drm_surface){0}; @@ -68,13 +70,24 @@ bool init_drm_surface(struct wlr_drm_surface *surf, return false; } + int drm_fd = wlr_renderer_get_drm_fd(renderer->wlr_rend); + if (renderer->wlr_rend->features.timeline && drm_fd >= 0) { + surf->timeline = wlr_drm_syncobj_timeline_create(drm_fd); + if (surf->timeline == NULL) { + finish_drm_surface(surf); + wlr_log(WLR_ERROR, "Failed to create DRM syncobj timeline"); + return false; + } + } + surf->renderer = renderer; return true; } struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, - struct wlr_buffer *buffer) { + struct wlr_buffer *buffer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { struct wlr_renderer *renderer = surf->renderer->wlr_rend; if (surf->swapchain->width != buffer->width || @@ -95,7 +108,12 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, goto error_tex; } - struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL); + surf->point++; + const struct wlr_buffer_pass_options pass_options = { + .signal_timeline = surf->timeline, + .signal_point = surf->point, + }; + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, &pass_options); if (pass == NULL) { wlr_log(WLR_ERROR, "Failed to begin render pass with multi-GPU destination buffer"); goto error_dst; @@ -104,6 +122,8 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = tex, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + .wait_timeline = wait_timeline, + .wait_point = wait_point, }); if (!wlr_render_pass_submit(pass)) { wlr_log(WLR_ERROR, "Failed to submit multi-GPU render pass"); diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 33b729aa..5b239a18 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -141,6 +141,9 @@ struct wlr_drm_connector_state { struct wlr_drm_fb *primary_fb; struct wlr_drm_fb *cursor_fb; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + // used by atomic uint32_t mode_id; uint32_t gamma_lut; diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index f53f720b..2cf98fdb 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -20,6 +20,9 @@ struct wlr_drm_renderer { struct wlr_drm_surface { struct wlr_drm_renderer *renderer; struct wlr_swapchain *swapchain; + + struct wlr_drm_syncobj_timeline *timeline; + uint64_t point; }; bool init_drm_renderer(struct wlr_drm_backend *drm, @@ -32,7 +35,8 @@ 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_buffer *buffer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point); bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer);