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