GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
src/web/mod.rs (2.4K)
1 //! The browser's side of cce-ui: a WebGPU renderer that draws the same
2 //! [`Frame2D`](crate::draw::Frame2D)s the Vulkan renderer does, from the same
3 //! shaders (`draw::shaders`), atlas (`draw::glyphs`) and image queue
4 //! (`draw::images`), and the shell that runs an `Application` on a canvas
5 //! with it ([`run`]) — the page's events in through the shared `Driver`,
6 //! `Pacer::turn` on animation frames — and a [`ComputeDevice`] that runs
7 //! `crate::compute` jobs on WebGPU.
8 //!
9 //! wasm32 only, and it needs web-sys's WebGPU bindings switched on
10 //! (`--cfg=web_sys_unstable_apis`, set for this crate in `.cargo/config.toml`).
11
12 mod compute;
13 mod renderer;
14 mod rt;
15 mod scene;
16 mod shell;
17
18 pub use compute::ComputeDevice;
19 pub use renderer::{Capture, PendingCapture, WebRenderer};
20 pub use shell::{capture, run, Fonts, Sizing};
21
22 use wasm_bindgen::JsValue;
23
24 /// Ask the browser for a WebGPU device, with `limits` (WebGPU limit names)
25 /// raised to what the adapter offers — a device is created at the spec's
26 /// defaults otherwise, which are below what most adapters can do. What the
27 /// device rejects later, and why it was lost, goes to the console: a WebGPU
28 /// validation error is otherwise silent.
29 pub(crate) async fn request_device(
30 limits: &[&str],
31 ) -> Result<(web_sys::Gpu, web_sys::GpuAdapter, web_sys::GpuDevice), JsValue> {
32 let window = web_sys::window().ok_or("no window")?;
33 let gpu = window.navigator().gpu();
34 let adapter = gpu
35 .request_adapter()
36 .await?
37 .into_option()
38 .ok_or("this browser offers no WebGPU adapter")?;
39 let desc = web_sys::GpuDeviceDescriptor::new();
40 if !limits.is_empty() {
41 let offered = adapter.limits();
42 let required = js_sys::Object::new();
43 for name in limits {
44 let v = js_sys::Reflect::get(&offered, &JsValue::from_str(name))?;
45 if !v.is_undefined() {
46 js_sys::Reflect::set(&required, &JsValue::from_str(name), &v)?;
47 }
48 }
49 js_sys::Reflect::set(&desc, &JsValue::from_str("requiredLimits"), &required)?;
50 }
51 let device: web_sys::GpuDevice = adapter.request_device_with_descriptor(&desc).await?;
52 js_sys::Function::new_with_args(
53 "d",
54 "d.onuncapturederror = (e) => console.error('cce-ui WebGPU:', e.error.message); \
55 d.lost.then((i) => console.error('cce-ui WebGPU device lost:', i.reason, i.message));",
56 )
57 .call1(&JsValue::NULL, &device)?;
58 Ok((gpu, adapter, device))
59 }