1+ /*! Allocating resource ids, and tracking the resources they refer to.
2+
3+ The `wgpu_core` API uses identifiers of type [`Id<R>`] to refer to
4+ resources of type `R`. For example, [`id::DeviceId`] is an alias for
5+ `Id<Device<Empty>>`, and [`id::BufferId`] is an alias for
6+ `Id<Buffer<Empty>>`. `Id` implements `Copy`, `Hash`, `Eq`, `Ord`, and
7+ of course `Debug`.
8+
9+ Each `Id` contains not only an index for the resource it denotes but
10+ also a [`Backend`] indicating which `wgpu` backend it belongs to. You
11+ can use the [`gfx_select`] macro to dynamically dispatch on an id's
12+ backend to a function specialized at compile time for a specific
13+ backend. See that macro's documentation for details.
14+
15+ `Id`s also incorporate a generation number, for additional validation.
16+
17+ The resources to which identifiers refer are freed explicitly.
18+ Attempting to use an identifier for a resource that has been freed
19+ elicits an error result.
20+
21+ ## Assigning ids to resources
22+
23+ The users of `wgpu_core` generally want resource ids to be assigned
24+ in one of two ways:
25+
26+ - Users like `wgpu` want `wgpu_core` to assign ids to resources itself.
27+ For example, `wgpu` expects to call `Global::device_create_buffer`
28+ and have the return value indicate the newly created buffer's id.
29+
30+ - Users like `player` and Firefox want to allocate ids themselves, and
31+ pass `Global::device_create_buffer` and friends the id to assign the
32+ new resource.
33+
34+ To accommodate either pattern, `wgpu_core` methods that create
35+ resources all expect an `id_in` argument that the caller can use to
36+ specify the id, and they all return the id used. For example, the
37+ declaration of `Global::device_create_buffer` looks like this:
38+
39+ ```ignore
40+ impl<G: GlobalIdentityHandlerFactory> Global<G> {
41+ /* ... */
42+ pub fn device_create_buffer<A: HalApi>(
43+ &self,
44+ device_id: id::DeviceId,
45+ desc: &resource::BufferDescriptor,
46+ id_in: Input<G, id::BufferId>,
47+ ) -> (id::BufferId, Option<resource::CreateBufferError>) {
48+ /* ... */
49+ }
50+ /* ... */
51+ }
52+ ```
53+
54+ Users that want to assign resource ids themselves pass in the id they
55+ want as the `id_in` argument, whereas users that want `wgpu_core`
56+ itself to choose ids always pass `()`. In either case, the id
57+ ultimately assigned is returned as the first element of the tuple.
58+
59+ Producing true identifiers from `id_in` values is the job of an
60+ [`IdentityHandler`] implementation, which has an associated type
61+ [`Input`] saying what type of `id_in` values it accepts, and a
62+ [`process`] method that turns such values into true identifiers of
63+ type `I`. There are two kinds of `IdentityHandler`s:
64+
65+ - Users that want `wgpu_core` to assign ids generally use
66+ [`IdentityManager`] ([wrapped in a mutex]). Its `Input` type is
67+ `()`, and it tracks assigned ids and generation numbers as
68+ necessary. (This is what `wgpu` does.)
69+
70+ - Users that want to assign ids themselves use an `IdentityHandler`
71+ whose `Input` type is `I` itself, and whose `process` method simply
72+ passes the `id_in` argument through unchanged. For example, the
73+ `player` crate uses an `IdentityPassThrough` type whose `process`
74+ method simply adjusts the id's backend (since recordings can be
75+ replayed on a different backend than the one they were created on)
76+ but passes the rest of the id's content through unchanged.
77+
78+ Because an `IdentityHandler<I>` can only create ids for a single
79+ resource type `I`, constructing a [`Global`] entails constructing a
80+ separate `IdentityHandler<I>` for each resource type `I` that the
81+ `Global` will manage: an `IdentityHandler<DeviceId>`, an
82+ `IdentityHandler<TextureId>`, and so on.
83+
84+ The [`Global::new`] function could simply take a large collection of
85+ `IdentityHandler<I>` implementations as arguments, but that would be
86+ ungainly. Instead, `Global::new` expects a `factory` argument that
87+ implements the [`GlobalIdentityHandlerFactory`] trait, which extends
88+ [`IdentityHandlerFactory<I>`] for each resource id type `I`. This
89+ trait, in turn, has a `spawn` method that constructs an
90+ `IdentityHandler<I>` for the `Global` to use.
91+
92+ What this means is that the types of resource creation functions'
93+ `id_in` arguments depend on the `Global`'s `G` type parameter. A
94+ `Global<G>`'s `IdentityHandler<I>` implementation is:
95+
96+ ```ignore
97+ <G as IdentityHandlerFactory<I>>::Filter
98+ ```
99+
100+ where `Filter` is an associated type of the `IdentityHandlerFactory` trait.
101+ Thus, its `id_in` type is:
102+
103+ ```ignore
104+ <<G as IdentityHandlerFactory<I>>::Filter as IdentityHandler<I>>::Input
105+ ```
106+
107+ The [`Input<G, I>`] type is an alias for this construction.
108+
109+ ## Id allocation and streaming
110+
111+ Perhaps surprisingly, allowing users to assign resource ids themselves
112+ enables major performance improvements in some applications.
113+
114+ The `wgpu_core` API is designed for use by Firefox's [WebGPU]
115+ implementation. For security, web content and GPU use must be kept
116+ segregated in separate processes, with all interaction between them
117+ mediated by an inter-process communication protocol. As web content uses
118+ the WebGPU API, the content process sends messages to the GPU process,
119+ which interacts with the platform's GPU APIs on content's behalf,
120+ occasionally sending results back.
121+
122+ In a classic Rust API, a resource allocation function takes parameters
123+ describing the resource to create, and if creation succeeds, it returns
124+ the resource id in a `Result::Ok` value. However, this design is a poor
125+ fit for the split-process design described above: content must wait for
126+ the reply to its buffer-creation message (say) before it can know which
127+ id it can use in the next message that uses that buffer. On a common
128+ usage pattern, the classic Rust design imposes the latency of a full
129+ cross-process round trip.
130+
131+ We can avoid incurring these round-trip latencies simply by letting the
132+ content process assign resource ids itself. With this approach, content
133+ can choose an id for the new buffer, send a message to create the
134+ buffer, and then immediately send the next message operating on that
135+ buffer, since it already knows its id. Allowing content and GPU process
136+ activity to be pipelined greatly improves throughput.
137+
138+ To help propagate errors correctly in this style of usage, when resource
139+ creation fails, the id supplied for that resource is marked to indicate
140+ as much, allowing subsequent operations using that id to be properly
141+ flagged as errors as well.
142+
143+ [`gfx_select`]: crate::gfx_select
144+ [`Input`]: IdentityHandler::Input
145+ [`process`]: IdentityHandler::process
146+ [`Id<R>`]: crate::id::Id
147+ [wrapped in a mutex]: trait.IdentityHandler.html#impl-IdentityHandler%3CI%3E-for-Mutex%3CIdentityManager%3E
148+ [WebGPU]: https://www.w3.org/TR/webgpu/
149+
150+ */
151+
1152use crate :: {
2153 binding_model:: { BindGroup , BindGroupLayout , PipelineLayout } ,
3154 command:: { CommandBuffer , RenderBundle } ,
@@ -36,6 +187,9 @@ use std::{fmt::Debug, marker::PhantomData, mem, ops};
36187/// - `IdentityManager` reuses the index values of freed ids before returning
37188/// ids with new index values. Freed vector entries get reused.
38189///
190+ /// See the module-level documentation for an overview of how this
191+ /// fits together.
192+ ///
39193/// [`Id`]: crate::id::Id
40194/// [`Backend`]: wgt::Backend;
41195/// [`alloc`]: IdentityManager::alloc
@@ -431,9 +585,24 @@ impl<'a, T> Drop for Token<'a, T> {
431585 }
432586}
433587
588+ /// A type that can build true ids from proto-ids, and free true ids.
589+ ///
590+ /// For some implementations, the true id is based on the proto-id.
591+ /// The caller is responsible for providing well-allocated proto-ids.
592+ ///
593+ /// For other implementations, the proto-id carries no information
594+ /// (it's `()`, say), and this `IdentityHandler` type takes care of
595+ /// allocating a fresh true id.
596+ ///
597+ /// See the module-level documentation for details.
434598pub trait IdentityHandler < I > : Debug {
599+ /// The type of proto-id consumed by this filter, to produce a true id.
435600 type Input : Clone + Debug ;
601+
602+ /// Given a proto-id value `id`, return a true id for `backend`.
436603 fn process ( & self , id : Self :: Input , backend : Backend ) -> I ;
604+
605+ /// Free the true id `id`.
437606 fn free ( & self , id : I ) ;
438607}
439608
@@ -447,11 +616,28 @@ impl<I: id::TypedId + Debug> IdentityHandler<I> for Mutex<IdentityManager> {
447616 }
448617}
449618
619+ /// A type that can produce [`IdentityHandler`] filters for ids of type `I`.
620+ ///
621+ /// See the module-level documentation for details.
450622pub trait IdentityHandlerFactory < I > {
623+ /// The type of filter this factory constructs.
624+ ///
625+ /// "Filter" and "handler" seem to both mean the same thing here:
626+ /// something that can produce true ids from proto-ids.
451627 type Filter : IdentityHandler < I > ;
628+
629+ /// Create an [`IdentityHandler<I>`] implementation that can
630+ /// transform proto-ids into ids of type `I`.
631+ ///
632+ /// [`IdentityHandler<I>`]: IdentityHandler
452633 fn spawn ( & self ) -> Self :: Filter ;
453634}
454635
636+ /// A global identity handler factory based on [`IdentityManager`].
637+ ///
638+ /// Each of this type's `IdentityHandlerFactory<I>::spawn` methods
639+ /// returns a `Mutex<IdentityManager<I>>`, which allocates fresh `I`
640+ /// ids itself, and takes `()` as its proto-id type.
455641#[ derive( Debug ) ]
456642pub struct IdentityManagerFactory ;
457643
@@ -462,6 +648,8 @@ impl<I: id::TypedId + Debug> IdentityHandlerFactory<I> for IdentityManagerFactor
462648 }
463649}
464650
651+ /// A factory that can build [`IdentityHandler`]s for all resource
652+ /// types.
465653pub trait GlobalIdentityHandlerFactory :
466654 IdentityHandlerFactory < id:: AdapterId >
467655 + IdentityHandlerFactory < id:: DeviceId >
0 commit comments