1use std::borrow::Cow;
6use std::ffi::CString;
7
8use dom_struct::dom_struct;
9use js::context::{JSContext, NoGC};
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 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 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 fn Label(&self) -> USVString {
137 self.label.borrow().clone()
138 }
139
140 fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
142 *self.label.safe_borrow_mut(no_gc) = value;
143 }
144
145 #[expect(unsafe_code)]
147 fn SetBindGroup(
148 &self,
149 no_gc: &NoGC,
150 index: u32,
151 bind_group: &GPUBindGroup,
152 dynamic_offsets: Vec<u32>,
153 ) {
154 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
155 unsafe {
156 wgpu_bundle::wgpu_render_bundle_set_bind_group(
157 encoder,
158 index,
159 Some(bind_group.id().0),
160 dynamic_offsets.as_ptr(),
161 dynamic_offsets.len(),
162 )
163 };
164 }
165 }
166
167 fn SetPipeline(&self, no_gc: &NoGC, pipeline: &GPURenderPipeline) {
169 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
170 wgpu_bundle::wgpu_render_bundle_set_pipeline(encoder, pipeline.id().0);
171 }
172 }
173
174 fn SetIndexBuffer(
176 &self,
177 no_gc: &NoGC,
178 buffer: &GPUBuffer,
179 index_format: GPUIndexFormat,
180 offset: u64,
181 size: u64,
182 ) {
183 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
184 wgpu_bundle::wgpu_render_bundle_set_index_buffer(
185 encoder,
186 buffer.id().0,
187 match index_format {
188 GPUIndexFormat::Uint16 => wgpu_types::IndexFormat::Uint16,
189 GPUIndexFormat::Uint32 => wgpu_types::IndexFormat::Uint32,
190 },
191 offset,
192 wgpu_types::BufferSize::new(size),
193 );
194 }
195 }
196
197 fn SetVertexBuffer(&self, no_gc: &NoGC, slot: u32, buffer: &GPUBuffer, offset: u64, size: u64) {
199 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
200 wgpu_bundle::wgpu_render_bundle_set_vertex_buffer(
201 encoder,
202 slot,
203 buffer.id().0,
204 offset,
205 wgpu_types::BufferSize::new(size),
206 );
207 }
208 }
209
210 fn Draw(
212 &self,
213 no_gc: &NoGC,
214 vertex_count: u32,
215 instance_count: u32,
216 first_vertex: u32,
217 first_instance: u32,
218 ) {
219 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
220 wgpu_bundle::wgpu_render_bundle_draw(
221 encoder,
222 vertex_count,
223 instance_count,
224 first_vertex,
225 first_instance,
226 );
227 }
228 }
229
230 fn DrawIndexed(
232 &self,
233 no_gc: &NoGC,
234 index_count: u32,
235 instance_count: u32,
236 first_index: u32,
237 base_vertex: i32,
238 first_instance: u32,
239 ) {
240 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
241 wgpu_bundle::wgpu_render_bundle_draw_indexed(
242 encoder,
243 index_count,
244 instance_count,
245 first_index,
246 base_vertex,
247 first_instance,
248 );
249 }
250 }
251
252 fn DrawIndirect(&self, no_gc: &NoGC, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
254 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
255 wgpu_bundle::wgpu_render_bundle_draw_indirect(
256 encoder,
257 indirect_buffer.id().0,
258 indirect_offset,
259 );
260 }
261 }
262
263 fn DrawIndexedIndirect(&self, no_gc: &NoGC, indirect_buffer: &GPUBuffer, indirect_offset: u64) {
265 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
266 wgpu_bundle::wgpu_render_bundle_draw_indexed_indirect(
267 encoder,
268 indirect_buffer.id().0,
269 indirect_offset,
270 );
271 }
272 }
273
274 #[expect(unsafe_code)]
276 fn PushDebugGroup(&self, no_gc: &NoGC, group_label: USVString) {
277 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
278 let label = CString::new(group_label.0).unwrap_or_default();
279 unsafe {
280 wgpu_bundle::wgpu_render_bundle_push_debug_group(encoder, label.as_ptr());
281 }
282 }
283 }
284
285 fn PopDebugGroup(&self, no_gc: &NoGC) {
287 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
288 wgpu_bundle::wgpu_render_bundle_pop_debug_group(encoder);
289 }
290 }
291
292 #[expect(unsafe_code)]
294 fn InsertDebugMarker(&self, no_gc: &NoGC, marker_label: USVString) {
295 if let Some(encoder) = self.render_bundle_encoder.safe_borrow_mut(no_gc).as_mut() {
296 let label = CString::new(marker_label.0).unwrap_or_default();
297 unsafe {
298 wgpu_bundle::wgpu_render_bundle_insert_debug_marker(encoder, label.as_ptr());
299 }
300 }
301 }
302
303 fn Finish(
305 &self,
306 cx: &mut JSContext,
307 descriptor: &GPURenderBundleDescriptor,
308 ) -> DomRoot<GPURenderBundle> {
309 let desc = wgpu_types::RenderBundleDescriptor {
310 label: (&descriptor.parent).convert(),
311 };
312 let encoder = self
313 .render_bundle_encoder
314 .safe_borrow_mut(cx)
315 .take()
316 .unwrap();
317 let render_bundle_id = self.global().wgpu_id_hub().create_render_bundle_id();
318
319 self.channel
320 .0
321 .send(WebGPURequest::RenderBundleEncoderFinish {
322 render_bundle_encoder: encoder,
323 descriptor: desc,
324 render_bundle_id,
325 device_id: self.device.id().0,
326 })
327 .expect("Failed to send RenderBundleEncoderFinish");
328
329 let render_bundle = WebGPURenderBundle(render_bundle_id);
330 GPURenderBundle::new(
331 cx,
332 &self.global(),
333 render_bundle,
334 self.device.id(),
335 self.channel.clone(),
336 descriptor.parent.label.clone(),
337 )
338 }
339}