mirror of
https://github.com/swaywm/sway.git
synced 2024-11-22 07:51:28 +00:00
view: add max_render_time
This commit is contained in:
parent
5421198489
commit
bd9a53f1a3
|
@ -145,6 +145,7 @@ sway_cmd cmd_kill;
|
||||||
sway_cmd cmd_layout;
|
sway_cmd cmd_layout;
|
||||||
sway_cmd cmd_log_colors;
|
sway_cmd cmd_log_colors;
|
||||||
sway_cmd cmd_mark;
|
sway_cmd cmd_mark;
|
||||||
|
sway_cmd cmd_max_render_time;
|
||||||
sway_cmd cmd_mode;
|
sway_cmd cmd_mode;
|
||||||
sway_cmd cmd_mouse_warping;
|
sway_cmd cmd_mouse_warping;
|
||||||
sway_cmd cmd_move;
|
sway_cmd cmd_move;
|
||||||
|
|
|
@ -72,7 +72,7 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
|
||||||
void output_add_workspace(struct sway_output *output,
|
void output_add_workspace(struct sway_output *output,
|
||||||
struct sway_workspace *workspace);
|
struct sway_workspace *workspace);
|
||||||
|
|
||||||
typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
|
typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,13 @@ struct sway_surface {
|
||||||
struct wlr_surface *wlr_surface;
|
struct wlr_surface *wlr_surface;
|
||||||
|
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This timer can be used for issuing delayed frame done callbacks (for
|
||||||
|
* example, to improve presentation latency). Its handler is set to a
|
||||||
|
* function that issues a frame done callback to this surface.
|
||||||
|
*/
|
||||||
|
struct wl_event_source *frame_done_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -108,6 +108,8 @@ struct sway_view {
|
||||||
} events;
|
} events;
|
||||||
|
|
||||||
struct wl_listener surface_new_subsurface;
|
struct wl_listener surface_new_subsurface;
|
||||||
|
|
||||||
|
int max_render_time; // In milliseconds
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sway_xdg_shell_view {
|
struct sway_xdg_shell_view {
|
||||||
|
|
|
@ -119,6 +119,7 @@ static struct cmd_handler command_handlers[] = {
|
||||||
{ "kill", cmd_kill },
|
{ "kill", cmd_kill },
|
||||||
{ "layout", cmd_layout },
|
{ "layout", cmd_layout },
|
||||||
{ "mark", cmd_mark },
|
{ "mark", cmd_mark },
|
||||||
|
{ "max_render_time", cmd_max_render_time },
|
||||||
{ "move", cmd_move },
|
{ "move", cmd_move },
|
||||||
{ "nop", cmd_nop },
|
{ "nop", cmd_nop },
|
||||||
{ "opacity", cmd_opacity },
|
{ "opacity", cmd_opacity },
|
||||||
|
|
32
sway/commands/max_render_time.c
Normal file
32
sway/commands/max_render_time.c
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include <strings.h>
|
||||||
|
#include "sway/commands.h"
|
||||||
|
#include "sway/config.h"
|
||||||
|
#include "sway/tree/view.h"
|
||||||
|
|
||||||
|
struct cmd_results *cmd_max_render_time(int argc, char **argv) {
|
||||||
|
if (!argc) {
|
||||||
|
return cmd_results_new(CMD_INVALID, "Missing max render time argument.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_render_time;
|
||||||
|
if (!strcmp(*argv, "off")) {
|
||||||
|
max_render_time = 0;
|
||||||
|
} else {
|
||||||
|
char *end;
|
||||||
|
max_render_time = strtol(*argv, &end, 10);
|
||||||
|
if (*end || max_render_time <= 0) {
|
||||||
|
return cmd_results_new(CMD_INVALID, "Invalid max render time.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_container *container = config->handler_context.container;
|
||||||
|
if (!container || !container->view) {
|
||||||
|
return cmd_results_new(CMD_INVALID,
|
||||||
|
"Only views can have a max_render_time");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_view *view = container->view;
|
||||||
|
view->max_render_time = max_render_time;
|
||||||
|
|
||||||
|
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||||
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
#include "sway/layers.h"
|
#include "sway/layers.h"
|
||||||
#include "sway/output.h"
|
#include "sway/output.h"
|
||||||
#include "sway/server.h"
|
#include "sway/server.h"
|
||||||
|
#include "sway/surface.h"
|
||||||
#include "sway/tree/arrange.h"
|
#include "sway/tree/arrange.h"
|
||||||
#include "sway/tree/container.h"
|
#include "sway/tree/container.h"
|
||||||
#include "sway/tree/root.h"
|
#include "sway/tree/root.h"
|
||||||
|
@ -80,6 +81,7 @@ struct surface_iterator_data {
|
||||||
void *user_data;
|
void *user_data;
|
||||||
|
|
||||||
struct sway_output *output;
|
struct sway_output *output;
|
||||||
|
struct sway_view *view;
|
||||||
double ox, oy;
|
double ox, oy;
|
||||||
int width, height;
|
int width, height;
|
||||||
float rotation;
|
float rotation;
|
||||||
|
@ -134,7 +136,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->user_iterator(data->output, surface, &box, data->rotation,
|
data->user_iterator(data->output, data->view, surface, &box, data->rotation,
|
||||||
data->user_data);
|
data->user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +147,7 @@ void output_surface_for_each_surface(struct sway_output *output,
|
||||||
.user_iterator = iterator,
|
.user_iterator = iterator,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
.output = output,
|
.output = output,
|
||||||
|
.view = NULL,
|
||||||
.ox = ox,
|
.ox = ox,
|
||||||
.oy = oy,
|
.oy = oy,
|
||||||
.width = surface->current.width,
|
.width = surface->current.width,
|
||||||
|
@ -163,6 +166,7 @@ void output_view_for_each_surface(struct sway_output *output,
|
||||||
.user_iterator = iterator,
|
.user_iterator = iterator,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
.output = output,
|
.output = output,
|
||||||
|
.view = view,
|
||||||
.ox = view->container->surface_x - output->lx
|
.ox = view->container->surface_x - output->lx
|
||||||
- view->geometry.x,
|
- view->geometry.x,
|
||||||
.oy = view->container->surface_y - output->ly
|
.oy = view->container->surface_y - output->ly
|
||||||
|
@ -182,6 +186,7 @@ void output_view_for_each_popup(struct sway_output *output,
|
||||||
.user_iterator = iterator,
|
.user_iterator = iterator,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
.output = output,
|
.output = output,
|
||||||
|
.view = view,
|
||||||
.ox = view->container->surface_x - output->lx
|
.ox = view->container->surface_x - output->lx
|
||||||
- view->geometry.x,
|
- view->geometry.x,
|
||||||
.oy = view->container->surface_y - output->ly
|
.oy = view->container->surface_y - output->ly
|
||||||
|
@ -224,6 +229,7 @@ void output_layer_for_each_surface(struct sway_output *output,
|
||||||
.user_iterator = iterator,
|
.user_iterator = iterator,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
.output = output,
|
.output = output,
|
||||||
|
.view = NULL,
|
||||||
.ox = popup_sx,
|
.ox = popup_sx,
|
||||||
.oy = popup_sy,
|
.oy = popup_sy,
|
||||||
.width = surface->current.width,
|
.width = surface->current.width,
|
||||||
|
@ -291,6 +297,7 @@ static void output_for_each_surface(struct sway_output *output,
|
||||||
.user_iterator = iterator,
|
.user_iterator = iterator,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
.output = output,
|
.output = output,
|
||||||
|
.view = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sway_workspace *workspace = output_get_active_workspace(output);
|
struct sway_workspace *workspace = output_get_active_workspace(output);
|
||||||
|
@ -401,18 +408,37 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_frame_done_iterator(struct sway_output *output,
|
struct send_frame_done_data {
|
||||||
|
struct timespec when;
|
||||||
|
int msec_until_refresh;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
||||||
void *data) {
|
void *user_data) {
|
||||||
struct timespec *when = data;
|
int view_max_render_time = 0;
|
||||||
wlr_surface_send_frame_done(surface, when);
|
if (view != NULL) {
|
||||||
|
view_max_render_time = view->max_render_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct send_frame_done_data *data = user_data;
|
||||||
|
|
||||||
|
int delay = data->msec_until_refresh - output->max_render_time
|
||||||
|
- view_max_render_time;
|
||||||
|
|
||||||
|
if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) {
|
||||||
|
wlr_surface_send_frame_done(surface, &data->when);
|
||||||
|
} else {
|
||||||
|
struct sway_surface *sway_surface = surface->data;
|
||||||
|
wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_frame_done(struct sway_output *output, struct timespec *when) {
|
static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) {
|
||||||
output_for_each_surface(output, send_frame_done_iterator, when);
|
output_for_each_surface(output, send_frame_done_iterator, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void count_surface_iterator(struct sway_output *output,
|
static void count_surface_iterator(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *_box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *_box, float rotation,
|
||||||
void *data) {
|
void *data) {
|
||||||
size_t *n = data;
|
size_t *n = data;
|
||||||
|
@ -533,7 +559,7 @@ int output_repaint_timer_handler(void *data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void damage_handle_frame(struct wl_listener *listener, void *data) {
|
static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
|
||||||
struct sway_output *output =
|
struct sway_output *output =
|
||||||
wl_container_of(listener, output, damage_frame);
|
wl_container_of(listener, output, damage_frame);
|
||||||
if (!output->enabled || !output->wlr_output->enabled) {
|
if (!output->enabled || !output->wlr_output->enabled) {
|
||||||
|
@ -592,9 +618,10 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send frame done to all visible surfaces
|
// Send frame done to all visible surfaces
|
||||||
struct timespec now;
|
struct send_frame_done_data data = {0};
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &data.when);
|
||||||
send_frame_done(output, &now);
|
data.msec_until_refresh = msec_until_refresh;
|
||||||
|
send_frame_done(output, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_damage_whole(struct sway_output *output) {
|
void output_damage_whole(struct sway_output *output) {
|
||||||
|
@ -605,7 +632,7 @@ void output_damage_whole(struct sway_output *output) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void damage_surface_iterator(struct sway_output *output,
|
static void damage_surface_iterator(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *_box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *_box, float rotation,
|
||||||
void *_data) {
|
void *_data) {
|
||||||
bool *data = _data;
|
bool *data = _data;
|
||||||
|
@ -811,7 +838,7 @@ static void handle_scale(struct wl_listener *listener, void *data) {
|
||||||
update_output_manager_config(output->server);
|
update_output_manager_config(output->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_presented_iterator(struct sway_output *output,
|
static void send_presented_iterator(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
||||||
void *data) {
|
void *data) {
|
||||||
struct wlr_presentation_event *event = data;
|
struct wlr_presentation_event *event = data;
|
||||||
|
|
|
@ -97,7 +97,7 @@ damage_finish:
|
||||||
pixman_region32_fini(&damage);
|
pixman_region32_fini(&damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_surface_iterator(struct sway_output *output,
|
static void render_surface_iterator(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *_box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *_box, float rotation,
|
||||||
void *_data) {
|
void *_data) {
|
||||||
struct render_data *data = _data;
|
struct render_data *data = _data;
|
||||||
|
@ -214,11 +214,11 @@ static void render_view_toplevels(struct sway_view *view,
|
||||||
render_surface_iterator, &data);
|
render_surface_iterator, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_popup_iterator(struct sway_output *output,
|
static void render_popup_iterator(struct sway_output *output, struct sway_view *view,
|
||||||
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
struct wlr_surface *surface, struct wlr_box *box, float rotation,
|
||||||
void *data) {
|
void *data) {
|
||||||
// Render this popup's surface
|
// Render this popup's surface
|
||||||
render_surface_iterator(output, surface, box, rotation, data);
|
render_surface_iterator(output, view, surface, box, rotation, data);
|
||||||
|
|
||||||
// Render this popup's child toplevels
|
// Render this popup's child toplevels
|
||||||
output_surface_for_each_surface(output, surface, box->x, box->y,
|
output_surface_for_each_surface(output, surface, box->x, box->y,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
#include <wlr/types/wlr_surface.h>
|
||||||
#include "sway/server.h"
|
#include "sway/server.h"
|
||||||
#include "sway/surface.h"
|
#include "sway/surface.h"
|
||||||
|
@ -9,9 +11,21 @@ void handle_destroy(struct wl_listener *listener, void *data) {
|
||||||
surface->wlr_surface->data = NULL;
|
surface->wlr_surface->data = NULL;
|
||||||
wl_list_remove(&surface->destroy.link);
|
wl_list_remove(&surface->destroy.link);
|
||||||
|
|
||||||
|
wl_event_source_remove(surface->frame_done_timer);
|
||||||
|
|
||||||
free(surface);
|
free(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int surface_frame_done_timer_handler(void *data) {
|
||||||
|
struct sway_surface *surface = data;
|
||||||
|
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
wlr_surface_send_frame_done(surface->wlr_surface, &now);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void handle_compositor_new_surface(struct wl_listener *listener, void *data) {
|
void handle_compositor_new_surface(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_surface *wlr_surface = data;
|
struct wlr_surface *wlr_surface = data;
|
||||||
|
|
||||||
|
@ -21,4 +35,7 @@ void handle_compositor_new_surface(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
surface->destroy.notify = handle_destroy;
|
surface->destroy.notify = handle_destroy;
|
||||||
wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
|
wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
|
||||||
|
|
||||||
|
surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
|
||||||
|
surface_frame_done_timer_handler, surface);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ sway_sources = files(
|
||||||
'commands/inhibit_idle.c',
|
'commands/inhibit_idle.c',
|
||||||
'commands/kill.c',
|
'commands/kill.c',
|
||||||
'commands/mark.c',
|
'commands/mark.c',
|
||||||
|
'commands/max_render_time.c',
|
||||||
'commands/opacity.c',
|
'commands/opacity.c',
|
||||||
'commands/include.c',
|
'commands/include.c',
|
||||||
'commands/input.c',
|
'commands/input.c',
|
||||||
|
|
|
@ -131,6 +131,9 @@ must be separated by one space. For example:
|
||||||
. Start with *max_render_time 1*. Increment by *1* if you see frame
|
. Start with *max_render_time 1*. Increment by *1* if you see frame
|
||||||
drops.
|
drops.
|
||||||
|
|
||||||
|
To achieve even lower latency, see the *max_render_time* surface
|
||||||
|
property in *sway*(5).
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*sway*(5) *sway-input*(5)
|
*sway*(5) *sway-input*(5)
|
||||||
|
|
|
@ -182,6 +182,22 @@ set|plus|minus <amount>
|
||||||
*layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...
|
*layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...
|
||||||
Cycles the layout mode of the focused container through a list of layouts.
|
Cycles the layout mode of the focused container through a list of layouts.
|
||||||
|
|
||||||
|
*max_render_time* off|<msec>
|
||||||
|
Works together with *output max_render_time* to reduce the latency even
|
||||||
|
further by delaying the frame callbacks sent to a surface. When set to
|
||||||
|
a positive number of milliseconds, delays the frame callback in such a
|
||||||
|
way that the surface has the specified number of milliseconds to render
|
||||||
|
and commit new contents before being sampled by the compositor for the
|
||||||
|
next presentation. See *max_render_time* in *sway-output*(5) for
|
||||||
|
further details.
|
||||||
|
|
||||||
|
To set this up for optimal latency:
|
||||||
|
. Set up *output max_render_time*.
|
||||||
|
. Put the target application in _full-screen_ and have it continuously
|
||||||
|
render something.
|
||||||
|
. Start by setting *max_render_time 1*. If the application drops
|
||||||
|
frames, increment by *1*.
|
||||||
|
|
||||||
*move* left|right|up|down [<px> px]
|
*move* left|right|up|down [<px> px]
|
||||||
Moves the focused container in the direction specified. If the container,
|
Moves the focused container in the direction specified. If the container,
|
||||||
the optional _px_ argument specifies how many pixels to move the container.
|
the optional _px_ argument specifies how many pixels to move the container.
|
||||||
|
|
Loading…
Reference in a new issue