Check in new files for tearing control protocol

* tearing-control-v1.xml:
* tearing_control.c:
* tests/tearing_control_test.c: New files.
This commit is contained in:
hujianwei 2022-11-19 06:28:50 +00:00
parent 94333293c8
commit 4c7b4a2c5a
3 changed files with 581 additions and 0 deletions

120
tearing-control-v1.xml Normal file
View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="tearing_control_v1">
<copyright>
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.
</copyright>
<interface name="wp_tearing_control_manager_v1" version="1">
<description summary="protocol for tearing control">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy tearing control factory object">
Destroy this tearing control factory object. Other objects, including
wp_tearing_control_v1 objects created by this factory, are not affected
by this request.
</description>
</request>
<enum name="error">
<entry name="tearing_control_exists" value="0"
summary="the surface already has a tearing object associated"/>
</enum>
<request name="get_tearing_control">
<description summary="extend surface interface for tearing control">
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.
</description>
<arg name="id" type="new_id" interface="wp_tearing_control_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
</interface>
<interface name="wp_tearing_control_v1" version="1">
<description summary="per-surface tearing control interface">
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.
</description>
<enum name="presentation_hint">
<description summary="presentation hint values">
This enum provides information for if submitted frames from the client
may be presented with tearing.
</description>
<entry name="vsync" value="0">
<description summary="tearing-free presentation">
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.
</description>
</entry>
<entry name="async" value="1">
<description summary="asynchronous presentation">
The content of this surface is meant to be presented with minimal
latency and tearing is acceptable.
</description>
</entry>
</enum>
<request name="set_presentation_hint">
<description summary="set presentation hint">
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.
</description>
<arg name="hint" type="uint" enum="presentation_hint"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy tearing control object">
Destroy this surface tearing object and revert the presentation hint to
vsync. The change will be applied on the next wl_surface.commit.
</description>
</request>
</interface>
</protocol>

220
tearing_control.c Normal file
View file

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

View file

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