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    #[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    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
88    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    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
121    fn Label(&self) -> USVString {
122        self.label.borrow().clone()
123    }
124
125    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
126    fn SetLabel(&self, value: USVString) {
127        *self.label.borrow_mut() = value;
128    }
129
130    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-begincomputepass>
131    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    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-beginrenderpass>
157    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    /// <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}