Merge branch 'syncobj_merge_2' into 'master'

scene: fine-grained explicit sync release points

See merge request wlroots/wlroots!4979
This commit is contained in:
Félix Poisot 2026-03-07 05:44:11 +00:00
commit 76e5eaf808
14 changed files with 337 additions and 67 deletions

View file

@ -423,12 +423,6 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) {
if (state->primary_in_fence_fd >= 0) {
close(state->primary_in_fence_fd);
}
if (state->out_fence_fd >= 0) {
// TODO: error handling
wlr_drm_syncobj_timeline_import_sync_file(state->base->signal_timeline,
state->base->signal_point, state->out_fence_fd);
close(state->out_fence_fd);
}
conn->colorspace = state->colorspace;
}
@ -446,9 +440,6 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state)
if (state->primary_in_fence_fd >= 0) {
close(state->primary_in_fence_fd);
}
if (state->out_fence_fd >= 0) {
close(state->out_fence_fd);
}
}
static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) {
@ -500,19 +491,6 @@ static void set_plane_in_fence_fd(struct atomic *atom,
atomic_add(atom, plane->id, plane->props.in_fence_fd, sync_file_fd);
}
static void set_crtc_out_fence_ptr(struct atomic *atom, struct wlr_drm_crtc *crtc,
int *fd_ptr) {
if (!crtc->props.out_fence_ptr) {
wlr_log(WLR_ERROR,
"CRTC %"PRIu32" is missing the OUT_FENCE_PTR property",
crtc->id);
atom->failed = true;
return;
}
atomic_add(atom, crtc->id, crtc->props.out_fence_ptr, (uintptr_t)fd_ptr);
}
static void atomic_connector_add(struct atomic *atom,
struct wlr_drm_connector_state *state, bool modeset) {
struct wlr_drm_connector *conn = state->connector;
@ -557,9 +535,6 @@ static void atomic_connector_add(struct atomic *atom,
if (state->primary_in_fence_fd >= 0) {
set_plane_in_fence_fd(atom, crtc->primary, state->primary_in_fence_fd);
}
if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) {
set_crtc_out_fence_ptr(atom, crtc, &state->out_fence_fd);
}
if (crtc->cursor) {
if (drm_connector_is_cursor_visible(conn)) {
struct wlr_fbox cursor_src = {

View file

@ -367,7 +367,17 @@ static void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
}
drm_fb_clear(&plane->queued_fb);
if (plane->queued_release_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(plane->queued_release_timeline, plane->queued_release_point);
wlr_drm_syncobj_timeline_unref(plane->queued_release_timeline);
plane->queued_release_timeline = NULL;
}
drm_fb_clear(&plane->current_fb);
if (plane->current_release_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(plane->current_release_timeline, plane->current_release_point);
wlr_drm_syncobj_timeline_unref(plane->current_release_timeline);
plane->current_release_timeline = NULL;
}
finish_drm_surface(&plane->mgpu_surf);
}
@ -556,6 +566,18 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta
struct wlr_drm_crtc *crtc = conn->crtc;
drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb);
if (crtc->primary->queued_release_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(crtc->primary->queued_release_timeline, crtc->primary->queued_release_point);
wlr_drm_syncobj_timeline_unref(crtc->primary->queued_release_timeline);
}
if (state->base->signal_timeline != NULL) {
crtc->primary->queued_release_timeline = wlr_drm_syncobj_timeline_ref(state->base->signal_timeline);
crtc->primary->queued_release_point = state->base->signal_point;
} else {
crtc->primary->queued_release_timeline = NULL;
crtc->primary->queued_release_point = 0;
}
crtc->primary->viewport = state->primary_viewport;
if (crtc->cursor != NULL) {
drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb);
@ -646,7 +668,6 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state,
.base = base,
.active = output_pending_enabled(&conn->output, base),
.primary_in_fence_fd = -1,
.out_fence_fd = -1,
};
struct wlr_output_mode *mode = conn->output.current_mode;
@ -2018,6 +2039,14 @@ static void handle_page_flip(int fd, unsigned seq,
struct wlr_drm_plane *plane = conn->crtc->primary;
if (plane->queued_fb) {
drm_fb_move(&plane->current_fb, &plane->queued_fb);
if (plane->current_release_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(plane->current_release_timeline, plane->current_release_point);
wlr_drm_syncobj_timeline_unref(plane->current_release_timeline);
}
plane->current_release_timeline = plane->queued_release_timeline;
plane->current_release_point = plane->queued_release_point;
plane->queued_release_timeline = NULL;
plane->queued_release_point = 0;
}
if (conn->crtc->cursor && conn->crtc->cursor->queued_fb) {
drm_fb_move(&conn->crtc->cursor->current_fb,

View file

@ -352,10 +352,6 @@ static bool add_connector(drmModeAtomicReq *req,
liftoff_layer_set_property(crtc->liftoff_composition_layer,
"IN_FENCE_FD", state->primary_in_fence_fd);
}
if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) {
ok = ok && add_prop(req, crtc->id, crtc->props.out_fence_ptr,
(uintptr_t)&state->out_fence_fd);
}
if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) {
for (size_t i = 0; i < state->base->layers_len; i++) {

View file

@ -29,8 +29,12 @@ struct wlr_drm_plane {
/* Buffer submitted to the kernel, will be presented on next vblank */
struct wlr_drm_fb *queued_fb;
struct wlr_drm_syncobj_timeline *queued_release_timeline;
uint64_t queued_release_point;
/* Buffer currently displayed on screen */
struct wlr_drm_fb *current_fb;
struct wlr_drm_syncobj_timeline *current_release_timeline;
uint64_t current_release_point;
/* Viewport belonging to the last committed fb */
struct wlr_drm_viewport viewport;
@ -156,7 +160,7 @@ struct wlr_drm_connector_state {
uint32_t mode_id;
uint32_t gamma_lut;
uint32_t fb_damage_clips;
int primary_in_fence_fd, out_fence_fd;
int primary_in_fence_fd;
bool vrr_enabled;
uint32_t colorspace;
uint32_t hdr_output_metadata;

View file

@ -0,0 +1,44 @@
#ifndef WLR_RENDER_DRM_SYNCOBJ_MERGER_H
#define WLR_RENDER_DRM_SYNCOBJ_MERGER_H
#include <wayland-server-core.h>
/**
* Accumulate timeline points, to have a destination timeline point be
* signalled when all inputs are
*/
struct wlr_drm_syncobj_merger {
int n_ref;
struct wlr_drm_syncobj_timeline *dst_timeline;
uint64_t dst_point;
int sync_fd;
};
/**
* Create a new merger.
*
* The given timeline point will be signalled when all input points are
* signalled and the merger is destroyed.
*/
struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_create(
struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point);
struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_ref(
struct wlr_drm_syncobj_merger *merger);
/**
* Target timeline point is materialized when all inputs are, and the merger is
* destroyed.
*/
void wlr_drm_syncobj_merger_unref(struct wlr_drm_syncobj_merger *merger);
/**
* Add a new timeline point to wait for.
*
* If the point is not materialized, the supplied event loop is used to schedule
* a wait.
*/
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);
#endif

View file

@ -90,6 +90,10 @@ bool wlr_drm_syncobj_timeline_transfer(struct wlr_drm_syncobj_timeline *dst,
*/
bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline,
uint64_t point, uint32_t flags, bool *result);
/**
* Signals a timeline point.
*/
bool wlr_drm_syncobj_timeline_signal(struct wlr_drm_syncobj_timeline *timeline, uint64_t point);
/**
* Asynchronously wait for a timeline point.
*

View file

@ -21,6 +21,8 @@ struct wlr_linux_drm_syncobj_surface_v1_state {
struct wlr_drm_syncobj_timeline *release_timeline;
uint64_t release_point;
struct wlr_drm_syncobj_merger *release_merger;
};
struct wlr_linux_drm_syncobj_manager_v1 {

View file

@ -155,6 +155,8 @@ struct wlr_scene_outputs_update_event {
struct wlr_scene_output_sample_event {
struct wlr_scene_output *output;
bool direct_scanout;
struct wlr_drm_syncobj_timeline *release_timeline;
uint64_t release_point;
};
struct wlr_scene_frame_done_event {
@ -209,6 +211,7 @@ struct wlr_scene_buffer {
struct wlr_drm_syncobj_timeline *wait_timeline;
uint64_t wait_point;
struct wlr_drm_syncobj_merger *release_merger;
struct wl_listener buffer_release;
struct wl_listener renderer_destroy;
@ -268,6 +271,8 @@ struct wlr_scene_output {
struct wlr_drm_syncobj_timeline *in_timeline;
uint64_t in_point;
struct wlr_drm_syncobj_timeline *out_timeline;
uint64_t out_point;
} WLR_PRIVATE;
};
@ -503,6 +508,9 @@ struct wlr_scene_buffer_set_buffer_options {
// Wait for a timeline synchronization point before reading from the buffer.
struct wlr_drm_syncobj_timeline *wait_timeline;
uint64_t wait_point;
// Synchronize with last read from the buffer.
struct wlr_drm_syncobj_merger *release_merger;
};
/**

View file

@ -177,6 +177,14 @@ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline,
return true;
}
bool wlr_drm_syncobj_timeline_signal(struct wlr_drm_syncobj_timeline *timeline, uint64_t point) {
if (drmSyncobjTimelineSignal(timeline->drm_fd, &timeline->handle, &point, 1) != 0) {
wlr_log(WLR_ERROR, "drmSyncobjTimelineSignal() failed");
return false;
}
return true;
}
static int handle_eventfd_ready(int ev_fd, uint32_t mask, void *data) {
struct wlr_drm_syncobj_timeline_waiter *waiter = data;

130
render/drm_syncobj_merger.c Normal file
View file

@ -0,0 +1,130 @@
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-util.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include "render/drm_syncobj_merger.h"
#include "config.h"
#if HAVE_LINUX_SYNC_FILE
#include <linux/sync_file.h>
#include <sys/ioctl.h>
static int sync_file_merge(int fd1, int fd2) {
// The kernel will automatically prune signalled fences
struct sync_merge_data merge_data = { .fd2 = fd2 };
if (ioctl(fd1, SYNC_IOC_MERGE, &merge_data) < 0) {
wlr_log_errno(WLR_ERROR, "ioctl(SYNC_IOC_MERGE) failed");
return -1;
}
return merge_data.fence;
}
#else
static int sync_file_merge(int fd1, int fd2) {
wlr_log(WLR_ERROR, "sync_file support is unavailable");
return -1;
}
#endif
struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_create(
struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point) {
struct wlr_drm_syncobj_merger *merger = calloc(1, sizeof(*merger));
if (merger == NULL) {
return NULL;
}
merger->n_ref = 1;
merger->dst_timeline = wlr_drm_syncobj_timeline_ref(dst_timeline);
merger->dst_point = dst_point;
merger->sync_fd = -1;
return merger;
}
struct wlr_drm_syncobj_merger *wlr_drm_syncobj_merger_ref(
struct wlr_drm_syncobj_merger *merger) {
assert(merger->n_ref > 0);
merger->n_ref++;
return merger;
}
void wlr_drm_syncobj_merger_unref(struct wlr_drm_syncobj_merger *merger) {
assert(merger->n_ref > 0);
merger->n_ref--;
if (merger->n_ref > 0) {
return;
}
if (merger->sync_fd != -1) {
wlr_drm_syncobj_timeline_import_sync_file(merger->dst_timeline,
merger->dst_point, merger->sync_fd);
close(merger->sync_fd);
} else {
wlr_drm_syncobj_timeline_signal(merger->dst_timeline, merger->dst_point);
}
wlr_drm_syncobj_timeline_unref(merger->dst_timeline);
free(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;
}
struct export_waiter {
struct wlr_drm_syncobj_timeline_waiter waiter;
struct wlr_drm_syncobj_merger *merger;
struct wlr_drm_syncobj_timeline *src_timeline;
uint64_t src_point;
};
static void export_waiter_handle_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) {
struct export_waiter *add = wl_container_of(waiter, add, waiter);
merger_add_exportable(add->merger, add->src_timeline, add->src_point);
wlr_drm_syncobj_merger_unref(add->merger);
wlr_drm_syncobj_timeline_unref(add->src_timeline);
wlr_drm_syncobj_timeline_waiter_finish(&add->waiter);
free(add);
}
bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger,
struct wlr_drm_syncobj_timeline *src_timeline, uint64_t src_point,
struct wl_event_loop *loop) {
assert(loop != NULL);
bool exportable = false;
int flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
if (!wlr_drm_syncobj_timeline_check(src_timeline, src_point, flags, &exportable)) {
return false;
}
if (exportable) {
return merger_add_exportable(merger, src_timeline, src_point);
}
struct export_waiter *add = calloc(1, sizeof(*add));
if (add == NULL) {
return false;
}
if (!wlr_drm_syncobj_timeline_waiter_init(&add->waiter, src_timeline, src_point,
flags, loop, export_waiter_handle_ready)) {
return false;
}
add->merger = merger;
add->src_timeline = wlr_drm_syncobj_timeline_ref(src_timeline);
add->src_point = src_point;
merger->n_ref++;
return true;
}

View file

@ -9,6 +9,7 @@ wlr_files += files(
'color.c',
'dmabuf.c',
'drm_format_set.c',
'drm_syncobj_merger.c',
'drm_syncobj.c',
'pass.c',
'pixel_format.c',
@ -28,6 +29,7 @@ else
endif
internal_config.set10('HAVE_EVENTFD', cc.has_header('sys/eventfd.h'))
internal_config.set10('HAVE_LINUX_SYNC_FILE', cc.has_header('linux/sync_file.h'))
if 'gles2' in renderers or 'auto' in renderers
egl = dependency('egl', required: 'gles2' in renderers)

View file

@ -311,27 +311,25 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) {
struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
wlr_linux_drm_syncobj_v1_get_surface_state(surface);
struct wlr_drm_syncobj_timeline *wait_timeline = NULL;
uint64_t wait_point = 0;
if (syncobj_surface_state != NULL) {
wait_timeline = syncobj_surface_state->acquire_timeline;
wait_point = syncobj_surface_state->acquire_point;
}
struct wlr_scene_buffer_set_buffer_options options = {
.damage = &surface->buffer_damage,
.wait_timeline = wait_timeline,
.wait_point = wait_point,
.wait_timeline = scene_buffer->wait_timeline,
.wait_point = scene_buffer->wait_point,
.release_merger = scene_buffer->release_merger,
};
if (surface->current.committed & WLR_SURFACE_STATE_BUFFER) {
if (syncobj_surface_state != NULL) {
options.wait_timeline = syncobj_surface_state->acquire_timeline;
options.wait_point = syncobj_surface_state->acquire_point;
options.release_merger = syncobj_surface_state->release_merger;
} else if (syncobj_surface_state == NULL) {
options.wait_timeline = NULL;
options.wait_point = 0;
options.release_merger = NULL;
}
}
wlr_scene_buffer_set_buffer_with_options(scene_buffer,
&surface->buffer->base, &options);
if (syncobj_surface_state != NULL &&
(surface->current.committed & WLR_SURFACE_STATE_BUFFER) &&
surface->buffer->source != NULL) {
wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state,
surface->buffer->source);
}
} else {
wlr_scene_buffer_set_buffer(scene_buffer, NULL);
}

View file

@ -16,6 +16,7 @@
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
#include "render/color.h"
#include "render/drm_syncobj_merger.h"
#include "types/wlr_output.h"
#include "types/wlr_scene.h"
#include "util/array.h"
@ -128,6 +129,9 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
scene_buffer_set_texture(scene_buffer, NULL);
pixman_region32_fini(&scene_buffer->opaque_region);
wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
if (scene_buffer->release_merger) {
wlr_drm_syncobj_merger_unref(scene_buffer->release_merger);
}
assert(wl_list_empty(&scene_buffer->events.output_leave.listener_list));
assert(wl_list_empty(&scene_buffer->events.output_enter.listener_list));
@ -847,16 +851,23 @@ static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer,
}
}
static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer,
struct wlr_drm_syncobj_timeline *timeline, uint64_t point) {
wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
if (timeline != NULL) {
scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline);
scene_buffer->wait_point = point;
} else {
scene_buffer->wait_timeline = NULL;
scene_buffer->wait_point = 0;
static void scene_buffer_set_timelines(struct wlr_scene_buffer *scene_buffer,
struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point,
struct wlr_drm_syncobj_merger *release_merger) {
if (wait_timeline != NULL) {
wait_timeline = wlr_drm_syncobj_timeline_ref(wait_timeline);
}
wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
scene_buffer->wait_timeline = wait_timeline;
scene_buffer->wait_point = wait_point;
if (release_merger != NULL) {
release_merger = wlr_drm_syncobj_merger_ref(release_merger);
}
if (scene_buffer->release_merger != NULL) {
wlr_drm_syncobj_merger_unref(scene_buffer->release_merger);
}
scene_buffer->release_merger = release_merger;
}
struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
@ -938,8 +949,8 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf
scene_buffer_set_buffer(scene_buffer, buffer);
scene_buffer_set_texture(scene_buffer, NULL);
scene_buffer_set_wait_timeline(scene_buffer,
options->wait_timeline, options->wait_point);
scene_buffer_set_timelines(scene_buffer, options->wait_timeline, options->wait_point,
options->release_merger);
if (update) {
scene_node_update(&scene_buffer->node, NULL);
@ -1199,6 +1210,16 @@ static struct wlr_texture *scene_buffer_get_texture(
return texture;
}
static void scene_buffer_sample(struct wlr_scene_buffer *buffer,
struct wlr_scene_output_sample_event *event, struct wl_event_loop *loop) {
if (event->release_timeline && buffer->release_merger) {
wlr_drm_syncobj_merger_add(buffer->release_merger,
event->release_timeline, event->release_point, loop);
}
wl_signal_emit_mutable(&buffer->events.output_sample, event);
}
void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) {
*width = 0;
*height = 0;
@ -1553,8 +1574,10 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
struct wlr_scene_output_sample_event sample_event = {
.output = data->output,
.direct_scanout = false,
.release_timeline = data->output->in_timeline,
.release_point = data->output->in_point,
};
wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event);
scene_buffer_sample(scene_buffer, &sample_event, data->output->output->event_loop);
if (entry->highlight_transparent_region) {
wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
@ -1785,7 +1808,10 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
if (drm_fd >= 0 && output->backend->features.timeline &&
output->renderer != NULL && output->renderer->features.timeline) {
scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
if (scene_output->in_timeline == NULL) {
scene_output->out_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
if (scene_output->in_timeline == NULL || scene_output->out_timeline == NULL) {
wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
wlr_drm_syncobj_timeline_unref(scene_output->out_timeline);
return NULL;
}
}
@ -1840,7 +1866,14 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_list_remove(&scene_output->output_commit.link);
wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link);
wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
if (scene_output->in_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(scene_output->in_timeline, UINT64_MAX);
wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
}
if (scene_output->out_timeline != NULL) {
wlr_drm_syncobj_timeline_signal(scene_output->out_timeline, UINT64_MAX);
wlr_drm_syncobj_timeline_unref(scene_output->out_timeline);
}
wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
@ -2136,6 +2169,12 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
if (buffer->wait_timeline != NULL) {
wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point);
}
if (scene_output->out_timeline) {
scene_output->out_point++;
wlr_output_state_set_signal_timeline(&pending, scene_output->out_timeline, scene_output->out_point);
}
if (!wlr_output_test_state(scene_output->output, &pending)) {
wlr_output_state_finish(&pending);
return SCANOUT_CANDIDATE;
@ -2147,8 +2186,10 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
struct wlr_scene_output_sample_event sample_event = {
.output = scene_output,
.direct_scanout = true,
.release_timeline = data->output->out_timeline,
.release_point = data->output->out_point,
};
wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
scene_buffer_sample(buffer, &sample_event, scene_output->output->event_loop);
return SCANOUT_SUCCESS;
}
@ -2606,6 +2647,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
if (scene_output->in_timeline != NULL) {
wlr_output_state_set_wait_timeline(state, scene_output->in_timeline,
scene_output->in_point);
scene_output->out_point++;
wlr_output_state_set_signal_timeline(state, scene_output->out_timeline,
scene_output->out_point);
}
if (!render_gamma_lut) {

View file

@ -11,6 +11,7 @@
#include <xf86drm.h>
#include "config.h"
#include "linux-drm-syncobj-v1-protocol.h"
#include "render/drm_syncobj_merger.h"
#define LINUX_DRM_SYNCOBJ_V1_VERSION 1
@ -158,6 +159,9 @@ static void surface_synced_finish_state(void *_state) {
struct wlr_linux_drm_syncobj_surface_v1_state *state = _state;
wlr_drm_syncobj_timeline_unref(state->acquire_timeline);
wlr_drm_syncobj_timeline_unref(state->release_timeline);
if (state->release_merger != NULL) {
wlr_drm_syncobj_merger_unref(state->release_merger);
}
}
static void surface_synced_move_state(void *_dst, void *_src) {
@ -168,10 +172,31 @@ static void surface_synced_move_state(void *_dst, void *_src) {
*src = (struct wlr_linux_drm_syncobj_surface_v1_state){0};
}
static void surface_synced_commit(struct wlr_surface_synced *synced) {
struct wlr_linux_drm_syncobj_surface_v1 *surface =
wl_container_of(synced, surface, synced);
struct wlr_linux_drm_syncobj_surface_v1_state *state = &surface->current;
if (state->release_merger != NULL || state->release_timeline == NULL) {
return;
}
state->release_merger = wlr_drm_syncobj_merger_create(
state->release_timeline, state->release_point);
if (state->release_merger == NULL) {
return;
}
struct wl_display *display = wl_client_get_display(surface->resource->client);
wlr_drm_syncobj_merger_add(state->release_merger,
state->acquire_timeline, state->acquire_point, wl_display_get_event_loop(display));
}
static const struct wlr_surface_synced_impl surface_synced_impl = {
.state_size = sizeof(struct wlr_linux_drm_syncobj_surface_v1_state),
.finish_state = surface_synced_finish_state,
.move_state = surface_synced_move_state,
.commit = surface_synced_commit,
};
static void manager_handle_destroy(struct wl_client *client,
@ -422,6 +447,11 @@ struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create
struct wl_display *display, uint32_t version, int drm_fd) {
assert(version <= LINUX_DRM_SYNCOBJ_V1_VERSION);
if (!HAVE_LINUX_SYNC_FILE) {
wlr_log(WLR_INFO, "Linux sync_file unavailable, disabling linux-drm-syncobj-v1");
return NULL;
}
if (!check_syncobj_eventfd(drm_fd)) {
wlr_log(WLR_INFO, "DRM syncobj eventfd unavailable, disabling linux-drm-syncobj-v1");
return NULL;
@ -475,11 +505,7 @@ struct release_signaller {
static void release_signaller_handle_buffer_release(struct wl_listener *listener, void *data) {
struct release_signaller *signaller = wl_container_of(listener, signaller, buffer_release);
if (drmSyncobjTimelineSignal(signaller->timeline->drm_fd, &signaller->timeline->handle,
&signaller->point, 1) != 0) {
wlr_log(WLR_ERROR, "drmSyncobjTimelineSignal() failed");
}
wlr_drm_syncobj_timeline_signal(signaller->timeline, signaller->point);
wlr_drm_syncobj_timeline_unref(signaller->timeline);
wl_list_remove(&signaller->buffer_release.link);
free(signaller);