wasm: use requestAnimationFrame and report FPS.
This commit is contained in:
parent
82b8ff1c6a
commit
af54d4b95e
|
@ -33,14 +33,14 @@ Note that `run server` will automatically detect modification to
|
||||||
|
|
||||||
## Preliminary results:
|
## Preliminary results:
|
||||||
|
|
||||||
* 2048x1024, draw1, release: 66.7ms
|
* 2048x1024, draw1, release: 23 fps
|
||||||
* 2048x1024, draw2, release: 34.1ms ( 21.7ms / 12.4ms)
|
* 2048x1024, draw2, release: 51 fps
|
||||||
* 2048x1024, draw3, release: 29.9ms ( 15.1ms / 14.8ms)
|
* 2048x1024, draw3, release: 60 fps
|
||||||
|
|
||||||
* 1024x1024, draw1, release: 47.5ms
|
* 1024x1024, draw1, release: 36 fps
|
||||||
* 1024x1024, draw2, release: 21.8ms ( 12.0ms / 9.8ms)
|
* 1024x1024, draw2, release: 60 fps
|
||||||
* 1024x1024, draw3, release: 16.7ms ( 6.9ms / 9.8ms)
|
* 1024x1024, draw3, release: 60 fps
|
||||||
|
|
||||||
* 1024x1024, draw1, debug: 376.6ms
|
* 1024x1024, draw1, debug: 3 fps
|
||||||
* 1024x1024, draw2, debug: 132.4ms (129.1ms / 3.3ms)
|
* 1024x1024, draw2, debug: 8 fps
|
||||||
* 1024x1024, draw3, debug: 131.4ms (128.8ms / 2.6ms)
|
* 1024x1024, draw3, debug: 9 fps
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<button id="play-pause"></button>
|
||||||
|
<pre id="fps">Frames per Second:</pre>
|
||||||
<canvas id="target" width="400" height="400"></canvas>
|
<canvas id="target" width="400" height="400"></canvas>
|
||||||
<script src='./bootstrap.js'></script>
|
<script src='./bootstrap.js'></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const WIDTH = 1024
|
const WIDTH = 2048
|
||||||
const HEIGHT = 1024
|
const HEIGHT = 1024
|
||||||
const MODE = 3
|
const MODE = 3
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import { memory } from './novnc_bg'
|
||||||
const canvas = document.getElementById('target')
|
const canvas = document.getElementById('target')
|
||||||
const ctx = canvas.getContext('2d')
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
canvas.width = WIDTH;
|
canvas.width = WIDTH
|
||||||
canvas.height = HEIGHT;
|
canvas.height = HEIGHT
|
||||||
|
|
||||||
if (MODE === 2 || MODE === 3) {
|
if (MODE === 2 || MODE === 3) {
|
||||||
let byteSize = WIDTH * HEIGHT * 4
|
let byteSize = WIDTH * HEIGHT * 4
|
||||||
|
@ -17,50 +17,98 @@ if (MODE === 2 || MODE === 3) {
|
||||||
|
|
||||||
var u8array = new Uint8ClampedArray(memory.buffer, pointer, byteSize)
|
var u8array = new Uint8ClampedArray(memory.buffer, pointer, byteSize)
|
||||||
var imgData = new ImageData(u8array, WIDTH, HEIGHT)
|
var imgData = new ImageData(u8array, WIDTH, HEIGHT)
|
||||||
console.log("imgData:", imgData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let msList1 = []
|
let frame = -1
|
||||||
let msList2 = []
|
|
||||||
|
|
||||||
function avg(l) {
|
function renderLoop() {
|
||||||
return (l.reduce((a,b) => a+b, 0)/l.length).toFixed(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
let ms1, ms2
|
|
||||||
const startMs = (new Date()).getTime()
|
const startMs = (new Date()).getTime()
|
||||||
const red = parseInt(Math.random()*256)
|
fps.render()
|
||||||
const green = parseInt(Math.random()*256)
|
frame += 1
|
||||||
const blue = parseInt(Math.random()*256)
|
|
||||||
console.log(`red: ${red}, green: ${green}, blue: ${blue}`)
|
|
||||||
if (MODE === 1) {
|
if (MODE === 1) {
|
||||||
novnc.draw1(ctx, WIDTH, HEIGHT,
|
novnc.draw1(ctx, WIDTH, HEIGHT, frame)
|
||||||
red, green, blue)
|
|
||||||
ms1 = (new Date()).getTime()
|
|
||||||
msList1.push(ms1 - startMs)
|
|
||||||
console.log(`frame elapsed: ${ms1 - startMs} (${avg(msList1)})`)
|
|
||||||
} else {
|
} else {
|
||||||
if (MODE === 2) {
|
if (MODE === 2) {
|
||||||
novnc.draw2(pointer, WIDTH, HEIGHT,
|
novnc.draw2(pointer, WIDTH, HEIGHT, frame)
|
||||||
red, green, blue)
|
|
||||||
} else if (MODE === 3) {
|
} else if (MODE === 3) {
|
||||||
novnc.draw3(pointer, WIDTH, HEIGHT,
|
novnc.draw3(pointer, WIDTH, HEIGHT, frame)
|
||||||
red, green, blue)
|
|
||||||
}
|
}
|
||||||
ms1 = (new Date()).getTime()
|
|
||||||
ctx.putImageData(imgData, 0, 0)
|
ctx.putImageData(imgData, 0, 0)
|
||||||
ms2 = (new Date()).getTime()
|
}
|
||||||
msList1.push(ms1 - startMs)
|
animationId = requestAnimationFrame(renderLoop)
|
||||||
msList2.push(ms2 - ms1)
|
//console.log("elapsed:", (new Date()).getTime() - startMs)
|
||||||
console.log(`draw elapsed: ${ms1 - startMs} (${avg(msList1)}), ` +
|
}
|
||||||
`putIMageData elapsed: ${ms2 - ms1} (${avg(msList2)})`)
|
|
||||||
|
|
||||||
//window.requestAnimationFrame(update)
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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', () => {
|
const playPauseButton = document.getElementById("play-pause")
|
||||||
update()
|
|
||||||
|
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 = "▶"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -34,14 +34,17 @@ extern "C" {
|
||||||
pub fn put_image_data(this: &CanvasRenderingContext2D, image_data: &ImageData, p_1: i32, p_2: i32);
|
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![];
|
let mut data: Vec<u8> = vec![];
|
||||||
|
|
||||||
for _x in 0..width {
|
for x in 0..width {
|
||||||
for _y in 0..height {
|
for y in 0..height {
|
||||||
data.push(red as u8);
|
let r = if (x%512) < 256 {x%256} else {255-(x%256)};
|
||||||
data.push(green as u8);
|
let g = if (y%512) < 256 {y%256} else {255-(y%256)};
|
||||||
data.push(blue as u8);
|
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);
|
data.push(255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,8 +53,8 @@ pub fn fill(width: u32, height: u32, red: u8, green: u8, blue: u8) -> Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn draw1(ctx: &CanvasRenderingContext2D, width: u32, height: u32, red: u8, green: u8, blue: u8) {
|
pub fn draw1(ctx: &CanvasRenderingContext2D, width: u32, height: u32, frame: u32) {
|
||||||
let data = fill(width, height, red, green, blue);
|
let data = fill(width, height, frame);
|
||||||
let uint8_array = Uint8ClampedArray::new(&data);
|
let uint8_array = Uint8ClampedArray::new(&data);
|
||||||
|
|
||||||
ctx.put_image_data(&ImageData::new(&uint8_array, width, height), 0, 0);
|
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]
|
#[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
|
// pixels are stored in RGBA, so each pixel is 4 bytes
|
||||||
let sl = unsafe { slice::from_raw_parts_mut(mem, width * height * 4) };
|
let sl = unsafe { slice::from_raw_parts_mut(mem, width * height * 4) };
|
||||||
|
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
for x in 0..width {
|
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;
|
let xy = x*4 + y*4*width;
|
||||||
sl[xy + 0] = red;
|
sl[xy + 0] = r as u8;
|
||||||
sl[xy + 1] = green;
|
sl[xy + 1] = g as u8;
|
||||||
sl[xy + 2] = blue;
|
sl[xy + 2] = b as u8;
|
||||||
sl[xy + 3] = 255;
|
sl[xy + 3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,17 +96,20 @@ pub fn draw2(mem: *mut u8, width: usize, height: usize, red: u8, green: u8, blue
|
||||||
/// draw3
|
/// draw3
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[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
|
// pixels are stored in RGBA
|
||||||
let sl = unsafe { slice::from_raw_parts_mut(mem, width * height) };
|
let sl = unsafe { slice::from_raw_parts_mut(mem, width * height) };
|
||||||
|
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
for x in 0..width {
|
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 |
|
let color = 0xff000000 |
|
||||||
((blue as u32) << 16) |
|
(b << 16) |
|
||||||
((green as u32) << 8) |
|
((g as u32) << 8) |
|
||||||
((red as u32) << 0);
|
((r as u32) << 0);
|
||||||
sl[y*width + x] = color;
|
sl[y*width + x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue