/* * Copyright 2025 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. */ #include "config.h" #include #include #include #include "libweston-internal.h" #include "shared/helpers.h" #include "shared/xalloc.h" struct weston_fifo { struct weston_surface *surface; struct wl_listener surface_destroy_listener; }; static void fifo_destructor(struct wl_resource *resource) { struct weston_fifo *fifo = wl_resource_get_user_data(resource); if (fifo->surface) wl_list_remove(&fifo->surface_destroy_listener.link); free(fifo); } static void fifo_set_barrier(struct wl_client *client, struct wl_resource *resource) { struct weston_fifo *fifo = wl_resource_get_user_data(resource); struct weston_surface *surface = fifo->surface; if (!surface) { wl_resource_post_error(resource, WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "surface destroyed"); return; } surface->pending.fifo_barrier = true; } static void fifo_wait_barrier(struct wl_client *client, struct wl_resource *resource) { struct weston_fifo *fifo = wl_resource_get_user_data(resource); struct weston_surface *surface = fifo->surface; if (!surface) { wl_resource_post_error(resource, WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "surface destroyed"); return; } surface->pending.fifo_wait = true; } static void fifo_destroy(struct wl_client *client, struct wl_resource *resource) { struct weston_fifo *fifo = wl_resource_get_user_data(resource); struct weston_surface *surface = fifo->surface; wl_resource_destroy(resource); if (!surface) return; surface->fifo = NULL; } static const struct wp_fifo_v1_interface weston_fifo_interface = { .set_barrier = fifo_set_barrier, .wait_barrier = fifo_wait_barrier, .destroy = fifo_destroy, }; static void fifo_manager_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void fifo_surface_destroy_cb(struct wl_listener *listener, void *data) { struct weston_fifo *fifo = container_of(listener, struct weston_fifo, surface_destroy_listener); fifo->surface = NULL; } static void fifo_manager_get_fifo(struct wl_client *client, struct wl_resource *fm_resource, uint32_t id, struct wl_resource *surface_resource) { struct weston_fifo *fifo; struct weston_surface *surface = wl_resource_get_user_data(surface_resource); struct wl_resource *res; if (surface->fifo) { wl_resource_post_error(fm_resource, WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, "Fifo resource already exists on surface"); return; } res = wl_resource_create(client, &wp_fifo_v1_interface, wl_resource_get_version(fm_resource), id); fifo = xzalloc(sizeof *fifo); fifo->surface = surface; fifo->surface_destroy_listener.notify = fifo_surface_destroy_cb; wl_signal_add(&surface->destroy_signal, &fifo->surface_destroy_listener); wl_resource_set_implementation(res, &weston_fifo_interface, fifo, fifo_destructor); surface->fifo = fifo; } static const struct wp_fifo_manager_v1_interface fifo_manager_interface_v1 = { .destroy = fifo_manager_destroy, .get_fifo = fifo_manager_get_fifo, }; static void bind_fifo_manager(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; struct weston_compositor *compositor = data; resource = wl_resource_create(client, &wp_fifo_manager_v1_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &fifo_manager_interface_v1, compositor, NULL); } /** Advertise fifo protocol support * * Sets up fifo_v1 support so it is advertiszed to clients. * * \param compositor The compositor to init for. * \return Zero on success, -1 on failure. */ int fifo_setup(struct weston_compositor *compositor) { if (!wl_global_create(compositor->wl_display, &wp_fifo_manager_v1_interface, 1, compositor, bind_fifo_manager)) return -1; return 0; } static void weston_fifo_surface_clear_barrier(struct weston_surface *surface) { surface->fifo_barrier = false; wl_list_remove(&surface->fifo_barrier_link); wl_list_init(&surface->fifo_barrier_link); } void weston_fifo_surface_set_barrier(struct weston_surface *surface) { /* If nothing is waiting on barriers, we could set multiple times * before a repaint occurs. * * Theoretically, this surface could have a different primary * output than the last time a barrier was created, so let's * just blow away any old barrier (should one exist) before * setting the current one. */ weston_fifo_surface_clear_barrier(surface); /* If the surface isn't associated with an output, we have no way * to clear a barrier - so just don't set one. */ if (!surface->output) return; surface->fifo_barrier = true; wl_list_insert(&surface->output->fifo_barrier_surfaces, &surface->fifo_barrier_link); } void weston_fifo_output_clear_barriers(struct weston_output *output) { struct weston_surface *surf, *tmp; wl_list_for_each_safe(surf, tmp, &output->fifo_barrier_surfaces, fifo_barrier_link) weston_fifo_surface_clear_barrier(surf); } bool weston_fifo_output_has_barriers(struct weston_output *output) { return !wl_list_empty(&output->fifo_barrier_surfaces); } bool weston_fifo_surface_state_ready(struct weston_surface *surface, struct weston_surface_state *state) { struct weston_subsurface *sub = weston_surface_to_subsurface(surface); bool e_sync = sub && sub->effectively_synchronized; if (!state->fifo_wait) return true; /* The barrier is clear. */ if (!surface->fifo_barrier) return true; /* Effectively synchronized surfaces ignore fifo */ if (e_sync) return true; /* If there's no driving output, fifo will never clear, * so just ignore the condition. */ if (!surface->output) return true; /* Occluded surfaces ignore fifo */ if (!weston_surface_visibility_mask(surface)) return true; return false; }