mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
61127f6d01
# Objective
- In 0.18, we had 10 different functions that load assets (I'm not even
counting `load_folder`).
- In 0.19, we've even added `load_erased` - but it unfortunately doesn't
support all the features that the other variants support.
- We apparently needed `load_acquire_with_settings_override` which 1)
loads the asset, 2) uses the settings provided, 3) allows reading
unapproved asset paths, and 4) drops a guard once the load completes.
- That's fine if that's necessary. But we needed to create an explicit
variant for that.
- We need fewer load paths!
## Solution
- Create a builder.
- Store all these options dynamically instead of statically handling
each case.
- Have the caller choose a particular "kind" of load when they are
ready: `load`, `load_erased`, `load_untyped`, or `load_untyped_async`.
- I intentionally didn't provide a `load_async` or `load_erased_async`,
since those can be replicated using `load`/`load_erased` +
`AssetServer::wait_for_asset_id` to get the exact same effect.
I am also intentionally leaving `NestedLoader` untouched in this PR, but
a followup will duplicate this API for `NestedLoader`, which should make
it easier to understand.
Unlike the `NestedLoader` API, we aren't doing any type-state craziness,
so the docs are much more clear: users don't need to understand how
type-state stuff works, they just call the handful of methods on the
type. The "cost" here is we now need to be careful about including the
cross product of loads between static asset type, runtime asset type, or
dynamic asset type, crossed with deferred or async. In theory, if we
added more kinds on either side, we would need to expand this cross
product a lot. In practice though, it seems unlikely there will be any
more variants there. (maybe there could be a blocking variant? I don't
think this is a popular opinion though).
A big con here is some somewhat common calls are now more verbose.
Specifically, `asset_server.load_with_settings()` has become
`asset_server.load_builder().with_settings().load()`. I am not really
concerned about this though, since it really isn't that painful.
## Testing
- Tests all pass!
---
## Showcase
Now instead of:
```rust
asset_server.load_acquire_with_settings_override("some_path", |settings: &mut GltfLoaderSettings| { ... }, my_lock_guard);
```
You can instead do:
```rust
asset_server.load_builder()
.with_guard(my_lock_guard)
.with_settings(|settings: &mut GltfLoaderSettings| { ... })
.override_unapproved()
.load("some_path");
```
We also now cover more variants! For example, you can now load an asset
untyped with a guard, or with override_unapproved, etc.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
629 lines
21 KiB
Rust
629 lines
21 KiB
Rust
//! Demonstrates realtime dynamic raytraced lighting using Bevy Solari.
|
|
|
|
use argh::FromArgs;
|
|
use bevy::{
|
|
camera::CameraMainTextureUsages,
|
|
camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
|
|
diagnostic::{Diagnostic, DiagnosticPath, DiagnosticsStore},
|
|
gltf::GltfMaterialName,
|
|
image::{ImageAddressMode, ImageLoaderSettings},
|
|
mesh::{Indices, VertexAttributeValues},
|
|
post_process::bloom::Bloom,
|
|
prelude::*,
|
|
render::{diagnostic::RenderDiagnosticsPlugin, render_resource::TextureUsages},
|
|
solari::{
|
|
pathtracer::{Pathtracer, PathtracingPlugin},
|
|
prelude::{RaytracingMesh3d, SolariLighting, SolariPlugins},
|
|
},
|
|
world_serialization::WorldInstanceReady,
|
|
};
|
|
use chacha20::ChaCha8Rng;
|
|
use rand::{RngExt, SeedableRng};
|
|
use std::f32::consts::PI;
|
|
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
|
|
use bevy::anti_alias::dlss::{
|
|
Dlss, DlssProjectId, DlssRayReconstructionFeature, DlssRayReconstructionSupported,
|
|
};
|
|
|
|
/// `bevy_solari` demo.
|
|
#[derive(FromArgs, Resource, Clone, Copy)]
|
|
struct Args {
|
|
/// use the reference pathtracer instead of the realtime lighting system.
|
|
#[argh(switch)]
|
|
pathtracer: Option<bool>,
|
|
/// stress test a scene with many lights.
|
|
#[argh(switch)]
|
|
many_lights: Option<bool>,
|
|
}
|
|
|
|
fn main() {
|
|
let args: Args = argh::from_env();
|
|
|
|
let mut app = App::new();
|
|
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
|
|
app.insert_resource(DlssProjectId(bevy_asset::uuid::uuid!(
|
|
"5417916c-0291-4e3f-8f65-326c1858ab96" // Don't copy paste this - generate your own UUID!
|
|
)));
|
|
|
|
app.add_plugins((
|
|
DefaultPlugins,
|
|
SolariPlugins,
|
|
FreeCameraPlugin,
|
|
RenderDiagnosticsPlugin,
|
|
))
|
|
.insert_resource(args);
|
|
|
|
if args.many_lights == Some(true) {
|
|
app.add_systems(Startup, setup_many_lights);
|
|
} else {
|
|
app.add_systems(Startup, setup_pica_pica);
|
|
}
|
|
|
|
if args.pathtracer == Some(true) {
|
|
app.add_plugins(PathtracingPlugin);
|
|
} else {
|
|
if args.many_lights != Some(true) {
|
|
app.add_systems(Update, (pause_scene, toggle_lights, patrol_path))
|
|
.add_systems(PostUpdate, update_control_text);
|
|
}
|
|
app.add_systems(PostUpdate, update_performance_text);
|
|
}
|
|
|
|
app.run();
|
|
}
|
|
|
|
fn setup_pica_pica(
|
|
mut commands: Commands,
|
|
asset_server: Res<AssetServer>,
|
|
args: Res<Args>,
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
|
|
Res<DlssRayReconstructionSupported>,
|
|
>,
|
|
) {
|
|
commands
|
|
.spawn((
|
|
WorldAssetRoot(
|
|
asset_server.load(
|
|
GltfAssetLabel::Scene(0)
|
|
.from_asset("https://github.com/bevyengine/bevy_asset_files/raw/2a5950295a8b6d9d051d59c0df69e87abcda58c3/pica_pica/mini_diorama_01.glb")
|
|
),
|
|
),
|
|
Transform::from_scale(Vec3::splat(10.0)),
|
|
))
|
|
.observe(add_raytracing_meshes_on_scene_load);
|
|
|
|
commands
|
|
.spawn((
|
|
WorldAssetRoot(asset_server.load(
|
|
GltfAssetLabel::Scene(0).from_asset("https://github.com/bevyengine/bevy_asset_files/raw/2a5950295a8b6d9d051d59c0df69e87abcda58c3/pica_pica/robot_01.glb")
|
|
)),
|
|
Transform::from_scale(Vec3::splat(2.0))
|
|
.with_translation(Vec3::new(-2.0, 0.05, -2.1))
|
|
.with_rotation(Quat::from_rotation_y(PI / 2.0)),
|
|
PatrolPath {
|
|
path: vec![
|
|
(Vec3::new(-2.0, 0.05, -2.1), Quat::from_rotation_y(PI / 2.0)),
|
|
(Vec3::new(2.2, 0.05, -2.1), Quat::from_rotation_y(0.0)),
|
|
(
|
|
Vec3::new(2.2, 0.05, 2.1),
|
|
Quat::from_rotation_y(3.0 * PI / 2.0),
|
|
),
|
|
(Vec3::new(-2.0, 0.05, 2.1), Quat::from_rotation_y(PI)),
|
|
],
|
|
i: 0,
|
|
},
|
|
))
|
|
.observe(add_raytracing_meshes_on_scene_load);
|
|
|
|
commands.spawn((
|
|
DirectionalLight {
|
|
illuminance: light_consts::lux::FULL_DAYLIGHT,
|
|
shadow_maps_enabled: false, // Solari replaces shadow mapping
|
|
..default()
|
|
},
|
|
Transform::from_rotation(Quat::from_xyzw(
|
|
-0.13334629,
|
|
-0.86597735,
|
|
-0.3586996,
|
|
0.3219264,
|
|
)),
|
|
));
|
|
|
|
let mut camera = commands.spawn((
|
|
Camera3d::default(),
|
|
Camera {
|
|
clear_color: ClearColorConfig::Custom(Color::BLACK),
|
|
..default()
|
|
},
|
|
FreeCamera {
|
|
walk_speed: 3.0,
|
|
run_speed: 10.0,
|
|
..Default::default()
|
|
},
|
|
Transform::from_translation(Vec3::new(0.219417, 2.5764852, 6.9718704)).with_rotation(
|
|
Quat::from_xyzw(-0.1466768, 0.013738206, 0.002037309, 0.989087),
|
|
),
|
|
// Msaa::Off and CameraMainTextureUsages with STORAGE_BINDING are required for Solari
|
|
CameraMainTextureUsages::default().with(TextureUsages::STORAGE_BINDING),
|
|
Msaa::Off,
|
|
));
|
|
|
|
if args.pathtracer == Some(true) {
|
|
camera.insert(Pathtracer::default());
|
|
} else {
|
|
camera.insert(SolariLighting::default());
|
|
}
|
|
|
|
// Using DLSS Ray Reconstruction for denoising (and cheaper rendering via upscaling) is _highly_ recommended when using Solari
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
|
|
if dlss_rr_supported.is_some() {
|
|
camera.insert(Dlss::<DlssRayReconstructionFeature> {
|
|
perf_quality_mode: Default::default(),
|
|
reset: Default::default(),
|
|
_phantom_data: Default::default(),
|
|
});
|
|
}
|
|
|
|
commands.spawn((
|
|
ControlText,
|
|
Text::default(),
|
|
Node {
|
|
position_type: PositionType::Absolute,
|
|
bottom: px(12.0),
|
|
left: px(12.0),
|
|
..default()
|
|
},
|
|
));
|
|
|
|
commands.spawn((
|
|
Node {
|
|
position_type: PositionType::Absolute,
|
|
right: px(0.0),
|
|
padding: px(4.0).all(),
|
|
border_radius: BorderRadius::bottom_left(px(4.0)),
|
|
..default()
|
|
},
|
|
BackgroundColor(Color::srgba(0.10, 0.10, 0.10, 0.8)),
|
|
children![(
|
|
PerformanceText,
|
|
Text::default(),
|
|
TextFont {
|
|
font_size: FontSize::Px(8.0),
|
|
..default()
|
|
},
|
|
)],
|
|
));
|
|
}
|
|
|
|
fn setup_many_lights(
|
|
mut commands: Commands,
|
|
asset_server: Res<AssetServer>,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
args: Res<Args>,
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
|
|
Res<DlssRayReconstructionSupported>,
|
|
>,
|
|
) {
|
|
let mut rng = ChaCha8Rng::seed_from_u64(42);
|
|
|
|
let mut plane_mesh = Plane3d::default()
|
|
.mesh()
|
|
.size(400.0, 400.0)
|
|
.build()
|
|
.with_generated_tangents()
|
|
.unwrap();
|
|
match plane_mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0).unwrap() {
|
|
VertexAttributeValues::Float32x2(items) => {
|
|
items.iter_mut().flatten().for_each(|x| *x *= 3.0);
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
let plane_mesh = meshes.add(plane_mesh);
|
|
let cube_mesh = meshes.add(
|
|
Cuboid::default()
|
|
.mesh()
|
|
.build()
|
|
.with_generated_tangents()
|
|
.unwrap(),
|
|
);
|
|
let sphere_mesh = meshes.add(
|
|
Sphere::new(1.0)
|
|
.mesh()
|
|
.build()
|
|
.with_generated_tangents()
|
|
.unwrap(),
|
|
);
|
|
|
|
commands
|
|
.spawn((
|
|
RaytracingMesh3d(plane_mesh.clone()),
|
|
MeshMaterial3d(
|
|
materials.add(StandardMaterial {
|
|
base_color_texture: Some(
|
|
asset_server
|
|
.load_builder()
|
|
.with_settings::<ImageLoaderSettings>(|settings| {
|
|
settings
|
|
.sampler
|
|
.get_or_init_descriptor()
|
|
.set_address_mode(ImageAddressMode::Repeat);
|
|
})
|
|
.load("textures/uv_checker_bw.png"),
|
|
),
|
|
perceptual_roughness: 0.0,
|
|
..default()
|
|
}),
|
|
),
|
|
))
|
|
.insert_if(Mesh3d(plane_mesh), || args.pathtracer != Some(true));
|
|
|
|
for _ in 0..8000 {
|
|
commands
|
|
.spawn((
|
|
RaytracingMesh3d(cube_mesh.clone()),
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
|
base_color: Color::srgb(rng.random(), rng.random(), rng.random()),
|
|
perceptual_roughness: rng.random(),
|
|
..default()
|
|
})),
|
|
Transform::default()
|
|
.with_scale(Vec3 {
|
|
x: rng.random_range(0.2..=2.0),
|
|
y: rng.random_range(0.2..=2.0),
|
|
z: rng.random_range(0.2..=2.0),
|
|
})
|
|
.with_translation(Vec3::new(
|
|
rng.random_range(-180.0..=180.0),
|
|
0.2,
|
|
rng.random_range(-180.0..=180.0),
|
|
)),
|
|
))
|
|
.insert_if(Mesh3d(cube_mesh.clone()), || args.pathtracer != Some(true));
|
|
}
|
|
|
|
for x in -10..=10 {
|
|
for y in -10..=10 {
|
|
commands
|
|
.spawn((
|
|
RaytracingMesh3d(sphere_mesh.clone()),
|
|
MeshMaterial3d(
|
|
materials.add(StandardMaterial {
|
|
emissive: Color::linear_rgb(
|
|
rng.random::<f32>() * 60000.0,
|
|
rng.random::<f32>() * 60000.0,
|
|
rng.random::<f32>() * 60000.0,
|
|
)
|
|
.into(),
|
|
..default()
|
|
}),
|
|
),
|
|
Transform::default().with_translation(Vec3::new(
|
|
(x * 20) as f32,
|
|
7.0,
|
|
(y * 20) as f32,
|
|
)),
|
|
))
|
|
.insert_if(Mesh3d(sphere_mesh.clone()), || {
|
|
args.pathtracer != Some(true)
|
|
});
|
|
}
|
|
}
|
|
|
|
let mut camera = commands.spawn((
|
|
Camera3d::default(),
|
|
Camera {
|
|
clear_color: ClearColorConfig::Custom(Color::BLACK),
|
|
..default()
|
|
},
|
|
FreeCamera {
|
|
walk_speed: 3.0,
|
|
run_speed: 10.0,
|
|
..Default::default()
|
|
},
|
|
Transform::from_translation(Vec3::new(6.11329, 166.74896, 451.8226)).with_rotation(
|
|
Quat::from_xyzw(-0.183938, 0.009093744, 0.0017017953, 0.9828943),
|
|
),
|
|
// Msaa::Off and CameraMainTextureUsages with STORAGE_BINDING are required for Solari
|
|
CameraMainTextureUsages::default().with(TextureUsages::STORAGE_BINDING),
|
|
Msaa::Off,
|
|
Bloom {
|
|
intensity: 0.1,
|
|
..Bloom::NATURAL
|
|
},
|
|
));
|
|
|
|
if args.pathtracer == Some(true) {
|
|
camera.insert(Pathtracer::default());
|
|
} else {
|
|
camera.insert(SolariLighting::default());
|
|
}
|
|
|
|
// Using DLSS Ray Reconstruction for denoising (and cheaper rendering via upscaling) is _highly_ recommended when using Solari
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
|
|
if dlss_rr_supported.is_some() {
|
|
camera.insert(Dlss::<DlssRayReconstructionFeature> {
|
|
perf_quality_mode: Default::default(),
|
|
reset: Default::default(),
|
|
_phantom_data: Default::default(),
|
|
});
|
|
}
|
|
|
|
commands.spawn((
|
|
Node {
|
|
position_type: PositionType::Absolute,
|
|
right: px(0.0),
|
|
padding: px(4.0).all(),
|
|
border_radius: BorderRadius::bottom_left(px(4.0)),
|
|
..default()
|
|
},
|
|
BackgroundColor(Color::srgba(0.10, 0.10, 0.10, 0.8)),
|
|
children![(
|
|
PerformanceText,
|
|
Text::default(),
|
|
TextFont {
|
|
font_size: FontSize::Px(8.0),
|
|
..default()
|
|
},
|
|
)],
|
|
));
|
|
}
|
|
|
|
fn add_raytracing_meshes_on_scene_load(
|
|
scene_ready: On<WorldInstanceReady>,
|
|
children: Query<&Children>,
|
|
mesh_query: Query<(
|
|
&Mesh3d,
|
|
&MeshMaterial3d<StandardMaterial>,
|
|
Option<&GltfMaterialName>,
|
|
)>,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
mut commands: Commands,
|
|
args: Res<Args>,
|
|
) {
|
|
for descendant in children.iter_descendants(scene_ready.entity) {
|
|
if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
|
|
mesh_query.get(descendant)
|
|
{
|
|
// Add raytracing mesh component
|
|
commands
|
|
.entity(descendant)
|
|
.insert(RaytracingMesh3d(mesh_handle.clone()));
|
|
|
|
// Ensure meshes are Solari compatible
|
|
let mut mesh = meshes.get_mut(mesh_handle).unwrap();
|
|
if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
|
|
let vertex_count = mesh.count_vertices();
|
|
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
|
|
mesh.insert_attribute(
|
|
Mesh::ATTRIBUTE_TANGENT,
|
|
vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
|
|
);
|
|
}
|
|
if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
|
|
mesh.generate_tangents().unwrap();
|
|
}
|
|
if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
|
|
mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
|
|
}
|
|
if let Some(indices) = mesh.indices_mut()
|
|
&& let Indices::U16(_) = indices
|
|
{
|
|
*indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
|
|
}
|
|
|
|
// Prevent rasterization if using pathtracer
|
|
if args.pathtracer == Some(true) {
|
|
commands.entity(descendant).remove::<Mesh3d>();
|
|
}
|
|
|
|
// Adjust scene materials to better demo Solari features
|
|
if material_name.map(|s| s.0.as_str()) == Some("material") {
|
|
let mut material = materials.get_mut(material_handle).unwrap();
|
|
material.emissive = LinearRgba::BLACK;
|
|
}
|
|
if material_name.map(|s| s.0.as_str()) == Some("Lights") {
|
|
let mut material = materials.get_mut(material_handle).unwrap();
|
|
material.emissive =
|
|
LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
|
|
material.alpha_mode = AlphaMode::Opaque;
|
|
material.specular_transmission = 0.0;
|
|
|
|
commands.insert_resource(RobotLightMaterial(material_handle.clone()));
|
|
}
|
|
if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
|
|
let mut material = materials.get_mut(material_handle).unwrap();
|
|
material.alpha_mode = AlphaMode::Opaque;
|
|
material.specular_transmission = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pause_scene(mut time: ResMut<Time<Virtual>>, key_input: Res<ButtonInput<KeyCode>>) {
|
|
if key_input.just_pressed(KeyCode::Space) {
|
|
time.toggle();
|
|
}
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
struct RobotLightMaterial(Handle<StandardMaterial>);
|
|
|
|
fn toggle_lights(
|
|
key_input: Res<ButtonInput<KeyCode>>,
|
|
robot_light_material: Option<Res<RobotLightMaterial>>,
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
directional_light: Query<Entity, With<DirectionalLight>>,
|
|
mut commands: Commands,
|
|
) {
|
|
if key_input.just_pressed(KeyCode::Digit1) {
|
|
if let Ok(directional_light) = directional_light.single() {
|
|
commands.entity(directional_light).despawn();
|
|
} else {
|
|
commands.spawn((
|
|
DirectionalLight {
|
|
illuminance: light_consts::lux::FULL_DAYLIGHT,
|
|
shadow_maps_enabled: false, // Solari replaces shadow mapping
|
|
..default()
|
|
},
|
|
Transform::from_rotation(Quat::from_xyzw(
|
|
-0.13334629,
|
|
-0.86597735,
|
|
-0.3586996,
|
|
0.3219264,
|
|
)),
|
|
));
|
|
}
|
|
}
|
|
|
|
if key_input.just_pressed(KeyCode::Digit2)
|
|
&& let Some(robot_light_material) = robot_light_material
|
|
{
|
|
let mut material = materials.get_mut(&robot_light_material.0).unwrap();
|
|
if material.emissive == LinearRgba::BLACK {
|
|
material.emissive = LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
|
|
} else {
|
|
material.emissive = LinearRgba::BLACK;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct PatrolPath {
|
|
path: Vec<(Vec3, Quat)>,
|
|
i: usize,
|
|
}
|
|
|
|
fn patrol_path(mut query: Query<(&mut PatrolPath, &mut Transform)>, time: Res<Time<Virtual>>) {
|
|
for (mut path, mut transform) in query.iter_mut() {
|
|
let (mut target_position, mut target_rotation) = path.path[path.i];
|
|
let mut distance_to_target = transform.translation.distance(target_position);
|
|
if distance_to_target < 0.01 {
|
|
transform.translation = target_position;
|
|
transform.rotation = target_rotation;
|
|
|
|
path.i = (path.i + 1) % path.path.len();
|
|
(target_position, target_rotation) = path.path[path.i];
|
|
distance_to_target = transform.translation.distance(target_position);
|
|
}
|
|
|
|
let direction = (target_position - transform.translation).normalize();
|
|
let movement = direction * time.delta_secs();
|
|
|
|
if movement.length() > distance_to_target {
|
|
transform.translation = target_position;
|
|
transform.rotation = target_rotation;
|
|
} else {
|
|
transform.translation += movement;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct ControlText;
|
|
|
|
fn update_control_text(
|
|
mut text: Single<&mut Text, With<ControlText>>,
|
|
robot_light_material: Option<Res<RobotLightMaterial>>,
|
|
materials: Res<Assets<StandardMaterial>>,
|
|
directional_light: Query<Entity, With<DirectionalLight>>,
|
|
time: Res<Time<Virtual>>,
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
|
|
Res<DlssRayReconstructionSupported>,
|
|
>,
|
|
) {
|
|
text.0.clear();
|
|
|
|
if time.is_paused() {
|
|
text.0.push_str("(Space): Resume");
|
|
} else {
|
|
text.0.push_str("(Space): Pause");
|
|
}
|
|
|
|
if directional_light.single().is_ok() {
|
|
text.0.push_str("\n(1): Disable directional light");
|
|
} else {
|
|
text.0.push_str("\n(1): Enable directional light");
|
|
}
|
|
|
|
match robot_light_material.and_then(|m| materials.get(&m.0)) {
|
|
Some(robot_light_material) if robot_light_material.emissive != LinearRgba::BLACK => {
|
|
text.0.push_str("\n(2): Disable robot emissive light");
|
|
}
|
|
_ => {
|
|
text.0.push_str("\n(2): Enable robot emissive light");
|
|
}
|
|
}
|
|
|
|
#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
|
|
if dlss_rr_supported.is_some() {
|
|
text.0
|
|
.push_str("\nDenoising: DLSS Ray Reconstruction enabled");
|
|
} else {
|
|
text.0
|
|
.push_str("\nDenoising: DLSS Ray Reconstruction not supported");
|
|
}
|
|
|
|
#[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))]
|
|
text.0
|
|
.push_str("\nDenoising: App not compiled with DLSS support");
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct PerformanceText;
|
|
|
|
fn update_performance_text(
|
|
mut text: Single<&mut Text, With<PerformanceText>>,
|
|
diagnostics: Res<DiagnosticsStore>,
|
|
) {
|
|
text.0.clear();
|
|
|
|
let mut total = 0.0;
|
|
let mut add_diagnostic = |name: &str, path: &'static str| {
|
|
let path = DiagnosticPath::new(path);
|
|
if let Some(average) = diagnostics.get(&path).and_then(Diagnostic::average) {
|
|
text.push_str(&format!("{name:17} {average:.2} ms\n"));
|
|
total += average;
|
|
}
|
|
};
|
|
|
|
(add_diagnostic)(
|
|
"Light tiles",
|
|
"render/solari_lighting/presample_light_tiles/elapsed_gpu",
|
|
);
|
|
(add_diagnostic)(
|
|
"World cache",
|
|
"render/solari_lighting/world_cache/elapsed_gpu",
|
|
);
|
|
(add_diagnostic)(
|
|
"Direct lighting",
|
|
"render/solari_lighting/direct_lighting/elapsed_gpu",
|
|
);
|
|
(add_diagnostic)(
|
|
"Diffuse indirect",
|
|
"render/solari_lighting/diffuse_indirect_lighting/elapsed_gpu",
|
|
);
|
|
(add_diagnostic)(
|
|
"Specular indirect",
|
|
"render/solari_lighting/specular_indirect_lighting/elapsed_gpu",
|
|
);
|
|
(add_diagnostic)("DLSS-RR", "render/dlss_ray_reconstruction/elapsed_gpu");
|
|
text.push_str(&format!("{:17} {total:.2} ms\n", "Total"));
|
|
|
|
if let Some(world_cache_active_cells_count) = diagnostics
|
|
.get(&DiagnosticPath::new(
|
|
"render/solari_lighting/world_cache_active_cells_count",
|
|
))
|
|
.and_then(Diagnostic::average)
|
|
{
|
|
text.push_str(&format!(
|
|
"\nWorld cache cells {} ({:.0}%)",
|
|
world_cache_active_cells_count as u32,
|
|
(world_cache_active_cells_count * 100.0) / (2u64.pow(20) as f64)
|
|
));
|
|
}
|
|
}
|