Add support for tearing-control-v1

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3871

Adds option to allow tearing per output, as well as an option to force
enable or disable tearing for a specific application using a window
rule. Only works with fullscreen applications.
This commit is contained in:
Ricardo Steijn 2024-08-05 02:13:49 +02:00 committed by GitHub
parent b881c2e84c
commit 9a1c411abd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 243 additions and 3 deletions

View file

@ -104,6 +104,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
sway_cmd cmd_exec_validate; sway_cmd cmd_exec_validate;
sway_cmd cmd_exec_process; sway_cmd cmd_exec_process;
sway_cmd cmd_allow_tearing;
sway_cmd cmd_assign; sway_cmd cmd_assign;
sway_cmd cmd_bar; sway_cmd cmd_bar;
sway_cmd cmd_bindcode; sway_cmd cmd_bindcode;
@ -283,6 +284,7 @@ sway_cmd input_cmd_xkb_switch_layout;
sway_cmd input_cmd_xkb_variant; sway_cmd input_cmd_xkb_variant;
sway_cmd output_cmd_adaptive_sync; sway_cmd output_cmd_adaptive_sync;
sway_cmd output_cmd_allow_tearing;
sway_cmd output_cmd_background; sway_cmd output_cmd_background;
sway_cmd output_cmd_color_profile; sway_cmd output_cmd_color_profile;
sway_cmd output_cmd_disable; sway_cmd output_cmd_disable;

View file

@ -289,6 +289,7 @@ struct output_config {
enum render_bit_depth render_bit_depth; enum render_bit_depth render_bit_depth;
bool set_color_transform; bool set_color_transform;
struct wlr_color_transform *color_transform; struct wlr_color_transform *color_transform;
int allow_tearing;
char *background; char *background;
char *background_option; char *background_option;

View file

@ -73,6 +73,7 @@ struct sway_output {
int max_render_time; // In milliseconds int max_render_time; // In milliseconds
struct wl_event_source *repaint_timer; struct wl_event_source *repaint_timer;
bool gamma_lut_changed; bool gamma_lut_changed;
bool allow_tearing;
}; };
struct sway_output_non_desktop { struct sway_output_non_desktop {

View file

@ -116,6 +116,10 @@ struct sway_server {
struct wl_listener request_set_cursor_shape; struct wl_listener request_set_cursor_shape;
struct wlr_tearing_control_manager_v1 *tearing_control_v1;
struct wl_listener tearing_control_new_object;
struct wl_list tearing_controllers; // sway_tearing_controller::link
struct wl_list pending_launcher_ctxs; // launcher_ctx::link struct wl_list pending_launcher_ctxs; // launcher_ctx::link
// The timeout for transactions, after which a transaction is applied // The timeout for transactions, after which a transaction is applied
@ -182,4 +186,6 @@ void xdg_activation_v1_handle_new_token(struct wl_listener *listener,
void set_rr_scheduling(void); void set_rr_scheduling(void);
void handle_new_tearing_hint(struct wl_listener *listener, void *data);
#endif #endif

View file

@ -4,6 +4,7 @@
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_tearing_control_v1.h>
#include "sway/config.h" #include "sway/config.h"
#if WLR_HAS_XWAYLAND #if WLR_HAS_XWAYLAND
#include <wlr/xwayland.h> #include <wlr/xwayland.h>
@ -34,6 +35,12 @@ enum sway_view_prop {
#endif #endif
}; };
enum sway_view_tearing_mode {
TEARING_OVERRIDE_FALSE,
TEARING_OVERRIDE_TRUE,
TEARING_WINDOW_HINT,
};
struct sway_view_impl { struct sway_view_impl {
void (*get_constraints)(struct sway_view *view, double *min_width, void (*get_constraints)(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height); double *max_width, double *min_height, double *max_height);
@ -111,6 +118,9 @@ struct sway_view {
int max_render_time; // In milliseconds int max_render_time; // In milliseconds
enum seat_config_shortcuts_inhibit shortcuts_inhibit; enum seat_config_shortcuts_inhibit shortcuts_inhibit;
enum sway_view_tearing_mode tearing_mode;
enum wp_tearing_control_v1_presentation_hint tearing_hint;
}; };
struct sway_xdg_shell_view { struct sway_xdg_shell_view {
@ -335,4 +345,6 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
void view_send_frame_done(struct sway_view *view); void view_send_frame_done(struct sway_view *view);
bool view_can_tear(struct sway_view *view);
#endif #endif

View file

@ -14,6 +14,7 @@ protocols = [
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
'wlr-layer-shell-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml',
'idle.xml', 'idle.xml',
'wlr-output-power-management-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml',

View file

@ -112,6 +112,7 @@ static const struct cmd_handler config_handlers[] = {
/* Runtime-only commands. Keep alphabetized */ /* Runtime-only commands. Keep alphabetized */
static const struct cmd_handler command_handlers[] = { static const struct cmd_handler command_handlers[] = {
{ "allow_tearing", cmd_allow_tearing },
{ "border", cmd_border }, { "border", cmd_border },
{ "create_output", cmd_create_output }, { "create_output", cmd_create_output },
{ "exit", cmd_exit }, { "exit", cmd_exit },

View file

@ -0,0 +1,24 @@
#include <sway/commands.h>
#include "sway/config.h"
#include "sway/tree/view.h"
#include "util.h"
struct cmd_results *cmd_allow_tearing(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) {
return error;
}
struct sway_container *container = config->handler_context.container;
if (!container || !container->view) {
return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views");
}
bool wants_tearing = parse_boolean(argv[0], true);
struct sway_view *view = container->view;
view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE :
TEARING_OVERRIDE_FALSE;
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -8,6 +8,7 @@
// must be in order for the bsearch // must be in order for the bsearch
static const struct cmd_handler output_handlers[] = { static const struct cmd_handler output_handlers[] = {
{ "adaptive_sync", output_cmd_adaptive_sync }, { "adaptive_sync", output_cmd_adaptive_sync },
{ "allow_tearing", output_cmd_allow_tearing },
{ "background", output_cmd_background }, { "background", output_cmd_background },
{ "bg", output_cmd_background }, { "bg", output_cmd_background },
{ "color_profile", output_cmd_color_profile }, { "color_profile", output_cmd_color_profile },

View file

@ -0,0 +1,23 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "util.h"
struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
if (argc == 0) {
return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument");
}
if (parse_boolean(argv[0],
(config->handler_context.output_config->allow_tearing == 1))) {
config->handler_context.output_config->allow_tearing = 1;
} else {
config->handler_context.output_config->allow_tearing = 0;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
}

View file

@ -79,6 +79,7 @@ struct output_config *new_output_config(const char *name) {
oc->set_color_transform = false; oc->set_color_transform = false;
oc->color_transform = NULL; oc->color_transform = NULL;
oc->power = -1; oc->power = -1;
oc->allow_tearing = -1;
return oc; return oc;
} }
@ -216,6 +217,9 @@ static void merge_output_config(struct output_config *dst, struct output_config
if (src->power != -1) { if (src->power != -1) {
dst->power = src->power; dst->power = src->power;
} }
if (src->allow_tearing != -1) {
dst->allow_tearing = src->allow_tearing;
}
} }
void store_output_config(struct output_config *oc) { void store_output_config(struct output_config *oc) {
@ -258,11 +262,11 @@ void store_output_config(struct output_config *oc) {
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
"(max render time: %d)", "(max render time: %d) (allow tearing: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->power, oc->transform, oc->background, oc->background_option, oc->power,
oc->max_render_time); oc->max_render_time, oc->allow_tearing);
// If the configuration was not merged into an existing configuration, add // If the configuration was not merged into an existing configuration, add
// it to the list. Otherwise we're done with it and can free it. // it to the list. Otherwise we're done with it and can free it.
@ -574,6 +578,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
wlr_color_transform_unref(output->color_transform); wlr_color_transform_unref(output->color_transform);
output->color_transform = oc->color_transform; output->color_transform = oc->color_transform;
} }
if (oc && oc->allow_tearing >= 0) {
sway_log(SWAY_DEBUG, "Set %s allow tearing to %d",
oc->name, oc->allow_tearing);
output->allow_tearing = oc->allow_tearing;
}
return true; return true;
} }
@ -594,6 +605,7 @@ static void default_output_config(struct output_config *oc,
oc->subpixel = output->detected_subpixel; oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->max_render_time = 0; oc->max_render_time = 0;
oc->allow_tearing = 0;
} }
// find_output_config returns a merged output_config containing all stored // find_output_config returns a merged output_config containing all stored

View file

@ -232,6 +232,23 @@ static void output_configure_scene(struct sway_output *output,
} }
} }
static bool output_can_tear(struct sway_output *output) {
struct sway_workspace *workspace = output->current.active_workspace;
if (!workspace) {
return false;
}
struct sway_container *fullscreen_con = root->fullscreen_global;
if (!fullscreen_con) {
fullscreen_con = workspace->current.fullscreen;
}
if (fullscreen_con && fullscreen_con->view) {
return (output->allow_tearing && view_can_tear(fullscreen_con->view));
}
return false;
}
static int output_repaint_timer_handler(void *data) { static int output_repaint_timer_handler(void *data) {
struct sway_output *output = data; struct sway_output *output = data;
@ -276,6 +293,17 @@ static int output_repaint_timer_handler(void *data) {
} }
} }
if (output_can_tear(output)) {
pending.tearing_page_flip = true;
if (!wlr_output_test_state(output->wlr_output, &pending)) {
sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip",
output->wlr_output->name);
pending.tearing_page_flip = false;
}
}
if (!wlr_output_commit_state(output->wlr_output, &pending)) { if (!wlr_output_commit_state(output->wlr_output, &pending)) {
sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name);
} }

62
sway/desktop/tearing.c Normal file
View file

@ -0,0 +1,62 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_tearing_control_v1.h>
#include "sway/server.h"
#include "sway/tree/view.h"
#include "sway/output.h"
#include "log.h"
struct sway_tearing_controller {
struct wlr_tearing_control_v1 *tearing_control;
struct wl_listener set_hint;
struct wl_listener destroy;
struct wl_list link; // sway_server::tearing_controllers
};
static void handle_tearing_controller_set_hint(struct wl_listener *listener,
void *data) {
struct sway_tearing_controller *controller =
wl_container_of(listener, controller, set_hint);
struct sway_view *view = view_from_wlr_surface(
controller->tearing_control->surface);
if (view) {
view->tearing_hint = controller->tearing_control->current;
}
}
static void handle_tearing_controller_destroy(struct wl_listener *listener,
void *data) {
struct sway_tearing_controller *controller =
wl_container_of(listener, controller, destroy);
wl_list_remove(&controller->link);
free(controller);
}
void handle_new_tearing_hint(struct wl_listener *listener,
void *data) {
struct sway_server *server =
wl_container_of(listener, server, tearing_control_new_object);
struct wlr_tearing_control_v1 *tearing_control = data;
enum wp_tearing_control_v1_presentation_hint hint =
wlr_tearing_control_manager_v1_surface_hint_from_surface(
server->tearing_control_v1, tearing_control->surface);
sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p",
hint, tearing_control->surface);
struct sway_tearing_controller *controller =
calloc(1, sizeof(struct sway_tearing_controller));
if (!controller) {
return;
}
controller->tearing_control = tearing_control;
controller->set_hint.notify = handle_tearing_controller_set_hint;
wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
controller->destroy.notify = handle_tearing_controller_destroy;
wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
wl_list_init(&controller->link);
wl_list_insert(&server->tearing_controllers, &controller->link);
}

View file

@ -399,6 +399,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
} }
json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time)); json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time));
json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing));
} }
json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
@ -593,6 +595,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time)); json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time));
json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view)));
json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view))); json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view)));
json_object_object_add(object, "inhibit_idle", json_object_object_add(object, "inhibit_idle",

View file

@ -18,6 +18,7 @@ sway_sources = files(
'desktop/idle_inhibit_v1.c', 'desktop/idle_inhibit_v1.c',
'desktop/layer_shell.c', 'desktop/layer_shell.c',
'desktop/output.c', 'desktop/output.c',
'desktop/tearing.c',
'desktop/transaction.c', 'desktop/transaction.c',
'desktop/xdg_shell.c', 'desktop/xdg_shell.c',
'desktop/launcher.c', 'desktop/launcher.c',
@ -41,6 +42,7 @@ sway_sources = files(
'config/seat.c', 'config/seat.c',
'config/input.c', 'config/input.c',
'commands/allow_tearing.c',
'commands/assign.c', 'commands/assign.c',
'commands/bar.c', 'commands/bar.c',
'commands/bind.c', 'commands/bind.c',
@ -188,6 +190,7 @@ sway_sources = files(
'commands/input/xkb_variant.c', 'commands/input/xkb_variant.c',
'commands/output/adaptive_sync.c', 'commands/output/adaptive_sync.c',
'commands/output/allow_tearing.c',
'commands/output/background.c', 'commands/output/background.c',
'commands/output/disable.c', 'commands/output/disable.c',
'commands/output/dpms.c', 'commands/output/dpms.c',

View file

@ -372,6 +372,13 @@ bool server_init(struct sway_server *server) {
wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_content_type_manager_v1_create(server->wl_display, 1);
wlr_fractional_scale_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
server->tearing_control_v1 =
wlr_tearing_control_manager_v1_create(server->wl_display, 1);
server->tearing_control_new_object.notify = handle_new_tearing_hint;
wl_signal_add(&server->tearing_control_v1->events.new_object,
&server->tearing_control_new_object);
wl_list_init(&server->tearing_controllers);
struct wlr_xdg_foreign_registry *foreign_registry = struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->wl_display); wlr_xdg_foreign_registry_create(server->wl_display);
wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);

View file

@ -190,6 +190,26 @@ must be separated by one space. For example:
may have no effect or produce unexpected output when used together with future may have no effect or produce unexpected output when used together with future
HDR support features. HDR support features.
*output* <name> allow_tearing yes|no
Allows or disallows screen tearing as a result of immediate page flips,
and an immediate presentation mode from a client. The default is that no
screen tearing is allowed.
With immediate page flips, frames from the client are presented as soon
as possible instead of synchronizing with the monitor's vblank interval
(VSync).
It is recommended to set *max_render_time* to *off*. In that case a page flip
happens as soon as a client updates. Otherwise, tearing will only happen if
rendering takes longer than the configured milliseconds before the next
display refresh.
To adjust whether tearing is allowed for specific applications, see
*allow_tearing* in *sway*(5). Note that tearing will only be enabled
when it's allowed for both the output and the application.
This setting only has effect when a window is fullscreen on the output.
# SEE ALSO # SEE ALSO
*sway*(5) *sway-input*(5) *sway*(5) *sway-input*(5)

View file

@ -215,6 +215,20 @@ set|plus|minus|toggle <amount>
effect on the output the window is currently on. See *sway-output*(5) for effect on the output the window is currently on. See *sway-output*(5) for
further details. further details.
*allow_tearing* yes|no
Allows or disallows screen tearing as a result of immediate page flips
for a fullscreen application.
When this option is not set, the tearing hints provided by the application
determine whether tearing is allowed. When _yes_ is specified,
the application allows tearing regardless of the tearing hints.
When _no_ is specified, tearing will never be allowed on the application,
regardless of the tearing hints.
This setting only has an effect if tearing is allowed on the output through
the per-output *allow_tearing* setting. See *sway-output*(5)
for further details.
*move* left|right|up|down [<px> px] *move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. The optional _px_ Moves the focused container in the direction specified. The optional _px_
argument specifies how many pixels to move the container. If unspecified, argument specifies how many pixels to move the container. If unspecified,

View file

@ -58,6 +58,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type,
view->executed_criteria = create_list(); view->executed_criteria = create_list();
view->allow_request_urgent = true; view->allow_request_urgent = true;
view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
view->tearing_mode = TEARING_WINDOW_HINT;
wl_signal_init(&view->events.unmap); wl_signal_init(&view->events.unmap);
return true; return true;
} }
@ -1260,6 +1261,19 @@ bool view_is_transient_for(struct sway_view *child,
child->impl->is_transient_for(child, ancestor); child->impl->is_transient_for(child, ancestor);
} }
bool view_can_tear(struct sway_view *view) {
switch (view->tearing_mode) {
case TEARING_OVERRIDE_FALSE:
return false;
case TEARING_OVERRIDE_TRUE:
return true;
case TEARING_WINDOW_HINT:
return view->tearing_hint ==
WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
}
return false;
}
static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
int x, int y, void *data) { int x, int y, void *data) {
struct timespec *when = data; struct timespec *when = data;

View file

@ -193,7 +193,7 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "current_workspace", &ws); json_object_object_get_ex(o, "current_workspace", &ws);
json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object_object_get_ex(o, "non_desktop", &non_desktop);
json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
*transform, *max_render_time, *adaptive_sync_status; *transform, *max_render_time, *adaptive_sync_status, *allow_tearing;
json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "make", &make);
json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "model", &model);
json_object_object_get_ex(o, "serial", &serial); json_object_object_get_ex(o, "serial", &serial);
@ -203,6 +203,7 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "transform", &transform); json_object_object_get_ex(o, "transform", &transform);
json_object_object_get_ex(o, "max_render_time", &max_render_time); json_object_object_get_ex(o, "max_render_time", &max_render_time);
json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status); json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status);
json_object_object_get_ex(o, "allow_tearing", &allow_tearing);
json_object *x, *y; json_object *x, *y;
json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "x", &x);
json_object_object_get_ex(rect, "y", &y); json_object_object_get_ex(rect, "y", &y);
@ -256,6 +257,9 @@ static void pretty_print_output(json_object *o) {
printf(" Adaptive sync: %s\n", printf(" Adaptive sync: %s\n",
json_object_get_string(adaptive_sync_status)); json_object_get_string(adaptive_sync_status));
printf(" Allow tearing: %s\n",
json_object_get_boolean(allow_tearing) ? "yes" : "no");
} else { } else {
printf( printf(
"Output %s '%s %s %s' (disabled)\n", "Output %s '%s %s %s' (disabled)\n",