diff --git a/app.js b/app.js index 8cacfee..f53cece 100644 --- a/app.js +++ b/app.js @@ -38,7 +38,7 @@ const bootstrap = async() => { await require('js/mandelbrot.js')(), await require('emscripten/mandelbrot.js')(), await require('assemblyscript/mandelbrot-asc.js')(), - await require('assemblyscript/mandelbrot-tsc.js')(), + // await require('assemblyscript/mandelbrot-tsc.js')(), await require('asmjs/mandelbrot.js')(), ]; diff --git a/assemblyscript/mandelbrot-asc.js b/assemblyscript/mandelbrot-asc.js index 75d4b14..4834c02 100644 --- a/assemblyscript/mandelbrot-asc.js +++ b/assemblyscript/mandelbrot-asc.js @@ -1,27 +1,40 @@ -module.exports = async() => { +module.exports = async () => { - const WIDTH = 1200, HEIGHT = 800; + const WIDTH = 1200; + const HEIGHT = 800; + const PAGE_SIZE = 65536; + + function allocateMemory() { + const initial = Math.floor(WIDTH * HEIGHT * 4 / PAGE_SIZE) + 1; + return new WebAssembly.Memory({ initial }); + } + + const imports = { + env: { + memoryBase: 0, + memory: allocateMemory() + } + }; const res = await fetch('assemblyscript/mandelbrot.wasm'); const buffer = await res.arrayBuffer(); - const instance = await WebAssembly.instantiate(buffer, {}); - - + const module = await WebAssembly.instantiate(buffer, imports); + const { mandelbrot, getDataBuffer, memory } = module.instance.exports; + let imgData = null; return { - render: (ctx, config) => { - console.log(instance.instance.exports.structured()); - instance.instance.exports.mandelbrot(config.iterations, config.x, config.y, config.d); + render: (ctx, { iterations, x, y, d }) => { + mandelbrot(iterations, x, y, d); - const imgData = ctx.createImageData(WIDTH, HEIGHT); - const offset = instance.instance.exports.getData(); - const linearMemory = new Uint8Array(instance.instance.exports.memory.buffer, offset, WIDTH * HEIGHT * 4); - for (let i = 0; i < linearMemory.length; i++) { + if (!imgData) imgData = ctx.createImageData(WIDTH, HEIGHT); + const offset = getDataBuffer(); + const linearMemory = new Uint8Array(memory.buffer, offset, WIDTH * HEIGHT * 4); + for (let i = 0, len = linearMemory.length; i < len; i++) { imgData.data[i] = linearMemory[i]; } ctx.putImageData(imgData, 0, 0); }, name: 'AssemblyScript', description: 'This implementation uses the AssemblyScript compiler to compile a TypeScript mandelbrot to WebAssembly.' - } + }; }; diff --git a/assemblyscript/mandelbrot-tsc.js b/assemblyscript/mandelbrot-tsc.js index 7f49f55..8d55ac7 100644 --- a/assemblyscript/mandelbrot-tsc.js +++ b/assemblyscript/mandelbrot-tsc.js @@ -1,18 +1,24 @@ const typeScriptMandelbrot = require('./mandelbrot.js'); +const WIDTH = 1200; +const HEIGHT = 800; + +var imgData = null; + +const { getData, mandelbrot } = typeScriptMandelbrot; + module.exports = () => Promise.resolve({ - render: (ctx, config) => { - const WIDTH = 1200, HEIGHT = 800; - - typeScriptMandelbrot.mandelbrot(config.iterations, config.x, config.y, config.d); - - const imgData = ctx.createImageData(WIDTH, HEIGHT); - const linearMemory = typeScriptMandelbrot.getData(); - for (let i = 0; i < linearMemory.length; i++) { + render: (ctx, { iterations, x, y, d }) => { + mandelbrot(iterations, x, y, d); + + if (!imgData) imgData = ctx.createImageData(WIDTH, HEIGHT); + const linearMemory = getData(); + + for (let i = 0, len = linearMemory.length; i < len; i++) { imgData.data[i] = linearMemory[i]; } ctx.putImageData(imgData, 0, 0); }, name: 'TypeScript', description: 'This implementation takes the TypeScript that is used by AssemblyScript to generate WebAssembly, but instead passes it through the TypeScript compiler to create JavaScript.' -}); \ No newline at end of file +}); diff --git a/assemblyscript/mandelbrot.js b/assemblyscript/mandelbrot.js index 66dd218..c9aabeb 100644 --- a/assemblyscript/mandelbrot.js +++ b/assemblyscript/mandelbrot.js @@ -1,23 +1,22 @@ define(["require", "exports"], function (require, exports) { "use strict"; - exports.__esModule = true; - var WIDTH = 1200, HEIGHT = 800; - var data = new Uint8Array(1200 * 800 * 4); + Object.defineProperty(exports, "__esModule", { value: true }); + const WIDTH = 1200; + const HEIGHT = 800; + var data = new Uint8Array(WIDTH * HEIGHT * 4); function colour(iteration, offset, scale) { - iteration = ((iteration * scale) + offset) % 1024; + iteration = (iteration * scale + offset) & 1023; if (iteration < 256) { return iteration; } else if (iteration < 512) { - return 255 - (iteration - 255); - } - else { - return 0; + return (255 - (iteration - 255)); } + return 0; } function iterateEquation(x0, y0, maxiterations) { - var a = 0, b = 0, rx = 0, ry = 0; - var iterations = 0; + let a = 0.0, b = 0.0, rx = 0.0, ry = 0.0; + let iterations = 0; while (iterations < maxiterations && (rx * rx + ry * ry <= 4)) { rx = a * a - b * b + x0; ry = 2 * a * b + y0; @@ -27,19 +26,22 @@ define(["require", "exports"], function (require, exports) { } return iterations; } - function scale(domainStart, domainLength, screenLength, step) { - return domainStart + domainLength * ((step - screenLength) / screenLength); + // @inline + function scale(domainStart, domainLength, invScreenLength, step) { + return domainStart + domainLength * (step * invScreenLength - 1.0); } function mandelbrot(maxIterations, cx, cy, diameter) { - var verticalDiameter = diameter * HEIGHT / WIDTH; - for (var x = 0; x < WIDTH; x++) { - for (var y = 0; y < HEIGHT; y++) { + const invWidth = 1.0 / WIDTH; + const invHeight = 1.0 / HEIGHT; + var verticalDiameter = diameter * HEIGHT * invWidth; + for (let x = 0; x < WIDTH; ++x) { + for (let y = 0; y < HEIGHT; ++y) { // convert from screen coordinates to mandelbrot coordinates - var rx = scale(cx, diameter, WIDTH, x); - var ry = scale(cy, verticalDiameter, HEIGHT, y); - var iterations = iterateEquation(rx, ry, maxIterations); - var idx = ((x + y * WIDTH) * 4); - data[idx] = iterations == maxIterations ? 0 : colour(iterations, 0, 4); + let rx = scale(cx, diameter, invWidth, x); + let ry = scale(cy, verticalDiameter, invHeight, y); + let iterations = iterateEquation(rx, ry, maxIterations); + let idx = (x + y * WIDTH) << 2; + data[idx + 0] = iterations == maxIterations ? 0 : colour(iterations, 0, 4); data[idx + 1] = iterations == maxIterations ? 0 : colour(iterations, 128, 4); data[idx + 2] = iterations == maxIterations ? 0 : colour(iterations, 356, 4); data[idx + 3] = 255; @@ -51,5 +53,9 @@ define(["require", "exports"], function (require, exports) { return data; } exports.getData = getData; + function getDataBuffer() { + return (data.buffer); + } + exports.getDataBuffer = getDataBuffer; ; }); diff --git a/assemblyscript/mandelbrot.ts b/assemblyscript/mandelbrot.ts index b86f24b..92cd230 100644 --- a/assemblyscript/mandelbrot.ts +++ b/assemblyscript/mandelbrot.ts @@ -1,5 +1,9 @@ -const WIDTH: f64 = 1200, HEIGHT: f64 = 800; -const data: Uint8Array = new Uint8Array(1200 * 800 * 4); +import "allocator/arena"; + +const WIDTH: i32 = 1200; +const HEIGHT: i32 = 800; + +var data: Uint8Array = new Uint8Array(WIDTH * HEIGHT * 4); function colour(iteration: i32, offset: i32, scale: i32): u8 { iteration = ((iteration * scale) + offset) % 1024; @@ -7,14 +11,13 @@ function colour(iteration: i32, offset: i32, scale: i32): u8 { return iteration as u8; } else if (iteration < 512) { return 255 - ((iteration as u8) - 255); - } else { - return 0; - } + } + return 0; } function iterateEquation(x0: f64, y0: f64, maxiterations: i32): i32 { - let a: f64 = 0, b: f64 = 0, rx: f64 = 0, ry: f64 = 0; - let iterations: i32 = 0; + let a = 0.0, b = 0.0, rx = 0.0, ry = 0.0; + let iterations = 0; while (iterations < maxiterations && (rx * rx + ry * ry <= 4)) { rx = a * a - b * b + x0; ry = 2 * a * b + y0; @@ -25,27 +28,33 @@ function iterateEquation(x0: f64, y0: f64, maxiterations: i32): i32 { return iterations; } +@inline function scale(domainStart: f64, domainLength: f64, screenLength: f64, step: f64): f64 { return domainStart + domainLength * ((step - screenLength) / screenLength); } export function mandelbrot(maxIterations: i32, cx: f64, cy: f64, diameter: f64): void { - const verticalDiameter: f64 = diameter * HEIGHT / WIDTH; - for (var x: f64 = 0; x < WIDTH; x++) { - for (var y: f64 = 0; y < HEIGHT; y++) { + let verticalDiameter: f64 = diameter * HEIGHT / WIDTH; + for (let x = 0; x < WIDTH; ++x) { + for (let y = 0; y < HEIGHT; ++y) { // convert from screen coordinates to mandelbrot coordinates - const rx: f64 = scale(cx, diameter, WIDTH, x); - const ry: f64 = scale(cy, verticalDiameter, HEIGHT, y); - const iterations: i32 = iterateEquation(rx, ry, maxIterations); - const idx: i32 = ((x + y * (WIDTH as f64)) * 4) as i32; - data[idx] = iterations == maxIterations ? 0 : colour(iterations, 0, 4); - data[idx + 1] = iterations == maxIterations ? 0 : colour(iterations, 128, 4); - data[idx + 2] = iterations == maxIterations ? 0 : colour(iterations, 356, 4); - data[idx+3] = 255; + let rx = scale(cx, diameter, WIDTH, x); + let ry = scale(cy, verticalDiameter, HEIGHT, y); + let iterations = iterateEquation(rx, ry, maxIterations); + let idx = (x + y * WIDTH) << 2; + + unchecked(data[idx + 0] = iterations == maxIterations ? 0 : colour(iterations, 0, 4)); + unchecked(data[idx + 1] = iterations == maxIterations ? 0 : colour(iterations, 128, 4)); + unchecked(data[idx + 2] = iterations == maxIterations ? 0 : colour(iterations, 356, 4)); + unchecked(data[idx + 3] = 255); } } } export function getData(): Uint8Array { return data; +} + +export function getDataBuffer(): ArrayBuffer { + return (data.buffer); }; diff --git a/assemblyscript/mandelbrot.wasm b/assemblyscript/mandelbrot.wasm index fd83cb5..a154a5d 100644 Binary files a/assemblyscript/mandelbrot.wasm and b/assemblyscript/mandelbrot.wasm differ diff --git a/assemblyscript/package.json b/assemblyscript/package.json index 09e0211..e883084 100644 --- a/assemblyscript/package.json +++ b/assemblyscript/package.json @@ -5,13 +5,15 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build-tsc": "tsc", - "build-asc": "asc -O3 mandelbrot.ts -o mandelbrot.wasm" + "build-tsc": "npx tsc", + "build-asc": "npx asc mandelbrot.ts -b mandelbrot.wasm -t mandelbrot.wat -O3 --validate --noDebug --noAssert --use abort=", + "build": "npm run build-asc && npm run build-tsc" }, "author": "ceberhardt@scottlogic.com", "license": "ISC", "dependencies": { - "assemblyscript": "^0.3.0", - "typescript": "^2.5.3" + "assemblyscript": "github:AssemblyScript/assemblyscript", + "glob": "^7.1.2", + "typescript": "^2.9.2" } } diff --git a/assemblyscript/tsconfig.json b/assemblyscript/tsconfig.json index 0c00e91..3a41d14 100644 --- a/assemblyscript/tsconfig.json +++ b/assemblyscript/tsconfig.json @@ -1,9 +1,10 @@ { - "extends": "./node_modules/assemblyscript/tsconfig.assembly.json", + "extends": "./node_modules/assemblyscript/std/assembly.json", "include": [ - "./*.ts" + "./**/*.ts" ], "compilerOptions": { + "target": "es6", "module": "amd" } }