Skip to content

Commit c414d2e

Browse files
committed
Fix corrupted linked list on startup
1 parent 9d0dbc3 commit c414d2e

File tree

4 files changed

+34
-28
lines changed

4 files changed

+34
-28
lines changed

crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
use crate::core_3d::Transparent3d;
1+
use crate::{
2+
core_3d::Transparent3d,
3+
oit::{resolve::OitResolvePipelineId, OrderIndependentTransparencySettings},
4+
};
25
use bevy_camera::{MainPassResolutionOverride, Viewport};
36
use bevy_ecs::{prelude::*, query::QueryItem};
47
use bevy_render::{
58
camera::ExtractedCamera,
69
diagnostic::RecordDiagnostics,
710
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
811
render_phase::ViewSortedRenderPhases,
9-
render_resource::{RenderPassDescriptor, StoreOp},
12+
render_resource::{PipelineCache, RenderPassDescriptor, StoreOp},
1013
renderer::RenderContext,
1114
view::{ExtractedView, ViewDepthTexture, ViewTarget},
1215
};
@@ -26,12 +29,14 @@ impl ViewNode for MainTransparentPass3dNode {
2629
&'static ViewTarget,
2730
&'static ViewDepthTexture,
2831
Option<&'static MainPassResolutionOverride>,
32+
Has<OrderIndependentTransparencySettings>,
33+
Option<&'static OitResolvePipelineId>,
2934
);
3035
fn run(
3136
&self,
3237
graph: &mut RenderGraphContext,
3338
render_context: &mut RenderContext,
34-
(camera, view, target, depth, resolution_override): QueryItem<Self::ViewQuery>,
39+
(camera, view, target, depth, resolution_override, has_oit,oit_resolve_pipeline_id): QueryItem<Self::ViewQuery>,
3540
world: &World,
3641
) -> Result<(), NodeRunError> {
3742
let view_entity = graph.view_entity();
@@ -47,6 +52,22 @@ impl ViewNode for MainTransparentPass3dNode {
4752
};
4853

4954
if !transparent_phase.items.is_empty() {
55+
if has_oit {
56+
// We can't run transparent phase if OitResolvePipelineId is not ready
57+
// Otherwise we will write to `oit_atomic_counter` and `oit_headers` buffer without resetting them
58+
// which causes corrupted linked list(can have circular references) on the next pass
59+
let Some(oit_resolve_pipeline_id) = oit_resolve_pipeline_id else {
60+
return Ok(());
61+
};
62+
let pipeline_cache = world.resource::<PipelineCache>();
63+
if pipeline_cache
64+
.get_render_pipeline(oit_resolve_pipeline_id.0)
65+
.is_none()
66+
{
67+
return Ok(());
68+
}
69+
}
70+
5071
// Run the transparent pass, sorted back-to-front
5172
// NOTE: Scoped to drop the mutable borrow of render_context
5273
#[cfg(feature = "trace")]

crates/bevy_core_pipeline/src/oit/mod.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -138,19 +138,9 @@ fn check_msaa(cameras: Query<&Msaa, With<OrderIndependentTransparencySettings>>)
138138

139139
#[derive(Clone, Copy, ShaderType)]
140140
pub struct OitFragmentNode {
141-
color: u32,
142-
depth_alpha: u32,
143-
next: u32,
144-
}
145-
146-
impl Default for OitFragmentNode {
147-
fn default() -> Self {
148-
Self {
149-
color: 0,
150-
depth_alpha: 0,
151-
next: u32::MAX,
152-
}
153-
}
141+
pub color: u32,
142+
pub depth_alpha: u32,
143+
pub next: u32,
154144
}
155145

156146
/// Holds the buffers that contain the data of all OIT layers.
@@ -176,8 +166,7 @@ pub fn init_oit_buffers(
176166

177167
let mut nodes = BufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
178168
nodes.set_label(Some("oit_nodes"));
179-
nodes.push(OitFragmentNode::default());
180-
nodes.write_buffer(&render_device, &render_queue);
169+
nodes.reserve(1, &render_device);
181170

182171
let mut headers = BufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
183172
headers.set_label(Some("oit_headers"));
@@ -256,12 +245,7 @@ pub fn prepare_oit_buffers(
256245
let nodes_size = ((max_size.x * max_size.y) as f32 * fragments_per_pixel_average) as usize;
257246
if buffers.nodes.capacity() < nodes_size {
258247
let start = Instant::now();
259-
buffers.nodes.clear();
260248
buffers.nodes.reserve(nodes_size, &render_device);
261-
for _ in 0..nodes_size {
262-
buffers.nodes.push(OitFragmentNode::default());
263-
}
264-
buffers.nodes.write_buffer(&render_device, &render_queue);
265249
trace!(
266250
"OIT nodes buffer updated in {:.01}ms with capacity {}, total size {} MiB",
267251
start.elapsed().as_millis(),

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ impl ViewNode for OitResolveNode {
4646
): QueryItem<Self::ViewQuery>,
4747
world: &World,
4848
) -> Result<(), NodeRunError> {
49-
let Some(resolve_pipeline) = world.get_resource::<OitResolvePipeline>() else {
50-
return Ok(());
51-
};
49+
// This *must* run after main_transparent_pass_3d to reset the `oit_atomic_counter` and `oit_headers` buffer
50+
// Otherwise transparent pass will construct a corrupted linked list(can have circular references which causes infinite loop and device lost) on the next pass
51+
52+
let resolve_pipeline = world.get_resource::<OitResolvePipeline>().unwrap();
5253

5354
// resolve oit
5455
// sorts the layers and renders the final blended color to the screen

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
@group(0) @binding(0) var<uniform> view: View;
55
@group(0) @binding(1) var<storage, read> nodes: array<OitFragmentNode>;
6-
@group(0) @binding(2) var<storage, read_write> headers: array<u32>;
7-
@group(0) @binding(3) var<storage, read_write> atomic_counter: u32;
6+
@group(0) @binding(2) var<storage, read_write> headers: array<u32>; // No need to be atomic
7+
@group(0) @binding(3) var<storage, read_write> atomic_counter: u32; // No need to be atomic
88

99
#ifndef DEPTH_PREPASS
1010
@group(1) @binding(0) var depth: texture_depth_2d;

0 commit comments

Comments
 (0)