Skip to content

Commit 9286dc2

Browse files
committed
improved documentation
1 parent e245a26 commit 9286dc2

File tree

3 files changed

+221
-165
lines changed

3 files changed

+221
-165
lines changed
Lines changed: 69 additions & 128 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,18 +15,20 @@ 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 {
25-
/// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
27+
// Todo: add comment
2628
#[allow(unused_variables)]
2729
fn prepare(&mut self, world: &'_ World) {}
2830

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

39-
/// An item which will be drawn to the screen. A phase item should be queued up for rendering
40-
/// during the [`RenderStage::Queue`](crate::RenderStage::Queue) stage.
41-
/// Afterwards it will be sorted and rendered automatically in the
42-
/// [`RenderStage::PhaseSort`](crate::RenderStage::PhaseSort) stage and
43-
/// [`RenderStage::Render`](crate::RenderStage::Render) stage, respectively.
44-
pub trait PhaseItem: Sized + Send + Sync + 'static {
45-
/// The type used for ordering the items. The smallest values are drawn first.
46-
type SortKey: Ord;
47-
fn entity(&self) -> Entity;
48-
49-
/// Determines the order in which the items are drawn during the corresponding [`RenderPhase`](super::RenderPhase).
50-
fn sort_key(&self) -> Self::SortKey;
51-
/// Specifies the [`Draw`] function used to render the item.
52-
fn draw_function(&self) -> DrawFunctionId;
53-
54-
/// Sorts a slice of phase items into render order. Generally if the same type
55-
/// implements [`BatchedPhaseItem`], this should use a stable sort like [`slice::sort_by_key`].
56-
/// In almost all other cases, this should not be altered from the default,
57-
/// which uses a unstable sort, as this provides the best balance of CPU and GPU
58-
/// performance.
59-
///
60-
/// Implementers can optionally not sort the list at all. This is generally advisable if and
61-
/// only if the renderer supports a depth prepass, which is by default not supported by
62-
/// the rest of Bevy's first party rendering crates. Even then, this may have a negative
63-
/// impact on GPU-side performance due to overdraw.
64-
///
65-
/// It's advised to always profile for performance changes when changing this implementation.
66-
#[inline]
67-
fn sort(items: &mut [Self]) {
68-
items.sort_unstable_by_key(|item| item.sort_key());
69-
}
70-
}
71-
7241
// TODO: make this generic?
7342
/// An identifier for a [`Draw`] function stored in [`DrawFunctions`].
7443
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
7544
pub struct DrawFunctionId(usize);
7645

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

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

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

96-
/// Adds the [`Draw`] function and associates it to the type `T`
67+
/// Adds the [`Draw`] function and maps it to the type `T`
9768
pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
9869
self.draw_functions.push(Box::new(draw_function));
9970
let id = DrawFunctionId(self.draw_functions.len() - 1);
@@ -116,7 +87,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
11687
/// Fallible wrapper for [`Self::get_id()`]
11788
///
11889
/// ## Panics
119-
/// If the id doesn't exist it will panic
90+
/// If the id doesn't exist, this function will panic.
12091
pub fn id<T: 'static>(&self) -> DrawFunctionId {
12192
self.get_id::<T>().unwrap_or_else(|| {
12293
panic!(
@@ -129,6 +100,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
129100
}
130101

131102
/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
103+
///
132104
/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
133105
#[derive(Resource)]
134106
pub struct DrawFunctions<P: PhaseItem> {
@@ -158,15 +130,20 @@ impl<P: PhaseItem> DrawFunctions<P> {
158130
}
159131
}
160132

161-
/// [`RenderCommand`] is a trait that runs an ECS query and produces one or more
162-
/// [`TrackedRenderPass`] calls. Types implementing this trait can be composed (as tuples).
133+
/// [`RenderCommand`]s are modular standardized pieces of render logic that can be composed into [`Draw`] functions.
163134
///
164-
/// They can be registered as a [`Draw`] function via the
165-
/// [`AddRenderCommand::add_render_command`] method.
135+
/// To turn a stateless render command into a usable draw function it has to be wrapped by a [`RenderCommandState`].
136+
/// This is done automatically when registering a render command as a [`Draw`] function via the [`AddRenderCommand::add_render_command`] method.
137+
///
138+
/// Compared to the draw function the required ECS data is fetched automatically (by the [`RenderCommandState`]) from the render world.
139+
/// Therefore the three types [`Param`](RenderCommand::Param), [`ViewWorldQuery`](RenderCommand::ViewWorldQuery) and [`WorldQuery`](RenderCommand::WorldQuery) are used.
140+
/// They specify which information is required to execute the render command.
141+
///
142+
/// Multiple render commands can be combined together by wrapping them in a tuple.
166143
///
167144
/// # Example
168145
/// The `DrawPbr` draw function is created from the following render command
169-
/// tuple. Const generics are used to set specific bind group locations:
146+
/// tuple. Const generics are used to set specific bind group locations:
170147
///
171148
/// ```ignore
172149
/// pub type DrawPbr = (
@@ -181,10 +158,12 @@ pub trait RenderCommand<P: PhaseItem> {
181158
/// Specifies all ECS data required by [`RenderCommand::render`].
182159
/// All parameters have to be read only.
183160
type Param: SystemParam + 'static;
161+
// Todo: add comment
184162
type ViewWorldQuery: ReadOnlyWorldQuery;
163+
// Todo: add comment
185164
type WorldQuery: ReadOnlyWorldQuery;
186165

187-
/// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
166+
/// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups, issuing draw calls, etc.) via the [`TrackedRenderPass`].
188167
fn render<'w>(
189168
item: &P,
190169
view: ROQueryItem<'w, Self::ViewWorldQuery>,
@@ -194,86 +173,12 @@ pub trait RenderCommand<P: PhaseItem> {
194173
) -> RenderCommandResult;
195174
}
196175

176+
/// The result of a [`RenderCommand`].
197177
pub enum RenderCommandResult {
198178
Success,
199179
Failure,
200180
}
201181

202-
pub trait CachedRenderPipelinePhaseItem: PhaseItem {
203-
fn cached_pipeline(&self) -> CachedRenderPipelineId;
204-
}
205-
206-
/// A [`PhaseItem`] that can be batched dynamically.
207-
///
208-
/// Batching is an optimization that regroups multiple items in the same vertex buffer
209-
/// to render them in a single draw call.
210-
///
211-
/// If this is implemented on a type, the implementation of [`PhaseItem::sort`] should
212-
/// be changed to implement a stable sort, or incorrect/suboptimal batching may result.
213-
pub trait BatchedPhaseItem: PhaseItem {
214-
/// Range in the vertex buffer of this item
215-
fn batch_range(&self) -> &Option<Range<u32>>;
216-
217-
/// Range in the vertex buffer of this item
218-
fn batch_range_mut(&mut self) -> &mut Option<Range<u32>>;
219-
220-
/// Batches another item within this item if they are compatible.
221-
/// Items can be batched together if they have the same entity, and consecutive ranges.
222-
/// If batching is successful, the `other` item should be discarded from the render pass.
223-
#[inline]
224-
fn add_to_batch(&mut self, other: &Self) -> BatchResult {
225-
let self_entity = self.entity();
226-
if let (Some(self_batch_range), Some(other_batch_range)) = (
227-
self.batch_range_mut().as_mut(),
228-
other.batch_range().as_ref(),
229-
) {
230-
// If the items are compatible, join their range into `self`
231-
if self_entity == other.entity() {
232-
if self_batch_range.end == other_batch_range.start {
233-
self_batch_range.end = other_batch_range.end;
234-
return BatchResult::Success;
235-
} else if self_batch_range.start == other_batch_range.end {
236-
self_batch_range.start = other_batch_range.start;
237-
return BatchResult::Success;
238-
}
239-
}
240-
}
241-
BatchResult::IncompatibleItems
242-
}
243-
}
244-
245-
pub enum BatchResult {
246-
/// The `other` item was batched into `self`
247-
Success,
248-
/// `self` and `other` cannot be batched together
249-
IncompatibleItems,
250-
}
251-
252-
pub struct SetItemPipeline;
253-
impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
254-
type Param = SRes<PipelineCache>;
255-
type ViewWorldQuery = ();
256-
type WorldQuery = ();
257-
#[inline]
258-
fn render<'w>(
259-
item: &P,
260-
_view: (),
261-
_entity: (),
262-
pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
263-
pass: &mut TrackedRenderPass<'w>,
264-
) -> RenderCommandResult {
265-
if let Some(pipeline) = pipeline_cache
266-
.into_inner()
267-
.get_render_pipeline(item.cached_pipeline())
268-
{
269-
pass.set_render_pipeline(pipeline);
270-
RenderCommandResult::Success
271-
} else {
272-
RenderCommandResult::Failure
273-
}
274-
}
275-
}
276-
277182
macro_rules! render_command_tuple_impl {
278183
($(($name: ident, $view: ident, $entity: ident)),*) => {
279184
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
@@ -301,14 +206,15 @@ macro_rules! render_command_tuple_impl {
301206
all_tuples!(render_command_tuple_impl, 0, 15, C, V, E);
302207

303208
/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
304-
/// Therefore the [`RenderCommand::Param`] is queried from the ECS and passed to the command.
209+
/// Therefore the [`RenderCommand::Param`], [`RenderCommand::ViewWorldQuery`] and [`RenderCommand::WorldQuery`] are queried from the ECS and passed to the command.
305210
pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
306211
state: SystemState<C::Param>,
307212
view: QueryState<C::ViewWorldQuery>,
308213
entity: QueryState<C::WorldQuery>,
309214
}
310215

311216
impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
217+
/// Creates a new [`RenderCommandState`] for the [`RenderCommand`].
312218
pub fn new(world: &mut World) -> Self {
313219
Self {
314220
state: SystemState::new(world),
@@ -322,12 +228,13 @@ impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for Rend
322228
where
323229
C::Param: ReadOnlySystemParam,
324230
{
231+
// Todo: add comment
325232
fn prepare(&mut self, world: &'_ World) {
326233
self.view.update_archetypes(world);
327234
self.entity.update_archetypes(world);
328235
}
329236

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

0 commit comments

Comments
 (0)