1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Runtime-global picking helpers — bridges the main-thread
//! [`web_sys::Window`] and worker [`web_sys::DedicatedWorkerGlobalScope`]
//! APIs behind a single call surface so the same renderer source
//! works in both contexts (the [`web_sys::OffscreenCanvas`] worker-mode
//! deployment shipped in Phase 4.4 needs the worker side of every
//! `navigator` / `performance` / `requestAnimationFrame` call).
//!
//! ### Current migration state
//!
//! The renderer-side audit is effectively complete — internal
//! subsystems route through [`navigator_gpu`] / [`performance`] /
//! [`request_animation_frame`] rather than touching `web_sys::window()`
//! directly. The only remaining direct call inside this crate is
//! `crate::workers::pool::default_worker_count` (intentional — it's
//! the main-thread bootstrap path; spawning a `WorkerPool` from
//! within a worker takes the explicit-count constructor anyway, so
//! the direct `window()` there is harmless). The lower-level
//! `renderer-core` has its own mirror in
//! [`awsm_renderer_core::web_global`] for the same reason; it's also
//! migrated.
//!
//! ### Why pick at runtime, not at compile time
//!
//! The same wasm binary serves both deployment modes. A consumer's
//! game might run on main thread (small browser game with DOM
//! overlay) while the same library compiled in the same build is
//! used for a worker-mode shipped title. Compile-time gating
//! (`#[cfg(target_feature = "worker")]` etc.) would force consumers
//! to ship two builds, defeating the "library, both modes
//! first-class" promise.
use ;
use js_sys;
/// Return the current JS global scope. On the main thread that's
/// `window`; in a worker it's the `DedicatedWorkerGlobalScope`.
/// `Some(window)` on the main thread; `None` in a worker.
/// `Some(scope)` in a worker; `None` on the main thread.
/// `navigator.gpu` from whichever global is active. Returns `None`
/// if WebGPU isn't exposed in the current context (locked behind a
/// flag, browser doesn't support, etc.).
///
/// `web_sys::Navigator::gpu()` is infallible at the binding level —
/// it returns a `Gpu` even when the underlying JS value is
/// `null`/`undefined` (Firefox without WebGPU enabled, Safari without
/// the relevant about:flags toggle, headless environments, etc.).
/// Without the `is_null() / is_undefined()` filter below, callers
/// would get a `Some(gpu)` that explodes on first use; the filter
/// makes the `Option` actually meaningful.
/// `performance` from whichever global is active. Workers expose
/// the same `Performance` API as main-thread (`performance.now()`,
/// `performance.measure(..)`, etc.).
/// Schedule a `requestAnimationFrame` callback against whichever
/// global is active. `DedicatedWorkerGlobalScope::requestAnimationFrame`
/// has shipped in every major browser since 2023.