forked from 12to11/12to11
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:
parent
94333293c8
commit
4c7b4a2c5a
3 changed files with 581 additions and 0 deletions
120
tearing-control-v1.xml
Normal file
120
tearing-control-v1.xml
Normal 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
220
tearing_control.c
Normal 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);
|
||||
}
|
241
tests/tearing_control_test.c
Normal file
241
tests/tearing_control_test.c
Normal 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 ();
|
||||
}
|
Loading…
Add table
Reference in a new issue