Skip to content
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

Slow shell start on niri #8236

Closed
kosayoda opened this issue Jan 20, 2025 · 14 comments
Closed

Slow shell start on niri #8236

kosayoda opened this issue Jan 20, 2025 · 14 comments
Labels

Comments

@kosayoda
Copy link

Describe the bug
I seem to be running into the same issue as described in #7540, but with niri instead of labwc.

To Reproduce
Steps to reproduce the behavior:

  1. Run niri with kitty: niri -- kitty.
  2. The shell does not appear for 250ms after the terminal window appears.

Environment details

$ niri --version
niri 25.01
kitty 0.39.0 created by Kovid Goyal
Linux quack 6.12.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 10 Jan 2025 00:39:41 +0000 x86_64
S{PRETTY_NAME} 6.12.9-arch1-1 (/dev/tty)

DISTRIB_ID="Arch"
DISTRIB_RELEASE="rolling"
DISTRIB_DESCRIPTION="Arch Linux"
Running under: Wayland (niri 25.01 (unknown commit)) missing: single_pixel_buffer
OpenGL: '4.6 (Core Profile) Mesa 24.3.3-arch1.2' Detected version: 4.6
Frozen: False
Fonts:
  medium: NotoSansMono-Regular: /usr/share/fonts/noto/NotoSansMono-Regular.ttf:0
          Features: ()
    bold: NotoSansMono-Bold: /usr/share/fonts/noto/NotoSansMono-Bold.ttf:0
          Features: ()
  italic: NotoSansMono-Regular: /usr/share/fonts/noto/NotoSansMono-Regular.ttf:0
          Features: ()
      bi: NotoSansMono-Bold: /usr/share/fonts/noto/NotoSansMono-Bold.ttf:0
          Features: ()
Paths:
  kitty: /usr/bin/kitty
  base dir: /usr/lib/kitty
  extensions dir: /usr/lib/kitty/kitty
  system shell: /usr/bin/fish
System color scheme: no_preference. Applied color theme type: none

Config options different from defaults:

Important environment variables seen by the kitty process:
	PATH                                /usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/lib/rustup/bin
	LANG                                en_US.UTF-8
	EDITOR                              nvim
	SHELL                               /usr/bin/fish
	GLFW_IM_MODULE                      ibus
	DISPLAY                             :0
	WAYLAND_DISPLAY                     wayland-1
	USER                                kosa
	XCURSOR_SIZE                        24
	XDG_VTNR                            1
	XDG_SEAT                            seat0
	XDG_SESSION_TYPE                    wayland
	XDG_DATA_HOME                       /home/kosa/.local/share
	LC_ALL                              en_US.UTF-8
	XDG_STATE_HOME                      /home/kosa/.local/state
	XDG_RUNTIME_DIR                     /run/user/1000
	XDG_CONFIG_HOME                     /home/kosa/.config
	XDG_SESSION_CLASS                   user
	XDG_CURRENT_DESKTOP                 niri
	XDG_SESSION_ID                      1
	XDG_CACHE_HOME                      /home/kosa/.cache

Additional context

  1. This only happens when the output scale is set to 1.0 in the niri configuration, the shell starts instantly if any other scale is used.
  2. I reached out to the maintainers of niri, with the following response (that I don't understand fully):

well yeah niri doesn't send preferred integer scale for scale = 1

the wayland protocol says:

Before receiving this event the preferred buffer scale for this surface is 1.

so niri doesn't send it because it is already 1 by default

so this sounds indeed like kitty trying to work around buggy compositors

i don't think it's an easy fix in smithay either because smithay doesn't have a good point to send preferred buffer scale = 1. It can't tell when the compositor hasn't set it yet and is going to or isn't going to

@kosayoda kosayoda added the bug label Jan 20, 2025
@kovidgoyal
Copy link
Owner

I don't see what kitty can do here. If niri claims to support fractional scale and preferred integer scale but then doesn't send those events for scale 1 surfaces then there is no way for kitty to know when niri is finished configuring the surface. I dont know what wayland backend niri uses but every other compositor that claims to support these protocols sends these events for scale 1 surfaces. And the wayland protocol has no other way to know when the compositor is done with initial set up for a window.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jan 21, 2025

And just for the record, here is the debug rendering log for niri:

[0.044] Compositor missing capabilities: single_pixel_buffer
[0.118] Creating window 1 at size: 640x400 and scale 1
[0.118] CSD: old.size: 0x0 new.size: 640x400 needs_update: 1 size_changed: 1 state_changed: 0 buffer_destroyed: 0
[0.119] Created decoration buffers at scale: 1.000000
[0.123] Fractional scale requested: 120/120 = 1.00 for window 1
[0.123] Resizing framebuffer of window: 1 to: 640x400 window size: 640x400 at scale: 1.000
[0.123] Waiting for swap to commit Wayland surface for window: 1
[0.123] CSD: old.size: 640x400 new.size: 640x400 needs_update: 0 size_changed: 0 state_changed: 0 buffer_destroyed: 0
[0.123] Compositor set top-level bounds of: 3808x2128 for window 1
[0.123] Compositor top-level capabilities: maximize=0 minimize=0 window_menu=0 fullscreen=1
[0.123] XDG top-level configure event for window 1: size: 1896x2128 states: 
[0.123] XDG surface configure event received and acknowledged for window 1
[0.123] Waiting for swap to commit Wayland surface for window: 1
[0.123] Resizing framebuffer of window: 1 to: 1896x2104 window size: 1896x2104 at scale: 1.000
[0.123] Waiting for swap to commit Wayland surface for window: 1
[0.123] CSD: old.size: 640x400 new.size: 1896x2104 needs_update: 1 size_changed: 1 state_changed: 0 buffer_destroyed: 0
[0.124] Created decoration buffers at scale: 1.000000
[0.124] Final window 1 content size: 1896x2104 resized: 1
[0.124] Setting window 1 "visible area" geometry in configure event: x=0 y=-24 1896x2128 viewport: 1896x2104
[0.124] Attached temp buffer during window 1 creation of size: 1896x2104 and rgba(30, 30, 30, 255)
[0.124] Waiting for compositor to send fractional scale for window 1
[0.128] XDG top-level configure event for window 1: size: 1896x2128 states: TOPLEVEL_STATE_ACTIVATED 
[0.128] XDG surface configure event received and acknowledged for window 1
[0.128] CSD: old.size: 1896x2104 new.size: 1896x2104 needs_update: 1 size_changed: 0 state_changed: 1 buffer_destroyed: 0
[0.128] Final window 1 content size: 1896x2104 resized: 0
[0.128] Setting window 1 "visible area" geometry in configure event: x=0 y=-24 1896x2128 viewport: 1896x2104
[0.129] Attached temp buffer during window 1 creation of size: 1896x2104 and rgba(30, 30, 30, 255)
[0.424] Creating OpenGL context and attaching it to window

We can see it sends a fractional scale of 1 before configure and then nothing after.
With scale != 1 instead

[0.049] Compositor missing capabilities: single_pixel_buffer
[0.135] Creating window 1 at size: 640x400 and scale 1
[0.135] CSD: old.size: 0x0 new.size: 640x400 needs_update: 1 size_changed: 1 state_changed: 0 buffer_destroyed: 0
[0.135] Created decoration buffers at scale: 1.000000
[0.140] Preferred integer buffer scale changed to: 3 for window 1
[0.140] Resizing framebuffer of window: 1 to: 1920x1200 window size: 640x400 at scale: 3.000
[0.140] Waiting for swap to commit Wayland surface for window: 1
[0.140] CSD: old.size: 640x400 new.size: 640x400 needs_update: 1 size_changed: 1 state_changed: 0 buffer_destroyed: 0
[0.150] Created decoration buffers at scale: 3.000000
[0.150] Fractional scale requested: 270/120 = 2.25 for window 1
[0.150] Resizing framebuffer of window: 1 to: 1440x900 window size: 640x400 at scale: 2.250
[0.150] Waiting for swap to commit Wayland surface for window: 1
[0.150] CSD: old.size: 640x400 new.size: 640x400 needs_update: 1 size_changed: 1 state_changed: 0 buffer_destroyed: 0
[0.154] Created decoration buffers at scale: 2.250000
[0.154] Compositor set top-level bounds of: 1675x928 for window 1
[0.154] Compositor top-level capabilities: maximize=0 minimize=0 window_menu=0 fullscreen=1
[0.154] XDG top-level configure event for window 1: size: 829x928 states: 
[0.154] XDG surface configure event received and acknowledged for window 1
[0.154] Waiting for swap to commit Wayland surface for window: 1
[0.154] Resizing framebuffer of window: 1 to: 1865x2034 window size: 829x904 at scale: 2.250
[0.154] Waiting for swap to commit Wayland surface for window: 1
[0.154] CSD: old.size: 640x400 new.size: 829x904 needs_update: 1 size_changed: 1 state_changed: 0 buffer_destroyed: 0
[0.156] Created decoration buffers at scale: 2.250000
[0.156] Final window 1 content size: 829x904 resized: 1
[0.156] Setting window 1 "visible area" geometry in configure event: x=0 y=-24 829x928 viewport: 829x904
[0.156] Creating OpenGL context and attaching it to window

Here kitty doesnt wait because even though the fractional scale was sent before configure and not after, the preferred buffer integer scale of 3 is sent before the configure and so kitty gets a signal that the window is fully setup.

The only "fix" I can see here is to detect niri and try to workaround it or detect hyprland and try to workaround it. It would be a lot more helpful if niri simply sent another fractional scale event after the configure event. Even if the scale is unchanged it serves as a signal that the setup is done and follows the behavior of all other compositors. Or alternatively sends a preferred integer scale event even though 1 is the default value.

@YaLTeR
Copy link

YaLTeR commented Jan 21, 2025

Hey, so yeah, we don't send preferred buffer scale = 1 (or preferred buffer transform = Normal for that matter) because it's the default as per the protocol. I don't see why Kitty needs to wait for anything in this case? If the compositor doesn't send all necessary events by the initial configure, that sounds like a compositor problem to me? As in, if the compositor wanted scale != 1 then it should send it before the initial configure, and if it left scale at default then that's what it should get.

@kovidgoyal
Copy link
Owner

It needs to wait because all other compositors send fractional scale after configure. Which leads to extra resize events which can cause software running inside kitty, not ready to receive SIGWNCH during startup to crash. So if niri simply sends either a preferred buffer scale before configure unconditionally even when the scale is 1 or it sends fractional scale after configure unconditionally even if it is one, kitty will work fine with it.

And yes, you are correct that this is a workaround for bugs (or more precisely sub-optimal behaviors) in other compositors, not in niri. But given that all other compositors work this way I dont have much choice in the matter. So sending one extra event that's harmless will allow kitty to work or if you really dont want to do that, I can try to detect niri and not wait on it.

@kovidgoyal
Copy link
Owner

Here I attach the log from Hyprland as an example:

[0.053] Compositor missing capabilities: blur
[0.127] Creating window 1 at size: 640x400 and scale 1
[0.132] Fractional scale requested: 120/120 = 1.00 for window 1
[0.132] Resizing framebuffer of window: 1 to: 640x400 window size: 640x400 at scale: 1.000
[0.132] Waiting for swap to commit Wayland surface for window: 1
[0.132] Compositor top-level capabilities: maximize=1 minimize=0 window_menu=0 fullscreen=1
[0.132] XDG decoration configure event received for window 1: has_server_side_decorations: 1
[0.132] XDG decoration configure event received for window 1: has_server_side_decorations: 1
[0.132] XDG top-level configure event for window 1: size: 1920x2160 states: TOPLEVEL_STATE_TILED_LEFT TOPLEVEL_STATE_TILED_RIGHT TOPLEVEL_STATE_TILED_TOP TOPLEVEL_STATE_TILED_BOTTOM 
[0.132] XDG surface configure event received and acknowledged for window 1
[0.132] Waiting for swap to commit Wayland surface for window: 1
[0.132] Resizing framebuffer of window: 1 to: 1920x2160 window size: 1920x2160 at scale: 1.000
[0.132] Waiting for swap to commit Wayland surface for window: 1
[0.132] Final window 1 content size: 1920x2160 resized: 1
[0.132] Setting window 1 "visible area" geometry in configure event: x=0 y=0 1920x2160 viewport: 1920x2160
[0.132] Attached temp buffer during window 1 creation of size: 1920x2160 and rgba(30, 30, 30, 255)
[0.132] Waiting for compositor to send fractional scale for window 1
[0.133] XDG top-level configure event for window 1: size: 1920x2160 states: TOPLEVEL_STATE_TILED_LEFT TOPLEVEL_STATE_TILED_RIGHT TOPLEVEL_STATE_TILED_TOP TOPLEVEL_STATE_TILED_BOTTOM TOPLEVEL_STATE_MAXIMIZED 
[0.133] Fractional scale requested: 120/120 = 1.00 for window 1

Notice how it send two fractional scale events each of scale 1, one before the surface is configured and one after the surface is configured and has a buffer attached. If the scale were not 1 the second one would cause a resize.

@kchibisov
Copy link

It needs to wait because all other compositors send fractional scale after configure. Which leads to extra resize events which can cause software running inside kitty, not ready to receive SIGWNCH during startup to crash. So if niri simply sends either a preferred buffer scale before configure unconditionally even when the scale is 1 or it sends fractional scale after configure unconditionally even if it is one, kitty will work fine with it.

It's a compositor problem, I'm not sure who all are in this case, all I know don't do so, and in anyway spec has it written down https://wayland.app/protocols/xdg-shell#xdg_surface .

The compositor will reply with initial wl_surface state such as wl_surface.preferred_buffer_scale followed by an xdg_surface.configure event. The client must acknowledge it and is then allowed to attach a buffer to map the surface.

that's from the spec, which means that they should send the scale along the first configure dance, thus you don't have sizes. If you really want to workaround bugs in compositor buy delaying startup, sure, but pretty much all wayland devs would tell you that it's a hyprland bug that it sends scale 1.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jan 21, 2025

Shrug. I tested all the major compositors at the time I wrote that code and they all send fractional scale after configure causing unnecessary resizes. Maybe things have improved since, I dont know, but I doubt it. It's nice that the spec considers that a bug, but I have to deal with the real world and in the real world compositors send fractional scale after the initial buffer is attached. Here is a log from current sway (1.10) for example:

[0.045] Compositor missing capabilities: blur
[0.120] Creating window 1 at size: 640x400 and scale 1
[0.124] Compositor top-level capabilities: maximize=1 minimize=0 window_menu=1 fullscreen=0
[0.124] XDG top-level configure event for window 1: size: 0x0 states: 
[0.124] XDG decoration configure event received for window 1: has_server_side_decorations: 1
[0.124] XDG surface configure event received and acknowledged for window 1
[0.124] Waiting for swap to commit Wayland surface for window: 1
[0.124] Final window 1 content size: 640x400 resized: 0
[0.124] Setting window 1 "visible area" geometry in configure event: x=0 y=0 640x400 viewport: 640x400
[0.124] Attached temp buffer during window 1 creation of size: 640x400 and rgba(30, 30, 30, 255)
[0.124] Waiting for compositor to send fractional scale for window 1
[0.125] Fractional scale requested: 360/120 = 3.00 for window 1
[0.125] Resizing framebuffer of window: 1 to: 1920x1200 window size: 640x400 at scale: 3.000

Fractional size comes after buffer is attached in configure event. Hyprland is unusual in that it sends a fractional scale of 1 before configure and the real fractional scale after, but that's it. It also sends the actual fractional scale after.

@kchibisov
Copy link

sway resizes way more than it should anyway iirc, so waiting for scale saves nothing on it. The initial size you'll on sway is always (0,0) which means pick yourself, and the real one comes after you've committed a buffer iirc. So it doesn't really save here anything.

I'm pretty sure hyprland will fix this bug if it'll be raised with them, they just probably not aware about it...

So sending one extra event that's harmless will allow kitty to work or if you really dont want to do that, I can try to detect niri and not wait on it.

IIRC it's not just niri who is doing that, I could be wrong but IIRC kde is also doing so, and other compositors could start as well. The issue is not just in niri, but any compositor that uses library smithay, since it's handled in it, so cosmic, etc, etc.

@YaLTeR
Copy link

YaLTeR commented Jan 21, 2025

Hm, seems like current GNOME sends both fractional scale and preferred_buffer_scale before the initial configure, even for scale = 1. Guess if GNOME does, then clients may easily start relying on this indeed, so we should make Smithay also send preferred_buffer_scale even when it's 1.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jan 21, 2025 via email

@vaxerski
Copy link

I'm pretty sure hyprland will fix this bug if it'll be raised with them, they just probably not aware about it...
I certainly hope they do. Ping vaxerski

it was raised once, but it's not invalid behavior and I shrugged it off. If it causes issues, I can look into it. Would be nice if someone opened an issue over at hyprland.

@kchibisov
Copy link

it was raised once, but it's not invalid behavior and I shrugged it off. If it causes issues, I can look into it. Would be nice if someone opened an issue over at hyprland.

It means that the first frame is rendered at low res, thus a not perfect -> bug, and wayland doesn't really like that, hence why we have all those new scale events in the first place. applies both to surface v6 and fractional. I decided not to ping, since one should open an issue, but not sure I would be the one.

@kovidgoyal
Copy link
Owner

Done: hyprwm/Hyprland#9126

@YaLTeR
Copy link

YaLTeR commented Jan 21, 2025

Opened a Smithay issue about sending the initial preferred buffer scale = 1: Smithay/smithay#1640

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants