zed icon indicating copy to clipboard operation
zed copied to clipboard

High refresh rate support

Open szlend opened this issue 7 months ago • 48 comments

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

szlend avatar Jan 24 '24 19:01 szlend

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:

image

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!

as-cii avatar Jan 25 '24 12:01 as-cii

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.

oliver-kriska avatar Jan 25 '24 15:01 oliver-kriska

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.

szlend avatar Jan 25 '24 18:01 szlend

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).

szlend avatar Jan 25 '24 20:01 szlend

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 avatar Jan 26 '24 11:01 mrnugget

@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.

szlend avatar Jan 26 '24 16:01 szlend

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.

mrnugget avatar Jan 26 '24 16:01 mrnugget

  • 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.

szlend avatar Jan 26 '24 17:01 szlend

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.

haze avatar Jan 26 '24 17:01 haze

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.

szlend avatar Jan 26 '24 17:01 szlend

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.

rev4324 avatar Jan 27 '24 22:01 rev4324

Hmmm, I wonder if that means the issue is in our editor somewhere, rather than in the framework 🤔, thanks for the research @rev4324

mikayla-maki avatar Jan 28 '24 03:01 mikayla-maki

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:

screenshot-2024-01-29-14 03 59@2x

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:

  1. 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?
  2. @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

mrnugget avatar Jan 29 '24 13:01 mrnugget

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.

oliver-kriska avatar Jan 29 '24 13:01 oliver-kriska

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...)

screenshot-2024-01-29-16 14 50@2x

mrnugget avatar Jan 29 '24 15:01 mrnugget

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

diocletiann avatar Jan 30 '24 03:01 diocletiann

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.

mrnugget avatar Jan 30 '24 08:01 mrnugget

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?

mrnugget avatar Jan 30 '24 17:01 mrnugget

  • The display-link branch in Direct mode feels much smoother now. However the Composited 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.

szlend avatar Jan 31 '24 08:01 szlend

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?

mrnugget avatar Jan 31 '24 08:01 mrnugget

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.

Screenshot 2024-01-31 at 09 10 03 Screenshot 2024-01-31 at 09 12 06

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 :)

szlend avatar Jan 31 '24 08:01 szlend

That does look like it's working as intended though, right? FPS is variable between 60 and 120hz here.

mrnugget avatar Jan 31 '24 08:01 mrnugget

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.

szlend avatar Jan 31 '24 08:01 szlend

Ah, I see what you're saying. It's not really variable, but only flipping between 60 and 120hz?

mrnugget avatar Jan 31 '24 08:01 mrnugget

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.

szlend avatar Jan 31 '24 08:01 szlend

Interesting. On my M3 Max I don't see the flip-flopping between 8ms/16ms. See screenshot above:

screenshot-2024-01-31-15 40 27@2x

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.

mrnugget avatar Jan 31 '24 14:01 mrnugget

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:

screenshot-2024-01-31-16 12 41@2x

Yes, I recorded myself doing this and then saw these bumps show up in the HUD too.

mrnugget avatar Jan 31 '24 15:01 mrnugget

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.

szlend avatar Jan 31 '24 15:01 szlend

It's difficult to compare smoothness of the display-link branch with the trackpad input boosted. What should I edit so it matches main?

diocletiann avatar Jan 31 '24 17:01 diocletiann

I just reverted that commit on the branch. You can try that again.

mrnugget avatar Jan 31 '24 17:01 mrnugget