前回の続きで、Rust側の実装を行って行きます。
[package]
name = "mandelbrot"
version = "0.1.0"
authors = ["taki"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
js-sys = "0.3.60"
wasm-bindgen = "0.2.63"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }
[dependencies.web-sys]
version = "0.3.60"
features = [
'CanvasRenderingContext2d',
'Document',
'Element',
'HtmlCanvasElement',
'ImageData',
'Performance',
'Window',
]
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
mod utils;
mod logic;
use wasm_bindgen::prelude::*;
use wasm_bindgen::{Clamped, JsCast};
use web_sys::ImageData;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(a: &str);
}
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
macro_rules! measure_elapsed_time {
($t:tt,$s:block) => {{
let window = web_sys::window().expect("should have a window in this context");
let performance = window
.performance()
.expect("performance should be available");
let start = performance.now();
let result = { $s };
let end = performance.now();
console_log!("{}:{}[ms]", $t, end - start);
result
}};
}
#[wasm_bindgen]
pub fn generate_mandelbrot_set(
canvas_w: usize,
canvas_h: usize,
x_min: f64,
x_max: f64,
y_min: f64,
y_max: f64,
max_iter: usize,
) -> Vec<u8> {
measure_elapsed_time!("generate:wasm\telapsed:", {
logic::generate_mandelbrot_set(canvas_w, canvas_h,
x_min, x_max, y_min, y_max, max_iter)
})
}
#[wasm_bindgen]
pub fn draw_mandelbrot_set() {
const CANVAS_ID: &str = "canvas_wasm";
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.get_element_by_id(CANVAS_ID).unwrap();
let canvas: web_sys::HtmlCanvasElement = canvas
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| ())
.unwrap();
let context = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.unwrap();
let canvas_w = canvas.width() as usize;
let canvas_h = canvas.height() as usize;
const X_MIN: f64 = -1.5;
const X_MAX: f64 = 0.5;
const Y_MIN: f64 = -1.0;
const Y_MAX: f64 = 1.0;
const MAX_ITER: usize = 64;
let mut result = measure_elapsed_time!("generate:wasm\telapsed:", {
logic::generate_mandelbrot_set(canvas_w, canvas_h,
X_MIN, X_MAX, Y_MIN, Y_MAX, MAX_ITER)
});
measure_elapsed_time!("draw:wasm\telapsed:", {
let data = ImageData::new_with_u8_clamped_array_and_sh(
Clamped(&mut result),
canvas.width(),
canvas.height(),
);
if let Ok(data) = data {
let _ = context.put_image_data(&data, 0.0, 0.0);
}
})
}
マジ疲れたわ。
JavaScript側でスペルミスがあって、うまくJS側からRustの処理を呼び出せないというミスをやらかして、
スペルミスに気がついて対処して動かすまでに1時間ぐらいかかったかな?
原因を探るためにWinMargeをインストールして、サンプルプログラムと書いたコードを比較して、と言う作業をやっていました。