1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::jsapi::{HandleObject, Heap, JSObject};
9use webgpu_traits::{
10 RequestDeviceError, WebGPU, WebGPUAdapter, WebGPUDeviceResponse, WebGPURequest,
11};
12use wgpu_types::{self, AdapterInfo, MemoryHints};
13
14use super::gpusupportedfeatures::GPUSupportedFeatures;
15use super::gpusupportedlimits::set_limit;
16use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
17 GPUAdapterMethods, GPUDeviceDescriptor, GPUDeviceLostReason,
18};
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::like::Setlike;
21use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::bindings::str::DOMString;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::promise::Promise;
26use crate::dom::types::{GPUAdapterInfo, GPUSupportedLimits};
27use crate::dom::webgpu::gpudevice::GPUDevice;
28use crate::dom::webgpu::gpusupportedfeatures::gpu_to_wgt_feature;
29use crate::realms::InRealm;
30use crate::routed_promise::{RoutedPromiseListener, callback_promise};
31use crate::script_runtime::CanGc;
32
33#[derive(JSTraceable, MallocSizeOf)]
34struct DroppableGPUAdapter {
35 #[ignore_malloc_size_of = "channels are hard"]
36 #[no_trace]
37 channel: WebGPU,
38 #[no_trace]
39 adapter: WebGPUAdapter,
40}
41
42impl Drop for DroppableGPUAdapter {
43 fn drop(&mut self) {
44 if let Err(e) = self
45 .channel
46 .0
47 .send(WebGPURequest::DropAdapter(self.adapter.0))
48 {
49 warn!(
50 "Failed to send WebGPURequest::DropAdapter({:?}) ({})",
51 self.adapter.0, e
52 );
53 };
54 }
55}
56
57#[dom_struct]
58pub(crate) struct GPUAdapter {
59 reflector_: Reflector,
60 name: DOMString,
61 #[ignore_malloc_size_of = "mozjs"]
62 extensions: Heap<*mut JSObject>,
63 features: Dom<GPUSupportedFeatures>,
64 limits: Dom<GPUSupportedLimits>,
65 info: Dom<GPUAdapterInfo>,
66 droppable: DroppableGPUAdapter,
67}
68
69impl GPUAdapter {
70 fn new_inherited(
71 channel: WebGPU,
72 name: DOMString,
73 features: &GPUSupportedFeatures,
74 limits: &GPUSupportedLimits,
75 info: &GPUAdapterInfo,
76 adapter: WebGPUAdapter,
77 ) -> Self {
78 Self {
79 reflector_: Reflector::new(),
80 name,
81 extensions: Heap::default(),
82 features: Dom::from_ref(features),
83 limits: Dom::from_ref(limits),
84 info: Dom::from_ref(info),
85 droppable: DroppableGPUAdapter { channel, adapter },
86 }
87 }
88
89 #[allow(clippy::too_many_arguments)]
90 pub(crate) fn new(
91 global: &GlobalScope,
92 channel: WebGPU,
93 name: DOMString,
94 extensions: HandleObject,
95 features: wgpu_types::Features,
96 limits: wgpu_types::Limits,
97 info: wgpu_types::AdapterInfo,
98 adapter: WebGPUAdapter,
99 can_gc: CanGc,
100 ) -> DomRoot<Self> {
101 let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
102 let limits = GPUSupportedLimits::new(global, limits, can_gc);
103 let info = GPUAdapter::create_adapter_info(global, info, &features, &limits, can_gc);
104 let dom_root = reflect_dom_object(
105 Box::new(GPUAdapter::new_inherited(
106 channel, name, &features, &limits, &info, adapter,
107 )),
108 global,
109 can_gc,
110 );
111 dom_root.extensions.set(*extensions);
112 dom_root
113 }
114
115 fn create_adapter_info(
117 global: &GlobalScope,
118 info: AdapterInfo,
119 features: &GPUSupportedFeatures,
120 limits: &GPUSupportedLimits,
121 can_gc: CanGc,
122 ) -> DomRoot<GPUAdapterInfo> {
123 let vendor = if info.vendor != 0 {
128 DOMString::from_string(info.vendor.to_string())
129 } else {
130 DOMString::new()
131 };
132
133 let architecture = DOMString::new();
141
142 let device = if info.device != 0 {
147 DOMString::from_string(info.device.to_string())
148 } else {
149 DOMString::new()
150 };
151
152 let description = DOMString::from_string(info.name.clone());
157
158 let (subgroup_min_size, subgroup_max_size) = if features.has("subgroups".into()) {
163 (
164 limits.wgpu_limits().min_subgroup_size,
165 limits.wgpu_limits().max_subgroup_size,
166 )
167 } else {
168 (4, 128)
169 };
170
171 let is_fallback_adapter = info.device_type == wgpu_types::DeviceType::Cpu;
173
174 GPUAdapterInfo::new(
176 global,
177 vendor,
178 architecture,
179 device,
180 description,
181 subgroup_min_size,
182 subgroup_max_size,
183 is_fallback_adapter,
184 can_gc,
185 )
186 }
187}
188
189impl GPUAdapterMethods<crate::DomTypeHolder> for GPUAdapter {
190 fn RequestDevice(
192 &self,
193 descriptor: &GPUDeviceDescriptor,
194 comp: InRealm,
195 can_gc: CanGc,
196 ) -> Rc<Promise> {
197 let promise = Promise::new_in_current_realm(comp, can_gc);
199 let callback = callback_promise(
200 &promise,
201 self,
202 self.global().task_manager().dom_manipulation_task_source(),
203 );
204 let mut required_features = wgpu_types::Features::empty();
205 for &ext in descriptor.requiredFeatures.iter() {
206 if let Some(feature) = gpu_to_wgt_feature(ext) {
207 required_features.insert(feature);
208 } else {
209 promise.reject_error(
210 Error::Type(format!("{} is not supported feature", ext.as_str())),
211 can_gc,
212 );
213 return promise;
214 }
215 }
216
217 let mut required_limits = wgpu_types::Limits::default();
218 if let Some(limits) = &descriptor.requiredLimits {
219 for (limit, value) in (*limits).iter() {
220 if !set_limit(&mut required_limits, &limit.str(), *value) {
221 warn!("Unknown GPUDevice limit: {limit}");
222 promise.reject_error(Error::Operation(None), can_gc);
223 return promise;
224 }
225 }
226 }
227
228 let desc = wgpu_types::DeviceDescriptor {
229 required_features,
230 required_limits,
231 label: Some(descriptor.parent.label.to_string()),
232 memory_hints: MemoryHints::MemoryUsage,
233 trace: wgpu_types::Trace::Off,
234 };
235 let device_id = self.global().wgpu_id_hub().create_device_id();
236 let queue_id = self.global().wgpu_id_hub().create_queue_id();
237 let pipeline_id = self.global().pipeline_id();
238 if self
239 .droppable
240 .channel
241 .0
242 .send(WebGPURequest::RequestDevice {
243 sender: callback,
244 adapter_id: self.droppable.adapter,
245 descriptor: desc,
246 device_id,
247 queue_id,
248 pipeline_id,
249 })
250 .is_err()
251 {
252 promise.reject_error(Error::Operation(None), can_gc);
253 }
254 promise
256 }
257
258 fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
260 DomRoot::from_ref(&self.features)
261 }
262
263 fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
265 DomRoot::from_ref(&self.limits)
266 }
267
268 fn Info(&self) -> DomRoot<GPUAdapterInfo> {
270 DomRoot::from_ref(&self.info)
271 }
272}
273
274impl RoutedPromiseListener<WebGPUDeviceResponse> for GPUAdapter {
275 fn handle_response(
277 &self,
278 response: WebGPUDeviceResponse,
279 promise: &Rc<Promise>,
280 can_gc: CanGc,
281 ) {
282 match response {
283 (device_id, queue_id, Ok(descriptor)) => {
285 let device = GPUDevice::new(
286 &self.global(),
287 self.droppable.channel.clone(),
288 self,
289 HandleObject::null(),
290 descriptor.required_features,
291 descriptor.required_limits,
292 device_id,
293 queue_id,
294 descriptor.label.unwrap_or_default(),
295 can_gc,
296 );
297 self.global().add_gpu_device(&device);
298 promise.resolve_native(&device, can_gc);
299 },
300 (_, _, Err(RequestDeviceError::UnsupportedFeature(f))) => promise.reject_error(
302 Error::Type(
303 wgpu_core::instance::RequestDeviceError::UnsupportedFeature(f).to_string(),
304 ),
305 can_gc,
306 ),
307 (_, _, Err(RequestDeviceError::LimitsExceeded(l))) => {
309 warn!(
310 "{}",
311 wgpu_core::instance::RequestDeviceError::LimitsExceeded(l)
312 );
313 promise.reject_error(Error::Operation(None), can_gc)
314 },
315 (device_id, queue_id, Err(RequestDeviceError::Other(e))) => {
317 let device = GPUDevice::new(
319 &self.global(),
320 self.droppable.channel.clone(),
321 self,
322 HandleObject::null(),
323 wgpu_types::Features::default(),
324 wgpu_types::Limits::default(),
325 device_id,
326 queue_id,
327 String::new(),
328 can_gc,
329 );
330 device.lose(GPUDeviceLostReason::Unknown, e);
332 promise.resolve_native(&device, can_gc);
333 },
334 }
335 }
336}