Extract common RenderPhase code into render method (#7013)

# Objective

All `RenderPhases` follow the same render procedure.
The same code is duplicated multiple times across the codebase.

## Solution

I simply extracted this code into a method on the `RenderPhase`. 
This avoids code duplication and makes setting up new `RenderPhases` easier.

---

## Changelog

### Changed

You can now set up the rendering code of a `RenderPhase` directly using the `RenderPhase::render` method, instead of implementing it manually in your render graph node.
This commit is contained in:
Kurt Kühnert 2022-12-27 03:29:59 +00:00
parent 5566d73d9e
commit ca85f6c903
5 changed files with 63 additions and 81 deletions

View File

@ -6,7 +6,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{ use bevy_render::{
camera::ExtractedCamera, camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass}, render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDescriptor}, render_resource::{LoadOp, Operations, RenderPassDescriptor},
renderer::RenderContext, renderer::RenderContext,
view::{ExtractedView, ViewTarget}, view::{ExtractedView, ViewTarget},
@ -77,21 +77,13 @@ impl Node for MainPass2dNode {
depth_stencil_attachment: None, depth_stencil_attachment: None,
}; };
let draw_functions = world.resource::<DrawFunctions<Transparent2d>>(); transparent_phase.render(
world,
let render_pass = render_context render_context,
.command_encoder view_entity,
.begin_render_pass(&pass_descriptor); camera.viewport.as_ref(),
pass_descriptor,
let mut draw_functions = draw_functions.write(); );
let mut tracked_pass = TrackedRenderPass::new(render_pass);
if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport);
}
for item in &transparent_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
// WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't // WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't

View File

@ -6,7 +6,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{ use bevy_render::{
camera::ExtractedCamera, camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass}, render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor}, render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
renderer::RenderContext, renderer::RenderContext,
view::{ExtractedView, ViewDepthTexture, ViewTarget}, view::{ExtractedView, ViewDepthTexture, ViewTarget},
@ -95,20 +95,13 @@ impl Node for MainPass3dNode {
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<Opaque3d>>(); opaque_phase.render(
world,
let render_pass = render_context render_context,
.command_encoder view_entity,
.begin_render_pass(&pass_descriptor); camera.viewport.as_ref(),
let mut draw_functions = draw_functions.write(); pass_descriptor,
let mut tracked_pass = TrackedRenderPass::new(render_pass); );
if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport);
}
for item in &opaque_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
if !alpha_mask_phase.items.is_empty() { if !alpha_mask_phase.items.is_empty() {
@ -134,20 +127,13 @@ impl Node for MainPass3dNode {
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<AlphaMask3d>>(); alpha_mask_phase.render(
world,
let render_pass = render_context render_context,
.command_encoder view_entity,
.begin_render_pass(&pass_descriptor); camera.viewport.as_ref(),
let mut draw_functions = draw_functions.write(); pass_descriptor,
let mut tracked_pass = TrackedRenderPass::new(render_pass); );
if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport);
}
for item in &alpha_mask_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
if !transparent_phase.items.is_empty() { if !transparent_phase.items.is_empty() {
@ -178,20 +164,13 @@ impl Node for MainPass3dNode {
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<Transparent3d>>(); transparent_phase.render(
world,
let render_pass = render_context render_context,
.command_encoder view_entity,
.begin_render_pass(&pass_descriptor); camera.viewport.as_ref(),
let mut draw_functions = draw_functions.write(); pass_descriptor,
let mut tracked_pass = TrackedRenderPass::new(render_pass); );
if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport);
}
for item in &transparent_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
} }
// WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't // WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't

View File

@ -1785,16 +1785,7 @@ impl Node for ShadowPassNode {
}), }),
}; };
let draw_functions = world.resource::<DrawFunctions<Shadow>>(); shadow_phase.render(world, render_context, view_entity, None, pass_descriptor);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for item in &shadow_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_light_entity, item);
}
} }
} }

View File

@ -1,10 +1,15 @@
mod draw; mod draw;
mod draw_state; mod draw_state;
use bevy_ecs::entity::Entity;
pub use draw::*; pub use draw::*;
pub use draw_state::*; pub use draw_state::*;
use wgpu::RenderPassDescriptor;
use crate::camera::Viewport;
use crate::renderer::RenderContext;
use bevy_ecs::prelude::{Component, Query}; use bevy_ecs::prelude::{Component, Query};
use bevy_ecs::world::World;
/// A resource to collect and sort draw requests for specific [`PhaseItems`](PhaseItem). /// A resource to collect and sort draw requests for specific [`PhaseItems`](PhaseItem).
#[derive(Component)] #[derive(Component)]
@ -29,6 +34,32 @@ impl<I: PhaseItem> RenderPhase<I> {
pub fn sort(&mut self) { pub fn sort(&mut self) {
I::sort(&mut self.items); I::sort(&mut self.items);
} }
pub fn render(
&self,
world: &World,
render_context: &mut RenderContext,
view: Entity,
viewport: Option<&Viewport>,
pass_descriptor: RenderPassDescriptor,
) {
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut render_pass = TrackedRenderPass::new(render_pass);
if let Some(viewport) = viewport {
render_pass.set_camera_viewport(viewport);
}
let draw_functions = world.resource::<DrawFunctions<I>>();
let mut draw_functions = draw_functions.write();
for item in &self.items {
let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
draw_function.draw(world, &mut render_pass, view, item);
}
}
} }
impl<I: BatchedPhaseItem> RenderPhase<I> { impl<I: BatchedPhaseItem> RenderPhase<I> {

View File

@ -85,18 +85,7 @@ impl Node for UiPassNode {
depth_stencil_attachment: None, depth_stencil_attachment: None,
}; };
let draw_functions = world.resource::<DrawFunctions<TransparentUi>>(); transparent_phase.render(world, render_context, view_entity, None, pass_descriptor);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for item in &transparent_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
Ok(()) Ok(())
} }
} }