Skip to main content

script/dom/webgpu/
gputexture.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
5use std::string::String;
6
7use dom_struct::dom_struct;
8use js::context::{JSContext, NoGC};
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
11use webgpu_traits::{WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView};
12use wgpu_core::resource::{self, TextureDescriptor};
13
14use super::gpuconvert::convert_texture_descriptor;
15use crate::conversions::Convert;
16use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
17    GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
18    GPUTextureMethods, GPUTextureViewDescriptor,
19};
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
23use crate::dom::bindings::str::USVString;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::webgpu::gpudevice::GPUDevice;
26use crate::dom::webgpu::gputextureview::GPUTextureView;
27
28#[derive(JSTraceable, MallocSizeOf)]
29struct DroppableGPUTexture {
30    #[no_trace]
31    channel: WebGPU,
32    #[no_trace]
33    texture: WebGPUTexture,
34}
35
36impl Drop for DroppableGPUTexture {
37    fn drop(&mut self) {
38        if let Err(e) = self
39            .channel
40            .0
41            .send(WebGPURequest::DropTexture(self.texture.0))
42        {
43            warn!(
44                "Failed to send WebGPURequest::DropTexture({:?}) ({})",
45                self.texture.0, e
46            );
47        };
48    }
49}
50
51#[dom_struct]
52pub(crate) struct GPUTexture {
53    reflector_: Reflector,
54    label: DomRefCell<USVString>,
55    device: Dom<GPUDevice>,
56    #[no_trace]
57    #[ignore_malloc_size_of = "External type"]
58    texture_size: wgpu_types::Extent3d,
59    mip_level_count: u32,
60    sample_count: u32,
61    dimension: GPUTextureDimension,
62    format: GPUTextureFormat,
63    texture_usage: u32,
64    droppable: DroppableGPUTexture,
65    default_view: MutNullableDom<GPUTextureView>,
66}
67
68impl GPUTexture {
69    #[expect(clippy::too_many_arguments)]
70    fn new_inherited(
71        texture: WebGPUTexture,
72        device: &GPUDevice,
73        channel: WebGPU,
74        texture_size: wgpu_types::Extent3d,
75        mip_level_count: u32,
76        sample_count: u32,
77        dimension: GPUTextureDimension,
78        format: GPUTextureFormat,
79        texture_usage: u32,
80        label: USVString,
81    ) -> Self {
82        Self {
83            reflector_: Reflector::new(),
84            label: DomRefCell::new(label),
85            device: Dom::from_ref(device),
86            texture_size,
87            mip_level_count,
88            sample_count,
89            dimension,
90            format,
91            texture_usage,
92            droppable: DroppableGPUTexture { channel, texture },
93            default_view: MutNullableDom::new(None),
94        }
95    }
96
97    #[expect(clippy::too_many_arguments)]
98    pub(crate) fn new(
99        cx: &mut JSContext,
100        global: &GlobalScope,
101        texture: WebGPUTexture,
102        device: &GPUDevice,
103        channel: WebGPU,
104        texture_size: wgpu_types::Extent3d,
105        mip_level_count: u32,
106        sample_count: u32,
107        dimension: GPUTextureDimension,
108        format: GPUTextureFormat,
109        texture_usage: u32,
110        label: USVString,
111    ) -> DomRoot<Self> {
112        reflect_dom_object_with_cx(
113            Box::new(GPUTexture::new_inherited(
114                texture,
115                device,
116                channel,
117                texture_size,
118                mip_level_count,
119                sample_count,
120                dimension,
121                format,
122                texture_usage,
123                label,
124            )),
125            global,
126            cx,
127        )
128    }
129}
130
131impl GPUTexture {
132    pub(crate) fn id(&self) -> WebGPUTexture {
133        self.droppable.texture
134    }
135
136    pub(crate) fn wgpu_texture_descriptor(&self) -> TextureDescriptor<'static> {
137        TextureDescriptor {
138            label: Some(self.label.borrow().to_string().into()),
139            size: self.texture_size,
140            mip_level_count: self.mip_level_count,
141            sample_count: self.sample_count,
142            dimension: self.dimension.convert(),
143            format: self.format.convert(),
144            usage: wgpu_types::TextureUsages::from_bits_retain(self.texture_usage),
145            view_formats: vec![],
146        }
147    }
148
149    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
150    pub(crate) fn create(
151        cx: &mut JSContext,
152        device: &GPUDevice,
153        descriptor: &GPUTextureDescriptor,
154    ) -> Fallible<DomRoot<GPUTexture>> {
155        let (desc, size) = convert_texture_descriptor(descriptor, device)?;
156
157        let texture_id = device.global().wgpu_id_hub().create_texture_id();
158
159        device
160            .channel()
161            .0
162            .send(WebGPURequest::CreateTexture {
163                device_id: device.id().0,
164                texture_id,
165                descriptor: desc,
166            })
167            .expect("Failed to create WebGPU Texture");
168
169        let texture = WebGPUTexture(texture_id);
170
171        Ok(GPUTexture::new(
172            cx,
173            &device.global(),
174            texture,
175            device,
176            device.channel(),
177            size,
178            descriptor.mipLevelCount,
179            descriptor.sampleCount,
180            descriptor.dimension,
181            descriptor.format,
182            descriptor.usage,
183            descriptor.parent.label.clone(),
184        ))
185    }
186
187    pub(crate) fn get_default_view(&self, cx: &mut JSContext) -> WebGPUTextureView {
188        self.default_view
189            .or_init(|| {
190                self.CreateView(cx, &GPUTextureViewDescriptor::default())
191                    .expect("Default descriptor should always be valid.")
192            })
193            .id()
194    }
195}
196
197impl GPUTextureMethods<crate::DomTypeHolder> for GPUTexture {
198    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
199    fn Label(&self) -> USVString {
200        self.label.borrow().clone()
201    }
202
203    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
204    fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
205        *self.label.safe_borrow_mut(no_gc) = value;
206    }
207
208    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-createview>
209    fn CreateView(
210        &self,
211        cx: &mut JSContext,
212        descriptor: &GPUTextureViewDescriptor,
213    ) -> Fallible<DomRoot<GPUTextureView>> {
214        let desc = if !matches!(descriptor.mipLevelCount, Some(0)) &&
215            !matches!(descriptor.arrayLayerCount, Some(0))
216        {
217            Some(resource::TextureViewDescriptor {
218                label: (&descriptor.parent).convert(),
219                format: descriptor
220                    .format
221                    .map(|f| self.device.validate_texture_format_required_features(&f))
222                    .transpose()?,
223                dimension: descriptor.dimension.map(|dimension| dimension.convert()),
224                usage: Some(wgpu_types::TextureUsages::from_bits_retain(
225                    descriptor.usage,
226                )),
227                range: wgpu_types::ImageSubresourceRange {
228                    aspect: match descriptor.aspect {
229                        GPUTextureAspect::All => wgpu_types::TextureAspect::All,
230                        GPUTextureAspect::Stencil_only => wgpu_types::TextureAspect::StencilOnly,
231                        GPUTextureAspect::Depth_only => wgpu_types::TextureAspect::DepthOnly,
232                    },
233                    base_mip_level: descriptor.baseMipLevel,
234                    mip_level_count: descriptor.mipLevelCount,
235                    base_array_layer: descriptor.baseArrayLayer,
236                    array_layer_count: descriptor.arrayLayerCount,
237                },
238            })
239        } else {
240            self.device
241                .dispatch_error(webgpu_traits::Error::Validation(String::from(
242                    "arrayLayerCount and mipLevelCount cannot be 0",
243                )));
244            None
245        };
246
247        let texture_view_id = self.global().wgpu_id_hub().create_texture_view_id();
248
249        self.droppable
250            .channel
251            .0
252            .send(WebGPURequest::CreateTextureView {
253                texture_id: self.id().0,
254                texture_view_id,
255                device_id: self.device.id().0,
256                descriptor: desc,
257            })
258            .expect("Failed to create WebGPU texture view");
259
260        let texture_view = WebGPUTextureView(texture_view_id);
261
262        Ok(GPUTextureView::new(
263            cx,
264            &self.global(),
265            self.droppable.channel.clone(),
266            texture_view,
267            self,
268            descriptor.parent.label.clone(),
269        ))
270    }
271
272    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-destroy>
273    fn Destroy(&self) {
274        if let Err(e) = self
275            .droppable
276            .channel
277            .0
278            .send(WebGPURequest::DestroyTexture(self.id().0))
279        {
280            warn!(
281                "Failed to send WebGPURequest::DestroyTexture({:?}) ({})",
282                self.id().0,
283                e
284            );
285        };
286    }
287
288    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-width>
289    fn Width(&self) -> u32 {
290        self.texture_size.width
291    }
292
293    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-height>
294    fn Height(&self) -> u32 {
295        self.texture_size.height
296    }
297
298    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-depthorarraylayers>
299    fn DepthOrArrayLayers(&self) -> u32 {
300        self.texture_size.depth_or_array_layers
301    }
302
303    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-miplevelcount>
304    fn MipLevelCount(&self) -> u32 {
305        self.mip_level_count
306    }
307
308    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-samplecount>
309    fn SampleCount(&self) -> u32 {
310        self.sample_count
311    }
312
313    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-dimension>
314    fn Dimension(&self) -> GPUTextureDimension {
315        self.dimension
316    }
317
318    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-format>
319    fn Format(&self) -> GPUTextureFormat {
320        self.format
321    }
322
323    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-usage>
324    fn Usage(&self) -> u32 {
325        self.texture_usage
326    }
327}