1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::jsapi::{HandleObject, Heap, JSObject};
9use js::realm::CurrentRealm;
10use script_bindings::cformat;
11use script_bindings::like::Setlike;
12use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
13use webgpu_traits::{
14 RequestDeviceError, WebGPU, WebGPUAdapter, WebGPUDeviceResponse, WebGPURequest,
15};
16use wgpu_types::{self, AdapterInfo, ExperimentalFeatures, MemoryHints};
17
18use super::gpusupportedfeatures::GPUSupportedFeatures;
19use super::gpusupportedlimits::set_limit;
20use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
21 GPUAdapterMethods, GPUDeviceDescriptor, GPUDeviceLostReason,
22};
23use crate::dom::bindings::error::Error;
24use crate::dom::bindings::reflector::DomGlobal;
25use crate::dom::bindings::root::{Dom, DomRoot};
26use crate::dom::bindings::str::DOMString;
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::promise::Promise;
29use crate::dom::types::{GPUAdapterInfo, GPUSupportedLimits};
30use crate::dom::webgpu::gpudevice::GPUDevice;
31use crate::dom::webgpu::gpusupportedfeatures::gpu_to_wgt_feature;
32use crate::routed_promise::{RoutedPromiseListener, callback_promise};
33
34#[derive(JSTraceable, MallocSizeOf)]
35struct DroppableGPUAdapter {
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 cx: &mut js::context::JSContext,
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 ) -> DomRoot<Self> {
101 let features = GPUSupportedFeatures::Constructor(cx, global, None, features).unwrap();
102 let limits = GPUSupportedLimits::new(cx, global, limits);
103 let info = GPUAdapter::create_adapter_info(cx, global, info, &features);
104 let dom_root = reflect_dom_object_with_cx(
105 Box::new(GPUAdapter::new_inherited(
106 channel, name, &features, &limits, &info, adapter,
107 )),
108 global,
109 cx,
110 );
111 dom_root.extensions.set(*extensions);
112 dom_root
113 }
114
115 fn create_adapter_info(
117 cx: &mut js::context::JSContext,
118 global: &GlobalScope,
119 info: AdapterInfo,
120 features: &GPUSupportedFeatures,
121 ) -> DomRoot<GPUAdapterInfo> {
122 let vendor = if info.vendor != 0 {
127 info.vendor.to_string().into()
128 } else {
129 DOMString::new()
130 };
131
132 let architecture = DOMString::new();
140
141 let device = if info.device != 0 {
146 info.device.to_string().into()
147 } else {
148 DOMString::new()
149 };
150
151 let description = info.name.clone().into();
156
157 let (subgroup_min_size, subgroup_max_size) = if features.has("subgroups".into()) {
162 (info.subgroup_min_size, info.subgroup_max_size)
163 } else {
164 (4, 128)
165 };
166
167 let is_fallback_adapter = info.device_type == wgpu_types::DeviceType::Cpu;
169
170 GPUAdapterInfo::new(
172 cx,
173 global,
174 vendor,
175 architecture,
176 device,
177 description,
178 subgroup_min_size,
179 subgroup_max_size,
180 is_fallback_adapter,
181 )
182 }
183}
184
185impl GPUAdapterMethods<crate::DomTypeHolder> for GPUAdapter {
186 fn RequestDevice(
188 &self,
189 cx: &mut CurrentRealm<'_>,
190 descriptor: &GPUDeviceDescriptor,
191 ) -> Rc<Promise> {
192 let promise = Promise::new_in_realm(cx);
194 let callback = callback_promise(
195 &promise,
196 self,
197 self.global().task_manager().dom_manipulation_task_source(),
198 );
199 let mut required_features = wgpu_types::Features::empty();
200 for &ext in descriptor.requiredFeatures.iter() {
201 if let Some(feature) = gpu_to_wgt_feature(ext) {
202 required_features.insert(feature);
203 } else {
204 promise.reject_error_with_cx(
205 cx,
206 Error::Type(cformat!("{} is not supported feature", ext.as_str())),
207 );
208 return promise;
209 }
210 }
211
212 let mut required_limits = wgpu_types::Limits::default();
213 if let Some(limits) = &descriptor.requiredLimits {
214 for (limit, value) in (*limits).iter() {
215 if !set_limit(&mut required_limits, &limit.str(), *value) {
216 warn!("Unknown GPUDevice limit: {limit}");
217 promise.reject_error_with_cx(cx, Error::Operation(None));
218 return promise;
219 }
220 }
221 }
222
223 let desc = wgpu_types::DeviceDescriptor {
224 required_features,
225 required_limits,
226 label: Some(descriptor.parent.label.to_string()),
227 memory_hints: MemoryHints::MemoryUsage,
228 trace: wgpu_types::Trace::Off,
229 experimental_features: ExperimentalFeatures::disabled(),
230 };
231 let device_id = self.global().wgpu_id_hub().create_device_id();
232 let queue_id = self.global().wgpu_id_hub().create_queue_id();
233 let pipeline_id = self.global().pipeline_id();
234 if self
235 .droppable
236 .channel
237 .0
238 .send(WebGPURequest::RequestDevice {
239 sender: callback,
240 adapter_id: self.droppable.adapter,
241 descriptor: desc,
242 device_id,
243 queue_id,
244 pipeline_id,
245 })
246 .is_err()
247 {
248 promise.reject_error_with_cx(cx, Error::Operation(None));
249 }
250 promise
252 }
253
254 fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
256 DomRoot::from_ref(&self.features)
257 }
258
259 fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
261 DomRoot::from_ref(&self.limits)
262 }
263
264 fn Info(&self) -> DomRoot<GPUAdapterInfo> {
266 DomRoot::from_ref(&self.info)
267 }
268}
269
270impl RoutedPromiseListener<WebGPUDeviceResponse> for GPUAdapter {
271 fn handle_response(
273 &self,
274 cx: &mut js::context::JSContext,
275 response: WebGPUDeviceResponse,
276 promise: &Rc<Promise>,
277 ) {
278 match response {
279 (device_id, queue_id, Ok(descriptor)) => {
281 let device = GPUDevice::new(
282 cx,
283 &self.global(),
284 self.droppable.channel.clone(),
285 self,
286 HandleObject::null(),
287 descriptor.required_features,
288 descriptor.required_limits,
289 device_id,
290 queue_id,
291 descriptor.label.unwrap_or_default(),
292 );
293 self.global().add_gpu_device(&device);
294 promise.resolve_native_with_cx(cx, &device);
295 },
296 (_, _, Err(RequestDeviceError::UnsupportedFeature(f))) => promise.reject_error_with_cx(
298 cx,
299 Error::Type(cformat!(
300 "{}",
301 wgpu_core::instance::RequestDeviceError::UnsupportedFeature(f)
302 )),
303 ),
304 (_, _, Err(RequestDeviceError::LimitsExceeded(l))) => {
306 warn!(
307 "{}",
308 wgpu_core::instance::RequestDeviceError::LimitsExceeded(l)
309 );
310 promise.reject_error_with_cx(cx, Error::Operation(None))
311 },
312 (device_id, queue_id, Err(RequestDeviceError::Other(e))) => {
314 let device = GPUDevice::new(
319 cx,
320 &self.global(),
321 self.droppable.channel.clone(),
322 self,
323 HandleObject::null(),
324 wgpu_types::Features::default(),
325 wgpu_types::Limits::default(),
326 device_id,
327 queue_id,
328 String::new(),
329 );
330 device.lose(GPUDeviceLostReason::Unknown, e);
332 promise.resolve_native_with_cx(cx, &device);
333 },
334 }
335 }
336}