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