Skip to main content

script/dom/webgpu/
gpurenderbundleencoder.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 std::borrow::Cow;
6
7use dom_struct::dom_struct;
8use js::context::{JSContext, NoGC};
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
11use webgpu_traits::{
12    RenderBundleCommand, WebGPU, WebGPURenderBundle, WebGPURenderBundleEncoder, WebGPURequest,
13};
14use wgpu_core::command::RenderBundleEncoderDescriptor;
15
16use crate::conversions::Convert;
17use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
18    GPUIndexFormat, GPURenderBundleDescriptor, GPURenderBundleEncoderDescriptor,
19    GPURenderBundleEncoderMethods,
20};
21use crate::dom::bindings::error::Fallible;
22use crate::dom::bindings::reflector::DomGlobal;
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::bindings::str::USVString;
25use crate::dom::globalscope::GlobalScope;
26use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
27use crate::dom::webgpu::gpubuffer::GPUBuffer;
28use crate::dom::webgpu::gpudevice::GPUDevice;
29use crate::dom::webgpu::gpurenderbundle::GPURenderBundle;
30use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
31
32#[derive(JSTraceable, MallocSizeOf)]
33struct DroppableGPURenderBundleEncoder {
34    #[no_trace]
35    channel: WebGPU,
36    #[no_trace]
37    render_bundle_encoder: WebGPURenderBundleEncoder,
38}
39
40impl Drop for DroppableGPURenderBundleEncoder {
41    fn drop(&mut self) {
42        if let Err(error) = self.channel.0.send(WebGPURequest::DropRenderBundleEncoder(
43            self.render_bundle_encoder.0,
44        )) {
45            warn!(
46                "Failed to send WebGPURequest::DropRenderBundleEncoder({:?}) ({error})",
47                self.render_bundle_encoder.0
48            );
49        }
50    }
51}
52
53#[dom_struct]
54pub(crate) struct GPURenderBundleEncoder {
55    reflector_: Reflector,
56    device: Dom<GPUDevice>,
57    label: DomRefCell<USVString>,
58    droppable: DroppableGPURenderBundleEncoder,
59}
60
61impl GPURenderBundleEncoder {
62    fn new_inherited(
63        device: &GPUDevice,
64        channel: WebGPU,
65        label: USVString,
66        render_bundle_encoder: WebGPURenderBundleEncoder,
67    ) -> Self {
68        Self {
69            reflector_: Reflector::new(),
70            device: Dom::from_ref(device),
71            droppable: DroppableGPURenderBundleEncoder {
72                channel,
73                render_bundle_encoder,
74            },
75            label: DomRefCell::new(label),
76        }
77    }
78
79    pub(crate) fn new(
80        cx: &mut JSContext,
81        global: &GlobalScope,
82        render_bundle_encoder: WebGPURenderBundleEncoder,
83        device: &GPUDevice,
84        channel: WebGPU,
85        label: USVString,
86    ) -> DomRoot<Self> {
87        reflect_dom_object_with_cx(
88            Box::new(GPURenderBundleEncoder::new_inherited(
89                device,
90                channel,
91                label,
92                render_bundle_encoder,
93            )),
94            global,
95            cx,
96        )
97    }
98}
99
100impl GPURenderBundleEncoder {
101    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
102    pub(crate) fn create(
103        cx: &mut JSContext,
104        device: &GPUDevice,
105        descriptor: &GPURenderBundleEncoderDescriptor,
106    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
107        let desc = RenderBundleEncoderDescriptor {
108            label: (&descriptor.parent.parent).convert(),
109            color_formats: Cow::Owned(
110                descriptor
111                    .parent
112                    .colorFormats
113                    .iter()
114                    .map(|format| {
115                        device
116                            .validate_texture_format_required_features(format)
117                            .map(Some)
118                    })
119                    .collect::<Fallible<Vec<_>>>()?,
120            ),
121            depth_stencil: descriptor
122                .parent
123                .depthStencilFormat
124                .map(|dsf| {
125                    device
126                        .validate_texture_format_required_features(&dsf)
127                        .map(|format| wgpu_types::RenderBundleDepthStencil {
128                            format,
129                            depth_read_only: descriptor.depthReadOnly,
130                            stencil_read_only: descriptor.stencilReadOnly,
131                        })
132                })
133                .transpose()?,
134            sample_count: descriptor.parent.sampleCount,
135            multiview: None,
136        };
137
138        let id = device
139            .global()
140            .wgpu_id_hub()
141            .create_render_bundle_encoder_id();
142        let render_bundle_encoder = WebGPURenderBundleEncoder(id);
143
144        let channel = device.channel();
145
146        if let Err(error) = channel.0.send(WebGPURequest::CreateRenderBundleEncoder {
147            device_id: device.id().0,
148            desc,
149            render_bundle_encoder_id: render_bundle_encoder.0,
150        }) {
151            warn!(
152                "Failed to send WebGPURequest::CreateRenderBundleEncoder({:?}) ({error})",
153                render_bundle_encoder.0
154            );
155        }
156
157        Ok(GPURenderBundleEncoder::new(
158            cx,
159            &device.global(),
160            render_bundle_encoder,
161            device,
162            device.channel(),
163            descriptor.parent.parent.label.clone(),
164        ))
165    }
166
167    pub(crate) fn id(&self) -> WebGPURenderBundleEncoder {
168        self.droppable.render_bundle_encoder
169    }
170}
171
172impl GPURenderBundleEncoderMethods<crate::DomTypeHolder> for GPURenderBundleEncoder {
173    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
174    fn Label(&self) -> USVString {
175        self.label.borrow().clone()
176    }
177
178    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
179    fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
180        *self.label.safe_borrow_mut(no_gc) = value;
181    }
182
183    /// <https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup>
184    fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, dynamic_offsets: Vec<u32>) {
185        if let Err(error) =
186            self.droppable
187                .channel
188                .0
189                .send(WebGPURequest::RenderBundleEncoderCommand {
190                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
191                    render_command: RenderBundleCommand::SetBindGroup {
192                        index,
193                        bind_group_id: bind_group.id().0,
194                        offsets: dynamic_offsets,
195                    },
196                    device_id: self.device.id().0,
197                })
198        {
199            warn!(
200                "Failed to send WebGPURequest::RenderBundleEncoderSetBindGroup({:?}) ({error})",
201                self.droppable.render_bundle_encoder.0
202            );
203        }
204    }
205
206    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline>
207    fn SetPipeline(&self, pipeline: &GPURenderPipeline) {
208        if let Err(error) =
209            self.droppable
210                .channel
211                .0
212                .send(WebGPURequest::RenderBundleEncoderCommand {
213                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
214                    render_command: RenderBundleCommand::SetPipeline(pipeline.id().0),
215                    device_id: self.device.id().0,
216                })
217        {
218            warn!(
219                "Failed to send WebGPURequest::RenderBundleEncoderSetPipeline({:?}) ({error})",
220                self.droppable.render_bundle_encoder.0
221            );
222        }
223    }
224
225    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setindexbuffer>
226    fn SetIndexBuffer(
227        &self,
228        buffer: &GPUBuffer,
229        index_format: GPUIndexFormat,
230        offset: u64,
231        size: u64,
232    ) {
233        if let Err(error) =
234            self.droppable
235                .channel
236                .0
237                .send(WebGPURequest::RenderBundleEncoderCommand {
238                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
239                    render_command: RenderBundleCommand::SetIndexBuffer {
240                        buffer_id: buffer.id().0,
241                        index_format: index_format.convert(),
242                        offset,
243                        size: wgpu_types::BufferSize::new(size),
244                    },
245                    device_id: self.device.id().0,
246                })
247        {
248            warn!(
249                "Failed to send WebGPURequest::RenderBundleEncoderSetIndexBuffer({:?}) ({error})",
250                self.droppable.render_bundle_encoder.0
251            );
252        }
253    }
254
255    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setvertexbuffer>
256    fn SetVertexBuffer(&self, slot: u32, buffer: Option<&GPUBuffer>, offset: u64, size: u64) {
257        if let Err(error) =
258            self.droppable
259                .channel
260                .0
261                .send(WebGPURequest::RenderBundleEncoderCommand {
262                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
263                    render_command: RenderBundleCommand::SetVertexBuffer {
264                        slot,
265                        buffer_id: buffer.map(|b| b.id().0),
266                        offset,
267                        size: wgpu_types::BufferSize::new(size),
268                    },
269                    device_id: self.device.id().0,
270                })
271        {
272            warn!(
273                "Failed to send WebGPURequest::RenderBundleEncoderSetVertexBuffer({:?}) ({error})",
274                self.droppable.render_bundle_encoder.0
275            );
276        }
277    }
278
279    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-draw>
280    fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) {
281        if let Err(error) =
282            self.droppable
283                .channel
284                .0
285                .send(WebGPURequest::RenderBundleEncoderCommand {
286                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
287                    render_command: RenderBundleCommand::Draw {
288                        vertex_count,
289                        instance_count,
290                        first_vertex,
291                        first_instance,
292                    },
293                    device_id: self.device.id().0,
294                })
295        {
296            warn!(
297                "Failed to send WebGPURequest::RenderBundleEncoderDraw({:?}) ({error})",
298                self.droppable.render_bundle_encoder.0
299            );
300        }
301    }
302
303    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexed>
304    fn DrawIndexed(
305        &self,
306        index_count: u32,
307        instance_count: u32,
308        first_index: u32,
309        base_vertex: i32,
310        first_instance: u32,
311    ) {
312        if let Err(error) =
313            self.droppable
314                .channel
315                .0
316                .send(WebGPURequest::RenderBundleEncoderCommand {
317                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
318                    render_command: RenderBundleCommand::DrawIndexed {
319                        index_count,
320                        instance_count,
321                        first_index,
322                        base_vertex,
323                        first_instance,
324                    },
325                    device_id: self.device.id().0,
326                })
327        {
328            warn!(
329                "Failed to send WebGPURequest::RenderBundleEncoderDrawIndexed({:?}) ({error})",
330                self.droppable.render_bundle_encoder.0
331            );
332        }
333    }
334
335    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindirect>
336    fn DrawIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
337        if let Err(error) =
338            self.droppable
339                .channel
340                .0
341                .send(WebGPURequest::RenderBundleEncoderCommand {
342                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
343                    render_command: RenderBundleCommand::DrawIndirect {
344                        buffer_id: indirect_buffer.id().0,
345                        offset: indirect_offset,
346                    },
347                    device_id: self.device.id().0,
348                })
349        {
350            warn!(
351                "Failed to send WebGPURequest::RenderBundleEncoderDrawIndirect({:?}) ({error})",
352                self.droppable.render_bundle_encoder.0
353            );
354        }
355    }
356
357    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexedindirect>
358    fn DrawIndexedIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
359        if let Err(error) =
360            self.droppable
361                .channel
362                .0
363                .send(WebGPURequest::RenderBundleEncoderCommand {
364                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
365                    render_command: RenderBundleCommand::DrawIndexedIndirect {
366                        buffer_id: indirect_buffer.id().0,
367                        offset: indirect_offset,
368                    },
369                    device_id: self.device.id().0,
370                })
371        {
372            warn!(
373                "Failed to send WebGPURequest::RenderBundleEncoderDrawIndexedIndirect({:?}) ({error})",
374                self.droppable.render_bundle_encoder.0
375            );
376        }
377    }
378
379    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-pushdebuggroup>
380    fn PushDebugGroup(&self, group_label: USVString) {
381        if let Err(error) =
382            self.droppable
383                .channel
384                .0
385                .send(WebGPURequest::RenderBundleEncoderCommand {
386                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
387                    render_command: RenderBundleCommand::PushDebugGroup(group_label.to_string()),
388                    device_id: self.device.id().0,
389                })
390        {
391            warn!(
392                "Failed to send WebGPURequest::RenderBundleEncoderPushDebugGroup({:?}) ({error})",
393                self.droppable.render_bundle_encoder.0
394            );
395        }
396    }
397
398    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-popdebuggroup>
399    fn PopDebugGroup(&self) {
400        if let Err(error) =
401            self.droppable
402                .channel
403                .0
404                .send(WebGPURequest::RenderBundleEncoderCommand {
405                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
406                    render_command: RenderBundleCommand::PopDebugGroup,
407                    device_id: self.device.id().0,
408                })
409        {
410            warn!(
411                "Failed to send WebGPURequest::RenderBundleEncoderPopDebugGroup({:?}) ({error})",
412                self.id()
413            );
414        }
415    }
416
417    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-insertdebugmarker>
418    fn InsertDebugMarker(&self, marker_label: USVString) {
419        if let Err(error) =
420            self.droppable
421                .channel
422                .0
423                .send(WebGPURequest::RenderBundleEncoderCommand {
424                    render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
425                    render_command: RenderBundleCommand::InsertDebugMarker(
426                        marker_label.to_string(),
427                    ),
428                    device_id: self.device.id().0,
429                })
430        {
431            warn!(
432                "Failed to send WebGPURequest::RenderBundleEncoderInsertDebugMarker({:?}) ({error})",
433                self.id()
434            );
435        }
436    }
437
438    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderbundleencoder-finish>
439    fn Finish(
440        &self,
441        cx: &mut JSContext,
442        descriptor: &GPURenderBundleDescriptor,
443    ) -> DomRoot<GPURenderBundle> {
444        let desc = wgpu_types::RenderBundleDescriptor {
445            label: (&descriptor.parent).convert(),
446        };
447        let render_bundle_id = self.global().wgpu_id_hub().create_render_bundle_id();
448
449        if let Err(error) =
450            self.droppable
451                .channel
452                .0
453                .send(WebGPURequest::RenderBundleEncoderFinish {
454                    render_bundle_encoder_id: self.id().0,
455                    descriptor: desc,
456                    render_bundle_id,
457                    device_id: self.device.id().0,
458                })
459        {
460            warn!(
461                "Failed to send WebGPURequest::RenderBundleEncoderFinish({:?}) ({error})",
462                self.id()
463            );
464        }
465
466        let render_bundle = WebGPURenderBundle(render_bundle_id);
467        GPURenderBundle::new(
468            cx,
469            &self.global(),
470            render_bundle,
471            self.device.id(),
472            self.droppable.channel.clone(),
473            descriptor.parent.label.clone(),
474        )
475    }
476}