Skip to main content

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