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