Skip to content

Commit

Permalink
extract prepass node
Browse files Browse the repository at this point in the history
  • Loading branch information
IceSentry committed Oct 27, 2022
1 parent 5e9de81 commit 4a3be2d
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 288 deletions.
20 changes: 1 addition & 19 deletions crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
clear_color::{ClearColor, ClearColorConfig},
core_3d::{AlphaMask3d, Camera3d, Opaque3d, Transparent3d},
prepass::PrepassSettings,
};
use bevy_ecs::prelude::*;
use bevy_render::{
Expand All @@ -14,25 +15,6 @@ use bevy_render::{
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;

/// Add a `PrepassSettings` component to a view to perform a depth and/or normal prepass.
/// These textures are useful for reducing overdraw in the main pass, and screen-space effects.
#[derive(Clone, Component)]
pub struct PrepassSettings {
/// If true then depth values will be copied to a separate texture available to the main pass.
pub output_depth: bool,
/// If true then vertex world normals will be copied to a separate texture available to the main pass.
pub output_normals: bool,
}

impl Default for PrepassSettings {
fn default() -> Self {
Self {
output_depth: true,
output_normals: true,
}
}
}

use super::Camera3dDepthLoadOp;

pub struct MainPass3dNode {
Expand Down
21 changes: 20 additions & 1 deletion crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod graph {
pub const VIEW_ENTITY: &str = "view_entity";
}
pub mod node {
pub const PREPASS: &str = "prepass";
pub const MAIN_PASS: &str = "main_pass";
pub const TONEMAPPING: &str = "tonemapping";
pub const UPSCALING: &str = "upscaling";
Expand Down Expand Up @@ -40,7 +41,11 @@ use bevy_render::{
};
use bevy_utils::{FloatOrd, HashMap};

use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
use crate::{
prepass::{node::PrepassNode, PrepassSettings},
tonemapping::TonemappingNode,
upscaling::UpscalingNode,
};

pub struct Core3dPlugin;

Expand All @@ -65,19 +70,30 @@ impl Plugin for Core3dPlugin {
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<AlphaMask3d>)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent3d>);

let prepass_node = PrepassNode::new(&mut render_app.world);
let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
let tonemapping = TonemappingNode::new(&mut render_app.world);
let upscaling = UpscalingNode::new(&mut render_app.world);
let mut graph = render_app.world.resource_mut::<RenderGraph>();

let mut draw_3d_graph = RenderGraph::default();
draw_3d_graph.add_node(graph::node::PREPASS, prepass_node);
draw_3d_graph.add_node(graph::node::MAIN_PASS, pass_node_3d);
draw_3d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
draw_3d_graph.add_node(graph::node::UPSCALING, upscaling);

let input_node_id = draw_3d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
)]);
draw_3d_graph
.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::PREPASS,
PrepassNode::IN_VIEW,
)
.unwrap();
draw_3d_graph
.add_slot_edge(
input_node_id,
Expand All @@ -102,6 +118,9 @@ impl Plugin for Core3dPlugin {
UpscalingNode::IN_VIEW,
)
.unwrap();
draw_3d_graph
.add_node_edge(graph::node::PREPASS, graph::node::MAIN_PASS)
.unwrap();
draw_3d_graph
.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING)
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod clear_color;
pub mod core_2d;
pub mod core_3d;
pub mod fullscreen_vertex_shader;
pub mod prepass;
pub mod tonemapping;
pub mod upscaling;

Expand Down
113 changes: 113 additions & 0 deletions crates/bevy_core_pipeline/src/prepass/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
pub mod node;

use bevy_ecs::prelude::*;
use bevy_render::{
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, EntityPhaseItem, PhaseItem},
render_resource::{CachedRenderPipelineId, Extent3d},
texture::CachedTexture,
};
use bevy_utils::FloatOrd;

/// Add a `PrepassSettings` component to a view to perform a depth and/or normal prepass.
/// These textures are useful for reducing overdraw in the main pass, and screen-space effects.
#[derive(Clone, Component)]
pub struct PrepassSettings {
/// If true then depth values will be copied to a separate texture available to the main pass.
pub output_depth: bool,
/// If true then vertex world normals will be copied to a separate texture available to the main pass.
pub output_normals: bool,
}

impl Default for PrepassSettings {
fn default() -> Self {
Self {
output_depth: true,
output_normals: true,
}
}
}

#[derive(Component)]
pub struct ViewPrepassTextures {
pub depth: Option<CachedTexture>,
pub normals: Option<CachedTexture>,
pub size: Extent3d,
}

pub struct OpaquePrepass {
pub distance: f32,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
}

impl PhaseItem for OpaquePrepass {
type SortKey = FloatOrd;

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
}

#[inline]
fn draw_function(&self) -> DrawFunctionId {
self.draw_function
}

#[inline]
fn sort(items: &mut [Self]) {
radsort::sort_by_key(items, |item| item.distance);
}
}

impl EntityPhaseItem for OpaquePrepass {
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for OpaquePrepass {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
self.pipeline_id
}
}

pub struct AlphaMaskPrepass {
pub distance: f32,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
}

impl PhaseItem for AlphaMaskPrepass {
type SortKey = FloatOrd;

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
}

#[inline]
fn draw_function(&self) -> DrawFunctionId {
self.draw_function
}

#[inline]
fn sort(items: &mut [Self]) {
radsort::sort_by_key(items, |item| item.distance);
}
}

impl EntityPhaseItem for AlphaMaskPrepass {
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for AlphaMaskPrepass {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
self.pipeline_id
}
}
145 changes: 145 additions & 0 deletions crates/bevy_core_pipeline/src/prepass/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryState;
use bevy_render::{
camera::ExtractedCamera,
prelude::Color,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor,
},
renderer::RenderContext,
view::{ExtractedView, ViewDepthTexture},
};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;

use super::{AlphaMaskPrepass, OpaquePrepass, ViewPrepassTextures};

pub struct PrepassNode {
main_view_query: QueryState<
(
&'static ExtractedCamera,
&'static RenderPhase<OpaquePrepass>,
&'static RenderPhase<AlphaMaskPrepass>,
&'static ViewDepthTexture,
&'static ViewPrepassTextures,
),
With<ExtractedView>,
>,
}

impl PrepassNode {
pub const IN_VIEW: &'static str = "view";

pub fn new(world: &mut World) -> Self {
Self {
main_view_query: QueryState::new(world),
}
}
}

impl Node for PrepassNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(PrepassNode::IN_VIEW, SlotType::Entity)]
}

fn update(&mut self, world: &mut World) {
self.main_view_query.update_archetypes(world);
}

fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
if let Ok((
camera,
opaque_prepass_phase,
alpha_mask_prepass_phase,
view_depth_texture,
view_prepass_textures,
)) = self.main_view_query.get_manual(world, view_entity)
{
if opaque_prepass_phase.items.is_empty() && alpha_mask_prepass_phase.items.is_empty() {
return Ok(());
}

let mut color_attachments = vec![];
if let Some(view_normals_texture) = &view_prepass_textures.normals {
color_attachments.push(Some(RenderPassColorAttachment {
view: &view_normals_texture.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::BLACK.into()),
store: true,
},
}));
}

{
// Set up the pass descriptor with the depth attachment and maybe colour attachment
let pass_descriptor = RenderPassDescriptor {
label: Some("prepass"),
color_attachments: &color_attachments,
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &view_depth_texture.view,
depth_ops: Some(Operations {
load: LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
}),
};

let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut tracked_pass = TrackedRenderPass::new(render_pass);
if let Some(viewport) = camera.viewport.as_ref() {
tracked_pass.set_camera_viewport(viewport);
}

{
// Run the depth prepass, sorted front-to-back
#[cfg(feature = "trace")]
let _opaque_prepass_span = info_span!("opaque_prepass").entered();
let draw_functions = world.resource::<DrawFunctions<OpaquePrepass>>();

let mut draw_functions = draw_functions.write();
for item in &opaque_prepass_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
}

{
// Run the depth prepass, sorted front-to-back
#[cfg(feature = "trace")]
let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
let draw_functions = world.resource::<DrawFunctions<AlphaMaskPrepass>>();

let mut draw_functions = draw_functions.write();
for item in &alpha_mask_prepass_phase.items {
let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
draw_function.draw(world, &mut tracked_pass, view_entity, item);
}
}
}

if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
// copy depth buffer to texture
render_context.command_encoder.copy_texture_to_texture(
view_depth_texture.texture.as_image_copy(),
prepass_depth_texture.texture.as_image_copy(),
view_prepass_textures.size,
);
}
}

Ok(())
}
}
5 changes: 3 additions & 2 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::{
GlobalLightMeta, GpuLights, GpuPointLights, LightMeta, NotShadowCaster, NotShadowReceiver,
ShadowPipeline, ViewClusterBindings, ViewLightsUniformOffset, ViewPrepassTextures,
ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
ShadowPipeline, ViewClusterBindings, ViewLightsUniformOffset, ViewShadowBindings,
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
};
use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
use bevy_core_pipeline::prepass::ViewPrepassTextures;
use bevy_ecs::{
prelude::*,
system::{lifetimeless::*, SystemParamItem, SystemState},
Expand Down
Loading

0 comments on commit 4a3be2d

Please sign in to comment.