Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Consider capping FPS when the terminal is being flooded with data #4135

Closed
Tyriar opened this issue Sep 23, 2022 · 16 comments
Closed

Consider capping FPS when the terminal is being flooded with data #4135

Tyriar opened this issue Sep 23, 2022 · 16 comments
Labels
area/performance type/proposal A proposal that needs some discussion before proceeding

Comments

@Tyriar
Copy link
Member

Tyriar commented Sep 23, 2022

See microsoft/vscode#160118

@0x00C5
Copy link

0x00C5 commented Sep 24, 2022

I posted a follow up on #160118 to help you recreate the issue. I'm not sure this is the correct diagnosis as the terminal is not flooded with data.

@jerch
Copy link
Member

jerch commented Sep 25, 2022

@Tyriar I not sure if that went out of focus too fast - @0x00C5 states several times, that the terminal has lit. no data to process in between. So the terminal might not even be the root of the additional processing time?

@Tyriar
Copy link
Member Author

Tyriar commented Sep 26, 2022

Yeah I was looking at another issue as well, must have gotten mixed up. The proposal still stands though, I've been thinking about a battery/low-power mode for VS Code for a while which would cap FPS to 10 fps or something.

@0x00C5
Copy link

0x00C5 commented Sep 27, 2022

Personally I don't see why a terminal would need more than 10 FPS. I'd prefer speed of build over real-time terminal output (plus, building is less resource intensive than reflowing the terminal). Perhaps when animation frame comes around, maybe only run the terminal update every 5th iteration with a flag to enable full 60 FPS for those who need it.

@jerch
Copy link
Member

jerch commented Sep 27, 2022

Personally I don't see why a terminal would need more than 10 FPS. I'd prefer speed of build over real-time terminal output

Well a terminal is mostly about interactivity, thus high FPS is needed to not expose a high input latency. Typically input lags are perceivable >30ms, and fast typers might even recognize latencies >20ms. Now the browser engines cap screen refresh at 60 FPS, which translates to ~16ms for screen update cycles. That leaves ~4ms for everything else, which is almost impossible to stay below (note that TEs are dumb terminals, thus have to send the input to the PTY, which sends the characters back for echoing). And that "backend-line" is esp. long for xterm.js due to several layers being involved (nodejs buffers, websocket, browser event loop). And for ssh connections things get even worse.
From the sampling theorem we know that frequencies should be at least twice as high to avoid perceivable artefacts. For xterm.js it means that backend latency must stay far below 8ms to get more than half of the typed chars within the next screen update cycle (screen update is the sampling frequency here). For busy event loops (much work placed on the browser page) that cannot be guaranteed anymore, thus most latency measurements for xterm.js integrations will see an overall latency of +32 ms (chars shown on second or even later update cycles). If the default FPS would be only 10, the screen update latency would jump to 100ms, not a good idea at all.

TL;DR: I cannot agree with you - high FPS is a must for a TE to avoid sluggish typing response.

The opposite happens, if app side creates tons of output (e.g. a very noisy compiler). Here the output latency is not as important anymore (the user cannot read along at all anymore), so it is a good idea to scale down FPS on purpose to save some processing time.

@Tyriar
Copy link
Member Author

Tyriar commented Sep 27, 2022

@jerch I was actually thinking we would do this by keeping the screen update after an input event as it is today, but throttling the rest. This would avoid the input latency problem while saving frames. Whether this applies to scrolling I'm not sure as that's one of the bigger things that determines how smooth or laggy as app feels.

@0x00C5 on why the terminal can do 60+ fps (up to 240 on my monitor 👀), I've used terminals that cap the frame rate and they just don't feel as nice to use imo. The amount of data needed to have a real difference is quite large anyway and rarely noticed in practice. A higher FPS also gives you additional visual feedback of how much output the command produced.

The hope is to someday move parsing to a worker (#1515) which would mean that rendering would only contend with other things in the application, such as the editor in VS Code. Part of the reason for the slowdown in parsing is because it's sharing the thread we need to split the parsing task up to keep the app responsive, which adds a bunch of overhead. That's not a problem if it's done in a worker.

@0x00C5
Copy link

0x00C5 commented Sep 27, 2022

@jerch I agree with you. I didn't mean to imply the terminals frame rate should be permanently capped. An immediate response to input is absolutely necessary and should be high priority and not throttled. I was referring to enabling a low FPS when building, even if the program floods the console (which I assume would be fairly rare in practice), it could give the compiler a higher priority but still be responsive.

@Tyriar sure I meant lowering the frame rate during build time which (I would imagine) shouldn't affect the overall responsiveness if the terminal spit out lines/chunks every 80ms verses 16ms. You'd still see lots of data coming thru at 12 FPS.

Granted, I'm not sure what a good frame rate would be or what the tradeoffs would be, but I wouldn't mind a option that capped the frame rate during build time or changed the weight of the build vs output.

@jerch
Copy link
Member

jerch commented Sep 29, 2022

@0x00C5 Well proper FPS is a very old discussion. Citing some details from Wikipedia: Old filmmakers of the area of silent films typically shot in only 16-20 FPS, while a later industry standard specced it at 24 FPS (note that much earlier Edison already propagated 48Hz as optimal for the human eye, but this was too cost intensive, therefore most projectors doubled the 24 FPS frames). TV did the interlacing trick, staying pretty close to that 24 value, but faking higher refresh rates by updating half images (at 50 or 60 Hz). For a while computer CRTs did the same, but since it was very stressful for the users, they raises refresh rates pretty quick, often in multiples of 30 or 60 Hz (thus 90, 120 or even 240 Hz, depending on resolution, as the beam had to get across the phosphor dots in that time), and later switched to progressive scanning.
Todays digital displays mostly operate in 60 Hz or 120 Hz. Why the higher 60Hz instead of a multiple of 24 took place idk - maybe because of technical limitations (higher device latencies might stack in the sense of sampling theorem creating more negative artefacts?)

Long story short - it seems 24 FPS is the bare minimum, but already stressful to watch for a longer time. To ensure updates get through in time we again have the sampling theorem issue, which tells us to get at least at 48 FPS. You can check this for yourself with an old webcam, they typically would send images at 30 FPS - thats sluggish as hell.

Now a terminal is not about watching a movie - true. Still users will have the same "sluggish feeling" from low terminal refresh rate, as thats a matter of perception and not of the content presented. Whether 30 FPS is still okish for most peeps in a terminal - idk. Anything below 30 FPS is certainly not, as content will break into stop-and-go.

Whether xterm.js would benefit from FPS halving (from 60 to 30):

  • DOM renderer - yes, as frame output is quite expensive there taking up to 50% of runtime, thus ~25% total runtime could be saved
  • canvas renderer - small benefit, rendering costs are at 20-30%, thus only 10-15% total runtime might be saved
  • webgl - nope, its so fast, that lowering FPS will account for <<5% runtime

Summary:
I dont see much use in lowering FPS for canvas and webgl renderer. For DOM renderer this might be an option though.

@Tyriar
Copy link
Member Author

Tyriar commented Sep 29, 2022

Reducing battery usage was one of my motivations for this topic, I probably won't bother looking into it unless VS Code as a whole has a push for such a low power mode

@0x00C5
Copy link

0x00C5 commented Sep 29, 2022

@jerch All your points are true. Since the Terminal is a line-oriented dump of text that scrolls by a fixed amount per line, I wouldn't think it requires the same refresh rate as the DOM to catch pixel-by-pixel scrolling. I suggested 12 FPS simply because terminal output is typically chunky in practice and can tolerate some gitter, unlike a video.

The best FPS to aim for would probably be best demonstrated with an example to see the tradeoffs specific to terminal output. I read an article while back [1] that showed the same GIF rendering at 8 to 60 FPS. Perhaps simulating the xterm output with a GIF at different frame rates would be a better way to see where an acceptable threshold would be? Maybe 12 FPS would be OK? Maybe 30? I dunno. The article mentions all modern browsers cannot render gifs at 60 FPS or greater.

Personally, I'd prefer a configuration option to customize xterm's OUTPUT FPS in steps of 6 FPS to 60 FPS (or whatever) which in turn can be used by different vscode modes. For example, a "low power mode" (like @Tyriar mentioned) which could cap INPUT and OUTPUT FPS. Or a "build mode" (like I want) which could cap the OUTPUT FPS during builds.

Note I define OUTPUT FPS as xterm waiting for its prompt to be yielded back.

[1] https://wunkolo.github.io/post/2020/02/buttery-smooth-10fps/

@jerch
Copy link
Member

jerch commented Sep 29, 2022

@0x00C5 Oh thats an interesting test with the black circle. For me only 33 & 50 FPS dont show any tearing/shadowing, all slower ones have bad artefacts here.

@0x00C5
Copy link

0x00C5 commented Sep 30, 2022

I would think skipping a couple animation frames to lower the FPS wouldn't cause any tearing/shadowing as the browser should still sync it at the proper interval. It will obviously get more choppy/jittery the frame rate gets lower, but it *may* not induce artifacts.

@jerch
Copy link
Member

jerch commented Sep 30, 2022

I would think skipping a couple animation frames to lower the FPS wouldn't cause any tearing/shadowing as the browser should still sync it at the proper interval. It will obviously get more choppy/jittery the frame rate gets lower, but it may not induce artifacts.

No clue how the browser deals with that - I clearly see "shadows" of circle parts with the slower FPS, but not for 33+. Maybe some interference pattern from gif FPS to display FPS, idk.

@Tyriar
Copy link
Member Author

Tyriar commented Sep 30, 2022

I've gotten used to my 144 and 240 FPS monitors and I can definitely detect the difference between 60 and 144, 144-240 not so much.

You're probably right that there is a sweet spot though since it's not a circle animating where we could cap FPS, however that doesn't actually help us that much in increasing throughput. Reducing FPS helps by potentially reducing battery usage, but as for increasing throughput I don't think we can't do much. Regardless of an FPS target, we need to ensure the embedding application window remains responsive, which means we still need to limit parsing to <= a single frame (we target ~16ms for 60 FPS currently). The real fix for throughput is the worker discussion in #1515

@Tyriar
Copy link
Member Author

Tyriar commented Sep 30, 2022

The highest fps possible representable in the GIF file format specification is 100 fps (0.01s), which no browser supports and 60hz consumer screens cannot faithfully display this anyways.

Also FYI this is inaccurate now FYI:

image

@0x00C5
Copy link

0x00C5 commented Sep 30, 2022

Maybe some interference pattern from gif FPS to display FPS, idk.

That's my understanding too as most of the gifs frames won't be in phase with the refresh rate of the screen. I think af avoids those issues.

Regardless of an FPS target, we need to ensure the embedding application window remains responsive, which means we still need to limit parsing to <= a single frame

Maybe. There might be some wiggle room. I have no idea what the sweet spot would be without some experimentation. I personally wouldn't mind the embedded terminal yielding to the parser or an option to due so like I mentioned earlier. I think the terminal is unique in that it can get away with a lower fps when dumping its output (regardless of the amount of output).

@xtermjs xtermjs locked and limited conversation to collaborators Dec 15, 2022
@Tyriar Tyriar converted this issue into discussion #4318 Dec 15, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
area/performance type/proposal A proposal that needs some discussion before proceeding
Projects
None yet
Development

No branches or pull requests

3 participants