Enable syncing of redraws to monitor refresh rate
Now, when large changes, such as scrolling occur, the OpenGL back buffer swapping is synchronized to the monitor refresh rate (if supported by the OS). This eliminates tearing artifacts when scrolling the screen fast. Fixes #318
This commit is contained in:
parent
807c8f9005
commit
6a51096304
@ -348,6 +348,15 @@ glyph in video RAM so that font rendering is not a bottleneck. Interaction
|
||||
with child programs takes place in a separate thread from rendering, to improve
|
||||
smoothness.
|
||||
|
||||
kitty goes to great lengths to reduce latency. In order to minimize latency,
|
||||
kitty dynamically decides whether to sync updates to the monitor refresh rate
|
||||
or not. When doing latency sensitive activities such as typing or selecting
|
||||
with the mouse, kitty does not sync to the monitor refresh rate, as that limits
|
||||
latency to a minimum of `17ms` for common monitors. However, when
|
||||
scrolling or making other large scale changes to the screen, kitty syncs to the
|
||||
monitor refresh rate, to avoid
|
||||
link:https://en.wikipedia.org/wiki/Screen_tearing[screen tearing].
|
||||
|
||||
There are two parameters you can tune to adjust the performance. ``repaint_delay``
|
||||
and ``input_delay``. These control the artificial delays introduced into the
|
||||
render loop to reduce CPU usage. See the link:kitty/kitty.conf[config file] for details.
|
||||
|
||||
@ -604,6 +604,7 @@ render_os_window(OSWindow *os_window, double now, unsigned int active_window_id)
|
||||
Tab *tab = os_window->tabs + os_window->active_tab;
|
||||
BorderRects *br = &tab->border_rects;
|
||||
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height);
|
||||
bool needs_vsync = false;
|
||||
if (TD.screen && os_window->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window, true);
|
||||
for (unsigned int i = 0; i < tab->num_windows; i++) {
|
||||
Window *w = tab->windows + i;
|
||||
@ -617,9 +618,11 @@ render_os_window(OSWindow *os_window, double now, unsigned int active_window_id)
|
||||
double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at);
|
||||
set_maximum_wait(bell_left);
|
||||
}
|
||||
if (!needs_vsync && (WD.screen->render_activity.large_change || WD.screen->render_activity.chars_written > 100)) needs_vsync = true;
|
||||
WD.screen->render_activity.large_change = false; WD.screen->render_activity.chars_written = 0;
|
||||
}
|
||||
}
|
||||
swap_window_buffers(os_window);
|
||||
swap_window_buffers(os_window, needs_vsync);
|
||||
br->is_dirty = false;
|
||||
os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id;
|
||||
os_window->focused_at_last_render = os_window->is_focused;
|
||||
|
||||
@ -626,7 +626,11 @@ hide_mouse(OSWindow *w) {
|
||||
}
|
||||
|
||||
void
|
||||
swap_window_buffers(OSWindow *w) {
|
||||
swap_window_buffers(OSWindow *w, bool needs_vsync) {
|
||||
if (needs_vsync != w->vsync_enabled) {
|
||||
glfwSwapInterval(needs_vsync ? 1 : 0);
|
||||
w->vsync_enabled = needs_vsync;
|
||||
}
|
||||
glfwSwapBuffers(w->handle);
|
||||
}
|
||||
|
||||
@ -739,7 +743,7 @@ os_window_swap_buffers(PyObject UNUSED *self, PyObject *args) {
|
||||
for (size_t i = 0; i < global_state.num_os_windows; i++) {
|
||||
OSWindow *w = global_state.os_windows + i;
|
||||
if (w->id == os_window_id) {
|
||||
swap_window_buffers(w); Py_RETURN_NONE;
|
||||
swap_window_buffers(w, false); Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
PyErr_SetString(PyExc_ValueError, "no such OSWindow");
|
||||
|
||||
@ -123,6 +123,7 @@ screen_reset(Screen *self) {
|
||||
init_tabstops(self->alt_tabstops, self->columns);
|
||||
cursor_reset(self->cursor);
|
||||
self->is_dirty = true;
|
||||
self->render_activity.large_change = true;
|
||||
screen_cursor_position(self, 1, 1);
|
||||
set_dynamic_color(self, 110, NULL);
|
||||
set_dynamic_color(self, 111, NULL);
|
||||
@ -202,6 +203,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
|
||||
self->cursor->y = num_content_lines;
|
||||
if (self->cursor->y >= self->lines) { self->cursor->y = self->lines - 1; screen_index(self); }
|
||||
}
|
||||
self->render_activity.large_change = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -354,6 +356,7 @@ screen_draw(Screen *self, uint32_t och) {
|
||||
line_right_shift(self->linebuf->line, self->cursor->x, char_width);
|
||||
}
|
||||
line_set_char(self->linebuf->line, self->cursor->x, ch, char_width, self->cursor, false);
|
||||
self->render_activity.chars_written += 1;
|
||||
self->cursor->x++;
|
||||
if (char_width == 2) {
|
||||
line_set_char(self->linebuf->line, self->cursor->x, 0, 0, self->cursor, true);
|
||||
@ -488,6 +491,7 @@ screen_toggle_screen_buffer(Screen *self) {
|
||||
}
|
||||
screen_history_scroll(self, SCROLL_FULL, false);
|
||||
self->is_dirty = true;
|
||||
self->render_activity.large_change = true;
|
||||
}
|
||||
|
||||
void screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI
|
||||
@ -724,7 +728,8 @@ screen_cursor_to_column(Screen *self, unsigned int column) {
|
||||
self->history_line_added_count++; \
|
||||
} \
|
||||
linebuf_clear_line(self->linebuf, bottom); \
|
||||
self->is_dirty = true;
|
||||
self->is_dirty = true; \
|
||||
self->render_activity.large_change = true;
|
||||
|
||||
void
|
||||
screen_index(Screen *self) {
|
||||
@ -749,7 +754,8 @@ screen_scroll(Screen *self, unsigned int count) {
|
||||
linebuf_reverse_index(self->linebuf, top, bottom); \
|
||||
linebuf_clear_line(self->linebuf, top); \
|
||||
INDEX_GRAPHICS(1) \
|
||||
self->is_dirty = true;
|
||||
self->is_dirty = true; \
|
||||
self->render_activity.large_change = true;
|
||||
|
||||
void
|
||||
screen_reverse_index(Screen *self) {
|
||||
@ -974,6 +980,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
||||
linebuf_mark_line_dirty(self->linebuf, i);
|
||||
}
|
||||
self->is_dirty = true;
|
||||
self->render_activity.large_change = true;
|
||||
}
|
||||
if (how != 2) {
|
||||
screen_erase_in_line(self, how, private);
|
||||
@ -983,6 +990,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
||||
if (self->scrolled_by != 0) {
|
||||
self->scrolled_by = 0;
|
||||
self->scroll_changed = true;
|
||||
self->render_activity.large_change = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -994,6 +1002,7 @@ screen_insert_lines(Screen *self, unsigned int count) {
|
||||
if (top <= self->cursor->y && self->cursor->y <= bottom) {
|
||||
linebuf_insert_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
self->render_activity.large_change = true;
|
||||
screen_carriage_return(self);
|
||||
}
|
||||
}
|
||||
@ -1005,6 +1014,7 @@ screen_delete_lines(Screen *self, unsigned int count) {
|
||||
if (top <= self->cursor->y && self->cursor->y <= bottom) {
|
||||
linebuf_delete_lines(self->linebuf, count, self->cursor->y, bottom);
|
||||
self->is_dirty = true;
|
||||
self->render_activity.large_change = true;
|
||||
screen_carriage_return(self);
|
||||
}
|
||||
}
|
||||
@ -1702,6 +1712,7 @@ screen_history_scroll(Screen *self, int amt, bool upwards) {
|
||||
if (new_scroll != self->scrolled_by) {
|
||||
self->scrolled_by = new_scroll;
|
||||
self->scroll_changed = true;
|
||||
self->render_activity.large_change = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -51,6 +51,12 @@ typedef struct {
|
||||
} SavemodesBuffer;
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool large_change;
|
||||
unsigned int chars_written;
|
||||
} RenderActivitity;
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
@ -84,6 +90,7 @@ typedef struct {
|
||||
pthread_mutex_t read_buf_lock, write_buf_lock;
|
||||
|
||||
CursorRenderInfo cursor_render_info;
|
||||
RenderActivitity render_activity;
|
||||
|
||||
} Screen;
|
||||
|
||||
|
||||
@ -114,6 +114,7 @@ typedef struct {
|
||||
bool is_damaged;
|
||||
uint32_t offscreen_texture_id;
|
||||
unsigned int clear_count;
|
||||
bool vsync_enabled;
|
||||
} OSWindow;
|
||||
|
||||
|
||||
@ -156,7 +157,7 @@ bool should_os_window_be_rendered(OSWindow* w);
|
||||
void set_dpi_from_os_window(OSWindow *w);
|
||||
void wakeup_main_loop();
|
||||
void event_loop_wait(double timeout);
|
||||
void swap_window_buffers(OSWindow *w);
|
||||
void swap_window_buffers(OSWindow *w, bool);
|
||||
void make_window_context_current(OSWindow *w);
|
||||
void hide_mouse(OSWindow *w);
|
||||
void destroy_os_window(OSWindow *w);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user