1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::jsapi::{HandleObject, Heap, JSObject};
9use js::realm::CurrentRealm;
10use jstraceable_derive::JSTraceable;
11use log::warn;
12use malloc_size_of_derive::MallocSizeOf;
13use script_bindings::codegen::GenericBindings::WebGPUBinding::{
14 GPUAdapterMethods, GPUAdapterWrap, GPUDeviceDescriptor,
15};
16use script_bindings::interfaces::{GlobalScopeHelpers, PromiseHelpers};
17use script_bindings::like::Setlike;
18use script_bindings::reflector::{DomGlobalGeneric, Reflector, reflect_dom_object_with_wrap};
19use script_bindings::{DomTypes, cformat};
20use webgpu_traits::{WebGPU, WebGPUAdapter, WebGPURequest};
21use wgpu_types::{AdapterInfo, ExperimentalFeatures, MemoryHints};
22
23use crate::dom::bindings::error::Error;
24use crate::dom::bindings::root::{Dom, DomRoot};
25use crate::dom::bindings::str::DOMString;
26use crate::gpuadapterinfo::GPUAdapterInfo;
27use crate::gpusupportedfeatures::{GPUSupportedFeatures, gpu_to_wgt_feature};
28use crate::gpusupportedlimits::{GPUSupportedLimits, set_limit};
29use crate::traits::{Equivalence, WebGPUGlobalTrait, WebGPUPromiseTrait};
30
31#[derive(JSTraceable, MallocSizeOf)]
32struct DroppableGPUAdapter {
33 #[no_trace]
34 channel: WebGPU,
35 #[no_trace]
36 adapter: WebGPUAdapter,
37}
38
39impl Drop for DroppableGPUAdapter {
40 fn drop(&mut self) {
41 if let Err(e) = self
42 .channel
43 .0
44 .send(WebGPURequest::DropAdapter(self.adapter.0))
45 {
46 warn!(
47 "Failed to send WebGPURequest::DropAdapter({:?}) ({})",
48 self.adapter.0, e
49 );
50 };
51 }
52}
53
54#[dom_struct]
55pub struct GPUAdapter<D: DomTypes> {
56 reflector_: Reflector,
57 name: DOMString,
58 #[ignore_malloc_size_of = "mozjs"]
59 extensions: Heap<*mut JSObject>,
60 features: Dom<GPUSupportedFeatures<D>>,
61 limits: Dom<GPUSupportedLimits<D>>,
62 info: Dom<GPUAdapterInfo<D>>,
63 droppable: DroppableGPUAdapter,
64}
65
66impl<D> GPUAdapter<D>
67where
68 D: Equivalence,
69{
70 fn new_inherited(
71 channel: WebGPU,
72 name: DOMString,
73 features: &GPUSupportedFeatures<D>,
74 limits: &GPUSupportedLimits<D>,
75 info: &GPUAdapterInfo<D>,
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 fn new(
91 cx: &mut js::context::JSContext,
92 global: &D::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_wrap::<D, _, _>(
105 Box::new(GPUAdapter::new_inherited(
106 channel, name, &features, &limits, &info, adapter,
107 )),
108 global,
109 cx,
110 GPUAdapterWrap::<D>,
111 );
112 dom_root.extensions.set(*extensions);
113 dom_root
114 }
115
116 fn create_adapter_info(
118 cx: &mut js::context::JSContext,
119 global: &D::GlobalScope,
120 info: AdapterInfo,
121 features: &GPUSupportedFeatures<D>,
122 ) -> DomRoot<GPUAdapterInfo<D>> {
123 let vendor = if info.vendor != 0 {
128 info.vendor.to_string().into()
129 } else {
130 DOMString::new()
131 };
132
133 let architecture = DOMString::new();
141
142 let device = if info.device != 0 {
147 info.device.to_string().into()
148 } else {
149 DOMString::new()
150 };
151
152 let description = info.name.clone().into();
157
158 let (subgroup_min_size, subgroup_max_size) = if features.has(cx, "subgroups".into()) {
163 (info.subgroup_min_size, info.subgroup_max_size)
164 } else {
165 (4, 128)
166 };
167
168 let is_fallback_adapter = info.device_type == wgpu_types::DeviceType::Cpu;
170
171 GPUAdapterInfo::new(
173 cx,
174 global,
175 vendor,
176 architecture,
177 device,
178 description,
179 subgroup_min_size,
180 subgroup_max_size,
181 is_fallback_adapter,
182 )
183 }
184
185 pub fn channel(&self) -> WebGPU {
186 self.droppable.channel.clone()
187 }
188
189 fn global(&self) -> DomRoot<D::GlobalScope> {
190 <Self as DomGlobalGeneric<D>>::global_from_reflector(self)
191 }
192}
193
194impl<D> GPUAdapterMethods<D> for GPUAdapter<D>
195where
196 D: Equivalence,
197 D::Promise: WebGPUPromiseTrait<D> + PromiseHelpers<D>,
198 D::GlobalScope: WebGPUGlobalTrait + GlobalScopeHelpers<D>,
199{
200 fn RequestDevice(
202 &self,
203 cx: &mut CurrentRealm<'_>,
204 descriptor: &GPUDeviceDescriptor,
205 ) -> Rc<D::Promise> {
206 let promise = D::Promise::new_in_realm(cx);
208
209 let callback = WebGPUPromiseTrait::<D>::callback_promise_adapter(&promise, self);
210 let mut required_features = wgpu_types::Features::empty();
211 for &ext in descriptor.requiredFeatures.iter() {
212 if let Some(feature) = gpu_to_wgt_feature(ext) {
213 required_features.insert(feature);
214 } else {
215 promise.reject_error(
216 cx,
217 Error::Type(cformat!("{} is not supported feature", ext.as_str())),
218 );
219 return promise;
220 }
221 }
222
223 let mut required_limits = wgpu_types::Limits::default();
224 if let Some(limits) = &descriptor.requiredLimits {
225 for (limit, value) in (*limits).iter() {
226 if !set_limit(&mut required_limits, &limit.str(), *value) {
227 warn!("Unknown GPUDevice limit: {limit}");
228 promise.reject_error(
229 cx,
230 Error::Operation(Some(format!("Unknown GPUDevice limit: {limit}"))),
231 );
232 return promise;
233 }
234 }
235 }
236
237 let desc = wgpu_types::DeviceDescriptor {
238 required_features,
239 required_limits,
240 label: Some(descriptor.parent.label.to_string()),
241 memory_hints: MemoryHints::MemoryUsage,
242 trace: wgpu_types::Trace::Off,
243 experimental_features: ExperimentalFeatures::disabled(),
244 };
245 let device_id = self.global().global_wgpu_id_hub().create_device_id();
246 let queue_id = self.global().global_wgpu_id_hub().create_queue_id();
247 let pipeline_id = self.global().pipeline_id();
248 if self
249 .droppable
250 .channel
251 .0
252 .send(WebGPURequest::RequestDevice {
253 sender: callback,
254 adapter_id: self.droppable.adapter,
255 descriptor: desc,
256 device_id,
257 queue_id,
258 pipeline_id,
259 })
260 .is_err()
261 {
262 promise.reject_error(
263 cx,
264 Error::Operation(Some("Could not Request GPU Device".to_string())),
265 );
266 }
267 promise
269 }
270
271 fn Features(&self) -> DomRoot<GPUSupportedFeatures<D>> {
273 DomRoot::from_ref(&self.features)
274 }
275
276 fn Limits(&self) -> DomRoot<GPUSupportedLimits<D>> {
278 DomRoot::from_ref(&self.limits)
279 }
280
281 fn Info(&self) -> DomRoot<GPUAdapterInfo<D>> {
283 DomRoot::from_ref(&self.info)
284 }
285}