Skip to content

Commit

Permalink
Keep track of when a texture is first cleared (#10325)
Browse files Browse the repository at this point in the history
# Objective
- Custom render passes, or future passes in the engine (such as
#10164) need a better way to know
and indicate to the core passes whether the view color/depth/prepass
attachments have been cleared or not yet this frame, to know if they
should clear it themselves or load it.

## Solution

- For all render targets (depth textures, shadow textures, prepass
textures, main textures) use an atomic bool to track whether or not each
texture has been cleared this frame. Abstracted away in the new
ColorAttachment and DepthAttachment wrappers.

---

## Changelog
- Changed `ViewTarget::get_color_attachment()`, removed arguments.
- Changed `ViewTarget::get_unsampled_color_attachment()`, removed
arguments.
- Removed `Camera3d::clear_color`.
- Removed `Camera2d::clear_color`.
- Added `Camera::clear_color`.
- Added `ExtractedCamera::clear_color`.
- Added `ColorAttachment` and `DepthAttachment` wrappers.
- Moved `ClearColor` and `ClearColorConfig` from
`bevy::core_pipeline::clear_color` to `bevy::render::camera`.
- Core render passes now track when a texture is first bound as an
attachment in order to decide whether to clear or load it.

## Migration Guide
- Remove arguments to `ViewTarget::get_color_attachment()` and
`ViewTarget::get_unsampled_color_attachment()`.
- Configure clear color on `Camera` instead of on `Camera3d` and
`Camera2d`.
- Moved `ClearColor` and `ClearColorConfig` from
`bevy::core_pipeline::clear_color` to `bevy::render::camera`.
- `ViewDepthTexture` must now be created via the `new()` method

---------

Co-authored-by: vero <[email protected]>
Co-authored-by: Alice Cecile <[email protected]>
  • Loading branch information
3 people authored Dec 31, 2023
1 parent 3d3a065 commit 70b0eac
Show file tree
Hide file tree
Showing 32 changed files with 374 additions and 413 deletions.
7 changes: 1 addition & 6 deletions crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,7 @@ impl ViewNode for BloomNode {
let mut upsampling_final_pass =
render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("bloom_upsampling_final_pass"),
color_attachments: &[Some(view_target.get_unsampled_color_attachment(
Operations {
load: LoadOp::Load,
store: StoreOp::Store,
},
))],
color_attachments: &[Some(view_target.get_unsampled_color_attachment())],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
Expand Down
13 changes: 4 additions & 9 deletions crates/bevy_core_pipeline/src/core_2d/camera_2d.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use crate::{
clear_color::ClearColorConfig,
tonemapping::{DebandDither, Tonemapping},
};
use crate::tonemapping::{DebandDither, Tonemapping};
use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use bevy_render::{
Expand All @@ -15,9 +12,7 @@ use bevy_transform::prelude::{GlobalTransform, Transform};
#[derive(Component, Default, Reflect, Clone, ExtractComponent)]
#[extract_component_filter(With<Camera>)]
#[reflect(Component)]
pub struct Camera2d {
pub clear_color: ClearColorConfig,
}
pub struct Camera2d;

#[derive(Bundle)]
pub struct Camera2dBundle {
Expand Down Expand Up @@ -57,7 +52,7 @@ impl Default for Camera2dBundle {
transform,
global_transform: Default::default(),
camera: Camera::default(),
camera_2d: Camera2d::default(),
camera_2d: Camera2d,
tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled,
}
Expand Down Expand Up @@ -95,7 +90,7 @@ impl Camera2dBundle {
transform,
global_transform: Default::default(),
camera: Camera::default(),
camera_2d: Camera2d::default(),
camera_2d: Camera2d,
tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled,
}
Expand Down
28 changes: 6 additions & 22 deletions crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use crate::{
clear_color::{ClearColor, ClearColorConfig},
core_2d::{camera_2d::Camera2d, Transparent2d},
};
use crate::core_2d::Transparent2d;
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDescriptor, StoreOp},
render_resource::RenderPassDescriptor,
renderer::RenderContext,
view::{ExtractedView, ViewTarget},
};
Expand All @@ -20,7 +17,6 @@ pub struct MainPass2dNode {
&'static ExtractedCamera,
&'static RenderPhase<Transparent2d>,
&'static ViewTarget,
&'static Camera2d,
),
With<ExtractedView>,
>,
Expand All @@ -46,28 +42,19 @@ impl Node for MainPass2dNode {
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();
let Ok((camera, transparent_phase, target, camera_2d)) =
self.query.get_manual(world, view_entity)
let Ok((camera, transparent_phase, target)) = self.query.get_manual(world, view_entity)
else {
// no target
return Ok(());
};

{
#[cfg(feature = "trace")]
let _main_pass_2d = info_span!("main_pass_2d").entered();

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_pass_2d"),
color_attachments: &[Some(target.get_color_attachment(Operations {
load: match camera_2d.clear_color {
ClearColorConfig::Default => {
LoadOp::Clear(world.resource::<ClearColor>().0.into())
}
ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
ClearColorConfig::None => LoadOp::Load,
},
store: StoreOp::Store,
}))],
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
Expand All @@ -88,10 +75,7 @@ impl Node for MainPass2dNode {
let _reset_viewport_pass_2d = info_span!("reset_viewport_pass_2d").entered();
let pass_descriptor = RenderPassDescriptor {
label: Some("reset_viewport_pass_2d"),
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}))],
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
Expand Down
10 changes: 2 additions & 8 deletions crates/bevy_core_pipeline/src/core_3d/camera_3d.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use crate::{
clear_color::ClearColorConfig,
tonemapping::{DebandDither, Tonemapping},
};
use crate::tonemapping::{DebandDither, Tonemapping};
use bevy_ecs::prelude::*;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::{
Expand All @@ -19,8 +16,6 @@ use serde::{Deserialize, Serialize};
#[extract_component_filter(With<Camera>)]
#[reflect(Component)]
pub struct Camera3d {
/// The clear color operation to perform for the main 3d pass.
pub clear_color: ClearColorConfig,
/// The depth clear operation to perform for the main 3d pass.
pub depth_load_op: Camera3dDepthLoadOp,
/// The texture usages for the depth texture created for the main 3d pass.
Expand All @@ -37,7 +32,7 @@ pub struct Camera3d {
/// regardless of this setting.
/// - Setting this to `0` disables the screen-space refraction effect entirely, and falls
/// back to refracting only the environment map light's texture.
/// - If set to more than `0`, any opaque [`clear_color`](Camera3d::clear_color) will obscure the environment
/// - If set to more than `0`, any opaque [`clear_color`](Camera::clear_color) will obscure the environment
/// map light's texture, preventing it from being visible “through” transmissive materials. If you'd like
/// to still have the environment map show up in your refractions, you can set the clear color's alpha to `0.0`.
/// Keep in mind that depending on the platform and your window settings, this may cause the window to become
Expand All @@ -55,7 +50,6 @@ pub struct Camera3d {
impl Default for Camera3d {
fn default() -> Self {
Self {
clear_color: ClearColorConfig::Default,
depth_load_op: Default::default(),
depth_texture_usages: TextureUsages::RENDER_ATTACHMENT.into(),
screen_space_specular_transmission_steps: 1,
Expand Down
61 changes: 5 additions & 56 deletions crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
use crate::{
clear_color::{ClearColor, ClearColorConfig},
core_3d::{Camera3d, Opaque3d},
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
core_3d::Opaque3d,
skybox::{SkyboxBindGroup, SkyboxPipelineId},
};
use bevy_ecs::{prelude::World, query::QueryItem};
use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::RenderPhase,
render_resource::{
LoadOp, Operations, PipelineCache, RenderPassDepthStencilAttachment, RenderPassDescriptor,
StoreOp,
},
render_resource::{PipelineCache, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;

use super::{AlphaMask3d, Camera3dDepthLoadOp};
use super::AlphaMask3d;

/// A [`bevy_render::render_graph::Node`] that runs the [`Opaque3d`] and [`AlphaMask3d`] [`RenderPhase`].
#[derive(Default)]
Expand All @@ -29,13 +24,8 @@ impl ViewNode for MainOpaquePass3dNode {
&'static ExtractedCamera,
&'static RenderPhase<Opaque3d>,
&'static RenderPhase<AlphaMask3d>,
&'static Camera3d,
&'static ViewTarget,
&'static ViewDepthTexture,
Option<&'static DepthPrepass>,
Option<&'static NormalPrepass>,
Option<&'static MotionVectorPrepass>,
Option<&'static DeferredPrepass>,
Option<&'static SkyboxPipelineId>,
Option<&'static SkyboxBindGroup>,
&'static ViewUniformOffset,
Expand All @@ -49,30 +39,14 @@ impl ViewNode for MainOpaquePass3dNode {
camera,
opaque_phase,
alpha_mask_phase,
camera_3d,
target,
depth,
depth_prepass,
normal_prepass,
motion_vector_prepass,
deferred_prepass,
skybox_pipeline,
skybox_bind_group,
view_uniform_offset,
): QueryItem<Self::ViewData>,
world: &World,
) -> Result<(), NodeRunError> {
let load = if deferred_prepass.is_none() {
match camera_3d.clear_color {
ClearColorConfig::Default => LoadOp::Clear(world.resource::<ClearColor>().0.into()),
ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
ClearColorConfig::None => LoadOp::Load,
}
} else {
// If the deferred lighting pass has run, don't clear again in this pass.
LoadOp::Load
};

// Run the opaque pass, sorted front-to-back
// NOTE: Scoped to drop the mutable borrow of render_context
#[cfg(feature = "trace")]
Expand All @@ -81,33 +55,8 @@ impl ViewNode for MainOpaquePass3dNode {
// Setup render pass
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_opaque_pass_3d"),
// NOTE: The opaque pass loads the color
// buffer as well as writing to it.
color_attachments: &[Some(target.get_color_attachment(Operations {
load,
store: StoreOp::Store,
}))],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: The opaque main pass loads the depth buffer and possibly overwrites it
depth_ops: Some(Operations {
load: if depth_prepass.is_some()
|| normal_prepass.is_some()
|| motion_vector_prepass.is_some()
|| deferred_prepass.is_some()
{
// if any prepass runs, it will generate a depth buffer so we should use it,
// even if only the normal_prepass is used.
Camera3dDepthLoadOp::Load
} else {
// NOTE: 0.0 is the far plane due to bevy's use of reverse-z projections.
camera_3d.depth_load_op.clone()
}
.into(),
store: StoreOp::Store,
}),
stencil_ops: None,
}),
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::RenderPhase,
render_resource::{
Extent3d, LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor,
StoreOp,
},
render_resource::{Extent3d, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget},
};
Expand Down Expand Up @@ -45,20 +42,8 @@ impl ViewNode for MainTransmissivePass3dNode {

let render_pass_descriptor = RenderPassDescriptor {
label: Some("main_transmissive_pass_3d"),
// NOTE: The transmissive pass loads the color buffer as well as overwriting it where appropriate.
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}))],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: The transmissive main pass loads the depth buffer and possibly overwrites it
depth_ops: Some(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}),
stencil_ops: None,
}),
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::RenderPhase,
render_resource::{
LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor, StoreOp,
},
render_resource::{RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget},
};
Expand Down Expand Up @@ -41,25 +39,14 @@ impl ViewNode for MainTransparentPass3dNode {

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_transparent_pass_3d"),
// NOTE: The transparent pass loads the color buffer as well as overwriting it where appropriate.
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}))],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: For the transparent pass we load the depth buffer. There should be no
// need to write to it, but store is set to `true` as a workaround for issue #3776,
// https://github.com/bevyengine/bevy/issues/3776
// so that wgpu does not clear the depth buffer.
// As the opaque and alpha mask passes run first, opaque meshes can occlude
// transparent ones.
depth_ops: Some(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}),
stencil_ops: None,
}),
color_attachments: &[Some(target.get_color_attachment())],
// NOTE: For the transparent pass we load the depth buffer. There should be no
// need to write to it, but store is set to `true` as a workaround for issue #3776,
// https://github.com/bevyengine/bevy/issues/3776
// so that wgpu does not clear the depth buffer.
// As the opaque and alpha mask passes run first, opaque meshes can occlude
// transparent ones.
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
});
Expand All @@ -79,10 +66,7 @@ impl ViewNode for MainTransparentPass3dNode {
let _reset_viewport_pass_3d = info_span!("reset_viewport_pass_3d").entered();
let pass_descriptor = RenderPassDescriptor {
label: Some("reset_viewport_pass_3d"),
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}))],
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
Expand Down
Loading

0 comments on commit 70b0eac

Please sign in to comment.