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