bevy_render/render_phase/
draw.rs1use crate::render_phase::{PhaseItem, TrackedRenderPass};
2use bevy_app::{App, SubApp};
3use bevy_ecs::{
4 entity::Entity,
5 query::{QueryEntityError, QueryState, ROQueryItem, ReadOnlyQueryData},
6 system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState},
7 world::World,
8};
9use bevy_utils::{all_tuples, TypeIdMap};
10use core::{any::TypeId, fmt::Debug, hash::Hash};
11use derive_more::derive::{Display, Error};
12use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
13
14pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
21 #[allow(unused_variables)]
25 fn prepare(&mut self, world: &'_ World) {}
26
27 fn draw<'w>(
29 &mut self,
30 world: &'w World,
31 pass: &mut TrackedRenderPass<'w>,
32 view: Entity,
33 item: &P,
34 ) -> Result<(), DrawError>;
35}
36
37#[derive(Error, Display, Debug, PartialEq, Eq)]
38pub enum DrawError {
39 #[display("Failed to execute render command {_0:?}")]
40 #[error(ignore)]
41 RenderCommandFailure(&'static str),
42 #[display("Failed to get execute view query")]
43 InvalidViewQuery,
44 #[display("View entity not found")]
45 ViewEntityNotFound,
46}
47
48#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
51pub struct DrawFunctionId(u32);
52
53pub struct DrawFunctionsInternal<P: PhaseItem> {
57 pub draw_functions: Vec<Box<dyn Draw<P>>>,
58 pub indices: TypeIdMap<DrawFunctionId>,
59}
60
61impl<P: PhaseItem> DrawFunctionsInternal<P> {
62 pub fn prepare(&mut self, world: &World) {
64 for function in &mut self.draw_functions {
65 function.prepare(world);
66 }
67 }
68
69 pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
71 self.add_with::<T, T>(draw_function)
72 }
73
74 pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
76 let id = DrawFunctionId(self.draw_functions.len().try_into().unwrap());
77 self.draw_functions.push(Box::new(draw_function));
78 self.indices.insert(TypeId::of::<T>(), id);
79 id
80 }
81
82 pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw<P>> {
84 self.draw_functions.get_mut(id.0 as usize).map(|f| &mut **f)
85 }
86
87 pub fn get_id<T: 'static>(&self) -> Option<DrawFunctionId> {
89 self.indices.get(&TypeId::of::<T>()).copied()
90 }
91
92 pub fn id<T: 'static>(&self) -> DrawFunctionId {
99 self.get_id::<T>().unwrap_or_else(|| {
100 panic!(
101 "Draw function {} not found for {}",
102 core::any::type_name::<T>(),
103 core::any::type_name::<P>()
104 )
105 })
106 }
107}
108
109#[derive(Resource)]
113pub struct DrawFunctions<P: PhaseItem> {
114 internal: RwLock<DrawFunctionsInternal<P>>,
115}
116
117impl<P: PhaseItem> Default for DrawFunctions<P> {
118 fn default() -> Self {
119 Self {
120 internal: RwLock::new(DrawFunctionsInternal {
121 draw_functions: Vec::new(),
122 indices: Default::default(),
123 }),
124 }
125 }
126}
127
128impl<P: PhaseItem> DrawFunctions<P> {
129 pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
131 self.internal.read().unwrap_or_else(PoisonError::into_inner)
132 }
133
134 pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
136 self.internal
137 .write()
138 .unwrap_or_else(PoisonError::into_inner)
139 }
140}
141
142pub trait RenderCommand<P: PhaseItem> {
179 type Param: SystemParam + 'static;
191 type ViewQuery: ReadOnlyQueryData;
197 type ItemQuery: ReadOnlyQueryData;
207
208 fn render<'w>(
211 item: &P,
212 view: ROQueryItem<'w, Self::ViewQuery>,
213 entity: Option<ROQueryItem<'w, Self::ItemQuery>>,
214 param: SystemParamItem<'w, '_, Self::Param>,
215 pass: &mut TrackedRenderPass<'w>,
216 ) -> RenderCommandResult;
217}
218
219#[derive(Debug)]
221pub enum RenderCommandResult {
222 Success,
223 Skip,
224 Failure(&'static str),
225}
226
227macro_rules! render_command_tuple_impl {
228 ($(#[$meta:meta])* $(($name: ident, $view: ident, $entity: ident)),*) => {
229 $(#[$meta])*
230 impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
231 type Param = ($($name::Param,)*);
232 type ViewQuery = ($($name::ViewQuery,)*);
233 type ItemQuery = ($($name::ItemQuery,)*);
234
235 #[allow(non_snake_case)]
236 fn render<'w>(
237 _item: &P,
238 ($($view,)*): ROQueryItem<'w, Self::ViewQuery>,
239 maybe_entities: Option<ROQueryItem<'w, Self::ItemQuery>>,
240 ($($name,)*): SystemParamItem<'w, '_, Self::Param>,
241 _pass: &mut TrackedRenderPass<'w>,
242 ) -> RenderCommandResult {
243 match maybe_entities {
244 None => {
245 $(
246 match $name::render(_item, $view, None, $name, _pass) {
247 RenderCommandResult::Skip => return RenderCommandResult::Skip,
248 RenderCommandResult::Failure(reason) => return RenderCommandResult::Failure(reason),
249 _ => {},
250 }
251 )*
252 }
253 Some(($($entity,)*)) => {
254 $(
255 match $name::render(_item, $view, Some($entity), $name, _pass) {
256 RenderCommandResult::Skip => return RenderCommandResult::Skip,
257 RenderCommandResult::Failure(reason) => return RenderCommandResult::Failure(reason),
258 _ => {},
259 }
260 )*
261 }
262 }
263 RenderCommandResult::Success
264 }
265 }
266 };
267}
268
269all_tuples!(
270 #[doc(fake_variadic)]
271 render_command_tuple_impl,
272 0,
273 15,
274 C,
275 V,
276 E
277);
278
279pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
284 state: SystemState<C::Param>,
285 view: QueryState<C::ViewQuery>,
286 entity: QueryState<C::ItemQuery>,
287}
288
289impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
290 pub fn new(world: &mut World) -> Self {
292 Self {
293 state: SystemState::new(world),
294 view: world.query(),
295 entity: world.query(),
296 }
297 }
298}
299
300impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for RenderCommandState<P, C>
301where
302 C::Param: ReadOnlySystemParam,
303{
304 fn prepare(&mut self, world: &'_ World) {
307 self.state.update_archetypes(world);
308 self.view.update_archetypes(world);
309 self.entity.update_archetypes(world);
310 }
311
312 fn draw<'w>(
314 &mut self,
315 world: &'w World,
316 pass: &mut TrackedRenderPass<'w>,
317 view: Entity,
318 item: &P,
319 ) -> Result<(), DrawError> {
320 let param = self.state.get_manual(world);
321 let view = match self.view.get_manual(world, view) {
322 Ok(view) => view,
323 Err(err) => match err {
324 QueryEntityError::NoSuchEntity(_) => return Err(DrawError::ViewEntityNotFound),
325 QueryEntityError::QueryDoesNotMatch(_, _)
326 | QueryEntityError::AliasedMutability(_) => {
327 return Err(DrawError::InvalidViewQuery)
328 }
329 },
330 };
331
332 let entity = self.entity.get_manual(world, item.entity()).ok();
333 match C::render(item, view, entity, param, pass) {
334 RenderCommandResult::Success | RenderCommandResult::Skip => Ok(()),
335 RenderCommandResult::Failure(reason) => Err(DrawError::RenderCommandFailure(reason)),
336 }
337 }
338}
339
340pub trait AddRenderCommand {
343 fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
345 &mut self,
346 ) -> &mut Self
347 where
348 C::Param: ReadOnlySystemParam;
349}
350
351impl AddRenderCommand for SubApp {
352 fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
353 &mut self,
354 ) -> &mut Self
355 where
356 C::Param: ReadOnlySystemParam,
357 {
358 let draw_function = RenderCommandState::<P, C>::new(self.world_mut());
359 let draw_functions = self
360 .world()
361 .get_resource::<DrawFunctions<P>>()
362 .unwrap_or_else(|| {
363 panic!(
364 "DrawFunctions<{}> must be added to the world as a resource \
365 before adding render commands to it",
366 core::any::type_name::<P>(),
367 );
368 });
369 draw_functions.write().add_with::<C, _>(draw_function);
370 self
371 }
372}
373
374impl AddRenderCommand for App {
375 fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
376 &mut self,
377 ) -> &mut Self
378 where
379 C::Param: ReadOnlySystemParam,
380 {
381 SubApp::add_render_command::<P, C>(self.main_mut());
382 self
383 }
384}