Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit and simplifying of dirty fields #6208

Closed
wants to merge 19 commits into from
Closed

Audit and simplifying of dirty fields #6208

wants to merge 19 commits into from

Conversation

1kjo
Copy link
Contributor

@1kjo 1kjo commented Jul 15, 2022

This PR simplifies the code related to knowing when the terminal should be redrawn:

  • It tries to separate the dirty (in WindowContext) and the hint_highlighted_dirty mechanisms by being more explicit about which is needed.
  • It stops setting the dirty field when it's not needed, which removes extra redraws that would take place when scrolling, typing and could impact latency (see previous PR Improve dirty detection when scrolling and typing characters #6206) without introducing too much complexity (only simple conditions).
  • It tries to remove the usage of the dirty field, so that the terminal can act as the source of truth and the code can be simplified. Note that this is not done everywhere. For example, the search functionality is still relying on the dirty field.

It should probably be read commit by commit. The commits are very small and I tried to explain what I did in the messages.

Greg Depoire--Ferrer added 3 commits July 15, 2022 14:00
Running "grep -R INSERT alacritty*/src" returns the following results:

In alacritty_terminal/src/term/mod.rs:
- const INSERT              = 0b0000_0000_0100_0000_0000;
- if self.mode.contains(TermMode::INSERT) &&
  self.grid.cursor.point.column + width < columns {
- ansi::Mode::Insert => self.mode.insert(TermMode::INSERT),
- ansi::Mode::Insert => self.mode.remove(TermMode::INSERT),

Insert mode only changes the way characters are inputted into the
terminal (the if condition above), so there is no need to damage the
terminal when enabling or disabling it.
This will allow to simplify the code mutating the terminal in future
commits by removing the need to set the dirty field every time.
With the previous commit, scroll_display will damage the display and
will trigger a redraw if needed. Furthermore, update_selection sets the
dirty flag by itself. This means that ActionContext::scroll doesn't need
to set the dirty field anymore.
Greg Depoire--Ferrer added 16 commits July 15, 2022 16:55
This simplifies the code and will allow us to simplify the code mutating
the selection in future commits by removing the need to set the dirty
field every time.
With the previous commit, the terminal will detect a change in selection
and trigger a redraw, so there is no longer a need to set the dirty
field.
When vi mode is enabled, the vi cursor might end up being on top of a
hint, which means that the current hint must be recalculated.

It is not necessary however to set the dirty field:
- If the first branch of the first if condition is taken, it will
  already damage the terminal.
- If the second branch is taken, it calls clear_selection which will
  damage the terminal.
- cancel_search sets the dirty flag if it modifies the UI.
In mouse_input:
- If the message bar is closed, pop_message already sets the dirty flag.
- Otherwise, if the mouse is pressed:
  - on_left_click mutates the selection using the *_selection methods or changes the vi cursor position which
    both damage the terminal.
  - Mouse bindings can be called, but actions already set the dirty flag
    or damage the terminal when needed.
- If the mouse is released, the only method that is called and could mutate the UI is trigger_hint, and only if the vi cursor moves. In that case, mark the highlighted hint as dirty.
This little change reduces the latency of 1 frame when typing in the
terminal. The reason is that it removes an extra redraw before receiving
the visual update from the PTY, and that allows not having to wait until
a frame can be rendered again.

Also, simplify the logic in on_typing_start by directly calling
update_cursor_blinking instead of copying its logic.
trigger_hint already sets the dirty field or damages the terminal when
there is a visual update.
update_cursor_blinking already sets the dirty field if it changes the
cursor visuals.

However, changing the terminal's is_focused property can change the
cursor's shape. See RenderableContent::new.
Wrap the mutation in a condition. This small change ensures there is no
extra redraw when the cursor times out.
Always set hint_highlight_dirty so that the hint is recalculated in
WindowContext and the dirty field is set there if needed.
The vi motion could change the vi cursor position so the highlighted
hint must be recalculated. However, it doesn't need to set the dirty
field.
Same as previous commit.
Separate the fact that the hint highlighting is dirty from the fact that
the terminal must be redrawn, and mark the highlighted hint as dirty
when the layout changes.

Although they add a few lines of code, these changes make a bit easier
to follow what is going on with the hint highlighting by being more
explicit.
Directly damage the lines in Display::update_highlighted_hints instead
of doing it in Display::update_damage.

This means we can stop returning a boolean that indicates whether the
terminal is dirty.
With commit ee40518, damaging the
terminal now signifies that it needs to be rendered as soon as possible.

Use the next_frame_damage_rects field instead of damaging the terminal
after rendering when we want the next frame to start with damage, as it
already exists for this purpose.
@1kjo 1kjo changed the title Audit of dirty fields Audit and simplifying of dirty fields Jul 15, 2022
@@ -327,7 +327,7 @@ impl WindowContext {
return;
}

if self.dirty {
if self.dirty || terminal.has_damage() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right condition would never be true, when the left is false.

In general all of your damage related changes must be discarded, since they are slowing down perf when actually rendering a frame. I'd suggest to not touch damage the way it's right now, hence it's optimal in terms of perf the way it's right now.

In general I'd suggest to focus on dirty bit.

@1kjo
Copy link
Contributor Author

1kjo commented Jul 15, 2022

The right condition would never be true, when the left is false.

In 5add7c0, we stop setting the field to true and rely on the terminal to check if the selection changed instead so that we can't actually miss one.

In general all of your damage related changes must be discarded, since they are slowing down perf when actually rendering a frame.

Is there a way to test this? With vtebench at least, I don't see slowing down:
output

@kchibisov
Copy link
Member

kchibisov commented Jul 15, 2022

I mean that Pty will send a Wakeup, which will set dirty bit. Those events are triggered if and only there's a terminal update, which means that it's dirty, so the dirty check on the terminal doesn't make any sense in a context of deciding to render or not, since Pty tells us that anyway.

The damaging is more rendering, etc related. In general you're enlarging the time we're under the terminal lock(which we must minimize) if we check for damage the way you do, since later we collect damage again, leading to iterating damage rects more than needed.

Note, you're not seeing any performance difference because your right condition is never executed, like at all on vte bench, since we're always dirty. I'm just saying that all damage changes are redundant and should be left as is.

The point of damaging wrt selection is to not convert it multiple times to a range, since it's not free iirc.

@1kjo
Copy link
Contributor Author

1kjo commented Jul 15, 2022

I mean that Pty will send a Wakeup, which will set dirty bit. Those events are triggered if and only there's a terminal update, which means that it's dirty, so the dirty check on the terminal doesn't make any sense in a context of deciding to render or not, since Pty tells us that anyway.

Wakeup is sent when the pty reads something new, but not when ActionContext changes the selection or the cursor position because of a user event. The idea was that because the terminal already tracks the cursor position and selection changes, it might as well help us here, instead of having the additional dirty that needs to be kept in sync.

The damaging is more rendering, etc related. In general you're enlarging the time we're under the terminal lock(which we must minimize) if we check for damage the way you do, since later we collect damage again, leading to iterating damage rects more than needed.

Note, you're not seeing any performance difference because your right condition is never executed, like at all on vte bench, since we're always dirty. I'm just saying that all damage changes are redundant and should be left as is.

The point of damaging wrt selection is to not convert it multiple times to a range, since it's not free iirc.

What about modifying Term to add a setter for selection, which would set the damage, and using that setter from ActionContext? It would fix this and it would even make the render path not call to_range at all if the selection hasn't changed unlike in master.

@kchibisov
Copy link
Member

Wakeup is sent when the pty reads something new, but not when ActionContext changes the selection or the cursor position because of a user event. The idea was that because the terminal already tracks the cursor position and selection changes, it might as well help us here, instead of having the additional dirty that needs to be kept in sync.
What about modifying Term to add a setter for selection, which would set the damage, and using that setter from ActionContext? It would fix this and it would even make the render path not call to_range at all if the selection hasn't changed unlike in master.

Just for your information selection is stored on the terminal already, the point is that Selection and SelectionRange are different types of object, and the later is an expanded version that it's a bit costly to compute, so it's being computed only once.

In general selection has nothing to do with alacritty terminal it just happened to be the way it's right now.

@kchibisov
Copy link
Member

Closing in favor of #6219

@kchibisov kchibisov closed this Jul 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants