1
- use crate :: {
2
- render_phase:: TrackedRenderPass ,
3
- render_resource:: { CachedRenderPipelineId , PipelineCache } ,
4
- } ;
1
+ use crate :: render_phase:: { PhaseItem , TrackedRenderPass } ;
5
2
use bevy_app:: App ;
6
3
use bevy_ecs:: {
7
4
all_tuples,
8
5
entity:: Entity ,
9
6
query:: { QueryState , ROQueryItem , ReadOnlyWorldQuery } ,
10
- system:: {
11
- lifetimeless:: SRes , ReadOnlySystemParam , Resource , SystemParam , SystemParamItem ,
12
- SystemState ,
13
- } ,
7
+ system:: { ReadOnlySystemParam , Resource , SystemParam , SystemParamItem , SystemState } ,
14
8
world:: World ,
15
9
} ;
16
10
use bevy_utils:: HashMap ;
17
11
use parking_lot:: { RwLock , RwLockReadGuard , RwLockWriteGuard } ;
18
- use std:: { any:: TypeId , fmt:: Debug , hash:: Hash , ops :: Range } ;
12
+ use std:: { any:: TypeId , fmt:: Debug , hash:: Hash } ;
19
13
20
- /// A draw function which is used to draw a specific [`PhaseItem`].
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.
21
17
///
22
- /// They are the general form of drawing items, whereas [`RenderCommands`](RenderCommand)
23
- /// are more modular .
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 .
24
20
pub trait Draw < P : PhaseItem > : Send + Sync + ' static {
25
21
/// 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` calls following a call to this function..
22
+ /// begins. There may be zero or more `draw` calls following a call to this function.
27
23
/// Implementing this is optional.
28
24
#[ allow( unused_variables) ]
29
25
fn prepare ( & mut self , world : & ' _ World ) { }
30
26
31
- /// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
27
+ /// Draws a [`PhaseItem`] by issuing zero or more ` draw` calls via the [`TrackedRenderPass`].
32
28
fn draw < ' w > (
33
29
& mut self ,
34
30
world : & ' w World ,
@@ -38,64 +34,33 @@ pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
38
34
) ;
39
35
}
40
36
41
- /// An item which will be drawn to the screen. A phase item should be queued up for rendering
42
- /// during the [`RenderStage::Queue`](crate::RenderStage::Queue) stage.
43
- /// Afterwards it will be sorted and rendered automatically in the
44
- /// [`RenderStage::PhaseSort`](crate::RenderStage::PhaseSort) stage and
45
- /// [`RenderStage::Render`](crate::RenderStage::Render) stage, respectively.
46
- pub trait PhaseItem : Sized + Send + Sync + ' static {
47
- /// The type used for ordering the items. The smallest values are drawn first.
48
- type SortKey : Ord ;
49
- fn entity ( & self ) -> Entity ;
50
-
51
- /// Determines the order in which the items are drawn during the corresponding [`RenderPhase`](super::RenderPhase).
52
- fn sort_key ( & self ) -> Self :: SortKey ;
53
- /// Specifies the [`Draw`] function used to render the item.
54
- fn draw_function ( & self ) -> DrawFunctionId ;
55
-
56
- /// Sorts a slice of phase items into render order. Generally if the same type
57
- /// implements [`BatchedPhaseItem`], this should use a stable sort like [`slice::sort_by_key`].
58
- /// In almost all other cases, this should not be altered from the default,
59
- /// which uses a unstable sort, as this provides the best balance of CPU and GPU
60
- /// performance.
61
- ///
62
- /// Implementers can optionally not sort the list at all. This is generally advisable if and
63
- /// only if the renderer supports a depth prepass, which is by default not supported by
64
- /// the rest of Bevy's first party rendering crates. Even then, this may have a negative
65
- /// impact on GPU-side performance due to overdraw.
66
- ///
67
- /// It's advised to always profile for performance changes when changing this implementation.
68
- #[ inline]
69
- fn sort ( items : & mut [ Self ] ) {
70
- items. sort_unstable_by_key ( |item| item. sort_key ( ) ) ;
71
- }
72
- }
73
-
74
37
// TODO: make this generic?
75
38
/// An identifier for a [`Draw`] function stored in [`DrawFunctions`].
76
39
#[ derive( Copy , Clone , Debug , Eq , PartialEq , Hash ) ]
77
40
pub struct DrawFunctionId ( u32 ) ;
78
41
79
- /// Stores all draw functions for the [`PhaseItem`] type.
80
- /// For retrieval they are associated with their [`TypeId`].
42
+ /// Stores all [`Draw`] functions for the [`PhaseItem`] type.
43
+ ///
44
+ /// For retrieval, the [`Draw`] functions are mapped to their respective [`TypeId`]s.
81
45
pub struct DrawFunctionsInternal < P : PhaseItem > {
82
46
pub draw_functions : Vec < Box < dyn Draw < P > > > ,
83
47
pub indices : HashMap < TypeId , DrawFunctionId > ,
84
48
}
85
49
86
50
impl < P : PhaseItem > DrawFunctionsInternal < P > {
51
+ /// Prepares all draw function. This is called once and only once before the phase begins.
87
52
pub fn prepare ( & mut self , world : & World ) {
88
53
for function in & mut self . draw_functions {
89
54
function. prepare ( world) ;
90
55
}
91
56
}
92
57
93
- /// Adds the [`Draw`] function and associates it to its own type.
58
+ /// Adds the [`Draw`] function and maps it to its own type.
94
59
pub fn add < T : Draw < P > > ( & mut self , draw_function : T ) -> DrawFunctionId {
95
60
self . add_with :: < T , T > ( draw_function)
96
61
}
97
62
98
- /// Adds the [`Draw`] function and associates it to the type `T`
63
+ /// Adds the [`Draw`] function and maps it to the type `T`
99
64
pub fn add_with < T : ' static , D : Draw < P > > ( & mut self , draw_function : D ) -> DrawFunctionId {
100
65
let id = DrawFunctionId ( self . draw_functions . len ( ) . try_into ( ) . unwrap ( ) ) ;
101
66
self . draw_functions . push ( Box :: new ( draw_function) ) ;
@@ -118,7 +83,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
118
83
/// Fallible wrapper for [`Self::get_id()`]
119
84
///
120
85
/// ## Panics
121
- /// If the id doesn't exist it will panic
86
+ /// If the id doesn't exist, this function will panic.
122
87
pub fn id < T : ' static > ( & self ) -> DrawFunctionId {
123
88
self . get_id :: < T > ( ) . unwrap_or_else ( || {
124
89
panic ! (
@@ -131,6 +96,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
131
96
}
132
97
133
98
/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
99
+ ///
134
100
/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
135
101
#[ derive( Resource ) ]
136
102
pub struct DrawFunctions < P : PhaseItem > {
@@ -160,15 +126,26 @@ impl<P: PhaseItem> DrawFunctions<P> {
160
126
}
161
127
}
162
128
163
- /// [`RenderCommand`] is a trait that runs an ECS query and produces one or more
164
- /// [`TrackedRenderPass `] calls. Types implementing this trait can be composed (as tuples) .
129
+ /// [`RenderCommand`]s are modular standardized pieces of render logic that can be composed into
130
+ /// [`Draw `] functions .
165
131
///
166
- /// They can be registered as a [`Draw`] function via the
132
+ /// To turn a stateless render command into a usable draw function it has to be wrapped by a
133
+ /// [`RenderCommandState`].
134
+ /// This is done automatically when registering a render command as a [`Draw`] function via the
167
135
/// [`AddRenderCommand::add_render_command`] method.
168
136
///
137
+ /// Compared to the draw function the required ECS data is fetched automatically
138
+ /// (by the [`RenderCommandState`]) from the render world.
139
+ /// Therefore the three types [`Param`](RenderCommand::Param),
140
+ /// [`ViewWorldQuery`](RenderCommand::ViewWorldQuery) and
141
+ /// [`ItemWorldQuery`](RenderCommand::ItemWorldQuery) are used.
142
+ /// They specify which information is required to execute the render command.
143
+ ///
144
+ /// Multiple render commands can be combined together by wrapping them in a tuple.
145
+ ///
169
146
/// # Example
170
147
/// The `DrawPbr` draw function is created from the following render command
171
- /// tuple. Const generics are used to set specific bind group locations:
148
+ /// tuple. Const generics are used to set specific bind group locations:
172
149
///
173
150
/// ```ignore
174
151
/// pub type DrawPbr = (
@@ -180,13 +157,24 @@ impl<P: PhaseItem> DrawFunctions<P> {
180
157
/// );
181
158
/// ```
182
159
pub trait RenderCommand < P : PhaseItem > {
183
- /// Specifies all ECS data required by [`RenderCommand::render`].
160
+ /// Specifies the general ECS data (e.g. resources) required by [`RenderCommand::render`].
161
+ ///
184
162
/// All parameters have to be read only.
185
163
type Param : SystemParam + ' static ;
164
+ /// Specifies the ECS data of the view entity required by [`RenderCommand::render`].
165
+ ///
166
+ /// The view entity refers to the camera, or shadow-casting light, etc. from which the phase
167
+ /// item will be rendered from.
168
+ /// All components have to be accessed read only.
186
169
type ViewWorldQuery : ReadOnlyWorldQuery ;
170
+ /// Specifies the ECS data of the item entity required by [`RenderCommand::render`].
171
+ ///
172
+ /// The item is the entity that will be rendered for the corresponding view.
173
+ /// All components have to be accessed read only.
187
174
type ItemWorldQuery : ReadOnlyWorldQuery ;
188
175
189
- /// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
176
+ /// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups,
177
+ /// issuing draw calls, etc.) via the [`TrackedRenderPass`].
190
178
fn render < ' w > (
191
179
item : & P ,
192
180
view : ROQueryItem < ' w , Self :: ViewWorldQuery > ,
@@ -196,86 +184,12 @@ pub trait RenderCommand<P: PhaseItem> {
196
184
) -> RenderCommandResult ;
197
185
}
198
186
187
+ /// The result of a [`RenderCommand`].
199
188
pub enum RenderCommandResult {
200
189
Success ,
201
190
Failure ,
202
191
}
203
192
204
- pub trait CachedRenderPipelinePhaseItem : PhaseItem {
205
- fn cached_pipeline ( & self ) -> CachedRenderPipelineId ;
206
- }
207
-
208
- /// A [`PhaseItem`] that can be batched dynamically.
209
- ///
210
- /// Batching is an optimization that regroups multiple items in the same vertex buffer
211
- /// to render them in a single draw call.
212
- ///
213
- /// If this is implemented on a type, the implementation of [`PhaseItem::sort`] should
214
- /// be changed to implement a stable sort, or incorrect/suboptimal batching may result.
215
- pub trait BatchedPhaseItem : PhaseItem {
216
- /// Range in the vertex buffer of this item
217
- fn batch_range ( & self ) -> & Option < Range < u32 > > ;
218
-
219
- /// Range in the vertex buffer of this item
220
- fn batch_range_mut ( & mut self ) -> & mut Option < Range < u32 > > ;
221
-
222
- /// Batches another item within this item if they are compatible.
223
- /// Items can be batched together if they have the same entity, and consecutive ranges.
224
- /// If batching is successful, the `other` item should be discarded from the render pass.
225
- #[ inline]
226
- fn add_to_batch ( & mut self , other : & Self ) -> BatchResult {
227
- let self_entity = self . entity ( ) ;
228
- if let ( Some ( self_batch_range) , Some ( other_batch_range) ) = (
229
- self . batch_range_mut ( ) . as_mut ( ) ,
230
- other. batch_range ( ) . as_ref ( ) ,
231
- ) {
232
- // If the items are compatible, join their range into `self`
233
- if self_entity == other. entity ( ) {
234
- if self_batch_range. end == other_batch_range. start {
235
- self_batch_range. end = other_batch_range. end ;
236
- return BatchResult :: Success ;
237
- } else if self_batch_range. start == other_batch_range. end {
238
- self_batch_range. start = other_batch_range. start ;
239
- return BatchResult :: Success ;
240
- }
241
- }
242
- }
243
- BatchResult :: IncompatibleItems
244
- }
245
- }
246
-
247
- pub enum BatchResult {
248
- /// The `other` item was batched into `self`
249
- Success ,
250
- /// `self` and `other` cannot be batched together
251
- IncompatibleItems ,
252
- }
253
-
254
- pub struct SetItemPipeline ;
255
- impl < P : CachedRenderPipelinePhaseItem > RenderCommand < P > for SetItemPipeline {
256
- type Param = SRes < PipelineCache > ;
257
- type ViewWorldQuery = ( ) ;
258
- type ItemWorldQuery = ( ) ;
259
- #[ inline]
260
- fn render < ' w > (
261
- item : & P ,
262
- _view : ( ) ,
263
- _entity : ( ) ,
264
- pipeline_cache : SystemParamItem < ' w , ' _ , Self :: Param > ,
265
- pass : & mut TrackedRenderPass < ' w > ,
266
- ) -> RenderCommandResult {
267
- if let Some ( pipeline) = pipeline_cache
268
- . into_inner ( )
269
- . get_render_pipeline ( item. cached_pipeline ( ) )
270
- {
271
- pass. set_render_pipeline ( pipeline) ;
272
- RenderCommandResult :: Success
273
- } else {
274
- RenderCommandResult :: Failure
275
- }
276
- }
277
- }
278
-
279
193
macro_rules! render_command_tuple_impl {
280
194
( $( ( $name: ident, $view: ident, $entity: ident) ) ,* ) => {
281
195
impl <P : PhaseItem , $( $name: RenderCommand <P >) ,* > RenderCommand <P > for ( $( $name, ) * ) {
@@ -303,14 +217,17 @@ macro_rules! render_command_tuple_impl {
303
217
all_tuples ! ( render_command_tuple_impl, 0 , 15 , C , V , E ) ;
304
218
305
219
/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
306
- /// Therefore the [`RenderCommand::Param`] is queried from the ECS and passed to the command.
220
+ ///
221
+ /// The [`RenderCommand::Param`], [`RenderCommand::ViewWorldQuery`] and
222
+ /// [`RenderCommand::ItemWorldQuery`] are fetched from the ECS and passed to the command.
307
223
pub struct RenderCommandState < P : PhaseItem + ' static , C : RenderCommand < P > > {
308
224
state : SystemState < C :: Param > ,
309
225
view : QueryState < C :: ViewWorldQuery > ,
310
226
entity : QueryState < C :: ItemWorldQuery > ,
311
227
}
312
228
313
229
impl < P : PhaseItem , C : RenderCommand < P > > RenderCommandState < P , C > {
230
+ /// Creates a new [`RenderCommandState`] for the [`RenderCommand`].
314
231
pub fn new ( world : & mut World ) -> Self {
315
232
Self {
316
233
state : SystemState :: new ( world) ,
@@ -324,12 +241,14 @@ impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for Rend
324
241
where
325
242
C :: Param : ReadOnlySystemParam ,
326
243
{
244
+ /// Prepares the render command to be used. This is called once and only once before the phase
245
+ /// begins. There may be zero or more `draw` calls following a call to this function.
327
246
fn prepare ( & mut self , world : & ' _ World ) {
328
247
self . view . update_archetypes ( world) ;
329
248
self . entity . update_archetypes ( world) ;
330
249
}
331
250
332
- /// Prepares the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
251
+ /// Fetches the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
333
252
fn draw < ' w > (
334
253
& mut self ,
335
254
world : & ' w World ,
@@ -340,6 +259,7 @@ where
340
259
let param = self . state . get ( world) ;
341
260
let view = self . view . get_manual ( world, view) . unwrap ( ) ;
342
261
let entity = self . entity . get_manual ( world, item. entity ( ) ) . unwrap ( ) ;
262
+ // TODO: handle/log `RenderCommand` failure
343
263
C :: render ( item, view, entity, param, pass) ;
344
264
}
345
265
}
0 commit comments