Skip to content

Commit

Permalink
Send serialized stuff to webworkers
Browse files Browse the repository at this point in the history
  • Loading branch information
mkeeter committed Apr 20, 2024
1 parent b16b1e1 commit 7f7a01b
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 49 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions fidget/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ordered-float = "3"
static_assertions = "1"
thiserror = "1"
workspace-hack = { version = "0.1", path = "../workspace-hack" }
serde = { version = "1.0", features = ["derive"] }

# JIT
dynasmrt = { version = "2.0", optional = true }
Expand Down
6 changes: 4 additions & 2 deletions fidget/src/core/compiler/op.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use serde::{Deserialize, Serialize};

/// Macro to generate a set of opcodes, using the given type for registers
macro_rules! opcodes {
(
Expand Down Expand Up @@ -139,7 +141,7 @@ opcodes!(
/// - RHS register (or immediate for `*Imm`)
///
/// Each "register" represents an SSA slot, which is never reused.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum SsaOp<u32> {
// default variants
}
Expand Down Expand Up @@ -250,7 +252,7 @@ opcodes!(
///
/// We have a maximum of 256 registers, though some tapes (e.g. ones
/// targeting physical hardware) may choose to use fewer.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum RegOp<u8> {
// default variants
/// Read from a memory slot to a register
Expand Down
3 changes: 2 additions & 1 deletion fidget/src/core/compiler/reg_tape.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! Tape used for evaluation
use crate::compiler::{RegOp, RegisterAllocator, SsaTape};
use serde::{Deserialize, Serialize};

/// Low-level tape for use with the Fidget virtual machine (or to be lowered
/// further into machine instructions).
#[derive(Clone, Default)]
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct RegTape {
tape: Vec<RegOp>,

Expand Down
3 changes: 2 additions & 1 deletion fidget/src/core/compiler/ssa_tape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
context::{BinaryOpcode, Node, Op, UnaryOpcode},
Context, Error,
};
use serde::{Deserialize, Serialize};

use std::collections::{HashMap, HashSet};

Expand All @@ -16,7 +17,7 @@ use std::collections::{HashMap, HashSet};
/// - 4-byte RHS register (or immediate `f32`)
///
/// All register addressing is absolute.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SsaTape {
/// The tape is stored in reverse order, such that the root of the tree is
/// the first item in the tape.
Expand Down
2 changes: 1 addition & 1 deletion fidget/src/core/vm/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use crate::{
/// Despite this peek at its internals, users are unlikely to touch `VmData`
/// directly; a [`VmShape`](crate::vm::VmShape) wraps the `VmData` and
/// implements our common traits.
#[derive(Default)]
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub struct VmData<const N: usize = { u8::MAX as usize }> {
ssa: SsaTape,
asm: RegTape,
Expand Down
6 changes: 6 additions & 0 deletions fidget/src/core/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ impl AsRef<[Choice]> for VmTrace {
#[derive(Clone)]
pub struct GenericVmShape<const N: usize>(Arc<VmData<N>>);

impl<const N: usize> From<VmData<N>> for GenericVmShape<N> {
fn from(d: VmData<N>) -> Self {
Self(d.into())
}
}

impl<const N: usize> GenericVmShape<N> {
pub(crate) fn simplify_inner(
&self,
Expand Down
31 changes: 31 additions & 0 deletions wasm-demo/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wasm-demo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
crate-type = ["cdylib"]

[dependencies]
bincode = "1.3.3"
fidget = {path = "../fidget", default-features = false, features = ["rhai", "mesh", "render"]}
wasm-bindgen = "0.2.92"
nalgebra = "0.31"
Expand Down
5 changes: 3 additions & 2 deletions wasm-demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { defaultKeymap } from "@codemirror/commands";

import {
ResponseKind,
ScriptRequest,
ShapeRequest,
StartRequest,
WorkerResponse,
WorkerRequest,
Expand Down Expand Up @@ -58,12 +58,13 @@ class App {
let result = null;
try {
const shapeTree = fidget.eval_script(text);
const tape = fidget.serialize_into_tape(shapeTree);
result = "Ok(..)";

this.start_time = performance.now();
this.workers_done = 0;
this.workers.forEach((w) => {
w.postMessage(new ScriptRequest(text));
w.postMessage(new ShapeRequest(tape));
});
} catch (error) {
// Do some string formatting to make errors cleaner
Expand Down
16 changes: 8 additions & 8 deletions wasm-demo/message.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export enum RequestKind {
Start,
Script,
Shape,
}

export class StartRequest {
Expand All @@ -13,17 +13,17 @@ export class StartRequest {
}
}

export class ScriptRequest {
kind: RequestKind.Script;
script: string;
export class ShapeRequest {
kind: RequestKind.Shape;
tape: Uint8Array;

constructor(s: string) {
this.script = s;
this.kind = RequestKind.Script;
constructor(tape: Uint8Array) {
this.tape = tape;
this.kind = RequestKind.Shape;
}
}

export type WorkerRequest = ScriptRequest | StartRequest;
export type WorkerRequest = ShapeRequest | StartRequest;

////////////////////////////////////////////////////////////////////////////////

Expand Down
50 changes: 22 additions & 28 deletions wasm-demo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use fidget::{
eval::MathShape,
render::{BitRenderMode, RenderConfig},
shape::Bounds,
vm::VmShape,
vm::{VmData, VmShape},
Error,
};
use wasm_bindgen::prelude::*;
Expand All @@ -12,35 +12,32 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct JsTree(Tree);

#[derive(Clone)]
#[wasm_bindgen]
pub struct JsVmShape(VmShape);

#[wasm_bindgen]
pub fn eval_script(s: &str) -> Result<JsTree, String> {
let mut engine = fidget::rhai::Engine::new();
let out = engine.eval(s);
out.map(JsTree).map_err(|e| format!("{e}"))
}

/// Serializes a `JsTree` into a `bincode`-packed `VmData`
#[wasm_bindgen]
pub fn render(t: JsTree, image_size: usize) -> Result<Vec<u8>, String> {
fn inner(t: Tree, image_size: usize) -> Result<Vec<u8>, Error> {
let mut ctx = Context::new();
let root = ctx.import(&t);

let cfg = RenderConfig::<2> {
image_size,
..RenderConfig::default()
};
pub fn serialize_into_tape(t: JsTree) -> Result<Vec<u8>, String> {
let mut ctx = Context::new();
let root = ctx.import(&t.0);
let shape = VmShape::new(&ctx, root).map_err(|e| format!("{e}"))?;
bincode::serialize(shape.data()).map_err(|e| format!("{e}"))
}

let shape = VmShape::new(&ctx, root)?;
let out = cfg.run(shape, &BitRenderMode)?;
Ok(out
.into_iter()
.flat_map(|b| {
let b = b as u8 * u8::MAX;
[b, b, b, 255]
})
.collect())
}
inner(t.0, image_size).map_err(|e| format!("{e}"))
/// Deserialize a `bincode`-packed `VmData` into a `VmShape`
#[wasm_bindgen]
pub fn deserialize_tape(data: Vec<u8>) -> Result<JsVmShape, String> {
let d: VmData<255> =
bincode::deserialize(&data).map_err(|e| format!("{e}"))?;
Ok(JsVmShape(VmShape::from(d)))
}

/// Renders a subregion of an image, for webworker-based multithreading
Expand All @@ -49,7 +46,7 @@ pub fn render(t: JsTree, image_size: usize) -> Result<Vec<u8>, String> {
/// into `0 <= pos < workers_per_side^2` tiles.
#[wasm_bindgen]
pub fn render_region(
t: JsTree,
shape: JsVmShape,
image_size: usize,
index: usize,
workers_per_side: usize,
Expand All @@ -63,14 +60,11 @@ pub fn render_region(
);
}
fn inner(
t: Tree,
shape: VmShape,
image_size: usize,
index: usize,
workers_per_side: usize,
) -> Result<Vec<u8>, Error> {
let mut ctx = Context::new();
let root = ctx.import(&t);

// Corner position in [0, workers_per_side] coordinates
let mut corner = nalgebra::Vector2::new(
index / workers_per_side,
Expand All @@ -95,7 +89,6 @@ pub fn render_region(
..RenderConfig::default()
};

let shape = VmShape::new(&ctx, root)?;
let out = cfg.run(shape, &BitRenderMode)?;
Ok(out
.into_iter()
Expand All @@ -105,5 +98,6 @@ pub fn render_region(
})
.collect())
}
inner(t.0, image_size, index, workers_per_side).map_err(|e| format!("{e}"))
inner(shape.0, image_size, index, workers_per_side)
.map_err(|e| format!("{e}"))
}
15 changes: 9 additions & 6 deletions wasm-demo/worker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
ImageResponse,
RequestKind,
ScriptRequest,
ShapeRequest,
StartRequest,
StartedResponse,
WorkerRequest,
Expand All @@ -19,10 +19,13 @@ class Worker {
this.index = req.index;
}

render(s: ScriptRequest) {
const tree = fidget.eval_script(s.script);
render(s: ShapeRequest) {
let start = performance.now();
const shape = fidget.deserialize_tape(s.tape);
let end = performance.now();
console.log(`deserialized in ${end - start} ms`);
const out = fidget.render_region(
tree,
shape,
RENDER_SIZE,
this.index,
WORKERS_PER_SIDE,
Expand All @@ -42,8 +45,8 @@ async function run() {
worker = new Worker(req as StartRequest);
break;
}
case RequestKind.Script: {
worker!.render(req as ScriptRequest);
case RequestKind.Shape: {
worker!.render(req as ShapeRequest);
break;
}
default:
Expand Down

0 comments on commit 7f7a01b

Please sign in to comment.