diff --git a/Cargo.toml b/Cargo.toml index ed2b369f5fe77..9066d51ec9fcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ members = [ "tools/*", # Bevy's error codes. This is a crate so we can automatically check all of the code blocks. "errors", + "core/simulatedentity", + "core/simulatedentity_macro", ] exclude = [ # Integration tests are not part of the workspace diff --git a/core/simulatedentity/Cargo.toml b/core/simulatedentity/Cargo.toml new file mode 100644 index 0000000000000..ce84234b237a7 --- /dev/null +++ b/core/simulatedentity/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "simulatedentity" +version = "0.1.0" +edition = "2024" + +[dependencies] +bevy_app = { path = "../../crates/bevy_app", version = "0.16.0-dev" } +bevy_ecs = { path = "../../crates/bevy_ecs", version = "0.16.0-dev" } +bevy_math = { path = "../../crates/bevy_math", version = "0.16.0-dev" } +bevy_time = { path = "../../crates/bevy_time", version = "0.16.0-dev" } +simulatedentity_macro = { path = "../simulatedentity_macro" } +memoffset = "0.9" + +[features] +default = [] + +[lints] +workspace = true diff --git a/core/simulatedentity/src/dod.rs b/core/simulatedentity/src/dod.rs new file mode 100644 index 0000000000000..c42eed6c8270d --- /dev/null +++ b/core/simulatedentity/src/dod.rs @@ -0,0 +1,75 @@ +#![allow(unsafe_code)] + +use bevy_ecs::{archetype::{Archetype, ArchetypeId}, prelude::*}; +use bevy_math::Vec4; +use crate::{Position, Velocity}; + +pub struct DODArray { + pub bytes: Vec, +} + +pub struct DODStorage { + pub pos: DODArray, + pub vel: DODArray, +} + +#[derive(Clone, Copy)] +pub struct FieldInfo { + pub offset: usize, + pub size: usize, +} + +pub trait DODConvertible: Sized { + const FIELD_LAYOUT: &'static [FieldInfo]; + fn to_bytes(&self) -> Vec; + fn from_bytes(bytes: &[u8]) -> Self; +} + +pub fn convert_chunk_to_dod(chunk: &Archetype) -> DODStorage { + let len = chunk.entities().len(); + use core::mem::size_of; + let pos_bytes = vec![0u8; len * size_of::()]; + let vel_bytes = vec![0u8; len * size_of::()]; + DODStorage { + pos: DODArray { bytes: pos_bytes }, + vel: DODArray { bytes: vel_bytes }, + } +} + +pub fn apply_dod_back(_world: &mut World, _storage: &DODStorage, _id: ArchetypeId) {} + +pub fn move_simd(storage: &mut DODStorage, dt: f32) { + move_simd_inner(storage, dt); +} + +#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2"))] +fn move_simd_inner(storage: &mut DODStorage, dt: f32) { + unsafe { + use core::arch::x86_64::*; + let len = storage.pos.bytes.len() / 16; + let pos_ptr = storage.pos.bytes.as_mut_ptr() as *mut __m128; + let vel_ptr = storage.vel.bytes.as_mut_ptr() as *mut __m128; + let dt_vec = _mm_set1_ps(dt); + for i in 0..len { + let p = _mm_load_ps(pos_ptr.add(i) as *const f32); + let v = _mm_load_ps(vel_ptr.add(i) as *const f32); + let v_dt = _mm_mul_ps(v, dt_vec); + let res = _mm_add_ps(p, v_dt); + _mm_store_ps(pos_ptr.add(i) as *mut f32, res); + } + } +} + +#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2")))] +fn move_simd_inner(storage: &mut DODStorage, dt: f32) { + let len = storage.pos.bytes.len() / 16; + for i in 0..len { + unsafe { + let pos_ptr = storage.pos.bytes.as_mut_ptr() as *mut Vec4; + let vel_ptr = storage.vel.bytes.as_ptr() as *const Vec4; + let pos = &mut *pos_ptr.add(i); + let vel = &*vel_ptr.add(i); + *pos = *pos + (*vel * dt); + } + } +} diff --git a/core/simulatedentity/src/lib.rs b/core/simulatedentity/src/lib.rs new file mode 100644 index 0000000000000..eea3fbdaa6f23 --- /dev/null +++ b/core/simulatedentity/src/lib.rs @@ -0,0 +1,104 @@ +#![allow(unsafe_code)] + +use bevy_app::{App, Plugin, PostUpdate}; +use bevy_ecs::{archetype::ArchetypeId, prelude::*}; +use bevy_math::{Vec3, Vec4}; +use bevy_time::Time; + +pub use simulatedentity_macro::DODConvertible; + +pub mod dod; +use dod::{apply_dod_back, convert_chunk_to_dod, move_simd, DODStorage}; + +#[derive(Component, Clone, Copy, DODConvertible)] +#[repr(C)] +pub struct Position { + pub value: Vec4, +} + +impl From for Position { + fn from(v: Vec3) -> Self { + Self { value: v.extend(0.0) } + } +} + +#[derive(Component, Clone, Copy, DODConvertible)] +#[repr(C)] +pub struct Velocity { + pub value: Vec4, +} + +impl From for Velocity { + fn from(v: Vec3) -> Self { + Self { value: v.extend(0.0) } + } +} + +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +pub struct UpdateSimulatedEntities; + +pub trait SimulatedEntityCommands { + fn spawn_simulated_entity(&mut self, bundle: impl Bundle) -> Entity; +} + +impl<'w, 's> SimulatedEntityCommands for Commands<'w, 's> { + fn spawn_simulated_entity(&mut self, bundle: impl Bundle) -> Entity { + self.spawn(bundle).id() + } +} + +#[derive(Default, Resource)] +struct DODStore(pub std::collections::HashMap); + +fn update_simulated_entities(world: &mut World) { + let pos_id = world.component_id::().unwrap(); + let vel_id = world.component_id::().unwrap(); + let ids: Vec = world + .archetypes() + .iter() + .filter(|a| a.len() as usize >= 1000 && a.contains(pos_id) && a.contains(vel_id)) + .map(|a| a.id()) + .collect(); + let dt = world.resource::