1use std::borrow::Cow;
6
7use dom_struct::dom_struct;
8use js::context::{JSContext, NoGC};
9use log::warn;
10use malloc_size_of_derive::MallocSizeOf;
11use script_bindings::DomTypes;
12use script_bindings::cell::DomRefCell;
13use script_bindings::codegen::GenericBindings::WebGPUBinding::{
14 GPUIndexFormat, GPURenderBundleDescriptor, GPURenderBundleEncoderDescriptor,
15 GPURenderBundleEncoderMethods, GPURenderBundleEncoderWrap,
16};
17use script_bindings::interfaces::PromiseHelpers;
18use script_bindings::reflector::{DomGlobalGeneric, Reflector, reflect_dom_object_with_wrap};
19use webgpu_traits::{
20 BindingCommand, BufferSize, DebugCommand, RenderBundleDepthStencil, RenderBundleDescriptor,
21 RenderBundleEncoderCommand, RenderBundleEncoderDescriptor, RenderCommand, WebGPU,
22 WebGPURenderBundle, WebGPURenderBundleEncoder, WebGPURequest,
23};
24
25use crate::JSTraceable;
26use crate::dom::bindings::error::Fallible;
27use crate::dom::bindings::root::{Dom, DomRoot};
28use crate::dom::bindings::str::USVString;
29use crate::gpubindgroup::GPUBindGroup;
30use crate::gpubuffer::GPUBuffer;
31use crate::gpuconvert::WebGPUConvert;
32use crate::gpurenderbundle::GPURenderBundle;
33use crate::gpurenderpipeline::GPURenderPipeline;
34use crate::traits::{Equivalence, WebGPUGlobalTrait, WebGPUPromise};
35
36#[derive(JSTraceable, MallocSizeOf)]
37struct DroppableGPURenderBundleEncoder {
38 #[no_trace]
39 channel: WebGPU,
40 #[no_trace]
41 render_bundle_encoder: WebGPURenderBundleEncoder,
42}
43
44impl Drop for DroppableGPURenderBundleEncoder {
45 fn drop(&mut self) {
46 if let Err(error) = self.channel.0.send(WebGPURequest::DropRenderBundleEncoder(
47 self.render_bundle_encoder.0,
48 )) {
49 warn!(
50 "Failed to send WebGPURequest::DropRenderBundleEncoder({:?}) ({error})",
51 self.render_bundle_encoder.0
52 );
53 }
54 }
55}
56
57#[dom_struct]
58pub struct GPURenderBundleEncoder<D: DomTypes> {
59 reflector_: Reflector,
60 device: Dom<D::GPUDevice>,
61 label: DomRefCell<USVString>,
62 droppable: DroppableGPURenderBundleEncoder,
63}
64
65impl<D> GPURenderBundleEncoder<D>
66where
67 D: Equivalence,
68{
69 fn new_inherited(
70 device: &D::GPUDevice,
71 channel: WebGPU,
72 label: USVString,
73 render_bundle_encoder: WebGPURenderBundleEncoder,
74 ) -> Self {
75 Self {
76 reflector_: Reflector::new(),
77 device: Dom::from_ref(device),
78 droppable: DroppableGPURenderBundleEncoder {
79 channel,
80 render_bundle_encoder,
81 },
82 label: DomRefCell::new(label),
83 }
84 }
85
86 pub(crate) fn new(
87 cx: &mut JSContext,
88 global: &D::GlobalScope,
89 render_bundle_encoder: WebGPURenderBundleEncoder,
90 device: &D::GPUDevice,
91 channel: WebGPU,
92 label: USVString,
93 ) -> DomRoot<Self> {
94 reflect_dom_object_with_wrap::<D, _, _>(
95 cx,
96 Box::new(GPURenderBundleEncoder::new_inherited(
97 device,
98 channel,
99 label,
100 render_bundle_encoder,
101 )),
102 global,
103 GPURenderBundleEncoderWrap::<D>,
104 )
105 }
106}
107
108impl<D> GPURenderBundleEncoder<D>
109where
110 D: Equivalence,
111 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
112{
113 pub(crate) fn create(
115 cx: &mut JSContext,
116 device: &D::GPUDevice,
117 descriptor: &GPURenderBundleEncoderDescriptor,
118 ) -> Fallible<DomRoot<GPURenderBundleEncoder<D>>> {
119 let desc = RenderBundleEncoderDescriptor {
120 label: (&descriptor.parent.parent).convert(),
121 color_formats: Cow::Owned(
122 descriptor
123 .parent
124 .colorFormats
125 .iter()
126 .map(|format| {
127 device
128 .validate_texture_format_required_features(format)
129 .map(Some)
130 })
131 .collect::<Fallible<Vec<_>>>()?,
132 ),
133 depth_stencil: descriptor
134 .parent
135 .depthStencilFormat
136 .map(|dsf| {
137 device
138 .validate_texture_format_required_features(&dsf)
139 .map(|format| RenderBundleDepthStencil {
140 format,
141 depth_read_only: descriptor.depthReadOnly,
142 stencil_read_only: descriptor.stencilReadOnly,
143 })
144 })
145 .transpose()?,
146 sample_count: descriptor.parent.sampleCount,
147 multiview: None,
148 };
149
150 let id = device
151 .global_from_reflector()
152 .global_wgpu_id_hub()
153 .create_render_bundle_encoder_id();
154 let render_bundle_encoder = WebGPURenderBundleEncoder(id);
155
156 let channel = device.channel();
157
158 if let Err(error) = channel.0.send(WebGPURequest::CreateRenderBundleEncoder {
159 device_id: device.id().0,
160 desc,
161 render_bundle_encoder_id: render_bundle_encoder.0,
162 }) {
163 warn!(
164 "Failed to send WebGPURequest::CreateRenderBundleEncoder({:?}) ({error})",
165 render_bundle_encoder.0
166 );
167 }
168
169 Ok(GPURenderBundleEncoder::new(
170 cx,
171 &*device.global_from_reflector(),
172 render_bundle_encoder,
173 device,
174 device.channel(),
175 descriptor.parent.parent.label.clone(),
176 ))
177 }
178
179 pub(crate) fn id(&self) -> WebGPURenderBundleEncoder {
180 self.droppable.render_bundle_encoder
181 }
182}
183
184impl<D> GPURenderBundleEncoderMethods<D> for GPURenderBundleEncoder<D>
185where
186 D: Equivalence,
187 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
188{
189 fn Label(&self) -> USVString {
191 self.label.borrow().clone()
192 }
193
194 fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
196 *self.label.safe_borrow_mut(no_gc) = value;
197 }
198
199 fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup<D>, dynamic_offsets: Vec<u32>) {
201 if let Err(error) =
202 self.droppable
203 .channel
204 .0
205 .send(WebGPURequest::RenderBundleEncoderCommand {
206 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
207 render_command: RenderBundleEncoderCommand::BindingCommand(
208 BindingCommand::SetBindGroup {
209 index,
210 bind_group: Some(bind_group.id().0),
211 dynamic_offsets,
212 },
213 ),
214 device_id: self.device.id().0,
215 })
216 {
217 warn!(
218 "Failed to send WebGPURequest::RenderBundleEncoderSetBindGroup({:?}) ({error})",
219 self.droppable.render_bundle_encoder.0
220 );
221 }
222 }
223
224 fn SetPipeline(&self, pipeline: &GPURenderPipeline<D>) {
226 if let Err(error) =
227 self.droppable
228 .channel
229 .0
230 .send(WebGPURequest::RenderBundleEncoderCommand {
231 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
232 render_command: RenderBundleEncoderCommand::RenderCommand(
233 RenderCommand::SetPipeline(pipeline.id().0),
234 ),
235 device_id: self.device.id().0,
236 })
237 {
238 warn!(
239 "Failed to send WebGPURequest::RenderBundleEncoderSetPipeline({:?}) ({error})",
240 self.droppable.render_bundle_encoder.0
241 );
242 }
243 }
244
245 fn SetIndexBuffer(
247 &self,
248 buffer: &D::GPUBuffer,
249 index_format: GPUIndexFormat,
250 offset: u64,
251 size: u64,
252 ) {
253 if let Err(error) =
254 self.droppable
255 .channel
256 .0
257 .send(WebGPURequest::RenderBundleEncoderCommand {
258 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
259 render_command: RenderBundleEncoderCommand::RenderCommand(
260 RenderCommand::SetIndexBuffer {
261 buffer: buffer.id().0,
262 index_format: index_format.convert(),
263 offset,
264 size: BufferSize::new(size),
265 },
266 ),
267 device_id: self.device.id().0,
268 })
269 {
270 warn!(
271 "Failed to send WebGPURequest::RenderBundleEncoderSetIndexBuffer({:?}) ({error})",
272 self.droppable.render_bundle_encoder.0
273 );
274 }
275 }
276
277 fn SetVertexBuffer(&self, slot: u32, buffer: Option<&GPUBuffer<D>>, offset: u64, size: u64) {
279 if let Err(error) =
280 self.droppable
281 .channel
282 .0
283 .send(WebGPURequest::RenderBundleEncoderCommand {
284 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
285 render_command: RenderBundleEncoderCommand::RenderCommand(
286 RenderCommand::SetVertexBuffer {
287 slot,
288 buffer: buffer.map(|b| b.id().0),
289 offset,
290 size: BufferSize::new(size),
291 },
292 ),
293 device_id: self.device.id().0,
294 })
295 {
296 warn!(
297 "Failed to send WebGPURequest::RenderBundleEncoderSetVertexBuffer({:?}) ({error})",
298 self.droppable.render_bundle_encoder.0
299 );
300 }
301 }
302
303 fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) {
305 if let Err(error) =
306 self.droppable
307 .channel
308 .0
309 .send(WebGPURequest::RenderBundleEncoderCommand {
310 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
311 render_command: RenderBundleEncoderCommand::RenderCommand(
312 RenderCommand::Draw {
313 vertex_count,
314 instance_count,
315 first_vertex,
316 first_instance,
317 },
318 ),
319 device_id: self.device.id().0,
320 })
321 {
322 warn!(
323 "Failed to send WebGPURequest::RenderBundleEncoderDraw({:?}) ({error})",
324 self.droppable.render_bundle_encoder.0
325 );
326 }
327 }
328
329 fn DrawIndexed(
331 &self,
332 index_count: u32,
333 instance_count: u32,
334 first_index: u32,
335 base_vertex: i32,
336 first_instance: u32,
337 ) {
338 if let Err(error) =
339 self.droppable
340 .channel
341 .0
342 .send(WebGPURequest::RenderBundleEncoderCommand {
343 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
344 render_command: RenderBundleEncoderCommand::RenderCommand(
345 RenderCommand::DrawIndexed {
346 index_count,
347 instance_count,
348 first_index,
349 base_vertex,
350 first_instance,
351 },
352 ),
353 device_id: self.device.id().0,
354 })
355 {
356 warn!(
357 "Failed to send WebGPURequest::RenderBundleEncoderDrawIndexed({:?}) ({error})",
358 self.droppable.render_bundle_encoder.0
359 );
360 }
361 }
362
363 fn DrawIndirect(&self, indirect_buffer: &GPUBuffer<D>, indirect_offset: u64) {
365 if let Err(error) =
366 self.droppable
367 .channel
368 .0
369 .send(WebGPURequest::RenderBundleEncoderCommand {
370 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
371 render_command: RenderBundleEncoderCommand::RenderCommand(
372 RenderCommand::DrawIndirect {
373 indirect_buffer: indirect_buffer.id().0,
374 indirect_offset,
375 },
376 ),
377 device_id: self.device.id().0,
378 })
379 {
380 warn!(
381 "Failed to send WebGPURequest::RenderBundleEncoderDrawIndirect({:?}) ({error})",
382 self.droppable.render_bundle_encoder.0
383 );
384 }
385 }
386
387 fn DrawIndexedIndirect(&self, indirect_buffer: &GPUBuffer<D>, indirect_offset: u64) {
389 if let Err(error) =
390 self.droppable
391 .channel
392 .0
393 .send(WebGPURequest::RenderBundleEncoderCommand {
394 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
395 render_command: RenderBundleEncoderCommand::RenderCommand(
396 RenderCommand::DrawIndexedIndirect {
397 indirect_buffer: indirect_buffer.id().0,
398 indirect_offset,
399 },
400 ),
401 device_id: self.device.id().0,
402 })
403 {
404 warn!(
405 "Failed to send WebGPURequest::RenderBundleEncoderDrawIndexedIndirect({:?}) ({error})",
406 self.droppable.render_bundle_encoder.0
407 );
408 }
409 }
410
411 fn PushDebugGroup(&self, group_label: USVString) {
413 if let Err(error) =
414 self.droppable
415 .channel
416 .0
417 .send(WebGPURequest::RenderBundleEncoderCommand {
418 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
419 render_command: RenderBundleEncoderCommand::DebugCommand(
420 DebugCommand::PushDebugGroup(group_label.to_string()),
421 ),
422 device_id: self.device.id().0,
423 })
424 {
425 warn!(
426 "Failed to send WebGPURequest::RenderBundleEncoderPushDebugGroup({:?}) ({error})",
427 self.droppable.render_bundle_encoder.0
428 );
429 }
430 }
431
432 fn PopDebugGroup(&self) {
434 if let Err(error) =
435 self.droppable
436 .channel
437 .0
438 .send(WebGPURequest::RenderBundleEncoderCommand {
439 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
440 render_command: RenderBundleEncoderCommand::DebugCommand(
441 DebugCommand::PopDebugGroup,
442 ),
443 device_id: self.device.id().0,
444 })
445 {
446 warn!(
447 "Failed to send WebGPURequest::RenderBundleEncoderPopDebugGroup({:?}) ({error})",
448 self.id()
449 );
450 }
451 }
452
453 fn InsertDebugMarker(&self, marker_label: USVString) {
455 if let Err(error) =
456 self.droppable
457 .channel
458 .0
459 .send(WebGPURequest::RenderBundleEncoderCommand {
460 render_bundle_encoder_id: self.droppable.render_bundle_encoder.0,
461 render_command: RenderBundleEncoderCommand::DebugCommand(
462 DebugCommand::InsertDebugMarker(marker_label.to_string()),
463 ),
464 device_id: self.device.id().0,
465 })
466 {
467 warn!(
468 "Failed to send WebGPURequest::RenderBundleEncoderInsertDebugMarker({:?}) ({error})",
469 self.id()
470 );
471 }
472 }
473
474 fn Finish(
476 &self,
477 cx: &mut JSContext,
478 descriptor: &GPURenderBundleDescriptor,
479 ) -> DomRoot<D::GPURenderBundle> {
480 let desc = RenderBundleDescriptor {
481 label: (&descriptor.parent).convert(),
482 };
483 let render_bundle_id = self
484 .global_from_reflector()
485 .global_wgpu_id_hub()
486 .create_render_bundle_id();
487
488 if let Err(error) =
489 self.droppable
490 .channel
491 .0
492 .send(WebGPURequest::RenderBundleEncoderFinish {
493 render_bundle_encoder_id: self.id().0,
494 descriptor: desc,
495 render_bundle_id,
496 device_id: self.device.id().0,
497 })
498 {
499 warn!(
500 "Failed to send WebGPURequest::RenderBundleEncoderFinish({:?}) ({error})",
501 self.id()
502 );
503 }
504
505 let render_bundle = WebGPURenderBundle(render_bundle_id);
506 GPURenderBundle::new(
507 cx,
508 &*self.global_from_reflector(),
509 render_bundle,
510 self.device.id(),
511 self.droppable.channel.clone(),
512 descriptor.parent.label.clone(),
513 )
514 }
515}