Skip to content

Commit 9d0dbc3

Browse files
committed
make use of depth prepass to filter out fragments
1 parent 7ebd383 commit 9d0dbc3

File tree

7 files changed

+76
-32
lines changed

7 files changed

+76
-32
lines changed

crates/bevy_core_pipeline/src/oit/mod.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use bevy_app::prelude::*;
44
use bevy_camera::Camera3d;
55
use bevy_ecs::{component::*, prelude::*};
6-
use bevy_math::{UVec2, UVec3};
6+
use bevy_math::UVec2;
77
use bevy_platform::time::Instant;
88
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
99
use bevy_render::{
@@ -40,7 +40,7 @@ pub struct OrderIndependentTransparencySettings {
4040
/// If the scene has more fragments than this, they will be merged approximately.
4141
/// More sorted fragments is more accurate but will be slower.
4242
pub sorted_fragment_max_count: u32,
43-
/// The average fragments per pixel stored in the buffer. This should be bigger enough to make oit succeed.
43+
/// The average fragments per pixel stored in the buffer. This should be bigger enough otherwise the fragments will be discarded.
4444
/// Higher values increase memory usage.
4545
pub fragments_per_pixel_average: f32,
4646
/// Threshold for which fragments will be added to the blending layers.
@@ -239,13 +239,15 @@ pub fn prepare_oit_buffers(
239239
if buffers.headers.capacity() < headers_size {
240240
let start = Instant::now();
241241
buffers.headers.clear();
242+
buffers.headers.reserve(headers_size, &render_device);
242243
for _ in 0..headers_size {
243244
buffers.headers.push(u32::MAX);
244245
}
245246
buffers.headers.write_buffer(&render_device, &render_queue);
246247
trace!(
247-
"OIT headers buffer updated in {:.01}ms with total size {} MiB",
248+
"OIT headers buffer updated in {:.01}ms with capacity {}, total size {} MiB",
248249
start.elapsed().as_millis(),
250+
buffers.headers.capacity(),
249251
buffers.headers.capacity() * size_of::<u32>() / 1024 / 1024,
250252
);
251253
}
@@ -255,14 +257,16 @@ pub fn prepare_oit_buffers(
255257
if buffers.nodes.capacity() < nodes_size {
256258
let start = Instant::now();
257259
buffers.nodes.clear();
260+
buffers.nodes.reserve(nodes_size, &render_device);
258261
for _ in 0..nodes_size {
259262
buffers.nodes.push(OitFragmentNode::default());
260263
}
261264
buffers.nodes.write_buffer(&render_device, &render_queue);
262265
trace!(
263-
"OIT nodes buffer updated in {:.01}ms with total size {} MiB",
266+
"OIT nodes buffer updated in {:.01}ms with capacity {}, total size {} MiB",
264267
start.elapsed().as_millis(),
265-
buffers.nodes.capacity() * size_of::<UVec3>() / 1024 / 1024,
268+
buffers.nodes.capacity(),
269+
buffers.nodes.capacity() * size_of::<OitFragmentNode>() / 1024 / 1024,
266270
);
267271
}
268272

crates/bevy_core_pipeline/src/oit/oit_draw.wgsl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,30 @@
22

33
#import bevy_pbr::mesh_view_bindings::{view, oit_nodes, oit_headers, oit_atomic_counter, oit_settings}
44
#import bevy_pbr::mesh_view_types::OitFragmentNode
5+
#import bevy_pbr::prepass_utils
56

67
#ifdef OIT_ENABLED
78
// Add the fragment to the oit buffer
89
fn oit_draw(position: vec4f, color: vec4f) {
10+
#ifdef DEPTH_PREPASS
11+
if position.z < prepass_utils::prepass_depth(position, 0u) {
12+
return;
13+
}
14+
#endif
915
// Don't add fully transparent fragments to the list
1016
// because we don't want to have to sort them in the resolve pass
1117
if color.a < oit_settings.alpha_threshold {
1218
return;
1319
}
1420
// get the index of the current fragment relative to the screen size
1521
let screen_index = u32(floor(position.x) + floor(position.y) * view.viewport.z);
16-
// get the size of the buffer.
17-
// It's always the size of the screen
22+
// get the size of oit_nodes. It's screen_size * fragments_per_pixel_average
1823
let buffer_size = u32(view.viewport.z * view.viewport.w * oit_settings.fragments_per_pixel_average);
1924

2025
var new_node_index = atomicAdd(&oit_atomic_counter, 1);
2126
// exit early if we've reached the maximum amount of fragments nodes
2227
if new_node_index >= buffer_size {
28+
// TODO for tail blending we should return the color here
2329
return;
2430
}
2531

crates/bevy_core_pipeline/src/oit/resolve/mod.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::num::NonZero;
22

33
use super::OitBuffers;
4-
use crate::{oit::OrderIndependentTransparencySettings, FullscreenShader};
4+
use crate::{oit::OrderIndependentTransparencySettings, prepass::DepthPrepass, FullscreenShader};
55
use bevy_app::Plugin;
66
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer};
77
use bevy_derive::Deref;
@@ -144,6 +144,7 @@ pub struct OitResolvePipelineId(pub CachedRenderPipelineId);
144144
pub struct OitResolvePipelineKey {
145145
hdr: bool,
146146
sorted_fragment_max_count: u32,
147+
depth_prepass: bool,
147148
}
148149

149150
pub fn queue_oit_resolve_pipeline(
@@ -155,6 +156,7 @@ pub fn queue_oit_resolve_pipeline(
155156
Entity,
156157
&ExtractedView,
157158
&OrderIndependentTransparencySettings,
159+
Has<DepthPrepass>,
158160
),
159161
With<OrderIndependentTransparencySettings>,
160162
>,
@@ -165,11 +167,12 @@ pub fn queue_oit_resolve_pipeline(
165167
mut cached_pipeline_id: Local<EntityHashMap<(OitResolvePipelineKey, CachedRenderPipelineId)>>,
166168
) {
167169
let mut current_view_entities = EntityHashSet::default();
168-
for (e, view, oit_settings) in &views {
170+
for (e, view, oit_settings, depth_prepass) in &views {
169171
current_view_entities.insert(e);
170172
let key = OitResolvePipelineKey {
171173
hdr: view.hdr,
172174
sorted_fragment_max_count: oit_settings.sorted_fragment_max_count,
175+
depth_prepass,
173176
};
174177

175178
if let Some((cached_key, id)) = cached_pipeline_id.get(&e)
@@ -210,19 +213,23 @@ fn specialize_oit_resolve_pipeline(
210213
} else {
211214
TextureFormat::bevy_default()
212215
};
216+
let mut layout = vec![resolve_pipeline.view_bind_group_layout.clone()];
217+
let mut shader_defs = vec![ShaderDefVal::UInt(
218+
"SORTED_FRAGMENT_MAX_COUNT".into(),
219+
key.sorted_fragment_max_count,
220+
)];
221+
if key.depth_prepass {
222+
shader_defs.push(ShaderDefVal::Bool("DEPTH_PREPASS".into(), true));
223+
} else {
224+
layout.push(resolve_pipeline.oit_depth_bind_group_layout.clone());
225+
}
213226

214227
RenderPipelineDescriptor {
215228
label: Some("oit_resolve_pipeline".into()),
216-
layout: vec![
217-
resolve_pipeline.view_bind_group_layout.clone(),
218-
resolve_pipeline.oit_depth_bind_group_layout.clone(),
219-
],
229+
layout,
220230
fragment: Some(FragmentState {
221231
shader: load_embedded_asset!(asset_server, "oit_resolve.wgsl"),
222-
shader_defs: vec![ShaderDefVal::UInt(
223-
"SORTED_FRAGMENT_MAX_COUNT".into(),
224-
key.sorted_fragment_max_count,
225-
)],
232+
shader_defs,
226233
targets: vec![Some(ColorTargetState {
227234
format,
228235
blend: Some(BlendState {

crates/bevy_core_pipeline/src/oit/resolve/node.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use bevy_render::{
99
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
1010
};
1111

12+
use crate::prepass::DepthPrepass;
13+
1214
use super::{OitResolveBindGroup, OitResolvePipeline, OitResolvePipelineId};
1315

1416
/// Render label for the OIT resolve pass.
@@ -26,15 +28,22 @@ impl ViewNode for OitResolveNode {
2628
&'static OitResolvePipelineId,
2729
&'static ViewDepthTexture,
2830
Option<&'static MainPassResolutionOverride>,
31+
Has<DepthPrepass>,
2932
);
3033

3134
fn run(
3235
&self,
3336
_graph: &mut RenderGraphContext,
3437
render_context: &mut RenderContext,
35-
(camera, view_target, view_uniform, oit_resolve_pipeline_id, depth, resolution_override): QueryItem<
36-
Self::ViewQuery,
37-
>,
38+
(
39+
camera,
40+
view_target,
41+
view_uniform,
42+
oit_resolve_pipeline_id,
43+
depth,
44+
resolution_override,
45+
depth_prepass,
46+
): QueryItem<Self::ViewQuery>,
3847
world: &World,
3948
) -> Result<(), NodeRunError> {
4049
let Some(resolve_pipeline) = world.get_resource::<OitResolvePipeline>() else {
@@ -53,12 +62,18 @@ impl ViewNode for OitResolveNode {
5362

5463
let diagnostics = render_context.diagnostic_recorder();
5564

56-
let depth_bind_group = render_context.render_device().create_bind_group(
57-
"oit_resolve_depth_bind_group",
58-
&pipeline_cache
59-
.get_bind_group_layout(&resolve_pipeline.oit_depth_bind_group_layout),
60-
&BindGroupEntries::single(depth.view()),
61-
);
65+
let depth_bind_group = if !depth_prepass {
66+
Some(
67+
render_context.render_device().create_bind_group(
68+
"oit_resolve_depth_bind_group",
69+
&pipeline_cache
70+
.get_bind_group_layout(&resolve_pipeline.oit_depth_bind_group_layout),
71+
&BindGroupEntries::single(depth.view()),
72+
),
73+
)
74+
} else {
75+
None
76+
};
6277

6378
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
6479
label: Some("oit_resolve"),
@@ -77,8 +92,9 @@ impl ViewNode for OitResolveNode {
7792

7893
render_pass.set_render_pipeline(pipeline);
7994
render_pass.set_bind_group(0, bind_group, &[view_uniform.offset]);
80-
render_pass.set_bind_group(1, &depth_bind_group, &[]);
81-
95+
if let Some(depth_bind_group) = &depth_bind_group {
96+
render_pass.set_bind_group(1, depth_bind_group, &[]);
97+
}
8298
render_pass.draw(0..3, 0..1);
8399

84100
pass_span.end(&mut render_pass);

crates/bevy_core_pipeline/src/oit/resolve/oit_resolve.wgsl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
@group(0) @binding(2) var<storage, read_write> headers: array<u32>;
77
@group(0) @binding(3) var<storage, read_write> atomic_counter: u32;
88

9+
#ifndef DEPTH_PREPASS
910
@group(1) @binding(0) var depth: texture_depth_2d;
11+
#endif
1012

1113
struct OitFragment {
1214
color: vec3<f32>,
@@ -35,11 +37,15 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
3537
}
3638
return vec4(0.0);
3739
} else {
38-
// Load depth for manual depth testing.
40+
#ifndef DEPTH_PREPASS
41+
// If depth prepass is disabled, load depth for manual depth testing.
3942
// This is necessary because early z doesn't seem to trigger in the transparent pass.
4043
// This should be done during the draw pass so those fragments simply don't exist in the list,
4144
// but this requires a bigger refactor
4245
let d = textureLoad(depth, vec2<i32>(in.position.xy), 0);
46+
#else
47+
let d = 0.0;
48+
#endif
4349
let color = resolve(header, d);
4450
headers[screen_index] = LINKED_LIST_END_SENTINEL;
4551
return color;
@@ -62,17 +68,19 @@ fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
6268
let depth_alpha = bevy_core_pipeline::oit::unpack_24bit_depth_8bit_alpha(fragment_node.depth_alpha);
6369
current_node = fragment_node.next;
6470

71+
#ifndef DEPTH_PREPASS
6572
// depth testing
6673
if depth_alpha.x < opaque_depth {
6774
continue;
6875
}
76+
#endif
6977

7078
if sorted_frag_count < SORTED_FRAGMENT_MAX_COUNT {
7179
// There is still room in the sorted list.
7280
// Insert the fragment so that the list stay sorted.
7381
var i = sorted_frag_count;
7482
for(; i > 0; i -= 1) {
75-
// short-circuit can't be used in for-loop, https://github.com/gfx-rs/wgpu/issues/4394
83+
// short-circuit can't be used in for(;;;), https://github.com/gfx-rs/wgpu/issues/4394
7684
if depth_alpha.x < fragment_list[i - 1].depth {
7785
fragment_list[i] = fragment_list[i - 1];
7886
} else {
@@ -91,7 +99,7 @@ fn resolve(header: u32, opaque_depth: f32) -> vec4<f32> {
9199
final_color = blend(vec4f(fragment_list[0].color * fragment_list[0].alpha, fragment_list[0].alpha), final_color);
92100
var i = 0u;
93101
for(; i < SORTED_FRAGMENT_MAX_COUNT - 1; i += 1) {
94-
// short-circuit can't be used in for-loop, https://github.com/gfx-rs/wgpu/issues/4394
102+
// short-circuit can't be used in for(;;;), https://github.com/gfx-rs/wgpu/issues/4394
95103
if fragment_list[i + 1].depth < depth_alpha.x {
96104
fragment_list[i] = fragment_list[i + 1];
97105
} else {

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,6 +2379,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
23792379
let (mut is_opaque, mut alpha_to_coverage_enabled) = (false, false);
23802380
if key.contains(MeshPipelineKey::OIT_ENABLED) && pass == MeshPipelineKey::BLEND_ALPHA {
23812381
label = "oit_mesh_pipeline".into();
2382+
// TODO tail blending would need alpha blending
23822383
blend = None;
23832384
shader_defs.push("OIT_ENABLED".into());
23842385
// TODO it should be possible to use this to combine MSAA and OIT

examples/3d/order_independent_transparency.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use bevy::{
77
camera::visibility::RenderLayers,
88
color::palettes::css::{BLUE, GREEN, RED, YELLOW},
9-
core_pipeline::oit::OrderIndependentTransparencySettings,
9+
core_pipeline::{oit::OrderIndependentTransparencySettings, prepass::DepthPrepass},
1010
prelude::*,
1111
};
1212

@@ -33,6 +33,8 @@ fn setup(
3333
RenderLayers::layer(1),
3434
// Msaa currently doesn't work with OIT
3535
Msaa::Off,
36+
// Optional: depth prepass can help OIT filter out fragments occluded by opaque objects
37+
DepthPrepass,
3638
));
3739

3840
// light

0 commit comments

Comments
 (0)