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