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};
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}
66
67impl GPUTexture {
68    #[expect(clippy::too_many_arguments)]
69    fn new_inherited(
70        texture: WebGPUTexture,
71        device: &GPUDevice,
72        channel: WebGPU,
73        texture_size: wgpu_types::Extent3d,
74        mip_level_count: u32,
75        sample_count: u32,
76        dimension: GPUTextureDimension,
77        format: GPUTextureFormat,
78        texture_usage: u32,
79        label: USVString,
80    ) -> Self {
81        Self {
82            reflector_: Reflector::new(),
83            label: DomRefCell::new(label),
84            device: Dom::from_ref(device),
85            texture_size,
86            mip_level_count,
87            sample_count,
88            dimension,
89            format,
90            texture_usage,
91            droppable: DroppableGPUTexture { channel, texture },
92        }
93    }
94
95    #[expect(clippy::too_many_arguments)]
96    pub(crate) fn new(
97        global: &GlobalScope,
98        texture: WebGPUTexture,
99        device: &GPUDevice,
100        channel: WebGPU,
101        texture_size: wgpu_types::Extent3d,
102        mip_level_count: u32,
103        sample_count: u32,
104        dimension: GPUTextureDimension,
105        format: GPUTextureFormat,
106        texture_usage: u32,
107        label: USVString,
108        can_gc: CanGc,
109    ) -> DomRoot<Self> {
110        reflect_dom_object(
111            Box::new(GPUTexture::new_inherited(
112                texture,
113                device,
114                channel,
115                texture_size,
116                mip_level_count,
117                sample_count,
118                dimension,
119                format,
120                texture_usage,
121                label,
122            )),
123            global,
124            can_gc,
125        )
126    }
127}
128
129impl GPUTexture {
130    pub(crate) fn id(&self) -> WebGPUTexture {
131        self.droppable.texture
132    }
133
134    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
135    pub(crate) fn create(
136        device: &GPUDevice,
137        descriptor: &GPUTextureDescriptor,
138        can_gc: CanGc,
139    ) -> Fallible<DomRoot<GPUTexture>> {
140        let (desc, size) = convert_texture_descriptor(descriptor, device)?;
141
142        let texture_id = device.global().wgpu_id_hub().create_texture_id();
143
144        device
145            .channel()
146            .0
147            .send(WebGPURequest::CreateTexture {
148                device_id: device.id().0,
149                texture_id,
150                descriptor: desc,
151            })
152            .expect("Failed to create WebGPU Texture");
153
154        let texture = WebGPUTexture(texture_id);
155
156        Ok(GPUTexture::new(
157            &device.global(),
158            texture,
159            device,
160            device.channel(),
161            size,
162            descriptor.mipLevelCount,
163            descriptor.sampleCount,
164            descriptor.dimension,
165            descriptor.format,
166            descriptor.usage,
167            descriptor.parent.label.clone(),
168            can_gc,
169        ))
170    }
171}
172
173impl GPUTextureMethods<crate::DomTypeHolder> for GPUTexture {
174    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
175    fn Label(&self) -> USVString {
176        self.label.borrow().clone()
177    }
178
179    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
180    fn SetLabel(&self, value: USVString) {
181        *self.label.borrow_mut() = value;
182    }
183
184    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-createview>
185    fn CreateView(
186        &self,
187        descriptor: &GPUTextureViewDescriptor,
188    ) -> Fallible<DomRoot<GPUTextureView>> {
189        let desc = if !matches!(descriptor.mipLevelCount, Some(0)) &&
190            !matches!(descriptor.arrayLayerCount, Some(0))
191        {
192            Some(resource::TextureViewDescriptor {
193                label: (&descriptor.parent).convert(),
194                format: descriptor
195                    .format
196                    .map(|f| self.device.validate_texture_format_required_features(&f))
197                    .transpose()?,
198                dimension: descriptor.dimension.map(|dimension| dimension.convert()),
199                usage: Some(wgpu_types::TextureUsages::from_bits_retain(
200                    descriptor.usage,
201                )),
202                range: wgpu_types::ImageSubresourceRange {
203                    aspect: match descriptor.aspect {
204                        GPUTextureAspect::All => wgpu_types::TextureAspect::All,
205                        GPUTextureAspect::Stencil_only => wgpu_types::TextureAspect::StencilOnly,
206                        GPUTextureAspect::Depth_only => wgpu_types::TextureAspect::DepthOnly,
207                    },
208                    base_mip_level: descriptor.baseMipLevel,
209                    mip_level_count: descriptor.mipLevelCount,
210                    base_array_layer: descriptor.baseArrayLayer,
211                    array_layer_count: descriptor.arrayLayerCount,
212                },
213            })
214        } else {
215            self.device
216                .dispatch_error(webgpu_traits::Error::Validation(String::from(
217                    "arrayLayerCount and mipLevelCount cannot be 0",
218                )));
219            None
220        };
221
222        let texture_view_id = self.global().wgpu_id_hub().create_texture_view_id();
223
224        self.droppable
225            .channel
226            .0
227            .send(WebGPURequest::CreateTextureView {
228                texture_id: self.id().0,
229                texture_view_id,
230                device_id: self.device.id().0,
231                descriptor: desc,
232            })
233            .expect("Failed to create WebGPU texture view");
234
235        let texture_view = WebGPUTextureView(texture_view_id);
236
237        Ok(GPUTextureView::new(
238            &self.global(),
239            self.droppable.channel.clone(),
240            texture_view,
241            self,
242            descriptor.parent.label.clone(),
243            CanGc::deprecated_note(),
244        ))
245    }
246
247    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-destroy>
248    fn Destroy(&self) {
249        if let Err(e) = self
250            .droppable
251            .channel
252            .0
253            .send(WebGPURequest::DestroyTexture(self.id().0))
254        {
255            warn!(
256                "Failed to send WebGPURequest::DestroyTexture({:?}) ({})",
257                self.id().0,
258                e
259            );
260        };
261    }
262
263    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-width>
264    fn Width(&self) -> u32 {
265        self.texture_size.width
266    }
267
268    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-height>
269    fn Height(&self) -> u32 {
270        self.texture_size.height
271    }
272
273    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-depthorarraylayers>
274    fn DepthOrArrayLayers(&self) -> u32 {
275        self.texture_size.depth_or_array_layers
276    }
277
278    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-miplevelcount>
279    fn MipLevelCount(&self) -> u32 {
280        self.mip_level_count
281    }
282
283    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-samplecount>
284    fn SampleCount(&self) -> u32 {
285        self.sample_count
286    }
287
288    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-dimension>
289    fn Dimension(&self) -> GPUTextureDimension {
290        self.dimension
291    }
292
293    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-format>
294    fn Format(&self) -> GPUTextureFormat {
295        self.format
296    }
297
298    /// <https://gpuweb.github.io/gpuweb/#dom-gputexture-usage>
299    fn Usage(&self) -> u32 {
300        self.texture_usage
301    }
302}