Check in new files for explicit synchronization

* explicit_synchronization.c:
* linux-explicit-synchronization-unstable-v1.xml: New files.
This commit is contained in:
oldosfan 2022-09-25 08:13:27 +00:00
parent 86216a1164
commit 28f1410e35
2 changed files with 746 additions and 0 deletions

490
explicit_synchronization.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>. */
#include <string.h>
#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);
}

View file

@ -0,0 +1,256 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="zwp_linux_explicit_synchronization_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zwp_linux_explicit_synchronization_v1" version="2">
<description summary="protocol for providing explicit synchronization">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy explicit synchronization factory object">
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.
</description>
</request>
<enum name="error">
<entry name="synchronization_exists" value="0"
summary="the surface already has a synchronization object associated"/>
</enum>
<request name="get_synchronization">
<description summary="extend surface interface for explicit synchronization">
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.
</description>
<arg name="id" type="new_id"
interface="zwp_linux_surface_synchronization_v1"
summary="the new synchronization interface id"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface"/>
</request>
</interface>
<interface name="zwp_linux_surface_synchronization_v1" version="2">
<description summary="per-surface explicit synchronization support">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy synchronization object">
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.
</description>
</request>
<enum name="error">
<entry name="invalid_fence" value="0"
summary="the fence specified by the client could not be imported"/>
<entry name="duplicate_fence" value="1"
summary="multiple fences added for a single surface commit"/>
<entry name="duplicate_release" value="2"
summary="multiple releases added for a single surface commit"/>
<entry name="no_surface" value="3"
summary="the associated wl_surface was destroyed"/>
<entry name="unsupported_buffer" value="4"
summary="the buffer does not support explicit synchronization"/>
<entry name="no_buffer" value="5"
summary="no buffer was attached"/>
</enum>
<request name="set_acquire_fence">
<description summary="set the acquire fence">
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.
</description>
<arg name="fd" type="fd" summary="acquire fence fd"/>
</request>
<request name="get_release">
<description summary="release fence for last-attached buffer">
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.
</description>
<arg name="release" type="new_id" interface="zwp_linux_buffer_release_v1"
summary="new zwp_linux_buffer_release_v1 object"/>
</request>
</interface>
<interface name="zwp_linux_buffer_release_v1" version="1">
<description summary="buffer release explicit synchronization">
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.
</description>
<event name="fenced_release" type="destructor">
<description summary="release buffer with fence">
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.
</description>
<arg name="fence" type="fd" summary="fence for last operation on buffer"/>
</event>
<event name="immediate_release" type="destructor">
<description summary="release buffer immediately">
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.
</description>
</event>
</interface>
</protocol>