wasm-bindgen/examples/raytrace-parallel/index.js
2018-10-23 08:50:58 -07:00

122 lines
3.4 KiB
JavaScript

const button = document.getElementById('render');
const canvas = document.getElementById('canvas');
const scene = document.getElementById('scene');
const concurrency = document.getElementById('concurrency');
const concurrencyAmt = document.getElementById('concurrency-amt');
const timing = document.getElementById('timing');
const timingVal = document.getElementById('timing-val');
const ctx = canvas.getContext('2d');
button.disabled = true;
concurrency.disabled = true;
// First up, but try to do feature detection to provide better error messages
function loadWasm() {
let msg = 'This demo currently requires Firefox Nightly (64.0) with\n'
msg += 'the `javascript.options.shared_memory` option enabled in `about:config`';
if (typeof SharedArrayBuffer !== 'function') {
alert('this browser does not have SharedArrayBuffer support enabled' + '\n\n' + msg);
return
}
// Test for bulk memory operations with passive data segments
// (module (memory 1) (data passive ""))
const buf = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x05, 0x03, 0x01, 0x00, 0x01, 0x0b, 0x03, 0x01, 0x01, 0x00]);
if (!WebAssembly.validate(buf)) {
alert('this browser does not support passive wasm memory, demo does not work' + '\n\n' + msg);
return
}
wasm_bindgen('./raytrace_parallel_bg.wasm')
.then(run)
.catch(console.error);
}
loadWasm();
const { Scene, WorkerPool } = wasm_bindgen;
function run() {
// The maximal concurrency of our web worker pool is `hardwareConcurrency`,
// so set that up here and this ideally is the only location we create web
// workers.
pool = new WorkerPool(navigator.hardwareConcurrency);
// Configure various buttons and such.
button.onclick = function() {
console.time('render');
let json;
try {
json = JSON.parse(scene.value);
} catch(e) {
alert(`invalid json: ${e}`);
return
}
canvas.width = json.width;
canvas.height = json.height;
render(new Scene(json));
};
button.innerText = 'Render!';
button.disabled = false;
concurrency.oninput = function() {
concurrencyAmt.innerText = 'Concurrency: ' + concurrency.value;
};
concurrency.min = 1;
concurrency.step = 1;
concurrency.max = navigator.hardwareConcurrency;
concurrency.value = concurrency.max;
concurrency.oninput();
concurrency.disabled = false;
}
let rendering = null;
let start = null;
let interval = null;
let pool = null;
class State {
constructor(wasm) {
this.start = performance.now();
this.wasm = wasm;
this.running = true;
this.counter = 1;
this.interval = setInterval(() => this.updateTimer(), 100);
wasm.promise()
.then(() => {
this.updateTimer();
this.stop();
})
.catch(console.error);
}
updateTimer() {
const dur = performance.now() - this.start;
timingVal.innerText = `${dur}ms`;
this.counter += 1;
if (this.wasm && this.counter % 3 == 0)
this.wasm.requestUpdate();
}
stop() {
if (!this.running)
return;
console.timeEnd('render');
this.running = false;
pool = this.wasm.cancel(); // this frees `wasm`, returning the worker pool
this.wasm = null;
clearInterval(this.interval);
}
}
function render(scene) {
if (rendering) {
rendering.stop();
rendering = null;
}
rendering = new State(scene.render(concurrency.value, pool, ctx));
pool = null; // previous call took ownership of `pool`, zero it out here too
}