Skip to main content

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