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