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

[bug] Resizing on Windows is much slower in Tauri than in Wry #6322

Open
FabianLars opened this issue Jan 3, 2023 · 27 comments
Open

[bug] Resizing on Windows is much slower in Tauri than in Wry #6322

FabianLars opened this issue Jan 3, 2023 · 27 comments
Labels
platform: Windows status: needs triage This issue needs to triage, applied to new issues type: bug

Comments

@FabianLars
Copy link
Member

FabianLars commented Jan 3, 2023

Describe the bug

We all know that Chromium is super slow to resize its content, it's present in all Chromium browsers too, but while trying out a few workarounds to make it less noticeable, like setting the window background color via win32 apis (changing the webview2 background doesn't change anything btw because the webview itself is not resized in time), i noticed that this is pretty much no problem in Wry but it got even worse in Tauri than the last time i really paid attention to it.
Here's a recording. Its resolution and fps makes it look a bit worse than it actually is.

2023-01-03.16-24-49.mp4

Note that i already disabled Tauri's window event handlers in its source code for that recording - no noticeable difference. The 2 apps in the video are release builds pointing to my local dev server. debug builds, and release builds with bundled artifacts have the same behavior.

I could not reproduce this issue on Linux.

Reproduction

Build literally any app on Windows. It's present even in the most lightweight vanillajs apps.

Expected behavior

i don't expect Tauri to be as fast as Wry but it shouldn't feel like my computer is crashing.

Platform and versions

Environment
› OS: Windows 10.0.19045 X64
› Webview2: 108.0.1462.54
› MSVC:
› Node.js: 18.12.1
› npm: 8.19.2
› pnpm: 7.21.0
› yarn: 1.22.19
› rustup: 1.25.1
› rustc: 1.66.0
› cargo: 1.66.0
› Rust toolchain: stable-x86_64-pc-windows-msvc

Packages
› @tauri-apps/cli [NPM]: 1.2.2
› @tauri-apps/api [NPM]: 1.2.0
› tauri [RUST]: 1.2.3, (overwritten with local path for testing but released version has the same problem)
› tauri-build [RUST]: 1.2.1,
› tao [RUST]: 0.15.8,
› wry [RUST]: 0.23.4,

App
› build-type: bundle
› CSP: unset
› distDir: ../dist
› devPath: http://localhost:5173/
› framework: React
› bundler: Vite

App directory structure
├─ dist
├─ node_modules
├─ src
└─ src-tauri

Stack trace

No response

Additional context

I also added some window event logging in Wry's source code for testing and that looks jittery too when used in Tauri.

@FabianLars FabianLars added type: bug status: needs triage This issue needs to triage, applied to new issues platform: Windows labels Jan 3, 2023
@metkm
Copy link

metkm commented Feb 15, 2023

I tried both wry and tauri but the problem is I get the same resizing performance. But your Tauri app looks worse than mine, like crashing and freezing. I don't have that.

Wry on the left, tauri on the right - Release builds

2023-02-15.14-13-41.mp4

@FabianLars
Copy link
Member Author

Yeah, it used to be the same for me a while back. but some months ago it changed to whatever the hell is going on in the video i posted (maybe it's something specific to my app idk). Note that it's not that bad, but OBS didn't like converting 144hz to a 30fps video i guess.

i guess on top of that we could use this issue as an continuation/extension of #4012 🤷

@metkm
Copy link

metkm commented Feb 15, 2023

@metkm
Copy link

metkm commented Feb 15, 2023

I also tried with this sample code sample.rs. the issue is still there, and the resizing performance is bad. Probably an issue with Webview2 itself

my webview 2 version is 110.0.1587.46

@FabianLars
Copy link
Member Author

Probably an issue with Webview2 itself

Don't get me wrong, resizing webview2, or chromium in general is incredibly slow, at least if hardware acceleration is enabled.
There are just 2 issues on top of.

  1. the one in my video, where it's much slower in tauri than in wry (though as we know it's not as must of a difference for everyone)
  2. the webview2 background color setting we thought would help us here does not work for us. We unfortunately have to change the color of the window itself which is also a bit tricky. - i think i documented my thoughts and experiments somewhere else, at least on discord 🤔

@metkm
Copy link

metkm commented Feb 15, 2023

@FabianLars yeah, To my knowledge microsoft edge also uses webview2 but It feels like It performs way better than wry, tauri, and the code I posted which uses only the windows API nothing else.

@FabianLars
Copy link
Member Author

To my knowledge microsoft edge also uses webview2

More like the other way around, webview2 is a modified edge browser, but same thing.

but It feels like It performs way better than wry, tauri, and the code I posted which uses only the windows API nothing else.

yep, something is wrong somewhere. But it's a really weird thing. like, we basically use the same apis like the ms employee in your issue said, etc. And then another weird thing is that Wails (a similar project to tauri but written in Go) was just as laggy as tauri last time i tested it (tho less obvious because they change the window color) - well, maybe not that weird if they make the same mistake we do but whatever.

@metkm
Copy link

metkm commented Feb 16, 2023

I tried adding delay to updating size of webview. I think It works a little better. What do you think? the delay I show in the video is 10ms, I just tried with 2ms and both works pretty good.

Without Delay

without_delay.mp4

With Delay

with_delay.mp4

@FabianLars
Copy link
Member Author

Yeah i guess that's a bit better indeed. Though i'm not aware of the possible consequences the change could have, maybe @amrbashir does, and/or maybe we need some more testing to be sure that wouldn't break anything (or maybe we find the actual cause&solution along the way :D )

@metkm
Copy link

metkm commented Feb 16, 2023

@FabianLars Yeah, It feels like this will break something. I just wanted to know if it feels better or if I'm going crazy

Maybe Windows triggers resize event with the same values twice or something? I don't know.
or maybe because we try to resize webview way too quickly.

@metkm
Copy link

metkm commented Feb 16, 2023

This is a project of mine. The difference in performance is easier to see here. Both projects are in release mode, I reduced the delay to 2ms.

WindowsAndMessaging::WM_SIZE => {
    std::thread::sleep(Duration::from_millis(2));
    let size = get_window_size(hwnd);
    unsafe {
        webview
            .controller
            .0
            .SetBounds(RECT {
                left: 0,
                top: 0,
                right: size.cx,
                bottom: size.cy,
            })
            .unwrap();
    }
    *frame.size.lock().expect("lock size") = size;
    LRESULT::default()
}

With Delay

with_delay.mp4

Without Delay

without_delay.mp4

@amrbashir
Copy link
Member

Good findings and a strange one I must say. I just tested and can confirm it works and it even works with just 1ms delay too, However, I don't think we should have this by default at all because the thread could sleep longer than expected and have unknown side-effects in some apps, see std::tread::sleep:

The thread may sleep longer than the duration specified due to scheduling specifics or platform-dependent functionality. It will never sleep less.

That said, I am fine with adding it behind an option or a feature-flag but not the default

@cogscides
Copy link

I'm facing with the same problem. @metkm Can you please help me with applying the same edit?
I'm newbie to the Tauri and Rust and wondering where I should put this code to make it work 😓

@metkm
Copy link

metkm commented Feb 19, 2023

@cogscides what I did was just for now is

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            let Some(window) = app.get_window("main") else {
                return Ok(())
            };

            window.on_window_event(|event| {
                match event {
                    WindowEvent::Resized(..) => {
                        std::thread::sleep(std::time::Duration::from_millis(1))
                    }
                    _ => {}
                }
            });

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

But be careful you are blocking the thread

@amrbashir amrbashir transferred this issue from tauri-apps/tauri Feb 20, 2023
@amrbashir amrbashir added good first issue Good for newcomers and removed type: bug status: needs triage This issue needs to triage, applied to new issues labels Feb 20, 2023
@amrbashir amrbashir transferred this issue from tauri-apps/wry Feb 20, 2023
@amrbashir amrbashir added type: bug status: needs triage This issue needs to triage, applied to new issues and removed good first issue Good for newcomers labels Feb 20, 2023
@amrbashir
Copy link
Member

Moving it back here since the original issue is about tauri vs wry performance and still not fixed.

@metkm feel free to open an issue or a PR to wry for the hack you found.

@FabianLars
Copy link
Member Author

FabianLars commented Feb 20, 2023

Am i crazy or does 1 micro second have the same effect as 1 milisecond?
Like, from_micros(1)) "fixes" it in the same way from_millis does.

Edit: Same for from_nanos(1) actually

@metkm
Copy link

metkm commented Feb 20, 2023

@FabianLars yeah I feel like they have the same effect. I don't know what the default sleep duration should be, or maybe we can leave that to the developer.

Maybe I should reactive this issue again in the webview2 repo

@FabianLars
Copy link
Member Author

well, i mean at least in rust there's no shorter duration than a nano second right? so imo we should just go with that because it has the least influence on the other components compared to not sleeping if that makes sense?

@wusyong
Copy link
Member

wusyong commented Feb 26, 2023

I saw tauri-apps/wry#892 and I feel like this better to put in tauri-runtime-wry instead?
Also it's better to add some comment about why we need it.

@amrbashir
Copy link
Member

@wusyong seems like we don't need to add it in wry or tauri after-all, users could add this in their app logic

 tauri::Builder::default()
        .on_window_event(|e| {
            if let WindowEvent::Resized(_) = e.event() {
                std::thread::sleep(std::time::Duration::from_nanos(1));
            }
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");

@FabianLars
Copy link
Member Author

FabianLars commented Mar 1, 2023

Honestly not the biggest fan of not including it in tauri in some way since virtually all windows users complain about the laggy resizing.

@amrbashir
Copy link
Member

std::thread::sleep could sleep longer than requested so I'd rather let users to choose to opt-in rather than opt-out

@Ciantic
Copy link

Ciantic commented Mar 10, 2023

I'm using Windows 11, I don't experience the same lag in resizing at the moment. I know that others are encountering it in Windows 11... but here is what it looks like without any hacks:

snipping-windows11-nohacks.mp4

It has low frame counts but that's only because of the video. It's smooth for me.

The current app code is here: https://github.com/Ciantic/winvd-monitoring (works only on Windows 11 because of my desktop library)

@simonhyll
Copy link
Sponsor Contributor

simonhyll commented Mar 11, 2023

Just wanna chime in here with that it's very weird that at least on Windows 11 if you put the window to the side so that it covers half the screen or just maximize / unmaximize it resizes perfectly fine, even more fluid than with the 1 nano second sleep workaround, which suggests that there is in fact a solution to this, we just don't know what it is.

Also, on my machine I have 2 graphics cards, one crappy and one good, and when I move the window to the monitor with the crappy card it's very clearly a difference in how effective the workaround is, suggesting part of this is just related to hardware, and that there is in fact a better solution since e.g. maximize / unmaximize performs much better even on the crappy card.

Reach out to Microsoft possibly?

@mempler
Copy link

mempler commented Mar 31, 2023

I remember having this issue with a normal Win32 window, this happens if the window receives too many event calls, I fixed this by render while the resize event was called and not after the event loop (which you would usually do).

allthough i don't know if this applies here aswell. just giving a piece of what i've experienced.

So, to fix this you would either have to render everything during the resize event or have some sort of "deadline" which if has been hit, will enqueue all future events to a new frame (and should cancel/ignore all non important events) and continue with the rendering

(a theory) the sleep causes a context switch and cancels all the Win32 resize events (except the very last one sent)
This is also why #6322 (comment) works

@7flash

This comment was marked as off-topic.

@Tnze
Copy link
Contributor

Tnze commented Dec 30, 2023

So, to fix this you would either have to render everything during the resize event or have some sort of "deadline" which if has been hit, will enqueue all future events to a new frame (and should cancel/ignore all non important events) and continue with the rendering

A test of limiting the rate of resizing webview:

[package]
name = "wry-test"
version = "0.1.0"
edition = "2021"

[dependencies]
winit = { version = "0.29.7", features = ["rwh_05"] }
wry = "0.35.1"
use std::time::{Duration, Instant};
use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;
use wry::{Rect, WebViewBuilder};

fn main() -> wry::Result<()> {
    let event_loop = EventLoop::new().unwrap();
    let window = WindowBuilder::new()
        .with_title("Hello World")
        .build(&event_loop)
        .unwrap();
    let webview = WebViewBuilder::new_as_child(&window)
        .with_url("https://tauri.app")?
        .build()?;

    let mut resize_request = None;
    let mut last_resize_time = Instant::now();
    event_loop
        .run(move |event, elwt| {
            match event {
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => {
                    println!("The close button was pressed; stopping");
                    elwt.exit();
                }
                Event::AboutToWait => {
                    if let Some(bound) = resize_request.take() {
                        webview.set_bounds(bound);
                    }
                    window.request_redraw();
                }
                Event::WindowEvent {
                    event: WindowEvent::Resized(size),
                    ..
                } => {
                    let bound = Rect {
                        x: 0,
                        y: 0,
                        width: size.width,
                        height: size.height,
                    };
                    // My monitor refresh rate: 144
                    if last_resize_time.elapsed() > Duration::from_millis(1000 / 144) {
                        webview.set_bounds(bound);
                        resize_request = None;
                        last_resize_time = Instant::now();
                    } else {
                        resize_request = Some(bound);
                    }
                }
                Event::WindowEvent {
                    event: WindowEvent::RedrawRequested,
                    ..
                } => {}
                _ => (),
            }
        })
        .expect("event loop run failed");
    Ok(())
}

And it is fast as the Edge browser.

Videos

Without rate limitation:

WithoutLimit.mp4

With rate limitation:

WithLimit.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: Windows status: needs triage This issue needs to triage, applied to new issues type: bug
Projects
Status: 📬Proposal
Development

No branches or pull requests

10 participants