Merge pull request 'Improve Performance in Scenes With Many Lights' (#14) from bugfix/many-lights-poor-performance into main
ci/woodpecker/push/debug Pipeline failed
Details
ci/woodpecker/push/debug Pipeline failed
Details
Reviewed-on: #14
This commit is contained in:
commit
6a11f7cbb7
|
@ -56,6 +56,15 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -938,6 +947,18 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixed-timestep-rotating-model"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-std",
|
||||||
|
"fps_counter",
|
||||||
|
"lyra-engine",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
|
@ -987,6 +1008,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"
|
||||||
|
@ -1087,6 +1117,19 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generator"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"rustversion",
|
||||||
|
"windows 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
|
@ -1747,6 +1790,19 @@ dependencies = [
|
||||||
"value-bag",
|
"value-bag",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loom"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e045d70ddfbc984eacfa964ded019534e8f6cbf36f6410aee0ed5cefa5a9175"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"generator",
|
||||||
|
"scoped-tls",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lyra-ecs"
|
name = "lyra-ecs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1798,12 +1854,15 @@ dependencies = [
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
"lyra-scene",
|
"lyra-scene",
|
||||||
"quote",
|
"quote",
|
||||||
|
"rustc-hash",
|
||||||
"syn 2.0.51",
|
"syn 2.0.51",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-log 0.1.4",
|
"tracing-log 0.1.4",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"tracing-tracy",
|
||||||
|
"unique",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
|
@ -1914,6 +1973,27 @@ 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]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata 0.1.10",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
|
@ -2655,6 +2735,50 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata 0.4.6",
|
||||||
|
"regex-syntax 0.8.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax 0.6.29",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax 0.8.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -2758,6 +2882,12 @@ dependencies = [
|
||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
|
@ -3355,14 +3485,49 @@ version = "0.3.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.2.0",
|
"tracing-log 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-tracy"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6024d04f84a69fd0d1dc1eee3a2b070bd246530a0582f9982ae487cb6c703614"
|
||||||
|
dependencies = [
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"tracy-client",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracy-client"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59fb931a64ff88984f86d3e9bcd1ae8843aa7fe44dd0f8097527bc172351741d"
|
||||||
|
dependencies = [
|
||||||
|
"loom",
|
||||||
|
"once_cell",
|
||||||
|
"tracy-client-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracy-client-sys"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d104d610dfa9dd154535102cc9c6164ae1fa37842bc2d9e83f9ac82b0ae0882"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
@ -3414,6 +3579,12 @@ version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unique"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d360722e1f3884f5b14d332185f02ff111f771f0c76a313268fe6af1409aba96"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -3803,6 +3974,15 @@ dependencies = [
|
||||||
"windows-targets 0.42.2",
|
"windows-targets 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.52.0"
|
version = "0.52.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", "examples/fixed-timestep-rotating-model"]
|
||||||
|
|
||||||
[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,22 @@
|
||||||
|
[package]
|
||||||
|
name = "fixed-timestep-rotating-model"
|
||||||
|
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.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
|
@ -0,0 +1,241 @@
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use lyra_engine::{
|
||||||
|
assets::{gltf::Gltf, ResourceManager},
|
||||||
|
ecs::{
|
||||||
|
query::{Res, ResMut, View},
|
||||||
|
system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem},
|
||||||
|
World,
|
||||||
|
},
|
||||||
|
game::Game,
|
||||||
|
input::{
|
||||||
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
|
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||||
|
},
|
||||||
|
math::{self, Transform, Vec3},
|
||||||
|
render::light::directional::DirectionalLight,
|
||||||
|
scene::{
|
||||||
|
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||||
|
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 tracing::info;
|
||||||
|
|
||||||
|
#[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 world = game.world_mut();
|
||||||
|
let resman = world.get_resource_mut::<ResourceManager>();
|
||||||
|
let camera_gltf = resman
|
||||||
|
.request::<Gltf>("../assets/AntiqueCamera.glb")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
camera_gltf.wait_recurse_dependencies_load();
|
||||||
|
let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0];
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
camera_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, -5.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 mut camera = CameraComponent::new_3d();
|
||||||
|
camera.transform.translation += math::Vec3::new(0.0, 0.0, 1.5);
|
||||||
|
world.spawn((camera, FreeFlyCamera::default()));
|
||||||
|
|
||||||
|
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(())
|
||||||
|
};
|
||||||
|
|
||||||
|
world.add_resource(fps_counter::FPSCounter::new());
|
||||||
|
|
||||||
|
let rotate_system = |dt: Res<DeltaTime>, view: View<&mut Transform>| -> anyhow::Result<()> {
|
||||||
|
const SPEED: f32 = 4.0;
|
||||||
|
let dt = **dt;
|
||||||
|
|
||||||
|
for mut transform in view.iter() {
|
||||||
|
info!("rotation: {:?}", transform.rotation);
|
||||||
|
transform.rotate_y(math::Angle::Degrees(SPEED * dt));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sys = BatchedSystem::new();
|
||||||
|
sys.with_criteria(FixedTimestep::new(60));
|
||||||
|
sys.with_system(rotate_system.into_system());
|
||||||
|
sys.with_system(fps_counter.into_system());
|
||||||
|
|
||||||
|
game.with_system("fixed_timestep", sys, &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FixedTimestep {
|
||||||
|
max_tps: u32,
|
||||||
|
fixed_time: f32,
|
||||||
|
accumulator: f32,
|
||||||
|
old_dt: Option<DeltaTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl FixedTimestep {
|
||||||
|
pub fn new(max_tps: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
max_tps,
|
||||||
|
fixed_time: Self::calc_fixed_time(max_tps),
|
||||||
|
accumulator: 0.0,
|
||||||
|
old_dt: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_fixed_time(max_tps: u32) -> f32 {
|
||||||
|
1.0 / max_tps as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_tps(&mut self, tps: u32) {
|
||||||
|
self.max_tps = tps;
|
||||||
|
self.fixed_time = Self::calc_fixed_time(tps);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tps(&self) -> u32 {
|
||||||
|
self.max_tps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixed_time(&self) -> f32 {
|
||||||
|
self.fixed_time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Criteria for FixedTimestep {
|
||||||
|
fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
|
||||||
|
let world = unsafe { world.as_mut() };
|
||||||
|
if check_count == 0 {
|
||||||
|
let delta_time = world.get_resource::<DeltaTime>();
|
||||||
|
self.accumulator += **delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.accumulator >= self.fixed_time {
|
||||||
|
self.accumulator -= self.fixed_time;
|
||||||
|
return CriteriaSchedule::YesAndLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
CriteriaSchedule::No
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modify_world(&mut self, mut world: NonNull<World>) {
|
||||||
|
let world = unsafe { world.as_mut() };
|
||||||
|
self.old_dt = world.try_get_resource().map(|r| *r);
|
||||||
|
|
||||||
|
world.add_resource(DeltaTime::from(self.fixed_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn undo_world_modifications(&mut self, mut world: NonNull<World>) {
|
||||||
|
let world = unsafe { world.as_mut() };
|
||||||
|
world.add_resource(
|
||||||
|
self.old_dt
|
||||||
|
.expect("DeltaTime resource was somehow never got from the world"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
[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.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[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, &[]);
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ struct FixedTimestep {
|
||||||
max_tps: u32,
|
max_tps: u32,
|
||||||
fixed_time: f32,
|
fixed_time: f32,
|
||||||
accumulator: f32,
|
accumulator: f32,
|
||||||
|
old_dt: Option<DeltaTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -16,6 +17,7 @@ impl FixedTimestep {
|
||||||
max_tps,
|
max_tps,
|
||||||
fixed_time: Self::calc_fixed_time(max_tps),
|
fixed_time: Self::calc_fixed_time(max_tps),
|
||||||
accumulator: 0.0,
|
accumulator: 0.0,
|
||||||
|
old_dt: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +54,21 @@ impl Criteria for FixedTimestep {
|
||||||
|
|
||||||
CriteriaSchedule::No
|
CriteriaSchedule::No
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn modify_world(&mut self, mut world: NonNull<World>) {
|
||||||
|
let world = unsafe { world.as_mut() };
|
||||||
|
self.old_dt = world.try_get_resource().map(|r| *r);
|
||||||
|
|
||||||
|
world.add_resource(DeltaTime::from(self.fixed_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn undo_world_modifications(&mut self, mut world: NonNull<World>) {
|
||||||
|
let world = unsafe { world.as_mut() };
|
||||||
|
world.add_resource(
|
||||||
|
self.old_dt
|
||||||
|
.expect("DeltaTime resource was somehow never got from the world"),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{query::{AsQuery, Query}, Archetype, World};
|
||||||
/// This means that entities that `Q` fetches are skipped, and entities that
|
/// This means that entities that `Q` fetches are skipped, and entities that
|
||||||
/// `Q` does not fetch are not skipped.
|
/// `Q` does not fetch are not skipped.
|
||||||
///
|
///
|
||||||
/// ```rust,nobuild
|
/// ```nobuild
|
||||||
/// // Iterate over entities that has a transform, and are not the origin of a `ChildOf` relationship.
|
/// // Iterate over entities that has a transform, and are not the origin of a `ChildOf` relationship.
|
||||||
/// for (en, pos, _) in world
|
/// for (en, pos, _) in world
|
||||||
/// .view::<(Entities, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>()
|
/// .view::<(Entities, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>()
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl<'a, Q1: Query, Q2: Query> Fetch<'a> for OrFetch<'a, Q1, Q2> {
|
||||||
///
|
///
|
||||||
/// This checks if `Q1` can fetch before checking `Q2`.
|
/// This checks if `Q1` can fetch before checking `Q2`.
|
||||||
///
|
///
|
||||||
/// ```rust,nobuild
|
/// ```nobuild
|
||||||
/// for (en, pos, _) in world
|
/// for (en, pos, _) in world
|
||||||
/// .view::<(Entities, &Transform, Or<Has<Mesh>, Has<Scene>>)>()
|
/// .view::<(Entities, &Transform, Or<Has<Mesh>, Has<Scene>>)>()
|
||||||
/// .iter()
|
/// .iter()
|
||||||
|
|
|
@ -27,6 +27,10 @@ mod world;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
|
||||||
|
mod optional;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use optional::*;
|
||||||
|
|
||||||
pub mod dynamic;
|
pub mod dynamic;
|
||||||
|
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
use crate::{Archetype, World};
|
||||||
|
|
||||||
|
use super::{AsQuery, Fetch, Query};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct OptionalFetcher<'a, Q: AsQuery> {
|
||||||
|
fetcher: Option<<Q::Query as Query>::Fetch<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Q: AsQuery> Fetch<'a> for OptionalFetcher<'a, Q> {
|
||||||
|
type Item = Option<<Q::Query as Query>::Item<'a>>;
|
||||||
|
|
||||||
|
fn dangling() -> Self {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item {
|
||||||
|
self.fetcher.as_mut()
|
||||||
|
.map(|f| f.get_item(entity))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_item(&mut self, entity: crate::ArchetypeEntityId) -> bool {
|
||||||
|
self.fetcher.as_mut()
|
||||||
|
.map(|f| f.can_visit_item(entity))
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Optional<Q: AsQuery> {
|
||||||
|
query: Q::Query,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: AsQuery> Copy for Optional<Q> { }
|
||||||
|
|
||||||
|
impl<Q: AsQuery> Clone for Optional<Q> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self { query: self.query.clone() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: AsQuery> Query for Optional<Q> {
|
||||||
|
type Item<'a> = Option<<Q::Query as Query>::Item<'a>>;
|
||||||
|
|
||||||
|
type Fetch<'a> = OptionalFetcher<'a, Q>;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Optional {
|
||||||
|
query: Q::Query::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, _: &Archetype) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, world: &'a World, arch: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||||
|
let fetcher = if self.query.can_visit_archetype(arch) {
|
||||||
|
Some(self.query.fetch(world, arch, tick))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
OptionalFetcher {
|
||||||
|
fetcher,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: AsQuery> AsQuery for Optional<Q> {
|
||||||
|
type Query = Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: AsQuery> AsQuery for Option<Q> {
|
||||||
|
type Query = Optional<Q>;
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use tracing::debug_span;
|
use tracing::{debug_span, instrument};
|
||||||
|
|
||||||
use crate::Access;
|
use crate::Access;
|
||||||
|
|
||||||
use super::{System, Criteria, IntoSystem};
|
use super::{Criteria, GraphExecutorError, IntoSystem, System};
|
||||||
|
|
||||||
/// A system that executes a batch of systems in order that they were given.
|
/// A system that executes a batch of systems in order that they were given.
|
||||||
/// You can optionally add criteria that must pass before the systems are
|
/// You can optionally add criteria that must pass before the systems are
|
||||||
|
@ -13,6 +13,7 @@ pub struct BatchedSystem {
|
||||||
systems: Vec<Box<dyn System>>,
|
systems: Vec<Box<dyn System>>,
|
||||||
criteria: Vec<Box<dyn Criteria>>,
|
criteria: Vec<Box<dyn Criteria>>,
|
||||||
criteria_checks: u32,
|
criteria_checks: u32,
|
||||||
|
did_run: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatchedSystem {
|
impl BatchedSystem {
|
||||||
|
@ -47,6 +48,7 @@ impl System for BatchedSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, world))]
|
||||||
fn execute(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
fn execute(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||||
let mut can_run = true;
|
let mut can_run = true;
|
||||||
let mut check_again = false;
|
let mut check_again = false;
|
||||||
|
@ -69,13 +71,26 @@ impl System for BatchedSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_run {
|
if can_run {
|
||||||
|
for criteria in self.criteria.iter_mut() {
|
||||||
|
criteria.modify_world(world);
|
||||||
|
}
|
||||||
|
|
||||||
for (idx, system) in self.systems.iter_mut().enumerate() {
|
for (idx, system) in self.systems.iter_mut().enumerate() {
|
||||||
let sys_span = debug_span!("batch", system=tracing::field::Empty);
|
let span = debug_span!("batch", system=idx);
|
||||||
sys_span.record("system", idx);
|
let _e = span.enter();
|
||||||
let _e = sys_span.enter();
|
|
||||||
|
|
||||||
system.execute(world)?;
|
system.execute(world)?;
|
||||||
|
|
||||||
|
/* let deferred_span = debug_span!("deferred_exec");
|
||||||
|
let _e = deferred_span.enter();
|
||||||
|
|
||||||
|
if let Err(e) = system.execute_deferred(world)
|
||||||
|
.map_err(|e| GraphExecutorError::Command(e)) {
|
||||||
|
return Err(e.into());
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.did_run = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if check_again {
|
if check_again {
|
||||||
|
@ -88,8 +103,25 @@ impl System for BatchedSystem {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_deferred(&mut self, _: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
#[instrument(skip(self, world))]
|
||||||
todo!()
|
fn execute_deferred(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||||
|
if self.did_run {
|
||||||
|
for (idx, system) in self.systems.iter_mut().enumerate() {
|
||||||
|
let span = debug_span!("batch", system=idx);
|
||||||
|
let _e = span.enter();
|
||||||
|
|
||||||
|
system.execute_deferred(world)
|
||||||
|
.map_err(|e| GraphExecutorError::Command(e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for criteria in self.criteria.iter_mut() {
|
||||||
|
criteria.undo_world_modifications(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.did_run = false;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,17 @@ pub trait Criteria {
|
||||||
/// * `world` - The ecs world.
|
/// * `world` - The ecs world.
|
||||||
/// * `check_count` - The amount of times the Criteria has been checked this tick.
|
/// * `check_count` - The amount of times the Criteria has been checked this tick.
|
||||||
fn can_run(&mut self, world: NonNull<World>, check_count: u32) -> CriteriaSchedule;
|
fn can_run(&mut self, world: NonNull<World>, check_count: u32) -> CriteriaSchedule;
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> Criteria for F
|
/// Modify the world after the [`Criteria`] in the system batch allows the systems to run.
|
||||||
where F: FnMut(&mut World, u32) -> CriteriaSchedule
|
///
|
||||||
{
|
/// This can be great if this Criteria limits the execution of systems based off of resources.
|
||||||
fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
|
/// A `FixedTimestep` criteria would use this to replace the [`DeltaTime`] resource in the
|
||||||
let world_mut = unsafe { world.as_mut() };
|
/// world to match the timestep time.
|
||||||
self(world_mut, check_count)
|
fn modify_world(&mut self, world: NonNull<World>);
|
||||||
}
|
|
||||||
|
/// Undo modifications to the world after the systems in the batch have been executed.
|
||||||
|
///
|
||||||
|
/// The `FixedTimestep` criteria (see docs for [`Criteria::modify_world`]) uses this
|
||||||
|
/// to replace the [`DeltaTime`] resource with its original value before it was replaced.
|
||||||
|
fn undo_world_modifications(&mut self, world: NonNull<World>);
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
|
use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
|
||||||
|
|
||||||
use tracing::{debug_span, info_span};
|
use tracing::{debug_span, info_span, instrument};
|
||||||
|
|
||||||
use super::System;
|
use super::System;
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ impl GraphExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the systems in the graph
|
/// Executes the systems in the graph
|
||||||
|
#[instrument(skip(self, world_ptr, stop_on_error))]
|
||||||
pub fn execute(&mut self, mut world_ptr: NonNull<World>, stop_on_error: bool)
|
pub fn execute(&mut self, mut world_ptr: NonNull<World>, stop_on_error: bool)
|
||||||
-> Result<Vec<GraphExecutorError>, GraphExecutorError> {
|
-> Result<Vec<GraphExecutorError>, GraphExecutorError> {
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
|
@ -71,13 +72,11 @@ impl GraphExecutor {
|
||||||
|
|
||||||
let mut possible_errors = Vec::new();
|
let mut possible_errors = Vec::new();
|
||||||
|
|
||||||
let sys_span = info_span!("graph_exec", system=tracing::field::Empty);
|
|
||||||
|
|
||||||
while let Some(node) = stack.pop_front() {
|
while let Some(node) = stack.pop_front() {
|
||||||
let system = self.systems.get_mut(node.as_str()).unwrap();
|
let system = self.systems.get_mut(node.as_str()).unwrap();
|
||||||
|
|
||||||
sys_span.record("system", system.name.clone());
|
let span = info_span!("graph_exec", system=system.name.clone());
|
||||||
let _e = sys_span.enter();
|
let _e = span.enter();
|
||||||
|
|
||||||
if let Err(e) = system.system.execute(world_ptr)
|
if let Err(e) = system.system.execute(world_ptr)
|
||||||
.map_err(|e| GraphExecutorError::SystemError(node, e)) {
|
.map_err(|e| GraphExecutorError::SystemError(node, e)) {
|
||||||
|
|
|
@ -128,22 +128,39 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a bundle into an existing entity. If the components are already existing on the
|
/// Insert a component bundle into an existing entity.
|
||||||
/// entity, they will be updated, else the entity will be moved to a different Archetype
|
///
|
||||||
/// that can store the entity. That may involve creating a new Archetype.
|
/// If the components are already existing on the entity, they will be updated, else the
|
||||||
|
/// entity will be moved to a different Archetype that can store the entity. That may
|
||||||
|
/// involve creating a new Archetype.
|
||||||
pub fn insert<B>(&mut self, entity: Entity, bundle: B)
|
pub fn insert<B>(&mut self, entity: Entity, bundle: B)
|
||||||
where
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
// TODO: If the entity already has the components in `bundle`, update the values of the
|
|
||||||
// components with the bundle.
|
|
||||||
|
|
||||||
let tick = self.tick();
|
let tick = self.tick();
|
||||||
|
|
||||||
let record = self.entities.entity_record(entity).unwrap();
|
let record = self.entities.entity_record(entity).unwrap();
|
||||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||||
let current_arch_len = current_arch.len();
|
let current_arch_len = current_arch.len();
|
||||||
|
|
||||||
|
let mut contains_all = true;
|
||||||
|
for id in bundle.type_ids() {
|
||||||
|
contains_all = contains_all && current_arch.get_column(id).is_some();
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains_all {
|
||||||
|
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
||||||
|
let entry_idx = *current_arch.entity_indexes()
|
||||||
|
.get(&entity).unwrap();
|
||||||
|
|
||||||
|
bundle.take(|ptr, id, _info| {
|
||||||
|
let col = current_arch.get_column_mut(id).unwrap();
|
||||||
|
unsafe { col.set_at(entry_idx.0 as _, ptr, tick) };
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// contains the type ids for the old component columns + the ids for the new components
|
// contains the type ids for the old component columns + the ids for the new components
|
||||||
let mut combined_column_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
|
let mut combined_column_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
|
||||||
combined_column_types.extend(bundle.type_ids());
|
combined_column_types.extend(bundle.type_ids());
|
||||||
|
@ -227,7 +244,7 @@ impl World {
|
||||||
/// A method used for debugging implementation details of the ECS.
|
/// A method used for debugging implementation details of the ECS.
|
||||||
///
|
///
|
||||||
/// Here's an example of the output:
|
/// Here's an example of the output:
|
||||||
/// ```
|
/// ```nobuild
|
||||||
/// Entities
|
/// Entities
|
||||||
/// 1 in archetype 0 at 0
|
/// 1 in archetype 0 at 0
|
||||||
/// 0 in archetype 1 at 0
|
/// 0 in archetype 1 at 0
|
||||||
|
@ -271,7 +288,7 @@ impl World {
|
||||||
/// the contents of entities inside the archetypes.
|
/// the contents of entities inside the archetypes.
|
||||||
///
|
///
|
||||||
/// Below is a template of the output:
|
/// Below is a template of the output:
|
||||||
/// ```
|
/// ```nobuild
|
||||||
/// Entities
|
/// Entities
|
||||||
/// %ENTITY_ID% in archetype %ARCHETYPE_ID% at %INDEX%
|
/// %ENTITY_ID% in archetype %ARCHETYPE_ID% at %INDEX%
|
||||||
/// Arch ID -- %ARCHETYPE_LEN% entities
|
/// Arch ID -- %ARCHETYPE_LEN% entities
|
||||||
|
@ -618,8 +635,6 @@ mod tests {
|
||||||
|
|
||||||
insert_and_assert(&mut world, e1, v2s[0], v3s[0]);
|
insert_and_assert(&mut world, e1, v2s[0], v3s[0]);
|
||||||
println!("Entity 1 is good");
|
println!("Entity 1 is good");
|
||||||
assert_eq!(world.archetypes.len(), 1);
|
|
||||||
println!("Empty archetype was removed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -655,4 +670,21 @@ mod tests {
|
||||||
assert!(tick >= world_tick);
|
assert!(tick >= world_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests replacing components using World::insert
|
||||||
|
#[test]
|
||||||
|
fn entity_insert_replace() {
|
||||||
|
let mut world = World::new();
|
||||||
|
let first = world.spawn((Vec2::new(10.0, 10.0),));
|
||||||
|
let second = world.spawn((Vec2::new(5.0, 5.0),));
|
||||||
|
|
||||||
|
world.insert(first, Vec2::new(50.0, 50.0));
|
||||||
|
|
||||||
|
let pos = world.view_one::<&mut Vec2>(first).get().unwrap();
|
||||||
|
assert_eq!(*pos, Vec2::new(50.0, 50.0));
|
||||||
|
drop(pos);
|
||||||
|
|
||||||
|
let pos = world.view_one::<&mut Vec2>(second).get().unwrap();
|
||||||
|
assert_eq!(*pos, Vec2::new(5.0, 5.0));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -11,14 +11,17 @@ lyra-math = { path = "../lyra-math" }
|
||||||
lyra-scene = { path = "../lyra-scene" }
|
lyra-scene = { path = "../lyra-scene" }
|
||||||
|
|
||||||
winit = "0.28.1"
|
winit = "0.28.1"
|
||||||
|
wgpu = "0.15.1"
|
||||||
|
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
||||||
tracing-log = "0.1.3"
|
tracing-log = "0.1.3"
|
||||||
tracing-appender = "0.2.2"
|
tracing-appender = "0.2.2"
|
||||||
wgpu = "0.15.1"
|
tracing-tracy = { version = "0.11.0", optional = true }
|
||||||
|
|
||||||
async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] }
|
async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] }
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
bytemuck = { version = "1.12", features = [ "derive" ] }
|
bytemuck = { version = "1.12", features = [ "derive", "min_const_generics" ] }
|
||||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
|
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
instant = "0.1"
|
instant = "0.1"
|
||||||
|
@ -30,3 +33,8 @@ quote = "1.0.29"
|
||||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
|
unique = "0.9.1"
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
tracy = ["dep:tracing-tracy"]
|
||||||
|
|
|
@ -4,9 +4,15 @@ use lyra_reflect::Reflect;
|
||||||
|
|
||||||
use crate::{plugin::Plugin, game::GameStages};
|
use crate::{plugin::Plugin, game::GameStages};
|
||||||
|
|
||||||
#[derive(Clone, Component, Default, Reflect)]
|
#[derive(Clone, Copy, Component, Default, Reflect)]
|
||||||
pub struct DeltaTime(f32, #[reflect(skip)] Option<Instant>);
|
pub struct DeltaTime(f32, #[reflect(skip)] Option<Instant>);
|
||||||
|
|
||||||
|
impl From<f32> for DeltaTime {
|
||||||
|
fn from(value: f32) -> Self {
|
||||||
|
DeltaTime(value, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for DeltaTime {
|
impl std::ops::Deref for DeltaTime {
|
||||||
type Target = f32;
|
type Target = f32;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{sync::Arc, collections::VecDeque, ptr::NonNull};
|
||||||
use async_std::task::block_on;
|
use async_std::task::block_on;
|
||||||
|
|
||||||
use lyra_ecs::{World, system::{System, IntoSystem}};
|
use lyra_ecs::{World, system::{System, IntoSystem}};
|
||||||
use tracing::{info, error, Level};
|
use tracing::{error, info, Level};
|
||||||
use tracing_appender::non_blocking;
|
use tracing_appender::non_blocking;
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
layer::SubscriberExt,
|
layer::SubscriberExt,
|
||||||
|
@ -344,15 +344,22 @@ impl Game {
|
||||||
pub async fn run(&mut self) {
|
pub async fn run(&mut self) {
|
||||||
// init logging
|
// init logging
|
||||||
let (stdout_layer, _stdout_nb) = non_blocking(std::io::stdout());
|
let (stdout_layer, _stdout_nb) = non_blocking(std::io::stdout());
|
||||||
tracing_subscriber::registry()
|
{
|
||||||
.with(fmt::layer().with_writer(stdout_layer))
|
let t = tracing_subscriber::registry()
|
||||||
.with(filter::Targets::new()
|
.with(fmt::layer().with_writer(stdout_layer));
|
||||||
|
|
||||||
|
#[cfg(feature = "tracy")]
|
||||||
|
let t = t.with(tracing_tracy::TracyLayer::default());
|
||||||
|
|
||||||
|
t.with(filter::Targets::new()
|
||||||
// done by prefix, so it includes all lyra subpackages
|
// done by prefix, so it includes all lyra subpackages
|
||||||
.with_target("lyra", Level::DEBUG)
|
.with_target("lyra", Level::DEBUG)
|
||||||
.with_target("wgpu", Level::WARN)
|
.with_target("wgpu", Level::WARN)
|
||||||
.with_target("winit", Level::DEBUG)
|
.with_target("winit", Level::DEBUG)
|
||||||
.with_default(Level::INFO))
|
.with_default(Level::INFO))
|
||||||
.init();
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let world = self.world.take().unwrap_or_default();
|
let world = self.world.take().unwrap_or_default();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,292 @@
|
||||||
|
use std::{alloc::Layout, cmp, marker::PhantomData, mem};
|
||||||
|
|
||||||
|
use std::{alloc, ptr};
|
||||||
|
use unique::Unique;
|
||||||
|
|
||||||
|
/// A [`Vec`] with its elements aligned to a runtime alignment value.
|
||||||
|
pub struct AVec<T> {
|
||||||
|
buf: Unique<u8>,
|
||||||
|
cap: usize,
|
||||||
|
len: usize,
|
||||||
|
align: usize,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AVec<T> {
|
||||||
|
// Tiny Vecs are dumb. Skip to:
|
||||||
|
// - 8 if the element size is 1, because any heap allocators are likely
|
||||||
|
// to round up a request of less than 8 bytes to at least 8 bytes.
|
||||||
|
// - 4 if elements are moderate-sized (<= 1 KiB).
|
||||||
|
// - 1 otherwise, to avoid wasting too much space for very short Vecs.
|
||||||
|
//
|
||||||
|
// Taken from Rust's standard library RawVec
|
||||||
|
pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::<T>() == 1 {
|
||||||
|
8
|
||||||
|
} else if mem::size_of::<T>() <= 1024 {
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn new(alignment: usize) -> Self {
|
||||||
|
debug_assert!(mem::size_of::<T>() > 0, "ZSTs not yet supported");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buf: Unique::dangling(),
|
||||||
|
cap: 0,
|
||||||
|
len: 0,
|
||||||
|
align: alignment,
|
||||||
|
_marker: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new, empty `AVec` with at least the specified capacity.
|
||||||
|
///
|
||||||
|
/// The aligned vector will be able to hold at least `capacity` elements without reallocating.
|
||||||
|
/// This method may allocate for more elements than `capacity`. If `capacity` is zero,
|
||||||
|
/// the vector will not allocate.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn with_capacity(alignment: usize, capacity: usize) -> Self {
|
||||||
|
let mut s = Self::new(alignment);
|
||||||
|
|
||||||
|
if capacity > 0 {
|
||||||
|
unsafe {
|
||||||
|
s.grow_amortized(0, capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the size of the 'slot' for a single **aligned** item.
|
||||||
|
#[inline(always)]
|
||||||
|
fn slot_size(&self) -> usize {
|
||||||
|
let a = self.align - 1;
|
||||||
|
mem::align_of::<T>() + (a) & !a
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
unsafe fn grow_amortized(&mut self, len: usize, additional: usize) {
|
||||||
|
debug_assert!(additional > 0);
|
||||||
|
|
||||||
|
let required_cap = len.checked_add(additional)
|
||||||
|
.expect("Capacity overflow");
|
||||||
|
|
||||||
|
let cap = cmp::max(self.cap * 2, required_cap);
|
||||||
|
let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
|
||||||
|
|
||||||
|
let new_layout = Layout::from_size_align_unchecked(cap * self.slot_size(), self.align);
|
||||||
|
|
||||||
|
let ptr = alloc::alloc(new_layout);
|
||||||
|
self.buf = Unique::new_unchecked(ptr);
|
||||||
|
self.cap = cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
unsafe fn grow_exact(&mut self, len: usize, additional: usize) {
|
||||||
|
debug_assert!(additional > 0);
|
||||||
|
|
||||||
|
let cap = len.checked_add(additional)
|
||||||
|
.expect("Capacity overflow");
|
||||||
|
|
||||||
|
let new_layout = Layout::from_size_align_unchecked(cap * self.slot_size(), self.align);
|
||||||
|
|
||||||
|
let ptr = alloc::alloc(new_layout);
|
||||||
|
self.buf = Unique::new_unchecked(ptr);
|
||||||
|
self.cap = cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reserves capacity for at least `additional` more elements.
|
||||||
|
///
|
||||||
|
/// The collection may reserve more space to speculatively avoid frequent reallocations.
|
||||||
|
/// After calling `reserve`, capacity will be greater than or equal to
|
||||||
|
/// `self.len() + additional`. Does nothing if capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
|
debug_assert!(additional > 0);
|
||||||
|
|
||||||
|
let remaining = self.capacity().wrapping_sub(self.len);
|
||||||
|
|
||||||
|
if additional > remaining {
|
||||||
|
unsafe { self.grow_amortized(self.len, additional) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reserves capacity for `additional` more elements.
|
||||||
|
///
|
||||||
|
/// Unlike [`reserve`], this will not over-allocate to speculatively avoid frequent
|
||||||
|
/// reallocations. After calling `reserve_exact`, capacity will be equal to
|
||||||
|
/// `self.len() + additional`. Does nothing if the capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// Prefer [`reserve`] if future insertions are expected.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn reserve_exact(&mut self, additional: usize) {
|
||||||
|
let remaining = self.capacity().wrapping_sub(self.len);
|
||||||
|
|
||||||
|
if additional > remaining {
|
||||||
|
unsafe { self.grow_exact(self.len, additional) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends an element to the back of the collection.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn push(&mut self, val: T) {
|
||||||
|
if self.len == self.cap {
|
||||||
|
self.reserve(self.slot_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// SAFETY: the length is ensured to be less than the capacity.
|
||||||
|
self.set_at_unchecked(self.len, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets an element at position `idx` within the vector to `val`.
|
||||||
|
///
|
||||||
|
/// # Unsafe
|
||||||
|
///
|
||||||
|
/// If `self.len > idx`, bytes past the length of the vector will be written to, potentially
|
||||||
|
/// also writing past the capacity of the vector.
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn set_at_unchecked(&mut self, idx: usize, val: T) {
|
||||||
|
let ptr = self.buf
|
||||||
|
.as_ptr()
|
||||||
|
.add(idx * self.slot_size());
|
||||||
|
|
||||||
|
std::ptr::write(ptr.cast::<T>(), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets an element at position `idx` within the vector to `val`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `idx >= self.len`.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_at(&mut self, idx: usize, val: T) {
|
||||||
|
assert!(self.len > idx);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.set_at_unchecked(idx, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortens the vector, keeping the first `len` elements and dropping the rest.
|
||||||
|
///
|
||||||
|
/// If `len` is greater or equal to the vector’s current length, this has no effect.
|
||||||
|
#[inline]
|
||||||
|
pub fn truncate(&mut self, len: usize) {
|
||||||
|
if len > self.len {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// drop each element past the new length
|
||||||
|
for i in len..self.len {
|
||||||
|
let ptr = self.buf.as_ptr()
|
||||||
|
.add(i * self.slot_size())
|
||||||
|
.cast::<T>();
|
||||||
|
|
||||||
|
ptr::drop_in_place(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_ptr(&self) -> *const u8 {
|
||||||
|
self.buf.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_mut_ptr(&self) -> *mut u8 {
|
||||||
|
self.buf.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the alignment of the elements in the vector.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn align(&self) -> usize {
|
||||||
|
self.align
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length of the vector.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the capacity of the vector.
|
||||||
|
///
|
||||||
|
/// The capacity is the amount of elements that the vector can store without reallocating.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn capacity(&self) -> usize {
|
||||||
|
self.cap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> AVec<T> {
|
||||||
|
/// Resized the `AVec` in-place so that `len` is equal to `new_len`.
|
||||||
|
///
|
||||||
|
/// If `new_len` is greater than `len`, the `AVec` is extended by the difference, and
|
||||||
|
/// each additional slot is filled with `value`. If `new_len` is less than `len`,
|
||||||
|
/// the `AVec` will be truncated by to be `new_len`
|
||||||
|
///
|
||||||
|
/// This method requires `T` to implement [`Clone`] in order to clone the passed value.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn resize(&mut self, new_len: usize, value: T) {
|
||||||
|
if new_len > self.len {
|
||||||
|
self.reserve(new_len - self.len);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut ptr = self.buf
|
||||||
|
.as_ptr().add(self.len * self.slot_size());
|
||||||
|
|
||||||
|
// write all elements besides the last one
|
||||||
|
for _ in 1..new_len {
|
||||||
|
std::ptr::write(ptr.cast::<T>(), value.clone());
|
||||||
|
ptr = ptr.add(self.slot_size());
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_len > 0 {
|
||||||
|
// the last element can be written without cloning
|
||||||
|
std::ptr::write(ptr.cast::<T>(), value.clone());
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.len = new_len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.truncate(new_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{borrow::Cow, mem, rc::Rc};
|
use std::{borrow::Cow, mem, rc::Rc};
|
||||||
|
|
||||||
use glam::UVec2;
|
use glam::UVec2;
|
||||||
|
use tracing::instrument;
|
||||||
use wgpu::{util::DeviceExt, ComputePipeline};
|
use wgpu::{util::DeviceExt, ComputePipeline};
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
@ -202,6 +203,7 @@ impl LightCullCompute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
pub fn update_screen_size(&mut self, size: PhysicalSize<u32>) {
|
pub fn update_screen_size(&mut self, size: PhysicalSize<u32>) {
|
||||||
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
||||||
&[UVec2::new(size.width, size.height)]);
|
&[UVec2::new(size.width, size.height)]);
|
||||||
|
@ -212,7 +214,9 @@ impl LightCullCompute {
|
||||||
self.light_indices_grid = Self::create_grid(&self.device, self.workgroup_size);
|
self.light_indices_grid = Self::create_grid(&self.device, self.workgroup_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, camera_buffers, lights_buffers, depth_texture))]
|
||||||
pub fn compute(&mut self, camera_buffers: &BufferWrapper, lights_buffers: &LightUniformBuffers, depth_texture: &RenderTexture) {
|
pub fn compute(&mut self, camera_buffers: &BufferWrapper, lights_buffers: &LightUniformBuffers, depth_texture: &RenderTexture) {
|
||||||
|
self.cleanup();
|
||||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
label: Some("LightCullCompute"),
|
label: Some("LightCullCompute"),
|
||||||
});
|
});
|
||||||
|
@ -234,8 +238,7 @@ impl LightCullCompute {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
self.device.poll(wgpu::Maintain::Wait);
|
//self.device.poll(wgpu::Maintain::Wait);
|
||||||
self.cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
|
|
|
@ -13,3 +13,4 @@ pub mod window;
|
||||||
pub mod transform_buffer_storage;
|
pub mod transform_buffer_storage;
|
||||||
pub mod light;
|
pub mod light;
|
||||||
pub mod light_cull_compute;
|
pub mod light_cull_compute;
|
||||||
|
pub mod avec;
|
|
@ -1,18 +1,17 @@
|
||||||
use std::collections::{HashMap, VecDeque, HashSet};
|
use std::collections::{VecDeque, HashSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
use instant::Instant;
|
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use lyra_ecs::query::filter::{Has, Not, Or};
|
use lyra_ecs::query::filter::{Has, Not, Or};
|
||||||
use lyra_ecs::relation::{ChildOf, RelationOriginComponent};
|
use lyra_ecs::relation::{ChildOf, RelationOriginComponent};
|
||||||
use lyra_ecs::{Entity, Tick};
|
use lyra_ecs::{Component, Entity};
|
||||||
use lyra_ecs::query::{Entities, TickOf};
|
use lyra_ecs::query::{Entities, Res, TickOf};
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use lyra_scene::{SceneGraph, WorldTransform};
|
use lyra_scene::{SceneGraph, WorldTransform};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wgpu::{BindGroupLayout, Limits};
|
use wgpu::{BindGroupLayout, Limits};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
@ -22,6 +21,7 @@ use crate::math::Transform;
|
||||||
use crate::render::material::MaterialUniform;
|
use crate::render::material::MaterialUniform;
|
||||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||||
use crate::scene::CameraComponent;
|
use crate::scene::CameraComponent;
|
||||||
|
use crate::DeltaTime;
|
||||||
|
|
||||||
use super::camera::{RenderCamera, CameraUniform};
|
use super::camera::{RenderCamera, CameraUniform};
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
|
@ -67,12 +67,10 @@ struct MeshBufferStorage {
|
||||||
//transform_index: TransformBufferIndices,
|
//transform_index: TransformBufferIndices,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Component)]
|
||||||
pub struct CachedTransform {
|
pub struct InterpTransform {
|
||||||
last_updated_at: Option<Instant>,
|
last_transform: Transform,
|
||||||
cached_at: Instant,
|
alpha: f32,
|
||||||
to_transform: Transform,
|
|
||||||
from_transform: Transform,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BasicRenderer {
|
pub struct BasicRenderer {
|
||||||
|
@ -85,13 +83,12 @@ pub struct BasicRenderer {
|
||||||
|
|
||||||
pub clear_color: wgpu::Color,
|
pub clear_color: wgpu::Color,
|
||||||
|
|
||||||
pub render_pipelines: HashMap<u64, Arc<FullRenderPipeline>>,
|
pub render_pipelines: rustc_hash::FxHashMap<u64, Arc<FullRenderPipeline>>,
|
||||||
pub render_jobs: VecDeque<RenderJob>,
|
pub render_jobs: VecDeque<RenderJob>,
|
||||||
|
|
||||||
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
mesh_buffers: rustc_hash::FxHashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
||||||
material_buffers: HashMap<uuid::Uuid, Rc<Material>>,
|
material_buffers: rustc_hash::FxHashMap<uuid::Uuid, Rc<Material>>,
|
||||||
entity_meshes: HashMap<Entity, uuid::Uuid>,
|
entity_meshes: rustc_hash::FxHashMap<Entity, uuid::Uuid>,
|
||||||
entity_last_transforms: HashMap<Entity, CachedTransform>,
|
|
||||||
|
|
||||||
transform_buffers: TransformBuffers,
|
transform_buffers: TransformBuffers,
|
||||||
|
|
||||||
|
@ -113,6 +110,7 @@ pub struct BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicRenderer {
|
impl BasicRenderer {
|
||||||
|
#[instrument(skip(window))]
|
||||||
pub async fn create_with_window(window: Arc<Window>) -> BasicRenderer {
|
pub async fn create_with_window(window: Arc<Window>) -> BasicRenderer {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
|
|
||||||
|
@ -168,7 +166,7 @@ impl BasicRenderer {
|
||||||
format: surface_format,
|
format: surface_format,
|
||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
present_mode,
|
present_mode: wgpu::PresentMode::Immediate,
|
||||||
alpha_mode: surface_caps.alpha_modes[0],
|
alpha_mode: surface_caps.alpha_modes[0],
|
||||||
view_formats: vec![],
|
view_formats: vec![],
|
||||||
};
|
};
|
||||||
|
@ -222,11 +220,11 @@ impl BasicRenderer {
|
||||||
b: 0.3,
|
b: 0.3,
|
||||||
a: 1.0,
|
a: 1.0,
|
||||||
},
|
},
|
||||||
render_pipelines: HashMap::new(),
|
render_pipelines: Default::default(),
|
||||||
render_jobs: VecDeque::new(),
|
render_jobs: Default::default(),
|
||||||
mesh_buffers: HashMap::new(),
|
mesh_buffers: Default::default(),
|
||||||
material_buffers: HashMap::new(),
|
material_buffers: Default::default(),
|
||||||
entity_meshes: HashMap::new(),
|
entity_meshes: Default::default(),
|
||||||
|
|
||||||
render_limits,
|
render_limits,
|
||||||
transform_buffers,
|
transform_buffers,
|
||||||
|
@ -237,7 +235,6 @@ impl BasicRenderer {
|
||||||
bgl_texture,
|
bgl_texture,
|
||||||
default_texture,
|
default_texture,
|
||||||
depth_buffer_texture: depth_texture,
|
depth_buffer_texture: depth_texture,
|
||||||
entity_last_transforms: HashMap::new(),
|
|
||||||
|
|
||||||
light_buffers: light_uniform_buffers,
|
light_buffers: light_uniform_buffers,
|
||||||
material_buffer: mat_buffer,
|
material_buffer: mat_buffer,
|
||||||
|
@ -245,7 +242,7 @@ impl BasicRenderer {
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the default pipelines
|
// create the default pipelines
|
||||||
let mut pipelines = HashMap::new();
|
let mut pipelines = rustc_hash::FxHashMap::default();
|
||||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
||||||
vec![super::vertex::Vertex::desc(),],
|
vec![super::vertex::Vertex::desc(),],
|
||||||
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout,
|
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout,
|
||||||
|
@ -260,6 +257,7 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the mesh buffers in the GPU need to be updated.
|
/// Checks if the mesh buffers in the GPU need to be updated.
|
||||||
|
#[instrument(skip(self, _entity, meshh))]
|
||||||
fn check_mesh_buffers(&mut self, _entity: Entity, meshh: &ResHandle<Mesh>) {
|
fn check_mesh_buffers(&mut self, _entity: Entity, meshh: &ResHandle<Mesh>) {
|
||||||
let mesh_uuid = meshh.uuid();
|
let mesh_uuid = meshh.uuid();
|
||||||
|
|
||||||
|
@ -299,6 +297,7 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, mesh))]
|
||||||
fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
||||||
let positions = mesh.position().unwrap();
|
let positions = mesh.position().unwrap();
|
||||||
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
||||||
|
@ -348,6 +347,7 @@ impl BasicRenderer {
|
||||||
( vertex_buffer, indices )
|
( vertex_buffer, indices )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, mesh))]
|
||||||
fn create_mesh_buffers(&mut self, mesh: &Mesh) -> MeshBufferStorage {
|
fn create_mesh_buffers(&mut self, mesh: &Mesh) -> MeshBufferStorage {
|
||||||
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
||||||
|
|
||||||
|
@ -374,6 +374,7 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
||||||
|
#[instrument(skip(self, transform, mesh, entity))]
|
||||||
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
||||||
let _ = transform;
|
let _ = transform;
|
||||||
/* if self.transform_buffers.should_expand() {
|
/* if self.transform_buffers.should_expand() {
|
||||||
|
@ -393,68 +394,79 @@ impl BasicRenderer {
|
||||||
true
|
true
|
||||||
} else { false }
|
} else { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpolate_transforms(&mut self, now: Instant, last_epoch: Tick, entity: Entity, transform: &Transform, transform_epoch: Tick) -> Transform {
|
|
||||||
let cached = match self.entity_last_transforms.get_mut(&entity) {
|
|
||||||
Some(last) if transform_epoch == last_epoch => {
|
|
||||||
last.from_transform = last.to_transform;
|
|
||||||
last.to_transform = *transform;
|
|
||||||
last.last_updated_at = Some(last.cached_at);
|
|
||||||
last.cached_at = now;
|
|
||||||
|
|
||||||
last.clone()
|
|
||||||
},
|
|
||||||
Some(last) => last.clone(),
|
|
||||||
None => {
|
|
||||||
let cached = CachedTransform {
|
|
||||||
last_updated_at: None,
|
|
||||||
cached_at: now,
|
|
||||||
from_transform: *transform,
|
|
||||||
to_transform: *transform,
|
|
||||||
};
|
|
||||||
self.entity_last_transforms.insert(entity, cached.clone());
|
|
||||||
cached
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let fixed_time = match cached.last_updated_at {
|
|
||||||
Some(last_updated_at) => cached.cached_at - last_updated_at,
|
|
||||||
None => now - cached.cached_at
|
|
||||||
}.as_secs_f32();
|
|
||||||
let accumulator = (now - cached.cached_at).as_secs_f32();
|
|
||||||
let alpha = accumulator / fixed_time;
|
|
||||||
|
|
||||||
cached.from_transform.lerp(cached.to_transform, alpha)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer for BasicRenderer {
|
impl Renderer for BasicRenderer {
|
||||||
|
#[instrument(skip(self, main_world))]
|
||||||
fn prepare(&mut self, main_world: &mut World) {
|
fn prepare(&mut self, main_world: &mut World) {
|
||||||
let last_epoch = main_world.current_tick();
|
let last_epoch = main_world.current_tick();
|
||||||
let now_inst = Instant::now();
|
|
||||||
let mut alive_entities = HashSet::new();
|
let mut alive_entities = HashSet::new();
|
||||||
|
|
||||||
let view = main_world.view_iter::<(Entities, &Transform, TickOf<Transform>,
|
let view = main_world.view_iter::<(
|
||||||
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>)>();
|
Entities,
|
||||||
|
&Transform,
|
||||||
|
TickOf<Transform>,
|
||||||
|
Or<
|
||||||
|
(&MeshHandle, TickOf<MeshHandle>),
|
||||||
|
(&SceneHandle, TickOf<SceneHandle>)
|
||||||
|
>,
|
||||||
|
Option<&mut InterpTransform>,
|
||||||
|
Res<DeltaTime>,
|
||||||
|
)>();
|
||||||
|
|
||||||
for (entity, transform, transform_epoch, (mesh_pair, scene_pair)) in view {
|
// used to store InterpTransform components to add to entities later
|
||||||
|
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
|
||||||
|
|
||||||
|
for (
|
||||||
|
entity,
|
||||||
|
transform,
|
||||||
|
_transform_epoch,
|
||||||
|
(
|
||||||
|
mesh_pair,
|
||||||
|
scene_pair
|
||||||
|
),
|
||||||
|
interp_tran,
|
||||||
|
delta_time,
|
||||||
|
) in view
|
||||||
|
{
|
||||||
alive_entities.insert(entity);
|
alive_entities.insert(entity);
|
||||||
|
|
||||||
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
|
let interp_transform = match interp_tran {
|
||||||
let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
|
Some(mut interp_transform) => {
|
||||||
|
// found in https://youtu.be/YJB1QnEmlTs?t=472
|
||||||
|
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(**delta_time);
|
||||||
|
|
||||||
|
interp_transform.last_transform = interp_transform.last_transform.lerp(*transform, interp_transform.alpha);
|
||||||
|
interp_transform.last_transform
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let interp = InterpTransform {
|
||||||
|
last_transform: *transform,
|
||||||
|
alpha: 0.5,
|
||||||
|
};
|
||||||
|
component_queue.push((entity, interp));
|
||||||
|
|
||||||
|
*transform
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
// dont need to be resent to the gpu.
|
// dont need to be resent to the gpu.
|
||||||
if !self.process_mesh(entity, interop_pos, &*mesh, mesh_han.uuid())
|
if !self.process_mesh(entity, interp_transform, &*mesh, mesh_han.uuid())
|
||||||
&& mesh_epoch == last_epoch {
|
&& mesh_epoch == last_epoch {
|
||||||
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.device, &self.queue, &self.render_limits,
|
||||||
group, || ( interop_pos.calculate_mat4(), glam::Mat3::from_quat(interop_pos.rotation) ));
|
group, interp_transform.calculate_mat4(), glam::Mat3::from_quat(interp_transform.rotation));
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
|
@ -466,14 +478,14 @@ impl Renderer for BasicRenderer {
|
||||||
|
|
||||||
if let Some((scene_han, scene_epoch)) = scene_pair {
|
if let Some((scene_han, scene_epoch)) = scene_pair {
|
||||||
if let Some(scene) = scene_han.data_ref() {
|
if let Some(scene) = scene_han.data_ref() {
|
||||||
|
if scene_epoch == last_epoch {
|
||||||
let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
|
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
|
||||||
|
}
|
||||||
let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
|
|
||||||
|
|
||||||
for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
|
for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
let mesh_interpo = interpo_pos + **pos;
|
let mesh_interpo = interp_transform + **pos;
|
||||||
|
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
|
@ -483,10 +495,14 @@ 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.device, &self.queue, &self.render_limits,
|
||||||
group, || ( mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) ));
|
group, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) );
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
|
@ -499,8 +515,12 @@ impl Renderer for BasicRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (en, interp) in component_queue {
|
||||||
|
main_world.insert(en, interp);
|
||||||
|
}
|
||||||
|
|
||||||
// collect dead entities
|
// collect dead entities
|
||||||
self.transform_buffers.tick();
|
self.transform_buffers.send_to_gpu(&self.queue);
|
||||||
|
|
||||||
// when buffer storage length does not match the amount of iterated entities,
|
// when buffer storage length does not match the amount of iterated entities,
|
||||||
// remove all dead entities, and their buffers, if they weren't iterated over
|
// remove all dead entities, and their buffers, if they weren't iterated over
|
||||||
|
@ -512,10 +532,9 @@ impl Renderer for BasicRenderer {
|
||||||
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
|
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update camera uniform
|
||||||
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
|
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
|
||||||
let uniform = self.inuse_camera.calc_view_projection(&camera);
|
let uniform = self.inuse_camera.calc_view_projection(&camera);
|
||||||
//let pos = camera.transform.translation;
|
|
||||||
//let uniform = CameraUniform::new(view_mat, *view_proj, pos);
|
|
||||||
self.camera_buffer.write_buffer(&self.queue, 0, &[uniform]);
|
self.camera_buffer.write_buffer(&self.queue, 0, &[uniform]);
|
||||||
} else {
|
} else {
|
||||||
warn!("Missing camera!");
|
warn!("Missing camera!");
|
||||||
|
@ -524,6 +543,7 @@ impl Renderer for BasicRenderer {
|
||||||
self.light_buffers.update_lights(&self.queue, last_epoch, main_world);
|
self.light_buffers.update_lights(&self.queue, last_epoch, main_world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||||
let output = self.surface.get_current_texture()?;
|
let output = self.surface.get_current_texture()?;
|
||||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
@ -593,7 +613,7 @@ impl Renderer for BasicRenderer {
|
||||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||||
let bindgroup = self.transform_buffers.bind_group(job.transform_id);
|
let bindgroup = self.transform_buffers.bind_group(job.transform_id);
|
||||||
let offset = self.transform_buffers.buffer_offset(job.transform_id);
|
let offset = self.transform_buffers.buffer_offset(job.transform_id);
|
||||||
render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
render_pass.set_bind_group(1, bindgroup, &[ offset, ]);
|
||||||
|
|
||||||
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
||||||
render_pass.set_bind_group(3, &self.light_buffers.bind_group_pair.bindgroup, &[]);
|
render_pass.set_bind_group(3, &self.light_buffers.bind_group_pair.bindgroup, &[]);
|
||||||
|
@ -624,6 +644,7 @@ impl Renderer for BasicRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
self.size = new_size;
|
self.size = new_size;
|
||||||
|
|
|
@ -21,6 +21,11 @@ struct VertexOutput {
|
||||||
@location(2) world_normal: vec3<f32>,
|
@location(2) world_normal: vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TransformData {
|
||||||
|
transform: mat4x4<f32>,
|
||||||
|
normal_matrix: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
struct CameraUniform {
|
struct CameraUniform {
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
inverse_projection: mat4x4<f32>,
|
inverse_projection: mat4x4<f32>,
|
||||||
|
@ -51,9 +56,7 @@ struct Lights {
|
||||||
};
|
};
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> u_model_transform: mat4x4<f32>;
|
var<uniform> u_model_transform_data: TransformData;
|
||||||
@group(1) @binding(1)
|
|
||||||
var<uniform> u_model_normal_matrix: mat4x4<f32>;
|
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> u_camera: CameraUniform;
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
@ -68,13 +71,14 @@ fn vs_main(
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
|
|
||||||
out.tex_coords = model.tex_coords;
|
out.tex_coords = model.tex_coords;
|
||||||
out.clip_position = u_camera.view_projection * u_model_transform * vec4<f32>(model.position, 1.0);
|
out.clip_position = u_camera.view_projection * u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
|
||||||
|
|
||||||
// the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36
|
// the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36
|
||||||
let normal_mat = mat3x3(u_model_normal_matrix[0].xyz, u_model_normal_matrix[1].xyz, u_model_normal_matrix[2].xyz);
|
let normal_mat4 = u_model_transform_data.normal_matrix;
|
||||||
|
let normal_mat = mat3x3(normal_mat4[0].xyz, normal_mat4[1].xyz, normal_mat4[2].xyz);
|
||||||
out.world_normal = normalize(normal_mat * model.normal, );
|
out.world_normal = normalize(normal_mat * model.normal, );
|
||||||
|
|
||||||
var world_position: vec4<f32> = u_model_transform * vec4<f32>(model.position, 1.0);
|
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
|
||||||
out.world_position = world_position.xyz;
|
out.world_position = world_position.xyz;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64};
|
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64};
|
||||||
|
|
||||||
use lyra_ecs::Entity;
|
use lyra_ecs::Entity;
|
||||||
|
use tracing::instrument;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wgpu::Limits;
|
use wgpu::Limits;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
use crate::render::avec::AVec;
|
||||||
|
|
||||||
/// A group id created from a [`TransformGroup`].
|
/// A group id created from a [`TransformGroup`].
|
||||||
///
|
///
|
||||||
/// This is mainly created so that [`TransformGroup::OwnedGroup`] can use another group inside of it.
|
/// This is mainly created so that [`TransformGroup::OwnedGroup`] can use another group inside of it.
|
||||||
|
@ -66,8 +69,10 @@ pub struct TransformIndex {
|
||||||
struct BufferEntry {
|
struct BufferEntry {
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub bindgroup: wgpu::BindGroup,
|
pub bindgroup: wgpu::BindGroup,
|
||||||
pub transform_buffer: wgpu::Buffer,
|
pub buffer: wgpu::Buffer,
|
||||||
pub normal_buffer: wgpu::Buffer,
|
transforms: AVec<TransformNormalMatPair>,
|
||||||
|
//pub normal_buffer: wgpu::Buffer,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A HashMap that caches values for reuse.
|
/// A HashMap that caches values for reuse.
|
||||||
|
@ -158,10 +163,12 @@ impl<K: Hash + Eq + PartialEq + Clone, V: Clone, S: BuildHasher> CachedValMap<K,
|
||||||
/// update, and retrieve the transforms.
|
/// update, and retrieve the transforms.
|
||||||
pub struct TransformBuffers {
|
pub struct TransformBuffers {
|
||||||
pub bindgroup_layout: wgpu::BindGroupLayout,
|
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||||
groups: CachedValMap<TransformGroupId, TransformIndex>,
|
//groups: CachedValMap<TransformGroupId, TransformIndex>,
|
||||||
|
//groups: SlotMap<TransformGroupId, TransformIndex>,
|
||||||
entries: Vec<BufferEntry>,
|
entries: Vec<BufferEntry>,
|
||||||
limits: wgpu::Limits,
|
limits: wgpu::Limits,
|
||||||
max_transform_count: usize,
|
max_transform_count: usize,
|
||||||
|
next_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBuffers {
|
impl TransformBuffers {
|
||||||
|
@ -180,26 +187,16 @@ impl TransformBuffers {
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: true,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
label: Some("transform_bind_group_layout"),
|
label: Some("transform_bind_group_layout"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
bindgroup_layout,
|
bindgroup_layout,
|
||||||
groups: Default::default(),
|
|
||||||
entries: Default::default(),
|
entries: Default::default(),
|
||||||
max_transform_count: (limits.max_uniform_buffer_binding_size / 2) as usize / (mem::size_of::<glam::Mat4>()),
|
max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::<glam::Mat4>()),
|
||||||
limits,
|
limits,
|
||||||
|
next_index: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the first uniform buffer
|
// create the first uniform buffer
|
||||||
|
@ -208,70 +205,59 @@ impl TransformBuffers {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an existing transform in the buffers.
|
/// Write the transform buffers to the gpu.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// This uses [`wgpu::Queue::write_buffer`], so the write is not immediately submitted,
|
||||||
/// Panics if the `entity_group` is not already inside of the buffers.
|
/// and instead enqueued internally to happen at the start of the next submit() call.
|
||||||
pub fn update_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, entity_group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex {
|
pub fn send_to_gpu(&mut self, queue: &wgpu::Queue) {
|
||||||
let index = *self.groups.get(entity_group.into())
|
self.next_index = 0;
|
||||||
.expect("Use 'push_transform' for new entities");
|
|
||||||
let entry = self.entries.get_mut(index.entry_index).unwrap();
|
|
||||||
|
|
||||||
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
for entry in &mut self.entries {
|
||||||
|
entry.len = 0;
|
||||||
|
|
||||||
// write the transform and normal to the end of the transform
|
let p = entry.transforms.as_ptr();
|
||||||
let offset = Self::get_buffer_offset(limits, index) as _;
|
let bytes = unsafe { std::slice::from_raw_parts(p as *const u8, entry.transforms.len() * entry.transforms.align()) };
|
||||||
queue.write_buffer(&entry.transform_buffer, offset, bytemuck::bytes_of(&transform));
|
|
||||||
queue.write_buffer(&entry.normal_buffer, offset, bytemuck::bytes_of(&normal_matrix));
|
|
||||||
|
|
||||||
index
|
queue.write_buffer(&entry.buffer, 0, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a new transform into the buffers.
|
|
||||||
pub fn push_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, entity_group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex {
|
|
||||||
self.groups.insert(entity_group.into(), || {
|
|
||||||
// this closure is only called when there are no values that can be reused,
|
|
||||||
// so we get a brand new index at the end of the last entry in the chain.
|
|
||||||
let last = self.entries.last_mut().unwrap();
|
|
||||||
|
|
||||||
// ensure the gpu buffer is not overflown
|
|
||||||
debug_assert!(last.len < self.max_transform_count,
|
|
||||||
"Transform buffer is filled and 'next_indices' was not incremented! \
|
|
||||||
Was a new buffer created?");
|
|
||||||
|
|
||||||
let tidx = last.len;
|
|
||||||
last.len += 1;
|
|
||||||
|
|
||||||
TransformIndex {
|
|
||||||
entry_index: self.entries.len() - 1,
|
|
||||||
transform_index: tidx
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.update_transform(queue, limits, entity_group, transform, normal_matrix)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect the dead transforms and prepare self to check next time.
|
|
||||||
pub fn tick(&mut self) {
|
|
||||||
self.groups.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a boolean indicating if the buffer contains this group.
|
|
||||||
pub fn contains(&self, group: TransformGroup) -> bool {
|
|
||||||
self.groups.contains(group.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an existing transform group or if its not existing yet, pushes it to the buffer.
|
/// Update an existing transform group or if its not existing yet, pushes it to the buffer.
|
||||||
///
|
///
|
||||||
/// Returns: the index that the transform is at in the buffers.
|
/// Returns: the index that the transform is at in the buffers.
|
||||||
pub fn update_or_push<F>(&mut self, queue: &wgpu::Queue, limits: &Limits, group: TransformGroup, transform_fn: F) -> TransformIndex
|
#[instrument(skip(self, device, queue, limits, group, transform, normal_matrix))]
|
||||||
where F: Fn() -> (glam::Mat4, glam::Mat3)
|
#[inline(always)]
|
||||||
|
pub fn update_or_push(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, limits: &Limits, group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex
|
||||||
{
|
{
|
||||||
let (transform, normal_matrix) = transform_fn();
|
// maybe will be used at some point again
|
||||||
if self.contains(group) {
|
let _ = (queue, limits, group);
|
||||||
self.update_transform(queue, limits, group, transform, normal_matrix)
|
|
||||||
} else {
|
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
||||||
self.push_transform(queue, limits, group, transform, normal_matrix)
|
|
||||||
|
let index = self.next_index;
|
||||||
|
self.next_index += 1;
|
||||||
|
|
||||||
|
// the index of the entry to put the transform into
|
||||||
|
let entry_index = index / self.max_transform_count;
|
||||||
|
// the index of the transform in the buffer
|
||||||
|
let transform_index = index % self.max_transform_count;
|
||||||
|
|
||||||
|
if entry_index >= self.entries.len() {
|
||||||
|
self.expand_buffers(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry = self.entries.get_mut(entry_index).unwrap();
|
||||||
|
|
||||||
|
// write the transform and normal to the end of the transform
|
||||||
|
entry.transforms.set_at(transform_index, TransformNormalMatPair {
|
||||||
|
transform,
|
||||||
|
normal_mat: normal_matrix,
|
||||||
|
});
|
||||||
|
entry.len += 1;
|
||||||
|
|
||||||
|
TransformIndex {
|
||||||
|
entry_index: 0,
|
||||||
|
transform_index: index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +265,7 @@ impl TransformBuffers {
|
||||||
///
|
///
|
||||||
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
||||||
/// "chain-link" is created.
|
/// "chain-link" is created.
|
||||||
|
#[instrument(skip(self, device))]
|
||||||
pub fn expand_buffers(&mut self, device: &wgpu::Device) {
|
pub fn expand_buffers(&mut self, device: &wgpu::Device) {
|
||||||
let limits = device.limits();
|
let limits = device.limits();
|
||||||
let max_buffer_sizes = self.max_transform_count as u64 * limits.min_uniform_buffer_offset_alignment as u64;
|
let max_buffer_sizes = self.max_transform_count as u64 * limits.min_uniform_buffer_offset_alignment as u64;
|
||||||
|
@ -292,21 +279,9 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let normal_mat_buffer = device.create_buffer(
|
let tran_stride = mem::size_of::<TransformNormalMatPair>();
|
||||||
&wgpu::BufferDescriptor {
|
|
||||||
label: Some(&format!("B_NormalMatrix_{}", self.entries.len())),
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
size: max_buffer_sizes,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let tran_stride = mem::size_of::<glam::Mat4>();
|
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
// although a normal matrix only needs to be a mat3, there's a weird issue with
|
|
||||||
// misalignment from wgpu or spirv-cross: https://github.com/gfx-rs/wgpu-rs/issues/36
|
|
||||||
let norm_stride = mem::size_of::<glam::Mat4>();
|
|
||||||
|
|
||||||
let transform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout: &self.bindgroup_layout,
|
layout: &self.bindgroup_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
|
@ -319,42 +294,34 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Buffer(
|
|
||||||
wgpu::BufferBinding {
|
|
||||||
buffer: &normal_mat_buffer,
|
|
||||||
offset: 0,
|
|
||||||
size: Some(NonZeroU64::new(norm_stride as u64).unwrap())
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
label: Some("BG_Transforms"),
|
label: Some("BG_Transforms"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut transforms = AVec::new(limits.min_uniform_buffer_offset_alignment as _);
|
||||||
|
transforms.resize(self.max_transform_count, TransformNormalMatPair {
|
||||||
|
transform: glam::Mat4::IDENTITY,
|
||||||
|
normal_mat: glam::Mat4::IDENTITY,
|
||||||
|
});
|
||||||
|
|
||||||
let entry = BufferEntry {
|
let entry = BufferEntry {
|
||||||
bindgroup: transform_bind_group,
|
bindgroup,
|
||||||
transform_buffer,
|
buffer: transform_buffer,
|
||||||
normal_buffer: normal_mat_buffer,
|
|
||||||
len: 0,
|
len: 0,
|
||||||
|
|
||||||
|
transforms,
|
||||||
};
|
};
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the bind group for the transform index.
|
/// Returns the bind group for the transform index.
|
||||||
|
#[inline(always)]
|
||||||
pub fn bind_group(&self, transform_id: TransformIndex) -> &wgpu::BindGroup {
|
pub fn bind_group(&self, transform_id: TransformIndex) -> &wgpu::BindGroup {
|
||||||
let entry = self.entries.get(transform_id.entry_index).unwrap();
|
let entry_index = transform_id.transform_index / self.max_transform_count;
|
||||||
|
let entry = self.entries.get(entry_index).unwrap();
|
||||||
&entry.bindgroup
|
&entry.bindgroup
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the buffer offset for a transform using wgpu limits.
|
|
||||||
///
|
|
||||||
/// If its possible to borrow immutably, use [`TransformBuffers::buffer_offset`].
|
|
||||||
fn get_buffer_offset(limits: &wgpu::Limits, transform_index: TransformIndex) -> u32 {
|
|
||||||
transform_index.transform_index as u32 * limits.min_uniform_buffer_offset_alignment as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the offset of the transform inside the bind group buffer.
|
/// Returns the offset of the transform inside the bind group buffer.
|
||||||
///
|
///
|
||||||
/// ```nobuild
|
/// ```nobuild
|
||||||
|
@ -362,8 +329,21 @@ impl TransformBuffers {
|
||||||
/// let offset = transform_buffers.buffer_offset(job.transform_id);
|
/// let offset = transform_buffers.buffer_offset(job.transform_id);
|
||||||
/// render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
/// render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
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)
|
||||||
|
let transform_index = transform_index.transform_index % self.max_transform_count;
|
||||||
|
let t = transform_index as u32 * self.limits.min_uniform_buffer_offset_alignment as u32;
|
||||||
|
//debug!("offset: {t}");
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean indicating if the buffers need to be expanded
|
||||||
|
pub fn needs_expand(&self) -> bool {
|
||||||
|
false
|
||||||
|
/* self.entries.last()
|
||||||
|
.map(|entry| entry.len >= self.max_transform_count)
|
||||||
|
.unwrap_or(false) */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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