-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Prevent flickering in nushell due to FTCS marks #14677
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might need you to walk me through how the "flush to terminal" callback becoming "paint and then also flush" actually prevents excess flushes 😄
@DHowett Okay I'm so sorry my handwriting is atrocious, but this is faster than trying to mermaid it (imagine an "after" beneath the dotted line) |
For real though: BeforesequenceDiagram
participant API Thread
participant Render Thread
participant Terminal
Note left of API Thread: {the start of the prompt}
Note left of API Thread: ^[[2q
activate API Thread
opt Parser
Note right of API Thread: StateMachine::ProcessString
Note right of API Thread: AdaptDispatch -> failed
Note right of API Thread: StateMachine::FlushToTerminal
opt XtermEngine
Note right of API Thread: WriteTerminalW
Note right of API Thread: write "^[[2q"to _buffer
Note right of API Thread: _Flush
API Thread ->> Terminal: _buffer goes out the pipe
end
Note left of API Thread: {the rest of the prompt}
Note right of API Thread: InvalidateFlush
Note right of API Thread: [paint the frame]
Note right of API Thread: _Flush
API Thread ->> Terminal: _buffer goes out the pipe
end
Note left of API Thread: Unlock console
API Thread --> -Render Thread:
activate Render Thread
Note right of Render Thread: PaintFrame
opt XtermEngine
Note right of Render Thread: [paint the frame]
Note right of Render Thread: _Flush
end
Render Thread ->> Terminal: _buffer goes out the pipe
deactivate Render Thread
After this PRsequenceDiagram
participant API Thread
participant Render Thread
participant Terminal
Note left of API Thread: {the start of the prompt}
Note left of API Thread: ^[[2q
activate API Thread
opt Parser
Note right of API Thread: StateMachine::ProcessString
Note right of API Thread: AdaptDispatch -> failed
API Thread -->> Render Thread: NotifyPaintFrame
Note right of API Thread: StateMachine::FlushToTerminal
opt XtermEngine
Note right of API Thread: WriteTerminalW
Note right of API Thread: write "^[[2q"to _buffer
end
end
Note left of API Thread: {the rest of the prompt}
opt XtermEngine
Note right of API Thread: InvalidateFlush
Note right of API Thread: [paint the frame]
end
Note left of API Thread: Unlock console
API Thread --> -Render Thread:
activate Render Thread
Note right of Render Thread: PaintFrame
opt XtermEngine
Note right of Render Thread: [paint the frame]
Note right of Render Thread: _Flush
end
Render Thread ->> Terminal: _buffer goes out the pipe
deactivate Render Thread
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code does look good to me and I'd approve it instantly, but I'm not sure whether this is the best approach. From what I can tell, flushing during circling isn't necessarily at all and I believe it'd be better in general to instead only flush when the console API call finishes (i.e. when the entire user string has been processed), because this will avoid calling WriteFile
for every line that's written. It should also solve the issue, while also improving ConPTY performance drastically.
That's kinda my thesis too 😉 Though, I was moreso tracking that in #12336. I figured best to separate the work into
|
This comment has been minimized.
This comment has been minimized.
// | ||
// Instead, we'll leave this frame in _buffer, and just keep appending to | ||
// it as needed. | ||
if (_noFlushOnEnd) [[unlikely]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI I don't think the [[unlikely]]
is worth it here. Its implementation is super dumb in MSVC and causes any and all affected unlikely code to be moved to the end of the function, even if it's literally just a single mov [rcx+1234], 0
instruction like here. I think it's still useful, but it seems like its usage is mostly restricted to a rather "nuanced" application, especially after observing the disassembly output.
Previously, all unknown escape sequences would lead to an immediate call to `VtEngine::_Flush()`. This lead to problems with nushell which uses FTCS marks that were unknown to us. Combined with the linewise redrawing that nushell does, Terminal would get the prompt in two separate frames, causing a slight flickering. #14677 fixed this by suppressing the `_Flush()` call when unknown sequences are encountered. Unfortunately, this triggered a bug due to our somewhat "inconsistent" architecture in conhost: `XtermEngine::WriteTerminalW` isn't just used to flush unknown sequences but also used directly by `InputBuffer::PassThroughWin32MouseRequest` to write its mouse sequence directly to the ConPTY host. `VtEngine` already contains a number of specialized member functions like `RequestWin32Input()` to ensure that `_Flush()` is called immediately and another member could've been added to solve this issue. This commit now adds `RequestMouseMode` in the same vein. But I believe we can make the system more robust in general by using eager flushing by default (= safe), similar to how a `write()` on a TCP socket flushes by default, and instead only selectively pause and unpause flushing with a system similar to `TCP_CORK`. This seems to work fairly well, as it solves: * The original nushell bug * The new bug * Improves overall throughput by ~33% (due to less flushing) In particular the last point is noteworthy, as this commit removes the last performance bottleneck in ConPTY that isn't `VtEngine`. Around ~95% of all CPU and wall time is spent in there now and any improvements to `VtEngine` should yield immediately results. Closes #15711 ## Validation Steps Performed * Clone/Run https://github.com/chrisant996/repro_enable_mouse_input * Hold Ctrl+Alt and circle with the mouse over the viewport * Repro.exe prints the current cursor coordinates ✅ * Run nushell * No flickering when typing in the prompt ✅
Tl;dr: Conpty would flush a frame whenever it encountered a FTCS mark. Combine that with the whole-line redrawing that nushell does, and the Terminal would get the prompt in two frames instead of one, causing a slight flickering. This fixes that by rendering the frame, but not flushing to the pipe when we encounter one of these sequences.
Closes #13710
A complication here: there are some sequences that we passthrough immediately when we encounter them. For example,
\x1b[ 2q
. we need to also not flush when we encounter one of these sequences. nushell emits one of these as a part of the prompt, and that would force the buffered frame to get written anyways, before writing that to the pipe.