From 43be92d3df6c6b41ee6d89f328c0b12ee7ea03f4 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Sat, 20 Apr 2024 18:11:26 -0400 Subject: [PATCH] multi-worker rendering is working! --- wasm-demo/constants.ts | 3 ++ wasm-demo/index.ts | 82 ++++++++++++++++++++++++------------------ wasm-demo/message.ts | 31 +++++----------- wasm-demo/src/lib.rs | 14 ++++++-- wasm-demo/worker.ts | 18 ++++++---- 5 files changed, 83 insertions(+), 65 deletions(-) create mode 100644 wasm-demo/constants.ts diff --git a/wasm-demo/constants.ts b/wasm-demo/constants.ts new file mode 100644 index 00000000..a41e133f --- /dev/null +++ b/wasm-demo/constants.ts @@ -0,0 +1,3 @@ +export const RENDER_SIZE = 512; +export const WORKERS_PER_SIDE = 4; +export const WORKER_COUNT = WORKERS_PER_SIDE * WORKERS_PER_SIDE; diff --git a/wasm-demo/index.ts b/wasm-demo/index.ts index 73e99aa6..da600a84 100644 --- a/wasm-demo/index.ts +++ b/wasm-demo/index.ts @@ -12,9 +12,12 @@ import { WorkerRequest, } from "./message"; -const RENDER_SIZE = 512; -const WORKERS_PER_SIDE = 4; -const WORKER_COUNT = WORKERS_PER_SIDE * WORKERS_PER_SIDE; +import { +RENDER_SIZE, +WORKERS_PER_SIDE, +WORKER_COUNT, +} from "./constants"; + const INITIAL_SCRIPT = "y + x*x"; var fidget: any = null; @@ -30,6 +33,8 @@ class App { scene: Scene; workers: Array; workers_started: number; + workers_done: number; + start_time: number; constructor() { this.scene = new Scene(); @@ -41,6 +46,7 @@ class App { this.output = new Output(document.getElementById("output-outer")); this.workers = []; this.workers_started = 0; + this.workers_done = 0; for (let i = 0; i < WORKER_COUNT; ++i) { const worker = new Worker(new URL("./worker.ts", import.meta.url)); @@ -58,14 +64,11 @@ class App { const shapeTree = fidget.eval_script(text); result = "Ok(..)"; - const startTime = performance.now(); + this.start_time = performance.now(); + this.workers_done = 0; this.workers.forEach((w) => { w.postMessage(new ScriptRequest(text)); }); - shape = fidget.render(shapeTree, RENDER_SIZE); - const endTime = performance.now(); - document.getElementById("status").textContent = - `Rendered in ${endTime - startTime} ms`; } catch (error) { // Do some string formatting to make errors cleaner result = error @@ -75,20 +78,27 @@ class App { .replace(" (expecting ", "\n(expecting "); } this.output.setText(result); - if (shape) { - this.scene.setTexture(shape); - this.scene.draw(); - } } onWorkerMessage(i: number, req: WorkerResponse) { switch (req.kind) { case ResponseKind.Image: { - console.log("GOT IMAGE"); + const region_size = RENDER_SIZE / WORKERS_PER_SIDE; + const x = Math.trunc(i / WORKERS_PER_SIDE) * region_size; + const y = (WORKERS_PER_SIDE - (i % WORKERS_PER_SIDE) - 1) * region_size; + this.scene.setTextureRegion(x, y, region_size, req.data); + this.scene.draw(); + + this.workers_done += 1; + if (this.workers_done == WORKER_COUNT) { + const endTime = performance.now(); + document.getElementById("status").textContent = + `Rendered in ${endTime - this.start_time} ms`; + } break; } case ResponseKind.Started: { - this.workers[i].postMessage(new StartRequest(i, WORKERS_PER_SIDE)); + this.workers[i].postMessage(new StartRequest(i)); this.workers_started += 1; if (this.workers_started == WORKER_COUNT) { this.onScriptChanged(INITIAL_SCRIPT); @@ -229,34 +239,38 @@ class Scene { this.buffers = new Buffers(this.gl); this.programInfo = new ProgramInfo(this.gl); this.texture = this.gl.createTexture(); - this.setTexture(new Uint8Array(RENDER_SIZE * RENDER_SIZE * 4)); - } - setTexture(data: Uint8Array) { + // Bind an initial texture of the correct size this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture); - - const level = 0; - const internalFormat = this.gl.RGBA; - const width = RENDER_SIZE; - const height = RENDER_SIZE; - const border = 0; - const srcFormat = this.gl.RGBA; - const srcType = this.gl.UNSIGNED_BYTE; this.gl.texImage2D( this.gl.TEXTURE_2D, - level, - internalFormat, - width, - height, - border, - srcFormat, - srcType, - data, + 0, + this.gl.RGBA, + RENDER_SIZE, + RENDER_SIZE, + 0, // border + this.gl.RGBA, + this.gl.UNSIGNED_BYTE, + new Uint8Array(RENDER_SIZE * RENDER_SIZE * 4), ); - this.gl.generateMipmap(this.gl.TEXTURE_2D); } + setTextureRegion(x: number, y: number, size: number, data: Uint8Array) { + this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture); + this.gl.texSubImage2D( + this.gl.TEXTURE_2D, + 0, + x, + y, + size, + size, + this.gl.RGBA, + this.gl.UNSIGNED_BYTE, + data, + ); + } + draw() { this.gl.clearColor(0.0, 0.0, 0.0, 1.0); this.gl.clear(this.gl.COLOR_BUFFER_BIT); diff --git a/wasm-demo/message.ts b/wasm-demo/message.ts index d10e4e3f..70f04dbc 100644 --- a/wasm-demo/message.ts +++ b/wasm-demo/message.ts @@ -1,21 +1,19 @@ -enum RequestKind { +export enum RequestKind { Start, Script, } -class StartRequest { +export class StartRequest { kind: RequestKind.Start; index: number; - workers_per_side: number; - constructor(index: number, workers_per_side: number) { + constructor(index: number) { this.index = index; - this.workers_per_side = workers_per_side; this.kind = RequestKind.Start; } } -class ScriptRequest { +export class ScriptRequest { kind: RequestKind.Script; script: string; @@ -25,16 +23,16 @@ class ScriptRequest { } } -type WorkerRequest = ScriptRequest | StartRequest; +export type WorkerRequest = ScriptRequest | StartRequest; //////////////////////////////////////////////////////////////////////////////// -enum ResponseKind { +export enum ResponseKind { Started, Image, } -class StartedResponse { +export class StartedResponse { kind: ResponseKind.Started; constructor() { @@ -42,7 +40,7 @@ class StartedResponse { } } -class ImageResponse { +export class ImageResponse { kind: ResponseKind.Image; data: Uint8Array; @@ -52,15 +50,4 @@ class ImageResponse { } } -type WorkerResponse = StartedResponse | ImageResponse; - -export { - ImageResponse, - RequestKind, - ResponseKind, - ScriptRequest, - StartRequest, - StartedResponse, - WorkerRequest, - WorkerResponse, -}; +export type WorkerResponse = StartedResponse | ImageResponse; diff --git a/wasm-demo/src/lib.rs b/wasm-demo/src/lib.rs index b25e6953..b98ed767 100644 --- a/wasm-demo/src/lib.rs +++ b/wasm-demo/src/lib.rs @@ -71,14 +71,14 @@ pub fn render_region( let mut ctx = Context::new(); let root = ctx.import(&t); - // Corner position in [0, 1] coordinates + // Corner position in [0, workers_per_side] coordinates let mut corner = nalgebra::Vector2::new( index / workers_per_side, index % workers_per_side, ) .cast::(); // Corner position in [-1, 1] coordinates - corner = (corner * 2.0).add_scalar(-1.0); + corner = (corner * 2.0 / workers_per_side as f32).add_scalar(-1.0); // Scale of each tile let scale = 2.0 / workers_per_side as f32; @@ -90,7 +90,7 @@ pub fn render_region( image_size: image_size / workers_per_side, bounds: Bounds { center, - size: scale, + size: scale / 2.0, }, ..RenderConfig::default() }; @@ -102,6 +102,14 @@ pub fn render_region( .flat_map(|b| { let b = b as u8 * u8::MAX; [b, b, b, 255] + /* + [ + if index % 2 == 0 { 255 } else { 0 }, + if (index / 2) % 2 == 0 { 255 } else { 0 }, + if (index / 4) % 2 == 0 { 255 } else { 0 }, + 255, + ] + */ }) .collect()) } diff --git a/wasm-demo/worker.ts b/wasm-demo/worker.ts index c8ab047f..8f47ecb1 100644 --- a/wasm-demo/worker.ts +++ b/wasm-demo/worker.ts @@ -7,23 +7,30 @@ import { WorkerRequest, } from "./message"; +import { +RENDER_SIZE, +WORKERS_PER_SIDE, +WORKER_COUNT, +} from "./constants"; + var fidget: any = null; class Worker { /// Index of this worker, between 0 and workers_per_side index: number; - /// Total number of workers per image side - workers_per_side: number; - constructor(req: StartRequest) { this.index = req.index; - this.workers_per_side = req.workers_per_side; } render(s: ScriptRequest) { const tree = fidget.eval_script(s.script); - const out = fidget.render_region(tree, 512, 0, 4); + const out = fidget.render_region( + tree, + RENDER_SIZE, + this.index, + WORKERS_PER_SIDE, + ); postMessage(new ImageResponse(out)); } } @@ -35,7 +42,6 @@ async function run() { let req = e.data as WorkerRequest; switch (req.kind) { case RequestKind.Start: { - console.log("STARTING"); let r = req as StartRequest; worker = new Worker(req as StartRequest); break;