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 }))
201 })
202 .collect::<Fallible<Vec<_>>>()?;
203 let render_pass_id = self.global().wgpu_id_hub().create_render_pass_id();
204
205 if let Err(e) = self.channel.0.send(WebGPURequest::BeginRenderPass {
206 command_encoder_id: self.id().0,
207 render_pass_id,
208 label: (&descriptor.parent).convert(),
209 depth_stencil_attachment,
210 color_attachments,
211 device_id: self.device.id().0,
212 }) {
213 warn!("Failed to send WebGPURequest::BeginRenderPass {e:?}");
214 }
215
216 Ok(GPURenderPassEncoder::new(
217 &self.global(),
218 self.channel.clone(),
219 WebGPURenderPass(render_pass_id),
220 self,
221 descriptor.parent.label.clone(),
222 CanGc::note(),
223 ))
224 }
225
226 fn CopyBufferToBuffer(
228 &self,
229 source: &GPUBuffer,
230 source_offset: GPUSize64,
231 destination: &GPUBuffer,
232 destination_offset: GPUSize64,
233 size: GPUSize64,
234 ) {
235 self.channel
236 .0
237 .send(WebGPURequest::CopyBufferToBuffer {
238 command_encoder_id: self.encoder.0,
239 source_id: source.id().0,
240 source_offset,
241 destination_id: destination.id().0,
242 destination_offset,
243 size,
244 })
245 .expect("Failed to send CopyBufferToBuffer");
246 }
247
248 fn CopyBufferToTexture(
250 &self,
251 source: &GPUImageCopyBuffer,
252 destination: &GPUImageCopyTexture,
253 copy_size: GPUExtent3D,
254 ) -> Fallible<()> {
255 self.channel
256 .0
257 .send(WebGPURequest::CopyBufferToTexture {
258 command_encoder_id: self.encoder.0,
259 source: source.convert(),
260 destination: destination.try_convert()?,
261 copy_size: (©_size).try_convert()?,
262 })
263 .expect("Failed to send CopyBufferToTexture");
264
265 Ok(())
266 }
267
268 fn CopyTextureToBuffer(
270 &self,
271 source: &GPUImageCopyTexture,
272 destination: &GPUImageCopyBuffer,
273 copy_size: GPUExtent3D,
274 ) -> Fallible<()> {
275 self.channel
276 .0
277 .send(WebGPURequest::CopyTextureToBuffer {
278 command_encoder_id: self.encoder.0,
279 source: source.try_convert()?,
280 destination: destination.convert(),
281 copy_size: (©_size).try_convert()?,
282 })
283 .expect("Failed to send CopyTextureToBuffer");
284
285 Ok(())
286 }
287
288 fn CopyTextureToTexture(
290 &self,
291 source: &GPUImageCopyTexture,
292 destination: &GPUImageCopyTexture,
293 copy_size: GPUExtent3D,
294 ) -> Fallible<()> {
295 self.channel
296 .0
297 .send(WebGPURequest::CopyTextureToTexture {
298 command_encoder_id: self.encoder.0,
299 source: source.try_convert()?,
300 destination: destination.try_convert()?,
301 copy_size: (©_size).try_convert()?,
302 })
303 .expect("Failed to send CopyTextureToTexture");
304
305 Ok(())
306 }
307
308 fn Finish(&self, descriptor: &GPUCommandBufferDescriptor) -> DomRoot<GPUCommandBuffer> {
310 self.channel
311 .0
312 .send(WebGPURequest::CommandEncoderFinish {
313 command_encoder_id: self.encoder.0,
314 device_id: self.device.id().0,
315 desc: wgpu_types::CommandBufferDescriptor {
316 label: (&descriptor.parent).convert(),
317 },
318 })
319 .expect("Failed to send Finish");
320
321 let buffer = WebGPUCommandBuffer(self.encoder.0.into_command_buffer_id());
322 GPUCommandBuffer::new(
323 &self.global(),
324 self.channel.clone(),
325 buffer,
326 descriptor.parent.label.clone(),
327 CanGc::note(),
328 )
329 }
330}