diff --git a/examples/pointer.c b/examples/pointer.c index f3ef5435e..5b0fb5b07 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -111,7 +111,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { state->clear_color[3], }, }); - wlr_output_add_software_cursors_to_render_pass(wlr_output, pass, NULL); + wlr_output_add_software_cursors_to_render_pass(wlr_output, pass, NULL, NULL, 0); wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &output_state); wlr_output_state_finish(&output_state); diff --git a/include/render/drm_syncobj_merger.h b/include/render/drm_syncobj_merger.h index c55c87314..397c5ce80 100644 --- a/include/render/drm_syncobj_merger.h +++ b/include/render/drm_syncobj_merger.h @@ -3,6 +3,8 @@ #include +struct wlr_buffer; + /** * Accumulate timeline points, to have a destination timeline point be * signalled when all inputs are @@ -41,4 +43,22 @@ bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger, struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point, struct wl_event_loop *loop); +/** + * Add a new sync file to wait for. + * + * Ownership of fd is transferred to the merger. + */ +bool wlr_drm_syncobj_merger_add_sync_file(struct wlr_drm_syncobj_merger *merger, + int fd); + +/** + * Add a new DMA-BUF release to wait for. + * + * Waits for write access. + * If the platform does not support DMA-BUF<->sync file interop, the supplied + * event_loop is used to schedule a wait. + */ +bool wlr_drm_syncobj_merger_add_dmabuf(struct wlr_drm_syncobj_merger *merger, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop); + #endif diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h index 7fd55ec2e..dfcbc4d43 100644 --- a/include/wlr/types/wlr_linux_drm_syncobj_v1.h +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -74,4 +74,21 @@ bool wlr_linux_drm_syncobj_v1_state_add_release_point( struct wlr_drm_syncobj_timeline *release_timeline, uint64_t release_point, struct wl_event_loop *event_loop); +/** + * Register the DMA-BUF release of a buffer for buffer usage. + * Non-dmabuf buffers are considered to be immediately available (no wait). + * + * This function may be called multiple times for the same commit. The client's + * release point will be signalled when all registered points are signalled, and + * a new buffer has been committed. + * + * Because the platform may not support DMA-BUF fence merges, a wl_event_loop + * must be supplied to schedule a wait internally, if needed + * + * Waits for write access + */ +bool wlr_linux_drm_syncobj_v1_state_add_release_from_implicit_sync( + struct wlr_linux_drm_syncobj_surface_v1_state *state, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop); + #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c8e44b0e6..3611c2e98 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -36,6 +36,11 @@ struct wlr_output_mode { struct wl_list link; }; +struct wlr_output_cursor_texture_sample_event { + struct wlr_drm_syncobj_timeline *release_timeline; + uint64_t release_point; +}; + struct wlr_output_cursor { struct wlr_output *output; double x, y; @@ -51,6 +56,10 @@ struct wlr_output_cursor { uint64_t wait_point; struct wl_list link; + struct { + struct wl_signal texture_sample; // struct wlr_output_cursor_texture_sample_event + } events; + struct { struct wl_listener renderer_destroy; struct wlr_color_transform *color_transform; @@ -442,7 +451,8 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock); * This is a utility function that can be called when compositors render. */ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, - struct wlr_render_pass *render_pass, const pixman_region32_t *damage); + struct wlr_render_pass *render_pass, const pixman_region32_t *damage, + struct wlr_drm_syncobj_timeline *release_timeline, uint64_t release_point); /** * Get the set of DRM formats suitable for the primary buffer, assuming a * buffer with the specified capabilities. diff --git a/render/drm_syncobj_merger.c b/render/drm_syncobj_merger.c index d50d28c28..0cfdfcc96 100644 --- a/render/drm_syncobj_merger.c +++ b/render/drm_syncobj_merger.c @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include "render/dmabuf.h" #include "render/drm_syncobj_merger.h" #include "config.h" @@ -79,14 +81,7 @@ void wlr_drm_syncobj_merger_unref(struct wlr_drm_syncobj_merger *merger) { static bool merger_add_exportable(struct wlr_drm_syncobj_merger *merger, struct wlr_drm_syncobj_timeline *src_timeline, uint64_t src_point) { int new_sync = wlr_drm_syncobj_timeline_export_sync_file(src_timeline, src_point); - if (merger->sync_fd != -1) { - int fd2 = new_sync; - new_sync = sync_file_merge(merger->sync_fd, fd2); - close(fd2); - close(merger->sync_fd); - } - merger->sync_fd = new_sync; - return true; + return wlr_drm_syncobj_merger_add_sync_file(merger, new_sync); } struct export_waiter { @@ -131,3 +126,69 @@ bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger, merger->n_ref++; return true; } + +bool wlr_drm_syncobj_merger_add_sync_file(struct wlr_drm_syncobj_merger *merger, + int fd) { + int new_sync = fd; + if (merger->sync_fd != -1) { + new_sync = sync_file_merge(merger->sync_fd, fd); + close(fd); + close(merger->sync_fd); + } + merger->sync_fd = new_sync; + return merger->sync_fd != -1; +} + +struct poll_waiter { + struct wl_event_source *event_source; + struct wlr_drm_syncobj_merger *merger; +}; + +static int poll_waiter_handle_done(int fd, uint32_t mask, void *data) { + struct poll_waiter *waiter = data; + wlr_drm_syncobj_merger_unref(waiter->merger); + wl_event_source_remove(waiter->event_source); + free(waiter); + return 0; +} + +bool wlr_drm_syncobj_merger_add_dmabuf(struct wlr_drm_syncobj_merger *merger, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop) { + struct wlr_dmabuf_attributes dmabuf_attributes; + if (!wlr_buffer_get_dmabuf(buffer, &dmabuf_attributes)) { + return true; + } + + bool res = true; + for (int i = 0; i < dmabuf_attributes.n_planes; ++i) { + int sync_fd = dmabuf_export_sync_file(dmabuf_attributes.fd[i], DMA_BUF_SYNC_WRITE); + if (sync_fd == -1) { + res = false; + break; + } + if (!wlr_drm_syncobj_merger_add_sync_file(merger, sync_fd)) { + return false; + } + } + + if (res) { + return true; + } + + uint32_t mask = WL_EVENT_ERROR | WL_EVENT_HANGUP | WL_EVENT_WRITABLE; + for (int i = 0; i < dmabuf_attributes.n_planes; ++i) { + struct poll_waiter *waiter = calloc(1, sizeof(*waiter)); + if (waiter == NULL) { + return false; + } + waiter->merger = wlr_drm_syncobj_merger_ref(merger); + waiter->event_source = wl_event_loop_add_fd(event_loop, + dmabuf_attributes.fd[i], mask, poll_waiter_handle_done, waiter); + if (waiter->event_source == NULL) { + wlr_drm_syncobj_merger_unref(waiter->merger); + free(waiter); + return false; + } + } + return true; +} diff --git a/types/output/cursor.c b/types/output/cursor.c index 4a823dab1..895554ef9 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -89,7 +89,8 @@ static void output_cursor_get_box(struct wlr_output_cursor *cursor, } void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, - struct wlr_render_pass *render_pass, const pixman_region32_t *damage) { + struct wlr_render_pass *render_pass, const pixman_region32_t *damage, + struct wlr_drm_syncobj_timeline *release_timeline, uint64_t release_point) { int width, height; wlr_output_transformed_resolution(output, &width, &height); @@ -128,7 +129,14 @@ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, .dst_box = box, .clip = &cursor_damage, .transform = output->transform, + .wait_timeline = cursor->wait_timeline, + .wait_point = cursor->wait_point, }); + struct wlr_output_cursor_texture_sample_event event = { + .release_timeline = release_timeline, + .release_point = release_point, + }; + wl_signal_emit_mutable(&cursor->events.texture_sample, &event); pixman_region32_fini(&cursor_damage); } @@ -285,6 +293,9 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) return NULL; } + struct wlr_output_cursor_texture_sample_event event = {0}; + wl_signal_emit_mutable(&cursor->events.texture_sample, &event); + return buffer; } @@ -472,6 +483,7 @@ struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { cursor->output = output; wl_list_insert(&output->cursors, &cursor->link); cursor->visible = true; // default position is at (0, 0) + wl_signal_init(&cursor->events.texture_sample); wl_list_init(&cursor->renderer_destroy.link); output_cursor_refresh_color_transform(cursor, output->image_description); return cursor; @@ -493,6 +505,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { } wlr_drm_syncobj_timeline_unref(cursor->wait_timeline); wl_list_remove(&cursor->link); + assert(wl_list_empty(&cursor->events.texture_sample.listener_list)); wlr_color_transform_unref(cursor->color_transform); free(cursor); } diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7231422e8..d6a2a3573 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2581,7 +2581,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage); + wlr_output_add_software_cursors_to_render_pass(output, render_pass, + &render_data.damage, scene_output->in_timeline, scene_output->in_point); pixman_region32_fini(&render_data.damage); if (!wlr_render_pass_submit(render_pass)) { diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 8d0d77475..1b9146c40 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -63,6 +63,7 @@ struct wlr_cursor_output_cursor { // only when using a surface as the cursor image struct wl_listener output_commit; + struct wl_listener texture_sample; // only when using an XCursor as the cursor image struct wlr_xcursor *xcursor; @@ -160,6 +161,7 @@ static void output_cursor_destroy(struct wlr_cursor_output_cursor *output_cursor wl_list_remove(&output_cursor->layout_output_destroy.link); wl_list_remove(&output_cursor->link); wl_list_remove(&output_cursor->output_commit.link); + wl_list_remove(&output_cursor->texture_sample.link); wlr_output_cursor_destroy(output_cursor->output_cursor); free(output_cursor); } @@ -579,13 +581,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ &src_box, dst_width, dst_height, surface->current.transform, hotspot_x, hotspot_y, wait_timeline, wait_point); - if (syncobj_surface_state != NULL && - surface->buffer != NULL && surface->buffer->source != NULL && - (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { - wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - surface->buffer->source); - } - if (output_cursor->output_cursor->visible) { wlr_surface_send_enter(surface, output); } else { @@ -645,6 +640,29 @@ static void output_cursor_output_handle_output_commit( } } +static void output_cursor_output_handle_texture_sample( + struct wl_listener *listener, void *data) { + struct wlr_cursor_output_cursor *output_cursor = + wl_container_of(listener, output_cursor, texture_sample); + const struct wlr_output_cursor_texture_sample_event *event = data; + + struct wlr_surface *surface = output_cursor->cursor->state->surface; + struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_state = NULL; + if (surface != NULL) { + syncobj_state = wlr_linux_drm_syncobj_v1_get_surface_state(surface); + } + if (syncobj_state != NULL) { + struct wl_event_loop *event_loop = output_cursor->output_cursor->output->event_loop; + if (event->release_timeline != NULL) { + wlr_linux_drm_syncobj_v1_state_add_release_point(syncobj_state, + event->release_timeline, event->release_point, event_loop); + } else if (surface->buffer != NULL && surface->buffer->source != NULL) { + wlr_linux_drm_syncobj_v1_state_add_release_from_implicit_sync(syncobj_state, + surface->buffer->source, event_loop); + } + } +} + static void cursor_update_outputs(struct wlr_cursor *cur) { struct wlr_cursor_output_cursor *output_cursor; wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { @@ -1167,6 +1185,9 @@ static void layout_add(struct wlr_cursor_state *state, wl_signal_add(&output_cursor->output_cursor->output->events.commit, &output_cursor->output_commit); output_cursor->output_commit.notify = output_cursor_output_handle_output_commit; + wl_signal_add(&output_cursor->output_cursor->events.texture_sample, + &output_cursor->texture_sample); + output_cursor->texture_sample.notify = output_cursor_output_handle_texture_sample; output_cursor_move(output_cursor); cursor_output_cursor_update(output_cursor); diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 53fc2fd43..b9865dcce 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -12,6 +12,7 @@ #include #include "config.h" #include "linux-drm-syncobj-v1-protocol.h" +#include "render/dmabuf.h" #include "render/drm_syncobj_merger.h" #define LINUX_DRM_SYNCOBJ_V1_VERSION 1 @@ -540,3 +541,12 @@ bool wlr_linux_drm_syncobj_v1_state_add_release_point( return wlr_drm_syncobj_merger_add(state->release_merger, release_timeline, release_point, event_loop); } + +bool wlr_linux_drm_syncobj_v1_state_add_release_from_implicit_sync( + struct wlr_linux_drm_syncobj_surface_v1_state *state, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop) { + if (state->release_merger != NULL) { + return true; + } + return wlr_drm_syncobj_merger_add_dmabuf(state->release_merger, buffer, event_loop); +}