use eframe::wasm_bindgen::JsCast as _;
use crate::HenadApp;
use crate::init::wgpu_configuration;
use crate::options::{AppOptions, WebStartError};
const CANVAS_ID: &str = "the_canvas_id";
const LOADING_TEXT_ID: &str = "loading_text";
#[cfg(target_feature = "atomics")]
const THREAD_POOL_NOTE: &str = "Models run on one thread. The page might lack the Cross-Origin-Opener-Policy and \
Cross-Origin-Embedder-Policy headers, and the browser console has the error.";
#[cfg(not(target_feature = "atomics"))]
const THREAD_POOL_NOTE: &str = "Models run on one thread. This build has no thread support.";
pub async fn start_web(options: impl FnOnce() -> AppOptions) -> Result<(), WebStartError> {
let Some((window, document)) = web_sys::window().and_then(|window| {
let document = window.document()?;
Some((window, document))
}) else {
return Err(WebStartError::no_window());
};
let pool_failed = !start_thread_pool(&window).await;
let mut options = options();
if pool_failed {
options.thread_pool_note = Some(THREAD_POOL_NOTE.to_owned());
}
let loading_text = document.get_element_by_id(LOADING_TEXT_ID);
let refuse = |error: WebStartError| {
if let Some(element) = &loading_text {
element.set_text_content(Some(&crate::ui::sweep::draft::capitalize(&error.to_string())));
}
Err(error)
};
if let Err(error) = options.check_opening() {
return refuse(WebStartError::opening(error));
}
let Some(canvas) = document
.get_element_by_id(CANVAS_ID)
.and_then(|element| element.dyn_into::<web_sys::HtmlCanvasElement>().ok())
else {
return refuse(WebStartError::missing_canvas(CANVAS_ID));
};
let web_options = eframe::WebOptions {
wgpu_options: wgpu_configuration(options.models.gpu_needs()),
..Default::default()
};
let started = eframe::WebRunner::new()
.start(
canvas,
web_options,
Box::new(|cc| Ok(Box::new(HenadApp::new(cc, options)))),
)
.await;
match started {
Ok(()) => {
if let Some(element) = &loading_text {
element.remove();
}
Ok(())
}
Err(error) => refuse(WebStartError::eframe(&error)),
}
}
#[cfg(target_feature = "atomics")]
async fn start_thread_pool(window: &web_sys::Window) -> bool {
let available = window.navigator().hardware_concurrency() as usize;
let search = window.location().search().unwrap_or_default();
let workers = crate::requested_threads(&search, available);
log::info!("thread pool: {workers} workers, {available} reported by the browser");
wasm_bindgen_futures::JsFuture::from(crate::init_thread_pool(workers))
.await
.inspect_err(|error| log::error!("thread pool init failed, models will run on one core: {error:?}"))
.is_ok()
}
#[cfg(not(target_feature = "atomics"))]
async fn start_thread_pool(_window: &web_sys::Window) -> bool {
log::warn!("this build has no atomics, models will run on one core");
false
}
pub fn init_web_logger(level: log::LevelFilter) {
eframe::WebLogger::init(level).ok();
}