wasm: use requestAnimationFrame and report FPS.

This commit is contained in:
Joel Martin 2018-07-26 15:16:54 +09:00
parent 82b8ff1c6a
commit af54d4b95e
4 changed files with 120 additions and 61 deletions

View File

@ -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

View File

@ -3,6 +3,8 @@
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<button id="play-pause"></button>
<pre id="fps">Frames per Second:</pre>
<canvas id="target" width="400" height="400"></canvas>
<script src='./bootstrap.js'></script>
</body>

View File

@ -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 = "▶"
//////////////////////////////////////////////////////////////////////////////

View File

@ -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<u8> {
pub fn fill(width: u32, height: u32, frame: u32) -> Vec<u8> {
let mut data: Vec<u8> = 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<u8> {
}
#[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;
}
}