script/dom/webgpu/
gpucommandencoder.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    #[no_trace]
36    channel: WebGPU,
37    label: DomRefCell<USVString>,
38    #[no_trace]
39    encoder: WebGPUCommandEncoder,
40    device: Dom<GPUDevice>,
41}
42
43impl GPUCommandEncoder {
44    pub(crate) fn new_inherited(
45        channel: WebGPU,
46        device: &GPUDevice,
47        encoder: WebGPUCommandEncoder,
48        label: USVString,
49    ) -> Self {
50        Self {
51            channel,
52            reflector_: Reflector::new(),
53            label: DomRefCell::new(label),
54            device: Dom::from_ref(device),
55            encoder,
56        }
57    }
58
59    pub(crate) fn new(
60        global: &GlobalScope,
61        channel: WebGPU,
62        device: &GPUDevice,
63        encoder: WebGPUCommandEncoder,
64        label: USVString,
65        can_gc: CanGc,
66    ) -> DomRoot<Self> {
67        reflect_dom_object(
68            Box::new(GPUCommandEncoder::new_inherited(
69                channel, device, encoder, label,
70            )),
71            global,
72            can_gc,
73        )
74    }
75}
76
77impl GPUCommandEncoder {
78    pub(crate) fn id(&self) -> WebGPUCommandEncoder {
79        self.encoder
80    }
81
82    pub(crate) fn device_id(&self) -> WebGPUDevice {
83        self.device.id()
84    }
85
86    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
87    pub(crate) fn create(
88        device: &GPUDevice,
89        descriptor: &GPUCommandEncoderDescriptor,
90        can_gc: CanGc,
91    ) -> DomRoot<GPUCommandEncoder> {
92        let command_encoder_id = device.global().wgpu_id_hub().create_command_encoder_id();
93        device
94            .channel()
95            .0
96            .send(WebGPURequest::CreateCommandEncoder {
97                device_id: device.id().0,
98                command_encoder_id,
99                desc: wgpu_types::CommandEncoderDescriptor {
100                    label: (&descriptor.parent).convert(),
101                },
102            })
103            .expect("Failed to create WebGPU command encoder");
104
105        let encoder = WebGPUCommandEncoder(command_encoder_id);
106
107        GPUCommandEncoder::new(
108            &device.global(),
109            device.channel().clone(),
110            device,
111            encoder,
112            descriptor.parent.label.clone(),
113            can_gc,
114        )
115    }
116}
117
118impl GPUCommandEncoderMethods<crate::DomTypeHolder> for GPUCommandEncoder {
119    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
120    fn Label(&self) -> USVString {
121        self.label.borrow().clone()
122    }
123
124    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
125    fn SetLabel(&self, value: USVString) {
126        *self.label.borrow_mut() = value;
127    }
128
129    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-begincomputepass>
130    fn BeginComputePass(
131        &self,
132        descriptor: &GPUComputePassDescriptor,
133    ) -> DomRoot<GPUComputePassEncoder> {
134        let compute_pass_id = self.global().wgpu_id_hub().create_compute_pass_id();
135
136        if let Err(e) = self.channel.0.send(WebGPURequest::BeginComputePass {
137            command_encoder_id: self.id().0,
138            compute_pass_id,
139            label: (&descriptor.parent).convert(),
140            device_id: self.device.id().0,
141        }) {
142            warn!("Failed to send WebGPURequest::BeginComputePass {e:?}");
143        }
144
145        GPUComputePassEncoder::new(
146            &self.global(),
147            self.channel.clone(),
148            self,
149            WebGPUComputePass(compute_pass_id),
150            descriptor.parent.label.clone(),
151            CanGc::note(),
152        )
153    }
154
155    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-beginrenderpass>
156    fn BeginRenderPass(
157        &self,
158        descriptor: &GPURenderPassDescriptor,
159    ) -> Fallible<DomRoot<GPURenderPassEncoder>> {
160        let depth_stencil_attachment = descriptor.depthStencilAttachment.as_ref().map(|ds| {
161            wgpu_com::RenderPassDepthStencilAttachment {
162                depth: wgpu_com::PassChannel {
163                    load_op: ds
164                        .depthLoadOp
165                        .as_ref()
166                        .map(|l| convert_load_op(l, ds.depthClearValue.map(|v| *v))),
167                    store_op: ds.depthStoreOp.as_ref().map(Convert::convert),
168                    read_only: ds.depthReadOnly,
169                },
170                stencil: wgpu_com::PassChannel {
171                    load_op: ds
172                        .stencilLoadOp
173                        .as_ref()
174                        .map(|l| convert_load_op(l, Some(ds.stencilClearValue))),
175                    store_op: ds.stencilStoreOp.as_ref().map(Convert::convert),
176                    read_only: ds.stencilReadOnly,
177                },
178                view: ds.view.id().0,
179            }
180        });
181
182        let color_attachments = descriptor
183            .colorAttachments
184            .iter()
185            .map(|color| -> Fallible<_> {
186                Ok(Some(wgpu_com::RenderPassColorAttachment {
187                    resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0),
188                    load_op: convert_load_op(
189                        &color.loadOp,
190                        color
191                            .clearValue
192                            .as_ref()
193                            .map(|color| (color).try_convert())
194                            .transpose()?
195                            .unwrap_or_default(),
196                    ),
197                    store_op: color.storeOp.convert(),
198                    view: color.view.id().0,
199                    depth_slice: None,
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    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertobuffer>
227    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    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertotexture>
249    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: (&copy_size).try_convert()?,
262            })
263            .expect("Failed to send CopyBufferToTexture");
264
265        Ok(())
266    }
267
268    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertotexture>
269    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: (&copy_size).try_convert()?,
282            })
283            .expect("Failed to send CopyTextureToBuffer");
284
285        Ok(())
286    }
287
288    /// <https://gpuweb.github.io/gpuweb/#GPUCommandEncoder-copyTextureToTexture>
289    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: (&copy_size).try_convert()?,
302            })
303            .expect("Failed to send CopyTextureToTexture");
304
305        Ok(())
306    }
307
308    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-finish>
309    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}