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;
6use std::ffi::CString;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::cell::DomRefCell;
11use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
12use webgpu_traits::{WebGPU, WebGPURenderBundle, WebGPURequest};
13use wgpu_core::command::{
14    RenderBundleEncoder, RenderBundleEncoderDescriptor, bundle_ffi as wgpu_bundle,
15};
16
17use crate::conversions::Convert;
18use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
19    GPUIndexFormat, GPURenderBundleDescriptor, GPURenderBundleEncoderDescriptor,
20    GPURenderBundleEncoderMethods,
21};
22use crate::dom::bindings::error::Fallible;
23use crate::dom::bindings::reflector::DomGlobal;
24use crate::dom::bindings::root::{Dom, DomRoot};
25use crate::dom::bindings::str::USVString;
26use crate::dom::globalscope::GlobalScope;
27use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
28use crate::dom::webgpu::gpubuffer::GPUBuffer;
29use crate::dom::webgpu::gpudevice::GPUDevice;
30use crate::dom::webgpu::gpurenderbundle::GPURenderBundle;
31use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
32
33#[dom_struct]
34pub(crate) struct GPURenderBundleEncoder {
35    reflector_: Reflector,
36    #[no_trace]
37    channel: WebGPU,
38    device: Dom<GPUDevice>,
39    #[ignore_malloc_size_of = "defined in wgpu-core"]
40    #[no_trace]
41    render_bundle_encoder: DomRefCell<Option<RenderBundleEncoder>>,
42    label: DomRefCell<USVString>,
43}
44
45impl GPURenderBundleEncoder {
46    fn new_inherited(
47        render_bundle_encoder: RenderBundleEncoder,
48        device: &GPUDevice,
49        channel: WebGPU,
50        label: USVString,
51    ) -> Self {
52        Self {
53            reflector_: Reflector::new(),
54            render_bundle_encoder: DomRefCell::new(Some(render_bundle_encoder)),
55            device: Dom::from_ref(device),
56            channel,
57            label: DomRefCell::new(label),
58        }
59    }
60
61    pub(crate) fn new(
62        cx: &mut JSContext,
63        global: &GlobalScope,
64        render_bundle_encoder: RenderBundleEncoder,
65        device: &GPUDevice,
66        channel: WebGPU,
67        label: USVString,
68    ) -> DomRoot<Self> {
69        reflect_dom_object_with_cx(
70            Box::new(GPURenderBundleEncoder::new_inherited(
71                render_bundle_encoder,
72                device,
73                channel,
74                label,
75            )),
76            global,
77            cx,
78        )
79    }
80}
81
82impl GPURenderBundleEncoder {
83    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
84    pub(crate) fn create(
85        cx: &mut JSContext,
86        device: &GPUDevice,
87        descriptor: &GPURenderBundleEncoderDescriptor,
88    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
89        let desc = RenderBundleEncoderDescriptor {
90            label: (&descriptor.parent.parent).convert(),
91            color_formats: Cow::Owned(
92                descriptor
93                    .parent
94                    .colorFormats
95                    .iter()
96                    .map(|format| {
97                        device
98                            .validate_texture_format_required_features(format)
99                            .map(Some)
100                    })
101                    .collect::<Fallible<Vec<_>>>()?,
102            ),
103            depth_stencil: descriptor
104                .parent
105                .depthStencilFormat
106                .map(|dsf| {
107                    device
108                        .validate_texture_format_required_features(&dsf)
109                        .map(|format| wgpu_types::RenderBundleDepthStencil {
110                            format,
111                            depth_read_only: descriptor.depthReadOnly,
112                            stencil_read_only: descriptor.stencilReadOnly,
113                        })
114                })
115                .transpose()?,
116            sample_count: descriptor.parent.sampleCount,
117            multiview: None,
118        };
119
120        // Handle error gracefully
121        let render_bundle_encoder = RenderBundleEncoder::new(&desc, device.id().0).unwrap();
122
123        Ok(GPURenderBundleEncoder::new(
124            cx,
125            &device.global(),
126            render_bundle_encoder,
127            device,
128            device.channel(),
129            descriptor.parent.parent.label.clone(),
130        ))
131    }
132}
133
134impl GPURenderBundleEncoderMethods<crate::DomTypeHolder> for GPURenderBundleEncoder {
135    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
136    fn Label(&self) -> USVString {
137        self.label.borrow().clone()
138    }
139
140    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
141    fn SetLabel(&self, value: USVString) {
142        *self.label.borrow_mut() = value;
143    }
144
145    /// <https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup>
146    #[expect(unsafe_code)]
147    fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, dynamic_offsets: Vec<u32>) {
148        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
149            unsafe {
150                wgpu_bundle::wgpu_render_bundle_set_bind_group(
151                    encoder,
152                    index,
153                    Some(bind_group.id().0),
154                    dynamic_offsets.as_ptr(),
155                    dynamic_offsets.len(),
156                )
157            };
158        }
159    }
160
161    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline>
162    fn SetPipeline(&self, pipeline: &GPURenderPipeline) {
163        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
164            wgpu_bundle::wgpu_render_bundle_set_pipeline(encoder, pipeline.id().0);
165        }
166    }
167
168    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setindexbuffer>
169    fn SetIndexBuffer(
170        &self,
171        buffer: &GPUBuffer,
172        index_format: GPUIndexFormat,
173        offset: u64,
174        size: u64,
175    ) {
176        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
177            wgpu_bundle::wgpu_render_bundle_set_index_buffer(
178                encoder,
179                buffer.id().0,
180                match index_format {
181                    GPUIndexFormat::Uint16 => wgpu_types::IndexFormat::Uint16,
182                    GPUIndexFormat::Uint32 => wgpu_types::IndexFormat::Uint32,
183                },
184                offset,
185                wgpu_types::BufferSize::new(size),
186            );
187        }
188    }
189
190    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setvertexbuffer>
191    fn SetVertexBuffer(&self, slot: u32, buffer: &GPUBuffer, offset: u64, size: u64) {
192        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
193            wgpu_bundle::wgpu_render_bundle_set_vertex_buffer(
194                encoder,
195                slot,
196                buffer.id().0,
197                offset,
198                wgpu_types::BufferSize::new(size),
199            );
200        }
201    }
202
203    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-draw>
204    fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) {
205        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
206            wgpu_bundle::wgpu_render_bundle_draw(
207                encoder,
208                vertex_count,
209                instance_count,
210                first_vertex,
211                first_instance,
212            );
213        }
214    }
215
216    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexed>
217    fn DrawIndexed(
218        &self,
219        index_count: u32,
220        instance_count: u32,
221        first_index: u32,
222        base_vertex: i32,
223        first_instance: u32,
224    ) {
225        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
226            wgpu_bundle::wgpu_render_bundle_draw_indexed(
227                encoder,
228                index_count,
229                instance_count,
230                first_index,
231                base_vertex,
232                first_instance,
233            );
234        }
235    }
236
237    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindirect>
238    fn DrawIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
239        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
240            wgpu_bundle::wgpu_render_bundle_draw_indirect(
241                encoder,
242                indirect_buffer.id().0,
243                indirect_offset,
244            );
245        }
246    }
247
248    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexedindirect>
249    fn DrawIndexedIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
250        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
251            wgpu_bundle::wgpu_render_bundle_draw_indexed_indirect(
252                encoder,
253                indirect_buffer.id().0,
254                indirect_offset,
255            );
256        }
257    }
258
259    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-pushdebuggroup>
260    #[expect(unsafe_code)]
261    fn PushDebugGroup(&self, group_label: USVString) {
262        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
263            let label = CString::new(group_label.0).unwrap_or_default();
264            unsafe {
265                wgpu_bundle::wgpu_render_bundle_push_debug_group(encoder, label.as_ptr());
266            }
267        }
268    }
269
270    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-popdebuggroup>
271    fn PopDebugGroup(&self) {
272        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
273            wgpu_bundle::wgpu_render_bundle_pop_debug_group(encoder);
274        }
275    }
276
277    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-insertdebugmarker>
278    #[expect(unsafe_code)]
279    fn InsertDebugMarker(&self, marker_label: USVString) {
280        if let Some(encoder) = self.render_bundle_encoder.borrow_mut().as_mut() {
281            let label = CString::new(marker_label.0).unwrap_or_default();
282            unsafe {
283                wgpu_bundle::wgpu_render_bundle_insert_debug_marker(encoder, label.as_ptr());
284            }
285        }
286    }
287
288    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderbundleencoder-finish>
289    fn Finish(
290        &self,
291        cx: &mut JSContext,
292        descriptor: &GPURenderBundleDescriptor,
293    ) -> DomRoot<GPURenderBundle> {
294        let desc = wgpu_types::RenderBundleDescriptor {
295            label: (&descriptor.parent).convert(),
296        };
297        let encoder = self.render_bundle_encoder.borrow_mut().take().unwrap();
298        let render_bundle_id = self.global().wgpu_id_hub().create_render_bundle_id();
299
300        self.channel
301            .0
302            .send(WebGPURequest::RenderBundleEncoderFinish {
303                render_bundle_encoder: encoder,
304                descriptor: desc,
305                render_bundle_id,
306                device_id: self.device.id().0,
307            })
308            .expect("Failed to send RenderBundleEncoderFinish");
309
310        let render_bundle = WebGPURenderBundle(render_bundle_id);
311        GPURenderBundle::new(
312            cx,
313            &self.global(),
314            render_bundle,
315            self.device.id(),
316            self.channel.clone(),
317            descriptor.parent.label.clone(),
318        )
319    }
320}