Compare commits

...

2 Commits

Author SHA1 Message Date
Matthew Gordon 04aec9231b Add file missing from earlier commit 2025-06-12 10:53:32 -03:00
Matthew Gordon 0d98b4016c Add some error handling 2025-06-12 10:53:10 -03:00
4 changed files with 75 additions and 15 deletions

View File

@ -0,0 +1,31 @@
use log::info;
pub struct StatisticsReporter {
total_time_seconds: f64,
frame_count: u32,
}
impl StatisticsReporter {
pub fn new() -> Self {
let total_time_seconds = 0.0;
let frame_count = 0;
Self {
total_time_seconds,
frame_count,
}
}
pub fn log_frame_time_seconds(&mut self, t: f64) {
self.total_time_seconds += t;
self.frame_count += 1;
if self.total_time_seconds >= 1.0 {
info!(
"Average frame time: {:.0} ms ({:.1} fps)",
self.total_time_seconds * 1000.0 / (self.frame_count as f64),
(self.frame_count as f64) / self.total_time_seconds
);
self.total_time_seconds = 0.0;
self.frame_count = 0;
}
}
}

View File

@ -23,6 +23,6 @@ pub fn main() {
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn init_pteropus() -> wasm::PteropusCanvas {
pub async fn init_pteropus() -> Result<wasm::PteropusCanvas, wasm::PteropusInitError> {
run(App::new(), Rc::new(Model::new())).await
}

View File

@ -9,7 +9,7 @@ use {
use crate::{
app::{App, Model},
mvu,
mvu::{Event, File, MvuApp, Size2i},
mvu::{Event, MvuApp, Size2i},
};
struct FrameTimer {
@ -52,9 +52,23 @@ pub struct PteropusCanvas {
model: Rc<Model>,
}
#[wasm_bindgen(getter_with_clone)]
#[derive(Clone)]
pub struct PteropusInitError {
pub message: String,
}
impl PteropusInitError {
fn new<M: Into<String>>(message: M) -> Self {
Self {
message: message.into(),
}
}
}
#[wasm_bindgen]
impl PteropusCanvas {
async fn init(&self) {
async fn init(&self) -> Result<(), PteropusInitError> {
let html_canvas = self.get_canvas();
let size = Size2i {
width: html_canvas.client_width() as u32,
@ -62,15 +76,17 @@ impl PteropusCanvas {
};
let instance = wgpu::Instance::default();
let surface_target = SurfaceTarget::Canvas(html_canvas);
let surface = instance
.create_surface(surface_target)
.expect("create graphics surface for canvas");
let surface = instance.create_surface(surface_target).map_err(|_| {
PteropusInitError::new(
"Could not initialize canvas. This browser may not support WebGPU.",
)
})?;
self.app
.lock()
.expect("get app mutex")
.init(&instance, surface, size, Box::new(FrameTimer::new()))
.await;
Ok(())
}
#[wasm_bindgen]
@ -134,11 +150,11 @@ impl PteropusCanvas {
}
}
pub async fn run(app: App, model: Rc<Model>) -> PteropusCanvas {
pub async fn run(app: App, model: Rc<Model>) -> Result<PteropusCanvas, PteropusInitError> {
let app = Rc::new(Mutex::new(app));
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger");
let pteropus_canvas = PteropusCanvas { app, model };
pteropus_canvas.init().await;
pteropus_canvas
pteropus_canvas.init().await?;
Ok(pteropus_canvas)
}

View File

@ -1,4 +1,4 @@
import init, {init_pteropus} from '../pkg/pteropus.js';
import init, {init_pteropus, PteropusInitError} from '../pkg/pteropus.js';
function fileDragOverHandler(event) {
event.preventDefault();
@ -7,13 +7,26 @@ function fileDragOverHandler(event) {
async function run() {
await init();
try {
let pteropus = await init_pteropus();
} catch(e) {
const errorParagraph = document.createElement("p");
if(e instanceof PteropusInitError) {
const errorMessage = document.createTextNode(e.message);
errorParagraph.appendChild(errorMessage);
} else {
const errorMessage = document.createTextNode("Something unexpected happened.");
errorParagraph.appendChild(errorMessage);
}
document.getElementById("whole-window").replaceWith(errorParagraph);
return;
}
let test_file_data = null;
let needs_resize = true;
let clear_scene = false;
let mainCanvas = document.getElementById("pteropus-canvas");
const mainCanvas = document.getElementById("pteropus-canvas");
mainCanvas.addEventListener("drop", async (event) => {
event.preventDefault();
test_file_data = event.dataTransfer.files[0]
@ -24,7 +37,7 @@ async function run() {
});
resizeObserver.observe(mainCanvas);
let fileUploadButton = document.getElementById("file-upload-file-input");
const fileUploadButton = document.getElementById("file-upload-file-input");
fileUploadButton.addEventListener("change", () => {
const files = fileUploadButton.files;
if(files.length > 0) {
@ -32,7 +45,7 @@ async function run() {
}
});
let clearSceneButton = document.getElementById("clear-scene-button");
const clearSceneButton = document.getElementById("clear-scene-button");
clearSceneButton.addEventListener("click", () => {
clear_scene = true;
});