From 28f1410e354f019b8bdc2ab96256091ff24f2bbd Mon Sep 17 00:00:00 2001 From: oldosfan Date: Sun, 25 Sep 2022 08:13:27 +0000 Subject: [PATCH] Check in new files for explicit synchronization * explicit_synchronization.c: * linux-explicit-synchronization-unstable-v1.xml: New files. --- explicit_synchronization.c | 490 ++++++++++++++++++ ...x-explicit-synchronization-unstable-v1.xml | 256 +++++++++ 2 files changed, 746 insertions(+) create mode 100644 explicit_synchronization.c create mode 100644 linux-explicit-synchronization-unstable-v1.xml 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. + + + + +