Improve Performance in Scenes With Many Lights #14
|
@ -996,6 +996,15 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fps_counter"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff23a4d90ba4b859f370ee3c12ca3b1ca80d8ee144b279e135f6852cdadd6dd6"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsevent-sys"
|
name = "fsevent-sys"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
|
@ -1950,6 +1959,18 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "many-lights"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-std",
|
||||||
|
"fps_counter",
|
||||||
|
"lyra-engine",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -10,11 +10,12 @@ members = [
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
"lyra-scripting",
|
"lyra-scripting",
|
||||||
"lyra-game", "lyra-math", "lyra-scene"]
|
"lyra-game", "lyra-math", "lyra-scene", "examples/many-lights"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
scripting = ["dep:lyra-scripting"]
|
scripting = ["dep:lyra-scripting"]
|
||||||
lua_scripting = ["scripting", "lyra-scripting/lua"]
|
lua_scripting = ["scripting", "lyra-scripting/lua"]
|
||||||
|
tracy = ["lyra-game/tracy"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-game = { path = "lyra-game" }
|
lyra-game = { path = "lyra-game" }
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "many-lights"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lyra-engine = { path = "../../", features = ["tracy"] }
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
async-std = "1.12.0"
|
||||||
|
tracing = "0.1.37"
|
||||||
|
rand = "0.8.5"
|
||||||
|
fps_counter = "3.0.0"
|
||||||
|
|
||||||
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
linker = "/usr/bin/clang"
|
||||||
|
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
|
@ -0,0 +1,189 @@
|
||||||
|
use lyra_engine::{assets::{gltf::Gltf, ResourceManager}, ecs::query::{Res, ResMut, View}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::light::{directional::DirectionalLight, PointLight}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime};
|
||||||
|
use rand::Rng;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
const MAX_POINT_LIGHT_RANGE: f32 = 1.0;
|
||||||
|
const MIN_POINT_LIGHT_RANGE: f32 = 0.5;
|
||||||
|
const POINT_LIGHT_MAX_INTENSITY: f32 = 1.0;
|
||||||
|
const POINT_LIGHT_MIN_INTENSITY: f32 = 0.3;
|
||||||
|
const POINT_LIGHT_CUBE_SCALE: f32 = 0.2;
|
||||||
|
const POINT_LIGHT_NUM: u32 = 500;
|
||||||
|
|
||||||
|
const POINT_LIGHT_MAX_X: f32 = 9.0;
|
||||||
|
const POINT_LIGHT_MIN_X: f32 = -9.0;
|
||||||
|
|
||||||
|
const POINT_LIGHT_MAX_Y: f32 = 3.0;
|
||||||
|
const POINT_LIGHT_MIN_Y: f32 = 0.5;
|
||||||
|
|
||||||
|
const POINT_LIGHT_MAX_Z: f32 = 4.0;
|
||||||
|
const POINT_LIGHT_MIN_Z: f32 = -5.0;
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
|
||||||
|
let action_handler_plugin = |game: &mut Game| {
|
||||||
|
let action_handler = ActionHandler::builder()
|
||||||
|
.add_layout(LayoutId::from(0))
|
||||||
|
|
||||||
|
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
||||||
|
.add_action("Debug", Action::new(ActionKind::Button))
|
||||||
|
|
||||||
|
.add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||||
|
.bind(ACTLBL_MOVE_FORWARD_BACKWARD, &[
|
||||||
|
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0)
|
||||||
|
])
|
||||||
|
.bind(ACTLBL_MOVE_LEFT_RIGHT, &[
|
||||||
|
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0)
|
||||||
|
])
|
||||||
|
.bind(ACTLBL_MOVE_UP_DOWN, &[
|
||||||
|
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0)
|
||||||
|
])
|
||||||
|
.bind(ACTLBL_LOOK_LEFT_RIGHT, &[
|
||||||
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
||||||
|
])
|
||||||
|
.bind(ACTLBL_LOOK_UP_DOWN, &[
|
||||||
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
||||||
|
])
|
||||||
|
.bind(ACTLBL_LOOK_ROLL, &[
|
||||||
|
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
||||||
|
])
|
||||||
|
.bind("Debug", &[
|
||||||
|
ActionSource::Keyboard(KeyCode::B).into_binding(),
|
||||||
|
])
|
||||||
|
.finish()
|
||||||
|
).finish();
|
||||||
|
|
||||||
|
let world = game.world_mut();
|
||||||
|
world.add_resource(action_handler);
|
||||||
|
game.with_plugin(InputActionPlugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
Game::initialize().await
|
||||||
|
.with_plugin(lyra_engine::DefaultPlugins)
|
||||||
|
.with_plugin(setup_scene_plugin)
|
||||||
|
.with_plugin(action_handler_plugin)
|
||||||
|
.with_plugin(camera_debug_plugin)
|
||||||
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_scene_plugin(game: &mut Game) {
|
||||||
|
let fps_counter = |mut counter: ResMut<fps_counter::FPSCounter>, delta: Res<DeltaTime>| -> anyhow::Result<()> {
|
||||||
|
let tick = counter.tick();
|
||||||
|
|
||||||
|
info!("FPS: {}, frame time: {}", tick, **delta);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
game.with_system("fps_counter", fps_counter, &[]);
|
||||||
|
|
||||||
|
let world = game.world_mut();
|
||||||
|
world.add_resource(fps_counter::FPSCounter::new());
|
||||||
|
|
||||||
|
let resman = world.get_resource_mut::<ResourceManager>();
|
||||||
|
let cube_gltf = resman.request::<Gltf>("../assets/texture-sep/texture-sep.gltf").unwrap();
|
||||||
|
|
||||||
|
cube_gltf.wait_recurse_dependencies_load();
|
||||||
|
let cube_mesh = &cube_gltf.data_ref()
|
||||||
|
.unwrap().meshes[0];
|
||||||
|
|
||||||
|
let sponza_model = resman.request::<Gltf>("../assets/sponza/Sponza.gltf").unwrap();
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
sponza_model.wait_recurse_dependencies_load();
|
||||||
|
let sponza_scene = &sponza_model.data_ref()
|
||||||
|
.unwrap().scenes[0];
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
sponza_scene.clone(),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
||||||
|
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||||
|
light_tran.rotate_x(math::Angle::Degrees(-45.0));
|
||||||
|
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
||||||
|
world.spawn((
|
||||||
|
DirectionalLight {
|
||||||
|
enabled: true,
|
||||||
|
color: Vec3::ONE,
|
||||||
|
intensity: 0.15
|
||||||
|
//..Default::default()
|
||||||
|
},
|
||||||
|
light_tran,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let x_range = POINT_LIGHT_MIN_X..POINT_LIGHT_MAX_X;
|
||||||
|
let y_range = POINT_LIGHT_MIN_Y..POINT_LIGHT_MAX_Y;
|
||||||
|
let z_range = POINT_LIGHT_MIN_Z..POINT_LIGHT_MAX_Z;
|
||||||
|
|
||||||
|
let mut rand = rand::thread_rng();
|
||||||
|
let mut rand_vec3 = || -> Vec3 {
|
||||||
|
let x = rand.gen_range(x_range.clone());
|
||||||
|
let y = rand.gen_range(y_range.clone());
|
||||||
|
let z = rand.gen_range(z_range.clone());
|
||||||
|
|
||||||
|
Vec3::new(x, y, z)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rand = rand::thread_rng();
|
||||||
|
for _ in 0..POINT_LIGHT_NUM {
|
||||||
|
let range = rand.gen_range(MIN_POINT_LIGHT_RANGE..MAX_POINT_LIGHT_RANGE);
|
||||||
|
let intensity = rand.gen_range(POINT_LIGHT_MIN_INTENSITY..POINT_LIGHT_MAX_INTENSITY);
|
||||||
|
let color = rand_vec3().normalize();
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
PointLight {
|
||||||
|
enabled: true,
|
||||||
|
color,
|
||||||
|
intensity,
|
||||||
|
range,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Transform::new(
|
||||||
|
rand_vec3(),
|
||||||
|
Quat::IDENTITY,
|
||||||
|
Vec3::new(POINT_LIGHT_CUBE_SCALE, POINT_LIGHT_CUBE_SCALE, POINT_LIGHT_CUBE_SCALE),
|
||||||
|
),
|
||||||
|
cube_mesh.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut camera = CameraComponent::new_3d();
|
||||||
|
// these values were taken by manually positioning the camera in the scene.
|
||||||
|
camera.transform = Transform::new(
|
||||||
|
Vec3::new(-10.0, 0.94, -0.28),
|
||||||
|
Quat::from_xyzw(0.03375484, -0.7116095, 0.0342693, 0.70092666),
|
||||||
|
Vec3::ONE
|
||||||
|
);
|
||||||
|
world.spawn(( camera, FreeFlyCamera::default() ));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn camera_debug_plugin(game: &mut Game) {
|
||||||
|
let sys = |handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
|
||||||
|
if handler.was_action_just_pressed("Debug") {
|
||||||
|
for mut cam in view.into_iter() {
|
||||||
|
cam.debug = !cam.debug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
game.with_system("camera_debug_trigger", sys, &[]);
|
||||||
|
}
|
|
@ -349,7 +349,7 @@ impl Game {
|
||||||
.with(fmt::layer().with_writer(stdout_layer));
|
.with(fmt::layer().with_writer(stdout_layer));
|
||||||
|
|
||||||
#[cfg(feature = "tracy")]
|
#[cfg(feature = "tracy")]
|
||||||
t.with(tracing_tracy::TracyLayer::default());
|
let t = t.with(tracing_tracy::TracyLayer::default());
|
||||||
|
|
||||||
t.with(filter::Targets::new()
|
t.with(filter::Targets::new()
|
||||||
// done by prefix, so it includes all lyra subpackages
|
// done by prefix, so it includes all lyra subpackages
|
||||||
|
|
|
@ -459,6 +459,10 @@ impl Renderer for BasicRenderer {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.transform_buffers.needs_expand() {
|
||||||
|
self.transform_buffers.expand_buffers(&self.device);
|
||||||
|
}
|
||||||
|
|
||||||
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
||||||
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
|
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
|
||||||
group, || ( interop_pos.calculate_mat4(), glam::Mat3::from_quat(interop_pos.rotation) ));
|
group, || ( interop_pos.calculate_mat4(), glam::Mat3::from_quat(interop_pos.rotation) ));
|
||||||
|
@ -492,6 +496,10 @@ impl Renderer for BasicRenderer {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.transform_buffers.needs_expand() {
|
||||||
|
self.transform_buffers.expand_buffers(&self.device);
|
||||||
|
}
|
||||||
|
|
||||||
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
||||||
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
||||||
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
|
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
|
||||||
|
|
|
@ -370,6 +370,13 @@ impl TransformBuffers {
|
||||||
pub fn buffer_offset(&self, transform_index: TransformIndex) -> u32 {
|
pub fn buffer_offset(&self, transform_index: TransformIndex) -> u32 {
|
||||||
Self::get_buffer_offset(&self.limits, transform_index)
|
Self::get_buffer_offset(&self.limits, transform_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean indicating if the buffers need to be expanded
|
||||||
|
pub fn needs_expand(&self) -> bool {
|
||||||
|
self.entries.last()
|
||||||
|
.map(|entry| entry.len >= self.max_transform_count)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{hash::{Hash, DefaultHasher, Hasher}, collections::{HashMap, HashSet, VecDeque}, ptr::NonNull, fmt::Debug};
|
use std::{hash::{Hash, DefaultHasher, Hasher}, collections::{HashMap, HashSet, VecDeque}, ptr::NonNull, fmt::Debug};
|
||||||
|
|
||||||
use lyra_ecs::{system::{GraphExecutor, GraphExecutorError, System}, World};
|
use lyra_ecs::{system::{GraphExecutor, GraphExecutorError, System}, World};
|
||||||
use tracing::info_span;
|
use tracing::{info_span, instrument};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum StagedExecutorError {
|
pub enum StagedExecutorError {
|
||||||
|
@ -112,6 +112,7 @@ impl StagedExecutor {
|
||||||
/// Execute the staged systems in order.
|
/// Execute the staged systems in order.
|
||||||
///
|
///
|
||||||
/// If `stop_on_error` is false but errors are encountered, those errors will be returned in a Vec.
|
/// If `stop_on_error` is false but errors are encountered, those errors will be returned in a Vec.
|
||||||
|
#[instrument(skip(self, world, stop_on_error))]
|
||||||
pub fn execute(&mut self, world: NonNull<World>, stop_on_error: bool) -> Result<Vec<StagedExecutorError>, StagedExecutorError> {
|
pub fn execute(&mut self, world: NonNull<World>, stop_on_error: bool) -> Result<Vec<StagedExecutorError>, StagedExecutorError> {
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
let mut visited = HashSet::new();
|
let mut visited = HashSet::new();
|
||||||
|
@ -120,13 +121,14 @@ impl StagedExecutor {
|
||||||
self.topological_sort(&mut stack, &mut visited, node)?;
|
self.topological_sort(&mut stack, &mut visited, node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stage_span = info_span!("stage_exec", stage=tracing::field::Empty);
|
//let stage_span = info_span!("stage_exec", stage=tracing::field::Empty);
|
||||||
|
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
while let Some(node) = stack.pop_front() {
|
while let Some(node) = stack.pop_front() {
|
||||||
let stage = self.stages.get_mut(&node).unwrap();
|
let stage = self.stages.get_mut(&node).unwrap();
|
||||||
|
|
||||||
stage_span.record("stage", stage.name.clone());
|
let stage_span = info_span!("stage_exec", stage=stage.name.clone());
|
||||||
|
//stage_span.record("stage", stage.name.clone());
|
||||||
let _e = stage_span.enter();
|
let _e = stage_span.enter();
|
||||||
|
|
||||||
if let Err(e) = stage.exec.execute(world, stop_on_error) {
|
if let Err(e) = stage.exec.execute(world, stop_on_error) {
|
||||||
|
|
Loading…
Reference in New Issue