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