diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 9e9f408..5844428 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -22,7 +22,7 @@ pub fn make_default_backend() -> anyhow::Result> { /// A WebAssembly runtime backend. pub trait Backend { - fn load_module(&self, module: &[u8]) -> anyhow::Result>; + fn load_module(&self, abi: ScriptAbi, module: &[u8]) -> anyhow::Result>; } /// An instance of a WebAssembly module. @@ -59,3 +59,101 @@ pub trait Instance { fn on_message(&self, panel_ud: u32, msg: Vec); } + +#[derive(Default)] +pub struct ScriptAbi { + draw_cmds: Mutex>, + font_store: Arc, + font_families: Mutex>, + loaded_fonts: RwLock>>, + text_layouts: RwLock>, + message_store: RwLock>>, +} + +impl ScriptAbi { + pub fn new(font_store: Arc) -> Self { + Self { + font_store, + ..Default::default() + } + } + + pub fn start_draw(&self) { + let mut lock = self.draw_cmds.lock(); + lock.clear(); + } + + pub fn draw_indexed(&self, vertices: &[MeshVertex], indices: &[MeshIndex]) { + self.draw_cmds.lock().push(DrawCommand::Mesh { + vertices: vertices.to_vec(), + indices: indices.to_vec(), + }) + } + + pub fn draw_text_layout(&self, id: u32, offset: Vec2, scale: f32, color: Color) { + // TODO multiple fonts per layout + let layouts = self.text_layouts.read(); + let layout = layouts.get(id as usize).unwrap(); + let glyphs = layout.glyphs.as_slice(); + let loaded = self.loaded_fonts.read(); + let font = loaded.get(layout.font_id as usize).unwrap(); + let cmds = font.draw(glyphs, offset, scale, color); + self.draw_cmds.lock().extend(cmds.into_iter()); + } + + pub fn with_draw_commands(&self, f: impl FnOnce(&[DrawCommand])) { + f(self.draw_cmds.lock().as_slice()); + } + + pub fn font_load(&self, family: &str) -> u32 { + let mut family_cache = self.font_families.lock(); + + if let Some(cached) = family_cache.get(family) { + return *cached; + } + + let font = self.font_store.load_font(family); + let mut loaded = self.loaded_fonts.write(); + let id = loaded.len() as u32; + family_cache.insert(family.to_string(), id); + loaded.push(font); + id + } + + pub fn text_layout_new(&self, font_id: u32, text: &str) -> u32 { + let loaded = self.loaded_fonts.read(); + let font = loaded.get(font_id as usize).unwrap(); + let layout = font.shape(text); + self.text_layouts.write().insert(layout) as u32 + } + + pub fn text_layout_delete(&self, id: u32) { + self.text_layouts.write().remove(id as usize); + } + + pub fn text_layout_get_bounds(&self, id: u32, dst: &mut Rect) { + let src = self.text_layouts.read().get(id as usize).unwrap().bounds; + let _ = std::mem::replace(dst, src); + } + + pub fn message_new(&self, data: Vec) -> u32 { + let mut store = self.message_store.write(); + let id = store.insert(data) as u32; + id + } + + pub fn message_free(&self, id: u32) { + let mut store = self.message_store.write(); + store.remove(id as usize); + } + + pub fn message_get_len(&self, id: u32) -> u32 { + self.message_store.read().get(id as usize).unwrap().len() as u32 + } + + pub fn message_get_data(&self, id: u32, dst: &mut [u8]) { + let store = self.message_store.read(); + let src = store.get(id as usize).unwrap(); + dst.copy_from_slice(src); + } +} diff --git a/src/backend/wasmtime.rs b/src/backend/wasmtime.rs index 74f5f85..5af182a 100644 --- a/src/backend/wasmtime.rs +++ b/src/backend/wasmtime.rs @@ -3,15 +3,15 @@ use std::ops::DerefMut; -use super::{Arc, Backend, Instance, PanelId}; -use crate::{DrawCommand, ScriptAbi, ScriptAbiImpl}; +use super::{Arc, Backend, Instance, PanelId, ScriptAbi}; +use crate::DrawCommand; use canary_script::{Color, CursorEventKind, Rect, Vec2}; use parking_lot::Mutex; -type Caller<'a> = wasmtime::Caller<'a, ScriptAbiImpl>; -type Store = wasmtime::Store; -type Linker = wasmtime::Linker; +type Caller<'a> = wasmtime::Caller<'a, ScriptAbi>; +type Store = wasmtime::Store; +type Linker = wasmtime::Linker; pub struct WasmtimeBackend { engine: wasmtime::Engine, @@ -30,9 +30,8 @@ impl WasmtimeBackend { } impl Backend for WasmtimeBackend { - fn load_module(&self, module: &[u8]) -> anyhow::Result> { + fn load_module(&self, abi: ScriptAbi, module: &[u8]) -> anyhow::Result> { 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)?; diff --git a/src/lib.rs b/src/lib.rs index c46fc26..c32bd15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,20 +10,26 @@ use std::sync::Arc; pub mod backend; pub mod text; -use backend::{Backend, Instance}; +use backend::{Backend, Instance, ScriptAbi}; +use text::FontStore; /// The main interface to Canary. pub struct Runtime { backend: Box, + font_store: Arc, } impl Runtime { pub fn new(backend: Box) -> anyhow::Result { - Ok(Self { backend }) + Ok(Self { + backend, + font_store: Arc::new(FontStore::new()), + }) } pub fn load_module(&self, module: &[u8]) -> anyhow::Result