Keep track of when a texture is first cleared (#10325)

# Objective
- Custom render passes, or future passes in the engine (such as
https://github.com/bevyengine/bevy/pull/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@atlasdostal.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
JMS55 2023-12-30 16:37:37 -08:00 committed by GitHub
parent 3d3a065820
commit 70b0eacc3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 374 additions and 413 deletions

View file

@ -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,

View file

@ -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::{
@ -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 {
@ -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,
}
@ -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,
}

View file

@ -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},
};
@ -20,7 +17,6 @@ pub struct MainPass2dNode {
&'static ExtractedCamera,
&'static RenderPhase<Transparent2d>,
&'static ViewTarget,
&'static Camera2d,
),
With<ExtractedView>,
>,
@ -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,
@ -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,

View file

@ -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::{
@ -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.
@ -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
@ -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,

View file

@ -1,7 +1,5 @@
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};
@ -9,17 +7,14 @@ 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)]
@ -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,
@ -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")]
@ -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,
});

View file

@ -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},
};
@ -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,
};

View file

@ -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},
};
@ -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,
});
@ -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,

View file

@ -42,6 +42,7 @@ use bevy_app::{App, Plugin, PostUpdate};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::{Camera, ExtractedCamera},
color::Color,
extract_component::ExtractComponentPlugin,
prelude::Msaa,
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
@ -54,7 +55,7 @@ use bevy_render::{
TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
},
renderer::RenderDevice,
texture::{BevyDefault, TextureCache},
texture::{BevyDefault, ColorAttachment, TextureCache},
view::{ExtractedView, ViewDepthTexture, ViewTarget},
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
@ -526,7 +527,7 @@ pub fn prepare_core_3d_depth_textures(
}
let mut textures = HashMap::default();
for (entity, camera, _, _) in &views_3d {
for (entity, camera, _, camera_3d) in &views_3d {
let Some(physical_target_size) = camera.physical_target_size else {
continue;
};
@ -560,10 +561,13 @@ pub fn prepare_core_3d_depth_textures(
})
.clone();
commands.entity(entity).insert(ViewDepthTexture {
texture: cached_texture.texture,
view: cached_texture.default_view,
});
commands.entity(entity).insert(ViewDepthTexture::new(
cached_texture,
match camera_3d.depth_load_op {
Camera3dDepthLoadOp::Clear(v) => Some(v),
Camera3dDepthLoadOp::Load => None,
},
));
}
}
@ -824,10 +828,14 @@ pub fn prepare_prepass_textures(
});
commands.entity(entity).insert(ViewPrepassTextures {
depth: cached_depth_texture,
normal: cached_normals_texture,
motion_vectors: cached_motion_vectors_texture,
deferred: cached_deferred_texture,
depth: cached_depth_texture.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
normal: cached_normals_texture.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
// Red and Green channels are X and Y components of the motion vectors
// Blue channel doesn't matter, but set to 0.0 for possible faster clear
// https://gpuopen.com/performance/#clears
motion_vectors: cached_motion_vectors_texture
.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
deferred: cached_deferred_texture.map(|t| ColorAttachment::new(t, None, Color::BLACK)),
deferred_lighting_pass_id: deferred_lighting_pass_id_texture,
size,
});

View file

@ -8,18 +8,14 @@ use bevy_render::{
prelude::Color,
render_graph::{NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor,
},
render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor},
renderer::RenderContext,
view::ViewDepthTexture,
};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use crate::core_3d::{Camera3d, Camera3dDepthLoadOp};
use crate::prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass, ViewPrepassTextures};
use crate::prepass::ViewPrepassTextures;
use super::{AlphaMask3dDeferred, Opaque3dDeferred};
@ -36,10 +32,6 @@ impl ViewNode for DeferredGBufferPrepassNode {
&'static RenderPhase<AlphaMask3dDeferred>,
&'static ViewDepthTexture,
&'static ViewPrepassTextures,
&'static Camera3d,
Option<&'static DepthPrepass>,
Option<&'static NormalPrepass>,
Option<&'static MotionVectorPrepass>,
);
fn run(
@ -52,10 +44,6 @@ impl ViewNode for DeferredGBufferPrepassNode {
alpha_mask_deferred_phase,
view_depth_texture,
view_prepass_textures,
camera_3d,
depth_prepass,
normal_prepass,
motion_vector_prepass,
): QueryItem<Self::ViewData>,
world: &World,
) -> Result<(), NodeRunError> {
@ -66,37 +54,14 @@ impl ViewNode for DeferredGBufferPrepassNode {
view_prepass_textures
.normal
.as_ref()
.map(|view_normals_texture| RenderPassColorAttachment {
view: &view_normals_texture.default_view,
resolve_target: None,
ops: Operations {
load: if normal_prepass.is_some() {
// Load if the normal_prepass has already run.
// The prepass will have already cleared this for the current frame.
LoadOp::Load
} else {
LoadOp::Clear(Color::BLACK.into())
},
store: StoreOp::Store,
},
}),
.map(|normals_texture| normals_texture.get_attachment()),
);
color_attachments.push(
view_prepass_textures
.motion_vectors
.as_ref()
.map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
);
color_attachments.push(view_prepass_textures.motion_vectors.as_ref().map(
|view_motion_vectors_texture| RenderPassColorAttachment {
view: &view_motion_vectors_texture.default_view,
resolve_target: None,
ops: Operations {
load: if motion_vector_prepass.is_some() {
// Load if the motion_vector_prepass has already run.
// The prepass will have already cleared this for the current frame.
LoadOp::Load
} else {
LoadOp::Clear(Color::BLACK.into())
},
store: StoreOp::Store,
},
},
));
// If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
// Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
@ -106,7 +71,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
if let Some(deferred_texture) = &view_prepass_textures.deferred {
render_context.command_encoder().clear_texture(
&deferred_texture.texture,
&deferred_texture.texture.texture,
&bevy_render::render_resource::ImageSubresourceRange::default(),
);
}
@ -115,16 +80,20 @@ impl ViewNode for DeferredGBufferPrepassNode {
view_prepass_textures
.deferred
.as_ref()
.map(|deferred_texture| RenderPassColorAttachment {
view: &deferred_texture.default_view,
resolve_target: None,
ops: Operations {
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
load: LoadOp::Load,
#[cfg(not(all(feature = "webgl", target_arch = "wasm32")))]
load: LoadOp::Clear(Default::default()),
store: StoreOp::Store,
},
.map(|deferred_texture| {
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
{
RenderPassColorAttachment {
view: &deferred_texture.texture.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Load,
store: StoreOp::Store,
},
}
}
#[cfg(not(all(feature = "webgl", target_arch = "wasm32")))]
deferred_texture.get_attachment()
}),
);
@ -136,7 +105,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
view: &deferred_lighting_pass_id.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Default::default()),
load: LoadOp::Clear(Color::BLACK.into()),
store: StoreOp::Store,
},
}),
@ -152,24 +121,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("deferred"),
color_attachments: &color_attachments,
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &view_depth_texture.view,
depth_ops: Some(Operations {
load: if depth_prepass.is_some()
|| normal_prepass.is_some()
|| motion_vector_prepass.is_some()
{
// If any prepass runs, it will generate a depth buffer so we should use it.
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,
}),
depth_stencil_attachment: Some(view_depth_texture.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
});
@ -198,7 +150,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
// 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(),
prepass_depth_texture.texture.texture.as_image_copy(),
view_prepass_textures.size,
);
}

View file

@ -1,6 +1,5 @@
pub mod blit;
pub mod bloom;
pub mod clear_color;
pub mod contrast_adaptive_sharpening;
pub mod core_2d;
pub mod core_3d;
@ -29,7 +28,6 @@ pub mod experimental {
pub mod prelude {
#[doc(hidden)]
pub use crate::{
clear_color::ClearColor,
core_2d::{Camera2d, Camera2dBundle},
core_3d::{Camera3d, Camera3dBundle},
};
@ -38,7 +36,6 @@ pub mod prelude {
use crate::{
blit::BlitPlugin,
bloom::BloomPlugin,
clear_color::{ClearColor, ClearColorConfig},
contrast_adaptive_sharpening::CASPlugin,
core_2d::Core2dPlugin,
core_3d::Core3dPlugin,
@ -46,13 +43,13 @@ use crate::{
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
fxaa::FxaaPlugin,
msaa_writeback::MsaaWritebackPlugin,
prepass::{DepthPrepass, NormalPrepass},
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
tonemapping::TonemappingPlugin,
upscaling::UpscalingPlugin,
};
use bevy_app::{App, Plugin};
use bevy_asset::load_internal_asset;
use bevy_render::{extract_resource::ExtractResourcePlugin, prelude::Shader};
use bevy_render::prelude::Shader;
#[derive(Default)]
pub struct CorePipelinePlugin;
@ -66,13 +63,11 @@ impl Plugin for CorePipelinePlugin {
Shader::from_wgsl
);
app.register_type::<ClearColor>()
.register_type::<ClearColorConfig>()
.register_type::<DepthPrepass>()
app.register_type::<DepthPrepass>()
.register_type::<NormalPrepass>()
.init_resource::<ClearColor>()
.register_type::<MotionVectorPrepass>()
.register_type::<DeferredPrepass>()
.add_plugins((
ExtractResourcePlugin::<ClearColor>::default(),
Core2dPlugin,
Core3dPlugin,
CopyDeferredLightingIdPlugin,

View file

@ -7,6 +7,7 @@ use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
color::Color,
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
render_resource::BindGroupEntries,
renderer::RenderContext,
@ -60,12 +61,17 @@ impl Node for MsaaWritebackNode {
fn update(&mut self, world: &mut World) {
self.cameras.update_archetypes(world);
}
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
if *world.resource::<Msaa>() == Msaa::Off {
return Ok(());
}
let view_entity = graph.view_entity();
if let Ok((target, blit_pipeline_id)) = self.cameras.get_manual(world, view_entity) {
let blit_pipeline = world.resource::<BlitPipeline>();
@ -81,13 +87,18 @@ impl Node for MsaaWritebackNode {
let pass_descriptor = RenderPassDescriptor {
label: Some("msaa_writeback"),
// The target's "resolve target" is the "destination" in post_process
// The target's "resolve target" is the "destination" in post_process.
// We will indirectly write the results to the "destination" using
// the MSAA resolve step.
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Clear(Default::default()),
store: StoreOp::Store,
}))],
color_attachments: &[Some(RenderPassColorAttachment {
// If MSAA is enabled, then the sampled texture will always exist
view: target.sampled_main_texture_view().unwrap(),
resolve_target: Some(post_process.destination),
ops: Operations {
load: LoadOp::Clear(Color::BLACK.into()),
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
@ -107,6 +118,7 @@ impl Node for MsaaWritebackNode {
render_pass.set_bind_group(0, &bind_group, &[]);
render_pass.draw(0..3, 0..1);
}
Ok(())
}
}

View file

@ -33,8 +33,8 @@ use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use bevy_render::{
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat},
texture::CachedTexture,
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
texture::{CachedTexture, ColorAttachment},
};
use bevy_utils::{nonmax::NonMaxU32, FloatOrd};
@ -66,16 +66,16 @@ pub struct DeferredPrepass;
pub struct ViewPrepassTextures {
/// The depth texture generated by the prepass.
/// Exists only if [`DepthPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
pub depth: Option<CachedTexture>,
pub depth: Option<ColorAttachment>,
/// The normals texture generated by the prepass.
/// Exists only if [`NormalPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
pub normal: Option<CachedTexture>,
pub normal: Option<ColorAttachment>,
/// The motion vectors texture generated by the prepass.
/// Exists only if [`MotionVectorPrepass`] is added to the `ViewTarget`
pub motion_vectors: Option<CachedTexture>,
pub motion_vectors: Option<ColorAttachment>,
/// The deferred gbuffer generated by the deferred pass.
/// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
pub deferred: Option<CachedTexture>,
pub deferred: Option<ColorAttachment>,
/// A texture that specifies the deferred lighting pass id for a material.
/// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
pub deferred_lighting_pass_id: Option<CachedTexture>,
@ -83,6 +83,26 @@ pub struct ViewPrepassTextures {
pub size: Extent3d,
}
impl ViewPrepassTextures {
pub fn depth_view(&self) -> Option<&TextureView> {
self.depth.as_ref().map(|t| &t.texture.default_view)
}
pub fn normal_view(&self) -> Option<&TextureView> {
self.normal.as_ref().map(|t| &t.texture.default_view)
}
pub fn motion_vectors_view(&self) -> Option<&TextureView> {
self.motion_vectors
.as_ref()
.map(|t| &t.texture.default_view)
}
pub fn deferred_view(&self) -> Option<&TextureView> {
self.deferred.as_ref().map(|t| &t.texture.default_view)
}
}
/// Opaque phase of the 3D prepass.
///
/// Sorted front-to-back by the z-distance in front of the camera.

View file

@ -4,13 +4,9 @@ use bevy_render::render_graph::ViewNode;
use bevy_render::render_resource::StoreOp;
use bevy_render::{
camera::ExtractedCamera,
prelude::Color,
render_graph::{NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor,
},
render_resource::RenderPassDescriptor,
renderer::RenderContext,
view::ViewDepthTexture,
};
@ -55,28 +51,11 @@ impl ViewNode for PrepassNode {
view_prepass_textures
.normal
.as_ref()
.map(|view_normals_texture| RenderPassColorAttachment {
view: &view_normals_texture.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::BLACK.into()),
store: StoreOp::Store,
},
}),
.map(|normals_texture| normals_texture.get_attachment()),
view_prepass_textures
.motion_vectors
.as_ref()
.map(|view_motion_vectors_texture| RenderPassColorAttachment {
view: &view_motion_vectors_texture.default_view,
resolve_target: None,
ops: Operations {
// Red and Green channels are X and Y components of the motion vectors
// Blue channel doesn't matter, but set to 0.0 for possible faster clear
// https://gpuopen.com/performance/#clears
load: LoadOp::Clear(Color::BLACK.into()),
store: StoreOp::Store,
},
}),
.map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
// Use None in place of Deferred attachments
None,
None,
@ -92,14 +71,7 @@ impl ViewNode for PrepassNode {
let mut render_pass = render_context.begin_tracked_render_pass(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: StoreOp::Store,
}),
stencil_ops: None,
}),
depth_stencil_attachment: Some(view_depth_texture.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
});
@ -128,7 +100,7 @@ impl ViewNode for PrepassNode {
// 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(),
prepass_depth_texture.texture.texture.as_image_copy(),
view_prepass_textures.size,
);
}

View file

@ -206,8 +206,8 @@ impl ViewNode for TemporalAntiAliasNode {
&BindGroupEntries::sequential((
view_target.source,
&taa_history_textures.read.default_view,
&prepass_motion_vectors_texture.default_view,
&prepass_depth_texture.default_view,
&prepass_motion_vectors_texture.texture.default_view,
&prepass_depth_texture.texture.default_view,
&pipelines.nearest_sampler,
&pipelines.linear_sampler,
)),

View file

@ -2,12 +2,10 @@ use crate::{MeshPipeline, MeshViewBindGroup, ScreenSpaceAmbientOcclusionSettings
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, Handle};
use bevy_core_pipeline::{
clear_color::ClearColorConfig,
core_3d,
deferred::{
copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
},
prelude::{Camera3d, ClearColor},
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
tonemapping::{DebandDither, Tonemapping},
};
@ -156,7 +154,6 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
&'static MeshViewBindGroup,
&'static ViewTarget,
&'static DeferredLightingIdDepthTexture,
&'static Camera3d,
&'static DeferredLightingPipeline,
);
@ -171,7 +168,6 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
mesh_view_bind_group,
target,
deferred_lighting_id_depth_texture,
camera_3d,
deferred_lighting_pipeline,
): QueryItem<Self::ViewData>,
world: &World,
@ -201,16 +197,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("deferred_lighting_pass"),
color_attachments: &[Some(target.get_color_attachment(Operations {
load: 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,
},
store: StoreOp::Store,
}))],
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &deferred_lighting_id_depth_texture.texture.default_view,
depth_ops: Some(Operations {

View file

@ -64,19 +64,12 @@ pub fn get_bindings(prepass_textures: Option<&ViewPrepassTextures>) -> [Option<T
};
let depth_view = prepass_textures
.and_then(|x| x.depth.as_ref())
.map(|texture| texture.texture.create_view(&depth_desc));
.map(|texture| texture.texture.texture.create_view(&depth_desc));
let normal_view = prepass_textures
.and_then(|x| x.normal.as_ref())
.map(|texture| texture.default_view.clone());
let motion_vectors_view = prepass_textures
.and_then(|x| x.motion_vectors.as_ref())
.map(|texture| texture.default_view.clone());
let deferred_view = prepass_textures
.and_then(|x| x.deferred.as_ref())
.map(|texture| texture.default_view.clone());
[depth_view, normal_view, motion_vectors_view, deferred_view]
[
depth_view,
prepass_textures.and_then(|pt| pt.normal_view().cloned()),
prepass_textures.and_then(|pt| pt.motion_vectors_view().cloned()),
prepass_textures.and_then(|pt| pt.deferred_view().cloned()),
]
}

View file

@ -521,7 +521,7 @@ fn face_index_to_name(face_index: usize) -> &'static str {
#[derive(Component)]
pub struct ShadowView {
pub depth_texture_view: TextureView,
pub depth_attachment: DepthAttachment,
pub pass_name: String,
}
@ -1008,7 +1008,7 @@ pub fn prepare_lights(
let view_light_entity = commands
.spawn((
ShadowView {
depth_texture_view,
depth_attachment: DepthAttachment::new(depth_texture_view, Some(0.0)),
pass_name: format!(
"shadow pass point light {} {}",
light_index,
@ -1070,7 +1070,7 @@ pub fn prepare_lights(
let view_light_entity = commands
.spawn((
ShadowView {
depth_texture_view,
depth_attachment: DepthAttachment::new(depth_texture_view, Some(0.0)),
pass_name: format!("shadow pass spot light {light_index}"),
},
ExtractedView {
@ -1135,7 +1135,7 @@ pub fn prepare_lights(
let view_light_entity = commands
.spawn((
ShadowView {
depth_texture_view,
depth_attachment: DepthAttachment::new(depth_texture_view, Some(0.0)),
pass_name: format!(
"shadow pass directional light {light_index} cascade {cascade_index}"),
},
@ -1767,14 +1767,9 @@ impl Node for ShadowPassNode {
render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some(&view_light.pass_name),
color_attachments: &[],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &view_light.depth_texture_view,
depth_ops: Some(Operations {
load: LoadOp::Clear(0.0),
store: StoreOp::Store,
}),
stencil_ops: None,
}),
depth_stencil_attachment: Some(
view_light.depth_attachment.get_attachment(StoreOp::Store),
),
timestamp_writes: None,
occlusion_query_set: None,
});

View file

@ -681,7 +681,7 @@ fn prepare_ssao_bind_groups(
"ssao_preprocess_depth_bind_group",
&pipelines.preprocess_depth_bind_group_layout,
&BindGroupEntries::sequential((
&prepass_textures.depth.as_ref().unwrap().default_view,
prepass_textures.depth_view().unwrap(),
&create_depth_view(0),
&create_depth_view(1),
&create_depth_view(2),
@ -695,7 +695,7 @@ fn prepare_ssao_bind_groups(
&pipelines.gtao_bind_group_layout,
&BindGroupEntries::sequential((
&ssao_textures.preprocessed_depth_texture.default_view,
&prepass_textures.normal.as_ref().unwrap().default_view,
prepass_textures.normal_view().unwrap(),
&pipelines.hilbert_index_lut,
&ssao_textures.ssao_noisy_texture.default_view,
&ssao_textures.depth_differences_texture.default_view,

View file

@ -32,7 +32,7 @@ use bevy_window::{
use std::{borrow::Cow, ops::Range};
use wgpu::{BlendState, LoadOp, TextureFormat};
use super::Projection;
use super::{ClearColorConfig, Projection};
/// Render viewport configuration for the [`Camera`] component.
///
@ -118,6 +118,8 @@ pub struct Camera {
/// "write their results on top" of previous camera results, and include them as a part of their render results. This is enabled by default to ensure
/// cameras with MSAA enabled layer their results in the same way as cameras without MSAA enabled by default.
pub msaa_writeback: bool,
/// The clear color operation to perform on the render target.
pub clear_color: ClearColorConfig,
}
impl Default for Camera {
@ -131,6 +133,7 @@ impl Default for Camera {
output_mode: Default::default(),
hdr: false,
msaa_writeback: true,
clear_color: Default::default(),
}
}
}
@ -632,6 +635,7 @@ pub struct ExtractedCamera {
pub order: isize,
pub output_mode: CameraOutputMode,
pub msaa_writeback: bool,
pub clear_color: ClearColorConfig,
pub sorted_camera_index_for_target: usize,
}
@ -701,6 +705,7 @@ pub fn extract_cameras(
order: camera.order,
output_mode: camera.output_mode,
msaa_writeback: camera.msaa_writeback,
clear_color: camera.clear_color.clone(),
// this will be set in sort_cameras
sorted_camera_index_for_target: 0,
},

View file

@ -1,7 +1,7 @@
use crate::{color::Color, extract_resource::ExtractResource};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::{color::Color, extract_resource::ExtractResource};
use serde::{Deserialize, Serialize};
/// For a camera, specifies the color used to clear the viewport before rendering.

View file

@ -1,11 +1,13 @@
#[allow(clippy::module_inception)]
mod camera;
mod camera_driver_node;
mod clear_color;
mod manual_texture_view;
mod projection;
pub use camera::*;
pub use camera_driver_node::*;
pub use clear_color::*;
pub use manual_texture_view::*;
pub use projection::*;
@ -27,12 +29,16 @@ impl Plugin for CameraPlugin {
.register_type::<ScalingMode>()
.register_type::<CameraRenderGraph>()
.register_type::<RenderTarget>()
.register_type::<ClearColor>()
.register_type::<ClearColorConfig>()
.init_resource::<ManualTextureViews>()
.init_resource::<ClearColor>()
.add_plugins((
CameraProjectionPlugin::<Projection>::default(),
CameraProjectionPlugin::<OrthographicProjection>::default(),
CameraProjectionPlugin::<PerspectiveProjection>::default(),
ExtractResourcePlugin::<ManualTextureViews>::default(),
ExtractResourcePlugin::<ClearColor>::default(),
));
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {

View file

@ -27,7 +27,10 @@ pub mod view;
pub mod prelude {
#[doc(hidden)]
pub use crate::{
camera::{Camera, OrthographicProjection, PerspectiveProjection, Projection},
camera::{
Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection,
Projection,
},
color::Color,
mesh::{morph::MorphWeights, shape, Mesh},
render_resource::Shader,

View file

@ -14,6 +14,7 @@ mod image;
mod image_loader;
#[cfg(feature = "ktx2")]
mod ktx2;
mod texture_attachment;
mod texture_cache;
pub(crate) mod image_texture_conversion;
@ -32,6 +33,7 @@ pub use hdr_texture_loader::*;
pub use compressed_image_saver::*;
pub use fallback_image::*;
pub use image_loader::*;
pub use texture_attachment::*;
pub use texture_cache::*;
use crate::{

View file

@ -0,0 +1,123 @@
use super::CachedTexture;
use crate::{prelude::Color, render_resource::TextureView};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use wgpu::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment, StoreOp,
};
/// A wrapper for a [`CachedTexture`] that is used as a [`RenderPassColorAttachment`].
#[derive(Clone)]
pub struct ColorAttachment {
pub texture: CachedTexture,
pub resolve_target: Option<CachedTexture>,
clear_color: Color,
is_first_call: Arc<AtomicBool>,
}
impl ColorAttachment {
pub fn new(
texture: CachedTexture,
resolve_target: Option<CachedTexture>,
clear_color: Color,
) -> Self {
Self {
texture,
resolve_target,
clear_color,
is_first_call: Arc::new(AtomicBool::new(true)),
}
}
/// Get this texture view as an attachment. The attachment will be cleared with a value of
/// `clear_color` if this is the first time calling this function, otherwise it will be loaded.
///
/// The returned attachment will always have writing enabled (`store: StoreOp::Load`).
pub fn get_attachment(&self) -> RenderPassColorAttachment {
if let Some(resolve_target) = self.resolve_target.as_ref() {
let first_call = self.is_first_call.fetch_and(false, Ordering::SeqCst);
RenderPassColorAttachment {
view: &resolve_target.default_view,
resolve_target: Some(&self.texture.default_view),
ops: Operations {
load: if first_call {
LoadOp::Clear(self.clear_color.into())
} else {
LoadOp::Load
},
store: StoreOp::Store,
},
}
} else {
self.get_unsampled_attachment()
}
}
/// Get this texture view as an attachment, without the resolve target. The attachment will be cleared with
/// a value of `clear_color` if this is the first time calling this function, otherwise it will be loaded.
///
/// The returned attachment will always have writing enabled (`store: StoreOp::Load`).
pub fn get_unsampled_attachment(&self) -> RenderPassColorAttachment {
let first_call = self.is_first_call.fetch_and(false, Ordering::SeqCst);
RenderPassColorAttachment {
view: &self.texture.default_view,
resolve_target: None,
ops: Operations {
load: if first_call {
LoadOp::Clear(self.clear_color.into())
} else {
LoadOp::Load
},
store: StoreOp::Store,
},
}
}
pub(crate) fn mark_as_cleared(&self) {
self.is_first_call.store(false, Ordering::SeqCst);
}
}
/// A wrapper for a [`TextureView`] that is used as a depth-only [`RenderPassDepthStencilAttachment`].
pub struct DepthAttachment {
pub view: TextureView,
clear_value: Option<f32>,
is_first_call: Arc<AtomicBool>,
}
impl DepthAttachment {
pub fn new(view: TextureView, clear_value: Option<f32>) -> Self {
Self {
view,
clear_value,
is_first_call: Arc::new(AtomicBool::new(clear_value.is_some())),
}
}
/// Get this texture view as an attachment. The attachment will be cleared with a value of
/// `clear_value` if this is the first time calling this function with `store` == [`StoreOp::Store`],
/// and a clear value was provided, otherwise it will be loaded.
pub fn get_attachment(&self, store: StoreOp) -> RenderPassDepthStencilAttachment {
let first_call = self
.is_first_call
.fetch_and(store != StoreOp::Store, Ordering::SeqCst);
RenderPassDepthStencilAttachment {
view: &self.view,
depth_ops: Some(Operations {
load: if first_call {
// If first_call is true, then a clear value will always have been provided in the constructor
LoadOp::Clear(self.clear_value.unwrap())
} else {
LoadOp::Load
},
store,
}),
stencil_ops: None,
}
}
}

View file

@ -6,7 +6,9 @@ pub use visibility::*;
pub use window::*;
use crate::{
camera::{ExtractedCamera, ManualTextureViews, MipBias, TemporalJitter},
camera::{
ClearColor, ClearColorConfig, ExtractedCamera, ManualTextureViews, MipBias, TemporalJitter,
},
extract_resource::{ExtractResource, ExtractResourcePlugin},
prelude::{Image, Shader},
primitives::Frustum,
@ -14,7 +16,7 @@ use crate::{
render_phase::ViewRangefinder3d,
render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView},
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, CachedTexture, TextureCache},
texture::{BevyDefault, CachedTexture, ColorAttachment, DepthAttachment, TextureCache},
Render, RenderApp, RenderSet,
};
use bevy_app::{App, Plugin};
@ -28,8 +30,8 @@ use std::sync::{
Arc,
};
use wgpu::{
Color, Extent3d, Operations, RenderPassColorAttachment, TextureDescriptor, TextureDimension,
TextureFormat, TextureUsages,
Extent3d, RenderPassColorAttachment, RenderPassDepthStencilAttachment, StoreOp,
TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
};
pub const VIEW_TYPE_HANDLE: Handle<Shader> = Handle::weak_from_u128(15421373904451797197);
@ -204,40 +206,30 @@ pub struct PostProcessWrite<'a> {
impl ViewTarget {
pub const TEXTURE_FORMAT_HDR: TextureFormat = TextureFormat::Rgba16Float;
/// Retrieve this target's color attachment. This will use [`Self::sampled_main_texture_view`] and resolve to [`Self::main_texture`] if
/// the target has sampling enabled. Otherwise it will use [`Self::main_texture`] directly.
pub fn get_color_attachment(&self, ops: Operations<Color>) -> RenderPassColorAttachment {
match &self.main_textures.sampled {
Some(CachedTexture {
default_view: sampled_texture_view,
..
}) => RenderPassColorAttachment {
view: sampled_texture_view,
resolve_target: Some(self.main_texture_view()),
ops,
},
None => self.get_unsampled_color_attachment(ops),
/// Retrieve this target's main texture's color attachment.
pub fn get_color_attachment(&self) -> RenderPassColorAttachment {
if self.main_texture.load(Ordering::SeqCst) == 0 {
self.main_textures.a.get_attachment()
} else {
self.main_textures.b.get_attachment()
}
}
/// Retrieve an "unsampled" color attachment using [`Self::main_texture`].
pub fn get_unsampled_color_attachment(
&self,
ops: Operations<Color>,
) -> RenderPassColorAttachment {
RenderPassColorAttachment {
view: self.main_texture_view(),
resolve_target: None,
ops,
/// Retrieve this target's "unsampled" main texture's color attachment.
pub fn get_unsampled_color_attachment(&self) -> RenderPassColorAttachment {
if self.main_texture.load(Ordering::SeqCst) == 0 {
self.main_textures.a.get_unsampled_attachment()
} else {
self.main_textures.b.get_unsampled_attachment()
}
}
/// The "main" unsampled texture.
pub fn main_texture(&self) -> &Texture {
if self.main_texture.load(Ordering::SeqCst) == 0 {
&self.main_textures.a.texture
&self.main_textures.a.texture.texture
} else {
&self.main_textures.b.texture
&self.main_textures.b.texture.texture
}
}
@ -249,18 +241,18 @@ impl ViewTarget {
/// ahead of time.
pub fn main_texture_other(&self) -> &Texture {
if self.main_texture.load(Ordering::SeqCst) == 0 {
&self.main_textures.b.texture
&self.main_textures.b.texture.texture
} else {
&self.main_textures.a.texture
&self.main_textures.a.texture.texture
}
}
/// The "main" unsampled texture.
pub fn main_texture_view(&self) -> &TextureView {
if self.main_texture.load(Ordering::SeqCst) == 0 {
&self.main_textures.a.default_view
&self.main_textures.a.texture.default_view
} else {
&self.main_textures.b.default_view
&self.main_textures.b.texture.default_view
}
}
@ -272,16 +264,17 @@ impl ViewTarget {
/// ahead of time.
pub fn main_texture_other_view(&self) -> &TextureView {
if self.main_texture.load(Ordering::SeqCst) == 0 {
&self.main_textures.b.default_view
&self.main_textures.b.texture.default_view
} else {
&self.main_textures.a.default_view
&self.main_textures.a.texture.default_view
}
}
/// The "main" sampled texture.
pub fn sampled_main_texture(&self) -> Option<&Texture> {
self.main_textures
.sampled
.a
.resolve_target
.as_ref()
.map(|sampled| &sampled.texture)
}
@ -289,7 +282,8 @@ impl ViewTarget {
/// The "main" sampled texture view.
pub fn sampled_main_texture_view(&self) -> Option<&TextureView> {
self.main_textures
.sampled
.a
.resolve_target
.as_ref()
.map(|sampled| &sampled.default_view)
}
@ -328,14 +322,16 @@ impl ViewTarget {
let old_is_a_main_texture = self.main_texture.fetch_xor(1, Ordering::SeqCst);
// if the old main texture is a, then the post processing must write from a to b
if old_is_a_main_texture == 0 {
self.main_textures.b.mark_as_cleared();
PostProcessWrite {
source: &self.main_textures.a.default_view,
destination: &self.main_textures.b.default_view,
source: &self.main_textures.a.texture.default_view,
destination: &self.main_textures.b.texture.default_view,
}
} else {
self.main_textures.a.mark_as_cleared();
PostProcessWrite {
source: &self.main_textures.b.default_view,
destination: &self.main_textures.a.default_view,
source: &self.main_textures.b.texture.default_view,
destination: &self.main_textures.a.texture.default_view,
}
}
}
@ -344,7 +340,24 @@ impl ViewTarget {
#[derive(Component)]
pub struct ViewDepthTexture {
pub texture: Texture,
pub view: TextureView,
attachment: DepthAttachment,
}
impl ViewDepthTexture {
pub fn new(texture: CachedTexture, clear_value: Option<f32>) -> Self {
Self {
texture: texture.texture,
attachment: DepthAttachment::new(texture.default_view, clear_value),
}
}
pub fn get_attachment(&self, store: StoreOp) -> RenderPassDepthStencilAttachment {
self.attachment.get_attachment(store)
}
pub fn view(&self) -> &TextureView {
&self.attachment.view
}
}
pub fn prepare_view_uniforms(
@ -420,9 +433,8 @@ pub fn prepare_view_uniforms(
#[derive(Clone)]
struct MainTargetTextures {
a: CachedTexture,
b: CachedTexture,
sampled: Option<CachedTexture>,
a: ColorAttachment,
b: ColorAttachment,
/// 0 represents `main_textures.a`, 1 represents `main_textures.b`
/// This is shared across view targets with the same render target
main_texture: Arc<AtomicUsize>,
@ -434,6 +446,7 @@ fn prepare_view_targets(
windows: Res<ExtractedWindows>,
images: Res<RenderAssets<Image>>,
msaa: Res<Msaa>,
clear_color_global: Res<ClearColor>,
render_device: Res<RenderDevice>,
mut texture_cache: ResMut<TextureCache>,
cameras: Query<(Entity, &ExtractedCamera, &ExtractedView)>,
@ -458,7 +471,12 @@ fn prepare_view_targets(
TextureFormat::bevy_default()
};
let main_textures = textures
let clear_color = match camera.clear_color {
ClearColorConfig::Custom(color) => color,
_ => clear_color_global.0,
};
let (a, b, sampled) = textures
.entry((camera.target.clone(), view.hdr))
.or_insert_with(|| {
let descriptor = TextureDescriptor {
@ -509,18 +527,19 @@ fn prepare_view_targets(
} else {
None
};
MainTargetTextures {
a,
b,
sampled,
main_texture: Arc::new(AtomicUsize::new(0)),
}
(a, b, sampled)
});
let main_textures = MainTargetTextures {
a: ColorAttachment::new(a.clone(), sampled.clone(), clear_color),
b: ColorAttachment::new(b.clone(), sampled.clone(), clear_color),
main_texture: Arc::new(AtomicUsize::new(0)),
};
commands.entity(entity).insert(ViewTarget {
main_textures: main_textures.clone(),
main_texture_format,
main_texture: main_textures.main_texture.clone(),
main_textures,
main_texture_format,
out_texture: out_texture_view.clone(),
out_texture_format: out_texture_format.add_srgb_suffix(),
});

View file

@ -9,7 +9,7 @@ use bevy_ecs::{
use bevy_render::{
render_graph::*,
render_phase::*,
render_resource::{CachedRenderPipelineId, LoadOp, Operations, RenderPassDescriptor, StoreOp},
render_resource::{CachedRenderPipelineId, RenderPassDescriptor},
renderer::*,
view::*,
};
@ -74,10 +74,7 @@ impl Node for UiPassNode {
};
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("ui_pass"),
color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
load: LoadOp::Load,
store: StoreOp::Store,
}))],
color_attachments: &[Some(target.get_unsampled_color_attachment())],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,

View file

@ -100,14 +100,11 @@ fn setup(
commands.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: Color::WHITE.into(),
..default()
},
camera: Camera {
// render before the "main pass" camera
order: -1,
target: RenderTarget::Image(image_handle.clone()),
clear_color: Color::WHITE.into(),
..default()
},
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0))

View file

@ -3,8 +3,7 @@
use std::f32::consts::PI;
use bevy::{
core_pipeline::clear_color::ClearColorConfig, pbr::CascadeShadowConfigBuilder, prelude::*,
render::camera::Viewport, window::WindowResized,
pbr::CascadeShadowConfigBuilder, prelude::*, render::camera::Viewport, window::WindowResized,
};
fn main() {
@ -67,10 +66,7 @@ fn setup(
camera: Camera {
// Renders the right camera after the left camera, which has a default priority of 0
order: 1,
..default()
},
camera_3d: Camera3d {
// don't clear on the second camera because the first camera already cleared the window
// Don't clear on the second camera because the first camera already cleared the window
clear_color: ClearColorConfig::None,
..default()
},

View file

@ -1,6 +1,6 @@
//! Renders two 3d passes to the same window from different perspectives.
use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*};
use bevy::prelude::*;
fn main() {
App::new()
@ -47,13 +47,10 @@ fn setup(
// camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(10.0, 10., -5.0).looking_at(Vec3::ZERO, Vec3::Y),
camera_3d: Camera3d {
clear_color: ClearColorConfig::None,
..default()
},
camera: Camera {
// renders after / on top of the main camera
order: 1,
clear_color: ClearColorConfig::None,
..default()
},
..default()

View file

@ -305,7 +305,7 @@ fn setup(
Camera3dBundle {
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 5.0))
.looking_at(Vec3::default(), Vec3::Y),
camera_3d: Camera3d {
camera: Camera {
clear_color: Color::WHITE.into(),
..default()
},

View file

@ -1,6 +1,6 @@
//! A test to confirm that `bevy` allows minimising the window
//! This is run in CI to ensure that this doesn't regress again.
use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*};
use bevy::prelude::*;
fn main() {
// TODO: Combine this with `resizing` once multiple_windows is simpler than
@ -72,11 +72,9 @@ fn setup_2d(mut commands: Commands) {
camera: Camera {
// render the 2d camera after the 3d camera
order: 1,
..default()
},
camera_2d: Camera2d {
// do not use a clear color
clear_color: ClearColorConfig::None,
..default()
},
..default()
});

View file

@ -1,7 +1,7 @@
//! A test to confirm that `bevy` allows setting the window to arbitrary small sizes
//! This is run in CI to ensure that this doesn't regress again.
use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*, window::WindowResolution};
use bevy::{prelude::*, window::WindowResolution};
// The smallest size reached is 1x1, as X11 doesn't support windows with a 0 dimension
// TODO: Add a check for platforms other than X11 for 0xk and kx0, despite those currently unsupported on CI.
@ -155,11 +155,9 @@ fn setup_2d(mut commands: Commands) {
camera: Camera {
// render the 2d camera after the 3d camera
order: 1,
..default()
},
camera_2d: Camera2d {
// do not use a clear color
clear_color: ClearColorConfig::None,
..default()
},
..default()
});