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