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