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::promise::{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: DomTypes<
69 GPUAdapter = GPUAdapter<D>,
70 GPUAdapterInfo = GPUAdapterInfo<D>,
71 GPUSupportedFeatures = GPUSupportedFeatures<D>,
72 GPUSupportedLimits = GPUSupportedLimits<D>,
73 >,
74{
75 fn new_inherited(
76 channel: WebGPU,
77 name: DOMString,
78 features: &GPUSupportedFeatures<D>,
79 limits: &GPUSupportedLimits<D>,
80 info: &GPUAdapterInfo<D>,
81 adapter: WebGPUAdapter,
82 ) -> Self {
83 Self {
84 reflector_: Reflector::new(),
85 name,
86 extensions: Heap::default(),
87 features: Dom::from_ref(features),
88 limits: Dom::from_ref(limits),
89 info: Dom::from_ref(info),
90 droppable: DroppableGPUAdapter { channel, adapter },
91 }
92 }
93
94 #[allow(clippy::too_many_arguments)]
95 pub fn new(
96 cx: &mut js::context::JSContext,
97 global: &D::GlobalScope,
98 channel: WebGPU,
99 name: DOMString,
100 extensions: HandleObject,
101 features: wgpu_types::Features,
102 limits: wgpu_types::Limits,
103 info: wgpu_types::AdapterInfo,
104 adapter: WebGPUAdapter,
105 ) -> DomRoot<Self> {
106 let features = GPUSupportedFeatures::Constructor(cx, global, None, features).unwrap();
107 let limits = GPUSupportedLimits::new(cx, global, limits);
108 let info = GPUAdapter::create_adapter_info(cx, global, info, &features);
109 let dom_root = reflect_dom_object_with_wrap::<D, _, _>(
110 Box::new(GPUAdapter::new_inherited(
111 channel, name, &features, &limits, &info, adapter,
112 )),
113 global,
114 cx,
115 GPUAdapterWrap::<D>,
116 );
117 dom_root.extensions.set(*extensions);
118 dom_root
119 }
120
121 fn create_adapter_info(
123 cx: &mut js::context::JSContext,
124 global: &D::GlobalScope,
125 info: AdapterInfo,
126 features: &GPUSupportedFeatures<D>,
127 ) -> DomRoot<GPUAdapterInfo<D>> {
128 let vendor = if info.vendor != 0 {
133 info.vendor.to_string().into()
134 } else {
135 DOMString::new()
136 };
137
138 let architecture = DOMString::new();
146
147 let device = if info.device != 0 {
152 info.device.to_string().into()
153 } else {
154 DOMString::new()
155 };
156
157 let description = info.name.clone().into();
162
163 let (subgroup_min_size, subgroup_max_size) = if features.has(cx, "subgroups".into()) {
168 (info.subgroup_min_size, info.subgroup_max_size)
169 } else {
170 (4, 128)
171 };
172
173 let is_fallback_adapter = info.device_type == wgpu_types::DeviceType::Cpu;
175
176 GPUAdapterInfo::new(
178 cx,
179 global,
180 vendor,
181 architecture,
182 device,
183 description,
184 subgroup_min_size,
185 subgroup_max_size,
186 is_fallback_adapter,
187 )
188 }
189
190 pub fn channel(&self) -> WebGPU {
191 self.droppable.channel.clone()
192 }
193
194 fn global(&self) -> DomRoot<D::GlobalScope> {
195 <Self as DomGlobalGeneric<D>>::global_from_reflector(self)
196 }
197}
198
199impl<D> GPUAdapterMethods<D> for GPUAdapter<D>
200where
201 D: DomTypes<
202 GPUAdapter = GPUAdapter<D>,
203 GPUAdapterInfo = GPUAdapterInfo<D>,
204 GPUSupportedFeatures = GPUSupportedFeatures<D>,
205 GPUSupportedLimits = GPUSupportedLimits<D>,
206 >,
207 D::Promise: WebGPUPromiseTrait<D> + PromiseHelpers<D>,
208 D::GlobalScope: WebGPUGlobalTrait + GlobalScopeHelpers<D>,
209{
210 fn RequestDevice(
212 &self,
213 cx: &mut CurrentRealm<'_>,
214 descriptor: &GPUDeviceDescriptor,
215 ) -> Rc<D::Promise> {
216 let promise = D::Promise::new_in_realm(cx);
218
219 let callback = WebGPUPromiseTrait::<D>::callback_promise(&promise, self);
220 let mut required_features = wgpu_types::Features::empty();
221 for &ext in descriptor.requiredFeatures.iter() {
222 if let Some(feature) = gpu_to_wgt_feature(ext) {
223 required_features.insert(feature);
224 } else {
225 promise.reject_error(
226 cx,
227 Error::Type(cformat!("{} is not supported feature", ext.as_str())),
228 );
229 return promise;
230 }
231 }
232
233 let mut required_limits = wgpu_types::Limits::default();
234 if let Some(limits) = &descriptor.requiredLimits {
235 for (limit, value) in (*limits).iter() {
236 if !set_limit(&mut required_limits, &limit.str(), *value) {
237 warn!("Unknown GPUDevice limit: {limit}");
238 promise.reject_error(
239 cx,
240 Error::Operation(Some(format!("Unknown GPUDevice limit: {limit}"))),
241 );
242 return promise;
243 }
244 }
245 }
246
247 let desc = wgpu_types::DeviceDescriptor {
248 required_features,
249 required_limits,
250 label: Some(descriptor.parent.label.to_string()),
251 memory_hints: MemoryHints::MemoryUsage,
252 trace: wgpu_types::Trace::Off,
253 experimental_features: ExperimentalFeatures::disabled(),
254 };
255 let device_id = self.global().global_wgpu_id_hub().create_device_id();
256 let queue_id = self.global().global_wgpu_id_hub().create_queue_id();
257 let pipeline_id = self.global().pipeline_id();
258 if self
259 .droppable
260 .channel
261 .0
262 .send(WebGPURequest::RequestDevice {
263 sender: callback,
264 adapter_id: self.droppable.adapter,
265 descriptor: desc,
266 device_id,
267 queue_id,
268 pipeline_id,
269 })
270 .is_err()
271 {
272 promise.reject_error(
273 cx,
274 Error::Operation(Some("Could not Request GPU Device".to_string())),
275 );
276 }
277 promise
279 }
280
281 fn Features(&self) -> DomRoot<GPUSupportedFeatures<D>> {
283 DomRoot::from_ref(&self.features)
284 }
285
286 fn Limits(&self) -> DomRoot<GPUSupportedLimits<D>> {
288 DomRoot::from_ref(&self.limits)
289 }
290
291 fn Info(&self) -> DomRoot<GPUAdapterInfo<D>> {
293 DomRoot::from_ref(&self.info)
294 }
295}