-
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
WIP: Pass through VT input unmodified #15630
Conversation
I've only briefly skimmed the code, but it seems like you're passing through the VT output directly to the conpty client, but also parsing it so it's rendered into the conhost buffer. And then the legacy console APIs still operate on the conhost buffer and flush any changes via the VtEngine as they currently do. The problem with this approach is that the conhost buffer and the conpty client's buffer will inevitably get out of sync, and then any legacy console APIs will be rendering in the wrong place and corrupting the output. So while it may be faster, it doesn't solve the problem of passing through unsupported sequences which desync the conpty client from conhost. Also, I thought the biggest problem with passthrough was in the handling of legacy state queries, which are synchronous (e.g. things like the cursor position and palette). I'm assuming you're relying on that state being tracked in conhost, which is not much worse than the existing conpty implementation, but it doesn't address client-side state changes, or anything triggered by the passthrough which is out of sync with the conhost side. |
Is there a possibility that in the future some kind of condrv interface will be provided to 3p terminals, so that these terminals can process Console API calls directly in their buffers without intermediaries? Would you like this for WT? This immediately solves a gazillion issues with performance and synchronization. |
FWIW this is more like what I had originally envisioned for passthrough. IMO I only ever wanted passthrough for apps that enabled it manually - "I solemnly swear that I am up to good and won't fuck with the console APIs anymore". Since passthrough's basically useless now, we may as well try to improve it a bit here. It can't hurt. I'm not sure it'll ever work great for apps that are mixing and matching VT and console APIs. |
Everyone understands that there is no pure VT here. That with the current approach, Console API calls are inevitable. Strictly speaking, Since there is no VT, it makes sense to process all Console API calls on the terminal side. Would you like to know how it would work on a live example if it were implemented? A year ago, I implemented this approach in my terminal emulator. Since then, all Console API calls are processed on the terminal side directly in its buffers. For almost a year of flight, there was not a single issue related to performance or synchronization. Nothing has changed for applications - cmd.exe, powershell.exe, pwsh.exe, wsl.exe, far.exe, mc, git, vim - all console applications run with high performance and without issues. Moreover, all Unix applications work whether they are in a Unix environment. |
See, the application calls |
Edit: Winner of the longest GitHub comment incoming... @j4james Initially I wrote this PR as sort of a "is this even possible" experiment. My idea was that if But I think I realized that this approach is a little flawed. I now get the feeling that the amount of simplification we could out of this is insufficient to have a meaningful, positive effect on our stability / bug count. Or am I just being pessimistic? I think I might just skip the idea from this PR and start implementing the direct-console-to-vt-translation code right away. I know that this doesn't solve the desync issue that plagues ConPTY, but I wasn't intending to solve it. I'm also not particularly interested in the performance aspect. The truth is: I really don't know how to solve these while keeping the current ConPTY architecture (= it being an external process). That's why my focus was on reducing the complexity of Due to ConPTY running in a different process it'll never reach the performance that OpenConsole has. With all my current and future performance PRs merged in, OpenConsole runs at >300MB/s whereas ConPTY with passthrough still only runs at ~50MB/s - the exact same speed as when OpenConsole still ran at ~100MB/s. It's practically entirely bottlenecked by the cross-process communication (and the redundant VT parsing). @zadjii-msft I'm not sure whether such an opt-in would meaningfully improve things over just implementing passthrough mode for all VT apps. I'm a little scared that such an opt-in would break applications that integrate 3rd party libraries and thus would end up not being heavily used. If we can build a "vt renderer" that can translate things, I'm convinced we can build regular functions that can translate them synchronously/directly as well. Such functions would also have the major benefit that we could unit test them more easily, because they'd be effectively state-less (even if they consume state, like the current buffer width, etc.). @o-sdn-o First of all, I saw console implementation a while ago and it's absolutely fantastic. Nice work! I'm not sure if I understand what you're saying, however. What you're suggesting is effectively the same as my suggestion above to have something like a "conhost.lib" right?
I personally would really enjoy if we had something like that but given our extremely limited resources and us not being able to write a "clean sheet" conhost like in vtm, while also having tons of complex feature requests in our backlog, we're backlogged for a very long time. I'm also not sure how many people would integrate such a library in the first place. I'd personally be happy about it, because it would solve one of 2 performance regressions, but... it'd be hard to sell working for months on this (extracting it out of conhost, testing shipping, ...). |
@lhecker I totally agree with this. But ideally the legacy console APIs wouldn't need to rely on the
OK, yeah, that sounds like we're thinking along the same lines, although I would just go all in with translating the APIs from the start, which I think is what miniksa's implementation was going for. That's why I wanted to get all the VT operations in place that we'd need for this.
But note that the redundant VT parsing would be dropped in a pure passthrough mode. There wouldn't be a conhost buffer at all.
Remember that this is not just about syncing between processes (i.e. conhost and WT). One of the main purposes of conpty is to support console applications over a remote connection like ssh. So this stuff has be implemented with VT sequences somehow. I suppose you could use some other protocol, like DCOM, but I don't think that makes anything easier. My view is that maybe we just have to live with the fact that these operations are potentially going to be slow in passthrough mode. So if you're calling an API to get the cursor position, it's going to send a As a later optimisation, we could try and cache certain queries, especially if we're willing to do at least some parsing of the passed-through content. Like if we can tell that we haven't passed through a palette changing sequence, then we don't need to requery the palette every time
Unless I'm missing something, I don't think that's a problem. With a pure passthrough mode, conhost doesn't have a buffer, so it doesn't need to know anything about the size of the client buffer, except when someone actually queries that information. Just reporting the size to conhost when it's changed in the client should be enough I think.
Yeah, that's the biggest problem in my opinion, because I think some console apps query the cursor position non-stop, which would require a constant stream of blocking
That's an orthogonal issue I think, and should be discussed separately. Unless I'm misunderstanding the concept, that isn't going to get legacy console apps running over ssh. That's just about optimising third-party Windows terminals running locally.
Yeah, if apps are required to opt-in to passthrough mode it seems kind of pointless to me. I'd expect cmd shell operations like |
@j4james For example, vtm does not need ConPTY at all to function remotely via SSH. Any (even legacy) console application could be launched inside a remotely launched vtm, and as a result, a pure VT stream goes via SSH to the local terminal. |
Thanks for your feedback! I does reduce my confidence that I'd be able to come up with an optimal system, however, because I wouldn't quite know at the moment how to implement several of the things that you mentioned. 😟 That aside, there's just one detail I don't quite get...
but then also said:
How would ConPTY work if we don't have a buffer but simultaneously want to make it work with applications that might call console functions that require a buffer ( I really like everything that you said, just for this one thing however I'd like to make a counter-argument:
I really don't want to push for making such a .lib just yet. I said it more as a long term vision/idea. I feel like we have many more important issues to address first (although it'd be nice if we could get rid of Edit: I just realized that me saying that sounds kind of silly, given my recent barrage of performance PRs. But that's just me being self-indulgent. Like eating too much chocolate. 😄 @o-sdn-o Your feedback and help is absolutely invaluable, but I would like to at least just once point out that your implementation isn't quite complete, purely from reading your source code. Maybe it works well in practice, but I'm afraid it might be at least somewhat more complex once you implement all the remaining terrible legacy nuances that ConPTY handles. (I mean it probably will never be as complex as our current code, since yours is a clean-sheet implementation, but... Well, you probably know what I mean...) |
@lhecker If something is not implemented there, then this is most likely done intentionally for security reasons, for example, as is the case with the DOSKEY functionality. And so, of course, you are right, there are such cases. But this is a question of the need for such functionality. If there is a need for a certain legacy function, then it is immediately directly implemented. |
@lhecker In general, the complexity of handling Console API calls is added by the fact that some of them require an immediate response with some result. |
@lhecker Well I was kind of hoping that there wouldn't be any of significance. I haven't actually gone through the whole list, so I might be wrong about that, but I know at least Then in the rare case (hopefully) that you have an app that doesn't work well with passthrough, you can open a profile with the non-passthrough version of conpty, and run the app from there. Worst case we just leave the current conpty implementation as the default, and you switch to the passthrough profile only when needed, or only enable it by default for WSL shells.
But if doesn't have anything to do with conpty passthrough, then I don't get why we're discussing it here. Like do we need a condrv lib in order to support conpty passthrough, or do we need conpty passthrough to support a condrv lib? If the answer is no in both cases, then it's just sidetracking us. We should open a separate issue for implementing a condrv lib and anyone interested in that can discuss the subject there. |
Hmm I see what you mean... I'll stop using "passthrough" as a tag/phrase for my work in this area then. If the passthrough mode, as originally described, requires explicit opt-in then many won't use it. Explicit opt-out is unfeasible unless it works with all console applications out of the box. If it were to work with all console applications out of the box, I'd be happy to pursue it right away, because this would allow me to replace the existing VT output code 1:1 with new VT output code.
I only wrote that in response to you mentioning the desync issue. It's the only robust solution to that problem that I can personally think of right now and it doesn't have anything to do with passing through input unmodified. I didn't mean to detract attention from the PR's contents. I'm sorry. |
I'll just note that Windows Terminal already doesn't work with all console applications out of the box.
I should probably reserve judgement until it's out of draft, but my initial impression was that this PR would make things worse. But if you can get it working somehow that would be brilliant, because it does seem a lot simpler than a full passthrough. |
Just driving by to say that I am profoundly averse to this solution, because I do not believe we should expose random applications to the implementation details of condrv or console API servicing. It brings us further away from the goal of converging on VT as a standard rather than closer to it. |
@lhecker In case it helps, these are some of the areas I thought might be problematic when I was investigating this before:
Just to be clear, I'm not expecting you to answer these question - I don't want to waste your time responding to me when you could be getting on with the code. I just want to make sure there aren't a lot of edge cases that you haven't considered. |
This comment was marked as outdated.
This comment was marked as outdated.
43967da
to
0052725
Compare
@check-spelling-bot Report🔴 Please reviewSee the 📂 files view or the 📜action log for details. Unrecognized words (1)atvis Previously acknowledged words that are now absentvtapi :arrow_right:Some files were automatically ignoredThese sample patterns would exclude them:
You should consider adding them to:
File matching is via Perl regular expressions. To check these files, more of their words need to be in the dictionary than not. You can use To accept ✔️ these unrecognized words as correct and remove the previously acknowledged and now absent words, run the following commands... in a clone of the git@github.com:microsoft/terminal.git repository curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.21/apply.pl' |
perl - 'https://github.com/microsoft/terminal/actions/runs/6747991815/attempts/1' Pattern suggestions ✂️ (1)You could add these patterns to .github/actions/spelling/patterns/0052725198dbbcef0ed7a07eb00379d0d7156fd2.txt:
Warnings (2)See the 📂 files view or the 📜action log for details.
See ℹ️ Event descriptions for more information. ✏️ Contributor please read thisBy default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
If the listed items are:
See the 🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉 If the flagged items are 🤯 false positivesIf items relate to a ...
|
❌ This cannot be merged as is, because I modified
Terminal::_systemMode
, but I actually need to transmit theDISABLE_NEWLINE_AUTO_RETURN
flag from the conhost side properly. I don't know how to do that and only after I do, this can be merged.This follows a different approach than the original. Instead of splitting the console API into a non-VT and VT style implementation via an interface and virtual dispatch, this one will focus on using if/else conditions. Most importantly, it hooks into
WriteChars()
to bypass theVtEngine
renderer and send the given input directly to the ConPTY client. This ensures we preserve the VT input as-is and improves throughput ~7x.My hope is that in the future we don't disable
VtEngine
, but rather enable it for all console APIs individually and flush it before returning from the console call. Then we can work towards reimplementing each such console API in terms of VT sequences if ConPTY is enabled (i.e. usingif()
and early returns).This still doesn't work 100% with pwsh. It doubles each typed character and then only removes every second character when backspacing. I can't quite figure out what's going on.
Validation Steps Performed
TBD