diff --git a/explicit_synchronization.c b/explicit_synchronization.c
new file mode 100644
index 0000000..bc1d5b6
--- /dev/null
+++ b/explicit_synchronization.c
@@ -0,0 +1,490 @@
+/* Wayland compositor running on top of an X server.
+
+Copyright (C) 2022 to various contributors.
+
+This file is part of 12to11.
+
+12to11 is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+12to11 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with 12to11. If not, see . */
+
+#include
+
+#include "compositor.h"
+
+#include "linux-explicit-synchronization-unstable-v1.h"
+
+struct _Synchronization
+{
+ /* The surface destroy listener. */
+ DestroyCallback *destroy_listener;
+
+ /* The surface. */
+ Surface *surface;
+
+ /* The file descriptor of any pending acquire fence. */
+ int acquire_fence;
+
+ /* Any associated release object. */
+ SyncRelease *release;
+
+ /* The associated resource. */
+ struct wl_resource *resource;
+};
+
+struct _SyncRelease
+{
+ /* The associated surface. */
+ Surface *surface;
+
+ /* The associated synchronization. */
+ Synchronization *synchronization;
+
+ /* The associated resource. */
+ struct wl_resource *resource;
+};
+
+/* The global zwp_linux_explicit_synchronization_v1 object. */
+static struct wl_global *explicit_sync_global;
+
+static void
+HandleReleaseDestroy (struct wl_resource *resource)
+{
+ SyncRelease *release;
+
+ release = wl_resource_get_user_data (resource);
+
+ /* If release is attached to a surface, remove it from the
+ surface. */
+
+ if (release->surface)
+ release->surface->release = NULL;
+
+ /* Do the same for the synchronization object. */
+
+ if (release->synchronization)
+ release->synchronization->release = NULL;
+
+ /* Free the release object. */
+ XLFree (release);
+}
+
+
+
+static void
+DestroySynchronization (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static void
+SetAcquireFence (struct wl_client *client, struct wl_resource *resource,
+ int32_t fd)
+{
+ Synchronization *synchronization;
+
+ synchronization = wl_resource_get_user_data (resource);
+
+#define NoSurface ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE
+
+ /* If the surface was destroyed, raise such an error. */
+ if (!synchronization->surface)
+ {
+ wl_resource_post_error (resource, NoSurface,
+ "the surface associated with this"
+ " resource was destroyed");
+ goto error;
+ }
+
+#undef NoSurface
+
+#define DuplicateFence \
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_FENCE
+
+ /* If a fence was already attached, raise that as an errore. */
+ if (synchronization->acquire_fence != -1)
+ {
+ wl_resource_post_error (resource, DuplicateFence,
+ "another fence has already been attached"
+ " during this commit cycle");
+ goto error;
+ }
+
+#undef DuplicateFence
+
+ /* Set the file descriptor and return. */
+ synchronization->acquire_fence = fd;
+ return;
+
+ error:
+ close (fd);
+}
+
+static void
+GetRelease (struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ Synchronization *synchronization;
+ SyncRelease *release;
+
+ /* The release lifecycle is somewhat like this:
+
+ First, it starts out as the `release' field of a Synchronization
+ resource. When the synchronization is committed, it is moved to
+ the release field of the surface, and it is detatched from both
+ once release events are sent. */
+
+ synchronization = wl_resource_get_user_data (resource);
+
+#define DuplicateRelease \
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_RELEASE
+
+ if (synchronization->release)
+ {
+ /* Uncommitted release already exists. Post an error. */
+ wl_resource_post_error (resource, DuplicateRelease,
+ "another release has already been acquired"
+ " during this commit cycle");
+ return;
+ }
+
+#undef DuplicateRelease
+
+ release = XLSafeMalloc (sizeof *release);
+
+ if (!release)
+ {
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ memset (release, 0, sizeof *release);
+ release->resource
+ = wl_resource_create (client,
+ &zwp_linux_buffer_release_v1_interface,
+ wl_resource_get_version (resource),
+ id);
+
+ if (!release->resource)
+ {
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ /* Attach the release. */
+ release->synchronization = synchronization;
+ synchronization->release = release;
+
+ /* Set the resource implementation. */
+ wl_resource_set_implementation (release->resource,
+ NULL, release,
+ HandleReleaseDestroy);
+}
+
+static void
+HandleSurfaceDestroy (void *data)
+{
+ Synchronization *synchronization;
+
+ synchronization = data;
+
+ /* The surface was destroyed. Mark the object as invalid. */
+ synchronization->surface = NULL;
+ synchronization->destroy_listener = NULL;
+}
+
+static void
+HandleSurfaceCommit (Synchronization *synchronization, Surface *surface)
+{
+#define NoBuffer ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER
+
+ if (synchronization->acquire_fence != -1)
+ {
+ if (!(surface->pending_state.pending & PendingBuffer
+ && surface->pending_state.buffer))
+ {
+ /* If no buffer was attached, report an error. */
+ wl_resource_post_error (synchronization->resource,
+ NoBuffer, "no buffer attached"
+ " but acquire fence provided");
+ close (synchronization->acquire_fence);
+ synchronization->acquire_fence = -1;
+
+ return;
+ }
+
+ if (surface->acquire_fence != -1)
+ close (surface->acquire_fence);
+
+ surface->acquire_fence = synchronization->acquire_fence;
+ synchronization->acquire_fence = -1;
+ }
+
+ /* Move the release callback to the surface. Assume one does
+ not already exist. */
+ XLAssert (surface->release == NULL);
+
+ /* surface->release can still end up NULL if no release was
+ attached. */
+ surface->release = synchronization->release;
+
+ if (surface->release)
+ {
+ /* Clear these fields, to detach the release from the
+ synchronization. */
+ surface->release->synchronization = NULL;
+ synchronization->release = NULL;
+
+ /* And set this field to attach the release to the
+ surface. */
+ surface->release->surface = surface;
+
+ if (surface->release
+ && !(surface->pending_state.pending & PendingBuffer
+ && surface->pending_state.buffer))
+ wl_resource_post_error (synchronization->resource,
+ NoBuffer, "no buffer attached"
+ " but release provided");
+ }
+#undef NoBuffer
+}
+
+static void
+HandleSynchronizationDestroy (struct wl_resource *resource)
+{
+ Synchronization *synchronization;
+
+ /* Free the synchronization as it has been destroyed. */
+ synchronization = wl_resource_get_user_data (resource);
+
+ if (synchronization->destroy_listener)
+ XLSurfaceCancelRunOnFree (synchronization->destroy_listener);
+
+ if (synchronization->surface)
+ /* Also detach it from the surface. */
+ synchronization->surface->synchronization = NULL;
+
+ /* Detach any attached release callback. */
+ if (synchronization->release)
+ wl_resource_destroy (synchronization->release->resource);
+
+ /* If a fence happens to still be attached, close it. */
+ if (synchronization->acquire_fence != -1)
+ close (synchronization->acquire_fence);
+
+ XLFree (synchronization);
+}
+
+static struct zwp_linux_surface_synchronization_v1_interface synchronization_impl =
+ {
+ .destroy = DestroySynchronization,
+ .set_acquire_fence = SetAcquireFence,
+ .get_release = GetRelease,
+ };
+
+
+
+static void
+Destroy (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static void
+GetSynchronization (struct wl_client *client, struct wl_resource *resource,
+ uint32_t id, struct wl_resource *surface_resource)
+{
+ Synchronization *synchronization;
+ Surface *surface;
+
+ surface = wl_resource_get_user_data (surface_resource);
+
+#define SynchronizationAlreadyExists \
+ ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS
+
+ if (surface->synchronization)
+ {
+ /* Don't let clients create more synchronization objects if one
+ already exists. */
+ wl_resource_post_error (resource, SynchronizationAlreadyExists,
+ "synchronization object already exists");
+ return;
+ }
+
+#undef SynchronizationAlreadyExists
+
+ /* Allocate the synchronization object. */
+ synchronization = XLSafeMalloc (sizeof *synchronization);
+
+ if (!synchronization)
+ {
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ memset (synchronization, 0, sizeof *synchronization);
+ synchronization->resource
+ = wl_resource_create (client,
+ &zwp_linux_surface_synchronization_v1_interface,
+ wl_resource_get_version (resource), id);
+
+ if (!synchronization->resource)
+ {
+ XLFree (synchronization);
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ /* Now, attach the synchronization object to the surface. */
+ surface->synchronization = synchronization;
+
+ /* And attach a destroy listener. */
+ synchronization->destroy_listener
+ = XLSurfaceRunOnFree (surface, HandleSurfaceDestroy,
+ synchronization);
+
+ /* And attach the surface to the synchronization. */
+ synchronization->surface = surface;
+
+ /* Set the implementation. */
+ wl_resource_set_implementation (synchronization->resource,
+ &synchronization_impl,
+ synchronization,
+ HandleSynchronizationDestroy);
+
+ /* Clear initial values. */
+ synchronization->acquire_fence = -1;
+}
+
+static struct zwp_linux_explicit_synchronization_v1_interface explicit_sync_impl =
+ {
+ .destroy = Destroy,
+ .get_synchronization = GetSynchronization,
+ };
+
+static void
+HandleBind (struct wl_client *client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource
+ = wl_resource_create (client,
+ &zwp_linux_explicit_synchronization_v1_interface,
+ version, id);
+ if (!resource)
+ {
+ wl_client_post_no_memory (client);
+ return;
+ }
+
+ wl_resource_set_implementation (resource, &explicit_sync_impl,
+ NULL, NULL);
+}
+
+void
+XLDestroyRelease (SyncRelease *release)
+{
+ /* Destroying the resoruce will cause the release to be freed. */
+ wl_resource_destroy (release->resource);
+}
+
+void
+XLSyncRelease (SyncRelease *release)
+{
+ Bool error;
+ int fd;
+
+ /* Optimization ideas. Every time a buffer is attached with a
+ release object, create new finish_fence for the buffer, that is
+ signalled whenever the contents are drawn. Then, just use that
+ fence here instead. */
+
+ error = False;
+ fd = RenderGetFinishFence (&error);
+
+ if (error)
+#define InvalidFence ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE
+ wl_resource_post_error (release->resource, InvalidFence,
+ "server failed to create finish fence");
+#undef InvalidFence
+ else
+ {
+ /* Post a fenced release with the fence. */
+ zwp_linux_buffer_release_v1_send_fenced_release (release->resource,
+ fd);
+ /* Close the file descriptor. */
+ close (fd);
+ }
+
+ /* Destroy the sync object. */
+ wl_resource_destroy (release->resource);
+}
+
+void
+XLSyncCommit (Synchronization *synchronization)
+{
+ HandleSurfaceCommit (synchronization, synchronization->surface);
+}
+
+void
+XLWaitFence (Surface *surface)
+{
+ RenderFence fence;
+ Bool error;
+
+ if (surface->acquire_fence == -1)
+ return;
+
+ error = False;
+
+ /* This is expected to close the file descriptor. */
+ fence = RenderImportFdFence (surface->acquire_fence, &error);
+
+ if (error)
+ {
+#define InvalidFence ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE
+ /* Importing the fence failed; signal an error to the
+ server. */
+ wl_resource_post_error (surface->resource,
+ InvalidFence, "the specified sync"
+ " fence could not be imported");
+#undef InvalidFence
+ /* Try closing the file descriptor as well. */
+ close (surface->acquire_fence);
+ surface->acquire_fence = -1;
+
+ return;
+ }
+
+ surface->acquire_fence = -1;
+
+ /* Wait for the fence to be triggered. */
+ RenderWaitFence (fence);
+
+ /* Delete the fence. */
+ RenderDeleteFence (fence);
+}
+
+void
+XLInitExplicitSynchronization (void)
+{
+ /* If the renderer does not support explicit synchronization,
+ return. */
+ if (!(renderer_flags & SupportsExplicitSync))
+ return;
+
+ explicit_sync_global
+ = wl_global_create (compositor.wl_display,
+ &zwp_linux_explicit_synchronization_v1_interface,
+ 2, NULL, HandleBind);
+}
diff --git a/linux-explicit-synchronization-unstable-v1.xml b/linux-explicit-synchronization-unstable-v1.xml
new file mode 100644
index 0000000..ac91641
--- /dev/null
+++ b/linux-explicit-synchronization-unstable-v1.xml
@@ -0,0 +1,256 @@
+
+
+
+
+ Copyright 2016 The Chromium Authors.
+ Copyright 2017 Intel Corporation
+ Copyright 2018 Collabora, Ltd
+
+ 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.
+
+
+
+
+ This global is a factory interface, allowing clients to request
+ explicit synchronization for buffers on a per-surface basis.
+
+ See zwp_linux_surface_synchronization_v1 for more information.
+
+ This interface is derived from Chromium's
+ zcr_linux_explicit_synchronization_v1.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ Destroy this explicit synchronization factory object. Other objects,
+ including zwp_linux_surface_synchronization_v1 objects created by this
+ factory, shall not be affected by this request.
+
+
+
+
+
+
+
+
+
+ Instantiate an interface extension for the given wl_surface to provide
+ explicit synchronization.
+
+ If the given wl_surface already has an explicit synchronization object
+ associated, the synchronization_exists protocol error is raised.
+
+ Graphics APIs, like EGL or Vulkan, that manage the buffer queue and
+ commits of a wl_surface themselves, are likely to be using this
+ extension internally. If a client is using such an API for a
+ wl_surface, it should not directly use this extension on that surface,
+ to avoid raising a synchronization_exists protocol error.
+
+
+
+
+
+
+
+
+
+ This object implements per-surface explicit synchronization.
+
+ Synchronization refers to co-ordination of pipelined operations performed
+ on buffers. Most GPU clients will schedule an asynchronous operation to
+ render to the buffer, then immediately send the buffer to the compositor
+ to be attached to a surface.
+
+ In implicit synchronization, ensuring that the rendering operation is
+ complete before the compositor displays the buffer is an implementation
+ detail handled by either the kernel or userspace graphics driver.
+
+ By contrast, in explicit synchronization, dma_fence objects mark when the
+ asynchronous operations are complete. When submitting a buffer, the
+ client provides an acquire fence which will be waited on before the
+ compositor accesses the buffer. The Wayland server, through a
+ zwp_linux_buffer_release_v1 object, will inform the client with an event
+ which may be accompanied by a release fence, when the compositor will no
+ longer access the buffer contents due to the specific commit that
+ requested the release event.
+
+ Each surface can be associated with only one object of this interface at
+ any time.
+
+ In version 1 of this interface, explicit synchronization is only
+ guaranteed to be supported for buffers created with any version of the
+ wp_linux_dmabuf buffer factory. Version 2 additionally guarantees
+ explicit synchronization support for opaque EGL buffers, which is a type
+ of platform specific buffers described in the EGL_WL_bind_wayland_display
+ extension. Compositors are free to support explicit synchronization for
+ additional buffer types.
+
+
+
+
+ Destroy this explicit synchronization object.
+
+ Any fence set by this object with set_acquire_fence since the last
+ commit will be discarded by the server. Any fences set by this object
+ before the last commit are not affected.
+
+ zwp_linux_buffer_release_v1 objects created by this object are not
+ affected by this request.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Set the acquire fence that must be signaled before the compositor
+ may sample from the buffer attached with wl_surface.attach. The fence
+ is a dma_fence kernel object.
+
+ The acquire fence is double-buffered state, and will be applied on the
+ next wl_surface.commit request for the associated surface. Thus, it
+ applies only to the buffer that is attached to the surface at commit
+ time.
+
+ If the provided fd is not a valid dma_fence fd, then an INVALID_FENCE
+ error is raised.
+
+ If a fence has already been attached during the same commit cycle, a
+ DUPLICATE_FENCE error is raised.
+
+ If the associated wl_surface was destroyed, a NO_SURFACE error is
+ raised.
+
+ If at surface commit time the attached buffer does not support explicit
+ synchronization, an UNSUPPORTED_BUFFER error is raised.
+
+ If at surface commit time there is no buffer attached, a NO_BUFFER
+ error is raised.
+
+
+
+
+
+
+ Create a listener for the release of the buffer attached by the
+ client with wl_surface.attach. See zwp_linux_buffer_release_v1
+ documentation for more information.
+
+ The release object is double-buffered state, and will be associated
+ with the buffer that is attached to the surface at wl_surface.commit
+ time.
+
+ If a zwp_linux_buffer_release_v1 object has already been requested for
+ the surface in the same commit cycle, a DUPLICATE_RELEASE error is
+ raised.
+
+ If the associated wl_surface was destroyed, a NO_SURFACE error
+ is raised.
+
+ If at surface commit time there is no buffer attached, a NO_BUFFER
+ error is raised.
+
+
+
+
+
+
+
+ This object is instantiated in response to a
+ zwp_linux_surface_synchronization_v1.get_release request.
+
+ It provides an alternative to wl_buffer.release events, providing a
+ unique release from a single wl_surface.commit request. The release event
+ also supports explicit synchronization, providing a fence FD for the
+ client to synchronize against.
+
+ Exactly one event, either a fenced_release or an immediate_release, will
+ be emitted for the wl_surface.commit request. The compositor can choose
+ release by release which event it uses.
+
+ This event does not replace wl_buffer.release events; servers are still
+ required to send those events.
+
+ Once a buffer release object has delivered a 'fenced_release' or an
+ 'immediate_release' event it is automatically destroyed.
+
+
+
+
+ Sent when the compositor has finalised its usage of the associated
+ buffer for the relevant commit, providing a dma_fence which will be
+ signaled when all operations by the compositor on that buffer for that
+ commit have finished.
+
+ Once the fence has signaled, and assuming the associated buffer is not
+ pending release from other wl_surface.commit requests, no additional
+ explicit or implicit synchronization is required to safely reuse or
+ destroy the buffer.
+
+ This event destroys the zwp_linux_buffer_release_v1 object.
+
+
+
+
+
+
+ Sent when the compositor has finalised its usage of the associated
+ buffer for the relevant commit, and either performed no operations
+ using it, or has a guarantee that all its operations on that buffer for
+ that commit have finished.
+
+ Once this event is received, and assuming the associated buffer is not
+ pending release from other wl_surface.commit requests, no additional
+ explicit or implicit synchronization is required to safely reuse or
+ destroy the buffer.
+
+ This event destroys the zwp_linux_buffer_release_v1 object.
+
+
+
+
+