From 4c7b4a2c5a7a83172119eab0755d6db64c3f7b45 Mon Sep 17 00:00:00 2001 From: hujianwei Date: Sat, 19 Nov 2022 06:28:50 +0000 Subject: [PATCH] Check in new files for tearing control protocol * tearing-control-v1.xml: * tearing_control.c: * tests/tearing_control_test.c: New files. --- tearing-control-v1.xml | 120 +++++++++++++++++ tearing_control.c | 220 ++++++++++++++++++++++++++++++++ tests/tearing_control_test.c | 241 +++++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 tearing-control-v1.xml create mode 100644 tearing_control.c create mode 100644 tests/tearing_control_test.c diff --git a/tearing-control-v1.xml b/tearing-control-v1.xml new file mode 100644 index 0000000..e38056d --- /dev/null +++ b/tearing-control-v1.xml @@ -0,0 +1,120 @@ + + + + Copyright © 2021 Xaver Hugl + + 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. + + + + + For some use cases like games or drawing tablets it can make sense to + reduce latency by accepting tearing with the use of asynchronous page + flips. This global is a factory interface, allowing clients to inform + which type of presentation the content of their surfaces is suitable for. + + 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 + tearing_control_exists protocol error. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + Destroy this tearing control factory object. Other objects, including + wp_tearing_control_v1 objects created by this factory, are not affected + by this request. + + + + + + + + + + Instantiate an interface extension for the given wl_surface to request + asynchronous page flips for presentation. + + If the given wl_surface already has a wp_tearing_control_v1 object + associated, the tearing_control_exists protocol error is raised. + + + + + + + + + An additional interface to a wl_surface object, which allows the client + to hint to the compositor if the content on the surface is suitable for + presentation with tearing. + The default presentation hint is vsync. See presentation_hint for more + details. + + + + + This enum provides information for if submitted frames from the client + may be presented with tearing. + + + + The content of this surface is meant to be synchronized to the + vertical blanking period. This should not result in visible tearing + and may result in a delay before a surface commit is presented. + + + + + The content of this surface is meant to be presented with minimal + latency and tearing is acceptable. + + + + + + + Set the presentation hint for the associated wl_surface. This state is + double-buffered and is applied on the next wl_surface.commit. + + The compositor is free to dynamically respect or ignore this hint based + on various conditions like hardware capabilities, surface state and + user preferences. + + + + + + + Destroy this surface tearing object and revert the presentation hint to + vsync. The change will be applied on the next wl_surface.commit. + + + + + diff --git a/tearing_control.c b/tearing_control.c new file mode 100644 index 0000000..48e6cc3 --- /dev/null +++ b/tearing_control.c @@ -0,0 +1,220 @@ +/* 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 "tearing-control-v1.h" + +typedef struct _TearingControl TearingControl; + +struct _TearingControl +{ + /* The associated surface. NULL when detached. */ + Surface *surface; + + /* The associated resource. */ + struct wl_resource *resource; +}; + +/* The tearing control manager. */ +static struct wl_global *tearing_control_manager_global; + + + +static void +DestroyTearingControl (struct wl_client *client, struct wl_resource *resource) +{ + TearingControl *control; + + control = wl_resource_get_user_data (resource); + + if (control->surface) + { + /* Reset the presentation hint. */ + control->surface->pending_state.presentation_hint + = PresentationHintVsync; + control->surface->pending_state.pending + |= PendingPresentationHint; + } + + wl_resource_destroy (resource); +} + +static void +SetPresentationHint (struct wl_client *client, struct wl_resource *resource, + uint32_t hint) +{ + TearingControl *control; + + control = wl_resource_get_user_data (resource); + + if (control->surface) + { + switch (hint) + { + case WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC: + control->surface->pending_state.presentation_hint + = PresentationHintAsync; + break; + + default: + control->surface->pending_state.presentation_hint + = PresentationHintVsync; + break; + } + + control->surface->pending_state.pending |= PendingPresentationHint; + } +} + +static const struct wp_tearing_control_v1_interface control_impl = + { + .destroy = DestroyTearingControl, + .set_presentation_hint = SetPresentationHint, + }; + +static void +HandleResourceDestroy (struct wl_resource *resource) +{ + TearingControl *control, **reference; + + control = wl_resource_get_user_data (resource); + + /* If the surface is still attached to the tearing control, remove + it from the surface. */ + + if (control->surface) + { + reference + = XLSurfaceFindClientData (control->surface, + TearingControlData); + XLAssert (reference != NULL); + + *reference = NULL; + } + + XLFree (control); +} + + + +static void +FreeTearingControlData (void *data) +{ + TearingControl **control; + + control = data; + + if (!*control) + return; + + /* Detach the surface from the tearing control. */ + (*control)->surface = NULL; +} + +static void +Destroy (struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +GetTearingControl (struct wl_client *client, struct wl_resource *resource, + uint32_t id, struct wl_resource *surface_resource) +{ + Surface *surface; + TearingControl **control; + + surface = wl_resource_get_user_data (surface_resource); + control = XLSurfaceGetClientData (surface, TearingControlData, + sizeof *control, + FreeTearingControlData); + +#define ControlExists \ + WP_TEARING_CONTROL_MANAGER_V1_ERROR_TEARING_CONTROL_EXISTS + + if (*control) + { + /* A tearing control resource already exists for this + surface. */ + wl_resource_post_error (resource, ControlExists, + "a wp_tearing_control_v1 resource already exists" + " for the specified surface"); + return; + } + +#undef ControlExists + + (*control) = XLCalloc (1, sizeof **control); + (*control)->resource + = wl_resource_create (client, + &wp_tearing_control_v1_interface, + wl_resource_get_version (resource), id); + + if (!(*control)->resource) + { + XLFree (*control); + (*control) = NULL; + + wl_resource_post_no_memory (resource); + return; + } + + (*control)->surface = surface; + wl_resource_set_implementation ((*control)->resource, &control_impl, + (*control), HandleResourceDestroy); +} + +static const struct wp_tearing_control_manager_v1_interface manager_impl = + { + .destroy = Destroy, + .get_tearing_control = GetTearingControl, + }; + + + +static void +HandleBind (struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &wp_tearing_control_manager_v1_interface, + version, id); + + if (!resource) + { + wl_client_post_no_memory (client); + return; + } + + wl_resource_set_implementation (resource, &manager_impl, + NULL, NULL); +} + +void +XLInitTearingControl (void) +{ + tearing_control_manager_global + = wl_global_create (compositor.wl_display, + &wp_tearing_control_manager_v1_interface, + 1, NULL, HandleBind); +} diff --git a/tests/tearing_control_test.c b/tests/tearing_control_test.c new file mode 100644 index 0000000..e897d8f --- /dev/null +++ b/tests/tearing_control_test.c @@ -0,0 +1,241 @@ +/* Tests for the Wayland compositor running on the 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 "test_harness.h" +#include "tearing-control-v1.h" + +/* Tests for buffer release. */ + +enum test_kind + { + TEARING_CONTROL_KIND, + TEARING_DESTROY_KIND, + }; + +static const char *test_names[] = + { + "tearing_control", + "tearing_destroy", + }; + +#define LAST_TEST TEARING_CONTROL_KIND + +/* The display. */ +static struct test_display *display; + +/* The tearing control manager. */ +static struct wp_tearing_control_manager_v1 *manager; + +/* Test interfaces. */ +static struct test_interface test_interfaces[] = + { + { "wp_tearing_control_manager_v1", &manager, + &wp_tearing_control_manager_v1_interface, 1, }, + }; + +/* The test surface and Wayland surface. */ +static struct test_surface *test_surface; +static struct wl_surface *wayland_surface; + +/* The tearing control. */ +static struct wp_tearing_control_v1 *tearing_control; + +/* The presentation hint used. 1 means async, and 0 means vsync. */ +static int used_presentation_mode; + + + +/* Forward declarations. */ +static void verify_async_used (void); +static void verify_vsync_used (void); + + + +static struct test_buffer * +make_test_buffer (void) +{ + struct wl_buffer *buffer; + struct test_buffer *test_buffer; + char *empty_data; + size_t stride; + + stride = get_image_stride (display, 24, 1); + + if (!stride) + report_test_failure ("unknown stride"); + + empty_data = calloc (1, stride); + + if (!empty_data) + report_test_failure ("failed to allocate buffer data"); + + buffer = upload_image_data (display, empty_data, 1, 1, 24); + free (empty_data); + + if (!buffer) + report_test_failure ("failed to create single pixel buffer"); + + test_buffer = get_test_buffer (display, buffer); + + if (!test_buffer) + report_test_failure ("failed to create test buffer"); + + return test_buffer; +} + +static void +test_single_step (enum test_kind kind) +{ + struct test_buffer *buffer; + + again: + + test_log ("running test step: %s", test_names[kind]); + + switch (kind) + { + case TEARING_CONTROL_KIND: +#define VSYNC WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC + wp_tearing_control_v1_set_presentation_hint (tearing_control, + VSYNC); +#undef VSYNC + buffer = make_test_buffer (); + + /* Attach the buffer. */ + wl_surface_attach (wayland_surface, buffer->buffer, 0, 0); + wl_surface_commit (wayland_surface); + + /* Now see what kind of presentation was used. */ + verify_vsync_used (); + +#define ASYNC WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC + wp_tearing_control_v1_set_presentation_hint (tearing_control, + ASYNC); +#undef ASYNC + wl_surface_commit (wayland_surface); + + /* Now verify that async presentation was used. */ + verify_async_used (); + + kind = TEARING_DESTROY_KIND; + goto again; + + case TEARING_DESTROY_KIND: + /* Destroy the tearing control resource. */ + wp_tearing_control_v1_destroy (tearing_control); + wl_surface_commit (wayland_surface); + + /* Verify that the tearing hint reverted to vsync. */ + verify_vsync_used (); + break; + } + + if (kind == LAST_TEST) + test_complete (); +} + + + +static void +handle_test_surface_mapped (void *data, struct test_surface *test_surface, + uint32_t xid, const char *display_string) +{ + +} + +static void +handle_test_surface_activated (void *data, struct test_surface *test_surface, + uint32_t months, uint32_t milliseconds, + struct wl_surface *activator_surface) +{ + +} + +static void +handle_test_surface_committed (void *data, struct test_surface *test_surface, + uint32_t presentation_hint) +{ + used_presentation_mode = presentation_hint; +} + +static const struct test_surface_listener test_surface_listener = + { + handle_test_surface_mapped, + handle_test_surface_activated, + handle_test_surface_committed, + }; + +static void +verify_async_used (void) +{ + wl_display_roundtrip (display->display); + + if (used_presentation_mode != 1) + report_test_failure ("async presentation not used where expected!"); +} + +static void +verify_vsync_used (void) +{ + wl_display_roundtrip (display->display); + + if (used_presentation_mode == 1) + report_test_failure ("vsync presentation not used where expected!"); +} + + + +static void +run_test (void) +{ + if (!make_test_surface (display, &wayland_surface, + &test_surface)) + report_test_failure ("failed to create test surface"); + + test_surface_add_listener (test_surface, &test_surface_listener, + NULL); + + tearing_control + = wp_tearing_control_manager_v1_get_tearing_control (manager, + wayland_surface); + + if (!tearing_control) + report_test_failure ("failed to create tearing control"); + + test_single_step (TEARING_CONTROL_KIND); + + while (true) + { + if (wl_display_dispatch (display->display) == -1) + die ("wl_display_dispatch"); + } +} + +int +main (void) +{ + test_init (); + display = open_test_display (test_interfaces, + ARRAYELTS (test_interfaces)); + + if (!display) + report_test_failure ("failed to open display"); + + run_test (); +}