forked from bkase/gameboy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Canvas image blitting 60FPS on iPhone
A Rust Vec<u8> controls the canvas at a full 60fps even on an iPhone
- Loading branch information
Showing
4 changed files
with
134 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,127 @@ | ||
extern crate cfg_if; | ||
extern crate wasm_bindgen; | ||
use std::cell::RefCell; | ||
use std::rc::Rc; | ||
|
||
mod utils; | ||
extern crate wasm_bindgen; | ||
|
||
use cfg_if::cfg_if; | ||
use wasm_bindgen::prelude::*; | ||
use wasm_bindgen::{JsCast, Clamped}; | ||
|
||
fn window() -> web_sys::Window { | ||
web_sys::window().expect("no global `window` exists") | ||
} | ||
|
||
fn request_animation_frame(f: &Closure<FnMut()>) { | ||
window() | ||
.request_animation_frame(f.as_ref().unchecked_ref()) | ||
.expect("should register `requestAnimationFrame` OK"); | ||
} | ||
|
||
cfg_if! { | ||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global | ||
// allocator. | ||
if #[cfg(feature = "wee_alloc")] { | ||
extern crate wee_alloc; | ||
#[global_allocator] | ||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; | ||
fn document() -> web_sys::Document { | ||
window() | ||
.document() | ||
.expect("should have a document on window") | ||
} | ||
|
||
fn draw(data: &mut Vec<u8>, width: u32, height: u32, i: u32) { | ||
for row in 0..height { | ||
for col in 0..width { | ||
let idx : usize = (row*width*4 + col*4) as usize; | ||
data[idx+0] = i as u8; | ||
data[idx+1] = i as u8; | ||
data[idx+2] = i as u8; | ||
data[idx+3] = 255; | ||
} | ||
} | ||
} | ||
|
||
|
||
#[wasm_bindgen] | ||
extern { | ||
fn alert(s: &str); | ||
extern "C" { | ||
// Use `js_namespace` here to bind `console.log(..)` instead of just | ||
// `log(..)` | ||
#[wasm_bindgen(js_namespace = console)] | ||
fn log(s: &str); | ||
|
||
#[wasm_bindgen(js_namespace = performance, js_name = now)] | ||
fn performance_now() -> f64; | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn greet() { | ||
alert("Hello, gameboy!"); | ||
// This function is automatically invoked after the wasm module is instantiated. | ||
#[wasm_bindgen(start)] | ||
pub fn run() -> Result<(), JsValue> { | ||
let canvas = document().get_element_by_id("canvas").unwrap(); | ||
let canvas: web_sys::HtmlCanvasElement = canvas | ||
.dyn_into::<web_sys::HtmlCanvasElement>() | ||
.map_err(|_| ()) | ||
.unwrap(); | ||
|
||
let width = canvas.width() as u32; | ||
let height = ((144.0 / 160.0) * width as f64).ceil() as u32; | ||
canvas.set_height(height); | ||
|
||
log(&format!("Height {} and width {}", height, width)); | ||
|
||
let ctx = canvas | ||
.get_context("2d") | ||
.unwrap() | ||
.unwrap() | ||
.dyn_into::<web_sys::CanvasRenderingContext2d>() | ||
.unwrap(); | ||
|
||
// Here we want to call `requestAnimationFrame` in a loop, but only a fixed | ||
// number of times. After it's done we want all our resources cleaned up. To | ||
// achieve this we're using an `Rc`. The `Rc` will eventually store the | ||
// closure we want to execute on each frame, but to start out it contains | ||
// `None`. | ||
// | ||
// After the `Rc` is made we'll actually create the closure, and the closure | ||
// will reference one of the `Rc` instances. The other `Rc` reference is | ||
// used to store the closure, request the first frame, and then is dropped | ||
// by this function. | ||
// | ||
// Inside the closure we've got a persistent `Rc` reference, which we use | ||
// for all future iterations of the loop | ||
let f = Rc::new(RefCell::new(None)); | ||
let g = f.clone(); | ||
|
||
let mut i = 0; | ||
let mut data = Vec::new(); | ||
for _ in 0 .. height { | ||
for _ in 0 .. width { | ||
for _ in 0 .. 4 { | ||
data.push(0); | ||
} | ||
} | ||
} | ||
let mut last = performance_now(); | ||
*g.borrow_mut() = Some(Closure::wrap(Box::new(move || { | ||
if i > 500 { | ||
// body().set_text_content(Some("All done!")); | ||
|
||
// Drop our handle to this closure so that it will get cleaned | ||
// up once we return. | ||
let _ = f.borrow_mut().take(); | ||
return; | ||
} | ||
|
||
let now = performance_now(); | ||
let diff = now - last; | ||
last = now; | ||
|
||
draw(&mut data, width, height, i); | ||
let data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut data), width, height).expect("u8 clamped array"); | ||
ctx.put_image_data(&data, 0.0, 0.0).expect("put_image_data"); | ||
ctx.fill_text(&format!("FPS {}", (1000.0 / diff).ceil()), 10.0, 50.0).expect("fill_text"); | ||
|
||
// Set the body's text content to how many times this | ||
// requestAnimationFrame callback has fired. | ||
i += 1; | ||
|
||
// Schedule ourself for another requestAnimationFrame callback. | ||
request_animation_frame(f.borrow().as_ref().unwrap()); | ||
}) as Box<FnMut()>)); | ||
|
||
request_animation_frame(g.borrow().as_ref().unwrap()); | ||
Ok(()) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
import * as wasm from "gameboy"; | ||
import('gameboy') | ||
.catch(console.error); | ||
|
||
wasm.greet(); |