1use 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 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 fn Label(&self) -> USVString {
217 self.label.borrow().clone()
218 }
219
220 fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
222 *self.label.safe_borrow_mut(no_gc) = value;
223 }
224
225 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 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 fn Width(&self) -> u32 {
310 self.texture_size.width
311 }
312
313 fn Height(&self) -> u32 {
315 self.texture_size.height
316 }
317
318 fn DepthOrArrayLayers(&self) -> u32 {
320 self.texture_size.depth_or_array_layers
321 }
322
323 fn MipLevelCount(&self) -> u32 {
325 self.mip_level_count
326 }
327
328 fn SampleCount(&self) -> u32 {
330 self.sample_count
331 }
332
333 fn Dimension(&self) -> GPUTextureDimension {
335 self.dimension
336 }
337
338 fn Format(&self) -> GPUTextureFormat {
340 self.format
341 }
342
343 fn Usage(&self) -> u32 {
345 self.texture_usage
346 }
347}