compositor: Support linux-drm-syncobj-v1

Add support for the linux-drm-syncobj-v1 protocol for explicit
synchronization.
This protocol supersedes linux-explicit-synchronization-unstable-v1
which has been supported in Weston for a long time but was not adopted
in the rest of the ecosystem.

For the acquire point, the implementation uses the DRM syncobj eventfd
mechanism and the wait is entirely in CPU.
The acquire fence cannot be simply imported into GPU rendering jobs, as
this protocol allows for client commits containing a timeline point for
which a fence has not even been materialized yet. Additionally, adding a
client's fence as a dependency for the compositor's GPU rendering job
has a risk of causing the compositor to miss a refresh cycle due to a
slow client.
Therefore, the eventfd method is used which ensures that the fence has
materialized in the kernel as well as efficiently waiting for it to be
signalled. This builds upon the deferred content update framework used
by the fifo and commit-timing protocols.

For the release point, a similar method as already existed for the
linux-explicit-synchronization-unstable-v1 protocol is followed.
The timeline release point is not immediately signalled after a GPU
rendering job, as the buffer may still be referred to by the compositor.
Therefore, the release point signalling is still mainly driven by the
compositor's buffer refcount, so effectively by CPU. A GPU fence is
still transferred to the timeline point at buffer's release when the
buffer had been used for the compositor's GPU rendering (and most likely
already signalled by the GPU by that time) just to ensure that any
remaining compositor GPU job has finished before the client reuses the
buffer.

Signed-off-by: Erico Nunes <nunes.erico@gmail.com>
This commit is contained in:
Erico Nunes 2026-04-24 12:40:32 +02:00
parent e0c5b8a26b
commit 37faecc770
21 changed files with 2606 additions and 71 deletions

View file

@ -111,6 +111,28 @@ simple_clients = [
'deps': [ 'egl', 'glesv2', 'gbm' ],
'options': [ 'renderer-gl' ]
},
{
'name': 'syncobj-egl',
'sources': [
'simple-syncobj-egl.c',
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
linux_drm_syncobj_v1_client_protocol_h,
linux_drm_syncobj_v1_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
weston_direct_display_client_protocol_h,
weston_direct_display_protocol_c,
],
'dep_objs': [
dep_wayland_client,
dep_libdrm,
dep_libm,
dep_matrix_c,
],
'deps': [ 'egl', 'glesv2', 'gbm' ],
'options': [ 'renderer-gl' ]
},
{
'name': 'dmabuf-v4l',
'sources': [

1605
clients/simple-syncobj-egl.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1799,6 +1799,25 @@ struct weston_view {
struct weston_log_pacer subsurface_parent_log_pacer;
};
struct weston_syncobj_release {
uint32_t ref_count;
uint32_t release_timeline_handle;
uint64_t release_point;
int fence_fd;
struct weston_renderer *renderer;
};
struct weston_syncobj_release_reference {
struct weston_syncobj_release *syncobj_release;
};
struct weston_syncobj_state {
uint64_t acquire_point;
uint32_t acquire_timeline_handle;
bool acquire_point_valid;
struct weston_syncobj_acquire_entry *acquire_entry;
};
enum weston_surface_status {
/** nothing has changed */
WESTON_SURFACE_CLEAN = 0,
@ -1876,6 +1895,10 @@ struct weston_surface_state {
bool fifo_barrier;
bool fifo_wait;
/* linux-drm-syncobj-v1 */
struct weston_syncobj_state syncobj;
struct weston_syncobj_release_reference syncobj_release_ref;
/* commit_timing_v1 */
struct weston_commit_timing_target update_time;
};
@ -2076,6 +2099,11 @@ struct weston_surface {
bool fifo_barrier; /* Cleared after display */
struct wl_list fifo_barrier_link; /* output::fifo_barrier_surfaces */
/* linux-drm-syncobj-v1 */
struct weston_syncobj_surface *syncobj_surface;
struct weston_syncobj_state syncobj;
struct weston_syncobj_release_reference syncobj_release_ref;
/** commit_timing_v1 */
struct weston_commit_timer *commit_timer;
};

View file

@ -409,6 +409,7 @@ struct drm_plane_state {
struct {
struct weston_buffer_reference buffer;
struct weston_buffer_release_reference release;
struct weston_syncobj_release_reference syncobj_release;
} fb_ref;
struct weston_paint_node *paint_node; /**< maintained for drm_assign_planes only */

View file

@ -70,6 +70,7 @@
#include "linux-dmabuf.h"
#include "linux-dmabuf-unstable-v1-server-protocol.h"
#include "linux-explicit-synchronization.h"
#include "syncobj.h"
static const char default_seat[] = "seat0";
@ -4822,6 +4823,8 @@ drm_backend_create(struct weston_compositor *compositor,
if (linux_explicit_synchronization_setup(compositor) < 0)
weston_log("Error: initializing explicit "
" synchronization support failed.\n");
if (syncobj_setup(compositor) != 0)
weston_log("Error: initializing drm syncobj support failed.\n");
}
if (device->atomic_modeset)

View file

@ -33,6 +33,7 @@
#include <xf86drmMode.h>
#include "drm-internal.h"
#include "syncobj.h"
#include "shared/weston-assert.h"
#include "shared/weston-drm-fourcc.h"
#include "shared/xalloc.h"
@ -112,6 +113,7 @@ drm_plane_state_free(struct drm_plane_state *state, bool force)
weston_buffer_reference(&state->fb_ref.buffer, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&state->fb_ref.release, NULL);
weston_syncobj_release_reference(&state->fb_ref.syncobj_release, NULL);
free(state);
}
}
@ -172,9 +174,12 @@ drm_plane_state_duplicate(struct drm_output_state *state_output,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&dst->fb_ref.release,
src->fb_ref.release.buffer_release);
weston_syncobj_release_reference(&dst->fb_ref.syncobj_release,
src->fb_ref.syncobj_release.syncobj_release);
} else {
assert(!src->fb_ref.buffer.buffer);
assert(!src->fb_ref.release.buffer_release);
assert(!src->fb_ref.syncobj_release.syncobj_release);
}
dst->output_state = state_output;
dst->complete = false;

View file

@ -37,6 +37,7 @@
#include <libweston/pixel-formats.h>
#include "drm-internal.h"
#include "syncobj.h"
#include "color.h"
#include "color-representation.h"
@ -217,6 +218,8 @@ drm_output_try_paint_node_on_plane(struct drm_plane_handle *handle,
BUFFER_MAY_BE_ACCESSED);
weston_buffer_release_reference(&state->fb_ref.release,
surface->buffer_release_ref.buffer_release);
weston_syncobj_release_reference(&state->fb_ref.syncobj_release,
surface->syncobj_release_ref.syncobj_release);
return state;
@ -1138,6 +1141,8 @@ drm_output_propose_state_try_reuse(struct weston_output *output_base,
BUFFER_MAY_BE_ACCESSED);
weston_buffer_release_reference(&pstate->fb_ref.release,
pnode->surface->buffer_release_ref.buffer_release);
weston_syncobj_release_reference(&pstate->fb_ref.syncobj_release,
pnode->surface->syncobj_release_ref.syncobj_release);
}
if (device->reused_state_failures > DRM_MAX_REUSE_FAILURES) {

View file

@ -39,6 +39,7 @@
#include <libweston/backend-headless.h>
#include "shared/helpers.h"
#include "linux-explicit-synchronization.h"
#include "syncobj.h"
#include "pixel-formats.h"
#include "pixman-renderer.h"
#include "renderer-gl/gl-renderer.h"
@ -757,6 +758,8 @@ headless_backend_create(struct weston_compositor *compositor,
* testing. */
if (linux_explicit_synchronization_setup(compositor) < 0)
goto err_input;
if (syncobj_setup(compositor) != 0)
weston_log("Error: initializing drm syncobj support failed.\n");
}
ret = weston_plugin_api_register(compositor,

View file

@ -64,6 +64,7 @@
#include "linux-dmabuf.h"
#include <libweston/pixel-formats.h>
#include <libweston/windowed-output-api.h>
#include "syncobj.h"
#define WINDOW_TITLE "Weston Compositor"
@ -2907,6 +2908,9 @@ wayland_backend_create(struct weston_compositor *compositor,
wl_event_source_check(b->parent.wl_source);
if (syncobj_setup(compositor) != 0)
weston_log("Error: initializing drm syncobj support failed.\n");
return b;
err_renderer:
compositor->renderer->destroy(compositor);

View file

@ -65,6 +65,7 @@
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf.h"
#include "linux-explicit-synchronization.h"
#include "syncobj.h"
#include <libweston/pixel-formats.h>
#include <libweston/windowed-output-api.h>
@ -2060,6 +2061,8 @@ x11_backend_create(struct weston_compositor *compositor,
if (linux_explicit_synchronization_setup(compositor) < 0)
weston_log("Error: initializing explicit "
" synchronization support failed.\n");
if (syncobj_setup(compositor) != 0)
weston_log("Error: initializing drm syncobj support failed.\n");
}
ret = weston_plugin_api_register(compositor,

View file

@ -88,6 +88,7 @@
#include "id-number-allocator.h"
#include "output-capture.h"
#include "pixman-renderer.h"
#include "syncobj.h"
#include "renderer-gl/gl-renderer.h"
#include "weston-trace.h"
#include "renderer-vulkan/vulkan-renderer.h"
@ -2773,6 +2774,7 @@ weston_surface_unref(struct weston_surface *surface)
weston_buffer_reference(&surface->buffer_ref, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&surface->buffer_release_ref, NULL);
weston_syncobj_release_reference(&surface->syncobj_release_ref, NULL);
pixman_region32_fini(&surface->damage);
pixman_region32_fini(&surface->opaque);
@ -3440,6 +3442,8 @@ output_accumulate_damage(struct weston_output *output)
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(
&pnode->surface->buffer_release_ref, NULL);
weston_syncobj_release_reference(
&pnode->surface->syncobj_release_ref, NULL);
}
}
}
@ -5188,6 +5192,9 @@ surface_commit(struct wl_client *client, struct wl_resource *resource)
return;
}
if (!weston_surface_check_pending_syncobj_valid(surface))
return;
if (!weston_surface_check_pending_color_representation_valid(surface))
return;

View file

@ -201,6 +201,24 @@ struct weston_renderer {
uint32_t format,
const uint64_t *modifiers, unsigned int count);
int (*syncobj_import_timeline)(struct weston_renderer *renderer,
int timeline_fd,
uint32_t *timeline_handle);
int (*syncobj_timeline_set_eventfd)(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point,
int ev_fd);
int (*syncobj_timeline_import_syncobj)(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point,
int sync_fd);
int (*syncobj_timeline_signal)(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point);
enum weston_renderer_type type;
const struct gl_renderer_interface *gl;
const struct vulkan_renderer_interface *vulkan;

View file

@ -40,6 +40,7 @@ srcs_libweston = [
'pixman-renderer.c',
'plugin-registry.c',
'surface-state.c',
'syncobj.c',
'timeline.c',
'touch-calibration.c',
'weston-log-wayland.c',
@ -57,6 +58,8 @@ srcs_libweston = [
fifo_v1_server_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
linux_dmabuf_unstable_v1_server_protocol_h,
linux_drm_syncobj_v1_protocol_c,
linux_drm_syncobj_v1_server_protocol_h,
linux_explicit_synchronization_unstable_v1_protocol_c,
linux_explicit_synchronization_unstable_v1_server_protocol_h,
input_method_unstable_v1_protocol_c,

View file

@ -439,6 +439,7 @@ struct gl_renderer {
EGLDeviceEXT egl_device;
const char *drm_device;
int drm_fd;
struct weston_drm_format_array supported_dmabuf_formats;

View file

@ -43,6 +43,8 @@
#include <gbm.h>
#endif
#include <xf86drm.h>
#include <libweston/linalg-4.h>
#include "linux-sync-file.h"
@ -58,6 +60,7 @@
#include "linux-explicit-synchronization.h"
#include "output-capture.h"
#include "pixel-formats.h"
#include "syncobj.h"
#include "shared/fd-util.h"
#include "shared/helpers.h"
@ -313,6 +316,7 @@ struct gl_surface_state {
* rather than either buffer or surface state */
struct weston_buffer_reference buffer_ref;
struct weston_buffer_release_reference buffer_release_ref;
struct weston_syncobj_release_reference syncobj_release_ref;
/* Whether this surface was used in the current output repaint.
Used only in the context of a gl_renderer_repaint_output call. */
@ -2746,48 +2750,31 @@ update_buffer_release_fences(struct weston_compositor *compositor,
continue;
gs = get_surface_state(pnode->surface);
buffer_release = gs->buffer_release_ref.buffer_release;
if (!gs->used_in_output_repaint || !buffer_release)
if (!gs->used_in_output_repaint)
continue;
fence_fd = gl_renderer_create_fence_fd(output);
/* If we have a buffer_release then it means we support fences,
* and we should be able to create the release fence. If we
* can't, something has gone horribly wrong, so disconnect the
* client.
*/
if (fence_fd == -1) {
linux_explicit_synchronization_send_server_error(
buffer_release->resource,
"Failed to create release fence");
fd_clear(&buffer_release->fence_fd);
if (fence_fd == -1)
continue;
/* The fence is not imported to the syncobj timeline here,
* because that would cause the release fence to be signalled
* when this repaint is done with it.
* The weston buffer may still be used by the compositor later,
* so defer importing this to the syncobj timeline at the
* compositor's buffer release */
if (gs->syncobj_release_ref.syncobj_release) {
int fd = (gs->buffer_release_ref.buffer_release) ?
dup(fence_fd) : fence_fd;
fd_update(&gs->syncobj_release_ref.syncobj_release->fence_fd, fd);
}
/* At the moment it is safe to just replace the fence_fd,
* discarding the previous one:
*
* 1. If the previous fence fd represents a sync fence from
* a previous repaint cycle, that fence fd is now not
* sufficient to provide the release guarantee and should
* be replaced.
*
* 2. If the fence fd represents a sync fence from another
* output in the same repaint cycle, it's fine to replace
* it since we are rendering to all outputs using the same
* EGL context, so a fence issued for a later output rendering
* is guaranteed to signal after fences for previous output
* renderings.
*
* Note that the above is only valid if the buffer_release
* fences only originate from the GL renderer, which guarantees
* a total order of operations and fences. If we introduce
* fences from other sources (e.g., plane out-fences), we will
* need to merge fences instead.
*/
fd_update(&buffer_release->fence_fd, fence_fd);
buffer_release = gs->buffer_release_ref.buffer_release;
if (buffer_release)
fd_update(&buffer_release->fence_fd, fence_fd);
else if (!gs->syncobj_release_ref.syncobj_release)
close(fence_fd);
}
}
@ -3411,6 +3398,7 @@ done:
weston_buffer_reference(&gs->buffer_ref, buffer,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&gs->buffer_release_ref, NULL);
weston_syncobj_release_reference(&gs->syncobj_release_ref, NULL);
}
static void
@ -4394,6 +4382,8 @@ success:
BUFFER_MAY_BE_ACCESSED);
weston_buffer_release_reference(&gs->buffer_release_ref,
es->buffer_release_ref.buffer_release);
weston_syncobj_release_reference(&gs->syncobj_release_ref,
es->syncobj_release_ref.syncobj_release);
return;
out:
@ -4401,6 +4391,7 @@ out:
weston_buffer_reference(&gs->buffer_ref, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&gs->buffer_release_ref, NULL);
weston_syncobj_release_reference(&gs->syncobj_release_ref, NULL);
}
static void
@ -4553,6 +4544,7 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr)
weston_buffer_reference(&gs->buffer_ref, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&gs->buffer_release_ref, NULL);
weston_syncobj_release_reference(&gs->syncobj_release_ref, NULL);
free(gs);
}
@ -5116,6 +5108,87 @@ gl_renderer_allocator_create(struct gl_renderer *gr,
return allocator;
}
static int
gl_renderer_syncobj_import_timeline(struct weston_renderer *renderer,
int timeline_fd,
uint32_t *timeline_handle)
{
struct gl_renderer *gr =
container_of(renderer, struct gl_renderer, base);
uint32_t handle;
if (gr->drm_fd < 0)
return -1;
if (drmSyncobjFDToHandle(gr->drm_fd, timeline_fd, &handle))
return -1;
*timeline_handle = handle;
return 0;
}
static int
gl_renderer_syncobj_timeline_import_syncobj(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point,
int sync_fd)
{
struct gl_renderer *gr =
container_of(renderer, struct gl_renderer, base);
uint32_t tmp_syncobj;
if (gr->drm_fd < 0)
return -1;
if (drmSyncobjCreate(gr->drm_fd, 0, &tmp_syncobj) != 0)
return -1;
if (drmSyncobjImportSyncFile(gr->drm_fd, tmp_syncobj, sync_fd) != 0) {
drmSyncobjDestroy(gr->drm_fd, tmp_syncobj);
return -1;
}
if (drmSyncobjTransfer(gr->drm_fd, timeline_handle, point,
tmp_syncobj, 0, 0) != 0) {
drmSyncobjDestroy(gr->drm_fd, tmp_syncobj);
return -1;
}
drmSyncobjDestroy(gr->drm_fd, tmp_syncobj);
return 0;
}
static int
gl_renderer_syncobj_timeline_signal(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point)
{
struct gl_renderer *gr =
container_of(renderer, struct gl_renderer, base);
if (gr->drm_fd < 0)
return -1;
return drmSyncobjTimelineSignal(gr->drm_fd, &timeline_handle,
&point, 1);
}
static int
gl_renderer_syncobj_timeline_set_eventfd(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point,
int ev_fd)
{
struct gl_renderer *gr =
container_of(renderer, struct gl_renderer, base);
if (gr->drm_fd < 0)
return -1;
return drmSyncobjEventfd(gr->drm_fd, timeline_handle, point,
ev_fd, 0);
}
static void
gl_renderer_destroy(struct weston_compositor *ec)
{
@ -5164,6 +5237,10 @@ gl_renderer_destroy(struct weston_compositor *ec)
weston_log_scope_destroy(gr->shader_scope);
weston_log_scope_destroy(gr->extensions_scope);
weston_log_scope_destroy(gr->paint_node_scope);
if (gr->drm_fd >= 0)
close(gr->drm_fd);
free(gr);
ec->renderer = NULL;
}
@ -5238,10 +5315,25 @@ gl_renderer_display_create(struct weston_compositor *ec,
gr->base.buffer_init = gl_renderer_buffer_init;
gr->base.output_set_border = gl_renderer_output_set_border;
gr->base.type = WESTON_RENDERER_GL;
gr->drm_fd = -1;
if (gl_renderer_setup_egl_display(gr, options->egl_native_display) < 0)
goto fail;
if (gr->drm_device) {
gr->drm_fd = open(gr->drm_device, O_RDWR | O_CLOEXEC);
if (gr->drm_fd >= 0) {
gr->base.syncobj_import_timeline =
gl_renderer_syncobj_import_timeline;
gr->base.syncobj_timeline_set_eventfd =
gl_renderer_syncobj_timeline_set_eventfd;
gr->base.syncobj_timeline_import_syncobj =
gl_renderer_syncobj_timeline_import_syncobj;
gr->base.syncobj_timeline_signal =
gl_renderer_syncobj_timeline_signal;
}
}
gr->allocator = gl_renderer_allocator_create(gr, options);
if (!gr->allocator)
weston_log("failed to initialize allocator\n");

View file

@ -34,7 +34,7 @@ deps_renderer_gl = [
dep_libm,
dep_pixman,
dep_libweston_private,
dep_libdrm_headers,
dep_libdrm,
dep_vertex_clipping
]

View file

@ -52,6 +52,7 @@
#include "linux-explicit-synchronization.h"
#include "output-capture.h"
#include "pixel-formats.h"
#include "syncobj.h"
#include "shared/fd-util.h"
#include "shared/helpers.h"
@ -245,6 +246,7 @@ struct vulkan_surface_state {
* rather than either buffer or surface state */
struct weston_buffer_reference buffer_ref;
struct weston_buffer_release_reference buffer_release_ref;
struct weston_syncobj_release_reference syncobj_release_ref;
/* Whether this surface was used in the current output repaint.
Used only in the context of a vulkan_renderer_repaint_output call. */
@ -527,6 +529,7 @@ surface_state_destroy(struct vulkan_surface_state *vs, struct vulkan_renderer *v
weston_buffer_reference(&vs->buffer_ref, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&vs->buffer_release_ref, NULL);
weston_syncobj_release_reference(&vs->syncobj_release_ref, NULL);
free(vs);
}
@ -2015,48 +2018,31 @@ update_buffer_release_fences(struct weston_compositor *compositor,
continue;
vs = get_surface_state(pnode->surface);
buffer_release = vs->buffer_release_ref.buffer_release;
if (!vs->used_in_output_repaint || !buffer_release)
if (!vs->used_in_output_repaint)
continue;
fence_fd = vulkan_renderer_create_fence_fd(output);
/* If we have a buffer_release then it means we support fences,
* and we should be able to create the release fence. If we
* can't, something has gone horribly wrong, so disconnect the
* client.
*/
if (fence_fd == -1) {
linux_explicit_synchronization_send_server_error(
buffer_release->resource,
"Failed to create release fence");
fd_clear(&buffer_release->fence_fd);
if (fence_fd == -1)
continue;
/* The fence is not imported to the syncobj timeline here,
* because that would cause the release fence to be signalled
* when this repaint is done with it.
* The weston buffer may still be used by the compositor later,
* so defer importing this to the syncobj timeline at the
* compositor's buffer release */
if (vs->syncobj_release_ref.syncobj_release) {
int fd = (vs->buffer_release_ref.buffer_release) ?
dup(fence_fd) : fence_fd;
fd_update(&vs->syncobj_release_ref.syncobj_release->fence_fd, fd);
}
/* At the moment it is safe to just replace the fence_fd,
* discarding the previous one:
*
* 1. If the previous fence fd represents a sync fence from
* a previous repaint cycle, that fence fd is now not
* sufficient to provide the release guarantee and should
* be replaced.
*
* 2. If the fence fd represents a sync fence from another
* output in the same repaint cycle, it's fine to replace
* it since we are rendering to all outputs using the same
* EGL context, so a fence issued for a later output rendering
* is guaranteed to signal after fences for previous output
* renderings.
*
* Note that the above is only valid if the buffer_release
* fences only originate from the GL renderer, which guarantees
* a total order of operations and fences. If we introduce
* fences from other sources (e.g., plane out-fences), we will
* need to merge fences instead.
*/
fd_update(&buffer_release->fence_fd, fence_fd);
buffer_release = vs->buffer_release_ref.buffer_release;
if (buffer_release)
fd_update(&buffer_release->fence_fd, fence_fd);
else if (!vs->syncobj_release_ref.syncobj_release)
close(fence_fd);
}
}
@ -2921,6 +2907,7 @@ done:
weston_buffer_reference(&vs->buffer_ref, buffer,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&vs->buffer_release_ref, NULL);
weston_syncobj_release_reference(&vs->syncobj_release_ref, NULL);
}
static void
@ -3162,6 +3149,8 @@ success:
BUFFER_MAY_BE_ACCESSED);
weston_buffer_release_reference(&vs->buffer_release_ref,
es->buffer_release_ref.buffer_release);
weston_syncobj_release_reference(&vs->syncobj_release_ref,
es->syncobj_release_ref.syncobj_release);
return;
out:
@ -3169,6 +3158,7 @@ out:
weston_buffer_reference(&vs->buffer_ref, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&vs->buffer_release_ref, NULL);
weston_syncobj_release_reference(&vs->syncobj_release_ref, NULL);
}
static void
@ -3714,6 +3704,87 @@ vulkan_renderer_output_surfaceless_create(struct weston_output *output,
return 0;
}
static int
vulkan_renderer_syncobj_import_timeline(struct weston_renderer *renderer,
int timeline_fd,
uint32_t *timeline_handle)
{
struct vulkan_renderer *vr =
container_of(renderer, struct vulkan_renderer, base);
uint32_t handle;
if (vr->drm_fd < 0)
return -1;
if (drmSyncobjFDToHandle(vr->drm_fd, timeline_fd, &handle))
return -1;
*timeline_handle = handle;
return 0;
}
static int
vulkan_renderer_syncobj_timeline_import_syncobj(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point,
int sync_fd)
{
struct vulkan_renderer *vr =
container_of(renderer, struct vulkan_renderer, base);
uint32_t tmp_syncobj;
if (vr->drm_fd < 0)
return -1;
if (drmSyncobjCreate(vr->drm_fd, 0, &tmp_syncobj) != 0)
return -1;
if (drmSyncobjImportSyncFile(vr->drm_fd, tmp_syncobj, sync_fd) != 0) {
drmSyncobjDestroy(vr->drm_fd, tmp_syncobj);
return -1;
}
if (drmSyncobjTransfer(vr->drm_fd, timeline_handle, point,
tmp_syncobj, 0, 0) != 0) {
drmSyncobjDestroy(vr->drm_fd, tmp_syncobj);
return -1;
}
drmSyncobjDestroy(vr->drm_fd, tmp_syncobj);
return 0;
}
static int
vulkan_renderer_syncobj_timeline_signal(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point)
{
struct vulkan_renderer *vr =
container_of(renderer, struct vulkan_renderer, base);
if (vr->drm_fd < 0)
return -1;
return drmSyncobjTimelineSignal(vr->drm_fd, &timeline_handle,
&point, 1);
}
static int
vulkan_renderer_syncobj_timeline_set_eventfd(struct weston_renderer *renderer,
uint32_t timeline_handle,
uint64_t point,
int ev_fd)
{
struct vulkan_renderer *vr =
container_of(renderer, struct vulkan_renderer, base);
if (vr->drm_fd < 0)
return -1;
return drmSyncobjEventfd(vr->drm_fd, timeline_handle, point,
ev_fd, 0);
}
static void
vulkan_renderer_destroy(struct weston_compositor *ec)
{
@ -4429,6 +4500,17 @@ vulkan_renderer_display_create(struct weston_compositor *ec,
if (vulkan_device_has(vr, EXTENSION_EXT_PHYSICAL_DEVICE_DRM))
vr->drm_fd = open_drm_device_node(vr);
if (vr->drm_fd >= 0) {
vr->base.syncobj_import_timeline =
vulkan_renderer_syncobj_import_timeline;
vr->base.syncobj_timeline_set_eventfd =
vulkan_renderer_syncobj_timeline_set_eventfd;
vr->base.syncobj_timeline_import_syncobj =
vulkan_renderer_syncobj_timeline_import_syncobj;
vr->base.syncobj_timeline_signal =
vulkan_renderer_syncobj_timeline_signal;
}
ec->capabilities |= WESTON_CAP_ROTATION_ANY;
ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;

View file

@ -31,6 +31,7 @@
#include <libweston/libweston.h>
#include <libweston/commit-timing.h>
#include <libweston/fifo.h>
#include <libweston/syncobj.h>
#include "libweston-internal.h"
#include "backend.h"
@ -162,6 +163,9 @@ weston_surface_state_init(struct weston_surface *surface,
state->fifo_barrier = false;
state->fifo_wait = false;
state->syncobj.acquire_point_valid = false;
state->syncobj.acquire_entry = NULL;
state->update_time.valid = false;
state->update_time.satisfied = false;
state->update_time.time.tv_sec = 0;
@ -189,10 +193,13 @@ weston_surface_state_fini(struct weston_surface_state *state)
fd_clear(&state->acquire_fence_fd);
weston_buffer_release_reference(&state->buffer_release_ref, NULL);
weston_syncobj_release_reference(&state->syncobj_release_ref, NULL);
weston_color_profile_unref(state->color_profile);
state->color_profile = NULL;
state->render_intent = NULL;
weston_syncobj_state_fini(&state->syncobj);
}
static enum weston_surface_status
@ -523,6 +530,9 @@ weston_surface_apply_state(struct weston_surface *surface,
weston_fifo_surface_set_barrier(surface);
state->fifo_barrier = false;
weston_syncobj_release_move(&surface->syncobj_release_ref,
&state->syncobj_release_ref);
if (weston_surface_status_invalidates_visibility(status))
surface->output_visibility_dirty_mask |= surface->output_mask;
@ -701,6 +711,10 @@ weston_surface_state_merge_from(struct weston_surface_state *dst,
dst->fifo_wait = src->fifo_wait;
src->fifo_wait = false;
weston_syncobj_state_move(&dst->syncobj, &src->syncobj);
weston_syncobj_release_move(&dst->syncobj_release_ref,
&src->syncobj_release_ref);
dst->update_time = src->update_time;
weston_commit_timing_clear_target(&src->update_time);
@ -835,6 +849,9 @@ weston_surface_state_ready(struct weston_surface *surface,
if (!weston_commit_timing_surface_state_ready(surface, state))
return false;
if (!weston_syncobj_surface_state_ready(surface, state))
return false;
return true;
}
@ -857,6 +874,11 @@ weston_surface_commit(struct weston_surface *surface)
state = &sub->cached;
}
/* Set up the eventfd for the acquire fence checking readiness,
* so that the acquire point will be valid */
if (state->syncobj.acquire_point_valid)
weston_syncobj_surface_set_acquire_point(surface, state);
/* Check if this surface is a member of a transaction list already.
* If it is, we're not ready to apply this state, so we'll have
* to make a new transaction and wait until we are.

571
libweston/syncobj.c Normal file
View file

@ -0,0 +1,571 @@
/*
* Copyright 2026 Erico Nunes
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <libweston/libweston.h>
#include "libweston-internal.h"
#include "shared/fd-util.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "syncobj.h"
struct weston_syncobj_timeline {
struct wl_listener timeline_destroy_listener;
int fd;
uint32_t handle;
struct wl_signal destroy_signal; /* callback argument: this timeline */
};
struct weston_syncobj_acquire_entry {
struct wl_list link; /* weston_syncobj_surface::acquire_entries */
int fd;
bool signalled;
struct weston_syncobj_state *owner;
struct weston_surface *surface;
struct wl_listener surface_destroy_listener;
struct wl_event_source *event_source;
};
struct weston_syncobj_surface {
struct weston_surface *surface;
struct wl_resource *resource;
struct wl_listener surface_destroy_listener;
struct wl_list acquire_entries;
};
static void
syncobj_timeline_destructor(struct wl_resource *resource)
{
struct weston_syncobj_timeline *timeline = wl_resource_get_user_data(resource);
wl_list_remove(&timeline->timeline_destroy_listener.link);
free(timeline);
}
static void
syncobj_timeline_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct wp_linux_drm_syncobj_timeline_v1_interface weston_syncobj_timeline_interface = {
.destroy = syncobj_timeline_destroy,
};
static void
syncobj_timeline_destroy_cb(struct wl_listener *listener, void *data)
{
struct weston_syncobj_timeline *timeline =
container_of(listener,
struct weston_syncobj_timeline, timeline_destroy_listener);
timeline->fd = -1;
timeline->handle = 0;
}
static void
syncobj_manager_import_timeline(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
int32_t fd)
{
struct weston_syncobj_timeline *timeline;
uint32_t handle;
struct wl_resource *res;
struct weston_compositor *compositor = wl_resource_get_user_data(resource);
res = wl_resource_create(client, &wp_linux_drm_syncobj_timeline_v1_interface,
wl_resource_get_version(resource), id);
timeline = xzalloc(sizeof *timeline);
timeline->fd = fd;
timeline->timeline_destroy_listener.notify = syncobj_timeline_destroy_cb;
wl_signal_init(&timeline->destroy_signal);
wl_signal_add(&timeline->destroy_signal, &timeline->timeline_destroy_listener);
wl_resource_set_implementation(res, &weston_syncobj_timeline_interface, timeline,
syncobj_timeline_destructor);
struct weston_renderer *renderer = compositor->renderer;
assert(renderer && renderer->syncobj_import_timeline);
if (renderer->syncobj_import_timeline(renderer, fd, &handle) != 0)
return;
timeline->handle = handle;
}
static void
acquire_entry_surface_destroy_cb(struct wl_listener *listener, void *data)
{
struct weston_syncobj_acquire_entry *entry =
container_of(listener,
struct weston_syncobj_acquire_entry,
surface_destroy_listener);
entry->surface = NULL;
}
static void
weston_syncobj_surface_cancel_entries(struct weston_syncobj_surface *syncobj_surface)
{
struct weston_syncobj_acquire_entry *entry, *tmp;
wl_list_for_each_safe(entry, tmp, &syncobj_surface->acquire_entries, link) {
wl_event_source_remove(entry->event_source);
close(entry->fd);
wl_list_remove(&entry->link);
if (entry->surface)
wl_list_remove(&entry->surface_destroy_listener.link);
if (entry->owner)
entry->owner->acquire_entry = NULL;
free(entry);
}
}
static void
syncobj_surface_destructor(struct wl_resource *resource)
{
struct weston_syncobj_surface *syncobj_surface = wl_resource_get_user_data(resource);
weston_syncobj_surface_cancel_entries(syncobj_surface);
if (syncobj_surface->surface) {
wl_list_remove(&syncobj_surface->surface_destroy_listener.link);
syncobj_surface->surface->syncobj_surface = NULL;
}
free(syncobj_surface);
}
static void
syncobj_surface_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
syncobj_surface_set_acquire_point(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *timeline,
uint32_t point_hi,
uint32_t point_lo)
{
struct weston_syncobj_surface *syncobj_surface = wl_resource_get_user_data(resource);
struct weston_syncobj_timeline *syncobj_timeline = wl_resource_get_user_data(timeline);
struct weston_surface *surface = syncobj_surface->surface;
uint64_t point;
if (!surface) {
wl_resource_post_error(resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE,
"surface no longer exists");
return;
}
point = ((uint64_t)point_hi << 32ul) | (uint64_t)(point_lo & 0xffffffff);
surface->pending.syncobj.acquire_point_valid = true;
surface->pending.syncobj.acquire_timeline_handle = syncobj_timeline->handle;
surface->pending.syncobj.acquire_point = point;
}
static void
syncobj_surface_set_release_point(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *timeline,
uint32_t point_hi,
uint32_t point_lo)
{
struct weston_syncobj_surface *syncobj_surface = wl_resource_get_user_data(resource);
struct weston_syncobj_timeline *syncobj_timeline = wl_resource_get_user_data(timeline);
struct weston_surface *surface = syncobj_surface->surface;
struct weston_syncobj_release *release;
uint64_t point;
if (!surface) {
wl_resource_post_error(resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE,
"surface no longer exists");
return;
}
point = ((uint64_t)point_hi << 32ul) | (uint64_t)(point_lo & 0xffffffff);
release = xzalloc(sizeof *release);
release->ref_count = 0;
release->release_timeline_handle = syncobj_timeline->handle;
release->release_point = point;
release->fence_fd = -1;
release->renderer = surface->compositor->renderer;
weston_syncobj_release_reference(&surface->pending.syncobj_release_ref, release);
}
static const struct wp_linux_drm_syncobj_surface_v1_interface weston_syncobj_surface_interface = {
.set_acquire_point = syncobj_surface_set_acquire_point,
.set_release_point = syncobj_surface_set_release_point,
.destroy = syncobj_surface_destroy,
};
static void
syncobj_surface_destroy_cb(struct wl_listener *listener, void *data)
{
struct weston_syncobj_surface *syncobj_surface =
container_of(listener,
struct weston_syncobj_surface, surface_destroy_listener);
weston_syncobj_surface_cancel_entries(syncobj_surface);
syncobj_surface->surface = NULL;
}
static void
syncobj_manager_get_surface(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource)
{
struct weston_syncobj_surface *syncobj_surface;
struct weston_surface *surface = wl_resource_get_user_data(surface_resource);
struct wl_resource *res;
if (surface->syncobj_surface) {
wl_resource_post_error(resource,
WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS,
"the surface already has a synchronization object associated");
return;
}
res = wl_resource_create(client, &wp_linux_drm_syncobj_surface_v1_interface,
wl_resource_get_version(resource), id);
syncobj_surface = xzalloc(sizeof *syncobj_surface);
syncobj_surface->surface = surface;
syncobj_surface->resource = res;
wl_list_init(&syncobj_surface->acquire_entries);
syncobj_surface->surface_destroy_listener.notify = syncobj_surface_destroy_cb;
wl_signal_add(&surface->destroy_signal, &syncobj_surface->surface_destroy_listener);
wl_resource_set_implementation(res, &weston_syncobj_surface_interface, syncobj_surface,
syncobj_surface_destructor);
surface->syncobj_surface = syncobj_surface;
}
static void
syncobj_manager_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct wp_linux_drm_syncobj_manager_v1_interface syncobj_manager_interface_v1 = {
.destroy = syncobj_manager_destroy,
.get_surface = syncobj_manager_get_surface,
.import_timeline = syncobj_manager_import_timeline,
};
static void
bind_syncobj_manager(struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
struct wl_resource *resource;
struct weston_compositor *compositor = data;
resource = wl_resource_create(client,
&wp_linux_drm_syncobj_manager_v1_interface,
version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource,
&syncobj_manager_interface_v1,
compositor, NULL);
}
bool
weston_surface_check_pending_syncobj_valid(struct weston_surface *surface)
{
struct weston_syncobj_surface *syncobj_surface = surface->syncobj_surface;
struct weston_surface_state *pend = &surface->pending;
bool has_acquire = pend->syncobj.acquire_point_valid;
bool has_release = pend->syncobj_release_ref.syncobj_release != NULL;
struct weston_syncobj_release *release;
struct weston_buffer *buffer = NULL;
if (!syncobj_surface)
return true;
if (pend->status & WESTON_SURFACE_DIRTY_BUFFER)
buffer = pend->buffer_ref.buffer;
if (has_acquire && !buffer) {
wl_resource_post_error(syncobj_surface->resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER,
"acquire point set but no buffer attached");
return false;
}
if (has_release && !buffer) {
wl_resource_post_error(syncobj_surface->resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER,
"release point set but no buffer attached");
return false;
}
if (buffer && !has_acquire) {
wl_resource_post_error(syncobj_surface->resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT,
"buffer attached but no acquire point set");
return false;
}
if (buffer && !has_release) {
wl_resource_post_error(syncobj_surface->resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT,
"buffer attached but no release point set");
return false;
}
if (has_acquire && has_release) {
release = pend->syncobj_release_ref.syncobj_release;
if (pend->syncobj.acquire_timeline_handle ==
release->release_timeline_handle &&
pend->syncobj.acquire_point >= release->release_point) {
wl_resource_post_error(syncobj_surface->resource,
WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS,
"acquire point must be less than release point on same timeline");
return false;
}
}
return true;
}
/** Advertise DRM synchronization object protocol support
*
* \param compositor The compositor to init for.
* \return Zero on success, -1 on failure.
*/
WL_EXPORT int
syncobj_setup(struct weston_compositor *compositor)
{
if (!compositor->renderer ||
!compositor->renderer->syncobj_import_timeline ||
!compositor->renderer->syncobj_timeline_set_eventfd)
return -1;
if (!wl_global_create(compositor->wl_display,
&wp_linux_drm_syncobj_manager_v1_interface,
1, compositor,
bind_syncobj_manager))
return -1;
return 0;
}
bool
weston_syncobj_surface_state_ready(struct weston_surface *surface,
struct weston_surface_state *state)
{
if (!state->syncobj.acquire_point_valid)
return true;
if (!state->syncobj.acquire_entry)
return true;
return state->syncobj.acquire_entry->signalled;
}
static int
acquire_point_eventfd_func(int fd, uint32_t mask, void *data)
{
struct weston_syncobj_acquire_entry *entry = data;
uint64_t value;
read(fd, &value, sizeof(value));
entry->signalled = true;
wl_event_source_remove(entry->event_source);
close(entry->fd);
entry->fd = -1;
entry->event_source = NULL;
wl_list_remove(&entry->link);
wl_list_init(&entry->link);
/* The eventfd may signal after all repaints triggered elsewhere, so
* a repaint must be scheduled here to ensure that the surface will
* actually be updated after the eventfd signals. */
weston_compositor_schedule_repaint(entry->surface->compositor);
if (entry->surface)
wl_list_remove(&entry->surface_destroy_listener.link);
entry->surface = NULL;
return 0;
}
void
weston_syncobj_surface_set_acquire_point(struct weston_surface *surface,
struct weston_surface_state *state)
{
struct weston_renderer *renderer = surface->compositor->renderer;
struct weston_syncobj_surface *syncobj_surface = surface->syncobj_surface;
struct weston_syncobj_acquire_entry *entry;
struct wl_event_loop *loop;
int sync_fd;
assert(renderer && renderer->syncobj_timeline_set_eventfd);
sync_fd = eventfd(0, EFD_CLOEXEC);
assert(sync_fd >= 0);
if (renderer->syncobj_timeline_set_eventfd(renderer,
state->syncobj.acquire_timeline_handle,
state->syncobj.acquire_point,
sync_fd) != 0) {
close(sync_fd);
assert(0);
return;
}
/* The eventfd creation triggers on a client's commit, to ensure that
* there would be time for it to signal and the eventfd be handled
* between the commit and a repaint.
* Since it is possible for a client to submit multiple commits before
* a repaint and an eventfd may already have been added, they need to
* be tracked separately. */
entry = xzalloc(sizeof *entry);
entry->fd = sync_fd;
entry->signalled = false;
entry->surface = surface;
entry->surface_destroy_listener.notify = acquire_entry_surface_destroy_cb;
wl_signal_add(&surface->destroy_signal, &entry->surface_destroy_listener);
entry->owner = &state->syncobj;
wl_list_insert(&syncobj_surface->acquire_entries, &entry->link);
loop = wl_display_get_event_loop(surface->compositor->wl_display);
entry->event_source = wl_event_loop_add_fd(loop, sync_fd,
WL_EVENT_READABLE,
acquire_point_eventfd_func,
entry);
state->syncobj.acquire_entry = entry;
}
void
weston_syncobj_state_fini(struct weston_syncobj_state *syncobj)
{
struct weston_syncobj_acquire_entry *entry = syncobj->acquire_entry;
if (!entry)
return;
if (entry->event_source) {
wl_event_source_remove(entry->event_source);
close(entry->fd);
wl_list_remove(&entry->link);
}
if (entry->surface)
wl_list_remove(&entry->surface_destroy_listener.link);
entry->owner = NULL;
free(entry);
syncobj->acquire_entry = NULL;
}
void
weston_syncobj_state_move(struct weston_syncobj_state *dst,
struct weston_syncobj_state *src)
{
*dst = *src;
if (dst->acquire_entry)
dst->acquire_entry->owner = dst;
src->acquire_point_valid = false;
src->acquire_entry = NULL;
}
static void
weston_syncobj_release_destroy(struct weston_syncobj_release *syncobj_release)
{
struct weston_renderer *r = syncobj_release->renderer;
assert(r);
/* At this time the compositor no longer uses the buffer, but use the
* fence_fd if available as a way to guarantee that no GPU rendering
* job is still pending */
if (syncobj_release->fence_fd >= 0) {
assert(r->syncobj_timeline_import_syncobj);
r->syncobj_timeline_import_syncobj(r, syncobj_release->release_timeline_handle,
syncobj_release->release_point,
syncobj_release->fence_fd);
close(syncobj_release->fence_fd);
} else {
assert(r->syncobj_timeline_signal);
r->syncobj_timeline_signal(r, syncobj_release->release_timeline_handle,
syncobj_release->release_point);
}
free(syncobj_release);
}
WL_EXPORT void
weston_syncobj_release_reference(struct weston_syncobj_release_reference *ref,
struct weston_syncobj_release *syncobj_release)
{
if (syncobj_release == ref->syncobj_release)
return;
if (ref->syncobj_release) {
ref->syncobj_release->ref_count--;
if (ref->syncobj_release->ref_count == 0)
weston_syncobj_release_destroy(ref->syncobj_release);
}
if (syncobj_release)
syncobj_release->ref_count++;
ref->syncobj_release = syncobj_release;
}
WL_EXPORT void
weston_syncobj_release_move(struct weston_syncobj_release_reference *dest,
struct weston_syncobj_release_reference *src)
{
weston_syncobj_release_reference(dest, src->syncobj_release);
weston_syncobj_release_reference(src, NULL);
}

59
libweston/syncobj.h Normal file
View file

@ -0,0 +1,59 @@
/*
* Copyright 2026 Erico Nunes
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "config.h"
#include "linux-drm-syncobj-v1-server-protocol.h"
int
syncobj_setup(struct weston_compositor *compositor);
bool
weston_surface_check_pending_syncobj_valid(struct weston_surface *surface);
bool
weston_syncobj_surface_state_ready(struct weston_surface *surface,
struct weston_surface_state *state);
void
weston_syncobj_surface_set_acquire_point(struct weston_surface *surface,
struct weston_surface_state *state);
void
weston_syncobj_state_fini(struct weston_syncobj_state *syncobj);
void
weston_syncobj_state_move(struct weston_syncobj_state *dst,
struct weston_syncobj_state *src);
void
weston_syncobj_release_reference(struct weston_syncobj_release_reference *ref,
struct weston_syncobj_release *syncobj_release);
void
weston_syncobj_release_move(struct weston_syncobj_release_reference *dest,
struct weston_syncobj_release_reference *src);

View file

@ -27,6 +27,7 @@ generated_protocols = [
[ 'ivi-hmi-controller', 'internal' ],
[ 'linux-dmabuf', 'unstable', 'v1' ],
[ 'linux-explicit-synchronization', 'unstable', 'v1' ],
[ 'linux-drm-syncobj', 'staging', 'v1' ],
[ 'presentation-time', 'stable' ],
[ 'pointer-constraints', 'unstable', 'v1' ],
[ 'relative-pointer', 'unstable', 'v1' ],