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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! A [`wgpu`] backend over the engine's `wasi:webgpu`, for a program built as a component.
//!
//! wgpu's `custom` feature lets an external crate implement its dispatch traits. This crate
//! implements them over the [`wasdk::webgpu`] bindings, so anything written against the `wgpu`
//! API (valo, and the Waft UI framework through it) runs in a component unchanged.
//! `wasi:webgpu` mirrors the WebGPU specification, so the object model mirrors wgpu's own
//! browser backend: each wgpu object owns the wasi resource behind it and drops it with itself,
//! and descriptors borrow those resources from their owners for the duration of a call.
//!
//! ```ignore
//! let instance = wasdk_wgpu::instance();
//! let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await?;
//! let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor::default()).await?;
//!
//! // A view is the window a surface is made for, shared by the program and the surface:
//! let view = wasdk_wgpu::SharedView::new(view);
//! let surface = instance.create_surface(view.clone())?;
//! let metrics = view.metrics();
//! let config = surface.get_default_config(&adapter, metrics.width, metrics.height).unwrap();
//! surface.configure(&device, &config);
//!
//! // Each frame:
//! let wgpu::CurrentSurfaceTexture::Success(frame) = surface.get_current_texture() else { .. };
//! // ...draw into `frame.texture` and submit...
//! view.set_layers(&layers); // a program with composition layers
//! queue.present(frame); // presents the view
//!
//! // A video frame's hardware buffer is sampled through a texture imported of it on the device:
//! let video_texture = wasdk_wgpu::import_texture(&device, &video_frame.hardware_buffer());
//! ```
//!
//! # Drawing on a view
//!
//! A view is drawn on through wgpu's surface, the one way this crate draws on one, as a browser's
//! canvas is (`surface`): a
//! [`SharedView`] is the window `Instance::create_surface` takes, its handle a
//! `WebCanvasWindowHandle` pointing at it, so a framework's renderer that makes its surface from
//! a window's handles runs unchanged. The surface offers the engine's canvas format, which the
//! canvas is drawn in, `PresentMode::Fifo`, opaque and premultiplied alpha, and drawing into,
//! sampling and copying its textures; `configure` sets the view's canvas to the size configured,
//! which its textures are from then on whatever the view's own size, and configures a canvas
//! context of the view's against the device, its view formats passed through, of which the
//! engine serves the format's sRGB counterpart. `Queue::present` presents the view, with the layers it holds beneath it
//! (`view.set_layers`); a frame dropped without a present is not presented.
//!
//! # What the boundary cannot express
//!
//! `wasi:webgpu@0.3.0-rc.2` is the WebGPU API and nothing beyond it. Everything wgpu offers
//! past WebGPU panics at the call with a message naming the gap, or returns the error where the
//! wgpu signature has one. The complete list:
//!
//! - **Surfaces on anything but a view.** `Instance::create_surface` with a window handle that is
//! not a [`SharedView`]'s, or a target that is not a window handle: wgpu gives a backend of its
//! own no way to make the `CreateSurfaceError` it would answer. A surface's present modes but
//! `Fifo` and the automatic ones, its alpha modes `PostMultiplied` and `Inherit`, and colour
//! spaces a WebGPU canvas has no name for (`ExtendedSrgbLinear`, `Bt2100Pq`, `Bt2100Hlg`).
//! - **`as_hal` and pipeline caches.** There is no hal underneath; `create_pipeline_cache` and
//! a `cache` in a pipeline descriptor.
//! - **Ray tracing.** BLAS/TLAS creation, compaction and builds,
//! `BindingType::AccelerationStructure`, `BufferUsages::BLAS_INPUT | TLAS_INPUT`.
//! - **External textures.** `create_external_texture`, `BindingType::ExternalTexture`.
//! - **Shader sources other than WGSL.** SPIR-V, GLSL, naga IR, and every passthrough language
//! but WGSL.
//! - **Binding arrays.** `BindGroupLayoutEntry::count`, `BindingResource::*Array`.
//! - **Transient attachments.** `TextureUsages::TRANSIENT_ATTACHMENT` is a hint the host
//! rejects, so it is stripped, as wgpu's browser backend strips it.
//! - **Browser-only entry points.** `Queue::copy_external_image_to_texture` and a mapped
//! range's `as_uint8array`, which exist on wgpu's API only for the web.
//! - **Mesh shaders.** `create_mesh_pipeline`, `draw_mesh_tasks*`.
//! - **`multi_draw_*_count`.** Plain `multi_draw_indirect` is unrolled into draws, as on the web.
//! - **Timestamps inside encoders and passes** (`write_timestamp`): WebGPU writes them only at
//! pass boundaries (`timestamp_writes`). **Pipeline statistics queries.**
//! - **`clear_texture`.**
//! - **Values without a WebGPU name:** texture formats `R64Uint`, `NV12`, `P010` and HDR ASTC;
//! `Float64*` vertex formats; `TextureUsages::STORAGE_ATOMIC`; `StorageTextureAccess::Atomic`;
//! `AddressMode::ClampToBorder` and `SamplerDescriptor::border_color`; `TextureAspect::Plane*`;
//! `PolygonMode::Line | Point` and conservative rasterization; multiview; shader stages beyond
//! vertex, fragment and compute.
//! - **Features.** A required feature without a WebGPU name (wgpu-native's) fails
//! `request_device` with a [`wgpu::RequestDeviceError`]. wasi features without a wgpu bit
//! (`core-features-and-limits`, `texture-formats-tier1/2`, `texture-component-swizzle`) are
//! not reported.
//!
//! Hints with no wasi counterpart and no effect on results are accepted and dropped:
//! `DeviceDescriptor::{trace, memory_hints, experimental_features}`,
//! `RequestAdapterOptions::apply_limit_buckets`, `ShaderRuntimeChecks`,
//! `PipelineCompilationOptions::zero_initialize_workgroup_memory` (WebGPU always zeroes) and
//! `start/stop_graphics_debugger_capture`. `transition_resources` is a no-op because WebGPU
//! tracks resource state itself.
//!
//! # Where the semantics differ from wgpu-core
//!
//! - **Mapping copies.** The boundary shares no memory: `get_mapped_range` copies the range into
//! the component (`get-mapped-range-get-with-copy`), and a mutable view is copied back when it
//! is dropped (`get-mapped-range-set-with-copy`); drop every `BufferViewMut` before `unmap`.
//! - **Callbacks fire when the program yields.** `request_adapter`, `request_device`,
//! `pop_error_scope` and `get_compilation_info` are wasi async calls awaited by the futures
//! wgpu returns. `map_async`, `on_submitted_work_done`, `set_device_lost_callback` and
//! `on_uncaptured_error` are spawned onto the program's executor and run when it next
//! `.await`s; `Device::poll` cannot wait for them and returns `PollStatus::QueueEmpty` at once,
//! exactly as wgpu's web backend does.
//! - **Submission indices** count submissions per queue; `PollType::Wait` cannot wait on one.
//! - **A frame on a lost device** is handed out as good: the canvas hands out an invalid texture
//! it cannot tell from a good one, and the loss comes through the device's lost callback.
//! - **`AdapterInfo`** reports `Backend::BrowserWebGpu` (the WebGPU API, not a browser) and zero
//! vendor/device ids: the wasi info is strings, which land in `name` and `driver_info`.
//! - **`LoadOp::DontCare`** clears, as on the web.
//! - **`Queue::write_buffer_with`** returns `None` for a write that fails validation
//! (alignment, bounds, `COPY_DST`), as wgpu documents, instead of raising a validation error.
//! - **Validation the boundary reports becomes a panic.** Six calls answer with an error the
//! wgpu signature has nowhere to put: `Queue::write_buffer`, `Buffer::unmap`,
//! `Buffer::get_mapped_range`, writing a mapped range back, `Device::create_query_set` and
//! `set_bind_group`. Each panics with the engine's message rather than reaching an error scope,
//! where wgpu-core would raise a validation error.
/// Recovers this backend's object from the wgpu API type or dispatch wrapper carrying it; an
/// object from another wgpu backend is a programming error.
use gpu_interop;
use HardwareBuffer;
use InstanceInterface;
pub use WasdkDevice;
pub use WasdkInstance;
pub use ;
pub use WasdkTexture;
/// The wgpu instance over the engine's WebGPU. `wgpu::Instance::new` on this target has no
/// backend; this is the one to use.
/// `buffer` imported as a texture on `device`, over its memory, as a `wgpu::Texture`
/// (`wasdk:platform/gpu-interop`): a buffer the program allocated, an image the hardware codecs
/// decoded or a video frame's, which the texture holds, with its frame, until it is dropped. Its
/// size and format are the buffer's, and its usages follow from the buffer's usage.