game: rewrite EventQueue due to new ecs requirement of Send + Sync for resources, use new SceneGraph in renderer

This commit is contained in:
SeanOMik 2024-03-31 13:24:32 -04:00
parent a39d259bb4
commit aa8d94851c
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
12 changed files with 169 additions and 48 deletions

1
Cargo.lock generated
View File

@ -1795,6 +1795,7 @@ dependencies = [
"lyra-math",
"lyra-reflect",
"lyra-resource",
"lyra-scene",
"quote",
"syn 2.0.51",
"thiserror",

View File

@ -1,6 +1,6 @@
use std::ptr::NonNull;
use std::{ptr::NonNull, thread, time::Duration};
use lyra_engine::{assets::gltf::Gltf, change_tracker::Ct, ecs::{query::{QueryBorrow, Res, View, ViewState}, system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, 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 lyra_engine::{assets::gltf::Gltf, change_tracker::Ct, ecs::{query::{Res, View}, system::{Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, 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 lyra_engine::assets::ResourceManager;
struct FixedTimestep {
@ -79,7 +79,8 @@ async fn main() {
let separate_gltf = resman.request::<Gltf>("assets/pos-testing/child-node-cubes.glb").unwrap(); */
//drop(resman);
cube_gltf.wait_recurse_dependencies_load();
//cube_gltf.wait_recurse_dependencies_load();
thread::sleep(Duration::from_millis(500));
let cube_mesh = &cube_gltf.data_ref()
.unwrap().meshes[0];
/* let crate_mesh = &crate_gltf.data_ref()
@ -91,7 +92,8 @@ async fn main() {
let sponza_model = resman.request::<Gltf>("../assets/sponza/Sponza.gltf").unwrap();
drop(resman);
sponza_model.wait_recurse_dependencies_load();
//sponza_model.wait_recurse_dependencies_load();
thread::sleep(Duration::from_millis(10000));
let sponza_scene = &sponza_model.data_ref()
.unwrap().scenes[0];

View File

@ -8,6 +8,7 @@ lyra-resource = { path = "../lyra-resource" }
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
lyra-math = { path = "../lyra-math" }
lyra-scene = { path = "../lyra-scene" }
winit = "0.28.1"
tracing = "0.1.37"

View File

@ -1,12 +1,13 @@
use std::any::Any;
pub trait CastableAny: Send + Sync + 'static {
/// A trait that is implemented for types that are `Send + Sync + 'static`.
pub trait AsSyncAny: Send + Sync + 'static {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
/// Implements this trait for anything that fits the type bounds
impl<T: Send + Sync + 'static> CastableAny for T {
impl<T: Send + Sync + 'static> AsSyncAny for T {
fn as_any(&self) -> &dyn Any {
self
}

View File

@ -1,15 +1,82 @@
use std::{collections::{HashMap, VecDeque}, any::{TypeId, Any}, cell::RefCell};
use std::{any::{Any, TypeId}, collections::{HashMap, VecDeque}, marker::PhantomData, mem::{self, MaybeUninit}, ops::Range};
use crate::{castable_any::CastableAny, plugin::Plugin};
use crate::plugin::Plugin;
pub trait Event: Clone + Send + Sync + 'static {}
impl<T: Clone + Send + Sync + 'static> Event for T {}
pub type Events<T> = VecDeque<T>;
#[derive(Clone)]
struct TypeErasedBuffer {
type_id: TypeId,
item_size: usize,
data: Vec<MaybeUninit<u8>>,
fn_drop_item: fn(item: *mut ()),
}
impl Drop for TypeErasedBuffer {
fn drop(&mut self) {
let range = self.data.as_mut_ptr_range();
let mut current = range.start;
let end = range.end;
while current < end {
// SAFETY: current pointer will either be the start of the buffer, or at the start of
// the next item
(self.fn_drop_item)(current.cast());
// SAFETY: the items are placed right after eachother
current = unsafe { current.add(self.item_size) };
}
// Safety: all of the events were just dropped
unsafe { self.data.set_len(0) };
}
}
impl TypeErasedBuffer {
pub fn new<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
item_size: mem::size_of::<T>(),
data: Default::default(),
// dropped immediately after the read
fn_drop_item: |item| unsafe { item.cast::<T>().drop_in_place() },
}
}
pub fn push<T: 'static>(&mut self, value: T) {
debug_assert!(TypeId::of::<T>() == self.type_id, "The type of the buffer does not match the type that was added to it");
// reserve enough bytes to store T
let old_len = self.data.len();
self.data.reserve(mem::size_of::<T>());
// Get a pointer to the end of the data.
// SAFETY: we just reserved enough memory to store the data.
let end_ptr = unsafe { self.data.as_mut_ptr().add(old_len) };
unsafe {
// write the command and its runner into the buffer
end_ptr.cast::<T>()
// written unaligned to keep everything packed
.write_unaligned(value);
// we wrote to the vec's buffer without using its api, so we need manually
// set the length of the vec.
self.data.set_len(old_len + mem::size_of::<T>());
}
}
pub fn len(&self) -> usize {
self.data.len() / self.item_size
}
}
pub struct EventQueue {
events: HashMap<TypeId, RefCell<Box<dyn CastableAny>>>,
event_write_queue: HashMap<TypeId, RefCell<Box<dyn CastableAny>>>
events: HashMap<TypeId, TypeErasedBuffer>,
event_write_queue: HashMap<TypeId, TypeErasedBuffer>,
}
impl Default for EventQueue {
@ -32,7 +99,7 @@ impl EventQueue {
E: Event
{
// the compiler wants me to explicit right here for some reason
let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn CastableAny>);
/* let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn AsSyncAny>);
// Get, or create, a list of events of this type
let type_id = event.type_id();
@ -42,7 +109,13 @@ impl EventQueue {
// downcast resource as an events storage
let e: &mut Events<E> = events.as_mut().as_any_mut().downcast_mut().unwrap();
e.push_back(event);
e.push_back(event); */
let type_id = event.type_id();
let events = self.event_write_queue.entry(type_id)
.or_insert_with(|| TypeErasedBuffer::new::<E>());
events.push(event);
}
// Clear events, this should happen at the start of every tick since events are cloned
@ -60,13 +133,55 @@ impl EventQueue {
}
}
pub fn read_events<E>(&self) -> Option<Events<E>>
pub fn read_events<E>(&self) -> Option<EventReader<E>>
where
E: Event
{
if let Some(event ) = self.events.get(&TypeId::of::<E>()) {
let eref = event.borrow();
Some(eref.as_ref().as_any().downcast_ref::<Events<E>>().unwrap().clone())
self.events.get(&TypeId::of::<E>())
.map(|event| EventReader::new(event.clone()))
}
}
pub struct EventReader<E: Event> {
buf: TypeErasedBuffer,
range: Option<Range<*mut MaybeUninit<u8>>>,
_marker: PhantomData<E>,
}
impl<E: Event> EventReader<E> {
fn new(buffer: TypeErasedBuffer) -> Self {
Self {
buf: buffer,
range: None,
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.buf.len()
}
}
impl<E: Event> Iterator for EventReader<E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
if self.range.is_none() {
self.range = Some(self.buf.data.as_mut_ptr_range());
}
let range = self.range.as_mut().unwrap();
if range.start < range.end {
// Retrieve the event in the buffer.
// SAFETY: current pointer will either be the start of the buffer, or at the start
// of the next event.
let event = unsafe { range.start.cast::<E>().read_unaligned() };
// Advance the pointer to be after this event.
range.start = unsafe { range.start.add(mem::size_of::<E>()) };
Some(event)
} else {
None
}

View File

@ -521,10 +521,10 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> {
}
}
let motion_avg = if let Some(mut mouse_events) = mouse_events {
let motion_avg = if let Some(mouse_events) = mouse_events {
let count = mouse_events.len();
let mut sum = Vec2::ZERO;
while let Some(mm) = mouse_events.pop_front() {
for mm in mouse_events {
sum += mm.delta;
}
Some(sum / count as f32)

View File

@ -110,8 +110,8 @@ impl crate::ecs::system::System for InputSystem {
return Ok(());
}
let mut events = queue.unwrap();
while let Some(event) = events.pop_front() {
let events = queue.unwrap();
for event in events {
self.process_event(world, &event);
}

View File

@ -8,7 +8,7 @@ pub mod game;
pub mod render;
pub mod resources;
pub mod input;
pub mod castable_any;
pub mod as_any;
pub mod plugin;
pub mod change_tracker;

View File

@ -10,7 +10,7 @@ use lyra_ecs::query::filter::{Has, Or};
use lyra_ecs::{Entity, Tick};
use lyra_ecs::query::{Entities, TickOf};
use lyra_ecs::World;
use lyra_resource::gltf::GltfScene;
use lyra_scene::SceneGraph;
use tracing::{debug, warn};
use uuid::Uuid;
use wgpu::{BindGroupLayout, Limits};
@ -36,7 +36,7 @@ use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, r
use lyra_resource::{gltf::Mesh, ResHandle};
type MeshHandle = ResHandle<Mesh>;
type SceneHandle = ResHandle<GltfScene>;
type SceneHandle = ResHandle<SceneGraph>;
pub trait Renderer {
fn prepare(&mut self, main_world: &mut World);
@ -465,27 +465,28 @@ impl Renderer for BasicRenderer {
if let Some(scene) = scene_han.data_ref() {
let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
let meshes = scene.collect_world_meshes();
for (mesh_han, mesh_pos) in meshes.into_iter() {
if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interpo_pos + mesh_pos;
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid())
&& scene_epoch == last_epoch {
self.check_mesh_buffers(entity, &mesh_han);
scene.traverse_down(|sw: &World, mesh_han, pos| {
if let Some(mesh_han) = sw.view_one::<&MeshHandle>(mesh_han.entity()).get() {
if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interpo_pos + pos;
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid())
&& scene_epoch == last_epoch {
self.check_mesh_buffers(entity, &mesh_han);
}
let material = mesh.material.as_ref().unwrap()
.data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid());
self.render_jobs.push_back(job);
}
let material = mesh.material.as_ref().unwrap()
.data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid());
self.render_jobs.push_back(job);
}
}
});
}
}
}

View File

@ -341,8 +341,8 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>();
if let Some(event_queue) = world.try_get_resource_mut::<EventQueue>() {
if let Some(mut events) = event_queue.read_events::<InputEvent>() {
while let Some(ev) = events.pop_front() {
if let Some(events) = event_queue.read_events::<InputEvent>() {
for ev in events {
match ev {
InputEvent::CursorEntered { .. } => {
opts.cursor_inside_window = true;

View File

@ -293,7 +293,7 @@ mod tests {
.data_ref().unwrap();
let mut node = None;
scene.traverse_down(|no, _tran| {
scene.traverse_down(|_, no, _tran| {
node = Some(no.clone());
});

View File

@ -135,7 +135,7 @@ impl SceneGraph {
/// The traversal does not include the root scene node.
pub fn traverse_down<F>(&self, mut callback: F)
where
F: FnMut(&SceneNode, Transform),
F: FnMut(&World, &SceneNode, Transform),
{
self.traverse_down_from(self.root_node.clone(), &mut callback);
}
@ -144,7 +144,7 @@ impl SceneGraph {
/// SceneNode and its world transform.
fn traverse_down_from<F>(&self, start: SceneNode, callback: &mut F)
where
F: FnMut(&SceneNode, Transform),
F: FnMut(&World, &SceneNode, Transform),
{
let v = self.world
.view::<(Entities, &Transform)>()
@ -153,7 +153,7 @@ impl SceneGraph {
for ((e, _), _rel) in v.iter() {
let node = SceneNode::new(Some(start.entity()), e);
let world_pos = node.world_transform(self);
callback(&node, world_pos);
callback(&self.world, &node, world_pos);
self.traverse_down_from(node, callback);
}
@ -222,7 +222,7 @@ pub mod tests {
assert!(b.parent(&scene).unwrap() == a);
let mut idx = 0;
scene.traverse_down(|_e, pos| {
scene.traverse_down(|_, _, pos| {
if idx == 0 {
assert_eq!(pos, Transform::from_translation(v2s[idx]));
} else if idx == 1 {