Files
rustytorch/crates/production/rtx-wasm-inference/src/runtime.rs
T
2026-03-04 00:08:42 +00:00

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"));
}
}