libweston/linux-dmabuf.h \
libweston/linux-explicit-synchronization.c \
libweston/linux-explicit-synchronization.h \
+ libweston/linux-sync-file.c \
+ libweston/linux-sync-file.h \
libweston/pixel-formats.c \
libweston/pixel-formats.h \
libweston/weston-debug.c \
libweston/weston-debug.h \
+ shared/fd-util.h \
shared/helpers.h \
shared/matrix.c \
shared/matrix.h \
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf.h"
#include "linux-dmabuf-unstable-v1-server-protocol.h"
+#include "linux-explicit-synchronization.h"
#ifndef DRM_CLIENT_CAP_ASPECT_RATIO
#define DRM_CLIENT_CAP_ASPECT_RATIO 4
WDRM_PLANE_FB_ID,
WDRM_PLANE_CRTC_ID,
WDRM_PLANE_IN_FORMATS,
+ WDRM_PLANE_IN_FENCE_FD,
WDRM_PLANE__COUNT
};
[WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
[WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
[WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" },
+ [WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" },
};
/**
bool complete;
+ /* We don't own the fd, so we shouldn't close it */
+ int in_fence_fd;
+
struct wl_list link; /* drm_output_state::plane_list */
};
assert(state);
state->output_state = state_output;
state->plane = plane;
+ state->in_fence_fd = -1;
/* Here we only add the plane state to the desired link, and not
* set the member. Having an output pointer set means that the
wl_list_remove(&state->link);
wl_list_init(&state->link);
state->output_state = NULL;
+ state->in_fence_fd = -1;
if (force || state != state->plane->state_cur) {
drm_fb_unref(state->fb);
extents->y2 != output->base.y + output->base.height)
return NULL;
+ /* If the surface buffer has an in-fence fd, but the plane doesn't
+ * support fences, we can't place the buffer on this plane. */
+ if (ev->surface->acquire_fence_fd >= 0 &&
+ (!b->atomic_modeset ||
+ scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0))
+ return NULL;
+
fb = drm_fb_get_from_view(output_state, ev);
if (!fb) {
drm_debug(b, "\t\t\t\t[scanout] not placing view %p on scanout: "
state->src_h != state->dest_h << 16))
goto err;
+ state->in_fence_fd = ev->surface->acquire_fence_fd;
+
/* In plane-only mode, we don't need to test the state now, as we
* will only test it once at the end. */
return state;
assert(scanout_state->dest_y == 0);
assert(scanout_state->dest_w == scanout_state->src_w >> 16);
assert(scanout_state->dest_h == scanout_state->src_h >> 16);
+ /* The legacy SetCrtc API doesn't support fences */
+ assert(scanout_state->in_fence_fd == -1);
mode = to_drm_mode(output->base.current_mode);
if (backend->state_invalid ||
assert(!ps->complete);
assert(!ps->output || ps->output == output);
assert(!!ps->output == !!ps->fb);
+ /* The legacy SetPlane API doesn't support fences */
+ assert(ps->in_fence_fd == -1);
if (ps->fb && !backend->sprites_hidden)
fb_id = ps->fb->fb_id;
(unsigned long) plane->plane_id,
pinfo ? pinfo->drm_format_name : "UNKNOWN");
+ if (plane_state->in_fence_fd >= 0) {
+ ret |= plane_add_prop(req, plane,
+ WDRM_PLANE_IN_FENCE_FD,
+ plane_state->in_fence_fd);
+ }
+
if (ret != 0) {
weston_log("couldn't set plane state\n");
return ret;
continue;
}
+ /* If the surface buffer has an in-fence fd, but the plane
+ * doesn't support fences, we can't place the buffer on this
+ * plane. */
+ if (ev->surface->acquire_fence_fd >= 0 &&
+ (!b->atomic_modeset ||
+ p->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0)) {
+ drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
+ "no in-fence support\n", ev);
+ drm_plane_state_put_back(state);
+ state = NULL;
+ continue;
+ }
+
/* We hold one reference for the lifetime of this function;
* from calling drm_fb_get_from_view, to the out label where
* we unconditionally drop the reference. So, we take another
* reference here to live within the state. */
state->fb = drm_fb_ref(fb);
+ state->in_fence_fd = ev->surface->acquire_fence_fd;
+
/* In planes-only mode, we don't have an incremental state to
* test against, so we just hope it'll work. */
if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
{
struct drm_output *output;
bool dmabuf_support_inited;
+ bool linux_explicit_sync_inited;
if (!b->use_pixman)
return;
dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
+ linux_explicit_sync_inited =
+ b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC;
weston_log("Switching to GL renderer\n");
weston_log("Error: initializing dmabuf "
"support failed.\n");
}
+
+ if (!linux_explicit_sync_inited &&
+ (b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC)) {
+ if (linux_explicit_synchronization_setup(b->compositor) < 0)
+ weston_log("Error: initializing explicit "
+ " synchronization support failed.\n");
+ }
}
static void
"support failed.\n");
}
+ if (compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC) {
+ if (linux_explicit_synchronization_setup(compositor) < 0)
+ weston_log("Error: initializing explicit "
+ " synchronization support failed.\n");
+ }
+
ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME,
&api, sizeof(api));
#include "pixman-renderer.h"
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf.h"
+#include "linux-explicit-synchronization.h"
#include "windowed-output-api.h"
#define DEFAULT_AXIS_STEP_DISTANCE 10
"support failed.\n");
}
+ if (compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC) {
+ if (linux_explicit_synchronization_setup(compositor) < 0)
+ weston_log("Error: initializing explicit "
+ " synchronization support failed.\n");
+ }
+
ret = weston_plugin_api_register(compositor, WESTON_WINDOWED_OUTPUT_API_NAME,
&api, sizeof(api));
#include <sys/time.h>
#include <time.h>
#include <errno.h>
+#include <inttypes.h>
#include "timeline.h"
#include "linux-dmabuf.h"
#include "viewporter-server-protocol.h"
#include "presentation-time-server-protocol.h"
+#include "linux-explicit-synchronization-unstable-v1-server-protocol.h"
+#include "shared/fd-util.h"
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/string-helpers.h"
state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
state->buffer_viewport.surface.width = -1;
state->buffer_viewport.changed = 0;
+
+ state->acquire_fence_fd = -1;
}
static void
if (state->buffer)
wl_list_remove(&state->buffer_destroy_listener.link);
state->buffer = NULL;
+
+ fd_clear(&state->acquire_fence_fd);
}
static void
wl_list_init(&surface->pointer_constraints);
+ surface->acquire_fence_fd = -1;
+
return surface;
}
link)
weston_pointer_constraint_destroy(constraint);
+ fd_clear(&surface->acquire_fence_fd);
+
free(surface);
}
surface->buffer_viewport = state->buffer_viewport;
/* wl_surface.attach */
- if (state->newly_attached)
+ if (state->newly_attached) {
+ /* zwp_surface_synchronization_v1.set_acquire_fence */
+ fd_move(&surface->acquire_fence_fd,
+ &state->acquire_fence_fd);
+
weston_surface_attach(surface, state->buffer);
+ }
weston_surface_state_set_buffer(state, NULL);
+ assert(state->acquire_fence_fd == -1);
weston_surface_build_buffer_matrix(surface,
&surface->surface_to_buffer_matrix);
return;
}
+ if (surface->pending.acquire_fence_fd >= 0) {
+ assert(surface->synchronization_resource);
+
+ if (!surface->pending.buffer) {
+ fd_clear(&surface->pending.acquire_fence_fd);
+ wl_resource_post_error(surface->synchronization_resource,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER,
+ "wl_surface@%"PRIu32" no buffer for synchronization",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ /* We support fences for both wp_linux_dmabuf and opaque EGL
+ * buffers, as mandated by minor version 2 of the
+ * zwp_linux_explicit_synchronization_v1 protocol. Since
+ * renderers that support fences currently only support these
+ * two buffer types plus SHM buffers, we can just check for the
+ * SHM buffer case here.
+ */
+ if (wl_shm_buffer_get(surface->pending.buffer->resource)) {
+ fd_clear(&surface->pending.acquire_fence_fd);
+ wl_resource_post_error(surface->synchronization_resource,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_UNSUPPORTED_BUFFER,
+ "wl_surface@%"PRIu32" unsupported buffer for synchronization",
+ wl_resource_get_id(resource));
+ return;
+ }
+ }
+
if (sub) {
weston_subsurface_commit(sub);
return;
surface->pending.buffer);
weston_presentation_feedback_discard_list(
&sub->cached.feedback_list);
+ /* zwp_surface_synchronization_v1.set_acquire_fence */
+ fd_move(&sub->cached.acquire_fence_fd,
+ &surface->pending.acquire_fence_fd);
}
+ assert(surface->pending.acquire_fence_fd == -1);
sub->cached.sx += surface->pending.sx;
sub->cached.sy += surface->pending.sy;
/* renderer supports weston_view_set_mask() clipping */
WESTON_CAP_VIEW_CLIP_MASK = 0x0010,
+
+ /* renderer supports explicit synchronization */
+ WESTON_CAP_EXPLICIT_SYNC = 0x0020,
};
/* Configuration struct for a backend.
/* wp_viewport.set_source */
/* wp_viewport.set_destination */
struct weston_buffer_viewport buffer_viewport;
+
+ /* zwp_surface_synchronization_v1.set_acquire_fence */
+ int acquire_fence_fd;
};
struct weston_surface_activation_data {
/* zwp_surface_synchronization_v1 resource for this surface */
struct wl_resource *synchronization_resource;
+ int acquire_fence_fd;
};
struct weston_subsurface {
#include "vertex-clipping.h"
#include "linux-dmabuf.h"
#include "linux-dmabuf-unstable-v1-server-protocol.h"
+#include "linux-explicit-synchronization.h"
#include "pixel-formats.h"
#include "shared/helpers.h"
PFNEGLCREATESYNCKHRPROC create_sync;
PFNEGLDESTROYSYNCKHRPROC destroy_sync;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC dup_native_fence_fd;
+
+ int has_wait_sync;
+ PFNEGLWAITSYNCKHRPROC wait_sync;
};
enum timeline_render_point_type {
glUniform1i(shader->tex_uniforms[i], i);
}
+static int
+ensure_surface_buffer_is_ready(struct gl_renderer *gr,
+ struct gl_surface_state *gs)
+{
+ EGLint attribs[] = {
+ EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
+ -1,
+ EGL_NONE
+ };
+ struct weston_surface *surface = gs->surface;
+ struct weston_buffer *buffer = gs->buffer_ref.buffer;
+ EGLSyncKHR sync;
+ EGLint wait_ret;
+ EGLint destroy_ret;
+
+ if (!buffer)
+ return 0;
+
+ if (surface->acquire_fence_fd < 0)
+ return 0;
+
+ /* We should only get a fence if we support EGLSyncKHR, since
+ * we don't advertise the explicit sync protocol otherwise. */
+ assert(gr->has_native_fence_sync);
+ /* We should only get a fence for non-SHM buffers, since surface
+ * commit would have failed otherwise. */
+ assert(wl_shm_buffer_get(buffer->resource) == NULL);
+
+ attribs[1] = dup(surface->acquire_fence_fd);
+ if (attribs[1] == -1) {
+ linux_explicit_synchronization_send_server_error(
+ gs->surface->synchronization_resource,
+ "Failed to dup acquire fence");
+ return -1;
+ }
+
+ sync = gr->create_sync(gr->egl_display,
+ EGL_SYNC_NATIVE_FENCE_ANDROID,
+ attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ linux_explicit_synchronization_send_server_error(
+ gs->surface->synchronization_resource,
+ "Failed to create EGLSyncKHR object");
+ close(attribs[1]);
+ return -1;
+ }
+
+ wait_ret = gr->wait_sync(gr->egl_display, sync, 0);
+ if (wait_ret == EGL_FALSE) {
+ linux_explicit_synchronization_send_server_error(
+ gs->surface->synchronization_resource,
+ "Failed to wait on EGLSyncKHR object");
+ /* Continue to try to destroy the sync object. */
+ }
+
+
+ destroy_ret = gr->destroy_sync(gr->egl_display, sync);
+ if (destroy_ret == EGL_FALSE) {
+ linux_explicit_synchronization_send_server_error(
+ gs->surface->synchronization_resource,
+ "Failed to destroy on EGLSyncKHR object");
+ }
+
+ return (wait_ret == EGL_TRUE && destroy_ret == EGL_TRUE) ? 0 : -1;
+}
+
static void
draw_view(struct weston_view *ev, struct weston_output *output,
pixman_region32_t *damage) /* in global coordinates */
if (!pixman_region32_not_empty(&repaint))
goto out;
+ if (ensure_surface_buffer_is_ready(gr, gs) < 0)
+ goto out;
+
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
if (gr->fan_debug) {
(void *) eglGetProcAddress("eglDupNativeFenceFDANDROID");
gr->has_native_fence_sync = 1;
} else {
- weston_log("warning: Disabling render GPU timeline due to "
- "missing EGL_ANDROID_native_fence_sync extension\n");
+ weston_log("warning: Disabling render GPU timeline and explicit "
+ "synchronization due to missing "
+ "EGL_ANDROID_native_fence_sync extension\n");
+ }
+
+ if (weston_check_egl_extension(extensions, "EGL_KHR_wait_sync")) {
+ gr->wait_sync = (void *) eglGetProcAddress("eglWaitSyncKHR");
+ gr->has_wait_sync = 1;
+ } else {
+ weston_log("warning: Disabling explicit synchronization due"
+ "to missing EGL_KHR_wait_sync extension\n");
}
renderer_setup_egl_client_extensions(gr);
}
ec->renderer = &gr->base;
- ec->capabilities |= WESTON_CAP_ROTATION_ANY;
- ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
- ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
if (gl_renderer_setup_egl_extensions(ec) < 0)
goto fail_with_error;
+ ec->capabilities |= WESTON_CAP_ROTATION_ANY;
+ ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
+ ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
+ if (gr->has_native_fence_sync && gr->has_wait_sync)
+ ec->capabilities |= WESTON_CAP_EXPLICIT_SYNC;
+
wl_list_init(&gr->dmabuf_images);
if (gr->has_dmabuf_import) {
gr->base.import_dmabuf = gl_renderer_import_dmabuf;
#include "config.h"
+#include <assert.h>
#include <inttypes.h>
#include "compositor.h"
#include "linux-explicit-synchronization.h"
#include "linux-explicit-synchronization-unstable-v1-server-protocol.h"
+#include "linux-sync-file.h"
+#include "shared/fd-util.h"
static void
destroy_linux_surface_synchronization(struct wl_resource *resource)
struct weston_surface *surface =
wl_resource_get_user_data(resource);
- if (surface)
+ if (surface) {
+ fd_clear(&surface->pending.acquire_fence_fd);
surface->synchronization_resource = NULL;
+ }
}
static void
struct wl_resource *resource,
int32_t fd)
{
- wl_client_post_no_memory(client);
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ if (!surface) {
+ wl_resource_post_error(
+ resource,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE,
+ "surface no longer exists");
+ goto err;
+ }
+
+ if (!linux_sync_file_is_valid(fd)) {
+ wl_resource_post_error(
+ resource,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE,
+ "invalid fence fd");
+ goto err;
+ }
+
+ if (surface->pending.acquire_fence_fd != -1) {
+ wl_resource_post_error(
+ resource,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_FENCE,
+ "already have a fence fd");
+ goto err;
+ }
+
+ fd_update(&surface->pending.acquire_fence_fd, fd);
+
+ return;
+
+err:
+ close(fd);
}
static void
return 0;
}
+
+/** Resolve an internal compositor error by disconnecting the client.
+ *
+ * This function is used in cases when explicit synchronization
+ * turns out to be unusable and there is no fallback path.
+ *
+ * It is possible the fault is caused by a compositor bug, the underlying
+ * graphics stack bug or normal behaviour, or perhaps a client mistake.
+ * In any case, the options are to either composite garbage or nothing,
+ * or disconnect the client. This is a helper function for the latter.
+ *
+ * The error is sent as an INVALID_OBJECT error on the client's wl_display.
+ *
+ * \param sync The explicit synchronization related resource that is unusable.
+ * \param msg A custom error message attached to the protocol error.
+ */
+WL_EXPORT void
+linux_explicit_synchronization_send_server_error(struct wl_resource *resource,
+ const char *msg)
+{
+ uint32_t id = wl_resource_get_id(resource);
+ const char *class = wl_resource_get_class(resource);
+ struct wl_client *client = wl_resource_get_client(resource);
+ struct wl_resource *display_resource = wl_client_get_object(client, 1);
+
+ assert(display_resource);
+ wl_resource_post_error(display_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "linux_explicit_synchronization server error "
+ "with %s@%"PRIu32": %s",
+ class, id, msg);
+}
#define WESTON_LINUX_EXPLICIT_SYNCHRONIZATION_H
struct weston_compositor;
+struct wl_resource;
int
linux_explicit_synchronization_setup(struct weston_compositor *compositor);
+void
+linux_explicit_synchronization_send_server_error(struct wl_resource *resource,
+ const char *msg);
+
#endif /* WESTON_LINUX_EXPLICIT_SYNCHRONIZATION */
'input.c',
'linux-dmabuf.c',
'linux-explicit-synchronization.c',
+ 'linux-sync-file.c',
'log.c',
'noop-renderer.c',
'pixel-formats.c',
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef FD_UTIL_H
+#define FD_UTIL_H
+
+#include <unistd.h>
+
+static inline void
+fd_update(int *fd, int new_fd)
+{
+ if (*fd == new_fd)
+ return;
+ if (*fd >= 0)
+ close(*fd);
+ *fd = new_fd;
+}
+
+static inline void
+fd_move(int *dest, int *src)
+{
+ if (dest == src)
+ return;
+ fd_update(dest, *src);
+ *src = -1;
+}
+
+static inline void
+fd_clear(int *fd)
+{
+ fd_update(fd, -1);
+}
+
+#endif /* FD_UTIL_H */
#include "config.h"
#include <string.h>
+#include <unistd.h>
#include "linux-explicit-synchronization-unstable-v1-client-protocol.h"
#include "weston-test-client-helper.h"
zwp_linux_surface_synchronization_v1_destroy(surface_sync1);
zwp_linux_explicit_synchronization_v1_destroy(sync);
}
+
+TEST(set_acquire_fence_with_invalid_fence_raises_error)
+{
+ struct client *client = create_test_client();
+ struct zwp_linux_explicit_synchronization_v1 *sync =
+ get_linux_explicit_synchronization(client);
+ struct zwp_linux_surface_synchronization_v1 *surface_sync =
+ zwp_linux_explicit_synchronization_v1_get_synchronization(
+ sync, client->surface->wl_surface);
+ int pipefd[2] = { -1, -1 };
+
+ assert(pipe(pipefd) == 0);
+
+ zwp_linux_surface_synchronization_v1_set_acquire_fence(surface_sync,
+ pipefd[0]);
+ expect_protocol_error(
+ client,
+ &zwp_linux_surface_synchronization_v1_interface,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE);
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+ zwp_linux_surface_synchronization_v1_destroy(surface_sync);
+ zwp_linux_explicit_synchronization_v1_destroy(sync);
+}
+
+TEST(set_acquire_fence_on_destroyed_surface_raises_error)
+{
+ struct client *client = create_test_client();
+ struct zwp_linux_explicit_synchronization_v1 *sync =
+ get_linux_explicit_synchronization(client);
+ struct zwp_linux_surface_synchronization_v1 *surface_sync =
+ zwp_linux_explicit_synchronization_v1_get_synchronization(
+ sync, client->surface->wl_surface);
+ int pipefd[2] = { -1, -1 };
+
+ assert(pipe(pipefd) == 0);
+
+ wl_surface_destroy(client->surface->wl_surface);
+ zwp_linux_surface_synchronization_v1_set_acquire_fence(surface_sync,
+ pipefd[0]);
+ expect_protocol_error(
+ client,
+ &zwp_linux_surface_synchronization_v1_interface,
+ ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE);
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+ zwp_linux_surface_synchronization_v1_destroy(surface_sync);
+ zwp_linux_explicit_synchronization_v1_destroy(sync);
+}