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                    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    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertobuffer>
228    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    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertotexture>
250    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: (&copy_size).try_convert()?,
263            })
264            .expect("Failed to send CopyBufferToTexture");
265
266        Ok(())
267    }
268
269    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertotexture>
270    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: (&copy_size).try_convert()?,
283            })
284            .expect("Failed to send CopyTextureToBuffer");
285
286        Ok(())
287    }
288
289    /// <https://gpuweb.github.io/gpuweb/#GPUCommandEncoder-copyTextureToTexture>
290    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: (&copy_size).try_convert()?,
303            })
304            .expect("Failed to send CopyTextureToTexture");
305
306        Ok(())
307    }
308
309    /// <https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-finish>
310    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}