337 lines
9.7 KiB
Rust
337 lines
9.7 KiB
Rust
//! WASM Runtime Detection and Utilities
|
|
//!
|
|
//! Detects the WASM runtime environment (browser, Node.js, etc.)
|
|
//! and provides utilities for performance measurement.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
/// WASM runtime environment types
|
|
#[wasm_bindgen]
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum WasmRuntime {
|
|
/// Running in a web browser
|
|
Browser,
|
|
/// Running in Node.js
|
|
NodeJs,
|
|
/// Running in a Web Worker
|
|
WebWorker,
|
|
/// Running in Deno
|
|
Deno,
|
|
/// Running in Wasmtime
|
|
Wasmtime,
|
|
/// Running in Wasmer
|
|
Wasmer,
|
|
/// Unknown runtime
|
|
Unknown,
|
|
}
|
|
|
|
/// Information about the WASM runtime environment
|
|
#[wasm_bindgen]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct WasmRuntimeInfo {
|
|
/// Detected runtime type
|
|
runtime: WasmRuntime,
|
|
/// Whether SIMD is available
|
|
simd_available: bool,
|
|
/// Whether threading is available
|
|
threads_available: bool,
|
|
/// Whether SharedArrayBuffer is available
|
|
shared_memory: bool,
|
|
/// Whether WebGPU is available
|
|
webgpu_available: bool,
|
|
/// Available memory in bytes (if detectable)
|
|
available_memory: Option<usize>,
|
|
/// User agent string (browser only)
|
|
user_agent: Option<String>,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmRuntimeInfo {
|
|
/// Detect the current runtime environment
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn detect() -> Self {
|
|
let (runtime, user_agent) = Self::detect_runtime();
|
|
let simd_available = Self::check_simd();
|
|
let threads_available = Self::check_threads();
|
|
let shared_memory = Self::check_shared_memory();
|
|
let webgpu_available = Self::check_webgpu();
|
|
let available_memory = Self::get_available_memory();
|
|
|
|
Self {
|
|
runtime,
|
|
simd_available,
|
|
threads_available,
|
|
shared_memory,
|
|
webgpu_available,
|
|
available_memory,
|
|
user_agent,
|
|
}
|
|
}
|
|
|
|
fn check_webgpu() -> bool {
|
|
let global = js_sys::global();
|
|
js_sys::Reflect::get(&global, &"navigator".into())
|
|
.ok()
|
|
.and_then(|nav| js_sys::Reflect::get(&nav, &"gpu".into()).ok())
|
|
.is_some_and(|gpu| !gpu.is_undefined())
|
|
}
|
|
|
|
fn detect_runtime() -> (WasmRuntime, Option<String>) {
|
|
// Check for browser environment
|
|
if let Ok(window) = js_sys::Reflect::get(&js_sys::global(), &"window".into()) {
|
|
if !window.is_undefined() {
|
|
let user_agent = web_sys::window().and_then(|w| w.navigator().user_agent().ok());
|
|
return (WasmRuntime::Browser, user_agent);
|
|
}
|
|
}
|
|
|
|
// Check for Web Worker
|
|
if let Ok(self_obj) = js_sys::Reflect::get(&js_sys::global(), &"WorkerGlobalScope".into()) {
|
|
if !self_obj.is_undefined() {
|
|
return (WasmRuntime::WebWorker, None);
|
|
}
|
|
}
|
|
|
|
// Check for Node.js
|
|
if let Ok(process) = js_sys::Reflect::get(&js_sys::global(), &"process".into()) {
|
|
if !process.is_undefined() {
|
|
if let Ok(versions) = js_sys::Reflect::get(&process, &"versions".into()) {
|
|
if let Ok(node) = js_sys::Reflect::get(&versions, &"node".into()) {
|
|
if !node.is_undefined() {
|
|
return (WasmRuntime::NodeJs, None);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for Deno
|
|
if let Ok(deno) = js_sys::Reflect::get(&js_sys::global(), &"Deno".into()) {
|
|
if !deno.is_undefined() {
|
|
return (WasmRuntime::Deno, None);
|
|
}
|
|
}
|
|
|
|
(WasmRuntime::Unknown, None)
|
|
}
|
|
|
|
fn check_simd() -> bool {
|
|
// Check if WASM SIMD is supported
|
|
#[cfg(feature = "simd")]
|
|
{
|
|
true
|
|
}
|
|
#[cfg(not(feature = "simd"))]
|
|
{
|
|
false
|
|
}
|
|
}
|
|
|
|
fn check_threads() -> bool {
|
|
// Check if SharedArrayBuffer is available (required for threads)
|
|
Self::check_shared_memory()
|
|
}
|
|
|
|
fn check_shared_memory() -> bool {
|
|
js_sys::Reflect::get(&js_sys::global(), &"SharedArrayBuffer".into())
|
|
.map(|v| !v.is_undefined())
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn get_available_memory() -> Option<usize> {
|
|
// Try to get memory info (Chrome only)
|
|
if let Some(window) = web_sys::window() {
|
|
if let Ok(performance) = js_sys::Reflect::get(&window, &"performance".into()) {
|
|
if let Ok(memory) = js_sys::Reflect::get(&performance, &"memory".into()) {
|
|
if let Ok(js_heap_limit) =
|
|
js_sys::Reflect::get(&memory, &"jsHeapSizeLimit".into())
|
|
{
|
|
if let Some(limit) = js_heap_limit.as_f64() {
|
|
return Some(limit as usize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Get the runtime type
|
|
#[wasm_bindgen(getter)]
|
|
pub fn runtime(&self) -> WasmRuntime {
|
|
self.runtime
|
|
}
|
|
|
|
/// Check if SIMD is available
|
|
#[wasm_bindgen(getter)]
|
|
pub fn simd_available(&self) -> bool {
|
|
self.simd_available
|
|
}
|
|
|
|
/// Check if threading is available
|
|
#[wasm_bindgen(getter)]
|
|
pub fn threads_available(&self) -> bool {
|
|
self.threads_available
|
|
}
|
|
|
|
/// Check if shared memory is available
|
|
#[wasm_bindgen(getter)]
|
|
pub fn shared_memory(&self) -> bool {
|
|
self.shared_memory
|
|
}
|
|
|
|
/// Check if WebGPU is available
|
|
#[wasm_bindgen(getter)]
|
|
pub fn webgpu_available(&self) -> bool {
|
|
self.webgpu_available
|
|
}
|
|
|
|
/// Get available memory
|
|
#[wasm_bindgen(getter)]
|
|
pub fn available_memory(&self) -> Option<usize> {
|
|
self.available_memory
|
|
}
|
|
|
|
/// Get user agent
|
|
#[wasm_bindgen(getter)]
|
|
pub fn user_agent(&self) -> Option<String> {
|
|
self.user_agent.clone()
|
|
}
|
|
|
|
/// Get high-resolution timestamp
|
|
pub fn performance_now(&self) -> f64 {
|
|
if let Some(window) = web_sys::window() {
|
|
if let Some(performance) = window.performance() {
|
|
return performance.now();
|
|
}
|
|
}
|
|
// Fallback for non-browser environments
|
|
js_sys::Date::now()
|
|
}
|
|
|
|
/// Check if running in browser
|
|
#[wasm_bindgen]
|
|
pub fn is_browser(&self) -> bool {
|
|
matches!(self.runtime, WasmRuntime::Browser)
|
|
}
|
|
|
|
/// Check if running in Node.js
|
|
#[wasm_bindgen]
|
|
pub fn is_node(&self) -> bool {
|
|
matches!(self.runtime, WasmRuntime::NodeJs)
|
|
}
|
|
|
|
/// Check if running in a worker
|
|
#[wasm_bindgen]
|
|
pub fn is_worker(&self) -> bool {
|
|
matches!(self.runtime, WasmRuntime::WebWorker)
|
|
}
|
|
|
|
/// Get runtime name as string
|
|
#[wasm_bindgen]
|
|
pub fn runtime_name(&self) -> String {
|
|
match self.runtime {
|
|
WasmRuntime::Browser => "browser".to_string(),
|
|
WasmRuntime::NodeJs => "nodejs".to_string(),
|
|
WasmRuntime::WebWorker => "webworker".to_string(),
|
|
WasmRuntime::Deno => "deno".to_string(),
|
|
WasmRuntime::Wasmtime => "wasmtime".to_string(),
|
|
WasmRuntime::Wasmer => "wasmer".to_string(),
|
|
WasmRuntime::Unknown => "unknown".to_string(),
|
|
}
|
|
}
|
|
|
|
/// Get capabilities summary
|
|
#[wasm_bindgen]
|
|
pub fn capabilities_summary(&self) -> String {
|
|
format!(
|
|
"Runtime: {}, SIMD: {}, Threads: {}, SharedMem: {}, WebGPU: {}",
|
|
self.runtime_name(),
|
|
self.simd_available,
|
|
self.threads_available,
|
|
self.shared_memory,
|
|
self.webgpu_available
|
|
)
|
|
}
|
|
|
|
/// Check if GPU acceleration is available
|
|
#[wasm_bindgen]
|
|
pub fn gpu_available(&self) -> bool {
|
|
self.webgpu_available
|
|
}
|
|
|
|
/// Get recommended compute backend
|
|
#[wasm_bindgen]
|
|
pub fn recommended_backend(&self) -> String {
|
|
if self.webgpu_available {
|
|
"webgpu".to_string()
|
|
} else {
|
|
"cpu".to_string()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Console logging utilities for WASM
|
|
#[wasm_bindgen]
|
|
pub fn console_log(message: &str) {
|
|
web_sys::console::log_1(&message.into());
|
|
}
|
|
|
|
/// Console warning for WASM
|
|
#[wasm_bindgen]
|
|
pub fn console_warn(message: &str) {
|
|
web_sys::console::warn_1(&message.into());
|
|
}
|
|
|
|
/// Console error for WASM
|
|
#[wasm_bindgen]
|
|
pub fn console_error(message: &str) {
|
|
web_sys::console::error_1(&message.into());
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_runtime_info() {
|
|
// In test context, runtime will be Unknown
|
|
let info = WasmRuntimeInfo {
|
|
runtime: WasmRuntime::Unknown,
|
|
simd_available: false,
|
|
threads_available: false,
|
|
shared_memory: false,
|
|
webgpu_available: false,
|
|
available_memory: None,
|
|
user_agent: None,
|
|
};
|
|
|
|
assert_eq!(info.runtime_name(), "unknown");
|
|
assert!(!info.is_browser());
|
|
assert!(!info.is_node());
|
|
assert!(!info.webgpu_available());
|
|
assert!(!info.gpu_available());
|
|
assert_eq!(info.recommended_backend(), "cpu");
|
|
}
|
|
|
|
#[test]
|
|
fn test_runtime_info_with_webgpu() {
|
|
let info = WasmRuntimeInfo {
|
|
runtime: WasmRuntime::Browser,
|
|
simd_available: true,
|
|
threads_available: true,
|
|
shared_memory: true,
|
|
webgpu_available: true,
|
|
available_memory: Some(4 * 1024 * 1024 * 1024),
|
|
user_agent: Some("Chrome/120".to_string()),
|
|
};
|
|
|
|
assert!(info.is_browser());
|
|
assert!(info.webgpu_available());
|
|
assert!(info.gpu_available());
|
|
assert_eq!(info.recommended_backend(), "webgpu");
|
|
assert!(info.capabilities_summary().contains("WebGPU: true"));
|
|
}
|
|
}
|