Skip to main content

script_webgpu/
gpusupportedfeatures.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5// check-tidy: no specs after this line
6
7use std::marker::PhantomData;
8
9use dom_struct::dom_struct;
10use indexmap::IndexSet;
11use js::context::JSContext;
12use js::rust::HandleObject;
13use malloc_size_of_derive::MallocSizeOf;
14use script_bindings::DomTypes;
15use script_bindings::cell::DomRefCell;
16use script_bindings::codegen::GenericBindings::WebGPUBinding::{
17    GPUFeatureName, GPUSupportedFeaturesMethods, GPUSupportedFeaturesWrap,
18};
19use script_bindings::like::Setlike;
20use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_wrap};
21use wgpu_types::Features;
22
23use crate::JSTraceable;
24use crate::dom::bindings::error::Fallible;
25use crate::dom::bindings::root::DomRoot;
26use crate::dom::bindings::str::DOMString;
27
28#[dom_struct]
29pub struct GPUSupportedFeatures<D: DomTypes> {
30    reflector: Reflector,
31    // internal storage for features
32    #[custom_trace]
33    internal: DomRefCell<IndexSet<GPUFeatureName>>,
34    #[ignore_malloc_size_of = "defined in wgpu-types"]
35    #[no_trace]
36    features: Features,
37    #[no_trace = "PhantomData does not exist"]
38    phantom: PhantomData<D>,
39}
40
41impl<D> GPUSupportedFeatures<D>
42where
43    D: DomTypes<GPUSupportedFeatures = GPUSupportedFeatures<D>>,
44{
45    fn new(
46        cx: &mut JSContext,
47        global: &D::GlobalScope,
48        proto: Option<HandleObject>,
49        features: Features,
50    ) -> DomRoot<GPUSupportedFeatures<D>> {
51        let mut set = IndexSet::new();
52        // everything that wgpu currently does is considered as part of "core"
53        set.insert(GPUFeatureName::Core_features_and_limits);
54        if features.contains(Features::DEPTH_CLIP_CONTROL) {
55            set.insert(GPUFeatureName::Depth_clip_control);
56        }
57        if features.contains(Features::DEPTH32FLOAT_STENCIL8) {
58            set.insert(GPUFeatureName::Depth32float_stencil8);
59        }
60        if features.contains(Features::TEXTURE_COMPRESSION_BC) {
61            set.insert(GPUFeatureName::Texture_compression_bc);
62        }
63        // TODO: texture-compression-bc-sliced-3d when wgpu supports it
64        if features.contains(Features::TEXTURE_COMPRESSION_ETC2) {
65            set.insert(GPUFeatureName::Texture_compression_etc2);
66        }
67        if features.contains(Features::TEXTURE_COMPRESSION_ASTC) {
68            set.insert(GPUFeatureName::Texture_compression_astc);
69        }
70        if features.contains(Features::TIMESTAMP_QUERY) {
71            set.insert(GPUFeatureName::Timestamp_query);
72        }
73        if features.contains(Features::INDIRECT_FIRST_INSTANCE) {
74            set.insert(GPUFeatureName::Indirect_first_instance);
75        }
76        // While this feature exists in wgpu, it's not supported by naga yet
77        // https://github.com/gfx-rs/wgpu/issues/4384
78        /*
79        if features.contains(Features::SHADER_F16) {
80            set.insert(GPUFeatureName::Shader_f16);
81        }
82        */
83        if features.contains(Features::RG11B10UFLOAT_RENDERABLE) {
84            set.insert(GPUFeatureName::Rg11b10ufloat_renderable);
85        }
86        if features.contains(Features::BGRA8UNORM_STORAGE) {
87            set.insert(GPUFeatureName::Bgra8unorm_storage);
88        }
89        if features.contains(Features::FLOAT32_FILTERABLE) {
90            set.insert(GPUFeatureName::Float32_filterable);
91        }
92        // TODO: clip-distances when wgpu supports it
93        if features.contains(Features::DUAL_SOURCE_BLENDING) {
94            set.insert(GPUFeatureName::Dual_source_blending);
95        }
96        // While this feature exists in wgpu, it's not supported by naga yet
97        // https://github.com/gfx-rs/wgpu/issues/5555
98        /*
99        if features.contains(Features::SUBGROUP) {
100            set.insert(GPUFeatureName::Subgroups);
101        }
102        */
103
104        reflect_dom_object_with_proto_and_wrap::<D, _, _>(
105            Box::new(GPUSupportedFeatures {
106                reflector: Reflector::new(),
107                internal: DomRefCell::new(set),
108                features,
109                phantom: PhantomData,
110            }),
111            global,
112            proto,
113            cx,
114            GPUSupportedFeaturesWrap::<D>,
115        )
116    }
117
118    #[expect(non_snake_case)]
119    pub fn Constructor(
120        cx: &mut JSContext,
121        global: &D::GlobalScope,
122        proto: Option<HandleObject>,
123        features: Features,
124    ) -> Fallible<DomRoot<GPUSupportedFeatures<D>>> {
125        Ok(GPUSupportedFeatures::new(cx, global, proto, features))
126    }
127}
128
129impl<D: DomTypes> GPUSupportedFeatures<D> {
130    pub fn wgpu_features(&self) -> &Features {
131        &self.features
132    }
133}
134
135impl<D: DomTypes> GPUSupportedFeaturesMethods<D> for GPUSupportedFeatures<D> {
136    fn Size(&self) -> u32 {
137        self.internal.borrow().len() as u32
138    }
139}
140
141pub(crate) fn gpu_to_wgt_feature(feature: GPUFeatureName) -> Option<Features> {
142    match feature {
143        // everything that wgpu currently does is considered as part of "core"
144        GPUFeatureName::Core_features_and_limits => Some(Features::empty()),
145        GPUFeatureName::Depth_clip_control => Some(Features::DEPTH_CLIP_CONTROL),
146        GPUFeatureName::Depth32float_stencil8 => Some(Features::DEPTH32FLOAT_STENCIL8),
147        GPUFeatureName::Texture_compression_bc => Some(Features::TEXTURE_COMPRESSION_BC),
148        GPUFeatureName::Texture_compression_etc2 => Some(Features::TEXTURE_COMPRESSION_ETC2),
149        GPUFeatureName::Texture_compression_astc => Some(Features::TEXTURE_COMPRESSION_ASTC),
150        GPUFeatureName::Timestamp_query => Some(Features::TIMESTAMP_QUERY),
151        GPUFeatureName::Indirect_first_instance => Some(Features::INDIRECT_FIRST_INSTANCE),
152        // While this feature exists in wgpu, it's not supported by naga yet
153        // https://github.com/gfx-rs/wgpu/issues/4384
154        GPUFeatureName::Shader_f16 => None,
155        GPUFeatureName::Rg11b10ufloat_renderable => Some(Features::RG11B10UFLOAT_RENDERABLE),
156        GPUFeatureName::Bgra8unorm_storage => Some(Features::BGRA8UNORM_STORAGE),
157        GPUFeatureName::Float32_filterable => Some(Features::FLOAT32_FILTERABLE),
158        GPUFeatureName::Dual_source_blending => Some(Features::DUAL_SOURCE_BLENDING),
159        GPUFeatureName::Texture_compression_bc_sliced_3d => None,
160        GPUFeatureName::Clip_distances => None,
161        // While this feature exists in wgpu, it's not supported by naga yet
162        // https://github.com/gfx-rs/wgpu/issues/5555
163        GPUFeatureName::Subgroups => None,
164    }
165}
166
167impl<D: DomTypes> Setlike for GPUSupportedFeatures<D> {
168    type Key = DOMString;
169
170    #[inline(always)]
171    fn get_index(&self, cx: &mut JSContext, index: u32) -> Option<Self::Key> {
172        self.internal
173            .get_index(cx, index)
174            .map(|key| key.as_str().into())
175    }
176    #[inline(always)]
177    fn size(&self, cx: &mut JSContext) -> u32 {
178        self.internal.size(cx)
179    }
180    #[inline(always)]
181    fn add(&self, _cx: &mut JSContext, _key: Self::Key) {
182        unreachable!("readonly");
183    }
184    #[inline(always)]
185    fn has(&self, cx: &mut JSContext, key: Self::Key) -> bool {
186        if let Ok(key) = key.parse() {
187            self.internal.has(cx, key)
188        } else {
189            false
190        }
191    }
192    #[inline(always)]
193    fn clear(&self, _cx: &mut JSContext) {
194        unreachable!("readonly");
195    }
196    #[inline(always)]
197    fn delete(&self, _cx: &mut JSContext, _key: Self::Key) -> bool {
198        unreachable!("readonly");
199    }
200}