diff --git a/Makefile.am b/Makefile.am index 00e298d6..3e95631a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -96,10 +96,13 @@ libweston_@LIBWESTON_MAJOR@_la_SOURCES = \ 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 \ diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index f19006f3..aa2b0bbd 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -66,6 +66,7 @@ #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 @@ -171,6 +172,7 @@ enum wdrm_plane_property { WDRM_PLANE_FB_ID, WDRM_PLANE_CRTC_ID, WDRM_PLANE_IN_FORMATS, + WDRM_PLANE_IN_FENCE_FD, WDRM_PLANE__COUNT }; @@ -213,6 +215,7 @@ static const struct drm_property_info plane_props[] = { [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" }, }; /** @@ -456,6 +459,9 @@ struct drm_plane_state { 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 */ }; @@ -1381,6 +1387,7 @@ drm_plane_state_alloc(struct drm_output_state *state_output, 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 @@ -1411,6 +1418,7 @@ drm_plane_state_free(struct drm_plane_state *state, bool force) 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); @@ -2010,6 +2018,13 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, 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: " @@ -2049,6 +2064,8 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, 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; @@ -2293,6 +2310,8 @@ drm_output_apply_state_legacy(struct drm_output_state *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 || @@ -2351,6 +2370,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 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; @@ -2568,6 +2589,12 @@ drm_output_apply_state_atomic(struct drm_output_state *state, (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; @@ -3262,12 +3289,27 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state, 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) { @@ -7014,11 +7056,14 @@ switch_to_gl_renderer(struct drm_backend *b) { 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"); @@ -7051,6 +7096,13 @@ switch_to_gl_renderer(struct drm_backend *b) 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 @@ -7482,6 +7534,12 @@ drm_backend_create(struct weston_compositor *compositor, "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)); diff --git a/libweston/compositor-x11.c b/libweston/compositor-x11.c index 4a9d068f..922e3c80 100644 --- a/libweston/compositor-x11.c +++ b/libweston/compositor-x11.c @@ -62,6 +62,7 @@ #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 @@ -1895,6 +1896,12 @@ x11_backend_create(struct weston_compositor *compositor, "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)); diff --git a/libweston/compositor.c b/libweston/compositor.c index 14033cbd..ce4f51ea 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -51,6 +51,7 @@ #include #include #include +#include #include "timeline.h" @@ -59,6 +60,8 @@ #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" @@ -448,6 +451,8 @@ weston_surface_state_init(struct weston_surface_state *state) 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 @@ -469,6 +474,8 @@ weston_surface_state_fini(struct weston_surface_state *state) if (state->buffer) wl_list_remove(&state->buffer_destroy_listener.link); state->buffer = NULL; + + fd_clear(&state->acquire_fence_fd); } static void @@ -525,6 +532,8 @@ weston_surface_create(struct weston_compositor *compositor) wl_list_init(&surface->pointer_constraints); + surface->acquire_fence_fd = -1; + return surface; } @@ -2017,6 +2026,8 @@ weston_surface_destroy(struct weston_surface *surface) link) weston_pointer_constraint_destroy(constraint); + fd_clear(&surface->acquire_fence_fd); + free(surface); } @@ -3216,9 +3227,15 @@ weston_surface_commit_state(struct weston_surface *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); @@ -3329,6 +3346,35 @@ surface_commit(struct wl_client *client, struct wl_resource *resource) 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; @@ -3535,7 +3581,11 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) 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; diff --git a/libweston/compositor.h b/libweston/compositor.h index 3f2ad01e..c58620fa 100644 --- a/libweston/compositor.h +++ b/libweston/compositor.h @@ -942,6 +942,9 @@ enum weston_capability { /* 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. @@ -1362,6 +1365,9 @@ struct weston_surface_state { /* 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 { @@ -1489,6 +1495,7 @@ struct weston_surface { /* zwp_surface_synchronization_v1 resource for this surface */ struct wl_resource *synchronization_resource; + int acquire_fence_fd; }; struct weston_subsurface { diff --git a/libweston/gl-renderer.c b/libweston/gl-renderer.c index 1a7577e3..4f65f956 100644 --- a/libweston/gl-renderer.c +++ b/libweston/gl-renderer.c @@ -49,6 +49,7 @@ #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" @@ -258,6 +259,9 @@ struct gl_renderer { PFNEGLCREATESYNCKHRPROC create_sync; PFNEGLDESTROYSYNCKHRPROC destroy_sync; PFNEGLDUPNATIVEFENCEFDANDROIDPROC dup_native_fence_fd; + + int has_wait_sync; + PFNEGLWAITSYNCKHRPROC wait_sync; }; enum timeline_render_point_type { @@ -861,6 +865,72 @@ shader_uniforms(struct gl_shader *shader, 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 */ @@ -891,6 +961,9 @@ draw_view(struct weston_view *ev, struct weston_output *output, 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) { @@ -3288,8 +3361,17 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec) (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); @@ -3521,13 +3603,16 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, } 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; diff --git a/libweston/linux-explicit-synchronization.c b/libweston/linux-explicit-synchronization.c index c42b8aa3..f577a189 100644 --- a/libweston/linux-explicit-synchronization.c +++ b/libweston/linux-explicit-synchronization.c @@ -25,11 +25,14 @@ #include "config.h" +#include #include #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) @@ -37,8 +40,10 @@ 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 @@ -53,7 +58,38 @@ linux_surface_synchronization_set_acquire_fence(struct wl_client *client, 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 @@ -162,3 +198,35 @@ linux_explicit_synchronization_setup(struct weston_compositor *compositor) 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); +} diff --git a/libweston/linux-explicit-synchronization.h b/libweston/linux-explicit-synchronization.h index 96edbbb6..55022879 100644 --- a/libweston/linux-explicit-synchronization.h +++ b/libweston/linux-explicit-synchronization.h @@ -27,8 +27,13 @@ #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 */ diff --git a/libweston/meson.build b/libweston/meson.build index 63e75ea6..f6e2a0fd 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -17,6 +17,7 @@ srcs_libweston = [ 'input.c', 'linux-dmabuf.c', 'linux-explicit-synchronization.c', + 'linux-sync-file.c', 'log.c', 'noop-renderer.c', 'pixel-formats.c', diff --git a/shared/fd-util.h b/shared/fd-util.h new file mode 100644 index 00000000..da4ef6fd --- /dev/null +++ b/shared/fd-util.h @@ -0,0 +1,56 @@ +/* + * 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 + +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 */ diff --git a/tests/linux-explicit-synchronization-test.c b/tests/linux-explicit-synchronization-test.c index 62a54e0d..9b74564c 100644 --- a/tests/linux-explicit-synchronization-test.c +++ b/tests/linux-explicit-synchronization-test.c @@ -26,6 +26,7 @@ #include "config.h" #include +#include #include "linux-explicit-synchronization-unstable-v1-client-protocol.h" #include "weston-test-client-helper.h" @@ -94,3 +95,54 @@ TEST(second_surface_synchronization_on_surface_raises_error) 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); +}