qualia_core_db/gpu_context/
caps.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8#[cfg(not(target_arch = "wasm32"))]
9pub struct GpuFeatureCaps {
10 pub timestamp_query: bool,
11 pub timestamp_query_inside_passes: bool,
12 pub pipeline_statistics_query: bool,
13 pub pipeline_cache: bool,
14 pub shader_f16: bool,
15 pub shader_int64: bool,
16 pub subgroup: bool,
17 pub subgroup_barrier: bool,
18 pub cooperative_matrix: bool,
19 pub ray_query: bool,
20}
21
22#[cfg(not(target_arch = "wasm32"))]
23impl GpuFeatureCaps {
24 pub fn from_features(features: wgpu::Features) -> Self {
25 Self {
26 timestamp_query: features.contains(wgpu::Features::TIMESTAMP_QUERY),
27 timestamp_query_inside_passes: features
28 .contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES),
29 pipeline_statistics_query: features.contains(wgpu::Features::PIPELINE_STATISTICS_QUERY),
30 pipeline_cache: features.contains(wgpu::Features::PIPELINE_CACHE),
31 shader_f16: features.contains(wgpu::Features::SHADER_F16),
32 shader_int64: features.contains(wgpu::Features::SHADER_INT64),
33 subgroup: features.contains(wgpu::Features::SUBGROUP),
34 subgroup_barrier: features.contains(wgpu::Features::SUBGROUP_BARRIER),
35 cooperative_matrix: features.contains(wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX),
36 ray_query: features.contains(wgpu::Features::EXPERIMENTAL_RAY_QUERY),
37 }
38 }
39
40 pub fn compact_flags(&self) -> String {
41 let mut out = String::with_capacity(96);
42 push_flag(&mut out, "ts", self.timestamp_query);
43 push_flag(&mut out, "ts-pass", self.timestamp_query_inside_passes);
44 push_flag(&mut out, "stats", self.pipeline_statistics_query);
45 push_flag(&mut out, "cache", self.pipeline_cache);
46 push_flag(&mut out, "f16", self.shader_f16);
47 push_flag(&mut out, "i64", self.shader_int64);
48 push_flag(&mut out, "subgroup", self.subgroup);
49 push_flag(&mut out, "subgroup-barrier", self.subgroup_barrier);
50 push_flag(&mut out, "coop-matrix", self.cooperative_matrix);
51 push_flag(&mut out, "ray-query", self.ray_query);
52 out
53 }
54}
55
56pub fn requested_native_llm_features(available: wgpu::Features) -> wgpu::Features {
57 let mut desired = wgpu::Features::TIMESTAMP_QUERY
58 | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES
59 | wgpu::Features::PIPELINE_STATISTICS_QUERY
60 | wgpu::Features::PIPELINE_CACHE
61 | wgpu::Features::SHADER_F16
62 | wgpu::Features::SUBGROUP
63 | wgpu::Features::SUBGROUP_BARRIER;
64 if experimental_features_allowed() {
65 desired |= wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX;
66 }
67 available & desired
68}
69
70pub fn experimental_features_allowed() -> bool {
71 matches!(
72 std::env::var("QUALIA_WGPU_EXPERIMENTAL_FEATURES")
73 .ok()
74 .as_deref(),
75 Some("1") | Some("true") | Some("yes")
76 )
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
80#[cfg(not(target_arch = "wasm32"))]
81pub struct GpuLimitCaps {
82 pub max_buffer_size: u64,
83 pub max_storage_buffer_binding_size: u64,
84 pub max_compute_workgroup_storage_size: u32,
85 pub max_compute_invocations_per_workgroup: u32,
86 pub max_compute_workgroup_size_x: u32,
87 pub max_compute_workgroups_per_dimension: u32,
88}
89
90#[cfg(not(target_arch = "wasm32"))]
91impl GpuLimitCaps {
92 pub fn from_limits(limits: &wgpu::Limits) -> Self {
93 Self {
94 max_buffer_size: limits.max_buffer_size,
95 max_storage_buffer_binding_size: limits.max_storage_buffer_binding_size,
96 max_compute_workgroup_storage_size: limits.max_compute_workgroup_storage_size,
97 max_compute_invocations_per_workgroup: limits.max_compute_invocations_per_workgroup,
98 max_compute_workgroup_size_x: limits.max_compute_workgroup_size_x,
99 max_compute_workgroups_per_dimension: limits.max_compute_workgroups_per_dimension,
100 }
101 }
102}
103
104#[derive(Debug, Clone)]
105#[cfg(not(target_arch = "wasm32"))]
106pub struct GpuAdapterCaps {
107 pub name: String,
108 pub backend: wgpu::Backend,
109 pub device_type: wgpu::DeviceType,
110 pub vendor: u32,
111 pub device: u32,
112 pub driver: String,
113 pub driver_info: String,
114 pub subgroup_min_size: u32,
115 pub subgroup_max_size: u32,
116 pub cooperative_matrix_tile_count: usize,
117 pub features: GpuFeatureCaps,
118 pub limits: GpuLimitCaps,
119}
120
121#[cfg(not(target_arch = "wasm32"))]
122impl GpuAdapterCaps {
123 pub fn from_adapter(adapter: &wgpu::Adapter) -> Self {
124 let info = adapter.get_info();
125 let features = adapter.features();
126 let limits = adapter.limits();
127 Self {
128 name: info.name,
129 backend: info.backend,
130 device_type: info.device_type,
131 vendor: info.vendor,
132 device: info.device,
133 driver: info.driver,
134 driver_info: info.driver_info,
135 subgroup_min_size: info.subgroup_min_size,
136 subgroup_max_size: info.subgroup_max_size,
137 cooperative_matrix_tile_count: adapter.cooperative_matrix_properties().len(),
138 features: GpuFeatureCaps::from_features(features),
139 limits: GpuLimitCaps::from_limits(&limits),
140 }
141 }
142
143 #[inline]
144 pub fn is_integrated_gpu(&self) -> bool {
145 matches!(self.device_type, wgpu::DeviceType::IntegratedGpu)
146 }
147
148 #[inline]
149 pub fn backend_label(&self) -> &'static str {
150 match self.backend {
151 wgpu::Backend::Noop => "noop",
152 wgpu::Backend::Vulkan => "vulkan",
153 wgpu::Backend::Metal => "metal",
154 wgpu::Backend::Dx12 => "dx12",
155 wgpu::Backend::Gl => "gl",
156 wgpu::Backend::BrowserWebGpu => "browser-webgpu",
157 }
158 }
159
160 #[inline]
161 pub fn device_type_label(&self) -> &'static str {
162 match self.device_type {
163 wgpu::DeviceType::Other => "other",
164 wgpu::DeviceType::IntegratedGpu => "integrated",
165 wgpu::DeviceType::DiscreteGpu => "discrete",
166 wgpu::DeviceType::VirtualGpu => "virtual",
167 wgpu::DeviceType::Cpu => "cpu",
168 }
169 }
170
171 pub fn summary_line(&self) -> String {
172 format!(
173 "{} | backend={} | type={} | vendor=0x{:04x} | device=0x{:04x} | driver={} {}",
174 self.name,
175 self.backend_label(),
176 self.device_type_label(),
177 self.vendor,
178 self.device,
179 self.driver,
180 self.driver_info
181 )
182 }
183
184 pub fn llm_feature_line(&self) -> String {
185 format!(
186 "features=[{}] subgroup={}..{} coop_tiles={} max_storage_binding={}MiB max_buffer={}MiB",
187 self.features.compact_flags(),
188 self.subgroup_min_size,
189 self.subgroup_max_size,
190 self.cooperative_matrix_tile_count,
191 self.limits.max_storage_buffer_binding_size / (1024 * 1024),
192 self.limits.max_buffer_size / (1024 * 1024)
193 )
194 }
195}
196
197#[cfg(not(target_arch = "wasm32"))]
198fn push_flag(out: &mut String, label: &str, enabled: bool) {
199 if !out.is_empty() {
200 out.push(' ');
201 }
202 out.push_str(label);
203 out.push('=');
204 out.push_str(if enabled { "1" } else { "0" });
205}
206
207#[cfg(not(target_arch = "wasm32"))]
218pub fn qualia_backend_override() -> Option<wgpu::Backends> {
219 if let Ok(raw) = std::env::var("QUALIA_WGPU_BACKEND") {
220 let v = raw.trim().to_ascii_lowercase();
221 return match v.as_str() {
222 "vulkan" | "vk" => Some(wgpu::Backends::VULKAN),
223 "dx12" | "d3d12" | "directx12" => Some(wgpu::Backends::DX12),
224 "metal" | "mtl" => Some(wgpu::Backends::METAL),
225 "gl" | "opengl" | "gles" => Some(wgpu::Backends::GL),
226 "primary" => Some(wgpu::Backends::PRIMARY),
227 "all" => Some(wgpu::Backends::all()),
228 other => {
229 log::warn!(
230 "QUALIA_WGPU_BACKEND='{other}' unrecognized — trying passport / default"
231 );
232 None
233 }
234 }
235 .or_else(passport_backend_override);
236 }
237 passport_backend_override()
238}
239
240#[cfg(not(target_arch = "wasm32"))]
242fn passport_backend_override() -> Option<wgpu::Backends> {
243 let backend = crate::hardware_passport::cached_preferred_wgpu_backend()?;
244 let token = crate::hardware_passport::backend_env_token(&backend)?;
245 log::info!("shared_gpu|backend_from_passport|{backend}|token={token}");
246 match token {
247 "vulkan" => Some(wgpu::Backends::VULKAN),
248 "dx12" => Some(wgpu::Backends::DX12),
249 "metal" => Some(wgpu::Backends::METAL),
250 "gl" => Some(wgpu::Backends::GL),
251 _ => None,
252 }
253}
254
255#[cfg(not(target_arch = "wasm32"))]
260pub fn recommend_inference_backend(caps: &GpuAdapterCaps) -> &'static str {
261 match caps.backend {
262 wgpu::Backend::Vulkan => "vulkan — vendor-neutral & portable (recommended)",
263 wgpu::Backend::Metal => "metal — Apple-native (recommended on macOS)",
264 wgpu::Backend::Dx12 => {
265 "dx12 — Windows-native; set QUALIA_WGPU_BACKEND=vulkan for the portable path"
266 }
267 wgpu::Backend::Gl => "gl — compatibility fallback, limited compute (last resort)",
268 _ => "noop/unknown — no GPU compute path",
269 }
270}