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, route_promise};
31use crate::script_runtime::CanGc;
32
33#[dom_struct]
34pub(crate) struct GPUAdapter {
35 reflector_: Reflector,
36 #[ignore_malloc_size_of = "channels are hard"]
37 #[no_trace]
38 channel: WebGPU,
39 name: DOMString,
40 #[ignore_malloc_size_of = "mozjs"]
41 extensions: Heap<*mut JSObject>,
42 features: Dom<GPUSupportedFeatures>,
43 limits: Dom<GPUSupportedLimits>,
44 info: Dom<GPUAdapterInfo>,
45 #[no_trace]
46 adapter: WebGPUAdapter,
47}
48
49impl GPUAdapter {
50 fn new_inherited(
51 channel: WebGPU,
52 name: DOMString,
53 features: &GPUSupportedFeatures,
54 limits: &GPUSupportedLimits,
55 info: &GPUAdapterInfo,
56 adapter: WebGPUAdapter,
57 ) -> Self {
58 Self {
59 reflector_: Reflector::new(),
60 channel,
61 name,
62 extensions: Heap::default(),
63 features: Dom::from_ref(features),
64 limits: Dom::from_ref(limits),
65 info: Dom::from_ref(info),
66 adapter,
67 }
68 }
69
70 #[allow(clippy::too_many_arguments)]
71 pub(crate) fn new(
72 global: &GlobalScope,
73 channel: WebGPU,
74 name: DOMString,
75 extensions: HandleObject,
76 features: wgpu_types::Features,
77 limits: wgpu_types::Limits,
78 info: wgpu_types::AdapterInfo,
79 adapter: WebGPUAdapter,
80 can_gc: CanGc,
81 ) -> DomRoot<Self> {
82 let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
83 let limits = GPUSupportedLimits::new(global, limits, can_gc);
84 let info = GPUAdapter::create_adapter_info(global, info, &features, &limits, can_gc);
85 let dom_root = reflect_dom_object(
86 Box::new(GPUAdapter::new_inherited(
87 channel, name, &features, &limits, &info, adapter,
88 )),
89 global,
90 can_gc,
91 );
92 dom_root.extensions.set(*extensions);
93 dom_root
94 }
95
96 fn create_adapter_info(
98 global: &GlobalScope,
99 info: AdapterInfo,
100 features: &GPUSupportedFeatures,
101 limits: &GPUSupportedLimits,
102 can_gc: CanGc,
103 ) -> DomRoot<GPUAdapterInfo> {
104 let vendor = if info.vendor != 0 {
109 DOMString::from_string(info.vendor.to_string())
110 } else {
111 DOMString::new()
112 };
113
114 let architecture = DOMString::new();
122
123 let device = if info.device != 0 {
128 DOMString::from_string(info.device.to_string())
129 } else {
130 DOMString::new()
131 };
132
133 let description = DOMString::from_string(info.name.clone());
138
139 let (subgroup_min_size, subgroup_max_size) = if features.has("subgroups".into()) {
144 (
145 limits.wgpu_limits().min_subgroup_size,
146 limits.wgpu_limits().max_subgroup_size,
147 )
148 } else {
149 (4, 128)
150 };
151
152 let is_fallback_adapter = info.device_type == wgpu_types::DeviceType::Cpu;
154
155 GPUAdapterInfo::new(
157 global,
158 vendor,
159 architecture,
160 device,
161 description,
162 subgroup_min_size,
163 subgroup_max_size,
164 is_fallback_adapter,
165 can_gc,
166 )
167 }
168}
169
170impl Drop for GPUAdapter {
171 fn drop(&mut self) {
172 if let Err(e) = self
173 .channel
174 .0
175 .send(WebGPURequest::DropAdapter(self.adapter.0))
176 {
177 warn!(
178 "Failed to send WebGPURequest::DropAdapter({:?}) ({})",
179 self.adapter.0, e
180 );
181 };
182 }
183}
184
185impl GPUAdapterMethods<crate::DomTypeHolder> for GPUAdapter {
186 fn RequestDevice(
188 &self,
189 descriptor: &GPUDeviceDescriptor,
190 comp: InRealm,
191 can_gc: CanGc,
192 ) -> Rc<Promise> {
193 let promise = Promise::new_in_current_realm(comp, can_gc);
195 let sender = route_promise(
196 &promise,
197 self,
198 self.global().task_manager().dom_manipulation_task_source(),
199 );
200 let mut required_features = wgpu_types::Features::empty();
201 for &ext in descriptor.requiredFeatures.iter() {
202 if let Some(feature) = gpu_to_wgt_feature(ext) {
203 required_features.insert(feature);
204 } else {
205 promise.reject_error(
206 Error::Type(format!("{} is not supported feature", ext.as_str())),
207 can_gc,
208 );
209 return promise;
210 }
211 }
212
213 let mut required_limits = wgpu_types::Limits::default();
214 if let Some(limits) = &descriptor.requiredLimits {
215 for (limit, value) in (*limits).iter() {
216 if !set_limit(&mut required_limits, &limit.str(), *value) {
217 warn!("Unknown GPUDevice limit: {limit}");
218 promise.reject_error(Error::Operation, can_gc);
219 return promise;
220 }
221 }
222 }
223
224 let desc = wgpu_types::DeviceDescriptor {
225 required_features,
226 required_limits,
227 label: Some(descriptor.parent.label.to_string()),
228 memory_hints: MemoryHints::MemoryUsage,
229 trace: wgpu_types::Trace::Off,
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 .channel
236 .0
237 .send(WebGPURequest::RequestDevice {
238 sender,
239 adapter_id: self.adapter,
240 descriptor: desc,
241 device_id,
242 queue_id,
243 pipeline_id,
244 })
245 .is_err()
246 {
247 promise.reject_error(Error::Operation, can_gc);
248 }
249 promise
251 }
252
253 fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
255 DomRoot::from_ref(&self.features)
256 }
257
258 fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
260 DomRoot::from_ref(&self.limits)
261 }
262
263 fn Info(&self) -> DomRoot<GPUAdapterInfo> {
265 DomRoot::from_ref(&self.info)
266 }
267}
268
269impl RoutedPromiseListener<WebGPUDeviceResponse> for GPUAdapter {
270 fn handle_response(
272 &self,
273 response: WebGPUDeviceResponse,
274 promise: &Rc<Promise>,
275 can_gc: CanGc,
276 ) {
277 match response {
278 (device_id, queue_id, Ok(descriptor)) => {
280 let device = GPUDevice::new(
281 &self.global(),
282 self.channel.clone(),
283 self,
284 HandleObject::null(),
285 descriptor.required_features,
286 descriptor.required_limits,
287 device_id,
288 queue_id,
289 descriptor.label.unwrap_or_default(),
290 can_gc,
291 );
292 self.global().add_gpu_device(&device);
293 promise.resolve_native(&device, can_gc);
294 },
295 (_, _, Err(RequestDeviceError::UnsupportedFeature(f))) => promise.reject_error(
297 Error::Type(
298 wgpu_core::instance::RequestDeviceError::UnsupportedFeature(f).to_string(),
299 ),
300 can_gc,
301 ),
302 (_, _, Err(RequestDeviceError::LimitsExceeded(l))) => {
304 warn!(
305 "{}",
306 wgpu_core::instance::RequestDeviceError::LimitsExceeded(l)
307 );
308 promise.reject_error(Error::Operation, can_gc)
309 },
310 (device_id, queue_id, Err(RequestDeviceError::Other(e))) => {
312 let device = GPUDevice::new(
314 &self.global(),
315 self.channel.clone(),
316 self,
317 HandleObject::null(),
318 wgpu_types::Features::default(),
319 wgpu_types::Limits::default(),
320 device_id,
321 queue_id,
322 String::new(),
323 can_gc,
324 );
325 device.lose(GPUDeviceLostReason::Unknown, e);
327 promise.resolve_native(&device, can_gc);
328 },
329 }
330 }
331}