Skip to content

Commit

Permalink
Web: implement WaitUntilStrategy (#3739)
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda authored Jun 20, 2024
1 parent b4e83a5 commit 3e6092b
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 14 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,19 @@ jobs:
command: check
log-level: error
arguments: --all-features --target ${{ matrix.platform.target }}

swc:
name: Minimize JavaScript
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install SWC
run: sudo npm i -g @swc/cli
- name: Run SWC
run: |
swc src/platform_impl/web/web_sys/worker.js -o src/platform_impl/web/web_sys/worker.min.js
- name: Check for diff
run: |
[[ -z $(git status -s) ]]
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@ target/
rls/
.vscode/
*~
*.wasm
*.ts
*.js
#*#
.DS_Store
12 changes: 12 additions & 0 deletions .swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"minify": true,
"jsc": {
"target": "es2022",
"minify": {
"compress": {
"unused": true
},
"mangle": true
}
}
}
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ features = [
'AbortController',
'AbortSignal',
'Blob',
'BlobPropertyBag',
'console',
'CssStyleDeclaration',
'Document',
Expand Down Expand Up @@ -320,6 +321,7 @@ features = [
'VisibilityState',
'Window',
'WheelEvent',
'Worker',
'Url',
]

Expand Down
5 changes: 5 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ changelog entry.

- On Web, add `EventLoopExtWebSys::(set_)poll_strategy()` to allow setting
control flow strategies before starting the event loop.
- On Web, add `WaitUntilStrategy`, which allows to set different strategies for
`ControlFlow::WaitUntil`. By default the Prioritized Task Scheduling API is
used, with a fallback to `setTimeout()` with a trick to circumvent throttling
to 4ms. But an option to use a Web worker to schedule the timer is available
as well, which commonly prevents any throttling when the window is not focused.

### Changed

Expand Down
69 changes: 69 additions & 0 deletions src/platform/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,20 @@ pub trait EventLoopExtWebSys {
///
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy;

/// Sets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);

/// Gets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn wait_until_strategy(&self) -> WaitUntilStrategy;
}

impl<T> EventLoopExtWebSys for EventLoop<T> {
Expand All @@ -213,6 +227,14 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
fn poll_strategy(&self) -> PollStrategy {
self.event_loop.poll_strategy()
}

fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.event_loop.set_wait_until_strategy(strategy);
}

fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.event_loop.wait_until_strategy()
}
}

pub trait ActiveEventLoopExtWebSys {
Expand All @@ -230,6 +252,20 @@ pub trait ActiveEventLoopExtWebSys {
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
fn poll_strategy(&self) -> PollStrategy;

/// Sets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy);

/// Gets the strategy for [`ControlFlow::WaitUntil`].
///
/// See [`WaitUntilStrategy`].
///
/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
fn wait_until_strategy(&self) -> WaitUntilStrategy;

/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
/// cursor has completely finished loading.
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
Expand All @@ -250,6 +286,16 @@ impl ActiveEventLoopExtWebSys for ActiveEventLoop {
fn poll_strategy(&self) -> PollStrategy {
self.p.poll_strategy()
}

#[inline]
fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.p.set_wait_until_strategy(strategy);
}

#[inline]
fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.p.wait_until_strategy()
}
}

/// Strategy used for [`ControlFlow::Poll`][crate::event_loop::ControlFlow::Poll].
Expand Down Expand Up @@ -278,6 +324,29 @@ pub enum PollStrategy {
Scheduler,
}

/// Strategy used for [`ControlFlow::WaitUntil`][crate::event_loop::ControlFlow::WaitUntil].
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum WaitUntilStrategy {
/// Uses the [Prioritized Task Scheduling API] to queue the next event loop. If not available
/// this will fallback to [`setTimeout()`].
///
/// This strategy is commonly not affected by browser throttling unless the window is not
/// focused.
///
/// This is the default strategy.
///
/// [Prioritized Task Scheduling API]: https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
/// [`setTimeout()`]: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
#[default]
Scheduler,
/// Equal to [`Scheduler`][Self::Scheduler] but wakes up the event loop from a [worker].
///
/// This strategy is commonly not affected by browser throttling regardless of window focus.
///
/// [worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
Worker,
}

pub trait CustomCursorExtWebSys {
/// Returns if this cursor is an animation.
fn is_animation(&self) -> bool;
Expand Down
10 changes: 9 additions & 1 deletion src/platform_impl/web/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::application::ApplicationHandler;
use crate::error::EventLoopError;
use crate::event::Event;
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
use crate::platform::web::{ActiveEventLoopExtWebSys, PollStrategy};
use crate::platform::web::{ActiveEventLoopExtWebSys, PollStrategy, WaitUntilStrategy};

use super::{backend, device, window};

Expand Down Expand Up @@ -85,6 +85,14 @@ impl<T> EventLoop<T> {
pub fn poll_strategy(&self) -> PollStrategy {
self.elw.poll_strategy()
}

pub fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.elw.set_wait_until_strategy(strategy);
}

pub fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.elw.wait_until_strategy()
}
}

fn handle_event<T: 'static, A: ApplicationHandler<T>>(
Expand Down
13 changes: 12 additions & 1 deletion src/platform_impl/web/event_loop/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::event::{
WindowEvent,
};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::PollStrategy;
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
use crate::platform_impl::platform::window::Inner;
Expand Down Expand Up @@ -43,6 +43,7 @@ pub struct Execution {
proxy_spawner: WakerSpawner<Weak<Self>>,
control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>,
wait_until_strategy: Cell<WaitUntilStrategy>,
exit: Cell<bool>,
runner: RefCell<RunnerEnum>,
suspended: Cell<bool>,
Expand Down Expand Up @@ -149,6 +150,7 @@ impl Shared {
proxy_spawner,
control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()),
wait_until_strategy: Cell::new(WaitUntilStrategy::default()),
exit: Cell::new(false),
runner: RefCell::new(RunnerEnum::Pending),
suspended: Cell::new(false),
Expand Down Expand Up @@ -688,6 +690,7 @@ impl Shared {
start,
end,
_timeout: backend::Schedule::new_with_duration(
self.wait_until_strategy(),
self.window(),
move || cloned.resume_time_reached(start, end),
delay,
Expand Down Expand Up @@ -800,6 +803,14 @@ impl Shared {
self.0.poll_strategy.get()
}

pub(crate) fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.0.wait_until_strategy.set(strategy)
}

pub(crate) fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.0.wait_until_strategy.get()
}

pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
self.0.proxy_spawner.waker()
}
Expand Down
10 changes: 9 additions & 1 deletion src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::event::{
};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::keyboard::ModifiersState;
use crate::platform::web::{CustomCursorFuture, PollStrategy};
use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::cursor::CustomCursor;
use crate::platform_impl::platform::r#async::Waker;
use crate::window::{
Expand Down Expand Up @@ -682,6 +682,14 @@ impl ActiveEventLoop {
self.runner.poll_strategy()
}

pub(crate) fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) {
self.runner.set_wait_until_strategy(strategy)
}

pub(crate) fn wait_until_strategy(&self) -> WaitUntilStrategy {
self.runner.wait_until_strategy()
}

pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
self.runner.waker()
}
Expand Down
Loading

0 comments on commit 3e6092b

Please sign in to comment.