Skip to content

Commit 06bf928

Browse files
Option to enable deterministic rendering (#11248)
# Objective Issue #10243: rendering multiple triangles in the same place results in flickering. ## Solution Considered these alternatives: - `depth_bias` may not work, because of high number of entities, so creating a material per entity is practically not possible - rendering at slightly different positions does not work, because when camera is far, float rounding causes the same issues (edit: assuming we have to use the same `depth_bias`) - considered implementing deterministic operation like `query.par_iter().flat_map(...).collect()` to be used in `check_visibility` system (which would solve the issue since query is deterministic), and could not figure out how to make it as cheap as current approach with thread-local collectors (#11249) So adding an option to sort entities after `check_visibility` system run. Should not be too bad, because after visibility check, only a handful entities remain. This is probably not the only source of non-determinism in Bevy, but this is one I could find so far. At least it fixes the repro example. ## Changelog - `DeterministicRenderingConfig` option to enable deterministic rendering ## Test <img width="1392" alt="image" src="https://github.com/bevyengine/bevy/assets/28969/c735bce1-3a71-44cd-8677-c19f6c0ee6bd"> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
1 parent 9c972f0 commit 06bf928

File tree

6 files changed

+124
-0
lines changed

6 files changed

+124
-0
lines changed

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,17 @@ description = "Showcases different blend modes"
598598
category = "3D Rendering"
599599
wasm = true
600600

601+
[[example]]
602+
name = "deterministic"
603+
path = "examples/3d/deterministic.rs"
604+
doc-scrape-examples = true
605+
606+
[package.metadata.example.deterministic]
607+
name = "Deterministic rendering"
608+
description = "Stop flickering from z-fighting at a performance cost"
609+
category = "3D Rendering"
610+
wasm = true
611+
601612
[[example]]
602613
name = "lighting"
603614
path = "examples/3d/lighting.rs"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use bevy_ecs::system::Resource;
2+
3+
/// Configure deterministic rendering to fix flickering due to z-fighting.
4+
#[derive(Resource, Default)]
5+
pub struct DeterministicRenderingConfig {
6+
/// Sort visible entities by id before rendering to avoid flickering.
7+
///
8+
/// Render is parallel by default, and if there's z-fighting, it may cause flickering.
9+
/// Default fix for the issue is to set `depth_bias` per material.
10+
/// When it is not possible, entities sorting can be used.
11+
///
12+
/// This option costs performance and disabled by default.
13+
pub stable_sort_z_fighting: bool,
14+
}

crates/bevy_render/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extern crate core;
66
pub mod batching;
77
pub mod camera;
88
pub mod color;
9+
pub mod deterministic;
910
pub mod extract_component;
1011
pub mod extract_instances;
1112
mod extract_param;
@@ -48,6 +49,7 @@ use bevy_window::{PrimaryWindow, RawHandleWrapper};
4849
use globals::GlobalsPlugin;
4950
use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
5051

52+
use crate::deterministic::DeterministicRenderingConfig;
5153
use crate::{
5254
camera::CameraPlugin,
5355
mesh::{morph::MorphPlugin, Mesh, MeshPlugin},
@@ -216,6 +218,8 @@ pub const MATHS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(106653563
216218
impl Plugin for RenderPlugin {
217219
/// Initializes the renderer, sets up the [`RenderSet`] and creates the rendering sub-app.
218220
fn build(&self, app: &mut App) {
221+
app.init_resource::<DeterministicRenderingConfig>();
222+
219223
app.init_asset::<Shader>()
220224
.init_asset_loader::<ShaderLoader>();
221225

crates/bevy_render/src/view/visibility/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use bevy_transform::{components::GlobalTransform, TransformSystem};
1212
use std::cell::Cell;
1313
use thread_local::ThreadLocal;
1414

15+
use crate::deterministic::DeterministicRenderingConfig;
1516
use crate::{
1617
camera::{
1718
camera_system, Camera, CameraProjection, OrthographicProjection, PerspectiveProjection,
@@ -392,6 +393,7 @@ pub fn check_visibility(
392393
&GlobalTransform,
393394
Has<NoFrustumCulling>,
394395
)>,
396+
deterministic_rendering_config: Res<DeterministicRenderingConfig>,
395397
) {
396398
for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query {
397399
if !camera.is_active {
@@ -452,6 +454,11 @@ pub fn check_visibility(
452454
for cell in &mut thread_queues {
453455
visible_entities.entities.append(cell.get_mut());
454456
}
457+
if deterministic_rendering_config.stable_sort_z_fighting {
458+
// We can use the faster unstable sort here because
459+
// the values (`Entity`) are guaranteed to be unique.
460+
visible_entities.entities.sort_unstable();
461+
}
455462
}
456463
}
457464

examples/3d/deterministic.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//! Shows how to enable deterministic rendering which helps with flickering due to z-fighting.
2+
//! Rendering is not deterministic by default.
3+
//! Note most users don't need rendering to be deterministic, and should rely on depth bias instead.
4+
5+
use bevy::app::App;
6+
use bevy::app::Startup;
7+
use bevy::prelude::shape::Plane;
8+
use bevy::prelude::*;
9+
use bevy::render::deterministic::DeterministicRenderingConfig;
10+
11+
fn main() {
12+
App::new()
13+
.add_plugins(DefaultPlugins)
14+
.add_systems(Startup, setup)
15+
.add_systems(Update, (keys, update_help).chain())
16+
.run();
17+
}
18+
19+
fn setup(
20+
mut commands: Commands,
21+
mut materials: ResMut<Assets<StandardMaterial>>,
22+
mut meshes: ResMut<Assets<Mesh>>,
23+
mut deterministic_rendering_config: ResMut<DeterministicRenderingConfig>,
24+
) {
25+
// Safe default.
26+
deterministic_rendering_config.stable_sort_z_fighting = true;
27+
28+
// Help message will be rendered there.
29+
commands.spawn(TextBundle::default());
30+
31+
commands.spawn(Camera3dBundle {
32+
transform: Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::new(0., 0., 0.), Vec3::Y),
33+
..default()
34+
});
35+
36+
let mesh = meshes.add(Plane::from_size(2.0));
37+
for i in 0..360 {
38+
let color = Color::hsl(i as f32, 1.0, 0.5);
39+
commands.spawn(PbrBundle {
40+
mesh: mesh.clone(),
41+
material: materials.add(StandardMaterial {
42+
base_color: color,
43+
// Setting depth bias would be a default choice to fix z-fighting.
44+
// When it is not possible, deterministic rendering can be used.
45+
// Here we intentionally don't use depth bias to demonstrate the issue.
46+
depth_bias: 0.0,
47+
unlit: true,
48+
..Default::default()
49+
}),
50+
..default()
51+
});
52+
}
53+
}
54+
55+
fn keys(
56+
mut deterministic_rendering_config: ResMut<DeterministicRenderingConfig>,
57+
keyboard_input: Res<ButtonInput<KeyCode>>,
58+
) {
59+
if keyboard_input.just_pressed(KeyCode::KeyD) {
60+
deterministic_rendering_config.stable_sort_z_fighting ^= true;
61+
}
62+
}
63+
64+
fn update_help(
65+
mut text: Query<&mut Text>,
66+
deterministic_rendering_config: Res<DeterministicRenderingConfig>,
67+
) {
68+
if deterministic_rendering_config.is_changed() {
69+
*text.single_mut() = Text::from_section(
70+
format!(
71+
"\
72+
Press D to enable/disable deterministic rendering\n\
73+
\n\
74+
Deterministic rendering: {}\n\
75+
\n\
76+
When rendering is not deterministic, you may notice flickering due to z-fighting\n\
77+
\n\
78+
Warning: may cause seizures for people with photosensitive epilepsy",
79+
deterministic_rendering_config.stable_sort_z_fighting
80+
),
81+
TextStyle {
82+
font_size: 20.,
83+
..default()
84+
},
85+
);
86+
}
87+
}

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ Example | Description
123123
[Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect
124124
[Blend Modes](../examples/3d/blend_modes.rs) | Showcases different blend modes
125125
[Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines
126+
[Deterministic rendering](../examples/3d/deterministic.rs) | Stop flickering from z-fighting at a performance cost
126127
[Fog](../examples/3d/fog.rs) | A scene showcasing the distance fog effect
127128
[Generate Custom Mesh](../examples/3d/generate_custom_mesh.rs) | Simple showcase of how to generate a custom mesh with a custom texture
128129
[Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene

0 commit comments

Comments
 (0)