From af54d4b95e359bc4b1f7831ad328a825fe0383d0 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 26 Jul 2018 15:16:54 +0900 Subject: [PATCH] wasm: use requestAnimationFrame and report FPS. --- core/wasm/README.md | 18 +++---- core/wasm/index.html | 2 + core/wasm/index.js | 120 ++++++++++++++++++++++++++++++------------- core/wasm/src/lib.rs | 41 +++++++++------ 4 files changed, 120 insertions(+), 61 deletions(-) diff --git a/core/wasm/README.md b/core/wasm/README.md index ee34d006..6cbf3d10 100644 --- a/core/wasm/README.md +++ b/core/wasm/README.md @@ -33,14 +33,14 @@ Note that `run server` will automatically detect modification to ## Preliminary results: -* 2048x1024, draw1, release: 66.7ms -* 2048x1024, draw2, release: 34.1ms ( 21.7ms / 12.4ms) -* 2048x1024, draw3, release: 29.9ms ( 15.1ms / 14.8ms) +* 2048x1024, draw1, release: 23 fps +* 2048x1024, draw2, release: 51 fps +* 2048x1024, draw3, release: 60 fps -* 1024x1024, draw1, release: 47.5ms -* 1024x1024, draw2, release: 21.8ms ( 12.0ms / 9.8ms) -* 1024x1024, draw3, release: 16.7ms ( 6.9ms / 9.8ms) +* 1024x1024, draw1, release: 36 fps +* 1024x1024, draw2, release: 60 fps +* 1024x1024, draw3, release: 60 fps -* 1024x1024, draw1, debug: 376.6ms -* 1024x1024, draw2, debug: 132.4ms (129.1ms / 3.3ms) -* 1024x1024, draw3, debug: 131.4ms (128.8ms / 2.6ms) +* 1024x1024, draw1, debug: 3 fps +* 1024x1024, draw2, debug: 8 fps +* 1024x1024, draw3, debug: 9 fps diff --git a/core/wasm/index.html b/core/wasm/index.html index 3d5703f0..cf9bdb03 100644 --- a/core/wasm/index.html +++ b/core/wasm/index.html @@ -3,6 +3,8 @@ + +
Frames per Second:
diff --git a/core/wasm/index.js b/core/wasm/index.js index 2dcda333..b317b76b 100644 --- a/core/wasm/index.js +++ b/core/wasm/index.js @@ -1,4 +1,4 @@ -const WIDTH = 1024 +const WIDTH = 2048 const HEIGHT = 1024 const MODE = 3 @@ -8,8 +8,8 @@ import { memory } from './novnc_bg' const canvas = document.getElementById('target') const ctx = canvas.getContext('2d') -canvas.width = WIDTH; -canvas.height = HEIGHT; +canvas.width = WIDTH +canvas.height = HEIGHT if (MODE === 2 || MODE === 3) { let byteSize = WIDTH * HEIGHT * 4 @@ -17,50 +17,98 @@ if (MODE === 2 || MODE === 3) { var u8array = new Uint8ClampedArray(memory.buffer, pointer, byteSize) var imgData = new ImageData(u8array, WIDTH, HEIGHT) - console.log("imgData:", imgData) } -let msList1 = [] -let msList2 = [] +let frame = -1 -function avg(l) { - return (l.reduce((a,b) => a+b, 0)/l.length).toFixed(2) -} - -function update() { - let ms1, ms2 +function renderLoop() { const startMs = (new Date()).getTime() - const red = parseInt(Math.random()*256) - const green = parseInt(Math.random()*256) - const blue = parseInt(Math.random()*256) - console.log(`red: ${red}, green: ${green}, blue: ${blue}`) + fps.render() + frame += 1 + if (MODE === 1) { - novnc.draw1(ctx, WIDTH, HEIGHT, - red, green, blue) - ms1 = (new Date()).getTime() - msList1.push(ms1 - startMs) - console.log(`frame elapsed: ${ms1 - startMs} (${avg(msList1)})`) + novnc.draw1(ctx, WIDTH, HEIGHT, frame) } else { if (MODE === 2) { - novnc.draw2(pointer, WIDTH, HEIGHT, - red, green, blue) + novnc.draw2(pointer, WIDTH, HEIGHT, frame) } else if (MODE === 3) { - novnc.draw3(pointer, WIDTH, HEIGHT, - red, green, blue) + novnc.draw3(pointer, WIDTH, HEIGHT, frame) } - ms1 = (new Date()).getTime() - ctx.putImageData(imgData, 0, 0) - ms2 = (new Date()).getTime() - msList1.push(ms1 - startMs) - msList2.push(ms2 - ms1) - console.log(`draw elapsed: ${ms1 - startMs} (${avg(msList1)}), ` + - `putIMageData elapsed: ${ms2 - ms1} (${avg(msList2)})`) - //window.requestAnimationFrame(update) + ctx.putImageData(imgData, 0, 0) + } + animationId = requestAnimationFrame(renderLoop) + //console.log("elapsed:", (new Date()).getTime() - startMs) +} + + +////////////////////////////////////////////////////////////////////////////// +// From: https://github.com/rustwasm/wasm_game_of_life/blob/3253fa3a1557bdb9525f3b5c134b58efa1041c55/index.js#L27 + +let animationId = null + +const fps = new class { + constructor() { + this.fps = document.getElementById("fps") + this.frames = [] + this.lastFrameTimeStamp = performance.now() + } + + render() { + const now = performance.now() + const delta = now - this.lastFrameTimeStamp + this.lastFrameTimeStamp = now + const fps = 1 / delta * 1000 + + this.frames.push(fps) + if (this.frames.length > 100) { + this.frames.shift() + } + + let min = Infinity + let max = -Infinity + let sum = 0 + for (let i = 0; i < this.frames.length; i++) { + sum += this.frames[i] + min = Math.min(this.frames[i], min) + max = Math.max(this.frames[i], max) + } + let mean = sum / this.frames.length + + this.fps.textContent = ` +Frames per Second: + latest = ${Math.round(fps)} +avg of last 100 = ${Math.round(mean)} +min of last 100 = ${Math.round(min)} +max of last 100 = ${Math.round(max)} +`.trim() } } -canvas.addEventListener('click', () => { - update() +const playPauseButton = document.getElementById("play-pause") + +const isPaused = () => { + return animationId === null +} + +const play = () => { + playPauseButton.textContent = "⏸" + renderLoop() +} + +const pause = () => { + playPauseButton.textContent = "▶" + cancelAnimationFrame(animationId) + animationId = null +} + +playPauseButton.addEventListener("click", event => { + if (isPaused()) { + play() + } else { + pause() + } }) -update() +playPauseButton.textContent = "▶" + +////////////////////////////////////////////////////////////////////////////// diff --git a/core/wasm/src/lib.rs b/core/wasm/src/lib.rs index a3c304f9..fc599b11 100644 --- a/core/wasm/src/lib.rs +++ b/core/wasm/src/lib.rs @@ -34,14 +34,17 @@ extern "C" { pub fn put_image_data(this: &CanvasRenderingContext2D, image_data: &ImageData, p_1: i32, p_2: i32); } -pub fn fill(width: u32, height: u32, red: u8, green: u8, blue: u8) -> Vec { +pub fn fill(width: u32, height: u32, frame: u32) -> Vec { let mut data: Vec = vec![]; - for _x in 0..width { - for _y in 0..height { - data.push(red as u8); - data.push(green as u8); - data.push(blue as u8); + for x in 0..width { + for y in 0..height { + let r = if (x%512) < 256 {x%256} else {255-(x%256)}; + let g = if (y%512) < 256 {y%256} else {255-(y%256)}; + let b = if (frame%512) < 256 {frame%256} else {255-(frame%256)}; + data.push(r as u8); + data.push(g as u8); + data.push(b as u8); data.push(255); } } @@ -50,8 +53,8 @@ pub fn fill(width: u32, height: u32, red: u8, green: u8, blue: u8) -> Vec { } #[wasm_bindgen] -pub fn draw1(ctx: &CanvasRenderingContext2D, width: u32, height: u32, red: u8, green: u8, blue: u8) { - let data = fill(width, height, red, green, blue); +pub fn draw1(ctx: &CanvasRenderingContext2D, width: u32, height: u32, frame: u32) { + let data = fill(width, height, frame); let uint8_array = Uint8ClampedArray::new(&data); ctx.put_image_data(&ImageData::new(&uint8_array, width, height), 0, 0); @@ -70,17 +73,20 @@ pub fn alloc(size: usize) -> *mut c_void { } #[wasm_bindgen] -pub fn draw2(mem: *mut u8, width: usize, height: usize, red: u8, green: u8, blue: u8) { +pub fn draw2(mem: *mut u8, width: usize, height: usize, frame: u32) { // pixels are stored in RGBA, so each pixel is 4 bytes let sl = unsafe { slice::from_raw_parts_mut(mem, width * height * 4) }; for y in 0..height { for x in 0..width { + let r = if (x%512) < 256 {x%256} else {255-(x%256)}; + let g = if (y%512) < 256 {y%256} else {255-(y%256)}; + let b = if (frame%512) < 256 {frame%256} else {255-(frame%256)}; let xy = x*4 + y*4*width; - sl[xy + 0] = red; - sl[xy + 1] = green; - sl[xy + 2] = blue; + sl[xy + 0] = r as u8; + sl[xy + 1] = g as u8; + sl[xy + 2] = b as u8; sl[xy + 3] = 255; } } @@ -90,17 +96,20 @@ pub fn draw2(mem: *mut u8, width: usize, height: usize, red: u8, green: u8, blue /// draw3 #[wasm_bindgen] -pub fn draw3(mem: *mut u32, width: usize, height: usize, red: u8, green: u8, blue: u8) { +pub fn draw3(mem: *mut u32, width: usize, height: usize, frame: u32) { // pixels are stored in RGBA let sl = unsafe { slice::from_raw_parts_mut(mem, width * height) }; for y in 0..height { for x in 0..width { + let r = if (x%512) < 256 {x%256} else {255-(x%256)}; + let g = if (y%512) < 256 {y%256} else {255-(y%256)}; + let b = if (frame%512) < 256 {frame%256} else {255-(frame%256)}; let color = 0xff000000 | - ((blue as u32) << 16) | - ((green as u32) << 8) | - ((red as u32) << 0); + (b << 16) | + ((g as u32) << 8) | + ((r as u32) << 0); sl[y*width + x] = color; } }