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