summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-07-03 15:33:08 -0400
committerMica White <botahamec@outlook.com>2026-07-03 15:33:08 -0400
commit73ffbee638c03da132aea0e5d600ca5e8144fd8e (patch)
tree4ea897e8b3aaad2876625e241e740493e9b5b8b8 /src
parentbb7525206da1782fd9af49621aa52d87bd227838 (diff)
Support WASM, kinda
Diffstat (limited to 'src')
-rw-r--r--src/file.rs16
-rw-r--r--src/main.rs27
-rw-r--r--src/scene.rs7
-rw-r--r--src/script.rs138
4 files changed, 112 insertions, 76 deletions
diff --git a/src/file.rs b/src/file.rs
index 42fecfa..2963879 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -1,4 +1,4 @@
-use std::{ffi::OsStr, path::Path, sync::Arc};
+use std::{ffi::OsStr, path::Path};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
@@ -9,7 +9,6 @@ use crate::scene::{Scene, scene_id_by_name};
type FileTask = Task<std::io::Result<FileData>>;
pub struct FileManager {
- pub wasm_compiler: Arc<wasmi::Engine>,
active_tasks: FxHashMap<Box<Path>, FileTask>,
cache: FxHashMap<Box<Path>, FileData>,
}
@@ -87,17 +86,20 @@ impl FileData {
impl FileManager {
pub fn new() -> Self {
Self {
- wasm_compiler: Arc::new(wasmi::Engine::default()),
active_tasks: FxHashMap::default(),
cache: FxHashMap::default(),
}
}
- pub fn load_file(&mut self, path: Box<Path>) -> std::io::Result<&FileData> {
+ pub fn load_file(
+ &mut self,
+ path: Box<Path>,
+ wasm_compiler: &wasmi::Engine,
+ ) -> std::io::Result<&FileData> {
let data = compile_data(
path.as_ref(),
&std::fs::read(path.as_ref()).map(Vec::into_boxed_slice)?,
- &self.wasm_compiler.clone(),
+ wasm_compiler,
)?;
self.cache.entry(path.clone()).insert_entry(data);
Ok(self.cache.get(&path).unwrap())
@@ -120,9 +122,9 @@ impl FileManager {
}
}
- pub fn start_loading_file(&mut self, path: impl AsRef<Path>) {
+ pub fn start_loading_file(&mut self, path: impl AsRef<Path>, wasm_compiler: &wasmi::Engine) {
let boxed_path = Box::from(path.as_ref());
- let compiler = self.wasm_compiler.clone();
+ let compiler = wasm_compiler.clone();
let task = unblock(move || {
compile_data(
&boxed_path,
diff --git a/src/main.rs b/src/main.rs
index 81fffcf..7c4f249 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,22 +7,21 @@ use tortuise::{
file::FileManager,
input::InputManager,
scene::switch_scenes,
- script::{ScriptContext, ScriptDeclaration, ScriptManager},
+ script::{ScriptContext, ScriptDeclaration, ScriptManager, WasmRunner},
};
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn get_timestamp() -> u128 {
- SystemTime::UNIX_EPOCH.elapsed().unwrap().as_nanos()
+ SystemTime::UNIX_EPOCH.elapsed().unwrap().as_micros()
}
-fn init_first_frame<'a>(_: &mut ScriptContext<'a>, state: &mut Box<[u8]>) {
+fn init_first_frame(_: &mut ScriptContext, state: &mut Box<[u8]>) {
*state = Box::from(get_timestamp().to_ne_bytes());
}
-fn render_frame_rate<'a>(ctx: &mut ScriptContext<'a>, state: &mut Box<[u8]>) {
- ctx.buffer.cells.clear();
+fn render_frame_rate(ctx: &mut ScriptContext, state: &mut Box<[u8]>) {
let end = get_timestamp();
let last_frame_start = u128::from_ne_bytes(*state.as_array().unwrap());
let frame_time = end - last_frame_start + 1;
@@ -53,6 +52,14 @@ fn exit_on_escape(ctx: &mut ScriptContext, _: &mut Box<[u8]>) {
}
fn main() {
+ let mut files = FileManager::default();
+ let wasm_runner = WasmRunner::new();
+ let manifest = files
+ .load_file(Box::from("manifest.toml".as_ref()), &wasm_runner.engine)
+ .unwrap()
+ .unwrap_manifest()
+ .clone();
+
let stdout = Box::new(std::io::stdout().lock());
let mut buffer = Buffer::new(stdout).unwrap();
let mut input = InputManager::new();
@@ -60,6 +67,7 @@ fn main() {
buffer: &mut buffer,
input: &input,
};
+
let scripts = [
ScriptDeclaration::Static {
start: Some(init_first_frame),
@@ -74,17 +82,12 @@ fn main() {
];
let mut scripts = ScriptManager::new(&scripts);
- let mut files = FileManager::default();
- let manifest = files
- .load_file(Box::from("manifest.toml".as_ref()))
- .unwrap()
- .unwrap_manifest()
- .clone();
switch_scenes(
&manifest.scenes,
&mut scripts,
&mut context,
&mut files,
+ &wasm_runner,
manifest.first_scene,
);
@@ -100,7 +103,7 @@ fn main() {
buffer,
input: &input,
};
- scripts.run_scripts(&mut context);
+ scripts.run_scripts(&mut context, &wasm_runner, &mut files);
input.reset_next_frame();
}
})
diff --git a/src/scene.rs b/src/scene.rs
index 8f70350..34ff877 100644
--- a/src/scene.rs
+++ b/src/scene.rs
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::{
file::FileManager,
- script::{ScriptContext, ScriptManager},
+ script::{ScriptContext, ScriptManager, WasmRunner},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -33,15 +33,16 @@ pub fn switch_scenes(
scripts: &mut ScriptManager,
context: &mut ScriptContext<'_>,
files: &mut FileManager,
+ wasm_runner: &WasmRunner,
new: usize,
) {
let active_scripts = FxHashSet::from_iter(scripts.active_scripts.iter().cloned());
let scripts_to_load = &scenes[new].scripts.required;
for script in scripts_to_load {
if !active_scripts.contains(script) {
- scripts.activate_script(*script, context, files);
+ scripts.activate_script(*script, context, wasm_runner, files);
} else {
- scripts.reset_script(*script, context);
+ scripts.reset_script(*script, context, wasm_runner, files);
}
}
}
diff --git a/src/script.rs b/src/script.rs
index eeb943f..e1634a6 100644
--- a/src/script.rs
+++ b/src/script.rs
@@ -1,7 +1,6 @@
use std::path::Path;
use rustc_hash::FxHashMap;
-use wasmer::{Imports, Instance, Module, Store};
use crate::{buffer::Buffer, file::FileManager, input::InputManager};
@@ -11,12 +10,17 @@ pub struct ScriptManager<'a> {
script_states: FxHashMap<usize, Box<[u8]>>,
}
+pub struct WasmRunner {
+ pub engine: wasmi::Engine,
+ pub linker: wasmi::Linker<&'static ScriptContext<'static>>,
+}
+
pub struct ScriptContext<'a> {
pub buffer: &'a mut Buffer,
pub input: &'a InputManager,
}
-type StaticScriptFunction = Option<fn(&mut ScriptContext, &mut Box<[u8]>)>;
+type StaticScriptFunction = Option<fn(&mut ScriptContext<'_>, &mut Box<[u8]>)>;
pub enum ScriptDeclaration {
Static {
@@ -29,17 +33,6 @@ pub enum ScriptDeclaration {
},
}
-pub struct WasmScript {
- pub module: Module,
- pub instance: Instance,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Profile {
- Debug,
- Release,
-}
-
impl<'a> ScriptContext<'a> {
pub fn exit(&mut self) -> ! {
let _ = self.buffer.finish();
@@ -51,6 +44,42 @@ fn get_script_state(states: &mut FxHashMap<usize, Box<[u8]>>, script_id: usize)
states.entry(script_id).or_insert(Box::from([]))
}
+impl Default for WasmRunner {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl WasmRunner {
+ pub fn new() -> Self {
+ let engine = wasmi::Engine::default();
+ let linker = wasmi::Linker::new(&engine);
+ Self { engine, linker }
+ }
+
+ pub fn compile(&self, data: &[u8]) -> Result<wasmi::Module, wasmi::Error> {
+ wasmi::Module::new(&self.engine, data)
+ }
+
+ pub fn run_function(
+ &self,
+ module: &wasmi::Module,
+ context: &mut ScriptContext<'_>,
+ function_name: &str,
+ ) -> Result<(), wasmi::Error> {
+ // safety: the store won't be kept around after this function ends. Only the
+ // lifetimes are being transmuted
+ let mut store = wasmi::Store::new(&self.engine, unsafe {
+ std::mem::transmute::<&ScriptContext<'_>, &ScriptContext<'_>>(context)
+ });
+ let instance = self.linker.instantiate_and_start(&mut store, module)?;
+ let function = instance.get_func(&store, function_name).ok_or_else(|| {
+ wasmi::Error::new(format!("No function named {function_name} was exported"))
+ })?;
+ function.call(&mut store, &[], &mut [])
+ }
+}
+
impl<'a> ScriptManager<'a> {
pub fn new(scripts: &'a [ScriptDeclaration]) -> Self {
Self {
@@ -64,6 +93,7 @@ impl<'a> ScriptManager<'a> {
&mut self,
script_id: usize,
context: &mut ScriptContext<'_>,
+ wasm_runner: &WasmRunner,
files: &mut FileManager,
) {
let Some(script_declaration) = self.scripts.get(script_id) else {
@@ -79,19 +109,23 @@ impl<'a> ScriptManager<'a> {
});
}
ScriptDeclaration::Wasm { path } => {
- todo!("wasm scripts")
- // let module = files.load_file(path.clone()).unwrap().unwrap_wasm_module();
- // let mut store = wasmi::Store::new(&files.wasm_compiler, context);
- // let instance = self
- // .linker
- // .instantiate_and_start(&mut store, &module)
- // .unwrap();
+ let module = files
+ .load_file(path.clone(), &wasm_runner.engine)
+ .unwrap()
+ .unwrap_wasm_module();
+ wasm_runner.run_function(module, context, "start");
}
}
self.active_scripts.push(script_id);
}
- pub fn deactivate_script(&mut self, script_id: usize, context: &mut ScriptContext<'_>) {
+ pub fn deactivate_script(
+ &mut self,
+ script_id: usize,
+ context: &mut ScriptContext<'_>,
+ wasm_runner: &WasmRunner,
+ files: &mut FileManager,
+ ) {
self.active_scripts.retain(|element| {
if *element != script_id {
let Some(script) = self.scripts.get(script_id) else {
@@ -106,7 +140,13 @@ impl<'a> ScriptManager<'a> {
);
});
}
- ScriptDeclaration::Wasm { .. } => todo!("wasm scripts"),
+ ScriptDeclaration::Wasm { path } => {
+ let module = files
+ .load_file(path.clone(), &wasm_runner.engine)
+ .unwrap()
+ .unwrap_wasm_module();
+ wasm_runner.run_function(module, context, "end");
+ }
};
true
@@ -116,12 +156,17 @@ impl<'a> ScriptManager<'a> {
});
}
- pub fn run_scripts(&mut self, context: &mut ScriptContext<'_>) {
+ pub fn run_scripts(
+ &mut self,
+ context: &mut ScriptContext<'_>,
+ wasm_runner: &WasmRunner,
+ files: &mut FileManager,
+ ) {
for script_id in &self.active_scripts {
let Some(script) = self.scripts.get(*script_id) else {
unreachable!("The active script does not exist");
};
- match *script {
+ match script {
ScriptDeclaration::Static { update, .. } => {
if let Some(s) = update {
s(
@@ -130,40 +175,25 @@ impl<'a> ScriptManager<'a> {
)
}
}
- ScriptDeclaration::Wasm { .. } => todo!("wasm scripts"),
- }
- }
- }
-
- pub fn reset_script(&mut self, script_id: usize, context: &mut ScriptContext<'_>) {
- let script = &self.scripts[script_id];
- match script {
- ScriptDeclaration::Static { start, end, .. } => {
- if let Some(s) = start {
- s(
- context,
- get_script_state(&mut self.script_states, script_id),
- )
- }
- if let Some(s) = end {
- s(
- context,
- get_script_state(&mut self.script_states, script_id),
- )
+ ScriptDeclaration::Wasm { path } => {
+ let module = files
+ .load_file(path.clone(), &wasm_runner.engine)
+ .unwrap()
+ .unwrap_wasm_module();
+ wasm_runner.run_function(module, context, "update");
}
}
- ScriptDeclaration::Wasm { .. } => todo!("wasm scripts"),
}
}
-}
-impl WasmScript {
- fn call_function(&self, name: &str, profile: Profile, store: &mut Store, imports: &Imports) {
- let instance = match profile {
- Profile::Debug => &Instance::new(store, &self.module, imports).unwrap(),
- Profile::Release => &self.instance,
- };
- let function = instance.exports.get_function(name).unwrap();
- function.call(store, &[]).unwrap();
+ pub fn reset_script(
+ &mut self,
+ script_id: usize,
+ context: &mut ScriptContext<'_>,
+ wasm_runner: &WasmRunner,
+ files: &mut FileManager,
+ ) {
+ self.activate_script(script_id, context, wasm_runner, files);
+ self.deactivate_script(script_id, context, wasm_runner, files);
}
}