Merge branch 'esync_cursor' into 'master'

Draft: cursor: explicit sync without `signal_release_with_buffer`

See merge request wlroots/wlroots!5302
This commit is contained in:
Félix Poisot 2026-04-12 11:47:16 +00:00
commit 6e0d30e84a
9 changed files with 172 additions and 19 deletions

View file

@ -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);

View file

@ -3,6 +3,8 @@
#include <wayland-server-core.h>
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

View file

@ -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

View file

@ -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.

View file

@ -4,8 +4,10 @@
#include <unistd.h>
#include <wayland-util.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#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;
}

View file

@ -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);
}

View file

@ -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)) {

View file

@ -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);

View file

@ -12,6 +12,7 @@
#include <xf86drm.h>
#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);
}