bevy_render/render_resource/
resource_macros.rs

1// structs containing wgpu types take a long time to compile. this is particularly bad for generic
2// structs containing wgpu structs. we avoid that in debug builds (and for cargo check and rust analyzer)
3// by type-erasing with the `render_resource_wrapper` macro. The resulting type behaves like Arc<$wgpu_type>,
4// but avoids explicitly storing an Arc<$wgpu_type> member.
5// analysis from https://github.com/bevyengine/bevy/pull/5950#issuecomment-1243473071 indicates this is
6// due to `evaluate_obligations`. we should check if this can be removed after a fix lands for
7// https://github.com/rust-lang/rust/issues/99188 (and after other `evaluate_obligations`-related changes).
8#[cfg(debug_assertions)]
9#[macro_export]
10macro_rules! render_resource_wrapper {
11    ($wrapper_type:ident, $wgpu_type:ty) => {
12        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
13        #[derive(Debug)]
14        // SAFETY: while self is live, self.0 comes from `into_raw` of an Arc<$wgpu_type> with a strong ref.
15        pub struct $wrapper_type(*const ());
16
17        #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
18        #[derive(Debug)]
19        pub struct $wrapper_type(send_wrapper::SendWrapper<*const ()>);
20
21        impl $wrapper_type {
22            pub fn new(value: $wgpu_type) -> Self {
23                let arc = std::sync::Arc::new(value);
24                let value_ptr = std::sync::Arc::into_raw(arc);
25                let unit_ptr = value_ptr.cast::<()>();
26
27                #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
28                return Self(unit_ptr);
29                #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
30                return Self(send_wrapper::SendWrapper::new(unit_ptr));
31            }
32
33            pub fn try_unwrap(self) -> Option<$wgpu_type> {
34                let value_ptr = self.0.cast::<$wgpu_type>();
35                // SAFETY: pointer refers to a valid Arc, and was created from Arc::into_raw.
36                let arc = unsafe { std::sync::Arc::from_raw(value_ptr) };
37
38                // we forget ourselves here since the reconstructed arc will be dropped/decremented within this scope
39                std::mem::forget(self);
40
41                std::sync::Arc::try_unwrap(arc).ok()
42            }
43        }
44
45        impl std::ops::Deref for $wrapper_type {
46            type Target = $wgpu_type;
47
48            fn deref(&self) -> &Self::Target {
49                let value_ptr = self.0.cast::<$wgpu_type>();
50                // SAFETY: the arc lives for 'self, so the ref lives for 'self
51                let value_ref = unsafe { value_ptr.as_ref() };
52                value_ref.unwrap()
53            }
54        }
55
56        impl Drop for $wrapper_type {
57            fn drop(&mut self) {
58                let value_ptr = self.0.cast::<$wgpu_type>();
59                // SAFETY: pointer refers to a valid Arc, and was created from Arc::into_raw.
60                // this reconstructed arc is dropped/decremented within this scope.
61                unsafe { std::sync::Arc::from_raw(value_ptr) };
62            }
63        }
64
65        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
66        // SAFETY: We manually implement Send and Sync, which is valid for Arc<T> when T: Send + Sync.
67        // We ensure correctness by checking that $wgpu_type does implement Send and Sync.
68        // If in future there is a case where a wrapper is required for a non-send/sync type
69        // we can implement a macro variant that omits these manual Send + Sync impls
70        unsafe impl Send for $wrapper_type {}
71        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
72        // SAFETY: As explained above, we ensure correctness by checking that $wgpu_type implements Send and Sync.
73        unsafe impl Sync for $wrapper_type {}
74        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
75        const _: () = {
76            trait AssertSendSyncBound: Send + Sync {}
77            impl AssertSendSyncBound for $wgpu_type {}
78        };
79
80        impl Clone for $wrapper_type {
81            fn clone(&self) -> Self {
82                let value_ptr = self.0.cast::<$wgpu_type>();
83                // SAFETY: pointer refers to a valid Arc, and was created from Arc::into_raw.
84                let arc = unsafe { std::sync::Arc::from_raw(value_ptr.cast::<$wgpu_type>()) };
85                let cloned = std::sync::Arc::clone(&arc);
86                // we forget the reconstructed Arc to avoid decrementing the ref counter, as self is still live.
87                std::mem::forget(arc);
88                let cloned_value_ptr = std::sync::Arc::into_raw(cloned);
89                let cloned_unit_ptr = cloned_value_ptr.cast::<()>();
90
91                #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
92                return Self(cloned_unit_ptr);
93
94                // Note: this implementation means that this Clone will panic
95                // when called off the wgpu thread.
96                #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
97                return Self(send_wrapper::SendWrapper::new(cloned_unit_ptr));
98            }
99        }
100    };
101}
102
103#[cfg(not(debug_assertions))]
104#[macro_export]
105macro_rules! render_resource_wrapper {
106    ($wrapper_type:ident, $wgpu_type:ty) => {
107        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
108        #[derive(Clone, Debug)]
109        pub struct $wrapper_type(std::sync::Arc<$wgpu_type>);
110        #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
111        #[derive(Clone, Debug)]
112        pub struct $wrapper_type(std::sync::Arc<send_wrapper::SendWrapper<$wgpu_type>>);
113
114        impl $wrapper_type {
115            pub fn new(value: $wgpu_type) -> Self {
116                #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
117                return Self(std::sync::Arc::new(value));
118
119                #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
120                return Self(std::sync::Arc::new(send_wrapper::SendWrapper::new(value)));
121            }
122
123            pub fn try_unwrap(self) -> Option<$wgpu_type> {
124                #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
125                return std::sync::Arc::try_unwrap(self.0).ok();
126
127                #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
128                return std::sync::Arc::try_unwrap(self.0).ok().map(|p| p.take());
129            }
130        }
131
132        impl std::ops::Deref for $wrapper_type {
133            type Target = $wgpu_type;
134
135            fn deref(&self) -> &Self::Target {
136                self.0.as_ref()
137            }
138        }
139
140        #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
141        const _: () = {
142            trait AssertSendSyncBound: Send + Sync {}
143            impl AssertSendSyncBound for $wgpu_type {}
144        };
145    };
146}
147
148#[macro_export]
149macro_rules! define_atomic_id {
150    ($atomic_id_type:ident) => {
151        #[derive(Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Debug)]
152        pub struct $atomic_id_type(core::num::NonZeroU32);
153
154        // We use new instead of default to indicate that each ID created will be unique.
155        #[allow(clippy::new_without_default)]
156        impl $atomic_id_type {
157            pub fn new() -> Self {
158                use std::sync::atomic::{AtomicU32, Ordering};
159
160                static COUNTER: AtomicU32 = AtomicU32::new(1);
161
162                let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
163                Self(core::num::NonZeroU32::new(counter).unwrap_or_else(|| {
164                    panic!(
165                        "The system ran out of unique `{}`s.",
166                        stringify!($atomic_id_type)
167                    );
168                }))
169            }
170        }
171
172        impl From<$atomic_id_type> for core::num::NonZeroU32 {
173            fn from(value: $atomic_id_type) -> Self {
174                value.0
175            }
176        }
177
178        impl From<core::num::NonZeroU32> for $atomic_id_type {
179            fn from(value: core::num::NonZeroU32) -> Self {
180                Self(value)
181            }
182        }
183    };
184}
185
186pub use render_resource_wrapper;