runmat_runtime/builtins/plotting/core/
context.rs1#[cfg(not(target_arch = "wasm32"))]
11use once_cell::sync::OnceCell;
12use runmat_accelerate_api::{AccelContextHandle, AccelContextKind, WgpuContextHandle};
13#[cfg(target_arch = "wasm32")]
14use runmat_thread_local::runmat_thread_local;
15#[cfg(target_arch = "wasm32")]
16use std::cell::RefCell;
17
18use crate::{build_runtime_error, BuiltinResult, RuntimeError};
19
20#[cfg(not(target_arch = "wasm32"))]
22static SHARED_WGPU_CONTEXT: OnceCell<WgpuContextHandle> = OnceCell::new();
23
24#[cfg(target_arch = "wasm32")]
25runmat_thread_local! {
26 static SHARED_WGPU_CONTEXT: RefCell<Option<WgpuContextHandle>> = RefCell::new(None);
27}
28
29pub fn shared_wgpu_context() -> Option<WgpuContextHandle> {
31 #[cfg(not(target_arch = "wasm32"))]
32 {
33 SHARED_WGPU_CONTEXT.get().cloned()
34 }
35 #[cfg(target_arch = "wasm32")]
36 {
37 SHARED_WGPU_CONTEXT.with(|cell| cell.borrow().clone())
38 }
39}
40
41pub fn install_wgpu_context(context: &WgpuContextHandle) {
44 #[cfg(not(target_arch = "wasm32"))]
45 {
46 let _ = SHARED_WGPU_CONTEXT.set(context.clone());
47 }
48 #[cfg(target_arch = "wasm32")]
49 {
50 SHARED_WGPU_CONTEXT.with(|cell| {
51 *cell.borrow_mut() = Some(context.clone());
52 });
53 }
54 propagate_to_plot_crate(context);
55}
56
57pub fn ensure_context_from_provider() -> BuiltinResult<WgpuContextHandle> {
60 if let Some(ctx) = shared_wgpu_context() {
61 return Ok(ctx);
62 }
63
64 let handle =
65 runmat_accelerate_api::export_context(AccelContextKind::Plotting).ok_or_else(|| {
66 context_error(
67 "plotting context unavailable (GPU provider did not export a shared device)",
68 )
69 })?;
70 match handle {
71 AccelContextHandle::Wgpu(ctx) => {
72 install_wgpu_context(&ctx);
73 Ok(ctx)
74 }
75 }
76}
77
78fn context_error(message: impl Into<String>) -> RuntimeError {
79 build_runtime_error(message)
80 .with_identifier("RunMat:plot:ContextUnavailable")
81 .build()
82}
83
84fn propagate_to_plot_crate(context: &WgpuContextHandle) {
85 #[cfg(any(
94 feature = "gui",
95 feature = "plot-core",
96 all(target_arch = "wasm32", feature = "plot-web")
97 ))]
98 {
99 use runmat_plot::context::{
100 install_shared_wgpu_context as install_plot_context, SharedWgpuContext,
101 };
102 use runmat_plot::gpu::tuning as plot_tuning;
103 install_plot_context(SharedWgpuContext {
104 instance: context.instance.clone(),
105 device: context.device.clone(),
106 queue: context.queue.clone(),
107 adapter: context.adapter.clone(),
108 adapter_info: context.adapter_info.clone(),
109 limits: context.limits.clone(),
110 features: context.features,
111 });
112 if let Some(wg) = runmat_accelerate_api::workgroup_size_hint() {
113 plot_tuning::set_effective_workgroup_size(wg);
114 }
115 }
116
117 #[cfg(not(any(feature = "gui", all(target_arch = "wasm32", feature = "plot-web"))))]
118 {
119 let _ = context;
120 }
121}
122
123#[cfg(all(test, feature = "plot-core"))]
124pub(crate) mod tests {
125 use super::*;
126 use pollster::FutureExt;
127 use std::sync::Arc;
128
129 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
130 #[test]
131 fn install_context_propagates_to_plot_crate() {
132 if std::env::var("RUNMAT_PLOT_SKIP_GPU_TESTS").is_ok() {
133 return;
134 }
135 let instance = Arc::new(wgpu::Instance::default());
136 let adapter = match instance
137 .request_adapter(&wgpu::RequestAdapterOptions {
138 power_preference: wgpu::PowerPreference::HighPerformance,
139 compatible_surface: None,
140 force_fallback_adapter: false,
141 })
142 .block_on()
143 {
144 Some(adapter) => adapter,
145 None => return,
146 };
147 let adapter_info = adapter.get_info();
148 let adapter_limits = adapter.limits();
149 let adapter_features = adapter.features();
150 let adapter = Arc::new(adapter);
151 let (device, queue) = match adapter
152 .request_device(
153 &wgpu::DeviceDescriptor {
154 label: Some("plotting-context-test-device"),
155 required_features: adapter_features,
156 required_limits: adapter_limits.clone(),
157 },
158 None,
159 )
160 .block_on()
161 {
162 Ok(pair) => pair,
163 Err(_) => return,
164 };
165 let handle = WgpuContextHandle {
166 instance: instance.clone(),
167 device: Arc::new(device),
168 queue: Arc::new(queue),
169 adapter: adapter.clone(),
170 adapter_info,
171 limits: adapter_limits.clone(),
172 features: adapter_features,
173 };
174 install_wgpu_context(&handle);
175 assert!(shared_wgpu_context().is_some());
176 assert!(runmat_plot::shared_wgpu_context().is_some());
177 }
178}