Skip to content

Commit 76de9f9

Browse files
committed
Improve render phase documentation (#7016)
# Objective The documentation of the bevy_render crate is still pretty incomplete. This PR follows up on #6885 and improves the documentation of the `render_phase` module. This module contains one of our most important rendering abstractions and the current documentation is pretty confusing. This PR tries to clarify what all of these pieces are for and how they work together to form bevy`s modular rendering logic. ## Solution ### Code Reformating - I have moved the `rangefinder` into the `render_phase` module since it is only used there. - I have moved the `PhaseItem` (and the `BatchedPhaseItem`) from `render_phase::draw` over to `render_phase::mod`. This does not change the public-facing API since they are reexported anyway, but this change makes the relation between `RenderPhase` and `PhaseItem` clear and easier to discover. ### Documentation - revised all documentation in the `render_phase` module - added a module-level explanation of how `RenderPhase`s, `RenderPass`es, `PhaseItem`s, `Draw` functions, and `RenderCommands` relate to each other and how they are used --- ## Changelog - The `rangefinder` module has been moved into the `render_phase` module. ## Migration Guide - The `rangefinder` module has been moved into the `render_phase` module. ```rust //old use bevy::render::rangefinder::*; // new use bevy::render::render_phase::rangefinder::*; ```
1 parent f4920bb commit 76de9f9

File tree

6 files changed

+307
-187
lines changed

6 files changed

+307
-187
lines changed

crates/bevy_render/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ pub mod extract_resource;
1111
pub mod globals;
1212
pub mod mesh;
1313
pub mod primitives;
14-
pub mod rangefinder;
1514
pub mod render_asset;
1615
pub mod render_graph;
1716
pub mod render_phase;

crates/bevy_render/src/render_phase/draw.rs

Lines changed: 55 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,30 @@
1-
use crate::{
2-
render_phase::TrackedRenderPass,
3-
render_resource::{CachedRenderPipelineId, PipelineCache},
4-
};
1+
use crate::render_phase::{PhaseItem, TrackedRenderPass};
52
use bevy_app::App;
63
use bevy_ecs::{
74
all_tuples,
85
entity::Entity,
96
query::{QueryState, ROQueryItem, ReadOnlyWorldQuery},
10-
system::{
11-
lifetimeless::SRes, ReadOnlySystemParam, Resource, SystemParam, SystemParamItem,
12-
SystemState,
13-
},
7+
system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState},
148
world::World,
159
};
1610
use bevy_utils::HashMap;
1711
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};
1913

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.
2117
///
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.
2420
pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
2521
/// 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.
2723
/// Implementing this is optional.
2824
#[allow(unused_variables)]
2925
fn prepare(&mut self, world: &'_ World) {}
3026

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`].
3228
fn draw<'w>(
3329
&mut self,
3430
world: &'w World,
@@ -38,64 +34,33 @@ pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
3834
);
3935
}
4036

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-
7437
// TODO: make this generic?
7538
/// An identifier for a [`Draw`] function stored in [`DrawFunctions`].
7639
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
7740
pub struct DrawFunctionId(u32);
7841

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.
8145
pub struct DrawFunctionsInternal<P: PhaseItem> {
8246
pub draw_functions: Vec<Box<dyn Draw<P>>>,
8347
pub indices: HashMap<TypeId, DrawFunctionId>,
8448
}
8549

8650
impl<P: PhaseItem> DrawFunctionsInternal<P> {
51+
/// Prepares all draw function. This is called once and only once before the phase begins.
8752
pub fn prepare(&mut self, world: &World) {
8853
for function in &mut self.draw_functions {
8954
function.prepare(world);
9055
}
9156
}
9257

93-
/// Adds the [`Draw`] function and associates it to its own type.
58+
/// Adds the [`Draw`] function and maps it to its own type.
9459
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
9560
self.add_with::<T, T>(draw_function)
9661
}
9762

98-
/// Adds the [`Draw`] function and associates it to the type `T`
63+
/// Adds the [`Draw`] function and maps it to the type `T`
9964
pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
10065
let id = DrawFunctionId(self.draw_functions.len().try_into().unwrap());
10166
self.draw_functions.push(Box::new(draw_function));
@@ -118,7 +83,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
11883
/// Fallible wrapper for [`Self::get_id()`]
11984
///
12085
/// ## Panics
121-
/// If the id doesn't exist it will panic
86+
/// If the id doesn't exist, this function will panic.
12287
pub fn id<T: 'static>(&self) -> DrawFunctionId {
12388
self.get_id::<T>().unwrap_or_else(|| {
12489
panic!(
@@ -131,6 +96,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
13196
}
13297

13398
/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
99+
///
134100
/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
135101
#[derive(Resource)]
136102
pub struct DrawFunctions<P: PhaseItem> {
@@ -160,15 +126,26 @@ impl<P: PhaseItem> DrawFunctions<P> {
160126
}
161127
}
162128

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.
165131
///
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
167135
/// [`AddRenderCommand::add_render_command`] method.
168136
///
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+
///
169146
/// # Example
170147
/// 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:
172149
///
173150
/// ```ignore
174151
/// pub type DrawPbr = (
@@ -180,13 +157,24 @@ impl<P: PhaseItem> DrawFunctions<P> {
180157
/// );
181158
/// ```
182159
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+
///
184162
/// All parameters have to be read only.
185163
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.
186169
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.
187174
type ItemWorldQuery: ReadOnlyWorldQuery;
188175

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`].
190178
fn render<'w>(
191179
item: &P,
192180
view: ROQueryItem<'w, Self::ViewWorldQuery>,
@@ -196,86 +184,12 @@ pub trait RenderCommand<P: PhaseItem> {
196184
) -> RenderCommandResult;
197185
}
198186

187+
/// The result of a [`RenderCommand`].
199188
pub enum RenderCommandResult {
200189
Success,
201190
Failure,
202191
}
203192

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-
279193
macro_rules! render_command_tuple_impl {
280194
($(($name: ident, $view: ident, $entity: ident)),*) => {
281195
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
@@ -303,14 +217,17 @@ macro_rules! render_command_tuple_impl {
303217
all_tuples!(render_command_tuple_impl, 0, 15, C, V, E);
304218

305219
/// 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.
307223
pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
308224
state: SystemState<C::Param>,
309225
view: QueryState<C::ViewWorldQuery>,
310226
entity: QueryState<C::ItemWorldQuery>,
311227
}
312228

313229
impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
230+
/// Creates a new [`RenderCommandState`] for the [`RenderCommand`].
314231
pub fn new(world: &mut World) -> Self {
315232
Self {
316233
state: SystemState::new(world),
@@ -324,12 +241,14 @@ impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for Rend
324241
where
325242
C::Param: ReadOnlySystemParam,
326243
{
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.
327246
fn prepare(&mut self, world: &'_ World) {
328247
self.view.update_archetypes(world);
329248
self.entity.update_archetypes(world);
330249
}
331250

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.
333252
fn draw<'w>(
334253
&mut self,
335254
world: &'w World,
@@ -340,6 +259,7 @@ where
340259
let param = self.state.get(world);
341260
let view = self.view.get_manual(world, view).unwrap();
342261
let entity = self.entity.get_manual(world, item.entity()).unwrap();
262+
// TODO: handle/log `RenderCommand` failure
343263
C::render(item, view, entity, param, pass);
344264
}
345265
}

0 commit comments

Comments
 (0)