Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/render_phase/draw.rs
9356 views
1
use bevy_material::labels::DrawFunctionId;
2
3
use crate::render_phase::{PhaseItem, TrackedRenderPass};
4
use bevy_app::{App, SubApp};
5
use bevy_ecs::{
6
entity::Entity,
7
query::{QueryEntityError, QueryState, ROQueryItem, ReadOnlyQueryData},
8
resource::Resource,
9
system::{ReadOnlySystemParam, SystemParam, SystemParamItem, SystemState},
10
world::World,
11
};
12
use bevy_utils::TypeIdMap;
13
use core::{any::TypeId, fmt::Debug};
14
use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
15
use thiserror::Error;
16
use variadics_please::all_tuples;
17
18
/// A draw function used to draw [`PhaseItem`]s.
19
///
20
/// The draw function can retrieve and query the required ECS data from the render world.
21
///
22
/// This trait can either be implemented directly or implicitly composed out of multiple modular
23
/// [`RenderCommand`]s. For more details and an example see the [`RenderCommand`] documentation.
24
pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
25
/// Prepares the draw function to be used. This is called once and only once before the phase
26
/// begins. There may be zero or more [`draw`](Draw::draw) calls following a call to this function.
27
/// Implementing this is optional.
28
#[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
/// Draws a [`PhaseItem`] by issuing zero or more `draw` calls via the [`TrackedRenderPass`].
35
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)]
45
pub 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
54
/// Stores all [`Draw`] functions for the [`PhaseItem`] type.
55
///
56
/// For retrieval, the [`Draw`] functions are mapped to their respective [`TypeId`]s.
57
pub struct DrawFunctionsInternal<P: PhaseItem> {
58
pub draw_functions: Vec<Box<dyn Draw<P>>>,
59
pub indices: TypeIdMap<DrawFunctionId>,
60
}
61
62
impl<P: PhaseItem> DrawFunctionsInternal<P> {
63
/// Prepares all draw function. This is called once and only once before the phase begins.
64
pub fn prepare(&mut self, world: &World) {
65
for function in &mut self.draw_functions {
66
function.prepare(world);
67
}
68
}
69
70
/// Adds the [`Draw`] function and maps it to its own type.
71
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
72
self.add_with::<T, T>(draw_function)
73
}
74
75
/// Adds the [`Draw`] function and maps it to the type `T`
76
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
/// Retrieves the [`Draw`] function corresponding to the `id` mutably.
84
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
/// Retrieves the id of the [`Draw`] function corresponding to their associated type `T`.
89
pub fn get_id<T: 'static>(&self) -> Option<DrawFunctionId> {
90
self.indices.get(&TypeId::of::<T>()).copied()
91
}
92
93
/// Retrieves the id of the [`Draw`] function corresponding to their associated type `T`.
94
///
95
/// Fallible wrapper for [`Self::get_id()`]
96
///
97
/// ## Panics
98
/// If the id doesn't exist, this function will panic.
99
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
/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
111
///
112
/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
113
#[derive(Resource)]
114
pub struct DrawFunctions<P: PhaseItem> {
115
internal: RwLock<DrawFunctionsInternal<P>>,
116
}
117
118
impl<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
129
impl<P: PhaseItem> DrawFunctions<P> {
130
/// Accesses the draw functions in read mode.
131
pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
132
self.internal.read().unwrap_or_else(PoisonError::into_inner)
133
}
134
135
/// Accesses the draw functions in write mode.
136
pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
137
self.internal
138
.write()
139
.unwrap_or_else(PoisonError::into_inner)
140
}
141
}
142
143
/// [`RenderCommand`]s are modular standardized pieces of render logic that can be composed into
144
/// [`Draw`] functions.
145
///
146
/// To turn a stateless render command into a usable draw function it has to be wrapped by a
147
/// [`RenderCommandState`].
148
/// This is done automatically when registering a render command as a [`Draw`] function via the
149
/// [`AddRenderCommand::add_render_command`] method.
150
///
151
/// Compared to the draw function the required ECS data is fetched automatically
152
/// (by the [`RenderCommandState`]) from the render world.
153
/// Therefore the three types [`Param`](RenderCommand::Param),
154
/// [`ViewQuery`](RenderCommand::ViewQuery) and
155
/// [`ItemQuery`](RenderCommand::ItemQuery) are used.
156
/// They specify which information is required to execute the render command.
157
///
158
/// Multiple render commands can be combined together by wrapping them in a tuple.
159
///
160
/// # Example
161
///
162
/// The `DrawMaterial` draw function is created from the following render command
163
/// tuple. Const generics are used to set specific bind group locations:
164
///
165
/// ```
166
/// # use bevy_render::render_phase::SetItemPipeline;
167
/// # struct SetMeshViewBindGroup<const N: usize>;
168
/// # struct SetMeshViewBindingArrayBindGroup<const N: usize>;
169
/// # struct SetMeshBindGroup<const N: usize>;
170
/// # struct SetMaterialBindGroup<M, const N: usize>(std::marker::PhantomData<M>);
171
/// # struct DrawMesh;
172
/// pub type DrawMaterial<M> = (
173
/// SetItemPipeline,
174
/// SetMeshViewBindGroup<0>,
175
/// SetMeshViewBindingArrayBindGroup<1>,
176
/// SetMeshBindGroup<2>,
177
/// SetMaterialBindGroup<M, 3>,
178
/// DrawMesh,
179
/// );
180
/// ```
181
pub trait RenderCommand<P: PhaseItem> {
182
/// Specifies the general ECS data (e.g. resources) required by [`RenderCommand::render`].
183
///
184
/// When fetching resources, note that, due to lifetime limitations of the `Deref` trait,
185
/// [`SRes::into_inner`] must be called on each [`SRes`] reference in the
186
/// [`RenderCommand::render`] method, instead of being automatically dereferenced as is the
187
/// case in normal `systems`.
188
///
189
/// All parameters have to be read only.
190
///
191
/// [`SRes`]: bevy_ecs::system::lifetimeless::SRes
192
/// [`SRes::into_inner`]: bevy_ecs::system::lifetimeless::SRes::into_inner
193
type Param: SystemParam + 'static;
194
/// Specifies the ECS data of the view entity required by [`RenderCommand::render`].
195
///
196
/// The view entity refers to the camera, or shadow-casting light, etc. from which the phase
197
/// item will be rendered from.
198
/// All components have to be accessed read only.
199
type ViewQuery: ReadOnlyQueryData;
200
/// Specifies the ECS data of the item entity required by [`RenderCommand::render`].
201
///
202
/// The item is the entity that will be rendered for the corresponding view.
203
/// All components have to be accessed read only.
204
///
205
/// For efficiency reasons, Bevy doesn't always extract entities to the
206
/// render world; for instance, entities that simply consist of meshes are
207
/// often not extracted. If the entity doesn't exist in the render world,
208
/// the supplied query data will be `None`.
209
type ItemQuery: ReadOnlyQueryData;
210
211
/// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups,
212
/// issuing draw calls, etc.) via the [`TrackedRenderPass`].
213
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
/// The result of a [`RenderCommand`].
223
#[derive(Debug)]
224
pub enum RenderCommandResult {
225
Success,
226
Skip,
227
Failure(&'static str),
228
}
229
230
macro_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
all_tuples!(
280
#[doc(fake_variadic)]
281
render_command_tuple_impl,
282
0,
283
15,
284
C,
285
V,
286
E
287
);
288
289
/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
290
///
291
/// The [`RenderCommand::Param`], [`RenderCommand::ViewQuery`] and
292
/// [`RenderCommand::ItemQuery`] are fetched from the ECS and passed to the command.
293
pub 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
299
impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
300
/// Creates a new [`RenderCommandState`] for the [`RenderCommand`].
301
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
310
impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for RenderCommandState<P, C>
311
where
312
C::Param: ReadOnlySystemParam,
313
{
314
/// Prepares the render command to be used. This is called once and only once before the phase
315
/// begins. There may be zero or more [`draw`](RenderCommandState::draw) calls following a call to this function.
316
fn prepare(&mut self, world: &'_ World) {
317
self.view.update_archetypes(world);
318
self.entity.update_archetypes(world);
319
}
320
321
/// Fetches the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
322
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);
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
349
/// Registers a [`RenderCommand`] as a [`Draw`] function.
350
/// They are stored inside the [`DrawFunctions`] resource of the app.
351
pub trait AddRenderCommand {
352
/// Adds the [`RenderCommand`] for the specified render phase to the app.
353
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
360
impl 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
383
impl 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
}
394
395