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