1use 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::TypeIdHashMap;
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(#[allow(unused_qualifications)]
#[automatically_derived]
impl ::thiserror::__private21::Error for DrawError { }
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::core::fmt::Display for DrawError {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter)
-> ::core::fmt::Result {
#[allow(unused_variables, deprecated, clippy ::
used_underscore_binding)]
match self {
DrawError::RenderCommandFailure(_0) =>
match (_0,) {
(__field0,) =>
__formatter.write_fmt(format_args!("Failed to execute render command {0:?}",
__field0)),
},
DrawError::InvalidViewQuery {} =>
__formatter.write_str("Failed to get execute view query"),
DrawError::ViewEntityNotFound {} =>
__formatter.write_str("View entity not found"),
}
}
}Error, #[automatically_derived]impl ::core::fmt::Debug for DrawError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::RenderCommandFailure(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"RenderCommandFailure", &__self_0),
Self::InvalidViewQuery =>
::core::fmt::Formatter::write_str(f, "InvalidViewQuery"),
Self::ViewEntityNotFound =>
::core::fmt::Formatter::write_str(f, "ViewEntityNotFound"),
}
}
}Debug, #[automatically_derived]impl ::core::marker::StructuralPartialEq for DrawError
{
}
#[automatically_derived]impl ::core::cmp::PartialEq for DrawError {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::RenderCommandFailure(__self_0),
Self::RenderCommandFailure(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for DrawError {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<&'static str>;
}
}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: TypeIdHashMap<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 {
::core::panicking::panic_fmt(format_args!("Draw function {0} not found for {1}",
core::any::type_name::<T>(), core::any::type_name::<P>()));
}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(impl<P: PhaseItem> bevy_ecs::component::Component for DrawFunctions<P> where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<DrawFunctions<P>>()
{
id
} else {
required_components.components_registrator().register_component::<DrawFunctions<P>>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl<P: PhaseItem> bevy_ecs::resource::Resource for DrawFunctions<P> where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}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(#[automatically_derived]impl ::core::fmt::Debug for RenderCommandResult {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Success => ::core::fmt::Formatter::write_str(f, "Success"),
Self::Skip => ::core::fmt::Formatter::write_str(f, "Skip"),
Self::Failure(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Failure", &__self_0),
}
}
}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
279#[doc(hidden)]
impl<P: PhaseItem, C0: RenderCommand<P>, C1: RenderCommand<P>,
C2: RenderCommand<P>, C3: RenderCommand<P>, C4: RenderCommand<P>,
C5: RenderCommand<P>, C6: RenderCommand<P>, C7: RenderCommand<P>,
C8: RenderCommand<P>, C9: RenderCommand<P>, C10: RenderCommand<P>,
C11: RenderCommand<P>, C12: RenderCommand<P>, C13: RenderCommand<P>,
C14: RenderCommand<P>> RenderCommand<P> for
(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14) {
type Param =
(C0::Param, C1::Param, C2::Param, C3::Param, C4::Param, C5::Param,
C6::Param, C7::Param, C8::Param, C9::Param, C10::Param, C11::Param,
C12::Param, C13::Param, C14::Param);
type ViewQuery =
(C0::ViewQuery, C1::ViewQuery, C2::ViewQuery, C3::ViewQuery,
C4::ViewQuery, C5::ViewQuery, C6::ViewQuery, C7::ViewQuery,
C8::ViewQuery, C9::ViewQuery, C10::ViewQuery, C11::ViewQuery,
C12::ViewQuery, C13::ViewQuery, C14::ViewQuery);
type ItemQuery =
(C0::ItemQuery, C1::ItemQuery, C2::ItemQuery, C3::ItemQuery,
C4::ItemQuery, C5::ItemQuery, C6::ItemQuery, C7::ItemQuery,
C8::ItemQuery, C9::ItemQuery, C10::ItemQuery, C11::ItemQuery,
C12::ItemQuery, C13::ItemQuery, C14::ItemQuery);
#[expect(clippy :: allow_attributes, reason =
"We are in a macro; as such, `non_snake_case` may not always lint.")]
#[allow(non_snake_case, reason =
"Parameter and variable names are provided by the macro invocation, not by us.")]
fn render<'w>(_item: &P,
(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14):
ROQueryItem<'w, '_, Self::ViewQuery>,
maybe_entities: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14):
SystemParamItem<'w, '_, Self::Param>,
_pass: &mut TrackedRenderPass<'w>) -> RenderCommandResult {
match maybe_entities {
None => {
match C0::render(_item, V0, None, C0, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C1::render(_item, V1, None, C1, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C2::render(_item, V2, None, C2, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C3::render(_item, V3, None, C3, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C4::render(_item, V4, None, C4, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C5::render(_item, V5, None, C5, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C6::render(_item, V6, None, C6, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C7::render(_item, V7, None, C7, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C8::render(_item, V8, None, C8, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C9::render(_item, V9, None, C9, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C10::render(_item, V10, None, C10, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C11::render(_item, V11, None, C11, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C12::render(_item, V12, None, C12, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C13::render(_item, V13, None, C13, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C14::render(_item, V14, None, C14, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
}
Some((E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13,
E14)) => {
match C0::render(_item, V0, Some(E0), C0, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C1::render(_item, V1, Some(E1), C1, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C2::render(_item, V2, Some(E2), C2, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C3::render(_item, V3, Some(E3), C3, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C4::render(_item, V4, Some(E4), C4, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C5::render(_item, V5, Some(E5), C5, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C6::render(_item, V6, Some(E6), C6, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C7::render(_item, V7, Some(E7), C7, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C8::render(_item, V8, Some(E8), C8, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C9::render(_item, V9, Some(E9), C9, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C10::render(_item, V10, Some(E10), C10, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C11::render(_item, V11, Some(E11), C11, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C12::render(_item, V12, Some(E12), C12, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C13::render(_item, V13, Some(E13), C13, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
match C14::render(_item, V14, Some(E14), C14, _pass) {
RenderCommandResult::Skip =>
return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) =>
return RenderCommandResult::Failure(reason),
_ => {}
}
}
}
RenderCommandResult::Success
}
}all_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 {
::core::panicking::panic_fmt(format_args!("DrawFunctions<{0}> must be added to the world as a resource before adding render commands to it",
core::any::type_name::<P>()));
};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}