When compiling Rust to wasm (web assembly), how can I sleep for 10 milliseconds?

空扰寡人 提交于 2021-02-08 15:09:27

问题


My rust program is managing memory for a 2d html canvas context, and I'm trying to hit ~60fps. I can calculate the delta between each frame easily, and it turns out to be roughly ~5ms.

I'm unclear on how to put my Rust webassembly program to sleep for the remaining 11ms. One option would be to have JavaScript call into Rust on every requestAnimationFrame and use that as the driver, but I'm curious to keep it all in Rust if possible.

I'm effectively looking for the Rust equivalent of JavaScript's setTimeout(renderNext, 11) when compiling out to the wasm target.


回答1:


I'm effectively looking for the Rust equivalent of JavaScript's setTimeout(renderNext, 11) when compiling out to the wasm target.

There are several Rust crates that have bindings to the JavaScript web API, most notably web-sys. Take a look at the documentation for one of the setTimeout overloads.

This is not really a Rust equivalent though, as it pretty directly calls the JS function. But you won't be able to get around that: sleeping or getting the current time are both functions that the host environment has to offer. They cannot be implemented in the raw language alone.

One option would be to have JavaScript call into Rust on every requestAnimationFrame and use that as the driver, but I'm curious to keep it all in Rust if possible.

Yes, you should use requestAnimationFrame (link to web-sys docs). This is much preferred over timing it yourself. In particular, this method will also pause calling your code when the tab is not active and stuff like that. In a desktop environment you would do the same: ask the host environment (i.e. the operating system, often via OpenGL or so) to synchronize your program to screen refreshes.




回答2:


In your requestAnimationFrame callback, call setTimeout, and have that in turn make the next call to requestAnimationFrame. You can see the JS version of this here.

Based on the example in the wasm-bindgen book, here's how I do this in Rust:

fn animate_limited(mut draw_frame: impl FnMut() + 'static, max_fps: i32) {
    // Based on:
    // https://rustwasm.github.io/docs/wasm-bindgen/examples/request-animation-frame.html#srclibrs

    // https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
    let animate_cb = Rc::new(RefCell::new(None));
    let animate_cb2 = animate_cb.clone();

    let timeout_cb = Rc::new(RefCell::new(None));
    let timeout_cb2 = timeout_cb.clone();

    let w = window();
    *timeout_cb2.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        request_animation_frame(&w, animate_cb.borrow().as_ref().unwrap());
    }) as Box<dyn FnMut()>));

    let w2 = window();
    *animate_cb2.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        draw_frame();

        set_timeout(&w2, timeout_cb.borrow().as_ref().unwrap(), 1000 / max_fps);
    }) as Box<dyn FnMut()>));

    request_animation_frame(&window(), animate_cb2.borrow().as_ref().unwrap());
}

fn window() -> web_sys::Window {
    web_sys::window().expect("no global `window` exists")
}

fn request_animation_frame(window: &web_sys::Window, f: &Closure<dyn FnMut()>) -> i32 {
    window
        .request_animation_frame(f.as_ref().unchecked_ref())
        .expect("should register `requestAnimationFrame` OK")
}

fn set_timeout(window: &web_sys::Window, f: &Closure<dyn FnMut()>, timeout_ms: i32) -> i32 {
    window
        .set_timeout_with_callback_and_timeout_and_arguments_0(
            f.as_ref().unchecked_ref(),
            timeout_ms,
        )
        .expect("should register `setTimeout` OK")
}

Then you simply pass animate_limited a function to do your drawing (a closure like move || { /* drawing logic here */ } will do the trick), and the maximum framerate you want.

There are almost certainly improvements to be made, there. I'm very new to Rust and just spent far too long figuring out how to make this work. Hopefully this makes it quicker for someone else in the future.



来源:https://stackoverflow.com/questions/57765987/when-compiling-rust-to-wasm-web-assembly-how-can-i-sleep-for-10-milliseconds

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!