1pub mod vitem;
3
4use std::{
5 any::{Any, TypeId},
6 collections::HashMap,
7 sync::Arc,
8};
9
10use variadics_please::all_tuples;
11
12use crate::utils::WgpuContext;
13
14use super::RenderTextures;
15
16pub trait RenderResource {
20 type Data;
22 fn init(ctx: &WgpuContext, data: &Self::Data) -> Self;
24 fn update(&mut self, ctx: &WgpuContext, data: &Self::Data);
26}
27
28pub trait RenderCommand {
30 fn encode_compute_pass_command(&self, cpass: &mut wgpu::ComputePass);
32 fn encode_render_pass_command(&self, rpass: &mut wgpu::RenderPass);
34 fn encode_render_command(
36 &self,
37 ctx: &WgpuContext,
38 pipelines: &mut super::PipelinesStorage,
39 encoder: &mut wgpu::CommandEncoder,
40 uniforms_bind_group: &wgpu::BindGroup,
41 render_textures: &RenderTextures,
42 #[cfg(feature = "profiling")] profiler: &mut wgpu_profiler::GpuProfiler,
43 );
44 fn debug(&self, _ctx: &WgpuContext) {}
46}
47
48macro_rules! impl_tuple_render_command {
49 ($($T:ident),*) => {
50 impl<$($T: RenderCommand,)*> RenderCommand for ($($T,)*) {
51 fn encode_compute_pass_command(
52 &self,
53 cpass: &mut wgpu::ComputePass,
54 ) {
55 #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")]
56 let ($($T,)*) = self;
57 $($T.encode_compute_pass_command(
58 cpass,
59 );)*
60 }
61 fn encode_render_pass_command(
62 &self,
63 rpass: &mut wgpu::RenderPass,
64 ) {
65 #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")]
66 let ($($T,)*) = self;
67 $($T.encode_render_pass_command(
68 rpass,
69 );)*
70 }
71 fn encode_render_command(
72 &self,
73 ctx: &WgpuContext,
74 pipelines: &mut super::PipelinesStorage,
75 encoder: &mut wgpu::CommandEncoder,
76 uniforms_bind_group: &wgpu::BindGroup,
77 render_textures: &RenderTextures,
78 #[cfg(feature = "profiling")] profiler: &mut wgpu_profiler::GpuProfiler,
79 ) {
80 #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")]
81 let ($($T,)*) = self;
82 $($T.encode_render_command(
83 ctx,
84 pipelines,
85 encoder,
86 uniforms_bind_group,
87 render_textures,
88 #[cfg(feature = "profiling")]
89 profiler,
90 );)*
91 }
92 fn debug(&self, ctx: &WgpuContext) {
93 #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")]
94 let ($($T,)*) = self;
95 $($T.debug(ctx);)*
96 }
97 }
98 };
99}
100
101all_tuples!(impl_tuple_render_command, 1, 16, T);
102
103impl<T: RenderCommand> RenderCommand for Vec<T> {
104 fn encode_compute_pass_command(&self, cpass: &mut wgpu::ComputePass) {
105 self.iter()
106 .for_each(|x| x.encode_compute_pass_command(cpass))
107 }
108 fn encode_render_pass_command(&self, rpass: &mut wgpu::RenderPass) {
109 self.iter()
110 .for_each(|x| x.encode_render_pass_command(rpass))
111 }
112 fn encode_render_command(
113 &self,
114 ctx: &WgpuContext,
115 pipelines: &mut super::PipelinesStorage,
116 encoder: &mut wgpu::CommandEncoder,
117 uniforms_bind_group: &wgpu::BindGroup,
118 render_textures: &RenderTextures,
119 #[cfg(feature = "profiling")] profiler: &mut wgpu_profiler::GpuProfiler,
120 ) {
121 self.iter().for_each(|x| {
122 x.encode_render_command(
123 ctx,
124 pipelines,
125 encoder,
126 uniforms_bind_group,
127 render_textures,
128 #[cfg(feature = "profiling")]
129 profiler,
130 )
131 });
132 }
133 fn debug(&self, _ctx: &WgpuContext) {
134 self.iter().for_each(|x| x.debug(_ctx));
135 }
136}
137
138impl<T: RenderCommand, const N: usize> RenderCommand for [T; N] {
139 fn encode_compute_pass_command(&self, cpass: &mut wgpu::ComputePass) {
140 self.iter()
141 .for_each(|x| x.encode_compute_pass_command(cpass))
142 }
143 fn encode_render_pass_command(&self, rpass: &mut wgpu::RenderPass) {
144 self.iter()
145 .for_each(|x| x.encode_render_pass_command(rpass))
146 }
147 fn encode_render_command(
148 &self,
149 ctx: &WgpuContext,
150 pipelines: &mut super::PipelinesStorage,
151 encoder: &mut wgpu::CommandEncoder,
152 uniforms_bind_group: &wgpu::BindGroup,
153 render_textures: &RenderTextures,
154 #[cfg(feature = "profiling")] profiler: &mut wgpu_profiler::GpuProfiler,
155 ) {
156 self.iter().for_each(|x| {
157 x.encode_render_command(
158 ctx,
159 pipelines,
160 encoder,
161 uniforms_bind_group,
162 render_textures,
163 #[cfg(feature = "profiling")]
164 profiler,
165 )
166 });
167 }
168 fn debug(&self, _ctx: &WgpuContext) {
169 self.iter().for_each(|x| x.debug(_ctx));
170 }
171}
172
173pub trait Primitive {
181 type RenderInstance: RenderResource<Data = Self> + RenderCommand;
183}
184
185slotmap::new_key_type! { pub struct RenderInstanceKey; }
186
187#[derive(Default)]
189pub struct RenderPool {
190 #[allow(clippy::type_complexity)]
191 inner: slotmap::SlotMap<
192 RenderInstanceKey,
193 (Arc<RenderInstanceKey>, TypeId, Box<dyn AnyRenderCommand>),
194 >,
195 last_frame_dropped: HashMap<TypeId, Vec<RenderInstanceKey>>,
196}
197
198impl RenderPool {
199 pub fn new() -> Self {
200 Self::default()
201 }
202
203 pub fn get(&self, key: RenderInstanceKey) -> Option<&dyn AnyRenderCommand> {
204 self.inner
205 .get(key)
206 .map(|x| x.2.as_ref() as &dyn AnyRenderCommand)
207 }
208
209 pub fn show(&self) {
210 self.inner
211 .iter()
212 .enumerate()
213 .for_each(|(idx, (_, (k, _, _)))| {
214 print!("{idx}: {}, ", Arc::strong_count(k));
215 });
216 println!();
217 }
218
219 pub fn alloc<T: RenderCommand + RenderResource<Data = D> + Send + Sync + 'static, D>(
220 &mut self,
221 ctx: &WgpuContext,
222 data: &D,
223 ) -> Arc<RenderInstanceKey> {
224 let last_frame_dropped = self
225 .last_frame_dropped
226 .entry(TypeId::of::<T>())
227 .or_default();
228 if let Some(key) = last_frame_dropped.pop() {
229 let entry = self.inner.get_mut(key).unwrap();
230 let key = entry.0.clone();
231 (entry.2.as_mut() as &mut dyn Any)
232 .downcast_mut::<T>()
233 .unwrap()
234 .update(ctx, data);
235 key
236 } else {
237 let handle = self.inner.insert_with_key(|key| {
238 (
239 Arc::new(key),
240 TypeId::of::<T>(),
241 Box::new(T::init(ctx, data)),
242 )
243 });
244 self.inner.get(handle).unwrap().0.clone()
245 }
246 }
247
248 pub fn clean(&mut self) {
249 self.inner.retain(|key, (_, t_id, _)| {
250 self.last_frame_dropped
251 .get(t_id)
252 .map(|x| !x.contains(&key))
253 .unwrap_or(true)
254 });
255 self.last_frame_dropped.clear();
257 self.inner
258 .iter()
259 .filter(|(_, (key, _, _))| Arc::strong_count(key) == 1)
260 .for_each(|(key, (_, t_id, _))| {
261 self.last_frame_dropped.entry(*t_id).or_default().push(key);
262 });
263 }
264}
265
266pub trait AnyRenderCommand: Any + RenderCommand + Send + Sync {}
268impl<T: RenderCommand + Any + Send + Sync> AnyRenderCommand for T {}
269
270impl RenderCommand for Vec<&dyn RenderCommand> {
271 fn encode_compute_pass_command(&self, cpass: &mut wgpu::ComputePass) {
272 for render_instance in self {
273 render_instance.encode_compute_pass_command(cpass);
274 }
275 }
276 fn encode_render_pass_command(&self, rpass: &mut wgpu::RenderPass) {
277 for render_instance in self {
278 render_instance.encode_render_pass_command(rpass);
279 }
280 }
281
282 fn encode_render_command(
283 &self,
284 ctx: &WgpuContext,
285 pipelines: &mut super::PipelinesStorage,
286 encoder: &mut wgpu::CommandEncoder,
287 uniforms_bind_group: &wgpu::BindGroup,
288 render_textures: &RenderTextures,
289 #[cfg(feature = "profiling")] profiler: &mut wgpu_profiler::GpuProfiler,
290 ) {
291 for render_instance in self {
292 render_instance.encode_render_command(
293 ctx,
294 pipelines,
295 encoder,
296 uniforms_bind_group,
297 render_textures,
298 #[cfg(feature = "profiling")]
299 profiler,
300 );
301 }
302 }
303 fn debug(&self, ctx: &WgpuContext) {
304 for render_instance in self {
305 render_instance.debug(ctx);
306 }
307 }
308}