228 lines
7.5 KiB
Rust
228 lines
7.5 KiB
Rust
use std::ops::DerefMut;
|
|
|
|
use super::{Arc, Backend, Instance, PanelId};
|
|
use crate::{DrawCommand, ScriptAbi, ScriptAbiImpl};
|
|
|
|
use canary_script::{Color, CursorEventKind, Rect, Vec2};
|
|
use parking_lot::Mutex;
|
|
|
|
type Caller<'a> = wasmtime::Caller<'a, ScriptAbiImpl>;
|
|
type Store = wasmtime::Store<ScriptAbiImpl>;
|
|
type Linker = wasmtime::Linker<ScriptAbiImpl>;
|
|
|
|
pub struct WasmtimeBackend {
|
|
engine: wasmtime::Engine,
|
|
}
|
|
impl WasmtimeBackend {
|
|
pub fn new() -> anyhow::Result<Self> {
|
|
let mut config = wasmtime::Config::new();
|
|
config.wasm_simd(true);
|
|
config.wasm_bulk_memory(true);
|
|
config.cranelift_opt_level(wasmtime::OptLevel::Speed);
|
|
|
|
let engine = wasmtime::Engine::new(&config)?;
|
|
|
|
Ok(Self { engine })
|
|
}
|
|
}
|
|
|
|
impl Backend for WasmtimeBackend {
|
|
fn load_module(&self, module: &[u8]) -> anyhow::Result<Arc<dyn Instance>> {
|
|
let module = wasmtime::Module::new(&self.engine, module)?;
|
|
let abi = ScriptAbiImpl::default();
|
|
let mut store = wasmtime::Store::new(&self.engine, abi);
|
|
let mut linker = Linker::new(&self.engine);
|
|
WasmtimeInstance::link(&mut linker)?;
|
|
let instance = linker.instantiate(&mut store, &module)?;
|
|
let bind_panel = instance.get_typed_func(&mut store, "bind_panel")?;
|
|
let update = instance.get_typed_func(&mut store, "update")?;
|
|
let draw = instance.get_typed_func(&mut store, "draw")?;
|
|
let on_cursor_event = instance.get_typed_func(&mut store, "on_cursor_event")?;
|
|
let on_message = instance.get_typed_func(&mut store, "on_message")?;
|
|
|
|
let instance = WasmtimeInstance {
|
|
store: Mutex::new(store),
|
|
bind_panel,
|
|
update,
|
|
draw,
|
|
on_cursor_event,
|
|
on_message,
|
|
};
|
|
|
|
let instance = Arc::new(instance);
|
|
|
|
Ok(instance)
|
|
}
|
|
}
|
|
|
|
pub struct WasmtimeInstance {
|
|
store: Mutex<Store>,
|
|
bind_panel: wasmtime::TypedFunc<(u32, u32), u32>,
|
|
update: wasmtime::TypedFunc<(u32, f32), ()>,
|
|
draw: wasmtime::TypedFunc<u32, ()>,
|
|
on_cursor_event: wasmtime::TypedFunc<(u32, u32, f32, f32), ()>,
|
|
on_message: wasmtime::TypedFunc<(u32, u32), ()>,
|
|
}
|
|
|
|
impl WasmtimeInstance {
|
|
pub fn link(linker: &mut Linker) -> anyhow::Result<()> {
|
|
let module = "env";
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"draw_indexed",
|
|
|mut caller: Caller<'_>,
|
|
vertices_ptr: u32,
|
|
vertices_num: u32,
|
|
indices_ptr: u32,
|
|
indices_num: u32| {
|
|
let vertices = Self::get_memory_slice(&mut caller, vertices_ptr, vertices_num);
|
|
let indices = Self::get_memory_slice(&mut caller, indices_ptr, indices_num);
|
|
caller.data().draw_indexed(vertices, indices);
|
|
},
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"draw_text_layout",
|
|
|caller: Caller<'_>, id: u32, xoff: f32, yoff: f32, scale: f32, color: u32| {
|
|
let offset = Vec2 { x: xoff, y: yoff };
|
|
let color = Color(color);
|
|
caller.data().draw_text_layout(id, offset, scale, color);
|
|
},
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"font_load",
|
|
|mut caller: Caller<'_>, family_ptr: u32, family_len: u32| {
|
|
let family = Self::get_memory_slice_str(&mut caller, family_ptr, family_len);
|
|
caller.data().font_load(family)
|
|
},
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"text_layout_new",
|
|
|mut caller: Caller<'_>, font_id: u32, text_ptr: u32, text_len: u32| {
|
|
let text = Self::get_memory_slice_str(&mut caller, text_ptr, text_len);
|
|
caller.data().text_layout_new(font_id, text)
|
|
},
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"text_layout_delete",
|
|
|caller: Caller<'_>, id: u32| caller.data().text_layout_delete(id),
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"text_layout_get_bounds",
|
|
|mut caller: Caller<'_>, id: u32, rect_ptr: u32| {
|
|
let rect: &mut Rect = Self::get_memory_ref(&mut caller, rect_ptr);
|
|
caller.data().text_layout_get_bounds(id, rect);
|
|
},
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"message_get_len",
|
|
|caller: Caller<'_>, id: u32| -> u32 { caller.data().message_get_len(id) },
|
|
)?;
|
|
|
|
linker.func_wrap(
|
|
module,
|
|
"message_get_data",
|
|
|mut caller: Caller<'_>, id: u32, ptr: u32| {
|
|
let ptr = ptr as usize;
|
|
let len = caller.data().message_get_len(id) as usize;
|
|
let dst = Self::get_memory_slice_bytes(&mut caller, ptr, len);
|
|
caller.data().message_get_data(id, dst)
|
|
},
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn get_memory_ref<D: bytemuck::Pod>(caller: &mut Caller<'_>, ptr: u32) -> &'static mut D {
|
|
let len = std::mem::size_of::<D>();
|
|
let bytes = Self::get_memory_slice_bytes(caller, ptr as usize, len);
|
|
bytemuck::from_bytes_mut(bytes)
|
|
}
|
|
|
|
fn get_memory_slice<D: bytemuck::Pod>(
|
|
caller: &mut Caller<'_>,
|
|
ptr: u32,
|
|
num: u32,
|
|
) -> &'static mut [D] {
|
|
let len = num as usize * std::mem::size_of::<D>();
|
|
let bytes = Self::get_memory_slice_bytes(caller, ptr as usize, len);
|
|
bytemuck::cast_slice_mut(bytes)
|
|
}
|
|
|
|
fn get_memory_slice_str(caller: &mut Caller<'_>, ptr: u32, len: u32) -> &'static mut str {
|
|
let memory = Self::get_memory_slice_bytes(caller, ptr as usize, len as usize);
|
|
std::str::from_utf8_mut(memory).unwrap()
|
|
}
|
|
|
|
fn get_memory_slice_bytes(
|
|
caller: &mut Caller<'_>,
|
|
ptr: usize,
|
|
len: usize,
|
|
) -> &'static mut [u8] {
|
|
let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
|
|
if ptr + len > memory.data_size(&caller) {
|
|
panic!("Attempted wasm memory read is out-of-bounds!");
|
|
}
|
|
|
|
unsafe {
|
|
let ptr = memory.data_ptr(caller).add(ptr);
|
|
std::slice::from_raw_parts_mut(ptr, len)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Instance for WasmtimeInstance {
|
|
fn bind_panel(&self, panel: PanelId, msg: Vec<u8>) -> u32 {
|
|
let mut store = self.store.lock();
|
|
let msg = store.data().message_new(msg);
|
|
let args = (panel.0 as u32, msg);
|
|
let data = self.bind_panel.call(store.deref_mut(), args).unwrap();
|
|
store.data().message_free(msg);
|
|
data
|
|
}
|
|
|
|
fn update(&self, panel_ud: u32, dt: f32) {
|
|
let mut store = self.store.lock();
|
|
self.update.call(store.deref_mut(), (panel_ud, dt)).unwrap();
|
|
}
|
|
|
|
fn draw(&self, panel_ud: u32) -> Vec<DrawCommand> {
|
|
let mut store = self.store.lock();
|
|
store.data().start_draw();
|
|
self.draw.call(store.deref_mut(), panel_ud).unwrap();
|
|
let mut cmds = Vec::new();
|
|
store
|
|
.data()
|
|
.with_draw_commands(|slice| cmds.extend_from_slice(slice));
|
|
cmds
|
|
}
|
|
|
|
fn on_cursor_event(&self, panel_ud: u32, kind: CursorEventKind, at: Vec2) {
|
|
let mut store = self.store.lock();
|
|
self.on_cursor_event
|
|
.call(store.deref_mut(), (panel_ud, kind as u32, at.x, at.y))
|
|
.unwrap();
|
|
}
|
|
|
|
fn on_message(&self, panel_ud: u32, msg: Vec<u8>) {
|
|
let mut store = self.store.lock();
|
|
let msg = store.data().message_new(msg);
|
|
self.on_message
|
|
.call(store.deref_mut(), (panel_ud, msg))
|
|
.unwrap();
|
|
store.data().message_free(msg);
|
|
}
|
|
}
|