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