1use dom_struct::dom_struct;
6use js::context::{JSContext, NoGC};
7use script_bindings::cell::DomRefCell;
8use script_bindings::reflector::{Reflector, reflect_dom_object};
9use script_webgpu::gpuconvert::{
10 WebGPUConvert, WebGPUTryConvert, convert_load_op, convert_texture_for_wgpu_with_cx,
11};
12use webgpu_traits::{
13 WebGPU, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePass, WebGPUDevice,
14 WebGPURenderPass, WebGPURequest,
15};
16use wgpu_core::command as wgpu_com;
17
18use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
19 GPUCommandBufferDescriptor, GPUCommandEncoderDescriptor, GPUCommandEncoderMethods,
20 GPUComputePassDescriptor, GPUExtent3D, GPURenderPassDescriptor, GPUSize64,
21 GPUTexelCopyBufferInfo, GPUTexelCopyTextureInfo,
22};
23use crate::dom::bindings::error::Fallible;
24use crate::dom::bindings::reflector::DomGlobal;
25use crate::dom::bindings::root::{Dom, DomRoot};
26use crate::dom::bindings::str::USVString;
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::types::GPUQuerySet;
29use crate::dom::webgpu::gpubuffer::GPUBuffer;
30use crate::dom::webgpu::gpucommandbuffer::GPUCommandBuffer;
31use crate::dom::webgpu::gpucomputepassencoder::GPUComputePassEncoder;
32use crate::dom::webgpu::gpudevice::GPUDevice;
33use crate::dom::webgpu::gpurenderpassencoder::GPURenderPassEncoder;
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(
86 cx,
87 Box::new(GPUCommandEncoder::new_inherited(
88 channel, device, encoder, label,
89 )),
90 global,
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
164 .timestampWrites
165 .as_ref()
166 .map(WebGPUConvert::convert),
167 device_id: self.device.id().0,
168 })
169 {
170 warn!("Failed to send WebGPURequest::BeginComputePass {error:?}");
171 }
172
173 GPUComputePassEncoder::new(
174 cx,
175 &self.global(),
176 self.droppable.channel.clone(),
177 self,
178 WebGPUComputePass(compute_pass_id),
179 descriptor.parent.label.clone(),
180 )
181 }
182
183 fn BeginRenderPass(
185 &self,
186 cx: &mut JSContext,
187 descriptor: &GPURenderPassDescriptor,
188 ) -> Fallible<DomRoot<GPURenderPassEncoder>> {
189 let depth_stencil_attachment = descriptor.depthStencilAttachment.as_ref().map(|ds| {
190 wgpu_com::RenderPassDepthStencilAttachment {
191 depth: wgpu_com::PassChannel {
192 load_op: ds
193 .depthLoadOp
194 .as_ref()
195 .map(|l| convert_load_op(l, ds.depthClearValue.map(|v| *v))),
196 store_op: ds.depthStoreOp.as_ref().map(WebGPUConvert::convert),
197 read_only: ds.depthReadOnly,
198 },
199 stencil: wgpu_com::PassChannel {
200 load_op: ds
201 .stencilLoadOp
202 .as_ref()
203 .map(|l| convert_load_op(l, Some(ds.stencilClearValue))),
204 store_op: ds.stencilStoreOp.as_ref().map(WebGPUConvert::convert),
205 read_only: ds.stencilReadOnly,
206 },
207 view: convert_texture_for_wgpu_with_cx(cx, &ds.view).0,
208 }
209 });
210
211 let color_attachments = descriptor
212 .colorAttachments
213 .iter()
214 .map(|color| -> Fallible<_> {
215 Ok(Some(wgpu_com::RenderPassColorAttachment {
216 resolve_target: color
217 .resolveTarget
218 .as_ref()
219 .map(|t| convert_texture_for_wgpu_with_cx(cx, t).0),
220 load_op: convert_load_op(
221 &color.loadOp,
222 color
223 .clearValue
224 .as_ref()
225 .map(|color| (color).try_convert())
226 .transpose()?
227 .unwrap_or_default(),
228 ),
229 store_op: color.storeOp.convert(),
230 view: convert_texture_for_wgpu_with_cx(cx, &color.view).0,
231 depth_slice: None,
232 }))
233 })
234 .collect::<Fallible<Vec<_>>>()?;
235 let render_pass_id = self.global().wgpu_id_hub().create_render_pass_id();
236
237 if let Err(error) = self
238 .droppable
239 .channel
240 .0
241 .send(WebGPURequest::BeginRenderPass {
242 command_encoder_id: self.id().0,
243 render_pass_id,
244 label: (&descriptor.parent).convert(),
245 depth_stencil_attachment,
246 color_attachments,
247 timestamp_writes: descriptor
248 .timestampWrites
249 .as_ref()
250 .map(WebGPUConvert::convert),
251 device_id: self.device.id().0,
252 })
253 {
254 warn!("Failed to send WebGPURequest::BeginRenderPass {error:?}");
255 }
256
257 Ok(GPURenderPassEncoder::new(
258 cx,
259 &self.global(),
260 self.droppable.channel.clone(),
261 WebGPURenderPass(render_pass_id),
262 self,
263 descriptor.parent.label.clone(),
264 ))
265 }
266
267 fn CopyBufferToBuffer(
269 &self,
270 source: &GPUBuffer,
271 source_offset: GPUSize64,
272 destination: &GPUBuffer,
273 destination_offset: GPUSize64,
274 size: GPUSize64,
275 ) {
276 self.droppable
277 .channel
278 .0
279 .send(WebGPURequest::CopyBufferToBuffer {
280 command_encoder_id: self.droppable.encoder.0,
281 source_id: source.id().0,
282 source_offset,
283 destination_id: destination.id().0,
284 destination_offset,
285 size,
286 device_id: self.device.id().0,
287 })
288 .expect("Failed to send CopyBufferToBuffer");
289 }
290
291 fn CopyBufferToTexture(
293 &self,
294 source: &GPUTexelCopyBufferInfo,
295 destination: &GPUTexelCopyTextureInfo,
296 copy_size: GPUExtent3D,
297 ) -> Fallible<()> {
298 self.droppable
299 .channel
300 .0
301 .send(WebGPURequest::CopyBufferToTexture {
302 command_encoder_id: self.droppable.encoder.0,
303 source: source.convert(),
304 destination: destination.try_convert()?,
305 copy_size: (©_size).try_convert()?,
306 device_id: self.device.id().0,
307 })
308 .expect("Failed to send CopyBufferToTexture");
309
310 Ok(())
311 }
312
313 fn CopyTextureToBuffer(
315 &self,
316 source: &GPUTexelCopyTextureInfo,
317 destination: &GPUTexelCopyBufferInfo,
318 copy_size: GPUExtent3D,
319 ) -> Fallible<()> {
320 self.droppable
321 .channel
322 .0
323 .send(WebGPURequest::CopyTextureToBuffer {
324 command_encoder_id: self.droppable.encoder.0,
325 source: source.try_convert()?,
326 destination: destination.convert(),
327 copy_size: (©_size).try_convert()?,
328 device_id: self.device.id().0,
329 })
330 .expect("Failed to send CopyTextureToBuffer");
331
332 Ok(())
333 }
334
335 fn CopyTextureToTexture(
337 &self,
338 source: &GPUTexelCopyTextureInfo,
339 destination: &GPUTexelCopyTextureInfo,
340 copy_size: GPUExtent3D,
341 ) -> Fallible<()> {
342 self.droppable
343 .channel
344 .0
345 .send(WebGPURequest::CopyTextureToTexture {
346 command_encoder_id: self.droppable.encoder.0,
347 source: source.try_convert()?,
348 destination: destination.try_convert()?,
349 copy_size: (©_size).try_convert()?,
350 device_id: self.device.id().0,
351 })
352 .expect("Failed to send CopyTextureToTexture");
353
354 Ok(())
355 }
356
357 fn Finish(
359 &self,
360 cx: &mut JSContext,
361 descriptor: &GPUCommandBufferDescriptor,
362 ) -> DomRoot<GPUCommandBuffer> {
363 let command_buffer_id = self.global().wgpu_id_hub().create_command_buffer_id();
364 self.droppable
365 .channel
366 .0
367 .send(WebGPURequest::CommandEncoderFinish {
368 command_encoder_id: self.droppable.encoder.0,
369 device_id: self.device.id().0,
370 desc: wgpu_types::CommandBufferDescriptor {
371 label: (&descriptor.parent).convert(),
372 },
373 command_buffer_id,
374 })
375 .expect("Failed to send Finish");
376
377 let buffer = WebGPUCommandBuffer(command_buffer_id);
378 GPUCommandBuffer::new(
379 cx,
380 &self.global(),
381 self.droppable.channel.clone(),
382 buffer,
383 descriptor.parent.label.clone(),
384 )
385 }
386
387 fn PushDebugGroup(&self, group_label: USVString) {
389 if let Err(e) = self
390 .droppable
391 .channel
392 .0
393 .send(WebGPURequest::CommandEncoderPushDebugGroup {
394 command_encoder_id: self.droppable.encoder.0,
395 label: group_label.to_string(),
396 device_id: self.device.id().0,
397 })
398 {
399 warn!("Error sending WebGPURequest::CommandEncoderPushDebugGroup: {e:?}")
400 }
401 }
402
403 fn PopDebugGroup(&self) {
405 if let Err(e) = self
406 .droppable
407 .channel
408 .0
409 .send(WebGPURequest::CommandEncoderPopDebugGroup {
410 command_encoder_id: self.droppable.encoder.0,
411 device_id: self.device.id().0,
412 })
413 {
414 warn!("Error sending WebGPURequest::CommandEncoderPopDebugGroup: {e:?}")
415 }
416 }
417
418 fn InsertDebugMarker(&self, marker_label: USVString) {
420 if let Err(e) =
421 self.droppable
422 .channel
423 .0
424 .send(WebGPURequest::CommandEncoderInsertDebugMarker {
425 command_encoder_id: self.droppable.encoder.0,
426 label: marker_label.to_string(),
427 device_id: self.device.id().0,
428 })
429 {
430 warn!("Error sending WebGPURequest::CommandEncoderInsertDebugMarker: {e:?}")
431 }
432 }
433
434 fn ResolveQuerySet(
435 &self,
436 query_set: &GPUQuerySet,
437 first_query: u32,
438 query_count: u32,
439 destination: &GPUBuffer,
440 destination_offset: u64,
441 ) {
442 if let Err(error) = self
443 .droppable
444 .channel
445 .0
446 .send(WebGPURequest::ResolveQuerySet {
447 command_encoder_id: self.droppable.encoder.0,
448 query_set_id: query_set.id().0,
449 start_query: first_query,
450 query_count,
451 destination: destination.id().0,
452 destination_offset,
453 device_id: self.device.id().0,
454 })
455 {
456 warn!("Error sending WebGPURequest::ResolveQuerySet: {error:?}")
457 }
458 }
459}