wgpu/documentation/internals/architecture.rs
1#![doc = crate::macros::doc_image!("architecture.webp")]
2#![doc = crate::macros::doc_image!("big-picture.webp")]
3/*!
4# Architecture: wgpu, wgpu-core, and wgpu-hal
5
6Here's an overview of `wgpu`'s architecture.
7
8![wgpu architecture][architecture.webp]
9
10Working through this diagram from the bottom up:
11
12- Each operating system provides its own API (or APIs) for getting at the
13 GPU:
14
15 - On Windows, [Direct3D] is the primary GPU programming API, but [Vulkan]
16 and [OpenGL] are often also available.
17
18 - On macOS, [Metal] is the official GPU programming API, although OpenGL is
19 still around for legacy programs, and the [MoltenVK] open-source project
20 implements the Vulkan API on top of Metal.
21
22 - On Linux, the [Mesa] library supports Vulkan and OpenGL.
23
24 If you want to write an application that runs on all three of these
25 platforms, you have a few options:
26
27 - You could target Vulkan, and for macOS support embed MoltenVK, or
28 require your users to install it.
29
30 - You could target OpenGL, which is no longer being developed and is
31 missing many modern features, and worry that macOS will drop support for
32 it altogether.
33
34 - You could port it to all three native platform APIs: Direct3D, Vulkan,
35 and Metal. This would be a lot of code.
36
37 - Or, you could just use `wgpu`!
38
39- The [`wgpu_hal`] crate implements a portable Rust API that can use any of
40 the platform-specific APIs mentioned above as a backend: a program that
41 uses `wgpu_hal` correctly should behave consistently regardless of what
42 platform you're running on.
43
44 However, `wgpu_hal`'s interface is completely unsafe; you must follow all
45 of its safety requirements to the letter to avoid provoking undefined
46 behavior from the underlying platforms. These requirements are complex,
47 and not well documented. And `wgpu_hal` performs almost no validation
48 beyond the minimum necessary to ensure portability.
49
50- The [`wgpu_core`] crate builds on `wgpu_hal` to provide an API with a
51 similar flavor, preserving its portability, and adding full bullet-proof
52 validation. By "bullet-proof", we mean that `wgpu_core` is intended to be
53 driven by untrusted code, like web content using the [WebGPU] API.
54 `wgpu_core` only assumes that Rust's safety rules are respected. Safe Rust
55 code should not be able to cause a crash using `wgpu_core`.
56
57 This means that `wgpu_core` is fully responsible for things like tracking
58 resource lifetimes, generating barriers for usage transitions, checking
59 parameters, and so on. We cover its duties in more detail below.
60
61 The `wgpu_core` crate's API is designed to be easy to use from other
62 languages via foreign function interfaces (FFI). This restricts the Rust
63 features we can use in the API somewhat. But this makes `wgpu_core` the
64 best dependency for crates like `wgpu_native` and applications like Deno
65 and Firefox that have their own binding systems.
66
67- The [`wgpu`](crate) crate provides an idiomatic Rust API on top of
68 `wgpu_core`, inheriting its validation and portability. This is what most
69 users in the Rust ecosystem will want.
70
71 You can also compile `wgpu` to WebAssembly and run it in a web browser,
72 having it drive the browser's [WebGPU] implementation. In this mode of use,
73 `wgpu_core` and `wgpu_hal` are not present. This can help you share code
74 between in-browser and native versions of your app, by providing a single
75 API for both versions to use.
76
77[WebGPU]: https://www.w3.org/TR/webgpu/
78[Direct3D]: https://learn.microsoft.com/en-us/windows/win32/direct3d
79[Vulkan]: https://www.vulkan.org/
80[Metal]: https://developer.apple.com/metal/
81[OpenGL]: https://www.opengl.org/
82[MoltenVK]: https://github.com/KhronosGroup/MoltenVK
83[Mesa]: https://mesa3d.org/
84[`wgpu_hal`]: https://docs.rs/wgpu-hal/latest/wgpu_hal/
85[`wgpu_core`]: https://docs.rs/wgpu-core/latest/wgpu_core/
86
87## wgpu
88
89The `wgpu` crate provides an idiomatic Rust API for cross-platform GPU-based
90graphics and computation, modeled after the [WebGPU] JavaScript API.
91
92The `wgpu` crate itself doesn't contain much interesting graphics-related
93code. It is mostly concerned with providing a consistent, idiomatic API that
94applications can use to drive any one of several implementations:
95
96- The [`wgpu::backend::wgpu_core`][w:cwc] module is available in native or
97 Emscripten environments. It forwards all operations to `wgpu_core`. Most
98 [`Backend`] values, like `Vulkan` or `Metal`, go through this module.
99
100- The [`wgpu::backend::webgpu`][w:cwg] module, available when `wgpu` has been
101 compiled to WebAssembly, forwards calls to a browser's WebGPU
102 implementation. The [`Backend::BrowserWebGpu`] backend selects this module.
103
104- The [`wgpu::backend::custom`][w:cc] module uses dynamic dispatch to let the
105 user supply their own implementation. These are created via a separate
106 mechanism, so they don't use any [`Backend`] value.
107
108[w:cwc]: https://github.com/gfx-rs/wgpu/blob/trunk/wgpu/src/backend/wgpu_core.rs
109[w:cwg]: https://github.com/gfx-rs/wgpu/blob/trunk/wgpu/src/backend/webgpu.rs
110[w:cc]: https://github.com/gfx-rs/wgpu/blob/trunk/wgpu/src/backend/custom.rs
111
112## wgpu_core
113
114The `wgpu_core` crate implements a safe, cross-platform API on top of
115`wgpu_hal`'s unsafe, cross-platform API. Almost every method in `wgpu_hal`'s
116API is marked `unsafe`; it is `wgpu_core`'s job to satisfy or enforce every
117requirement in `wgpu_hal`'s safety contracts. Then, `wgpu_hal`'s only
118responsibility is to get consistent behavior across all its backends. This
119relationship is explained more in
120[`wgpu_hal`'s documentation][hal safety].
121
122Beyond safety, `wgpu_core` also implements a few convenience APIs (for
123example, mapped-at-creation buffers) on top of `wgpu_hal`'s more primitive
124features.
125
126[hal safety]: https://docs.rs/wgpu-hal/latest/wgpu_hal/#validation-is-the-calling-codes-responsibility-not-wgpu-hals
127
128### Lifetime tracking
129
130The `wgpu_hal` API requires some objects to outlive others. For example, a
131[`wgpu_hal::Device`] must outlive all resources (`wgpu_hal::Buffer`,
132`wgpu_hal::CommandEncoder`, etc.) created from it. The `wgpu_core` types
133ensure that these requirements are upheld, mostly just by using `Arc`.
134
135TODO: Command-buffer resource usage through submission probably counts as
136"lifetime tracking" too.
137
138This `wgpu_hal` requirement arises mostly from the Vulkan backend. Direct3D
139and Metal both use reference counting internally for most operations, but
140Vulkan foists the entire problem off on its users.
141
142[`wgpu_hal::Device`]: https://docs.rs/wgpu-hal/latest/wgpu_hal/trait.Device.html
143
144### Synchronization
145
146Some types in the `wgpu_hal` API cannot be accessed simultaneously by
147multiple threads. For example, most [`wgpu_hal::CommandEncoder`] methods take
148`&mut self`. `wgpu_core` protects these hal types with `Mutex` as
149appropriate.
150
151There are a few other interesting requirements; for example, calls to
152[`wgpu_hal::Queue::submit`] on a given `Queue` that could occur
153simultaneously must use different `Fence`s. `wgpu_core` addresses this by
154simply wrapping `wgpu_hal::Queue` in a `Mutex`.
155
156[`wgpu_hal::CommandEncoder`]: https://docs.rs/wgpu-hal/latest/wgpu_hal/trait.CommandEncoder.html
157[`wgpu_hal::Queue::submit`]: https://docs.rs/wgpu-hal/latest/wgpu_hal/trait.Queue.html#tymethod.submit
158
159### Barrier generation
160
161Vulkan and Direct3D require the user to include barriers in the command
162stream to notify the driver when a particular buffer or texture is
163transitioning from one kind of use to another. For example, if a texture is
164used as a render target and then sampled by a shader, Direct3D 12 requires a
165[ResourceBarrier] command between the two uses. Similarly, Vulkan requires
166[VkCmdPipelineBarrier] commands in some cases to ensure that values written
167to a resource by one operation will be seen by reads in a subsequent
168operation.
169
170The `wgpu_hal` API passes these backends' requirements through to its own
171users, requiring calls to [`transition_buffers`] and [`transition_textures`]
172between different kinds of access to such resources. But, following the lead
173of [WebGPU], the `wgpu` API does not require its users to record barriers.
174This means that `wgpu_core` must track how each resource was last used, and
175generate the barriers itself.
176
177Since `wgpu` encourages users to record `CommandBuffer`s in advance (ideally
178in multiple threads), and then submit them in whatever order they please,
179`wgpu_core` cannot know what states the resources that a `CommandBuffer` uses
180will be in until it is actually submitted. Thus, each
181`wgpu_core::CommandBuffer` merely records what states it expects the
182resources it uses to be in initially, and which states it will leave those
183resources in when it is done. Submission is then responsible for comparing
184resources' actual states with those expected by each
185`wgpu_core::CommandBuffer`, and recording the necessary barriers in a fresh
186`wgpu_hal::CommandBuffer` submitted ahead of it.
187
188When recording a command buffer, since the commands are only being recorded
189for later execution, and not executed immediately, a `CommandEncoder` tracks
190the state each resource *will be in* when the given commands are executed,
191and adds barriers to the recorded stream as appropriate. Naturally, the
192`CommandEncoder` must treat each resource's initial state as unknown, as it
193has no way to know what other commands will be submitted ahead of it.
194
195Note that, within a single `wgpu_hal::Texture`, individual array elements and
196mip levels can be in different states. `wgpu_hal::Buffer`s, however, have only
197a single state.
198
199Within `wgpu_core`, the `Tracker` type records a set of resources, their
200current states, and (for command buffers being recorded) the initial state
201they should be in. `Tracker` also notes which stateless resources (samplers;
202render pipelines) a `CommandBuffer` uses, in order to keep them alive until
203the commands have been submitted and finished running.
204
205[ResourceBarrier]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-resourcebarrier
206[VkCmdPipelineBarrier]: https://docs.vulkan.org/refpages/latest/refpages/source/vkCmdPipelineBarrier.html
207[`transition_buffers`]: https://docs.rs/wgpu-hal/latest/wgpu_hal/trait.DynCommandEncoder.html#tymethod.transition_buffers
208[`transition_textures`]: https://docs.rs/wgpu-hal/latest/wgpu_hal/trait.DynCommandEncoder.html#tymethod.transition_textures
209
210### MemoryInitTracker
211
212Tracks the memory-initialization state of buffer resources, i.e. whether
213they have been written to either by the user or by `wgpu` previously
214zero-initializing them (WebGPU requires all buffers to behave *as if* they
215are zero-initialized). A `MemoryInitTracker` tracks a single buffer and
216allows inserting zero-initialization lazily on use. Zero-init is inserted if
217necessary at:
218
219- memory mapping, and
220- `queue_submit` (for all bindings).
221
222### Device maintain
223
224TODO (what/why).
225
226### Cross-device use
227
228The `wgpu_hal` API requires that a resource created with one
229[`wgpu_hal::Device`] may not be used with a different `Device`. `wgpu_core`
230tracks the `Device` to which each resource belongs, and checks for
231cross-device use where necessary.
232
233### General parameter validation
234
235The `wgpu_core` crate is responsible for all the usual parameter validation
236for graphics operations: a texture's format must be supported by the device;
237copies must not exceed the bounds of the buffers or textures involved; bind
238groups need to have the right layout for the pipeline; and so on.
239
240## API tracing
241
242Enabled via a feature flag. Allows recording all usage (WebGPU API functions,
243essentially) to a `.ron` file. Any additional data (data uploaded to a
244buffer, etc.) is stored in additional files. A trace can be replayed with the
245`player` compiled from the same `wgpu` revision. Used for testing and bug
246reporting. See
247[Debugging wgpu Applications](crate::documentation::debugging::debugging_applications#tracing-infrastructure).
248
249## Other WebGPU implementations
250
251Note that `wgpu` represents only one possible way to implement the
252[WebGPU][WebGPU API] API; Google's [Dawn] and
253[WebKit's WebGPU module][webkit] are other WebGPU implementations used in
254production.
255
256[WebGPU API]: https://www.w3.org/TR/webgpu/
257[Dawn]: https://dawn.googlesource.com/dawn
258[webkit]: https://github.com/WebKit/WebKit/tree/main/Source/WebCore/Modules/WebGPU
259
260## The wider gfx-rs ecosystem
261
262`wgpu` is one piece of the larger gfx-rs ecosystem. For a map of all the
263components and how they fit together, see the big picture:
264
265![The gfx-rs big picture][big-picture.webp]
266
267*/
268
269use crate::Backend;