Skip to main content

script_webgpu/
gpurenderpassencoder.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 js::context::{JSContext, NoGC};
7use log::warn;
8use malloc_size_of_derive::MallocSizeOf;
9use script_bindings::DomTypes;
10use script_bindings::cell::DomRefCell;
11use script_bindings::codegen::GenericBindings::WebGPUBinding::{
12    GPUIndexFormat, GPURenderPassEncoderMethods, GPURenderPassEncoderWrap,
13};
14use script_bindings::codegen::GenericUnionTypes::DoubleSequenceOrGPUColorDict as GPUColor;
15use script_bindings::error::Fallible;
16use script_bindings::interfaces::PromiseHelpers;
17use script_bindings::reflector::{Reflector, reflect_dom_object_with_wrap};
18use script_bindings::root::DomRoot;
19use webgpu_traits::{
20    BindingCommand, BufferSize, DebugCommand, IndexFormat, RenderCommand, RenderPassEncoderCommand,
21    WebGPU, WebGPURenderPass, WebGPURequest,
22};
23
24use crate::JSTraceable;
25use crate::dom::bindings::num::Finite;
26use crate::dom::bindings::root::Dom;
27use crate::dom::bindings::str::USVString;
28use crate::gpubindgroup::GPUBindGroup;
29use crate::gpubuffer::GPUBuffer;
30use crate::gpucommandencoder::GPUCommandEncoder;
31use crate::gpuconvert::WebGPUTryConvert;
32use crate::gpurenderbundle::GPURenderBundle;
33use crate::gpurenderpipeline::GPURenderPipeline;
34use crate::traits::{Equivalence, WebGPUPromise};
35
36#[derive(MallocSizeOf)]
37struct DroppableGPURenderPassEncoder {
38    channel: WebGPU,
39    render_pass: WebGPURenderPass,
40}
41
42impl Drop for DroppableGPURenderPassEncoder {
43    fn drop(&mut self) {
44        if let Err(e) = self
45            .channel
46            .0
47            .send(WebGPURequest::DropRenderPass(self.render_pass.0))
48        {
49            warn!("Failed to send WebGPURequest::DropRenderPass with {e:?}");
50        }
51    }
52}
53#[dom_struct]
54pub struct GPURenderPassEncoder<D: DomTypes> {
55    reflector_: Reflector,
56    label: DomRefCell<USVString>,
57    command_encoder: Dom<GPUCommandEncoder<D>>,
58    #[no_trace]
59    droppable: DroppableGPURenderPassEncoder,
60}
61
62impl<D> GPURenderPassEncoder<D>
63where
64    D: Equivalence,
65    <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
66{
67    fn new_inherited(
68        channel: WebGPU,
69        render_pass: WebGPURenderPass,
70        parent: &GPUCommandEncoder<D>,
71        label: USVString,
72    ) -> Self {
73        Self {
74            reflector_: Reflector::new(),
75            label: DomRefCell::new(label),
76            command_encoder: Dom::from_ref(parent),
77            droppable: DroppableGPURenderPassEncoder {
78                channel,
79                render_pass,
80            },
81        }
82    }
83
84    pub(crate) fn new(
85        cx: &mut JSContext,
86        global: &D::GlobalScope,
87        channel: WebGPU,
88        render_pass: WebGPURenderPass,
89        parent: &GPUCommandEncoder<D>,
90        label: USVString,
91    ) -> DomRoot<Self> {
92        reflect_dom_object_with_wrap::<D, _, _>(
93            cx,
94            Box::new(GPURenderPassEncoder::new_inherited(
95                channel,
96                render_pass,
97                parent,
98                label,
99            )),
100            global,
101            GPURenderPassEncoderWrap::<D>,
102        )
103    }
104
105    fn send_render_command(&self, render_command: RenderPassEncoderCommand) {
106        if let Err(e) = self
107            .droppable
108            .channel
109            .0
110            .send(WebGPURequest::RenderPassCommand {
111                render_pass_id: self.id().0,
112                render_command,
113                device_id: self.command_encoder.device_id().0,
114            })
115        {
116            warn!("Error sending WebGPURequest::RenderPassCommand: {e:?}")
117        }
118    }
119}
120
121impl<D: DomTypes> GPURenderPassEncoder<D> {
122    pub(crate) fn id(&self) -> WebGPURenderPass {
123        self.droppable.render_pass
124    }
125}
126
127impl<D> GPURenderPassEncoderMethods<D> for GPURenderPassEncoder<D>
128where
129    D: Equivalence,
130    <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
131{
132    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
133    fn Label(&self) -> USVString {
134        self.label.borrow().clone()
135    }
136
137    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
138    fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
139        *self.label.safe_borrow_mut(no_gc) = value;
140    }
141
142    /// <https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup>
143    fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup<D>, offsets: Vec<u32>) {
144        self.send_render_command(RenderPassEncoderCommand::BindingCommand(
145            BindingCommand::SetBindGroup {
146                index,
147                bind_group: Some(bind_group.id().0),
148                dynamic_offsets: offsets,
149            },
150        ))
151    }
152
153    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setviewport>
154    fn SetViewport(
155        &self,
156        x: Finite<f32>,
157        y: Finite<f32>,
158        width: Finite<f32>,
159        height: Finite<f32>,
160        min_depth: Finite<f32>,
161        max_depth: Finite<f32>,
162    ) {
163        self.send_render_command(RenderPassEncoderCommand::SetViewport {
164            x: *x,
165            y: *y,
166            width: *width,
167            height: *height,
168            min_depth: *min_depth,
169            max_depth: *max_depth,
170        })
171    }
172
173    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setscissorrect>
174    fn SetScissorRect(&self, x: u32, y: u32, width: u32, height: u32) {
175        self.send_render_command(RenderPassEncoderCommand::SetScissorRect {
176            x,
177            y,
178            width,
179            height,
180        })
181    }
182
183    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setblendcolor>
184    fn SetBlendConstant(&self, color: GPUColor) -> Fallible<()> {
185        self.send_render_command(RenderPassEncoderCommand::SetBlendConstant(
186            (&color).try_convert()?,
187        ));
188        Ok(())
189    }
190
191    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setstencilreference>
192    fn SetStencilReference(&self, reference: u32) {
193        self.send_render_command(RenderPassEncoderCommand::SetStencilReference(reference))
194    }
195
196    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-end>
197    fn End(&self) {
198        if let Err(e) = self.droppable.channel.0.send(WebGPURequest::EndRenderPass {
199            render_pass_id: self.id().0,
200            device_id: self.command_encoder.device_id().0,
201        }) {
202            warn!("Failed to send WebGPURequest::EndRenderPass: {e:?}");
203        }
204    }
205
206    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline>
207    fn SetPipeline(&self, pipeline: &GPURenderPipeline<D>) {
208        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
209            RenderCommand::SetPipeline(pipeline.id().0),
210        ))
211    }
212
213    /// <https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-setindexbuffer>
214    fn SetIndexBuffer(
215        &self,
216        buffer: &GPUBuffer<D>,
217        index_format: GPUIndexFormat,
218        offset: u64,
219        size: u64,
220    ) {
221        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
222            RenderCommand::SetIndexBuffer {
223                buffer: buffer.id().0,
224                index_format: match index_format {
225                    GPUIndexFormat::Uint16 => IndexFormat::Uint16,
226                    GPUIndexFormat::Uint32 => IndexFormat::Uint32,
227                },
228                offset,
229                size: BufferSize::new(size),
230            },
231        ))
232    }
233
234    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setvertexbuffer>
235    fn SetVertexBuffer(&self, slot: u32, buffer: Option<&GPUBuffer<D>>, offset: u64, size: u64) {
236        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
237            RenderCommand::SetVertexBuffer {
238                slot,
239                buffer: buffer.map(|b| b.id().0),
240                offset,
241                size: BufferSize::new(size),
242            },
243        ))
244    }
245
246    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-draw>
247    fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) {
248        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
249            RenderCommand::Draw {
250                vertex_count,
251                instance_count,
252                first_vertex,
253                first_instance,
254            },
255        ))
256    }
257
258    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexed>
259    fn DrawIndexed(
260        &self,
261        index_count: u32,
262        instance_count: u32,
263        first_index: u32,
264        base_vertex: i32,
265        first_instance: u32,
266    ) {
267        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
268            RenderCommand::DrawIndexed {
269                index_count,
270                instance_count,
271                first_index,
272                base_vertex,
273                first_instance,
274            },
275        ))
276    }
277
278    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindirect>
279    fn DrawIndirect(&self, buffer: &GPUBuffer<D>, offset: u64) {
280        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
281            RenderCommand::DrawIndirect {
282                indirect_buffer: buffer.id().0,
283                indirect_offset: offset,
284            },
285        ))
286    }
287
288    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexedindirect>
289    fn DrawIndexedIndirect(&self, buffer: &GPUBuffer<D>, offset: u64) {
290        self.send_render_command(RenderPassEncoderCommand::RenderCommand(
291            RenderCommand::DrawIndexedIndirect {
292                indirect_buffer: buffer.id().0,
293                indirect_offset: offset,
294            },
295        ))
296    }
297
298    /// <https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-executebundles>
299    fn ExecuteBundles(&self, bundles: Vec<DomRoot<GPURenderBundle<D>>>) {
300        let bundle_ids: Vec<_> = bundles.iter().map(|b| b.id().0).collect();
301        self.send_render_command(RenderPassEncoderCommand::ExecuteBundles(bundle_ids))
302    }
303
304    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-pushdebuggroup>
305    fn PushDebugGroup(&self, group_label: USVString) {
306        self.send_render_command(RenderPassEncoderCommand::DebugCommand(
307            DebugCommand::PushDebugGroup(group_label.to_string()),
308        ))
309    }
310
311    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-popdebuggroup>
312    fn PopDebugGroup(&self) {
313        self.send_render_command(RenderPassEncoderCommand::DebugCommand(
314            DebugCommand::PopDebugGroup,
315        ))
316    }
317
318    /// <https://gpuweb.github.io/gpuweb/#dom-gpudebugcommandsmixin-insertdebugmarker>
319    fn InsertDebugMarker(&self, marker_label: USVString) {
320        self.send_render_command(RenderPassEncoderCommand::DebugCommand(
321            DebugCommand::InsertDebugMarker(marker_label.to_string()),
322        ))
323    }
324}