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
+
1
152
use crate :: {
2
153
binding_model:: { BindGroup , BindGroupLayout , PipelineLayout } ,
3
154
command:: { CommandBuffer , RenderBundle } ,
@@ -36,6 +187,9 @@ use std::{fmt::Debug, marker::PhantomData, mem, ops};
36
187
/// - `IdentityManager` reuses the index values of freed ids before returning
37
188
/// ids with new index values. Freed vector entries get reused.
38
189
///
190
+ /// See the module-level documentation for an overview of how this
191
+ /// fits together.
192
+ ///
39
193
/// [`Id`]: crate::id::Id
40
194
/// [`Backend`]: wgt::Backend;
41
195
/// [`alloc`]: IdentityManager::alloc
@@ -431,9 +585,24 @@ impl<'a, T> Drop for Token<'a, T> {
431
585
}
432
586
}
433
587
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.
434
598
pub trait IdentityHandler < I > : Debug {
599
+ /// The type of proto-id consumed by this filter, to produce a true id.
435
600
type Input : Clone + Debug ;
601
+
602
+ /// Given a proto-id value `id`, return a true id for `backend`.
436
603
fn process ( & self , id : Self :: Input , backend : Backend ) -> I ;
604
+
605
+ /// Free the true id `id`.
437
606
fn free ( & self , id : I ) ;
438
607
}
439
608
@@ -447,11 +616,28 @@ impl<I: id::TypedId + Debug> IdentityHandler<I> for Mutex<IdentityManager> {
447
616
}
448
617
}
449
618
619
+ /// A type that can produce [`IdentityHandler`] filters for ids of type `I`.
620
+ ///
621
+ /// See the module-level documentation for details.
450
622
pub 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.
451
627
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
452
633
fn spawn ( & self ) -> Self :: Filter ;
453
634
}
454
635
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.
455
641
#[ derive( Debug ) ]
456
642
pub struct IdentityManagerFactory ;
457
643
@@ -462,6 +648,8 @@ impl<I: id::TypedId + Debug> IdentityHandlerFactory<I> for IdentityManagerFactor
462
648
}
463
649
}
464
650
651
+ /// A factory that can build [`IdentityHandler`]s for all resource
652
+ /// types.
465
653
pub trait GlobalIdentityHandlerFactory :
466
654
IdentityHandlerFactory < id:: AdapterId >
467
655
+ IdentityHandlerFactory < id:: DeviceId >
0 commit comments