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, 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::reflector::{DomGlobal, Reflector, reflect_dom_object};
21use crate::dom::bindings::root::{Dom, DomRoot};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::promise::Promise;
25use crate::dom::types::{GPUAdapterInfo, GPUSupportedLimits};
26use crate::dom::webgpu::gpudevice::GPUDevice;
27use crate::dom::webgpu::gpusupportedfeatures::gpu_to_wgt_feature;
28use crate::realms::InRealm;
29use crate::routed_promise::{RoutedPromiseListener, route_promise};
30use crate::script_runtime::CanGc;
31
32#[dom_struct]
33pub(crate) struct GPUAdapter {
34 reflector_: Reflector,
35 #[ignore_malloc_size_of = "channels are hard"]
36 #[no_trace]
37 channel: WebGPU,
38 name: DOMString,
39 #[ignore_malloc_size_of = "mozjs"]
40 extensions: Heap<*mut JSObject>,
41 features: Dom<GPUSupportedFeatures>,
42 limits: Dom<GPUSupportedLimits>,
43 info: Dom<GPUAdapterInfo>,
44 #[no_trace]
45 adapter: WebGPUAdapter,
46}
47
48impl GPUAdapter {
49 fn new_inherited(
50 channel: WebGPU,
51 name: DOMString,
52 features: &GPUSupportedFeatures,
53 limits: &GPUSupportedLimits,
54 info: &GPUAdapterInfo,
55 adapter: WebGPUAdapter,
56 ) -> Self {
57 Self {
58 reflector_: Reflector::new(),
59 channel,
60 name,
61 extensions: Heap::default(),
62 features: Dom::from_ref(features),
63 limits: Dom::from_ref(limits),
64 info: Dom::from_ref(info),
65 adapter,
66 }
67 }
68
69 #[allow(clippy::too_many_arguments)]
70 pub(crate) fn new(
71 global: &GlobalScope,
72 channel: WebGPU,
73 name: DOMString,
74 extensions: HandleObject,
75 features: wgpu_types::Features,
76 limits: wgpu_types::Limits,
77 info: wgpu_types::AdapterInfo,
78 adapter: WebGPUAdapter,
79 can_gc: CanGc,
80 ) -> DomRoot<Self> {
81 let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
82 let limits = GPUSupportedLimits::new(global, limits, can_gc);
83 let info = GPUAdapterInfo::new(global, info, can_gc);
84 let dom_root = reflect_dom_object(
85 Box::new(GPUAdapter::new_inherited(
86 channel, name, &features, &limits, &info, adapter,
87 )),
88 global,
89 can_gc,
90 );
91 dom_root.extensions.set(*extensions);
92 dom_root
93 }
94}
95
96impl Drop for GPUAdapter {
97 fn drop(&mut self) {
98 if let Err(e) = self
99 .channel
100 .0
101 .send(WebGPURequest::DropAdapter(self.adapter.0))
102 {
103 warn!(
104 "Failed to send WebGPURequest::DropAdapter({:?}) ({})",
105 self.adapter.0, e
106 );
107 };
108 }
109}
110
111impl GPUAdapterMethods<crate::DomTypeHolder> for GPUAdapter {
112 fn RequestDevice(
114 &self,
115 descriptor: &GPUDeviceDescriptor,
116 comp: InRealm,
117 can_gc: CanGc,
118 ) -> Rc<Promise> {
119 let promise = Promise::new_in_current_realm(comp, can_gc);
121 let sender = route_promise(
122 &promise,
123 self,
124 self.global().task_manager().dom_manipulation_task_source(),
125 );
126 let mut required_features = wgpu_types::Features::empty();
127 for &ext in descriptor.requiredFeatures.iter() {
128 if let Some(feature) = gpu_to_wgt_feature(ext) {
129 required_features.insert(feature);
130 } else {
131 promise.reject_error(
132 Error::Type(format!("{} is not supported feature", ext.as_str())),
133 can_gc,
134 );
135 return promise;
136 }
137 }
138
139 let mut required_limits = wgpu_types::Limits::default();
140 if let Some(limits) = &descriptor.requiredLimits {
141 for (limit, value) in (*limits).iter() {
142 if !set_limit(&mut required_limits, limit.as_ref(), *value) {
143 warn!("Unknown GPUDevice limit: {limit}");
144 promise.reject_error(Error::Operation, can_gc);
145 return promise;
146 }
147 }
148 }
149
150 let desc = wgpu_types::DeviceDescriptor {
151 required_features,
152 required_limits,
153 label: Some(descriptor.parent.label.to_string()),
154 memory_hints: MemoryHints::MemoryUsage,
155 trace: wgpu_types::Trace::Off,
156 };
157 let device_id = self.global().wgpu_id_hub().create_device_id();
158 let queue_id = self.global().wgpu_id_hub().create_queue_id();
159 let pipeline_id = self.global().pipeline_id();
160 if self
161 .channel
162 .0
163 .send(WebGPURequest::RequestDevice {
164 sender,
165 adapter_id: self.adapter,
166 descriptor: desc,
167 device_id,
168 queue_id,
169 pipeline_id,
170 })
171 .is_err()
172 {
173 promise.reject_error(Error::Operation, can_gc);
174 }
175 promise
177 }
178
179 fn IsFallbackAdapter(&self) -> bool {
181 false
183 }
184
185 fn RequestAdapterInfo(
187 &self,
188 unmask_hints: Vec<DOMString>,
189 comp: InRealm,
190 can_gc: CanGc,
191 ) -> Rc<Promise> {
192 let promise = Promise::new_in_current_realm(comp, can_gc);
195 if !unmask_hints.is_empty() {
197 todo!("unmaskHints on RequestAdapterInfo");
198 }
199 promise.resolve_native(&*self.info, can_gc);
200 promise
202 }
203
204 fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
206 DomRoot::from_ref(&self.features)
207 }
208
209 fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
211 DomRoot::from_ref(&self.limits)
212 }
213}
214
215impl RoutedPromiseListener<WebGPUDeviceResponse> for GPUAdapter {
216 fn handle_response(
218 &self,
219 response: WebGPUDeviceResponse,
220 promise: &Rc<Promise>,
221 can_gc: CanGc,
222 ) {
223 match response {
224 (device_id, queue_id, Ok(descriptor)) => {
226 let device = GPUDevice::new(
227 &self.global(),
228 self.channel.clone(),
229 self,
230 HandleObject::null(),
231 descriptor.required_features,
232 descriptor.required_limits,
233 device_id,
234 queue_id,
235 descriptor.label.unwrap_or_default(),
236 can_gc,
237 );
238 self.global().add_gpu_device(&device);
239 promise.resolve_native(&device, can_gc);
240 },
241 (_, _, Err(RequestDeviceError::UnsupportedFeature(f))) => promise.reject_error(
243 Error::Type(
244 wgpu_core::instance::RequestDeviceError::UnsupportedFeature(f).to_string(),
245 ),
246 can_gc,
247 ),
248 (_, _, Err(RequestDeviceError::LimitsExceeded(l))) => {
250 warn!(
251 "{}",
252 wgpu_core::instance::RequestDeviceError::LimitsExceeded(l)
253 );
254 promise.reject_error(Error::Operation, can_gc)
255 },
256 (device_id, queue_id, Err(RequestDeviceError::Other(e))) => {
258 let device = GPUDevice::new(
260 &self.global(),
261 self.channel.clone(),
262 self,
263 HandleObject::null(),
264 wgpu_types::Features::default(),
265 wgpu_types::Limits::default(),
266 device_id,
267 queue_id,
268 String::new(),
269 can_gc,
270 );
271 device.lose(GPUDeviceLostReason::Unknown, e);
273 promise.resolve_native(&device, can_gc);
274 },
275 }
276 }
277}