1use crate::browser_support;
10use crate::compiled::CompiledGraph;
11use crate::device_ext::{self, BROWSER_DEVICE_PRIORITY};
12use crate::session::Session;
13use rlx_driver::Device;
14use rlx_ir::Graph;
15
16#[derive(Debug, Clone)]
18pub struct BrowserError(pub String);
19
20impl std::fmt::Display for BrowserError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "{}", self.0)
23 }
24}
25
26impl std::error::Error for BrowserError {}
27
28impl From<String> for BrowserError {
29 fn from(s: String) -> Self {
30 Self(s)
31 }
32}
33
34#[cfg(all(feature = "gpu", target_arch = "wasm32"))]
37pub async fn init_webgpu() -> bool {
38 rlx_wgpu::device::init_wgpu_device().await
39}
40
41#[cfg(not(all(feature = "gpu", target_arch = "wasm32")))]
42pub async fn init_webgpu() -> bool {
43 #[cfg(feature = "gpu")]
44 {
45 rlx_wgpu::is_available()
46 }
47 #[cfg(not(feature = "gpu"))]
48 {
49 false
50 }
51}
52
53pub use device_ext::BROWSER_DEVICE_PRIORITY as browser_device_priority;
55
56pub fn available_browser_devices() -> Vec<Device> {
58 device_ext::available_browser_devices()
59}
60
61pub fn preferred_browser_device() -> Option<Device> {
63 device_ext::preferred_browser_device()
64}
65
66pub fn supports_browser_graph(graph: &Graph) -> bool {
68 browser_support::select_browser_device_for_graph(graph).is_some()
69}
70
71pub fn browser_block_reason(graph: &Graph) -> Option<String> {
73 if let Some(reason) = browser_support::collective_block_reason(graph) {
74 return Some(reason);
75 }
76 for &device in BROWSER_DEVICE_PRIORITY {
77 if !device_ext::is_available(device) {
78 continue;
79 }
80 if let Some((_, op)) = browser_support::first_browser_unsupported_op(graph, device) {
81 return Some(format!(
82 "op {op:?} not supported on browser backend {device:?}"
83 ));
84 }
85 if device_ext::supports_graph(device, graph) {
86 return None;
87 }
88 }
89 Some("no browser backend available — enable `webgpu` and/or `opengl` features".into())
90}
91
92pub struct BrowserSession {
94 device: Device,
95}
96
97impl BrowserSession {
98 pub fn new_preferred() -> Result<Self, BrowserError> {
100 let device = preferred_browser_device().ok_or_else(|| {
101 BrowserError(
102 "no browser backend available — call init_webgpu() and/or enable opengl feature"
103 .into(),
104 )
105 })?;
106 Ok(Self { device })
107 }
108
109 pub fn new(device: Device) -> Result<Self, BrowserError> {
111 if !BROWSER_DEVICE_PRIORITY.contains(&device) {
112 return Err(BrowserError(format!(
113 "{device:?} is not a browser backend (use WebGpu, OpenGl, or Cpu)"
114 )));
115 }
116 if !device_ext::is_available(device) {
117 return Err(BrowserError(format!(
118 "browser backend {device:?} is not available"
119 )));
120 }
121 Ok(Self { device })
122 }
123
124 pub fn compile_for_graph(graph: &Graph) -> Result<BrowserCompiledGraph, BrowserError> {
126 if let Some(reason) = browser_support::collective_block_reason(graph) {
127 return Err(BrowserError(reason));
128 }
129 let device = browser_support::select_browser_device_for_graph(graph).ok_or_else(|| {
130 browser_block_reason(graph)
131 .unwrap_or_else(|| "graph not supported on any browser backend".into())
132 })?;
133 BrowserSession { device }.compile(graph.clone())
134 }
135
136 pub fn device(&self) -> Device {
137 self.device
138 }
139
140 pub fn compile(&self, graph: Graph) -> Result<BrowserCompiledGraph, BrowserError> {
142 if let Some(reason) = browser_support::collective_block_reason(&graph) {
143 return Err(BrowserError(reason));
144 }
145 if !device_ext::supports_graph(self.device, &graph) {
146 return Err(BrowserError(browser_block_reason(&graph).unwrap_or_else(
147 || format!("graph not supported on {:?}", self.device),
148 )));
149 }
150 let compiled = Session::new(self.device).compile(graph);
151 Ok(BrowserCompiledGraph {
152 inner: compiled,
153 device: self.device,
154 })
155 }
156}
157
158pub struct BrowserCompiledGraph {
160 inner: CompiledGraph,
161 device: Device,
162}
163
164impl BrowserCompiledGraph {
165 pub fn device(&self) -> Device {
166 self.device
167 }
168
169 pub fn set_param(&mut self, name: &str, data: &[f32]) {
170 self.inner.set_param(name, data);
171 }
172
173 pub fn run(&mut self, inputs: &[(&str, &[f32])]) -> Vec<Vec<f32>> {
175 self.inner.run(inputs)
176 }
177
178 #[cfg(target_arch = "wasm32")]
180 pub async fn run_async(
181 &mut self,
182 inputs: &[(&str, &[f32])],
183 ) -> Result<Vec<Vec<f32>>, BrowserError> {
184 match self.device {
185 Device::WebGpu | Device::Gpu => {
186 self.inner.run_async(inputs).await.map_err(BrowserError)
187 }
188 _ => Ok(self.inner.run(inputs)),
189 }
190 }
191
192 #[cfg(not(target_arch = "wasm32"))]
193 pub async fn run_async(
194 &mut self,
195 inputs: &[(&str, &[f32])],
196 ) -> Result<Vec<Vec<f32>>, BrowserError> {
197 Ok(self.inner.run(inputs))
198 }
199}