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 script_bindings::cell::DomRefCell;
9use script_bindings::reflector::{Reflector, reflect_dom_object};
10use webgpu_traits::{WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView};
11use wgpu_core::resource;
12
13use super::gpuconvert::convert_texture_descriptor;
14use crate::conversions::Convert;
15use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
16    GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
17    GPUTextureMethods, GPUTextureViewDescriptor,
18};
19use crate::dom::bindings::error::Fallible;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
22use crate::dom::bindings::str::USVString;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::webgpu::gpudevice::GPUDevice;
25use crate::dom::webgpu::gputextureview::GPUTextureView;
26use crate::script_runtime::CanGc;
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        global: &GlobalScope,
100        texture: WebGPUTexture,
101        device: &GPUDevice,
102        channel: WebGPU,
103        texture_size: wgpu_types::Extent3d,
104        mip_level_count: u32,
105        sample_count: u32,
106        dimension: GPUTextureDimension,
107        format: GPUTextureFormat,
108        texture_usage: u32,
109        label: USVString,
110        can_gc: CanGc,
111    ) -> DomRoot<Self> {
112        reflect_dom_object(
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            can_gc,
127        )
128    }
129}
130
131impl GPUTexture {
132    pub(crate) fn id(&self) -> WebGPUTexture {
133        self.droppable.texture
134    }
135
136    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
137    pub(crate) fn create(
138        device: &GPUDevice,
139        descriptor: &GPUTextureDescriptor,
140        can_gc: CanGc,
141    ) -> Fallible<DomRoot<GPUTexture>> {
142        let (desc, size) = convert_texture_descriptor(descriptor, device)?;
143
144        let texture_id = device.global().wgpu_id_hub().create_texture_id();
145
146        device
147            .channel()
148            .0
149            .send(WebGPURequest::CreateTexture {
150                device_id: device.id().0,
151                texture_id,
152                descriptor: desc,
153            })
154            .expect("Failed to create WebGPU Texture");
155
156        let texture = WebGPUTexture(texture_id);
157
158        Ok(GPUTexture::new(
159            &device.global(),
160            texture,
161            device,
162            device.channel(),
163            size,
164            descriptor.mipLevelCount,
165            descriptor.sampleCount,
166            descriptor.dimension,
167            descriptor.format,
168            descriptor.usage,
169            descriptor.parent.label.clone(),
170            can_gc,
171        ))
172    }
173
174    pub(crate) fn get_default_view(&self) -> WebGPUTextureView {
175        self.default_view
176            .or_init(|| {
177                self.CreateView(&GPUTextureViewDescriptor::default())
178                    .expect("Default descriptor should always be valid.")
179            })
180            .id()
181    }
182}
183
184impl GPUTextureMethods<crate::DomTypeHolder> for GPUTexture {
185    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
186    fn Label(&self) -> USVString {
187        self.label.borrow().clone()
188    }
189
190    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
191    fn SetLabel(&self, value: USVString) {
192        *self.label.borrow_mut() = value;
193    }
194
195    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-createview>
196    fn CreateView(
197        &self,
198        descriptor: &GPUTextureViewDescriptor,
199    ) -> Fallible<DomRoot<GPUTextureView>> {
200        let desc = if !matches!(descriptor.mipLevelCount, Some(0)) &&
201            !matches!(descriptor.arrayLayerCount, Some(0))
202        {
203            Some(resource::TextureViewDescriptor {
204                label: (&descriptor.parent).convert(),
205                format: descriptor
206                    .format
207                    .map(|f| self.device.validate_texture_format_required_features(&f))
208                    .transpose()?,
209                dimension: descriptor.dimension.map(|dimension| dimension.convert()),
210                usage: Some(wgpu_types::TextureUsages::from_bits_retain(
211                    descriptor.usage,
212                )),
213                range: wgpu_types::ImageSubresourceRange {
214                    aspect: match descriptor.aspect {
215                        GPUTextureAspect::All => wgpu_types::TextureAspect::All,
216                        GPUTextureAspect::Stencil_only => wgpu_types::TextureAspect::StencilOnly,
217                        GPUTextureAspect::Depth_only => wgpu_types::TextureAspect::DepthOnly,
218                    },
219                    base_mip_level: descriptor.baseMipLevel,
220                    mip_level_count: descriptor.mipLevelCount,
221                    base_array_layer: descriptor.baseArrayLayer,
222                    array_layer_count: descriptor.arrayLayerCount,
223                },
224            })
225        } else {
226            self.device
227                .dispatch_error(webgpu_traits::Error::Validation(String::from(
228                    "arrayLayerCount and mipLevelCount cannot be 0",
229                )));
230            None
231        };
232
233        let texture_view_id = self.global().wgpu_id_hub().create_texture_view_id();
234
235        self.droppable
236            .channel
237            .0
238            .send(WebGPURequest::CreateTextureView {
239                texture_id: self.id().0,
240                texture_view_id,
241                device_id: self.device.id().0,
242                descriptor: desc,
243            })
244            .expect("Failed to create WebGPU texture view");
245
246        let texture_view = WebGPUTextureView(texture_view_id);
247
248        Ok(GPUTextureView::new(
249            &self.global(),
250            self.droppable.channel.clone(),
251            texture_view,
252            self,
253            descriptor.parent.label.clone(),
254            CanGc::deprecated_note(),
255        ))
256    }
257
258    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-destroy>
259    fn Destroy(&self) {
260        if let Err(e) = self
261            .droppable
262            .channel
263            .0
264            .send(WebGPURequest::DestroyTexture(self.id().0))
265        {
266            warn!(
267                "Failed to send WebGPURequest::DestroyTexture({:?}) ({})",
268                self.id().0,
269                e
270            );
271        };
272    }
273
274    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-width>
275    fn Width(&self) -> u32 {
276        self.texture_size.width
277    }
278
279    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-height>
280    fn Height(&self) -> u32 {
281        self.texture_size.height
282    }
283
284    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-depthorarraylayers>
285    fn DepthOrArrayLayers(&self) -> u32 {
286        self.texture_size.depth_or_array_layers
287    }
288
289    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-miplevelcount>
290    fn MipLevelCount(&self) -> u32 {
291        self.mip_level_count
292    }
293
294    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-samplecount>
295    fn SampleCount(&self) -> u32 {
296        self.sample_count
297    }
298
299    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-dimension>
300    fn Dimension(&self) -> GPUTextureDimension {
301        self.dimension
302    }
303
304    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-format>
305    fn Format(&self) -> GPUTextureFormat {
306        self.format
307    }
308
309    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-usage>
310    fn Usage(&self) -> u32 {
311        self.texture_usage
312    }
313}