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