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