Skip to content

Commit 3ed80d3

Browse files
authored
Solari: Double buffer prepass textures (#22102)
Fixes Solari crashing with `Partial copy of 0..1600 on X dimension with size 3200 is not supported for the Source texture format Depth32Float with 1 samples` after #21746. Also slightly better performance by avoiding the copy. Code is extremely cludgy though, we're just piling hacks upon hacks for the prepass texture stuff.
1 parent e7b64b6 commit 3ed80d3

File tree

8 files changed

+171
-83
lines changed

8 files changed

+171
-83
lines changed

crates/bevy_core_pipeline/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bevy_app = { path = "../bevy_app", version = "0.18.0-dev" }
2020
bevy_asset = { path = "../bevy_asset", version = "0.18.0-dev" }
2121
bevy_color = { path = "../bevy_color", version = "0.18.0-dev" }
2222
bevy_derive = { path = "../bevy_derive", version = "0.18.0-dev" }
23+
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.18.0-dev" }
2324
bevy_ecs = { path = "../bevy_ecs", version = "0.18.0-dev" }
2425
bevy_image = { path = "../bevy_image", version = "0.18.0-dev" }
2526
bevy_camera = { path = "../bevy_camera", version = "0.18.0-dev" }

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 123 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = true;
7373
use core::ops::Range;
7474

7575
use bevy_camera::{Camera, Camera3d, Camera3dDepthLoadOp};
76+
use bevy_diagnostic::FrameCount;
7677
use bevy_render::{
7778
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
7879
camera::CameraRenderGraph,
7980
experimental::occlusion_culling::OcclusionCulling,
8081
mesh::allocator::SlabId,
8182
render_phase::PhaseItemBatchSetKey,
83+
texture::CachedTexture,
8284
view::{prepare_view_targets, NoIndirectDrawing, RetainedViewEntity},
8385
};
8486
pub use main_opaque_pass_3d_node::*;
@@ -124,9 +126,10 @@ use crate::{
124126
},
125127
prepass::{
126128
node::{EarlyPrepassNode, LatePrepassNode},
127-
AlphaMask3dPrepass, DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass,
128-
Opaque3dPrepass, OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey,
129-
ViewPrepassTextures, MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
129+
AlphaMask3dPrepass, DeferredPrepass, DeferredPrepassDoubleBuffer, DepthPrepass,
130+
DepthPrepassDoubleBuffer, MotionVectorPrepass, NormalPrepass, Opaque3dPrepass,
131+
OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey, ViewPrepassTextures,
132+
MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
130133
},
131134
skybox::SkyboxPlugin,
132135
tonemapping::{DebandDither, Tonemapping, TonemappingNode},
@@ -676,6 +679,8 @@ pub fn extract_camera_prepass_phase(
676679
Has<NormalPrepass>,
677680
Has<MotionVectorPrepass>,
678681
Has<DeferredPrepass>,
682+
Has<DepthPrepassDoubleBuffer>,
683+
Has<DeferredPrepassDoubleBuffer>,
679684
),
680685
With<Camera3d>,
681686
>,
@@ -694,6 +699,8 @@ pub fn extract_camera_prepass_phase(
694699
normal_prepass,
695700
motion_vector_prepass,
696701
deferred_prepass,
702+
depth_prepass_double_buffer,
703+
deferred_prepass_double_buffer,
697704
) in cameras_3d.iter()
698705
{
699706
if !camera.is_active {
@@ -761,6 +768,18 @@ pub fn extract_camera_prepass_phase(
761768
} else {
762769
camera_commands.remove::<DeferredPrepass>();
763770
}
771+
772+
if depth_prepass_double_buffer {
773+
camera_commands.insert(DepthPrepassDoubleBuffer);
774+
} else {
775+
camera_commands.remove::<DepthPrepassDoubleBuffer>();
776+
}
777+
778+
if deferred_prepass_double_buffer {
779+
camera_commands.insert(DeferredPrepassDoubleBuffer);
780+
} else {
781+
camera_commands.remove::<DeferredPrepassDoubleBuffer>();
782+
}
764783
}
765784

766785
opaque_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity));
@@ -973,6 +992,7 @@ pub fn prepare_prepass_textures(
973992
mut commands: Commands,
974993
mut texture_cache: ResMut<TextureCache>,
975994
render_device: Res<RenderDevice>,
995+
frame_count: Res<FrameCount>,
976996
opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
977997
alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
978998
opaque_3d_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
@@ -986,11 +1006,15 @@ pub fn prepare_prepass_textures(
9861006
Has<NormalPrepass>,
9871007
Has<MotionVectorPrepass>,
9881008
Has<DeferredPrepass>,
1009+
Has<DepthPrepassDoubleBuffer>,
1010+
Has<DeferredPrepassDoubleBuffer>,
9891011
)>,
9901012
) {
991-
let mut depth_textures = <HashMap<_, _>>::default();
1013+
let mut depth_textures1 = <HashMap<_, _>>::default();
1014+
let mut depth_textures2 = <HashMap<_, _>>::default();
9921015
let mut normal_textures = <HashMap<_, _>>::default();
993-
let mut deferred_textures = <HashMap<_, _>>::default();
1016+
let mut deferred_textures1: HashMap<_, _> = <HashMap<_, _>>::default();
1017+
let mut deferred_textures2: HashMap<_, _> = <HashMap<_, _>>::default();
9941018
let mut deferred_lighting_id_textures = <HashMap<_, _>>::default();
9951019
let mut motion_vectors_textures = <HashMap<_, _>>::default();
9961020
for (
@@ -1002,6 +1026,8 @@ pub fn prepare_prepass_textures(
10021026
normal_prepass,
10031027
motion_vector_prepass,
10041028
deferred_prepass,
1029+
depth_prepass_double_buffer,
1030+
deferred_prepass_double_buffer,
10051031
) in &views_3d
10061032
{
10071033
if !opaque_3d_prepass_phases.contains_key(&view.retained_view_entity)
@@ -1019,21 +1045,41 @@ pub fn prepare_prepass_textures(
10191045

10201046
let size = physical_target_size.to_extents();
10211047

1022-
let cached_depth_texture = depth_prepass.then(|| {
1023-
depth_textures
1048+
let cached_depth_texture1 = depth_prepass.then(|| {
1049+
depth_textures1
1050+
.entry(camera.target.clone())
1051+
.or_insert_with(|| {
1052+
let descriptor = TextureDescriptor {
1053+
label: Some("prepass_depth_texture_1"),
1054+
size,
1055+
mip_level_count: 1,
1056+
sample_count: msaa.samples(),
1057+
dimension: TextureDimension::D2,
1058+
format: CORE_3D_DEPTH_FORMAT,
1059+
usage: TextureUsages::COPY_DST
1060+
| TextureUsages::RENDER_ATTACHMENT
1061+
| TextureUsages::TEXTURE_BINDING,
1062+
view_formats: &[],
1063+
};
1064+
texture_cache.get(&render_device, descriptor)
1065+
})
1066+
.clone()
1067+
});
1068+
1069+
let cached_depth_texture2 = depth_prepass_double_buffer.then(|| {
1070+
depth_textures2
10241071
.entry(camera.target.clone())
10251072
.or_insert_with(|| {
10261073
let descriptor = TextureDescriptor {
1027-
label: Some("prepass_depth_texture"),
1074+
label: Some("prepass_depth_texture_2"),
10281075
size,
10291076
mip_level_count: 1,
10301077
sample_count: msaa.samples(),
10311078
dimension: TextureDimension::D2,
10321079
format: CORE_3D_DEPTH_FORMAT,
10331080
usage: TextureUsages::COPY_DST
10341081
| TextureUsages::RENDER_ATTACHMENT
1035-
| TextureUsages::TEXTURE_BINDING
1036-
| TextureUsages::COPY_SRC, // TODO: Remove COPY_SRC, double buffer instead (for bevy_solari)
1082+
| TextureUsages::TEXTURE_BINDING,
10371083
view_formats: &[],
10381084
};
10391085
texture_cache.get(&render_device, descriptor)
@@ -1085,22 +1131,43 @@ pub fn prepare_prepass_textures(
10851131
.clone()
10861132
});
10871133

1088-
let cached_deferred_texture = deferred_prepass.then(|| {
1089-
deferred_textures
1134+
let cached_deferred_texture1 = deferred_prepass.then(|| {
1135+
deferred_textures1
1136+
.entry(camera.target.clone())
1137+
.or_insert_with(|| {
1138+
texture_cache.get(
1139+
&render_device,
1140+
TextureDescriptor {
1141+
label: Some("prepass_deferred_texture_1"),
1142+
size,
1143+
mip_level_count: 1,
1144+
sample_count: 1,
1145+
dimension: TextureDimension::D2,
1146+
format: DEFERRED_PREPASS_FORMAT,
1147+
usage: TextureUsages::RENDER_ATTACHMENT
1148+
| TextureUsages::TEXTURE_BINDING,
1149+
view_formats: &[],
1150+
},
1151+
)
1152+
})
1153+
.clone()
1154+
});
1155+
1156+
let cached_deferred_texture2 = deferred_prepass_double_buffer.then(|| {
1157+
deferred_textures2
10901158
.entry(camera.target.clone())
10911159
.or_insert_with(|| {
10921160
texture_cache.get(
10931161
&render_device,
10941162
TextureDescriptor {
1095-
label: Some("prepass_deferred_texture"),
1163+
label: Some("prepass_deferred_texture_2"),
10961164
size,
10971165
mip_level_count: 1,
10981166
sample_count: 1,
10991167
dimension: TextureDimension::D2,
11001168
format: DEFERRED_PREPASS_FORMAT,
11011169
usage: TextureUsages::RENDER_ATTACHMENT
1102-
| TextureUsages::TEXTURE_BINDING
1103-
| TextureUsages::COPY_SRC, // TODO: Remove COPY_SRC, double buffer instead (for bevy_solari)
1170+
| TextureUsages::TEXTURE_BINDING,
11041171
view_formats: &[],
11051172
},
11061173
)
@@ -1131,20 +1198,54 @@ pub fn prepare_prepass_textures(
11311198
});
11321199

11331200
commands.entity(entity).insert(ViewPrepassTextures {
1134-
depth: cached_depth_texture
1135-
.map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
1201+
depth: package_double_buffered_texture(
1202+
cached_depth_texture1,
1203+
cached_depth_texture2,
1204+
frame_count.0,
1205+
),
11361206
normal: cached_normals_texture
1137-
.map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
1207+
.map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK))),
11381208
// Red and Green channels are X and Y components of the motion vectors
11391209
// Blue channel doesn't matter, but set to 0.0 for possible faster clear
11401210
// https://gpuopen.com/performance/#clears
11411211
motion_vectors: cached_motion_vectors_texture
1142-
.map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
1143-
deferred: cached_deferred_texture
1144-
.map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
1212+
.map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK))),
1213+
deferred: package_double_buffered_texture(
1214+
cached_deferred_texture1,
1215+
cached_deferred_texture2,
1216+
frame_count.0,
1217+
),
11451218
deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture
1146-
.map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
1219+
.map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK))),
11471220
size,
11481221
});
11491222
}
11501223
}
1224+
1225+
fn package_double_buffered_texture(
1226+
texture1: Option<CachedTexture>,
1227+
texture2: Option<CachedTexture>,
1228+
frame_count: u32,
1229+
) -> Option<ColorAttachment> {
1230+
match (texture1, texture2) {
1231+
(Some(t1), None) => Some(ColorAttachment::new(
1232+
t1,
1233+
None,
1234+
None,
1235+
Some(LinearRgba::BLACK),
1236+
)),
1237+
(Some(t1), Some(t2)) if frame_count.is_multiple_of(2) => Some(ColorAttachment::new(
1238+
t1,
1239+
None,
1240+
Some(t2),
1241+
Some(LinearRgba::BLACK),
1242+
)),
1243+
(Some(t1), Some(t2)) => Some(ColorAttachment::new(
1244+
t2,
1245+
None,
1246+
Some(t1),
1247+
Some(LinearRgba::BLACK),
1248+
)),
1249+
_ => None,
1250+
}
1251+
}

crates/bevy_core_pipeline/src/prepass/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ pub struct MotionVectorPrepass;
7777
#[reflect(Component, Default)]
7878
pub struct DeferredPrepass;
7979

80+
/// Allows querying the previous frame's [`DepthPrepass`].
81+
#[derive(Component, Default, Reflect, Clone)]
82+
#[reflect(Component, Default, Clone)]
83+
#[require(DepthPrepass)]
84+
pub struct DepthPrepassDoubleBuffer;
85+
86+
/// Allows querying the previous frame's [`DeferredPrepass`].
87+
#[derive(Component, Default, Reflect, Clone)]
88+
#[reflect(Component, Default, Clone)]
89+
#[require(DeferredPrepass)]
90+
pub struct DeferredPrepassDoubleBuffer;
91+
8092
/// View matrices from the previous frame.
8193
///
8294
/// Useful for temporal rendering techniques that need access to last frame's camera data.
@@ -128,6 +140,12 @@ impl ViewPrepassTextures {
128140
self.depth.as_ref().map(|t| &t.texture.default_view)
129141
}
130142

143+
pub fn previous_depth_view(&self) -> Option<&TextureView> {
144+
self.depth
145+
.as_ref()
146+
.and_then(|t| t.previous_frame_texture.as_ref().map(|t| &t.default_view))
147+
}
148+
131149
pub fn normal_view(&self) -> Option<&TextureView> {
132150
self.normal.as_ref().map(|t| &t.texture.default_view)
133151
}
@@ -141,6 +159,12 @@ impl ViewPrepassTextures {
141159
pub fn deferred_view(&self) -> Option<&TextureView> {
142160
self.deferred.as_ref().map(|t| &t.texture.default_view)
143161
}
162+
163+
pub fn previous_deferred_view(&self) -> Option<&TextureView> {
164+
self.deferred
165+
.as_ref()
166+
.and_then(|t| t.previous_frame_texture.as_ref().map(|t| &t.default_view))
167+
}
144168
}
145169

146170
/// Opaque phase of the 3D prepass.

crates/bevy_render/src/texture/texture_attachment.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use wgpu::{
1212
pub struct ColorAttachment {
1313
pub texture: CachedTexture,
1414
pub resolve_target: Option<CachedTexture>,
15+
pub previous_frame_texture: Option<CachedTexture>,
1516
clear_color: Option<LinearRgba>,
1617
is_first_call: Arc<AtomicBool>,
1718
}
@@ -20,11 +21,13 @@ impl ColorAttachment {
2021
pub fn new(
2122
texture: CachedTexture,
2223
resolve_target: Option<CachedTexture>,
24+
previous_frame_texture: Option<CachedTexture>,
2325
clear_color: Option<LinearRgba>,
2426
) -> Self {
2527
Self {
2628
texture,
2729
resolve_target,
30+
previous_frame_texture,
2831
clear_color,
2932
is_first_call: Arc::new(AtomicBool::new(true)),
3033
}

crates/bevy_render/src/view/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,8 +1141,8 @@ pub fn prepare_view_targets(
11411141
let converted_clear_color = clear_color.map(Into::into);
11421142

11431143
let main_textures = MainTargetTextures {
1144-
a: ColorAttachment::new(a.clone(), sampled.clone(), converted_clear_color),
1145-
b: ColorAttachment::new(b.clone(), sampled.clone(), converted_clear_color),
1144+
a: ColorAttachment::new(a.clone(), sampled.clone(), None, converted_clear_color),
1145+
b: ColorAttachment::new(b.clone(), sampled.clone(), None, converted_clear_color),
11461146
main_texture: main_texture.clone(),
11471147
};
11481148

crates/bevy_solari/src/realtime/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ use bevy_app::{App, Plugin};
77
use bevy_asset::embedded_asset;
88
use bevy_core_pipeline::{
99
core_3d::graph::{Core3d, Node3d},
10-
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass},
10+
prepass::{
11+
DeferredPrepass, DeferredPrepassDoubleBuffer, DepthPrepass, DepthPrepassDoubleBuffer,
12+
MotionVectorPrepass,
13+
},
1114
};
1215
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
1316
use bevy_pbr::DefaultOpaqueRendererMethod;
@@ -87,7 +90,14 @@ impl Plugin for SolariLightingPlugin {
8790
/// `Msaa::Off`.
8891
#[derive(Component, Reflect, Clone)]
8992
#[reflect(Component, Default, Clone)]
90-
#[require(Hdr, DeferredPrepass, DepthPrepass, MotionVectorPrepass)]
93+
#[require(
94+
Hdr,
95+
DeferredPrepass,
96+
DepthPrepass,
97+
MotionVectorPrepass,
98+
DeferredPrepassDoubleBuffer,
99+
DepthPrepassDoubleBuffer
100+
)]
91101
pub struct SolariLighting {
92102
/// Set to true to delete the saved temporal history (past frames).
93103
///

0 commit comments

Comments
 (0)