zed
zed copied to clipboard
High refresh rate support
Check for existing issues
- [X] Completed
Describe the feature
Scrolling through text in Zed feels choppier compared to Visual Studio Code on a high refresh rate display (MacBook Pro M1). It would be great if Zed supported rendering at high (or variable) refresh rates.
I'm not really sure how to verify if this is the case but is rendering capped at 60 fps right now?
If applicable, add mockups / screenshots to help present your vision of the feature
No response
Zed should adapt to the refresh rate of the screen it is being displayed on, so I think something weird is happening here. It would be great if you could enable the Metal Performance HUD:
/bin/launchctl setenv MTL_HUD_ENABLED 1
And then restart Zed. Something like this should appear on the top-right corner:
It would also be really helpful if you could capture a CPU Profile (via Instruments.app) of the zed
process while you scroll around. Thank you!
Hello, I feel I have same issue. I recorded Metal Performance HUD:
https://github.com/zed-industries/zed/assets/39122854/cedc0afc-e5b1-4115-944b-aac6bfb49fc4
but I was not able to run CPU Profile, it crash every time when I select CPU Profiler in Instruments.app.
I opened Zed with MTL_HUD_ENABLED=1
. I then kept scrolling to the bottom of the page, which took a few seconds as it was a large document. After the initial ramp up, the FPS (while scrolling) settled, averaging around 70fps.
I'll see if I can get Instruments.app running to get a CPU profile. I need to set up Xcode first though.
It does seem like variable refresh rate works though, so maybe worth relabeling this as a bug.
Here's a trace of me scrolling in Zed: zed-scrolling.trace.zip
FWIW, I also opened devtools and did some profiling in vscode. I can confirm that vscode scrolls at a consistent 120fps (~8.3ms per frame).
Hey @szlend and @oliver-kriska! Thanks for reporting. I've also seen some interesting FPS "problems" in past few days but I can't really track them down.
Me and @as-cii looked at your trace (thanks!) but nothing really stood out. We also couldn't reproduce it:
- I opened https://raw.githubusercontent.com/microsoft/TypeScript/main/src/compiler/types.ts in Zed and VS Code on my M3 Max display and scrolled with the MacBook touchpad as far as I could. I reached the same FPS numbers in both: ~110-120 depending on scroll speed.
Can you give us some more data points? All of the following might have an influence:
- Zed configuration: buffer font-family? UI font-family? Font-sizes? Line-height? Ligatures enabled or not?
- File you're scrolling through: what file? How long is file? Do you use a language server? What's the configuration (inlay hints, diagnostics, hints) you use there? Lots of errors/diagnostics to show?
- Display: what's the resolution and refresh rate? Do you have an external display connected too?
- How do you scroll? Keyboard/trackpad/mouse wheel? Does it show up with another mouse? Do you use consistent as-fast-as-possible scrolling or do you use the "momentum" thing of the touchpad?
Maybe we can also turn this around: can you open the types.ts
file and see how fast you can scroll in that one?
Also, I know this is asking too much, but if you're in the mood: a phone recording of the Zed on a display being scrolled would be ideal, becaues that way it's easy to see how fast the scroll is (because it's hard to capture on a screenrecording).
@mrnugget I think I might have figured out how to reproduce this. The scrolling is smooth as long as the editor is open in windowed mode. As soon as I fullscreen it (so it moves to another workspace) it stars lagging again.
With the Metal HUD I can see that it switches from Composited
to Direct
when going fullscreen.
Let me know if you're able to reproduce it this way. If not, I can give you more data points as requested, or we can hop on a call.
Okay, that's very interesting. From what I understand, Direct
should be faster than Composited
.
I still can't reproduce it, even in fullscreen mode.
- Zed configuration: Defaults. I disabled all customizations to test this.
- File you're scrolling through: types.ts
- Display: Just the MacBook Pro M1 display (Sonoma 14.2.1), 3456 × 2234 (scaled 1728 x 1117) @ 120hz
- How do you scroll? Macbook trackpad. Keep scrolling to the bottom without losing momentum. But it's the same effect if you just jiggle up and down repeatedly. Or if you use the mouse cursor to move the scrollbar. I don't think it's related to the input device.
Another repro here:
Zed configuration
{
"theme": "Summercamp",
"telemetry": {
"diagnostics": false,
"metrics": false
},
"vim_mode": true,
"ui_font_size": 16,
"buffer_font_size": 16
}
File you're scrolling through https://github.com/haze/rehype-tree-sitter/blob/main/src/lib.rs
Display Default 2023 14" Display (3024x1964 @ 120Hz (as 1512x982) [Built-in])
How do you scroll Same as @szlend; scrolling all the way to the top and bottoms of file, jiggling, etc.
When I'm scrolling in Direct
mode the Metal HUD shows the min FPS exactly 60 and max FPS exactly 120. To me it seems like variable refresh rate doesn't work in Direct mode and it alternates between 120fps and 60fps vsync. But that's just a guess.
Edit: Actually, I think it's the same in Compositor
mode but the framerate is more consistent for some reason. I think variable refresh rate simply doesn't work.
File you're scrolling through: types.ts
Display 240Hz 1440p monitor rendering in scaled 5K - Gigabyte M27Q X, hooked up via Type-C to my M1 Air
How do you scroll? MacBook trackpad. I tried to scroll fast without losing inertia. Zed is definitely scrolling faster using the trackpad, it's just less smooth.
Zed config
``` { "project_panel": { "dock": "right" }, "vim_mode": true, "base_keymap": "VSCode", "show_copilot_suggestions": false, "theme": "Rosé Pine", "buffer_font_size": 15, "buffer_font_family": "Hack", "formatter": "prettier" } ```
I have a VFR 240Hz, 1440p monitor set as fake HiDPI (macOS is rendering in 5K and scaling down to 1440p), so I figured I compare VSCode (Insiders) scrolling with Zed's (I have stable channel version installed).
Here's a screen recording with FPS meters in both VSCode and Zed. It doesn't really show the difference, but in person the difference is pretty apparent. https://github.com/zed-industries/zed/assets/69842744/83373266-527d-495d-8944-9b50a9787f2f
The faster I scroll the slower it gets on both editors, but Zed's "baseline", normal speed scrolling is ~70-100fps, while VSCode's is 200-240fps.
Hmmm, I wonder if that means the issue is in our editor somewhere, rather than in the framework 🤔, thanks for the research @rev4324
Hey all! Thanks for the recordings and data everyone. We're still debugging this.
The problem is that this is really hard to reliably reproduce. It's also hard to know which measurements are exact and which aren't. Example: Apple's Metal HUD seems like it has a life of its own and sometimes it shows the Hz of the external display on which it's displayed, sometimes it doesn't. VS Code's FPS meter constantly shows 120fps, even if nothing happens on screen.
I can say that I can get 120fps to show up on the MBP display when scrolling in Zed, BUT the Pre
times that are on the Metal HUD (supposedly "time between presents" or "frame times" according to Apple) don't match what we log as frame times. See here:
https://github.com/zed-industries/zed/assets/1185253/55663864-d5bd-41f4-9410-ead20aa1bf78
The frame durations
we log (when ZED_MEASUREMENTS=1
is set) are consistently <5ms, yet the Metal HUD shows Pre
time as 18ms:
That's one interesting thing I want to investigate.
The other is that it's really hard to reliably reproduce any consistent frame rates: sometimes I get ~60FPS on this external display, sometimes ~80fps, sometimes on the MBP it hovers around 80fps, sometimes around 120fps.
The other thing worth mentioning here is: does this maybe have something to do with the scrolling itself and not the FPS? VS Code has a very high scroll speed. It's really easy to just flick on the touchpad and scroll down 400-500 lines, vs. the 250 you get in Zed. So maybe scrolling is involved too. Investigating.
If anyone wants to help investigate this further:
- Can you try running Zed with
ZED_MEASUREMENTS=1
and see what frame durations are reported and whether they match what's reported in the Metal HUD? - @as-cii and I have investigated a new approach for our main render loop, which is simplified and entirely based on macOS' display link. It could be faster (it feels faster but the HUD doesn't confirm, at least not consistently). So if you want to try this out, that'd be very interesting:
- Check out
display-link
branch - Run
cargo run --release
- Scroll your heart out, see if this feels different
I tried that branch and it feels same. And hud shows lower FPS, max about 80. Preview shows max about 110 FPS. Frame duration in console is about 5-9 and a few 4, not a lot. So it's a little slower, that's my basic feelings.
Just dropping more data in here. I took profiles/traces of Zed in Instruments.app, only on the MBP, no external display attached.
And, well: we do hit the 8ms pretty consistently. There are some blocking wait times, but as far as I understand everything that's going on that's because the main thread is blocked on events. So these blocks also coincide with me hitting the touch pad to scroll (I have to admit: I learned in the past few days that I'm pretty bad at consistently scrolling fast through a 10k line file a touch pad...)
I also had noticeable jitter on the 120hz MBP display a couple of days ago (gone today) and it reminded me of https://github.com/neovide/neovide/pull/2102
I also had noticeable jitter on the 120hz MBP display a couple of days ago (gone today) and it reminded me of neovide/neovide#2102
Yup, that's what we have implemented in the display-link
branch. I'll try to make progress on that today as much as possible.
Little end-of-day-update:
- Made progress on the
display-link
branch, removed the blocking call, made it redraw on window resize and window launch. So now it should work as before. - I still can't really say that we don't support high-refresh, because I can get it to render at ~120fps with and without that branch.
- @ConradIrwin, @bennetbo and I then investigated the scrolling more and pushed a commit to the branch that does the same thing that
winit
does: it uses the scale-factor when doing pixel-scrolling. https://github.com/zed-industries/zed/commit/229f7e44fe8ed3bdd988fa6cfd39cc1956997e14 That makes scrolling a lot faster. It still feels a bit off though. Maybe we need a logarithmic-scale here? But then again: VS Code does its own thing and has really fast and aggressive scroll. So maybe we need to lean into that direction completely?
- The
display-link
branch inDirect
mode feels much smoother now. However theComposited
mode is now the laggy one. - In the Metal HUD I still see FPS alternating between 60fps and 120fps exactly. Is Zed running at vsync and Metal just throws the extra frames away? The M1+ laptops should support variable refresh rates so I would expect framerates in-between as well. I suspect this is what makes the scrolling actually feel choppy.
- I'm not sure if I like the new scrolling behavior. I thought the old one was fine (I didn't actually notice the difference to vscode until I compared the two side by side). But the new scrolling behavior immediately feels off - way too twitchy.
In the Metal HUD I still see FPS alternating between 60fps and 120fps exactly. Is Zed running at vsync and Metal just throws the extra frames away? The M1+ laptops should support variable refresh rates so I would expect framerates in-between as well. I suspect this is what makes the scrolling actually feel choppy.
Whoa, that's really interesting. On my M3 I get variable FPS, up to 120fps, but it's never really exact. I'm going to later try this on the M1 I have here.
And yes, the display-link
branch is essentially handing the reigns to the OS so it can request a frame when it needs to, which is how Neovide did it too: https://github.com/neovide/neovide/pull/2102 I'm still not 100% convinced of this approach here and what the HUD says.
So overall you would say the display-link
branch is not an improvement because Direct
is smoother but composite
is laggy?
Whoa, that's really interesting. On my M3 I get variable FPS, up to 120fps, but it's never really exact. I'm going to later try this on the M1 I have here.
Note that I'm ignoring the average FPS and only looking at min
, max
framerate and the pre
chart.
See the pre
and fps
times match vsync exactly. The pre
chart shows the spikes over time and they always exactly double.
So overall you would say the display-link branch is not an improvement because Direct is smoother but composite is laggy?
Side-grade yeah. I usually program in full screen so for me it's an improvement :)
That does look like it's working as intended though, right? FPS is variable between 60 and 120hz here.
That does look like it's working as intended though, right? FPS is variable between 60 and 120hz here.
No, the FPS shown is average FPS. If I have one frame rendered at 60fps intervals and two frames rendered at 120fps intervals, then the average framerate is 80fps.
Look at the blue spikes in the Pre
chart. It's alternating between 8.33ms and 16.67ms.
Ah, I see what you're saying. It's not really variable, but only flipping between 60 and 120hz?
Ah, I see what you're saying. It's not really variable, but only flipping between 60 and 120hz?
Yeah exactly. Afaik MacOS has an API for Adaptive Sync which is supposed to match the screen refresh rate to the content rendered by the GPU as opposed to the other way around. Right now when the renderer misses a 120hz frame interval, it has to wait for another (60hz).
That would explain the choppiness, as a sudden 50% framerate drop is pretty jarring. I'm not saying adaptive sync is necessarily the solution though (it might be more trouble than it's worth). But if zed is able to consistently deliver frames at 120fps+, then vsync isn't really a problem. If it isn't able to, then adaptive sync can help.
Interesting. On my M3 Max I don't see the flip-flopping between 8ms/16ms. See screenshot above:
Also worth noting: according to this documentation on Adaptive Sync one requirement for adaptive sync is
And lastly, your app needs to be running in full-screen mode.
So that wouldn't buy us much, since you said that in Direct
rendering mode the display-link
branch is already smoother and Direct
is what's being used when you're fullscreen.
Just adding another puzzle piece: on the M3 Max display (no other display attached, attached to power), I can scroll through types.ts
and hit 120fps pretty consistently. It only drops down to ~10ms frame time when I hit the touch pad. The drops in CPU/context-switches/... and spikes in frame time here happen every time my fingers hit the touch pad:
Yes, I recorded myself doing this and then saw these bumps show up in the HUD too.
It's interesting to me that your max
FPS is going up to 575FPS. Is that right? For me it never goes above exactly my refresh rate (120fps with ProDisplay, or 60fps if I change to 60hz). This suggests that there's something different to our setups.
The drops in CPU/context-switches/... and spikes in frame time here happen every time my fingers hit the touch pad
Same here, but I don't think it's completely isolated to input.
It's difficult to compare smoothness of the display-link branch with the trackpad input boosted. What should I edit so it matches main?
I just reverted that commit on the branch. You can try that again.