При компиляции Rust в wasm (веб-сборка) как я могу заснуть на 10 миллисекунд?

Моя программа на ржавчине управляет памятью для контекста 2d html canvas, и я пытаюсь достичь ~ 60 кадров в секунду. Я могу легко рассчитать дельту между каждым кадром, и она составляет примерно ~ 5 мс.

Я не понимаю, как перевести мою программу веб-сборки Rust в спящий режим на оставшиеся 11 мс. Одним из вариантов было бы, чтобы JavaScript вызывал Rust для каждого requestAnimationFrame и использовал его в качестве драйвера, но мне любопытно оставить все это в Rust, если это возможно.

Я эффективно ищу Rust-эквивалент setTimeout(renderNext, 11) JavaScript при компиляции в цель wasm.


person sgrove    schedule 03.09.2019    source источник


Ответы (2)


Я эффективно ищу Rust-эквивалент JavaScript setTimeout(renderNext, 11) при компиляции в цель wasm.

Есть несколько ящиков Rust, которые имеют привязки к веб-API JavaScript, в первую очередь web-sys. Взгляните на документацию для одного из setTimeout перегружает.

Однако на самом деле это не эквивалент Rust, поскольку он напрямую вызывает функцию JS. Но вы не сможете обойти это: сон или получение текущего времени — обе функции, которые может предложить хост-среда. Они не могут быть реализованы только на необработанном языке.

Одним из вариантов было бы, чтобы JavaScript вызывал Rust на каждом requestAnimationFrame и использовал его в качестве драйвера, но мне любопытно, если это возможно, оставить все это в Rust.

Да, вы должны использовать requestAnimationFrame (ссылка на web-sys документы). Это намного предпочтительнее, чем рассчитать время самостоятельно. В частности, этот метод также приостанавливает вызов вашего кода, когда вкладка неактивна и тому подобное. В среде рабочего стола вы бы сделали то же самое: попросите хост-среду (то есть операционную систему, часто через OpenGL или около того) синхронизировать вашу программу с обновлениями экрана.

person Lukas Kalbertodt    schedule 03.09.2019

В обратном вызове requestAnimationFrame вызовите setTimeout, а он, в свою очередь, сделает следующий вызов requestAnimationFrame. Вы можете увидеть JS-версию этого здесь.

На основе примера из wasm-bindgenкниги, вот как я это делаю в 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")
}

Затем вы просто передаете animate_limited функцию для рисования (замыкание вроде move || { /* drawing logic here */ } поможет) и максимальную желаемую частоту кадров.

Там почти наверняка есть улучшения. Я очень новичок в Rust и потратил слишком много времени на выяснение того, как заставить это работать. Надеюсь, это сделает это быстрее для кого-то еще в будущем.

person Daniel    schedule 30.08.2020