Skip to content

Commit 218a848

Browse files
committed
improved documentation
1 parent 2d727af commit 218a848

File tree

7 files changed

+230
-163
lines changed

7 files changed

+230
-163
lines changed

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ use bevy_render::{
2929
prelude::Msaa,
3030
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
3131
render_phase::{
32+
<<<<<<< HEAD
3233
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
3334
RenderPhase,
35+
=======
36+
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions,
37+
PhaseItem, RenderPhase,
38+
>>>>>>> b411da63f (Flatten EntityPhaseItem into PhaseItem)
3439
},
3540
render_resource::{
3641
CachedRenderPipelineId, Extent3d, TextureDescriptor, TextureDimension, TextureFormat,

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;
Lines changed: 68 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
render_phase::TrackedRenderPass,
2+
render_phase::{PhaseItem, TrackedRenderPass},
33
render_resource::{CachedRenderPipelineId, PipelineCache},
44
};
55
use bevy_app::App;
@@ -15,20 +15,22 @@ use bevy_ecs::{
1515
};
1616
use bevy_utils::HashMap;
1717
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
18-
use std::{any::TypeId, fmt::Debug, hash::Hash, ops::Range};
18+
use std::{any::TypeId, fmt::Debug, hash::Hash};
1919

20-
/// A draw function which is used to draw a specific [`PhaseItem`].
20+
// Todo: consider renaming this to `DrawFunction`
21+
/// A draw function used to draw [`PhaseItem`]s.
2122
///
22-
/// They are the general form of drawing items, whereas [`RenderCommands`](RenderCommand)
23-
/// are more modular.
23+
/// Therefore the draw function can retrieve and query the required ECS data from the render world.
24+
///
25+
/// This trait can either be implemented directly or implicitly composed out of multiple modular [`RenderCommand`]s.
2426
pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
2527
/// Prepares the draw function to be used. This is called once and only once before the phase
2628
/// begins. There may be zero or more `draw` calls following a call to this function..
2729
/// Implementing this is optional.
2830
#[allow(unused_variables)]
2931
fn prepare(&mut self, world: &'_ World) {}
3032

31-
/// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
33+
/// Draws a [`PhaseItem`] by issuing one or more draw calls via the [`TrackedRenderPass`].
3234
fn draw<'w>(
3335
&mut self,
3436
world: &'w World,
@@ -38,64 +40,33 @@ pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
3840
);
3941
}
4042

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

79-
/// Stores all draw functions for the [`PhaseItem`] type.
80-
/// For retrieval they are associated with their [`TypeId`].
48+
/// Stores all [`Draw`] functions for the [`PhaseItem`] type.
49+
///
50+
/// For retrieval, the [`Draw`] functions are mapped to their respective [`TypeId`]s.
8151
pub struct DrawFunctionsInternal<P: PhaseItem> {
8252
pub draw_functions: Vec<Box<dyn Draw<P>>>,
8353
pub indices: HashMap<TypeId, DrawFunctionId>,
8454
}
8555

8656
impl<P: PhaseItem> DrawFunctionsInternal<P> {
57+
// Todo: add comment
8758
pub fn prepare(&mut self, world: &World) {
8859
for function in &mut self.draw_functions {
8960
function.prepare(world);
9061
}
9162
}
9263

93-
/// Adds the [`Draw`] function and associates it to its own type.
64+
/// Adds the [`Draw`] function and maps it to its own type.
9465
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
9566
self.add_with::<T, T>(draw_function)
9667
}
9768

98-
/// Adds the [`Draw`] function and associates it to the type `T`
69+
/// Adds the [`Draw`] function and maps it to the type `T`
9970
pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
10071
let id = DrawFunctionId(self.draw_functions.len().try_into().unwrap());
10172
self.draw_functions.push(Box::new(draw_function));
@@ -118,7 +89,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
11889
/// Fallible wrapper for [`Self::get_id()`]
11990
///
12091
/// ## Panics
121-
/// If the id doesn't exist it will panic
92+
/// If the id doesn't exist, this function will panic.
12293
pub fn id<T: 'static>(&self) -> DrawFunctionId {
12394
self.get_id::<T>().unwrap_or_else(|| {
12495
panic!(
@@ -131,6 +102,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
131102
}
132103

133104
/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
105+
///
134106
/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
135107
#[derive(Resource)]
136108
pub struct DrawFunctions<P: PhaseItem> {
@@ -160,15 +132,20 @@ impl<P: PhaseItem> DrawFunctions<P> {
160132
}
161133
}
162134

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).
135+
/// [`RenderCommand`]s are modular standardized pieces of render logic that can be composed into [`Draw`] functions.
165136
///
166-
/// They can be registered as a [`Draw`] function via the
167-
/// [`AddRenderCommand::add_render_command`] method.
137+
/// To turn a stateless render command into a usable draw function it has to be wrapped by a [`RenderCommandState`].
138+
/// This is done automatically when registering a render command as a [`Draw`] function via the [`AddRenderCommand::add_render_command`] method.
139+
///
140+
/// Compared to the draw function the required ECS data is fetched automatically (by the [`RenderCommandState`]) from the render world.
141+
/// Therefore the three types [`Param`](RenderCommand::Param), [`ViewWorldQuery`](RenderCommand::ViewWorldQuery) and [`WorldQuery`](RenderCommand::WorldQuery) 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.
168145
///
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 = (
@@ -183,10 +160,12 @@ pub trait RenderCommand<P: PhaseItem> {
183160
/// Specifies all ECS data required by [`RenderCommand::render`].
184161
/// All parameters have to be read only.
185162
type Param: SystemParam + 'static;
163+
// Todo: add comment
186164
type ViewWorldQuery: ReadOnlyWorldQuery;
165+
// Todo: add comment
187166
type ItemWorldQuery: ReadOnlyWorldQuery;
188167

189-
/// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
168+
/// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups, issuing draw calls, etc.) via the [`TrackedRenderPass`].
190169
fn render<'w>(
191170
item: &P,
192171
view: ROQueryItem<'w, Self::ViewWorldQuery>,
@@ -196,86 +175,12 @@ pub trait RenderCommand<P: PhaseItem> {
196175
) -> RenderCommandResult;
197176
}
198177

178+
/// The result of a [`RenderCommand`].
199179
pub enum RenderCommandResult {
200180
Success,
201181
Failure,
202182
}
203183

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-
279184
macro_rules! render_command_tuple_impl {
280185
($(($name: ident, $view: ident, $entity: ident)),*) => {
281186
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
@@ -303,14 +208,15 @@ macro_rules! render_command_tuple_impl {
303208
all_tuples!(render_command_tuple_impl, 0, 15, C, V, E);
304209

305210
/// 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.
211+
/// Therefore the [`RenderCommand::Param`], [`RenderCommand::ViewWorldQuery`] and [`RenderCommand::WorldQuery`] are queried from the ECS and passed to the command.
307212
pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
308213
state: SystemState<C::Param>,
309214
view: QueryState<C::ViewWorldQuery>,
310215
entity: QueryState<C::ItemWorldQuery>,
311216
}
312217

313218
impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
219+
/// Creates a new [`RenderCommandState`] for the [`RenderCommand`].
314220
pub fn new(world: &mut World) -> Self {
315221
Self {
316222
state: SystemState::new(world),
@@ -324,12 +230,13 @@ impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for Rend
324230
where
325231
C::Param: ReadOnlySystemParam,
326232
{
233+
// Todo: add comment
327234
fn prepare(&mut self, world: &'_ World) {
328235
self.view.update_archetypes(world);
329236
self.entity.update_archetypes(world);
330237
}
331238

332-
/// Prepares the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
239+
/// Fetches the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
333240
fn draw<'w>(
334241
&mut self,
335242
world: &'w World,
@@ -340,6 +247,7 @@ where
340247
let param = self.state.get(world);
341248
let view = self.view.get_manual(world, view).unwrap();
342249
let entity = self.entity.get_manual(world, item.entity()).unwrap();
250+
// Todo: handle/log `RenderCommand` failure
343251
C::render(item, view, entity, param, pass);
344252
}
345253
}
@@ -377,3 +285,36 @@ impl AddRenderCommand for App {
377285
self
378286
}
379287
}
288+
289+
// Todo: If this is always needed, combine this with PhaseItem?
290+
pub trait CachedRenderPipelinePhaseItem: PhaseItem {
291+
fn cached_pipeline(&self) -> CachedRenderPipelineId;
292+
}
293+
294+
/// A [`RenderCommand`] that sets the pipeline for the `[PhaseItem]`.
295+
pub struct SetItemPipeline;
296+
297+
impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
298+
type Param = SRes<PipelineCache>;
299+
type ViewWorldQuery = ();
300+
type WorldQuery = ();
301+
302+
#[inline]
303+
fn render<'w>(
304+
item: &P,
305+
_view: (),
306+
_entity: (),
307+
pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
308+
pass: &mut TrackedRenderPass<'w>,
309+
) -> RenderCommandResult {
310+
if let Some(pipeline) = pipeline_cache
311+
.into_inner()
312+
.get_render_pipeline(item.cached_pipeline())
313+
{
314+
pass.set_render_pipeline(pipeline);
315+
RenderCommandResult::Success
316+
} else {
317+
RenderCommandResult::Failure
318+
}
319+
}
320+
}

0 commit comments

Comments
 (0)