Use a wit resource for the ecs world and a slab to allow multiple worlds

This commit is contained in:
SeanOMik 2024-04-12 23:59:10 -04:00
parent 57539df4e7
commit 5d9843cdb0
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
6 changed files with 202 additions and 89 deletions

11
Cargo.lock generated
View File

@ -1266,6 +1266,15 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "slice-group-by"
version = "0.3.1"
@ -2271,6 +2280,8 @@ version = "0.1.0"
dependencies = [
"async-trait",
"lyra-ecs",
"slab",
"thiserror",
"tokio",
"wasmtime",
"wasmtime-wasi",

View File

@ -11,4 +11,6 @@ async-trait = "0.1.80"
tokio = { version = "1.37.0", features = ["full"] }
wasmtime = "19.0.2"
wasmtime-wasi = "19.0.2"
lyra-ecs = { path = "./lyra-engine/lyra-ecs" }
lyra-ecs = { path = "./lyra-engine/lyra-ecs" }
slab = "0.4.9"
thiserror = "1.0.58"

View File

@ -4,20 +4,20 @@ use std::mem;
use std::ptr::NonNull;
use async_trait::async_trait;
use component::witguest::{ecs_world, math};
use component::witguest::ecs::WasmTypeId;
use component::witguest::{ecs as wasm_ecs, math};
use ecs::query::dynamic::QueryDynamicType;
use lyra_ecs::{Component, World};
use lyra_ecs::Component;
use lyra_ecs as ecs;
use lyra_engine::ecs::component::witguest::ecs_world::WasmTypeId;
use slab::Slab;
use thiserror::Error;
use wasmtime_wasi::WasiView;
#[allow(unused_imports)]
pub(crate) mod lyra_engine {
pub(crate) mod ecs {
pub use super::super::*;
}
pub use lyra_ecs as ecs;
}
wasmtime::component::bindgen!({
@ -42,7 +42,7 @@ impl Into<ecs::DynTypeId> for WasmTypeId {
}
}
impl Into<ecs::ComponentInfo> for ecs_world::ComponentInfo {
impl Into<ecs::ComponentInfo> for wasm_ecs::ComponentInfo {
fn into(self) -> ecs::ComponentInfo {
ecs::ComponentInfo::new_unknown(
None,
@ -52,10 +52,10 @@ impl Into<ecs::ComponentInfo> for ecs_world::ComponentInfo {
}
}
impl From<ecs::Entity> for ecs_world::Entity {
impl From<ecs::Entity> for wasm_ecs::Entity {
fn from(value: ecs::Entity) -> Self {
Self {
id: ecs_world::EntityId { id: value.id().0 },
id: wasm_ecs::EntityId { id: value.id().0 },
generation: value.generation(),
}
}
@ -70,7 +70,7 @@ struct Vec3 {
}
struct Imports {
world: lyra_ecs::World,
world_slab: Slab<lyra_ecs::World>,
}
impl WasiView for Imports {
@ -93,13 +93,23 @@ impl ExampleImports for Imports {
impl math::Host for Imports {}
#[derive(Error, Debug)]
pub enum WasmError {
#[error("an invalid '{0}' resource handle was used by the guest")]
InvalidResourceHandle(&'static str)
}
#[async_trait]
impl ecs_world::Host for Imports {
async fn spawn(
&mut self,
mut components: Vec<u8>,
infos: Vec<ecs_world::ComponentInfo>,
) -> wasmtime::Result<ecs_world::Entity> {
impl wasm_ecs::HostEcsWorld for Imports {
async fn new(&mut self,) -> wasmtime::Result<wasmtime::component::Resource<wasm_ecs::EcsWorld>> {
let world = self.world_slab.insert(lyra_ecs::World::new());
Ok(wasmtime::component::Resource::new_own(world as _))
}
async fn spawn(&mut self, this: wasmtime::component::Resource<wasm_ecs::EcsWorld>, mut components: Vec<u8>, infos: Vec<wasm_ecs::ComponentInfo>) -> wasmtime::Result<wasm_ecs::Entity> {
let world = self.world_slab.get_mut(this.rep() as _)
.ok_or(WasmError::InvalidResourceHandle("EcsWorld"))?;
// add the components in the tightly packed `components` buffer into the dynamic bundle
let mut bundle = ecs::DynamicBundle::new();
let mut offset = 0;
@ -112,13 +122,16 @@ impl ecs_world::Host for Imports {
offset += info.size as usize;
}
let en = self.world.spawn(bundle);
let en = world.spawn(bundle);
Ok(en.into())
}
async fn view(&mut self, infos: Vec<ecs_world::ComponentInfo>) -> wasmtime::Result<Vec<u8>> {
async fn view(&mut self, this: wasmtime::component::Resource<wasm_ecs::EcsWorld>, infos: Vec<wasm_ecs::ComponentInfo>) -> wasmtime::Result<Vec<u8>> {
let world = self.world_slab.get_mut(this.rep() as _)
.ok_or(WasmError::InvalidResourceHandle("EcsWorld"))?;
// Create a dynamic view for querying for the components
let mut view = self.world.dynamic_view();
let mut view = world.dynamic_view();
for info in infos.clone() {
view.push(QueryDynamicType::from_info(info.into()));
}
@ -145,6 +158,18 @@ impl ecs_world::Host for Imports {
Ok(components)
}
fn drop(&mut self, this: wasmtime::component::Resource<wasm_ecs::EcsWorld>) -> wasmtime::Result<()> {
self.world_slab.try_remove(this.rep() as _)
.ok_or(WasmError::InvalidResourceHandle("EcsWorld"))?;
Ok(())
}
}
#[async_trait]
impl wasm_ecs::Host for Imports {
}
#[tokio::main]
@ -164,7 +189,9 @@ async fn main() -> wasmtime::Result<()> {
let mut store = wasmtime::Store::new(
&engine,
Imports {
world: World::new(),
// There will likely be a world, but a slab is used in the case that the guest
// wants another world for some reason.
world_slab: Slab::with_capacity(1),
},
);

View File

@ -82,7 +82,7 @@ pub mod component {
#[allow(clippy::all)]
pub mod ecs_world {
pub mod ecs {
#[used]
#[doc(hidden)]
#[cfg(target_arch = "wasm32")]
@ -131,72 +131,138 @@ pub mod component {
f.debug_struct("ComponentInfo").field("size", &self.size).field("alignment", &self.alignment).field("type-id", &self.type_id).finish()
}
}
#[allow(unused_unsafe, clippy::all)]
/// expects components to be tightly packed in the same order of component-infos
pub fn spawn(components: &[u8],component_infos: &[ComponentInfo],) -> Entity{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[repr(align(8))]
struct RetArea([u8; 16]);
let mut ret_area = ::core::mem::MaybeUninit::<RetArea>::uninit();
let vec0 = components;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
let vec1 = component_infos;
let ptr1 = vec1.as_ptr() as i32;
let len1 = vec1.len() as i32;
let ptr2 = ret_area.as_mut_ptr() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs-world")]
extern "C" {
#[link_name = "spawn"]
fn wit_import(_: i32, _: i32, _: i32, _: i32, _: i32, );
#[derive(Debug)]
#[repr(transparent)]
pub struct EcsWorld{
handle: wit_bindgen::rt::Resource<EcsWorld>,
}
impl EcsWorld{
#[doc(hidden)]
pub unsafe fn from_handle(handle: u32) -> Self {
Self {
handle: wit_bindgen::rt::Resource::from_handle(handle),
}
}
#[doc(hidden)]
pub fn handle(&self) -> u32 {
wit_bindgen::rt::Resource::handle(&self.handle)
}
}
unsafe impl wit_bindgen::rt::WasmResource for EcsWorld{
#[inline]
unsafe fn drop(_handle: u32) {
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, _: i32, _: i32, ){ unreachable!() }
wit_import(ptr0, len0, ptr1, len1, ptr2);
let l3 = *((ptr2 + 0) as *const i64);
let l4 = *((ptr2 + 8) as *const i64);
Entity{
id: EntityId{
id: l3 as u64,
},
generation: l4 as u64,
unreachable!();
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[resource-drop]ecs-world"]
fn drop(_: u32);
}
drop(_handle);
}
}
}
#[allow(unused_unsafe, clippy::all)]
pub fn view(component_infos: &[ComponentInfo],) -> wit_bindgen::rt::vec::Vec::<u8>{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
impl EcsWorld {
#[allow(unused_unsafe, clippy::all)]
pub fn new() -> Self{
#[repr(align(4))]
struct RetArea([u8; 8]);
let mut ret_area = ::core::mem::MaybeUninit::<RetArea>::uninit();
let vec0 = component_infos;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
let ptr1 = ret_area.as_mut_ptr() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs-world")]
extern "C" {
#[link_name = "view"]
fn wit_import(_: i32, _: i32, _: i32, );
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[constructor]ecs-world"]
fn wit_import() -> i32;
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import() -> i32{ unreachable!() }
let ret = wit_import();
EcsWorld::from_handle(ret as u32)
}
}
}
impl EcsWorld {
#[allow(unused_unsafe, clippy::all)]
pub fn spawn(&self,components: &[u8],component_infos: &[ComponentInfo],) -> Entity{
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, ){ unreachable!() }
wit_import(ptr0, len0, ptr1);
let l2 = *((ptr1 + 0) as *const i32);
let l3 = *((ptr1 + 4) as *const i32);
let len4 = l3 as usize;
Vec::from_raw_parts(l2 as *mut _, len4, len4)
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[repr(align(8))]
struct RetArea([u8; 16]);
let mut ret_area = ::core::mem::MaybeUninit::<RetArea>::uninit();
let vec0 = components;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
let vec1 = component_infos;
let ptr1 = vec1.as_ptr() as i32;
let len1 = vec1.len() as i32;
let ptr2 = ret_area.as_mut_ptr() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[method]ecs-world.spawn"]
fn wit_import(_: i32, _: i32, _: i32, _: i32, _: i32, _: i32, );
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, _: i32, _: i32, _: i32, ){ unreachable!() }
wit_import((self).handle() as i32, ptr0, len0, ptr1, len1, ptr2);
let l3 = *((ptr2 + 0) as *const i64);
let l4 = *((ptr2 + 8) as *const i64);
Entity{
id: EntityId{
id: l3 as u64,
},
generation: l4 as u64,
}
}
}
}
impl EcsWorld {
#[allow(unused_unsafe, clippy::all)]
pub fn view(&self,component_infos: &[ComponentInfo],) -> wit_bindgen::rt::vec::Vec::<u8>{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[repr(align(4))]
struct RetArea([u8; 8]);
let mut ret_area = ::core::mem::MaybeUninit::<RetArea>::uninit();
let vec0 = component_infos;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
let ptr1 = ret_area.as_mut_ptr() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[method]ecs-world.view"]
fn wit_import(_: i32, _: i32, _: i32, _: i32, );
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, _: i32, ){ unreachable!() }
wit_import((self).handle() as i32, ptr0, len0, ptr1);
let l2 = *((ptr1 + 0) as *const i32);
let l3 = *((ptr1 + 4) as *const i32);
let len4 = l3 as usize;
Vec::from_raw_parts(l2 as *mut _, len4, len4)
}
}
}
@ -208,7 +274,7 @@ pub mod component {
#[cfg(target_arch = "wasm32")]
#[link_section = "component-type:example"]
#[doc(hidden)]
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 514] = [0, 97, 115, 109, 13, 0, 1, 0, 0, 25, 22, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 4, 0, 7, 133, 3, 1, 65, 2, 1, 65, 9, 1, 66, 2, 1, 114, 3, 1, 120, 118, 1, 121, 118, 1, 122, 118, 4, 0, 4, 118, 101, 99, 51, 3, 0, 0, 3, 1, 23, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 109, 97, 116, 104, 5, 0, 1, 66, 15, 1, 114, 1, 2, 105, 100, 119, 4, 0, 9, 101, 110, 116, 105, 116, 121, 45, 105, 100, 3, 0, 0, 1, 114, 2, 2, 105, 100, 1, 10, 103, 101, 110, 101, 114, 97, 116, 105, 111, 110, 119, 4, 0, 6, 101, 110, 116, 105, 116, 121, 3, 0, 2, 1, 111, 2, 119, 119, 1, 114, 1, 5, 105, 110, 110, 101, 114, 4, 4, 0, 12, 119, 97, 115, 109, 45, 116, 121, 112, 101, 45, 105, 100, 3, 0, 5, 1, 114, 3, 4, 115, 105, 122, 101, 119, 9, 97, 108, 105, 103, 110, 109, 101, 110, 116, 119, 7, 116, 121, 112, 101, 45, 105, 100, 6, 4, 0, 14, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 3, 0, 7, 1, 112, 125, 1, 112, 8, 1, 64, 2, 10, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 9, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 10, 0, 3, 4, 0, 5, 115, 112, 97, 119, 110, 1, 11, 1, 64, 1, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 10, 0, 9, 4, 0, 4, 118, 105, 101, 119, 1, 12, 3, 1, 28, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 99, 115, 45, 119, 111, 114, 108, 100, 5, 1, 1, 64, 1, 3, 109, 115, 103, 115, 1, 0, 3, 0, 10, 104, 111, 115, 116, 45, 112, 114, 105, 110, 116, 1, 2, 1, 106, 1, 125, 0, 1, 64, 0, 0, 3, 4, 0, 7, 111, 110, 45, 105, 110, 105, 116, 1, 4, 4, 1, 26, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 120, 97, 109, 112, 108, 101, 4, 0, 11, 13, 1, 0, 7, 101, 120, 97, 109, 112, 108, 101, 3, 0, 0, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 50, 49, 46, 48, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 56, 46, 48];
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 608] = [0, 97, 115, 109, 13, 0, 1, 0, 0, 25, 22, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 4, 0, 7, 227, 3, 1, 65, 2, 1, 65, 9, 1, 66, 2, 1, 114, 3, 1, 120, 118, 1, 121, 118, 1, 122, 118, 4, 0, 4, 118, 101, 99, 51, 3, 0, 0, 3, 1, 23, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 109, 97, 116, 104, 5, 0, 1, 66, 20, 1, 114, 1, 2, 105, 100, 119, 4, 0, 9, 101, 110, 116, 105, 116, 121, 45, 105, 100, 3, 0, 0, 1, 114, 2, 2, 105, 100, 1, 10, 103, 101, 110, 101, 114, 97, 116, 105, 111, 110, 119, 4, 0, 6, 101, 110, 116, 105, 116, 121, 3, 0, 2, 1, 111, 2, 119, 119, 1, 114, 1, 5, 105, 110, 110, 101, 114, 4, 4, 0, 12, 119, 97, 115, 109, 45, 116, 121, 112, 101, 45, 105, 100, 3, 0, 5, 1, 114, 3, 4, 115, 105, 122, 101, 119, 9, 97, 108, 105, 103, 110, 109, 101, 110, 116, 119, 7, 116, 121, 112, 101, 45, 105, 100, 6, 4, 0, 14, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 3, 0, 7, 4, 0, 9, 101, 99, 115, 45, 119, 111, 114, 108, 100, 3, 1, 1, 105, 9, 1, 64, 0, 0, 10, 4, 0, 22, 91, 99, 111, 110, 115, 116, 114, 117, 99, 116, 111, 114, 93, 101, 99, 115, 45, 119, 111, 114, 108, 100, 1, 11, 1, 104, 9, 1, 112, 125, 1, 112, 8, 1, 64, 3, 4, 115, 101, 108, 102, 12, 10, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 13, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 14, 0, 3, 4, 0, 23, 91, 109, 101, 116, 104, 111, 100, 93, 101, 99, 115, 45, 119, 111, 114, 108, 100, 46, 115, 112, 97, 119, 110, 1, 15, 1, 64, 2, 4, 115, 101, 108, 102, 12, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 14, 0, 13, 4, 0, 22, 91, 109, 101, 116, 104, 111, 100, 93, 101, 99, 115, 45, 119, 111, 114, 108, 100, 46, 118, 105, 101, 119, 1, 16, 3, 1, 22, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 99, 115, 5, 1, 1, 64, 1, 3, 109, 115, 103, 115, 1, 0, 3, 0, 10, 104, 111, 115, 116, 45, 112, 114, 105, 110, 116, 1, 2, 1, 106, 1, 125, 0, 1, 64, 0, 0, 3, 4, 0, 7, 111, 110, 45, 105, 110, 105, 116, 1, 4, 4, 1, 26, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 120, 97, 109, 112, 108, 101, 4, 0, 11, 13, 1, 0, 7, 101, 120, 97, 109, 112, 108, 101, 3, 0, 0, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 50, 49, 46, 48, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 56, 46, 48];
#[inline(never)]
#[doc(hidden)]

View File

@ -1,7 +1,7 @@
#[allow(warnings)]
mod bindings;
use bindings::{component::witguest::ecs_world::{self, ComponentInfo, WasmTypeId}, Guest};
use bindings::{component::witguest::ecs::{ComponentInfo, EcsWorld, WasmTypeId}, Guest};
use std::{alloc::Layout, any::TypeId, mem};
@ -49,11 +49,14 @@ impl Guest for Component {
let comps = comps.as_mut_ptr().cast::<u8>();
let comps = unsafe { Vec::from_raw_parts(comps, mem::size_of::<Vec3>(), 1) };
let en = ecs_world::spawn(&comps, info);
let world = EcsWorld::new();
let en = world.spawn(&comps, info);
//let en = ecs_world::spawn(&comps, info);
bindings::host_print(&format!("Spawned entity: {}", en.id.id));
let mut buffer = ecs_world::view(&[Vec3::component_info()]);
let mut buffer = world.view(&[Vec3::component_info()]);
bindings::host_print(&format!("Retrieved vec3 of {} bytes long", buffer.len()));
let fetched_vec = unsafe { std::ptr::read(buffer.as_mut_ptr().cast::<Vec3>()) };
bindings::host_print(&format!("Retrieved vec3: {:?}", fetched_vec));

View File

@ -8,7 +8,7 @@ interface math {
}
}
interface ecs-world {
interface ecs {
//use math.{vec3};
record entity-id {
@ -30,15 +30,19 @@ interface ecs-world {
type-id: wasm-type-id, // a u128
}
// expects components to be tightly packed in the same order of component-infos
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
view: func(component-infos: list<component-info>) -> list<u8>;
resource ecs-world {
constructor();
// expects components to be tightly packed in the same order of component-infos
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
view: func(component-infos: list<component-info>) -> list<u8>;
}
}
/// An example world for the component to target.
world example {
import math;
import ecs-world;
import ecs;
import host-print: func(msg: string);
export on-init: func() -> result<u8>;