bevy_render/render_phase/
draw.rs1use bevy_material::labels::DrawFunctionId;
2
3use crate::render_phase::{PhaseItem, TrackedRenderPass};
4use bevy_app::{App, SubApp};
5use bevy_ecs::{
6 entity::Entity,
7 query::{QueryEntityError, QueryState, ROQueryItem, ReadOnlyQueryData},
8 resource::Resource,
9 system::{ReadOnlySystemParam, SystemParam, SystemParamItem, SystemState},
10 world::World,
11};
12use bevy_utils::TypeIdMap;
13use core::{any::TypeId, fmt::Debug};
14use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
15use thiserror::Error;
16use variadics_please::all_tuples;
17
18pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
25 #[expect(
29 unused_variables,
30 reason = "The parameters here are intentionally unused by the default implementation; however, putting underscores here will result in the underscores being copied by rust-analyzer's tab completion."
31 )]
32 fn prepare(&mut self, world: &'_ World) {}
33
34 fn draw<'w>(
36 &mut self,
37 world: &'w World,
38 pass: &mut TrackedRenderPass<'w>,
39 view: Entity,
40 item: &P,
41 ) -> Result<(), DrawError>;
42}
43
44#[derive(Error, Debug, PartialEq, Eq)]
45pub enum DrawError {
46 #[error("Failed to execute render command {0:?}")]
47 RenderCommandFailure(&'static str),
48 #[error("Failed to get execute view query")]
49 InvalidViewQuery,
50 #[error("View entity not found")]
51 ViewEntityNotFound,
52}
53
54pub struct DrawFunctionsInternal<P: PhaseItem> {
58 pub draw_functions: Vec<Box<dyn Draw<P>>>,
59 pub indices: TypeIdMap<DrawFunctionId>,
60}
61
62impl<P: PhaseItem> DrawFunctionsInternal<P> {
63 pub fn prepare(&mut self, world: &World) {
65 for function in &mut self.draw_functions {
66 function.prepare(world);
67 }
68 }
69
70 pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
72 self.add_with::<T, T>(draw_function)
73 }
74
75 pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
77 let id = DrawFunctionId(self.draw_functions.len().try_into().unwrap());
78 self.draw_functions.push(Box::new(draw_function));
79 self.indices.insert(TypeId::of::<T>(), id);
80 id
81 }
82
83 pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw<P>> {
85 self.draw_functions.get_mut(id.0 as usize).map(|f| &mut **f)
86 }
87
88 pub fn get_id<T: 'static>(&self) -> Option<DrawFunctionId> {
90 self.indices.get(&TypeId::of::<T>()).copied()
91 }
92
93 pub fn id<T: 'static>(&self) -> DrawFunctionId {
100 self.get_id::<T>().unwrap_or_else(|| {
101 panic!(
102 "Draw function {} not found for {}",
103 core::any::type_name::<T>(),
104 core::any::type_name::<P>()
105 )
106 })
107 }
108}
109
110#[derive(Resource)]
114pub struct DrawFunctions<P: PhaseItem> {
115 internal: RwLock<DrawFunctionsInternal<P>>,
116}
117
118impl<P: PhaseItem> Default for DrawFunctions<P> {
119 fn default() -> Self {
120 Self {
121 internal: RwLock::new(DrawFunctionsInternal {
122 draw_functions: Vec::new(),
123 indices: Default::default(),
124 }),
125 }
126 }
127}
128
129impl<P: PhaseItem> DrawFunctions<P> {
130 pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
132 self.internal.read().unwrap_or_else(PoisonError::into_inner)
133 }
134
135 pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
137 self.internal
138 .write()
139 .unwrap_or_else(PoisonError::into_inner)
140 }
141}
142
143pub trait RenderCommand<P: PhaseItem> {
182 type Param: SystemParam + 'static;
194 type ViewQuery: ReadOnlyQueryData;
200 type ItemQuery: ReadOnlyQueryData;
210
211 fn render<'w>(
214 item: &P,
215 view: ROQueryItem<'w, '_, Self::ViewQuery>,
216 entity: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
217 param: SystemParamItem<'w, '_, Self::Param>,
218 pass: &mut TrackedRenderPass<'w>,
219 ) -> RenderCommandResult;
220}
221
222#[derive(Debug)]
224pub enum RenderCommandResult {
225 Success,
226 Skip,
227 Failure(&'static str),
228}
229
230macro_rules! render_command_tuple_impl {
231 ($(#[$meta:meta])* $(($name: ident, $view: ident, $entity: ident)),*) => {
232 $(#[$meta])*
233 impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
234 type Param = ($($name::Param,)*);
235 type ViewQuery = ($($name::ViewQuery,)*);
236 type ItemQuery = ($($name::ItemQuery,)*);
237
238 #[expect(
239 clippy::allow_attributes,
240 reason = "We are in a macro; as such, `non_snake_case` may not always lint."
241 )]
242 #[allow(
243 non_snake_case,
244 reason = "Parameter and variable names are provided by the macro invocation, not by us."
245 )]
246 fn render<'w>(
247 _item: &P,
248 ($($view,)*): ROQueryItem<'w, '_, Self::ViewQuery>,
249 maybe_entities: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
250 ($($name,)*): SystemParamItem<'w, '_, Self::Param>,
251 _pass: &mut TrackedRenderPass<'w>,
252 ) -> RenderCommandResult {
253 match maybe_entities {
254 None => {
255 $(
256 match $name::render(_item, $view, None, $name, _pass) {
257 RenderCommandResult::Skip => return RenderCommandResult::Skip,
258 RenderCommandResult::Failure(reason) => return RenderCommandResult::Failure(reason),
259 _ => {},
260 }
261 )*
262 }
263 Some(($($entity,)*)) => {
264 $(
265 match $name::render(_item, $view, Some($entity), $name, _pass) {
266 RenderCommandResult::Skip => return RenderCommandResult::Skip,
267 RenderCommandResult::Failure(reason) => return RenderCommandResult::Failure(reason),
268 _ => {},
269 }
270 )*
271 }
272 }
273 RenderCommandResult::Success
274 }
275 }
276 };
277}
278
279all_tuples!(
280 #[doc(fake_variadic)]
281 render_command_tuple_impl,
282 0,
283 15,
284 C,
285 V,
286 E
287);
288
289pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
294 state: SystemState<C::Param>,
295 view: QueryState<C::ViewQuery>,
296 entity: QueryState<C::ItemQuery>,
297}
298
299impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
300 pub fn new(world: &mut World) -> Self {
302 Self {
303 state: SystemState::new(world),
304 view: world.query(),
305 entity: world.query(),
306 }
307 }
308}
309
310impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for RenderCommandState<P, C>
311where
312 C::Param: ReadOnlySystemParam,
313{
314 fn prepare(&mut self, world: &'_ World) {
317 self.view.update_archetypes(world);
318 self.entity.update_archetypes(world);
319 }
320
321 fn draw<'w>(
323 &mut self,
324 world: &'w World,
325 pass: &mut TrackedRenderPass<'w>,
326 view: Entity,
327 item: &P,
328 ) -> Result<(), DrawError> {
329 let param = self.state.get(world).unwrap();
330 let view = match self.view.get_manual(world, view) {
331 Ok(view) => view,
332 Err(err) => match err {
333 QueryEntityError::NotSpawned(_) => return Err(DrawError::ViewEntityNotFound),
334 QueryEntityError::QueryDoesNotMatch(_, _)
335 | QueryEntityError::AliasedMutability(_) => {
336 return Err(DrawError::InvalidViewQuery)
337 }
338 },
339 };
340
341 let entity = self.entity.get_manual(world, item.entity()).ok();
342 match C::render(item, view, entity, param, pass) {
343 RenderCommandResult::Success | RenderCommandResult::Skip => Ok(()),
344 RenderCommandResult::Failure(reason) => Err(DrawError::RenderCommandFailure(reason)),
345 }
346 }
347}
348
349pub trait AddRenderCommand {
352 fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
354 &mut self,
355 ) -> &mut Self
356 where
357 C::Param: ReadOnlySystemParam;
358}
359
360impl AddRenderCommand for SubApp {
361 fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
362 &mut self,
363 ) -> &mut Self
364 where
365 C::Param: ReadOnlySystemParam,
366 {
367 let draw_function = RenderCommandState::<P, C>::new(self.world_mut());
368 let draw_functions = self
369 .world()
370 .get_resource::<DrawFunctions<P>>()
371 .unwrap_or_else(|| {
372 panic!(
373 "DrawFunctions<{}> must be added to the world as a resource \
374 before adding render commands to it",
375 core::any::type_name::<P>(),
376 );
377 });
378 draw_functions.write().add_with::<C, _>(draw_function);
379 self
380 }
381}
382
383impl AddRenderCommand for App {
384 fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
385 &mut self,
386 ) -> &mut Self
387 where
388 C::Param: ReadOnlySystemParam,
389 {
390 SubApp::add_render_command::<P, C>(self.main_mut());
391 self
392 }
393}