1use dom_struct::dom_struct;
6use script_bindings::cell::DomRefCell;
7use script_bindings::reflector::{Reflector, reflect_dom_object};
8use webgpu_traits::{
9 WebGPU, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePass, WebGPUDevice,
10 WebGPURenderPass, WebGPURequest,
11};
12use wgpu_core::command as wgpu_com;
13
14use crate::conversions::{Convert, TryConvert};
15use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
16 GPUCommandBufferDescriptor, GPUCommandEncoderDescriptor, GPUCommandEncoderMethods,
17 GPUComputePassDescriptor, GPUExtent3D, GPUImageCopyBuffer, GPUImageCopyTexture,
18 GPURenderPassDescriptor, GPUSize64,
19};
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::bindings::str::USVString;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::gpuconvert::convert_load_op;
26use crate::dom::webgpu::gpubuffer::GPUBuffer;
27use crate::dom::webgpu::gpucommandbuffer::GPUCommandBuffer;
28use crate::dom::webgpu::gpucomputepassencoder::GPUComputePassEncoder;
29use crate::dom::webgpu::gpudevice::GPUDevice;
30use crate::dom::webgpu::gpurenderpassencoder::GPURenderPassEncoder;
31use crate::script_runtime::CanGc;
32
33#[derive(JSTraceable, MallocSizeOf)]
34struct DroppableGPUCommandEncoder {
35 #[no_trace]
36 channel: WebGPU,
37 #[no_trace]
38 encoder: WebGPUCommandEncoder,
39}
40
41#[dom_struct]
42pub(crate) struct GPUCommandEncoder {
43 reflector_: Reflector,
44 droppable: DroppableGPUCommandEncoder,
45 label: DomRefCell<USVString>,
46 device: Dom<GPUDevice>,
47}
48
49impl Drop for DroppableGPUCommandEncoder {
50 fn drop(&mut self) {
51 if let Err(e) = self
52 .channel
53 .0
54 .send(WebGPURequest::DropCommandEncoder(self.encoder.0))
55 {
56 warn!("Failed to send WebGPURequest::DropCommandEncoder with {e:?}");
57 }
58 }
59}
60
61impl GPUCommandEncoder {
62 pub(crate) fn new_inherited(
63 channel: WebGPU,
64 device: &GPUDevice,
65 encoder: WebGPUCommandEncoder,
66 label: USVString,
67 ) -> Self {
68 Self {
69 droppable: DroppableGPUCommandEncoder { channel, encoder },
70 reflector_: Reflector::new(),
71 label: DomRefCell::new(label),
72 device: Dom::from_ref(device),
73 }
74 }
75
76 pub(crate) fn new(
77 global: &GlobalScope,
78 channel: WebGPU,
79 device: &GPUDevice,
80 encoder: WebGPUCommandEncoder,
81 label: USVString,
82 can_gc: CanGc,
83 ) -> DomRoot<Self> {
84 reflect_dom_object(
85 Box::new(GPUCommandEncoder::new_inherited(
86 channel, device, encoder, label,
87 )),
88 global,
89 can_gc,
90 )
91 }
92}
93
94impl GPUCommandEncoder {
95 pub(crate) fn id(&self) -> WebGPUCommandEncoder {
96 self.droppable.encoder
97 }
98
99 pub(crate) fn device_id(&self) -> WebGPUDevice {
100 self.device.id()
101 }
102
103 pub(crate) fn create(
105 device: &GPUDevice,
106 descriptor: &GPUCommandEncoderDescriptor,
107 can_gc: CanGc,
108 ) -> DomRoot<GPUCommandEncoder> {
109 let command_encoder_id = device.global().wgpu_id_hub().create_command_encoder_id();
110 device
111 .channel()
112 .0
113 .send(WebGPURequest::CreateCommandEncoder {
114 device_id: device.id().0,
115 command_encoder_id,
116 desc: wgpu_types::CommandEncoderDescriptor {
117 label: (&descriptor.parent).convert(),
118 },
119 })
120 .expect("Failed to create WebGPU command encoder");
121
122 let encoder = WebGPUCommandEncoder(command_encoder_id);
123
124 GPUCommandEncoder::new(
125 &device.global(),
126 device.channel(),
127 device,
128 encoder,
129 descriptor.parent.label.clone(),
130 can_gc,
131 )
132 }
133}
134
135impl GPUCommandEncoderMethods<crate::DomTypeHolder> for GPUCommandEncoder {
136 fn Label(&self) -> USVString {
138 self.label.borrow().clone()
139 }
140
141 fn SetLabel(&self, value: USVString) {
143 *self.label.borrow_mut() = value;
144 }
145
146 fn BeginComputePass(
148 &self,
149 descriptor: &GPUComputePassDescriptor,
150 ) -> DomRoot<GPUComputePassEncoder> {
151 let compute_pass_id = self.global().wgpu_id_hub().create_compute_pass_id();
152
153 if let Err(e) = self
154 .droppable
155 .channel
156 .0
157 .send(WebGPURequest::BeginComputePass {
158 command_encoder_id: self.id().0,
159 compute_pass_id,
160 label: (&descriptor.parent).convert(),
161 device_id: self.device.id().0,
162 })
163 {
164 warn!("Failed to send WebGPURequest::BeginComputePass {e:?}");
165 }
166
167 GPUComputePassEncoder::new(
168 &self.global(),
169 self.droppable.channel.clone(),
170 self,
171 WebGPUComputePass(compute_pass_id),
172 descriptor.parent.label.clone(),
173 CanGc::deprecated_note(),
174 )
175 }
176
177 fn BeginRenderPass(
179 &self,
180 descriptor: &GPURenderPassDescriptor,
181 ) -> Fallible<DomRoot<GPURenderPassEncoder>> {
182 let depth_stencil_attachment = descriptor.depthStencilAttachment.as_ref().map(|ds| {
183 wgpu_com::RenderPassDepthStencilAttachment {
184 depth: wgpu_com::PassChannel {
185 load_op: ds
186 .depthLoadOp
187 .as_ref()
188 .map(|l| convert_load_op(l, ds.depthClearValue.map(|v| *v))),
189 store_op: ds.depthStoreOp.as_ref().map(Convert::convert),
190 read_only: ds.depthReadOnly,
191 },
192 stencil: wgpu_com::PassChannel {
193 load_op: ds
194 .stencilLoadOp
195 .as_ref()
196 .map(|l| convert_load_op(l, Some(ds.stencilClearValue))),
197 store_op: ds.stencilStoreOp.as_ref().map(Convert::convert),
198 read_only: ds.stencilReadOnly,
199 },
200 view: ds.view.id().0,
201 }
202 });
203
204 let color_attachments = descriptor
205 .colorAttachments
206 .iter()
207 .map(|color| -> Fallible<_> {
208 Ok(Some(wgpu_com::RenderPassColorAttachment {
209 resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0),
210 load_op: convert_load_op(
211 &color.loadOp,
212 color
213 .clearValue
214 .as_ref()
215 .map(|color| (color).try_convert())
216 .transpose()?
217 .unwrap_or_default(),
218 ),
219 store_op: color.storeOp.convert(),
220 view: color.view.id().0,
221 depth_slice: None,
222 }))
223 })
224 .collect::<Fallible<Vec<_>>>()?;
225 let render_pass_id = self.global().wgpu_id_hub().create_render_pass_id();
226
227 if let Err(e) = self
228 .droppable
229 .channel
230 .0
231 .send(WebGPURequest::BeginRenderPass {
232 command_encoder_id: self.id().0,
233 render_pass_id,
234 label: (&descriptor.parent).convert(),
235 depth_stencil_attachment,
236 color_attachments,
237 device_id: self.device.id().0,
238 })
239 {
240 warn!("Failed to send WebGPURequest::BeginRenderPass {e:?}");
241 }
242
243 Ok(GPURenderPassEncoder::new(
244 &self.global(),
245 self.droppable.channel.clone(),
246 WebGPURenderPass(render_pass_id),
247 self,
248 descriptor.parent.label.clone(),
249 CanGc::deprecated_note(),
250 ))
251 }
252
253 fn CopyBufferToBuffer(
255 &self,
256 source: &GPUBuffer,
257 source_offset: GPUSize64,
258 destination: &GPUBuffer,
259 destination_offset: GPUSize64,
260 size: GPUSize64,
261 ) {
262 self.droppable
263 .channel
264 .0
265 .send(WebGPURequest::CopyBufferToBuffer {
266 command_encoder_id: self.droppable.encoder.0,
267 source_id: source.id().0,
268 source_offset,
269 destination_id: destination.id().0,
270 destination_offset,
271 size,
272 device_id: self.device.id().0,
273 })
274 .expect("Failed to send CopyBufferToBuffer");
275 }
276
277 fn CopyBufferToTexture(
279 &self,
280 source: &GPUImageCopyBuffer,
281 destination: &GPUImageCopyTexture,
282 copy_size: GPUExtent3D,
283 ) -> Fallible<()> {
284 self.droppable
285 .channel
286 .0
287 .send(WebGPURequest::CopyBufferToTexture {
288 command_encoder_id: self.droppable.encoder.0,
289 source: source.convert(),
290 destination: destination.try_convert()?,
291 copy_size: (©_size).try_convert()?,
292 device_id: self.device.id().0,
293 })
294 .expect("Failed to send CopyBufferToTexture");
295
296 Ok(())
297 }
298
299 fn CopyTextureToBuffer(
301 &self,
302 source: &GPUImageCopyTexture,
303 destination: &GPUImageCopyBuffer,
304 copy_size: GPUExtent3D,
305 ) -> Fallible<()> {
306 self.droppable
307 .channel
308 .0
309 .send(WebGPURequest::CopyTextureToBuffer {
310 command_encoder_id: self.droppable.encoder.0,
311 source: source.try_convert()?,
312 destination: destination.convert(),
313 copy_size: (©_size).try_convert()?,
314 device_id: self.device.id().0,
315 })
316 .expect("Failed to send CopyTextureToBuffer");
317
318 Ok(())
319 }
320
321 fn CopyTextureToTexture(
323 &self,
324 source: &GPUImageCopyTexture,
325 destination: &GPUImageCopyTexture,
326 copy_size: GPUExtent3D,
327 ) -> Fallible<()> {
328 self.droppable
329 .channel
330 .0
331 .send(WebGPURequest::CopyTextureToTexture {
332 command_encoder_id: self.droppable.encoder.0,
333 source: source.try_convert()?,
334 destination: destination.try_convert()?,
335 copy_size: (©_size).try_convert()?,
336 device_id: self.device.id().0,
337 })
338 .expect("Failed to send CopyTextureToTexture");
339
340 Ok(())
341 }
342
343 fn Finish(&self, descriptor: &GPUCommandBufferDescriptor) -> DomRoot<GPUCommandBuffer> {
345 let command_buffer_id = self.global().wgpu_id_hub().create_command_buffer_id();
346 self.droppable
347 .channel
348 .0
349 .send(WebGPURequest::CommandEncoderFinish {
350 command_encoder_id: self.droppable.encoder.0,
351 device_id: self.device.id().0,
352 desc: wgpu_types::CommandBufferDescriptor {
353 label: (&descriptor.parent).convert(),
354 },
355 command_buffer_id,
356 })
357 .expect("Failed to send Finish");
358
359 let buffer = WebGPUCommandBuffer(command_buffer_id);
360 GPUCommandBuffer::new(
361 &self.global(),
362 self.droppable.channel.clone(),
363 buffer,
364 descriptor.parent.label.clone(),
365 CanGc::deprecated_note(),
366 )
367 }
368}