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