1use std::string::String;
6
7use dom_struct::dom_struct;
8use js::context::{JSContext, NoGC};
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
11use webgpu_traits::{WebGPU, WebGPURequest, WebGPUTexture, WebGPUTextureView};
12use wgpu_core::resource::{self, TextureDescriptor};
13
14use super::gpuconvert::convert_texture_descriptor;
15use crate::conversions::Convert;
16use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
17 GPUTextureAspect, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat,
18 GPUTextureMethods, GPUTextureViewDescriptor,
19};
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
23use crate::dom::bindings::str::USVString;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::webgpu::gpudevice::GPUDevice;
26use crate::dom::webgpu::gputextureview::GPUTextureView;
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 cx: &mut JSContext,
100 global: &GlobalScope,
101 texture: WebGPUTexture,
102 device: &GPUDevice,
103 channel: WebGPU,
104 texture_size: wgpu_types::Extent3d,
105 mip_level_count: u32,
106 sample_count: u32,
107 dimension: GPUTextureDimension,
108 format: GPUTextureFormat,
109 texture_usage: u32,
110 label: USVString,
111 ) -> DomRoot<Self> {
112 reflect_dom_object_with_cx(
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 cx,
127 )
128 }
129}
130
131impl GPUTexture {
132 pub(crate) fn id(&self) -> WebGPUTexture {
133 self.droppable.texture
134 }
135
136 pub(crate) fn wgpu_texture_descriptor(&self) -> TextureDescriptor<'static> {
137 TextureDescriptor {
138 label: Some(self.label.borrow().to_string().into()),
139 size: self.texture_size,
140 mip_level_count: self.mip_level_count,
141 sample_count: self.sample_count,
142 dimension: self.dimension.convert(),
143 format: self.format.convert(),
144 usage: wgpu_types::TextureUsages::from_bits_retain(self.texture_usage),
145 view_formats: vec![],
146 }
147 }
148
149 pub(crate) fn create(
151 cx: &mut JSContext,
152 device: &GPUDevice,
153 descriptor: &GPUTextureDescriptor,
154 ) -> Fallible<DomRoot<GPUTexture>> {
155 let (desc, size) = convert_texture_descriptor(descriptor, device)?;
156
157 let texture_id = device.global().wgpu_id_hub().create_texture_id();
158
159 device
160 .channel()
161 .0
162 .send(WebGPURequest::CreateTexture {
163 device_id: device.id().0,
164 texture_id,
165 descriptor: desc,
166 })
167 .expect("Failed to create WebGPU Texture");
168
169 let texture = WebGPUTexture(texture_id);
170
171 Ok(GPUTexture::new(
172 cx,
173 &device.global(),
174 texture,
175 device,
176 device.channel(),
177 size,
178 descriptor.mipLevelCount,
179 descriptor.sampleCount,
180 descriptor.dimension,
181 descriptor.format,
182 descriptor.usage,
183 descriptor.parent.label.clone(),
184 ))
185 }
186
187 pub(crate) fn get_default_view(&self, cx: &mut JSContext) -> WebGPUTextureView {
188 self.default_view
189 .or_init(|| {
190 self.CreateView(cx, &GPUTextureViewDescriptor::default())
191 .expect("Default descriptor should always be valid.")
192 })
193 .id()
194 }
195}
196
197impl GPUTextureMethods<crate::DomTypeHolder> for GPUTexture {
198 fn Label(&self) -> USVString {
200 self.label.borrow().clone()
201 }
202
203 fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
205 *self.label.safe_borrow_mut(no_gc) = value;
206 }
207
208 fn CreateView(
210 &self,
211 cx: &mut JSContext,
212 descriptor: &GPUTextureViewDescriptor,
213 ) -> Fallible<DomRoot<GPUTextureView>> {
214 let desc = if !matches!(descriptor.mipLevelCount, Some(0)) &&
215 !matches!(descriptor.arrayLayerCount, Some(0))
216 {
217 Some(resource::TextureViewDescriptor {
218 label: (&descriptor.parent).convert(),
219 format: descriptor
220 .format
221 .map(|f| self.device.validate_texture_format_required_features(&f))
222 .transpose()?,
223 dimension: descriptor.dimension.map(|dimension| dimension.convert()),
224 usage: Some(wgpu_types::TextureUsages::from_bits_retain(
225 descriptor.usage,
226 )),
227 range: wgpu_types::ImageSubresourceRange {
228 aspect: match descriptor.aspect {
229 GPUTextureAspect::All => wgpu_types::TextureAspect::All,
230 GPUTextureAspect::Stencil_only => wgpu_types::TextureAspect::StencilOnly,
231 GPUTextureAspect::Depth_only => wgpu_types::TextureAspect::DepthOnly,
232 },
233 base_mip_level: descriptor.baseMipLevel,
234 mip_level_count: descriptor.mipLevelCount,
235 base_array_layer: descriptor.baseArrayLayer,
236 array_layer_count: descriptor.arrayLayerCount,
237 },
238 })
239 } else {
240 self.device
241 .dispatch_error(webgpu_traits::Error::Validation(String::from(
242 "arrayLayerCount and mipLevelCount cannot be 0",
243 )));
244 None
245 };
246
247 let texture_view_id = self.global().wgpu_id_hub().create_texture_view_id();
248
249 self.droppable
250 .channel
251 .0
252 .send(WebGPURequest::CreateTextureView {
253 texture_id: self.id().0,
254 texture_view_id,
255 device_id: self.device.id().0,
256 descriptor: desc,
257 })
258 .expect("Failed to create WebGPU texture view");
259
260 let texture_view = WebGPUTextureView(texture_view_id);
261
262 Ok(GPUTextureView::new(
263 cx,
264 &self.global(),
265 self.droppable.channel.clone(),
266 texture_view,
267 self,
268 descriptor.parent.label.clone(),
269 ))
270 }
271
272 fn Destroy(&self) {
274 if let Err(e) = self
275 .droppable
276 .channel
277 .0
278 .send(WebGPURequest::DestroyTexture(self.id().0))
279 {
280 warn!(
281 "Failed to send WebGPURequest::DestroyTexture({:?}) ({})",
282 self.id().0,
283 e
284 );
285 };
286 }
287
288 fn Width(&self) -> u32 {
290 self.texture_size.width
291 }
292
293 fn Height(&self) -> u32 {
295 self.texture_size.height
296 }
297
298 fn DepthOrArrayLayers(&self) -> u32 {
300 self.texture_size.depth_or_array_layers
301 }
302
303 fn MipLevelCount(&self) -> u32 {
305 self.mip_level_count
306 }
307
308 fn SampleCount(&self) -> u32 {
310 self.sample_count
311 }
312
313 fn Dimension(&self) -> GPUTextureDimension {
315 self.dimension
316 }
317
318 fn Format(&self) -> GPUTextureFormat {
320 self.format
321 }
322
323 fn Usage(&self) -> u32 {
325 self.texture_usage
326 }
327}