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