YieldFrame: pause wasm environment till end of frame
This commit is contained in:
parent
13b61a6def
commit
03a8ae9a24
@ -7,8 +7,17 @@ use crate::go_builder::GoBuilder;
|
||||
mod go_builder;
|
||||
|
||||
fn main() -> VResult<()> {
|
||||
let builder = GoBuilder::new(&PathBuf::from("example_project"))?;
|
||||
let builder = GoBuilder::new(
|
||||
&PathBuf::from("example_project"))?;
|
||||
let wasm = builder.build()?;
|
||||
player::run_entry_point(&wasm)?;
|
||||
let mut executor = player::Executor::new(&wasm)?;
|
||||
|
||||
while executor.is_running() {
|
||||
println!("update started");
|
||||
executor.update();
|
||||
println!("update completed");
|
||||
executor.get_error()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
31
crates/player/src/engine_api.rs
Normal file
31
crates/player/src/engine_api.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use viperid::VResult;
|
||||
use wasmi::{core::{HostError, Trap}, Caller, Linker};
|
||||
|
||||
use crate::executor::ExecutorState;
|
||||
|
||||
pub(crate) fn integrate(linker: &mut Linker<ExecutorState>) -> VResult<()> {
|
||||
linker.func_wrap("viperid", "YieldFrame", yield_frame)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn yield_frame(caller: Caller<ExecutorState>) -> Result<(), Trap> {
|
||||
Err(Trap::from(YieldFrame {}))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct YieldFrame {
|
||||
|
||||
}
|
||||
|
||||
impl Display for YieldFrame {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("<device wants to display a frame>")
|
||||
}
|
||||
}
|
||||
|
||||
impl HostError for YieldFrame {
|
||||
|
||||
}
|
@ -1,9 +1,16 @@
|
||||
use std::{fmt::Display, mem};
|
||||
|
||||
use viperid::VResult;
|
||||
use wasmi::{Engine, Linker, Module, Store};
|
||||
use wasmi::{core::{HostError, Trap}, Engine, Linker, Module, Store, TypedFunc, TypedResumableCall, TypedResumableInvocation};
|
||||
|
||||
use crate::wasi::{self, StockWasi};
|
||||
use crate::{engine_api::{self, YieldFrame}, wasi::{self, StockWasi}};
|
||||
|
||||
struct ExecutorState {
|
||||
pub struct Executor {
|
||||
store: Store<ExecutorState>,
|
||||
resumable: Resumable
|
||||
}
|
||||
|
||||
pub(crate) struct ExecutorState {
|
||||
wasi: wasi::StockWasi
|
||||
}
|
||||
|
||||
@ -16,22 +23,105 @@ impl ExecutorState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_entry_point(wasm: &[u8]) -> VResult<()> {
|
||||
let engine = Engine::default();
|
||||
|
||||
let module = Module::new(&engine, wasm)?;
|
||||
impl Executor {
|
||||
pub fn new(wasm: &[u8]) -> VResult<Self> {
|
||||
let engine = Engine::default();
|
||||
|
||||
let mut store = Store::new(&engine, ExecutorState::new());
|
||||
let module = Module::new(&engine, wasm)?;
|
||||
|
||||
let mut linker = <Linker<ExecutorState>>::new(&engine);
|
||||
wasi::integrate(&mut linker, |hs| &mut hs.wasi)?;
|
||||
let mut store = Store::new(&engine, ExecutorState::new());
|
||||
|
||||
let instance = linker
|
||||
.instantiate(&mut store, &module)?
|
||||
.start(&mut store)?;
|
||||
let mut linker = <Linker<ExecutorState>>::new(&engine);
|
||||
wasi::integrate(&mut linker, |hs| &mut hs.wasi)?;
|
||||
engine_api::integrate(&mut linker);
|
||||
|
||||
let main = instance.get_typed_func::<(), ()>(&store, "_start")?;
|
||||
main.call(&mut store, ())?;
|
||||
let instance = linker
|
||||
.instantiate(&mut store, &module)?
|
||||
.start(&mut store)?;
|
||||
|
||||
Ok(())
|
||||
let main = instance.get_typed_func::<(), ()>(&store, "_start")?;
|
||||
|
||||
Ok(Executor {
|
||||
store,
|
||||
resumable: Resumable::Unstarted(main)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.resumable.is_running()
|
||||
}
|
||||
|
||||
pub fn get_error(&self) -> VResult<()> {
|
||||
if let Err(e) = self.resumable.get_error() {
|
||||
anyhow::bail!("executor error: {}", e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
let mut tmp = Resumable::Failed(wasmi::Error::Trap(Trap::new("unexpected crash during update")));
|
||||
mem::swap(&mut tmp, &mut self.resumable);
|
||||
let mut tmp2 = tmp.resume(&mut self.store);
|
||||
mem::swap(&mut tmp2, &mut self.resumable);
|
||||
}
|
||||
}
|
||||
|
||||
enum Resumable {
|
||||
Unstarted(TypedFunc<(), ()>),
|
||||
Paused(TypedResumableInvocation<()>),
|
||||
Failed(wasmi::Error),
|
||||
Completed(())
|
||||
}
|
||||
|
||||
|
||||
impl Resumable {
|
||||
pub fn resume(self, store: &mut Store<ExecutorState>) -> Resumable {
|
||||
let result: Result<TypedResumableCall<()>, wasmi::Error> = match self {
|
||||
Resumable::Failed(f) => return Resumable::Failed(f),
|
||||
Resumable::Completed(c) => return Resumable::Completed(c),
|
||||
Resumable::Unstarted(u) => {
|
||||
match u.call_resumable(store, ()) {
|
||||
Ok(o) => Ok(o),
|
||||
Err(e) => Err(wasmi::Error::Trap(e))
|
||||
}
|
||||
}
|
||||
Resumable::Paused(p) => {
|
||||
let trap = p.host_error();
|
||||
|
||||
if let Some(_) = trap.downcast_ref::<YieldFrame>() {
|
||||
p.resume(store, &[])
|
||||
} else {
|
||||
Err(wasmi::Error::Trap(Trap::new(format!("unexpected host error: {}", trap))))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(o) => match o {
|
||||
TypedResumableCall::Finished(f) => Resumable::Completed(f),
|
||||
TypedResumableCall::Resumable(p) => Resumable::Paused(p)
|
||||
}
|
||||
Err(e) => Resumable::Failed(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
match self {
|
||||
Resumable::Unstarted(_) => true,
|
||||
Resumable::Paused(_) => true,
|
||||
Resumable::Failed(_) => false,
|
||||
Resumable::Completed(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error(&self) -> Result<(), &wasmi::Error> {
|
||||
match self {
|
||||
Resumable::Unstarted(_) => (),
|
||||
Resumable::Paused(_) => (),
|
||||
Resumable::Failed(e) => return Err(e),
|
||||
Resumable::Completed(_) => ()
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
mod engine_api;
|
||||
mod executor;
|
||||
mod wasi;
|
||||
|
||||
pub use executor::run_entry_point;
|
||||
pub use executor::Executor;
|
1
example_project/.gitignore
vendored
1
example_project/.gitignore
vendored
@ -0,0 +1 @@
|
||||
build/
|
Binary file not shown.
1
example_project/go.work
Normal file
1
example_project/go.work
Normal file
@ -0,0 +1 @@
|
||||
go 1.21.5
|
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -10,7 +9,13 @@ func main() {
|
||||
for {
|
||||
fmt.Printf("Hello! %d", count)
|
||||
count++
|
||||
debug.PrintStack()
|
||||
panic("pizza!")
|
||||
/*
|
||||
debug.PrintStack()
|
||||
*/
|
||||
YieldFrame()
|
||||
}
|
||||
}
|
||||
|
||||
//go:wasmimport viperid YieldFrame
|
||||
//go:noescape
|
||||
func YieldFrame()
|
||||
|
Loading…
Reference in New Issue
Block a user