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