1use dom_struct::dom_struct;
6use js::context::{JSContext, NoGC};
7use script_bindings::cell::DomRefCell;
8use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
9use webgpu_traits::{
10 WebGPU, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePass, WebGPUDevice,
11 WebGPURenderPass, WebGPURequest,
12};
13use wgpu_core::command as wgpu_com;
14
15use crate::conversions::{Convert, TryConvert};
16use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
17 GPUCommandBufferDescriptor, GPUCommandEncoderDescriptor, GPUCommandEncoderMethods,
18 GPUComputePassDescriptor, GPUExtent3D, GPURenderPassDescriptor, GPUSize64,
19 GPUTexelCopyBufferInfo, GPUTexelCopyTextureInfo,
20};
21use crate::dom::bindings::error::Fallible;
22use crate::dom::bindings::reflector::DomGlobal;
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::bindings::str::USVString;
25use crate::dom::globalscope::GlobalScope;
26use crate::dom::gpuconvert::{convert_load_op, convert_texture_for_wgpu_with_cx};
27use crate::dom::types::GPUQuerySet;
28use crate::dom::webgpu::gpubuffer::GPUBuffer;
29use crate::dom::webgpu::gpucommandbuffer::GPUCommandBuffer;
30use crate::dom::webgpu::gpucomputepassencoder::GPUComputePassEncoder;
31use crate::dom::webgpu::gpudevice::GPUDevice;
32use crate::dom::webgpu::gpurenderpassencoder::GPURenderPassEncoder;
33
34#[derive(JSTraceable, MallocSizeOf)]
35struct DroppableGPUCommandEncoder {
36 #[no_trace]
37 channel: WebGPU,
38 #[no_trace]
39 encoder: WebGPUCommandEncoder,
40}
41
42#[dom_struct]
43pub(crate) struct GPUCommandEncoder {
44 reflector_: Reflector,
45 droppable: DroppableGPUCommandEncoder,
46 label: DomRefCell<USVString>,
47 device: Dom<GPUDevice>,
48}
49
50impl Drop for DroppableGPUCommandEncoder {
51 fn drop(&mut self) {
52 if let Err(e) = self
53 .channel
54 .0
55 .send(WebGPURequest::DropCommandEncoder(self.encoder.0))
56 {
57 warn!("Failed to send WebGPURequest::DropCommandEncoder with {e:?}");
58 }
59 }
60}
61
62impl GPUCommandEncoder {
63 pub(crate) fn new_inherited(
64 channel: WebGPU,
65 device: &GPUDevice,
66 encoder: WebGPUCommandEncoder,
67 label: USVString,
68 ) -> Self {
69 Self {
70 droppable: DroppableGPUCommandEncoder { channel, encoder },
71 reflector_: Reflector::new(),
72 label: DomRefCell::new(label),
73 device: Dom::from_ref(device),
74 }
75 }
76
77 pub(crate) fn new(
78 cx: &mut JSContext,
79 global: &GlobalScope,
80 channel: WebGPU,
81 device: &GPUDevice,
82 encoder: WebGPUCommandEncoder,
83 label: USVString,
84 ) -> DomRoot<Self> {
85 reflect_dom_object_with_cx(
86 Box::new(GPUCommandEncoder::new_inherited(
87 channel, device, encoder, label,
88 )),
89 global,
90 cx,
91 )
92 }
93}
94
95impl GPUCommandEncoder {
96 pub(crate) fn id(&self) -> WebGPUCommandEncoder {
97 self.droppable.encoder
98 }
99
100 pub(crate) fn device_id(&self) -> WebGPUDevice {
101 self.device.id()
102 }
103
104 pub(crate) fn create(
106 cx: &mut JSContext,
107 device: &GPUDevice,
108 descriptor: &GPUCommandEncoderDescriptor,
109 ) -> DomRoot<GPUCommandEncoder> {
110 let command_encoder_id = device.global().wgpu_id_hub().create_command_encoder_id();
111 device
112 .channel()
113 .0
114 .send(WebGPURequest::CreateCommandEncoder {
115 device_id: device.id().0,
116 command_encoder_id,
117 desc: wgpu_types::CommandEncoderDescriptor {
118 label: (&descriptor.parent).convert(),
119 },
120 })
121 .expect("Failed to create WebGPU command encoder");
122
123 let encoder = WebGPUCommandEncoder(command_encoder_id);
124
125 GPUCommandEncoder::new(
126 cx,
127 &device.global(),
128 device.channel(),
129 device,
130 encoder,
131 descriptor.parent.label.clone(),
132 )
133 }
134}
135
136impl GPUCommandEncoderMethods<crate::DomTypeHolder> for GPUCommandEncoder {
137 fn Label(&self) -> USVString {
139 self.label.borrow().clone()
140 }
141
142 fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
144 *self.label.safe_borrow_mut(no_gc) = value;
145 }
146
147 fn BeginComputePass(
149 &self,
150 cx: &mut JSContext,
151 descriptor: &GPUComputePassDescriptor,
152 ) -> DomRoot<GPUComputePassEncoder> {
153 let compute_pass_id = self.global().wgpu_id_hub().create_compute_pass_id();
154
155 if let Err(error) = self
156 .droppable
157 .channel
158 .0
159 .send(WebGPURequest::BeginComputePass {
160 command_encoder_id: self.id().0,
161 compute_pass_id,
162 label: (&descriptor.parent).convert(),
163 timestamp_writes: descriptor.timestampWrites.as_ref().map(Convert::convert),
164 device_id: self.device.id().0,
165 })
166 {
167 warn!("Failed to send WebGPURequest::BeginComputePass {error:?}");
168 }
169
170 GPUComputePassEncoder::new(
171 cx,
172 &self.global(),
173 self.droppable.channel.clone(),
174 self,
175 WebGPUComputePass(compute_pass_id),
176 descriptor.parent.label.clone(),
177 )
178 }
179
180 fn BeginRenderPass(
182 &self,
183 cx: &mut JSContext,
184 descriptor: &GPURenderPassDescriptor,
185 ) -> Fallible<DomRoot<GPURenderPassEncoder>> {
186 let depth_stencil_attachment = descriptor.depthStencilAttachment.as_ref().map(|ds| {
187 wgpu_com::RenderPassDepthStencilAttachment {
188 depth: wgpu_com::PassChannel {
189 load_op: ds
190 .depthLoadOp
191 .as_ref()
192 .map(|l| convert_load_op(l, ds.depthClearValue.map(|v| *v))),
193 store_op: ds.depthStoreOp.as_ref().map(Convert::convert),
194 read_only: ds.depthReadOnly,
195 },
196 stencil: wgpu_com::PassChannel {
197 load_op: ds
198 .stencilLoadOp
199 .as_ref()
200 .map(|l| convert_load_op(l, Some(ds.stencilClearValue))),
201 store_op: ds.stencilStoreOp.as_ref().map(Convert::convert),
202 read_only: ds.stencilReadOnly,
203 },
204 view: convert_texture_for_wgpu_with_cx(cx, &ds.view).0,
205 }
206 });
207
208 let color_attachments = descriptor
209 .colorAttachments
210 .iter()
211 .map(|color| -> Fallible<_> {
212 Ok(Some(wgpu_com::RenderPassColorAttachment {
213 resolve_target: color
214 .resolveTarget
215 .as_ref()
216 .map(|t| convert_texture_for_wgpu_with_cx(cx, t).0),
217 load_op: convert_load_op(
218 &color.loadOp,
219 color
220 .clearValue
221 .as_ref()
222 .map(|color| (color).try_convert())
223 .transpose()?
224 .unwrap_or_default(),
225 ),
226 store_op: color.storeOp.convert(),
227 view: convert_texture_for_wgpu_with_cx(cx, &color.view).0,
228 depth_slice: None,
229 }))
230 })
231 .collect::<Fallible<Vec<_>>>()?;
232 let render_pass_id = self.global().wgpu_id_hub().create_render_pass_id();
233
234 if let Err(error) = self
235 .droppable
236 .channel
237 .0
238 .send(WebGPURequest::BeginRenderPass {
239 command_encoder_id: self.id().0,
240 render_pass_id,
241 label: (&descriptor.parent).convert(),
242 depth_stencil_attachment,
243 color_attachments,
244 timestamp_writes: descriptor.timestampWrites.as_ref().map(Convert::convert),
245 device_id: self.device.id().0,
246 })
247 {
248 warn!("Failed to send WebGPURequest::BeginRenderPass {error:?}");
249 }
250
251 Ok(GPURenderPassEncoder::new(
252 cx,
253 &self.global(),
254 self.droppable.channel.clone(),
255 WebGPURenderPass(render_pass_id),
256 self,
257 descriptor.parent.label.clone(),
258 ))
259 }
260
261 fn CopyBufferToBuffer(
263 &self,
264 source: &GPUBuffer,
265 source_offset: GPUSize64,
266 destination: &GPUBuffer,
267 destination_offset: GPUSize64,
268 size: GPUSize64,
269 ) {
270 self.droppable
271 .channel
272 .0
273 .send(WebGPURequest::CopyBufferToBuffer {
274 command_encoder_id: self.droppable.encoder.0,
275 source_id: source.id().0,
276 source_offset,
277 destination_id: destination.id().0,
278 destination_offset,
279 size,
280 device_id: self.device.id().0,
281 })
282 .expect("Failed to send CopyBufferToBuffer");
283 }
284
285 fn CopyBufferToTexture(
287 &self,
288 source: &GPUTexelCopyBufferInfo,
289 destination: &GPUTexelCopyTextureInfo,
290 copy_size: GPUExtent3D,
291 ) -> Fallible<()> {
292 self.droppable
293 .channel
294 .0
295 .send(WebGPURequest::CopyBufferToTexture {
296 command_encoder_id: self.droppable.encoder.0,
297 source: source.convert(),
298 destination: destination.try_convert()?,
299 copy_size: (©_size).try_convert()?,
300 device_id: self.device.id().0,
301 })
302 .expect("Failed to send CopyBufferToTexture");
303
304 Ok(())
305 }
306
307 fn CopyTextureToBuffer(
309 &self,
310 source: &GPUTexelCopyTextureInfo,
311 destination: &GPUTexelCopyBufferInfo,
312 copy_size: GPUExtent3D,
313 ) -> Fallible<()> {
314 self.droppable
315 .channel
316 .0
317 .send(WebGPURequest::CopyTextureToBuffer {
318 command_encoder_id: self.droppable.encoder.0,
319 source: source.try_convert()?,
320 destination: destination.convert(),
321 copy_size: (©_size).try_convert()?,
322 device_id: self.device.id().0,
323 })
324 .expect("Failed to send CopyTextureToBuffer");
325
326 Ok(())
327 }
328
329 fn CopyTextureToTexture(
331 &self,
332 source: &GPUTexelCopyTextureInfo,
333 destination: &GPUTexelCopyTextureInfo,
334 copy_size: GPUExtent3D,
335 ) -> Fallible<()> {
336 self.droppable
337 .channel
338 .0
339 .send(WebGPURequest::CopyTextureToTexture {
340 command_encoder_id: self.droppable.encoder.0,
341 source: source.try_convert()?,
342 destination: destination.try_convert()?,
343 copy_size: (©_size).try_convert()?,
344 device_id: self.device.id().0,
345 })
346 .expect("Failed to send CopyTextureToTexture");
347
348 Ok(())
349 }
350
351 fn Finish(
353 &self,
354 cx: &mut JSContext,
355 descriptor: &GPUCommandBufferDescriptor,
356 ) -> DomRoot<GPUCommandBuffer> {
357 let command_buffer_id = self.global().wgpu_id_hub().create_command_buffer_id();
358 self.droppable
359 .channel
360 .0
361 .send(WebGPURequest::CommandEncoderFinish {
362 command_encoder_id: self.droppable.encoder.0,
363 device_id: self.device.id().0,
364 desc: wgpu_types::CommandBufferDescriptor {
365 label: (&descriptor.parent).convert(),
366 },
367 command_buffer_id,
368 })
369 .expect("Failed to send Finish");
370
371 let buffer = WebGPUCommandBuffer(command_buffer_id);
372 GPUCommandBuffer::new(
373 cx,
374 &self.global(),
375 self.droppable.channel.clone(),
376 buffer,
377 descriptor.parent.label.clone(),
378 )
379 }
380
381 fn PushDebugGroup(&self, group_label: USVString) {
383 if let Err(e) = self
384 .droppable
385 .channel
386 .0
387 .send(WebGPURequest::CommandEncoderPushDebugGroup {
388 command_encoder_id: self.droppable.encoder.0,
389 label: group_label.to_string(),
390 device_id: self.device.id().0,
391 })
392 {
393 warn!("Error sending WebGPURequest::CommandEncoderPushDebugGroup: {e:?}")
394 }
395 }
396
397 fn PopDebugGroup(&self) {
399 if let Err(e) = self
400 .droppable
401 .channel
402 .0
403 .send(WebGPURequest::CommandEncoderPopDebugGroup {
404 command_encoder_id: self.droppable.encoder.0,
405 device_id: self.device.id().0,
406 })
407 {
408 warn!("Error sending WebGPURequest::CommandEncoderPopDebugGroup: {e:?}")
409 }
410 }
411
412 fn InsertDebugMarker(&self, marker_label: USVString) {
414 if let Err(e) =
415 self.droppable
416 .channel
417 .0
418 .send(WebGPURequest::CommandEncoderInsertDebugMarker {
419 command_encoder_id: self.droppable.encoder.0,
420 label: marker_label.to_string(),
421 device_id: self.device.id().0,
422 })
423 {
424 warn!("Error sending WebGPURequest::CommandEncoderInsertDebugMarker: {e:?}")
425 }
426 }
427
428 fn ResolveQuerySet(
429 &self,
430 query_set: &GPUQuerySet,
431 first_query: u32,
432 query_count: u32,
433 destination: &GPUBuffer,
434 destination_offset: u64,
435 ) {
436 if let Err(error) = self
437 .droppable
438 .channel
439 .0
440 .send(WebGPURequest::ResolveQuerySet {
441 command_encoder_id: self.droppable.encoder.0,
442 query_set_id: query_set.id().0,
443 start_query: first_query,
444 query_count,
445 destination: destination.id().0,
446 destination_offset,
447 device_id: self.device.id().0,
448 })
449 {
450 warn!("Error sending WebGPURequest::ResolveQuerySet: {error:?}")
451 }
452 }
453}