mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
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>
This commit is contained in:
parent
9c972f037e
commit
06bf928927
6 changed files with 124 additions and 0 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -598,6 +598,17 @@ description = "Showcases different blend modes"
|
|||
category = "3D Rendering"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "deterministic"
|
||||
path = "examples/3d/deterministic.rs"
|
||||
doc-scrape-examples = true
|
||||
|
||||
[package.metadata.example.deterministic]
|
||||
name = "Deterministic rendering"
|
||||
description = "Stop flickering from z-fighting at a performance cost"
|
||||
category = "3D Rendering"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "lighting"
|
||||
path = "examples/3d/lighting.rs"
|
||||
|
|
14
crates/bevy_render/src/deterministic.rs
Normal file
14
crates/bevy_render/src/deterministic.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use bevy_ecs::system::Resource;
|
||||
|
||||
/// Configure deterministic rendering to fix flickering due to z-fighting.
|
||||
#[derive(Resource, Default)]
|
||||
pub struct DeterministicRenderingConfig {
|
||||
/// Sort visible entities by id before rendering to avoid flickering.
|
||||
///
|
||||
/// Render is parallel by default, and if there's z-fighting, it may cause flickering.
|
||||
/// Default fix for the issue is to set `depth_bias` per material.
|
||||
/// When it is not possible, entities sorting can be used.
|
||||
///
|
||||
/// This option costs performance and disabled by default.
|
||||
pub stable_sort_z_fighting: bool,
|
||||
}
|
|
@ -6,6 +6,7 @@ extern crate core;
|
|||
pub mod batching;
|
||||
pub mod camera;
|
||||
pub mod color;
|
||||
pub mod deterministic;
|
||||
pub mod extract_component;
|
||||
pub mod extract_instances;
|
||||
mod extract_param;
|
||||
|
@ -48,6 +49,7 @@ use bevy_window::{PrimaryWindow, RawHandleWrapper};
|
|||
use globals::GlobalsPlugin;
|
||||
use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
|
||||
|
||||
use crate::deterministic::DeterministicRenderingConfig;
|
||||
use crate::{
|
||||
camera::CameraPlugin,
|
||||
mesh::{morph::MorphPlugin, Mesh, MeshPlugin},
|
||||
|
@ -216,6 +218,8 @@ pub const MATHS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(106653563
|
|||
impl Plugin for RenderPlugin {
|
||||
/// Initializes the renderer, sets up the [`RenderSet`] and creates the rendering sub-app.
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<DeterministicRenderingConfig>();
|
||||
|
||||
app.init_asset::<Shader>()
|
||||
.init_asset_loader::<ShaderLoader>();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use bevy_transform::{components::GlobalTransform, TransformSystem};
|
|||
use std::cell::Cell;
|
||||
use thread_local::ThreadLocal;
|
||||
|
||||
use crate::deterministic::DeterministicRenderingConfig;
|
||||
use crate::{
|
||||
camera::{
|
||||
camera_system, Camera, CameraProjection, OrthographicProjection, PerspectiveProjection,
|
||||
|
@ -392,6 +393,7 @@ pub fn check_visibility(
|
|||
&GlobalTransform,
|
||||
Has<NoFrustumCulling>,
|
||||
)>,
|
||||
deterministic_rendering_config: Res<DeterministicRenderingConfig>,
|
||||
) {
|
||||
for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query {
|
||||
if !camera.is_active {
|
||||
|
@ -452,6 +454,11 @@ pub fn check_visibility(
|
|||
for cell in &mut thread_queues {
|
||||
visible_entities.entities.append(cell.get_mut());
|
||||
}
|
||||
if deterministic_rendering_config.stable_sort_z_fighting {
|
||||
// We can use the faster unstable sort here because
|
||||
// the values (`Entity`) are guaranteed to be unique.
|
||||
visible_entities.entities.sort_unstable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
87
examples/3d/deterministic.rs
Normal file
87
examples/3d/deterministic.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
//! Shows how to enable deterministic rendering which helps with flickering due to z-fighting.
|
||||
//! Rendering is not deterministic by default.
|
||||
//! Note most users don't need rendering to be deterministic, and should rely on depth bias instead.
|
||||
|
||||
use bevy::app::App;
|
||||
use bevy::app::Startup;
|
||||
use bevy::prelude::shape::Plane;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::deterministic::DeterministicRenderingConfig;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (keys, update_help).chain())
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut deterministic_rendering_config: ResMut<DeterministicRenderingConfig>,
|
||||
) {
|
||||
// Safe default.
|
||||
deterministic_rendering_config.stable_sort_z_fighting = true;
|
||||
|
||||
// Help message will be rendered there.
|
||||
commands.spawn(TextBundle::default());
|
||||
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::new(0., 0., 0.), Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
let mesh = meshes.add(Plane::from_size(2.0));
|
||||
for i in 0..360 {
|
||||
let color = Color::hsl(i as f32, 1.0, 0.5);
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: mesh.clone(),
|
||||
material: materials.add(StandardMaterial {
|
||||
base_color: color,
|
||||
// Setting depth bias would be a default choice to fix z-fighting.
|
||||
// When it is not possible, deterministic rendering can be used.
|
||||
// Here we intentionally don't use depth bias to demonstrate the issue.
|
||||
depth_bias: 0.0,
|
||||
unlit: true,
|
||||
..Default::default()
|
||||
}),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn keys(
|
||||
mut deterministic_rendering_config: ResMut<DeterministicRenderingConfig>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(KeyCode::KeyD) {
|
||||
deterministic_rendering_config.stable_sort_z_fighting ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_help(
|
||||
mut text: Query<&mut Text>,
|
||||
deterministic_rendering_config: Res<DeterministicRenderingConfig>,
|
||||
) {
|
||||
if deterministic_rendering_config.is_changed() {
|
||||
*text.single_mut() = Text::from_section(
|
||||
format!(
|
||||
"\
|
||||
Press D to enable/disable deterministic rendering\n\
|
||||
\n\
|
||||
Deterministic rendering: {}\n\
|
||||
\n\
|
||||
When rendering is not deterministic, you may notice flickering due to z-fighting\n\
|
||||
\n\
|
||||
Warning: may cause seizures for people with photosensitive epilepsy",
|
||||
deterministic_rendering_config.stable_sort_z_fighting
|
||||
),
|
||||
TextStyle {
|
||||
font_size: 20.,
|
||||
..default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -123,6 +123,7 @@ Example | Description
|
|||
[Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect
|
||||
[Blend Modes](../examples/3d/blend_modes.rs) | Showcases different blend modes
|
||||
[Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines
|
||||
[Deterministic rendering](../examples/3d/deterministic.rs) | Stop flickering from z-fighting at a performance cost
|
||||
[Fog](../examples/3d/fog.rs) | A scene showcasing the distance fog effect
|
||||
[Generate Custom Mesh](../examples/3d/generate_custom_mesh.rs) | Simple showcase of how to generate a custom mesh with a custom texture
|
||||
[Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene
|
||||
|
|
Loading…
Reference in a new issue