bevy_render/render_phase/
draw.rs

1use 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
14/// A draw function used to draw [`PhaseItem`]s.
15///
16/// The draw function can retrieve and query the required ECS data from the render world.
17///
18/// This trait can either be implemented directly or implicitly composed out of multiple modular
19/// [`RenderCommand`]s. For more details and an example see the [`RenderCommand`] documentation.
20pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
21    /// Prepares the draw function to be used. This is called once and only once before the phase
22    /// begins. There may be zero or more [`draw`](Draw::draw) calls following a call to this function.
23    /// Implementing this is optional.
24    #[allow(unused_variables)]
25    fn prepare(&mut self, world: &'_ World) {}
26
27    /// Draws a [`PhaseItem`] by issuing zero or more `draw` calls via the [`TrackedRenderPass`].
28    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// TODO: make this generic?
49/// An identifier for a [`Draw`] function stored in [`DrawFunctions`].
50#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
51pub struct DrawFunctionId(u32);
52
53/// Stores all [`Draw`] functions for the [`PhaseItem`] type.
54///
55/// For retrieval, the [`Draw`] functions are mapped to their respective [`TypeId`]s.
56pub 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    /// Prepares all draw function. This is called once and only once before the phase begins.
63    pub fn prepare(&mut self, world: &World) {
64        for function in &mut self.draw_functions {
65            function.prepare(world);
66        }
67    }
68
69    /// Adds the [`Draw`] function and maps it to its own type.
70    pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
71        self.add_with::<T, T>(draw_function)
72    }
73
74    /// Adds the [`Draw`] function and maps it to the type `T`
75    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    /// Retrieves the [`Draw`] function corresponding to the `id` mutably.
83    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    /// Retrieves the id of the [`Draw`] function corresponding to their associated type `T`.
88    pub fn get_id<T: 'static>(&self) -> Option<DrawFunctionId> {
89        self.indices.get(&TypeId::of::<T>()).copied()
90    }
91
92    /// Retrieves the id of the [`Draw`] function corresponding to their associated type `T`.
93    ///
94    /// Fallible wrapper for [`Self::get_id()`]
95    ///
96    /// ## Panics
97    /// If the id doesn't exist, this function will panic.
98    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/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
110///
111/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
112#[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    /// Accesses the draw functions in read mode.
130    pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
131        self.internal.read().unwrap_or_else(PoisonError::into_inner)
132    }
133
134    /// Accesses the draw functions in write mode.
135    pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
136        self.internal
137            .write()
138            .unwrap_or_else(PoisonError::into_inner)
139    }
140}
141
142/// [`RenderCommand`]s are modular standardized pieces of render logic that can be composed into
143/// [`Draw`] functions.
144///
145/// To turn a stateless render command into a usable draw function it has to be wrapped by a
146/// [`RenderCommandState`].
147/// This is done automatically when registering a render command as a [`Draw`] function via the
148/// [`AddRenderCommand::add_render_command`] method.
149///
150/// Compared to the draw function the required ECS data is fetched automatically
151/// (by the [`RenderCommandState`]) from the render world.
152/// Therefore the three types [`Param`](RenderCommand::Param),
153/// [`ViewQuery`](RenderCommand::ViewQuery) and
154/// [`ItemQuery`](RenderCommand::ItemQuery) are used.
155/// They specify which information is required to execute the render command.
156///
157/// Multiple render commands can be combined together by wrapping them in a tuple.
158///
159/// # Example
160///
161/// The `DrawMaterial` draw function is created from the following render command
162/// tuple. Const generics are used to set specific bind group locations:
163///
164/// ```
165/// # use bevy_render::render_phase::SetItemPipeline;
166/// # struct SetMeshViewBindGroup<const N: usize>;
167/// # struct SetMeshBindGroup<const N: usize>;
168/// # struct SetMaterialBindGroup<M, const N: usize>(std::marker::PhantomData<M>);
169/// # struct DrawMesh;
170/// pub type DrawMaterial<M> = (
171///     SetItemPipeline,
172///     SetMeshViewBindGroup<0>,
173///     SetMeshBindGroup<1>,
174///     SetMaterialBindGroup<M, 2>,
175///     DrawMesh,
176/// );
177/// ```
178pub trait RenderCommand<P: PhaseItem> {
179    /// Specifies the general ECS data (e.g. resources) required by [`RenderCommand::render`].
180    ///
181    /// When fetching resources, note that, due to lifetime limitations of the `Deref` trait,
182    /// [`SRes::into_inner`] must be called on each [`SRes`] reference in the
183    /// [`RenderCommand::render`] method, instead of being automatically dereferenced as is the
184    /// case in normal `systems`.
185    ///
186    /// All parameters have to be read only.
187    ///
188    /// [`SRes`]: bevy_ecs::system::lifetimeless::SRes
189    /// [`SRes::into_inner`]: bevy_ecs::system::lifetimeless::SRes::into_inner
190    type Param: SystemParam + 'static;
191    /// Specifies the ECS data of the view entity required by [`RenderCommand::render`].
192    ///
193    /// The view entity refers to the camera, or shadow-casting light, etc. from which the phase
194    /// item will be rendered from.
195    /// All components have to be accessed read only.
196    type ViewQuery: ReadOnlyQueryData;
197    /// Specifies the ECS data of the item entity required by [`RenderCommand::render`].
198    ///
199    /// The item is the entity that will be rendered for the corresponding view.
200    /// All components have to be accessed read only.
201    ///
202    /// For efficiency reasons, Bevy doesn't always extract entities to the
203    /// render world; for instance, entities that simply consist of meshes are
204    /// often not extracted. If the entity doesn't exist in the render world,
205    /// the supplied query data will be `None`.
206    type ItemQuery: ReadOnlyQueryData;
207
208    /// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups,
209    /// issuing draw calls, etc.) via the [`TrackedRenderPass`].
210    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/// The result of a [`RenderCommand`].
220#[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
279/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
280///
281/// The [`RenderCommand::Param`], [`RenderCommand::ViewQuery`] and
282/// [`RenderCommand::ItemQuery`] are fetched from the ECS and passed to the command.
283pub 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    /// Creates a new [`RenderCommandState`] for the [`RenderCommand`].
291    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    /// Prepares the render command to be used. This is called once and only once before the phase
305    /// begins. There may be zero or more [`draw`](RenderCommandState::draw) calls following a call to this function.
306    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    /// Fetches the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
313    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
340/// Registers a [`RenderCommand`] as a [`Draw`] function.
341/// They are stored inside the [`DrawFunctions`] resource of the app.
342pub trait AddRenderCommand {
343    /// Adds the [`RenderCommand`] for the specified render phase to the app.
344    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}