Skip to main content

wgpu_hal/gles/
command.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use core::{mem, ops::Range};
4
5use arrayvec::ArrayVec;
6
7use super::{conv, Command as C};
8
9#[derive(Clone, Copy, Debug, Default)]
10struct TextureSlotDesc {
11    tex_target: super::BindTarget,
12    sampler_index: Option<u8>,
13}
14
15pub(super) struct State {
16    topology: u32,
17    primitive: super::PrimitiveState,
18    index_format: wgt::IndexFormat,
19    index_offset: wgt::BufferAddress,
20    vertex_buffers: [(
21        Option<super::VertexBufferDesc>,
22        Option<super::BufferBinding>,
23    ); crate::MAX_VERTEX_BUFFERS],
24    vertex_attributes: ArrayVec<super::AttributeDesc, { super::MAX_VERTEX_ATTRIBUTES }>,
25    color_targets: ArrayVec<super::ColorTargetDesc, { crate::MAX_COLOR_ATTACHMENTS }>,
26    stencil: super::StencilState,
27    depth_bias: wgt::DepthBiasState,
28    alpha_to_coverage_enabled: bool,
29    samplers: [Option<glow::Sampler>; super::MAX_SAMPLERS],
30    texture_slots: [TextureSlotDesc; super::MAX_TEXTURE_SLOTS],
31    render_size: wgt::Extent3d,
32    resolve_attachments: ArrayVec<(u32, super::TextureView), { crate::MAX_COLOR_ATTACHMENTS }>,
33    invalidate_attachments: ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>,
34    has_pass_label: bool,
35    instance_vbuf_mask: usize,
36    dirty_vbuf_mask: usize,
37    active_first_instance: u32,
38    first_instance_location: Option<glow::UniformLocation>,
39    immediates_descs: ArrayVec<super::ImmediateDesc, { super::MAX_IMMEDIATES_COMMANDS }>,
40    // The current state of the immediate data block.
41    current_immediates_data: [u32; super::MAX_IMMEDIATES],
42    end_of_pass_timestamp: Option<glow::Query>,
43    clip_distance_count: u32,
44}
45
46impl Default for State {
47    fn default() -> Self {
48        Self {
49            topology: Default::default(),
50            primitive: Default::default(),
51            index_format: Default::default(),
52            index_offset: Default::default(),
53            vertex_buffers: Default::default(),
54            vertex_attributes: Default::default(),
55            color_targets: Default::default(),
56            stencil: Default::default(),
57            depth_bias: Default::default(),
58            alpha_to_coverage_enabled: Default::default(),
59            samplers: Default::default(),
60            texture_slots: Default::default(),
61            render_size: Default::default(),
62            resolve_attachments: Default::default(),
63            invalidate_attachments: Default::default(),
64            has_pass_label: Default::default(),
65            instance_vbuf_mask: Default::default(),
66            dirty_vbuf_mask: Default::default(),
67            active_first_instance: Default::default(),
68            first_instance_location: Default::default(),
69            immediates_descs: Default::default(),
70            current_immediates_data: [0; super::MAX_IMMEDIATES],
71            end_of_pass_timestamp: Default::default(),
72            clip_distance_count: Default::default(),
73        }
74    }
75}
76
77impl super::CommandBuffer {
78    fn clear(&mut self) {
79        self.label = None;
80        self.commands.clear();
81        self.data_bytes.clear();
82        self.queries.clear();
83    }
84
85    fn add_marker(&mut self, marker: &str) -> Range<u32> {
86        let start = self.data_bytes.len() as u32;
87        self.data_bytes.extend(marker.as_bytes());
88        start..self.data_bytes.len() as u32
89    }
90
91    fn add_immediates_data(&mut self, data: &[u32]) -> Range<u32> {
92        let data_raw = bytemuck::cast_slice(data);
93        let start = self.data_bytes.len();
94        assert!(start < u32::MAX as usize);
95        self.data_bytes.extend_from_slice(data_raw);
96        let end = self.data_bytes.len();
97        assert!(end < u32::MAX as usize);
98        (start as u32)..(end as u32)
99    }
100}
101
102impl Drop for super::CommandEncoder {
103    fn drop(&mut self) {
104        use crate::CommandEncoder;
105        unsafe { self.discard_encoding() }
106        self.counters.command_encoders.sub(1);
107    }
108}
109
110impl super::CommandEncoder {
111    fn rebind_stencil_func(&mut self) {
112        fn make(s: &super::StencilSide, face: u32) -> C {
113            C::SetStencilFunc {
114                face,
115                function: s.function,
116                reference: s.reference,
117                read_mask: s.mask_read,
118            }
119        }
120
121        let s = &self.state.stencil;
122        if s.front.function == s.back.function
123            && s.front.mask_read == s.back.mask_read
124            && s.front.reference == s.back.reference
125        {
126            self.cmd_buffer
127                .commands
128                .push(make(&s.front, glow::FRONT_AND_BACK));
129        } else {
130            self.cmd_buffer.commands.push(make(&s.front, glow::FRONT));
131            self.cmd_buffer.commands.push(make(&s.back, glow::BACK));
132        }
133    }
134
135    fn rebind_vertex_data(&mut self, first_instance: u32) {
136        if self
137            .private_caps
138            .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
139        {
140            for (index, pair) in self.state.vertex_buffers.iter().enumerate() {
141                if self.state.dirty_vbuf_mask & (1 << index) == 0 {
142                    continue;
143                }
144                let (buffer_desc, vb) = match *pair {
145                    (Some(ref vb_desc), Some(ref vb)) => (vb_desc.clone(), vb),
146                    // Not all dirty bindings are necessarily filled. Some may be unused.
147                    (_, _) => continue,
148                };
149                let instance_offset = match buffer_desc.step {
150                    wgt::VertexStepMode::Vertex => 0,
151                    wgt::VertexStepMode::Instance => first_instance * buffer_desc.stride,
152                };
153
154                self.cmd_buffer.commands.push(C::SetVertexBuffer {
155                    index: index as u32,
156                    buffer: super::BufferBinding {
157                        raw: vb.raw,
158                        offset: vb.offset + instance_offset as wgt::BufferAddress,
159                    },
160                    buffer_desc,
161                });
162                self.state.dirty_vbuf_mask ^= 1 << index;
163            }
164        } else {
165            let mut vbuf_mask = 0;
166            for attribute in self.state.vertex_attributes.iter() {
167                if self.state.dirty_vbuf_mask & (1 << attribute.buffer_index) == 0 {
168                    continue;
169                }
170                let (buffer_desc, vb) =
171                    match self.state.vertex_buffers[attribute.buffer_index as usize] {
172                        (Some(ref vb_desc), Some(ref vb)) => (vb_desc.clone(), vb),
173                        // Not all dirty bindings are necessarily filled. Some may be unused.
174                        (_, _) => continue,
175                    };
176
177                let mut attribute_desc = attribute.clone();
178                attribute_desc.offset += vb.offset as u32;
179                if buffer_desc.step == wgt::VertexStepMode::Instance {
180                    attribute_desc.offset += buffer_desc.stride * first_instance;
181                }
182
183                self.cmd_buffer.commands.push(C::SetVertexAttribute {
184                    buffer: Some(vb.raw),
185                    buffer_desc,
186                    attribute_desc,
187                });
188                vbuf_mask |= 1 << attribute.buffer_index;
189            }
190            self.state.dirty_vbuf_mask ^= vbuf_mask;
191        }
192    }
193
194    fn rebind_sampler_states(&mut self, dirty_textures: u32, dirty_samplers: u32) {
195        for (texture_index, slot) in self.state.texture_slots.iter().enumerate() {
196            if dirty_textures & (1 << texture_index) != 0
197                || slot
198                    .sampler_index
199                    .is_some_and(|si| dirty_samplers & (1 << si) != 0)
200            {
201                let sampler = slot
202                    .sampler_index
203                    .and_then(|si| self.state.samplers[si as usize]);
204                self.cmd_buffer
205                    .commands
206                    .push(C::BindSampler(texture_index as u32, sampler));
207            }
208        }
209    }
210
211    fn prepare_draw(&mut self, first_instance: u32) {
212        // If we support fully featured instancing, we want to bind everything as normal
213        // and let the draw call sort it out.
214        let emulated_first_instance_value = if self
215            .private_caps
216            .contains(super::PrivateCapabilities::FULLY_FEATURED_INSTANCING)
217        {
218            0
219        } else {
220            first_instance
221        };
222
223        if emulated_first_instance_value != self.state.active_first_instance {
224            // rebind all per-instance buffers on first-instance change
225            self.state.dirty_vbuf_mask |= self.state.instance_vbuf_mask;
226            self.state.active_first_instance = emulated_first_instance_value;
227        }
228        if self.state.dirty_vbuf_mask != 0 {
229            self.rebind_vertex_data(emulated_first_instance_value);
230        }
231    }
232
233    fn set_pipeline_inner(&mut self, inner: &super::PipelineInner) {
234        self.cmd_buffer.commands.push(C::SetProgram(inner.program));
235
236        self.state
237            .first_instance_location
238            .clone_from(&inner.first_instance_location);
239        self.state
240            .immediates_descs
241            .clone_from(&inner.immediates_descs);
242
243        // rebind textures, if needed
244        let mut dirty_textures = 0u32;
245        for (texture_index, (slot, &sampler_index)) in self
246            .state
247            .texture_slots
248            .iter_mut()
249            .zip(inner.sampler_map.iter())
250            .enumerate()
251        {
252            if slot.sampler_index != sampler_index {
253                slot.sampler_index = sampler_index;
254                dirty_textures |= 1 << texture_index;
255            }
256        }
257        if dirty_textures != 0 {
258            self.rebind_sampler_states(dirty_textures, 0);
259        }
260    }
261}
262
263impl crate::CommandEncoder for super::CommandEncoder {
264    type A = super::Api;
265
266    unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> {
267        self.state = State::default();
268        self.cmd_buffer.label = label.map(String::from);
269        Ok(())
270    }
271    unsafe fn discard_encoding(&mut self) {
272        self.cmd_buffer.clear();
273    }
274    unsafe fn end_encoding(&mut self) -> Result<super::CommandBuffer, crate::DeviceError> {
275        Ok(mem::take(&mut self.cmd_buffer))
276    }
277    unsafe fn reset_all<I>(&mut self, _command_buffers: I) {
278        //TODO: could re-use the allocations in all these command buffers
279    }
280
281    unsafe fn transition_buffers<'a, T>(&mut self, barriers: T)
282    where
283        T: Iterator<Item = crate::BufferBarrier<'a, super::Buffer>>,
284    {
285        if !self
286            .private_caps
287            .contains(super::PrivateCapabilities::MEMORY_BARRIERS)
288        {
289            return;
290        }
291        for bar in barriers {
292            // GLES only synchronizes storage -> anything explicitly
293            if !bar.usage.from.contains(wgt::BufferUses::STORAGE_READ_WRITE) {
294                continue;
295            }
296            self.cmd_buffer
297                .commands
298                .push(C::BufferBarrier(bar.buffer.raw.unwrap(), bar.usage.to));
299        }
300    }
301
302    unsafe fn transition_textures<'a, T>(&mut self, barriers: T)
303    where
304        T: Iterator<Item = crate::TextureBarrier<'a, super::Texture>>,
305    {
306        if !self
307            .private_caps
308            .contains(super::PrivateCapabilities::MEMORY_BARRIERS)
309        {
310            return;
311        }
312
313        let mut combined_usage = wgt::TextureUses::empty();
314        for bar in barriers {
315            // GLES only synchronizes storage -> anything explicitly
316            // if shader writes to a texture then barriers should be placed
317            if !bar.usage.from.intersects(
318                wgt::TextureUses::STORAGE_READ_WRITE | wgt::TextureUses::STORAGE_WRITE_ONLY,
319            ) {
320                continue;
321            }
322            // unlike buffers, there is no need for a concrete texture
323            // object to be bound anywhere for a barrier
324            combined_usage |= bar.usage.to;
325        }
326
327        if !combined_usage.is_empty() {
328            self.cmd_buffer
329                .commands
330                .push(C::TextureBarrier(combined_usage));
331        }
332    }
333
334    unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) {
335        self.cmd_buffer
336            .commands
337            .push(C::ClearBuffer(Box::new(super::ClearBufferOp {
338                dst: buffer.clone(),
339                dst_target: buffer.target,
340                range,
341            })));
342    }
343
344    unsafe fn copy_buffer_to_buffer<T>(
345        &mut self,
346        src: &super::Buffer,
347        dst: &super::Buffer,
348        regions: T,
349    ) where
350        T: Iterator<Item = crate::BufferCopy>,
351    {
352        let (src_target, dst_target) = if src.target == dst.target {
353            (glow::COPY_READ_BUFFER, glow::COPY_WRITE_BUFFER)
354        } else {
355            (src.target, dst.target)
356        };
357        for copy in regions {
358            self.cmd_buffer
359                .commands
360                .push(C::CopyBufferToBuffer(Box::new(super::BufferToBufferCopy {
361                    src: src.clone(),
362                    src_target,
363                    dst: dst.clone(),
364                    dst_target,
365                    copy,
366                })))
367        }
368    }
369
370    #[cfg(webgl)]
371    unsafe fn copy_external_image_to_texture<T>(
372        &mut self,
373        src: &wgt::CopyExternalImageSourceInfo,
374        dst: &super::Texture,
375        dst_premultiplication: bool,
376        regions: T,
377    ) where
378        T: Iterator<Item = crate::TextureCopy>,
379    {
380        let (dst_raw, dst_target) = dst.inner.as_native();
381        for copy in regions {
382            self.cmd_buffer
383                .commands
384                .push(C::CopyExternalImageToTexture(Box::new(
385                    super::ExternalImageToTextureCopy {
386                        src: src.clone(),
387                        dst: dst_raw,
388                        dst_target,
389                        dst_format: dst.format,
390                        dst_premultiplication,
391                        copy,
392                    },
393                )))
394        }
395    }
396
397    unsafe fn copy_texture_to_texture<T>(
398        &mut self,
399        src: &super::Texture,
400        _src_usage: wgt::TextureUses,
401        dst: &super::Texture,
402        regions: T,
403    ) where
404        T: Iterator<Item = crate::TextureCopy>,
405    {
406        let (src_raw, src_target) = src.inner.as_native();
407        let (dst_raw, dst_target) = dst.inner.as_native();
408        for mut copy in regions {
409            copy.clamp_size_to_virtual(&src.copy_size, &dst.copy_size);
410            self.cmd_buffer
411                .commands
412                .push(C::CopyTextureToTexture(Box::new(
413                    super::TextureToTextureCopy {
414                        src: src_raw,
415                        src_target,
416                        dst: dst_raw,
417                        dst_target,
418                        copy,
419                    },
420                )))
421        }
422    }
423
424    unsafe fn copy_buffer_to_texture<T>(
425        &mut self,
426        src: &super::Buffer,
427        dst: &super::Texture,
428        regions: T,
429    ) where
430        T: Iterator<Item = crate::BufferTextureCopy>,
431    {
432        let (dst_raw, dst_target) = dst.inner.as_native();
433
434        for mut copy in regions {
435            copy.clamp_size_to_virtual(&dst.copy_size);
436            self.cmd_buffer
437                .commands
438                .push(C::CopyBufferToTexture(Box::new(
439                    super::BufferToTextureCopy {
440                        src: src.clone(),
441                        src_target: src.target,
442                        dst: dst_raw,
443                        dst_target,
444                        dst_format: dst.format,
445                        copy,
446                    },
447                )))
448        }
449    }
450
451    unsafe fn copy_texture_to_buffer<T>(
452        &mut self,
453        src: &super::Texture,
454        _src_usage: wgt::TextureUses,
455        dst: &super::Buffer,
456        regions: T,
457    ) where
458        T: Iterator<Item = crate::BufferTextureCopy>,
459    {
460        let (src_raw, src_target) = src.inner.as_native();
461        for mut copy in regions {
462            copy.clamp_size_to_virtual(&src.copy_size);
463            self.cmd_buffer
464                .commands
465                .push(C::CopyTextureToBuffer(Box::new(
466                    super::TextureToBufferCopy {
467                        src: src_raw,
468                        src_target,
469                        src_format: src.format,
470                        dst: dst.clone(),
471                        dst_target: dst.target,
472                        copy,
473                    },
474                )))
475        }
476    }
477
478    unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) {
479        let query = set.queries[index as usize];
480        self.cmd_buffer
481            .commands
482            .push(C::BeginQuery(query, set.target));
483    }
484    unsafe fn end_query(&mut self, set: &super::QuerySet, _index: u32) {
485        self.cmd_buffer.commands.push(C::EndQuery(set.target));
486    }
487    unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) {
488        let query = set.queries[index as usize];
489        self.cmd_buffer.commands.push(C::TimestampQuery(query));
490    }
491    unsafe fn reset_queries(&mut self, _set: &super::QuerySet, _range: Range<u32>) {
492        //TODO: what do we do here?
493    }
494    unsafe fn copy_query_results(
495        &mut self,
496        set: &super::QuerySet,
497        range: Range<u32>,
498        buffer: &super::Buffer,
499        offset: wgt::BufferAddress,
500        _stride: wgt::BufferSize,
501    ) {
502        let start = self.cmd_buffer.queries.len();
503        self.cmd_buffer
504            .queries
505            .extend_from_slice(&set.queries[range.start as usize..range.end as usize]);
506        let query_range = start as u32..self.cmd_buffer.queries.len() as u32;
507        self.cmd_buffer
508            .commands
509            .push(C::CopyQueryResults(Box::new(super::QueryResultsCopy {
510                query_range,
511                dst: buffer.clone(),
512                dst_target: buffer.target,
513                dst_offset: offset,
514            })));
515    }
516
517    // render
518
519    unsafe fn begin_render_pass(
520        &mut self,
521        desc: &crate::RenderPassDescriptor<super::QuerySet, super::TextureView>,
522    ) -> Result<(), crate::DeviceError> {
523        debug_assert!(self.state.end_of_pass_timestamp.is_none());
524        if let Some(ref t) = desc.timestamp_writes {
525            if let Some(index) = t.beginning_of_pass_write_index {
526                unsafe { self.write_timestamp(t.query_set, index) }
527            }
528            self.state.end_of_pass_timestamp = t
529                .end_of_pass_write_index
530                .map(|index| t.query_set.queries[index as usize]);
531        }
532
533        self.state.render_size = desc.extent;
534        self.state.resolve_attachments.clear();
535        self.state.invalidate_attachments.clear();
536        if let Some(label) = desc.label {
537            let range = self.cmd_buffer.add_marker(label);
538            self.cmd_buffer.commands.push(C::PushDebugGroup(range));
539            self.state.has_pass_label = true;
540        }
541
542        let rendering_to_external_framebuffer = desc
543            .color_attachments
544            .iter()
545            .filter_map(|at| at.as_ref())
546            .any(|at| match at.target.view.inner {
547                #[cfg(webgl)]
548                super::TextureInner::ExternalFramebuffer { .. } => true,
549                #[cfg(native)]
550                super::TextureInner::ExternalNativeFramebuffer { .. } => true,
551                _ => false,
552            });
553
554        if rendering_to_external_framebuffer && desc.color_attachments.len() != 1 {
555            panic!("Multiple render attachments with external framebuffers are not supported.");
556        }
557
558        // `COLOR_ATTACHMENT0` to `COLOR_ATTACHMENT31` gives 32 possible color attachments.
559        assert!(desc.color_attachments.len() <= 32);
560
561        match desc
562            .color_attachments
563            .first()
564            .filter(|at| at.is_some())
565            .and_then(|at| at.as_ref().map(|at| &at.target.view.inner))
566        {
567            // default framebuffer (provided externally)
568            Some(&super::TextureInner::DefaultRenderbuffer) => {
569                self.cmd_buffer
570                    .commands
571                    .push(C::ResetFramebuffer { is_default: true });
572            }
573            _ => {
574                // set the framebuffer
575                self.cmd_buffer
576                    .commands
577                    .push(C::ResetFramebuffer { is_default: false });
578
579                for (i, cat) in desc.color_attachments.iter().enumerate() {
580                    if let Some(cat) = cat.as_ref() {
581                        let attachment = glow::COLOR_ATTACHMENT0 + i as u32;
582                        // Try to use the multisampled render-to-texture extension to avoid resolving
583                        if let Some(ref rat) = cat.resolve_target {
584                            if matches!(rat.view.inner, super::TextureInner::Texture { .. })
585                                && self.private_caps.contains(
586                                    super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
587                                )
588                                && !cat.ops.contains(crate::AttachmentOps::STORE)
589                                // Extension specifies that only COLOR_ATTACHMENT0 is valid
590                                && i == 0
591                            {
592                                self.cmd_buffer.commands.push(C::BindAttachment(Box::new(
593                                    super::AttachmentBinding {
594                                        attachment,
595                                        view: rat.view.clone(),
596                                        depth_slice: None,
597                                        sample_count: desc.sample_count,
598                                    },
599                                )));
600                                continue;
601                            }
602                        }
603                        self.cmd_buffer.commands.push(C::BindAttachment(Box::new(
604                            super::AttachmentBinding {
605                                attachment,
606                                view: cat.target.view.clone(),
607                                depth_slice: cat.depth_slice,
608                                sample_count: 1,
609                            },
610                        )));
611                        if let Some(ref rat) = cat.resolve_target {
612                            self.state
613                                .resolve_attachments
614                                .push((attachment, rat.view.clone()));
615                        }
616                        if cat.ops.contains(crate::AttachmentOps::STORE_DISCARD) {
617                            self.state.invalidate_attachments.push(attachment);
618                        }
619                    }
620                }
621                if let Some(ref dsat) = desc.depth_stencil_attachment {
622                    let aspects = dsat.target.view.aspects;
623                    let attachment = match aspects {
624                        crate::FormatAspects::DEPTH => glow::DEPTH_ATTACHMENT,
625                        crate::FormatAspects::STENCIL => glow::STENCIL_ATTACHMENT,
626                        _ => glow::DEPTH_STENCIL_ATTACHMENT,
627                    };
628                    self.cmd_buffer.commands.push(C::BindAttachment(Box::new(
629                        super::AttachmentBinding {
630                            attachment,
631                            view: dsat.target.view.clone(),
632                            depth_slice: None,
633                            sample_count: 1,
634                        },
635                    )));
636                    if aspects.contains(crate::FormatAspects::DEPTH)
637                        && dsat.depth_ops.contains(crate::AttachmentOps::STORE_DISCARD)
638                    {
639                        self.state
640                            .invalidate_attachments
641                            .push(glow::DEPTH_ATTACHMENT);
642                    }
643                    if aspects.contains(crate::FormatAspects::STENCIL)
644                        && dsat
645                            .stencil_ops
646                            .contains(crate::AttachmentOps::STORE_DISCARD)
647                    {
648                        self.state
649                            .invalidate_attachments
650                            .push(glow::STENCIL_ATTACHMENT);
651                    }
652                }
653            }
654        }
655
656        let rect = crate::Rect {
657            x: 0,
658            y: 0,
659            w: desc.extent.width as i32,
660            h: desc.extent.height as i32,
661        };
662        self.cmd_buffer.commands.push(C::SetScissor(rect.clone()));
663        self.cmd_buffer.commands.push(C::SetViewport {
664            rect,
665            depth: 0.0..1.0,
666        });
667
668        if !rendering_to_external_framebuffer {
669            // set the draw buffers and states
670            self.cmd_buffer
671                .commands
672                .push(C::SetDrawColorBuffers(desc.color_attachments.len() as u8));
673        }
674
675        // issue the clears
676        for (i, cat) in desc
677            .color_attachments
678            .iter()
679            .filter_map(|at| at.as_ref())
680            .enumerate()
681        {
682            if cat.ops.contains(crate::AttachmentOps::LOAD_CLEAR) {
683                let c = &cat.clear_value;
684                self.cmd_buffer.commands.push(
685                    match cat.target.view.format.sample_type(None, None).unwrap() {
686                        wgt::TextureSampleType::Float { .. } => C::ClearColorF {
687                            draw_buffer: i as u32,
688                            color: [c.r as f32, c.g as f32, c.b as f32, c.a as f32],
689                            is_srgb: cat.target.view.format.is_srgb(),
690                        },
691                        wgt::TextureSampleType::Uint => C::ClearColorU(
692                            i as u32,
693                            [c.r as u32, c.g as u32, c.b as u32, c.a as u32],
694                        ),
695                        wgt::TextureSampleType::Sint => C::ClearColorI(
696                            i as u32,
697                            [c.r as i32, c.g as i32, c.b as i32, c.a as i32],
698                        ),
699                        wgt::TextureSampleType::Depth => unreachable!(),
700                    },
701                );
702            }
703        }
704
705        if let Some(ref dsat) = desc.depth_stencil_attachment {
706            let clear_depth = dsat.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR);
707            let clear_stencil = dsat.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR);
708
709            if clear_depth && clear_stencil {
710                self.cmd_buffer.commands.push(C::ClearDepthAndStencil(
711                    dsat.clear_value.0,
712                    dsat.clear_value.1,
713                ));
714            } else if clear_depth {
715                self.cmd_buffer
716                    .commands
717                    .push(C::ClearDepth(dsat.clear_value.0));
718            } else if clear_stencil {
719                self.cmd_buffer
720                    .commands
721                    .push(C::ClearStencil(dsat.clear_value.1));
722            }
723        }
724        Ok(())
725    }
726    unsafe fn end_render_pass(&mut self) {
727        for (attachment, dst) in self.state.resolve_attachments.drain(..) {
728            self.cmd_buffer.commands.push(C::ResolveAttachment(Box::new(
729                super::AttachmentResolve {
730                    attachment,
731                    dst,
732                    size: self.state.render_size,
733                },
734            )));
735        }
736        if !self.state.invalidate_attachments.is_empty() {
737            self.cmd_buffer.commands.push(C::InvalidateAttachments(
738                self.state.invalidate_attachments.clone(),
739            ));
740            self.state.invalidate_attachments.clear();
741        }
742        if self.state.has_pass_label {
743            self.cmd_buffer.commands.push(C::PopDebugGroup);
744            self.state.has_pass_label = false;
745        }
746        self.state.instance_vbuf_mask = 0;
747        self.state.dirty_vbuf_mask = 0;
748        self.state.active_first_instance = 0;
749        self.state.color_targets.clear();
750        for vat in &self.state.vertex_attributes {
751            self.cmd_buffer
752                .commands
753                .push(C::UnsetVertexAttribute(vat.location));
754        }
755        self.state.vertex_attributes.clear();
756        self.state.primitive = super::PrimitiveState::default();
757
758        if let Some(query) = self.state.end_of_pass_timestamp.take() {
759            self.cmd_buffer.commands.push(C::TimestampQuery(query));
760        }
761    }
762
763    unsafe fn set_bind_group(
764        &mut self,
765        layout: &super::PipelineLayout,
766        index: u32,
767        group: &super::BindGroup,
768        dynamic_offsets: &[wgt::DynamicOffset],
769    ) {
770        let mut do_index = 0;
771        let mut dirty_textures = 0u32;
772        let mut dirty_samplers = 0u32;
773        let group_info = layout.group_infos[index as usize].as_ref().unwrap();
774
775        for (binding_layout, raw_binding) in group_info.entries.iter().zip(group.contents.iter()) {
776            let slot = group_info.binding_to_slot[binding_layout.binding as usize] as u32;
777            match *raw_binding {
778                super::RawBinding::Buffer {
779                    raw,
780                    offset: base_offset,
781                    size,
782                } => {
783                    let mut offset = base_offset;
784                    let target = match binding_layout.ty {
785                        wgt::BindingType::Buffer {
786                            ty,
787                            has_dynamic_offset,
788                            min_binding_size: _,
789                        } => {
790                            if has_dynamic_offset {
791                                offset += dynamic_offsets[do_index] as i32;
792                                do_index += 1;
793                            }
794                            match ty {
795                                wgt::BufferBindingType::Uniform => glow::UNIFORM_BUFFER,
796                                wgt::BufferBindingType::Storage { .. } => {
797                                    glow::SHADER_STORAGE_BUFFER
798                                }
799                            }
800                        }
801                        _ => unreachable!(),
802                    };
803                    self.cmd_buffer.commands.push(C::BindBuffer {
804                        target,
805                        slot,
806                        buffer: raw,
807                        offset,
808                        size,
809                    });
810                }
811                super::RawBinding::Sampler(sampler) => {
812                    dirty_samplers |= 1 << slot;
813                    self.state.samplers[slot as usize] = Some(sampler);
814                }
815                super::RawBinding::Texture {
816                    raw,
817                    target,
818                    aspects,
819                    ref mip_levels,
820                } => {
821                    dirty_textures |= 1 << slot;
822                    self.state.texture_slots[slot as usize].tex_target = target;
823                    self.cmd_buffer.commands.push(C::BindTexture {
824                        slot,
825                        texture: raw,
826                        target,
827                        aspects,
828                        mip_levels: mip_levels.clone(),
829                    });
830                }
831                super::RawBinding::Image(ref binding) => {
832                    self.cmd_buffer.commands.push(C::BindImage {
833                        slot,
834                        binding: binding.clone(),
835                    });
836                }
837            }
838        }
839
840        self.rebind_sampler_states(dirty_textures, dirty_samplers);
841    }
842
843    unsafe fn set_immediates(
844        &mut self,
845        _layout: &super::PipelineLayout,
846        offset_bytes: u32,
847        data: &[u32],
848    ) {
849        // There is nothing preventing the user from trying to update a single value within
850        // a vector or matrix in the set_immediates call, as to the user, all of this is
851        // just memory. However OpenGL does not allow partial uniform updates.
852        //
853        // As such, we locally keep a copy of the current state of the immediate data memory
854        // block. If the user tries to update a single value, we have the data to update the entirety
855        // of the uniform.
856        let start_words = offset_bytes / 4;
857        let end_words = start_words + data.len() as u32;
858        self.state.current_immediates_data[start_words as usize..end_words as usize]
859            .copy_from_slice(data);
860
861        // We iterate over the uniform list as there may be multiple uniforms that need
862        // updating from the same immediate data memory (one for each shader stage).
863        //
864        // Additionally, any statically unused uniform descs will have been removed from this list
865        // by OpenGL, so the uniform list is not contiguous.
866        for uniform in self.state.immediates_descs.iter().cloned() {
867            let uniform_size_words = uniform.size_bytes / 4;
868            let uniform_start_words = uniform.offset / 4;
869            let uniform_end_words = uniform_start_words + uniform_size_words;
870
871            // Is true if any word within the uniform binding was updated
872            let needs_updating =
873                start_words < uniform_end_words || uniform_start_words <= end_words;
874
875            if needs_updating {
876                let uniform_data = &self.state.current_immediates_data
877                    [uniform_start_words as usize..uniform_end_words as usize];
878
879                let range = self.cmd_buffer.add_immediates_data(uniform_data);
880
881                self.cmd_buffer.commands.push(C::SetImmediates {
882                    uniform,
883                    offset: range.start,
884                });
885            }
886        }
887    }
888
889    unsafe fn insert_debug_marker(&mut self, label: &str) {
890        let range = self.cmd_buffer.add_marker(label);
891        self.cmd_buffer.commands.push(C::InsertDebugMarker(range));
892    }
893    unsafe fn begin_debug_marker(&mut self, group_label: &str) {
894        let range = self.cmd_buffer.add_marker(group_label);
895        self.cmd_buffer.commands.push(C::PushDebugGroup(range));
896    }
897    unsafe fn end_debug_marker(&mut self) {
898        self.cmd_buffer.commands.push(C::PopDebugGroup);
899    }
900
901    unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) {
902        self.state.topology = conv::map_primitive_topology(pipeline.primitive.topology);
903
904        if self
905            .private_caps
906            .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
907        {
908            for vat in pipeline.vertex_attributes.iter() {
909                let vb = pipeline.vertex_buffers[vat.buffer_index as usize]
910                    .as_ref()
911                    .unwrap();
912                // set the layout
913                self.cmd_buffer.commands.push(C::SetVertexAttribute {
914                    buffer: None,
915                    buffer_desc: vb.clone(),
916                    attribute_desc: vat.clone(),
917                });
918            }
919        } else {
920            for vat in &self.state.vertex_attributes {
921                self.cmd_buffer
922                    .commands
923                    .push(C::UnsetVertexAttribute(vat.location));
924            }
925            self.state.vertex_attributes.clear();
926
927            self.state.dirty_vbuf_mask = 0;
928            // copy vertex attributes
929            for vat in pipeline.vertex_attributes.iter() {
930                //Note: we can invalidate more carefully here.
931                self.state.dirty_vbuf_mask |= 1 << vat.buffer_index;
932                self.state.vertex_attributes.push(vat.clone());
933            }
934        }
935
936        self.state.instance_vbuf_mask = 0;
937        // copy vertex state
938        for (index, (&mut (ref mut state_desc, _), pipe_desc)) in self
939            .state
940            .vertex_buffers
941            .iter_mut()
942            .zip(pipeline.vertex_buffers.iter())
943            .enumerate()
944        {
945            let Some(pipe_desc) = pipe_desc else {
946                continue;
947            };
948            if pipe_desc.step == wgt::VertexStepMode::Instance {
949                self.state.instance_vbuf_mask |= 1 << index;
950            }
951            if state_desc.as_ref() != Some(pipe_desc) {
952                self.state.dirty_vbuf_mask |= 1 << index;
953                *state_desc = Some(pipe_desc.clone());
954            }
955        }
956
957        self.set_pipeline_inner(&pipeline.inner);
958
959        // set primitive state
960        let prim_state = conv::map_primitive_state(&pipeline.primitive);
961        if prim_state != self.state.primitive {
962            self.cmd_buffer
963                .commands
964                .push(C::SetPrimitive(prim_state.clone()));
965            self.state.primitive = prim_state;
966        }
967
968        // set depth/stencil states
969        let mut aspects = crate::FormatAspects::empty();
970        if pipeline.depth_bias != self.state.depth_bias {
971            self.state.depth_bias = pipeline.depth_bias;
972            self.cmd_buffer
973                .commands
974                .push(C::SetDepthBias(pipeline.depth_bias));
975        }
976        if let Some(ref depth) = pipeline.depth {
977            aspects |= crate::FormatAspects::DEPTH;
978            self.cmd_buffer.commands.push(C::SetDepth(depth.clone()));
979        }
980        if let Some(ref stencil) = pipeline.stencil {
981            aspects |= crate::FormatAspects::STENCIL;
982            self.state.stencil = stencil.clone();
983            self.rebind_stencil_func();
984            if stencil.front.ops == stencil.back.ops
985                && stencil.front.mask_write == stencil.back.mask_write
986            {
987                self.cmd_buffer.commands.push(C::SetStencilOps {
988                    face: glow::FRONT_AND_BACK,
989                    write_mask: stencil.front.mask_write,
990                    ops: stencil.front.ops.clone(),
991                });
992            } else {
993                self.cmd_buffer.commands.push(C::SetStencilOps {
994                    face: glow::FRONT,
995                    write_mask: stencil.front.mask_write,
996                    ops: stencil.front.ops.clone(),
997                });
998                self.cmd_buffer.commands.push(C::SetStencilOps {
999                    face: glow::BACK,
1000                    write_mask: stencil.back.mask_write,
1001                    ops: stencil.back.ops.clone(),
1002                });
1003            }
1004        }
1005        self.cmd_buffer
1006            .commands
1007            .push(C::ConfigureDepthStencil(aspects));
1008
1009        // set multisampling state
1010        if pipeline.alpha_to_coverage_enabled != self.state.alpha_to_coverage_enabled {
1011            self.state.alpha_to_coverage_enabled = pipeline.alpha_to_coverage_enabled;
1012            self.cmd_buffer
1013                .commands
1014                .push(C::SetAlphaToCoverage(pipeline.alpha_to_coverage_enabled));
1015        }
1016
1017        // set blend states
1018        if self.state.color_targets[..] != pipeline.color_targets[..] {
1019            if pipeline
1020                .color_targets
1021                .iter()
1022                .skip(1)
1023                .any(|ct| *ct != pipeline.color_targets[0])
1024            {
1025                for (index, ct) in pipeline.color_targets.iter().enumerate() {
1026                    self.cmd_buffer.commands.push(C::SetColorTarget {
1027                        draw_buffer_index: Some(index as u32),
1028                        desc: ct.clone(),
1029                    });
1030                }
1031            } else {
1032                self.cmd_buffer.commands.push(C::SetColorTarget {
1033                    draw_buffer_index: None,
1034                    desc: pipeline.color_targets.first().cloned().unwrap_or_default(),
1035                });
1036            }
1037        }
1038        self.state.color_targets.clear();
1039        for ct in pipeline.color_targets.iter() {
1040            self.state.color_targets.push(ct.clone());
1041        }
1042
1043        // set clip plane count
1044        if pipeline.inner.clip_distance_count != self.state.clip_distance_count {
1045            self.cmd_buffer.commands.push(C::SetClipDistances {
1046                old_count: self.state.clip_distance_count,
1047                new_count: pipeline.inner.clip_distance_count,
1048            });
1049            self.state.clip_distance_count = pipeline.inner.clip_distance_count;
1050        }
1051    }
1052
1053    unsafe fn set_index_buffer<'a>(
1054        &mut self,
1055        binding: crate::BufferBinding<'a, super::Buffer>,
1056        format: wgt::IndexFormat,
1057    ) {
1058        self.state.index_offset = binding.offset;
1059        self.state.index_format = format;
1060        self.cmd_buffer
1061            .commands
1062            .push(C::SetIndexBuffer(binding.buffer.raw.unwrap()));
1063    }
1064    unsafe fn set_vertex_buffer<'a>(
1065        &mut self,
1066        index: u32,
1067        binding: crate::BufferBinding<'a, super::Buffer>,
1068    ) {
1069        self.state.dirty_vbuf_mask |= 1 << index;
1070        let (_, ref mut vb) = self.state.vertex_buffers[index as usize];
1071        *vb = Some(super::BufferBinding {
1072            raw: binding.buffer.raw.unwrap(),
1073            offset: binding.offset,
1074        });
1075    }
1076    unsafe fn set_viewport(&mut self, rect: &crate::Rect<f32>, depth: Range<f32>) {
1077        self.cmd_buffer.commands.push(C::SetViewport {
1078            rect: crate::Rect {
1079                x: rect.x as i32,
1080                y: rect.y as i32,
1081                w: rect.w as i32,
1082                h: rect.h as i32,
1083            },
1084            depth,
1085        });
1086    }
1087    unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect<u32>) {
1088        self.cmd_buffer.commands.push(C::SetScissor(crate::Rect {
1089            x: rect.x as i32,
1090            y: rect.y as i32,
1091            w: rect.w as i32,
1092            h: rect.h as i32,
1093        }));
1094    }
1095    unsafe fn set_stencil_reference(&mut self, value: u32) {
1096        self.state.stencil.front.reference = value;
1097        self.state.stencil.back.reference = value;
1098        self.rebind_stencil_func();
1099    }
1100    unsafe fn set_blend_constants(&mut self, color: &[f32; 4]) {
1101        self.cmd_buffer.commands.push(C::SetBlendConstant(*color));
1102    }
1103
1104    unsafe fn draw(
1105        &mut self,
1106        first_vertex: u32,
1107        vertex_count: u32,
1108        first_instance: u32,
1109        instance_count: u32,
1110    ) {
1111        self.prepare_draw(first_instance);
1112        #[allow(
1113            clippy::clone_on_copy,
1114            reason = "False positive when cloning glow::UniformLocation"
1115        )]
1116        self.cmd_buffer.commands.push(C::Draw {
1117            topology: self.state.topology,
1118            first_vertex,
1119            vertex_count,
1120            first_instance,
1121            instance_count,
1122            first_instance_location: self.state.first_instance_location.clone(),
1123        });
1124    }
1125    unsafe fn draw_indexed(
1126        &mut self,
1127        first_index: u32,
1128        index_count: u32,
1129        base_vertex: i32,
1130        first_instance: u32,
1131        instance_count: u32,
1132    ) {
1133        self.prepare_draw(first_instance);
1134        let (index_size, index_type) = match self.state.index_format {
1135            wgt::IndexFormat::Uint16 => (2, glow::UNSIGNED_SHORT),
1136            wgt::IndexFormat::Uint32 => (4, glow::UNSIGNED_INT),
1137        };
1138        let index_offset = self.state.index_offset + index_size * first_index as wgt::BufferAddress;
1139        #[allow(
1140            clippy::clone_on_copy,
1141            reason = "False positive when cloning glow::UniformLocation"
1142        )]
1143        self.cmd_buffer.commands.push(C::DrawIndexed {
1144            topology: self.state.topology,
1145            index_type,
1146            index_offset,
1147            index_count,
1148            base_vertex,
1149            first_instance,
1150            instance_count,
1151            first_instance_location: self.state.first_instance_location.clone(),
1152        });
1153    }
1154    unsafe fn draw_mesh_tasks(
1155        &mut self,
1156        _group_count_x: u32,
1157        _group_count_y: u32,
1158        _group_count_z: u32,
1159    ) {
1160        unreachable!()
1161    }
1162    unsafe fn draw_indirect(
1163        &mut self,
1164        buffer: &super::Buffer,
1165        offset: wgt::BufferAddress,
1166        draw_count: u32,
1167    ) {
1168        self.prepare_draw(0);
1169        for draw in 0..draw_count as wgt::BufferAddress {
1170            let indirect_offset =
1171                offset + draw * size_of::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
1172            #[allow(
1173                clippy::clone_on_copy,
1174                reason = "False positive when cloning glow::UniformLocation"
1175            )]
1176            self.cmd_buffer.commands.push(C::DrawIndirect {
1177                topology: self.state.topology,
1178                indirect_buf: buffer.raw.unwrap(),
1179                indirect_offset,
1180                first_instance_location: self.state.first_instance_location.clone(),
1181            });
1182        }
1183    }
1184    unsafe fn draw_indexed_indirect(
1185        &mut self,
1186        buffer: &super::Buffer,
1187        offset: wgt::BufferAddress,
1188        draw_count: u32,
1189    ) {
1190        self.prepare_draw(0);
1191        let index_type = match self.state.index_format {
1192            wgt::IndexFormat::Uint16 => glow::UNSIGNED_SHORT,
1193            wgt::IndexFormat::Uint32 => glow::UNSIGNED_INT,
1194        };
1195        for draw in 0..draw_count as wgt::BufferAddress {
1196            let indirect_offset =
1197                offset + draw * size_of::<wgt::DrawIndexedIndirectArgs>() as wgt::BufferAddress;
1198            #[allow(
1199                clippy::clone_on_copy,
1200                reason = "False positive when cloning glow::UniformLocation"
1201            )]
1202            self.cmd_buffer.commands.push(C::DrawIndexedIndirect {
1203                topology: self.state.topology,
1204                index_type,
1205                indirect_buf: buffer.raw.unwrap(),
1206                indirect_offset,
1207                first_instance_location: self.state.first_instance_location.clone(),
1208            });
1209        }
1210    }
1211    unsafe fn draw_mesh_tasks_indirect(
1212        &mut self,
1213        _buffer: &<Self::A as crate::Api>::Buffer,
1214        _offset: wgt::BufferAddress,
1215        _draw_count: u32,
1216    ) {
1217        unreachable!()
1218    }
1219    unsafe fn draw_indirect_count(
1220        &mut self,
1221        _buffer: &super::Buffer,
1222        _offset: wgt::BufferAddress,
1223        _count_buffer: &super::Buffer,
1224        _count_offset: wgt::BufferAddress,
1225        _max_count: u32,
1226    ) {
1227        unreachable!()
1228    }
1229    unsafe fn draw_indexed_indirect_count(
1230        &mut self,
1231        _buffer: &super::Buffer,
1232        _offset: wgt::BufferAddress,
1233        _count_buffer: &super::Buffer,
1234        _count_offset: wgt::BufferAddress,
1235        _max_count: u32,
1236    ) {
1237        unreachable!()
1238    }
1239    unsafe fn draw_mesh_tasks_indirect_count(
1240        &mut self,
1241        _buffer: &<Self::A as crate::Api>::Buffer,
1242        _offset: wgt::BufferAddress,
1243        _count_buffer: &<Self::A as crate::Api>::Buffer,
1244        _count_offset: wgt::BufferAddress,
1245        _max_count: u32,
1246    ) {
1247        unreachable!()
1248    }
1249
1250    // compute
1251
1252    unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor<super::QuerySet>) {
1253        debug_assert!(self.state.end_of_pass_timestamp.is_none());
1254        if let Some(ref t) = desc.timestamp_writes {
1255            if let Some(index) = t.beginning_of_pass_write_index {
1256                unsafe { self.write_timestamp(t.query_set, index) }
1257            }
1258            self.state.end_of_pass_timestamp = t
1259                .end_of_pass_write_index
1260                .map(|index| t.query_set.queries[index as usize]);
1261        }
1262
1263        if let Some(label) = desc.label {
1264            let range = self.cmd_buffer.add_marker(label);
1265            self.cmd_buffer.commands.push(C::PushDebugGroup(range));
1266            self.state.has_pass_label = true;
1267        }
1268    }
1269    unsafe fn end_compute_pass(&mut self) {
1270        if self.state.has_pass_label {
1271            self.cmd_buffer.commands.push(C::PopDebugGroup);
1272            self.state.has_pass_label = false;
1273        }
1274
1275        if let Some(query) = self.state.end_of_pass_timestamp.take() {
1276            self.cmd_buffer.commands.push(C::TimestampQuery(query));
1277        }
1278    }
1279
1280    unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) {
1281        self.set_pipeline_inner(&pipeline.inner);
1282    }
1283
1284    unsafe fn dispatch_workgroups(&mut self, count: [u32; 3]) {
1285        // Empty dispatches are invalid in OpenGL, but valid in WebGPU.
1286        if count.contains(&0) {
1287            return;
1288        }
1289        self.cmd_buffer.commands.push(C::Dispatch(count));
1290    }
1291    unsafe fn dispatch_workgroups_indirect(
1292        &mut self,
1293        buffer: &super::Buffer,
1294        offset: wgt::BufferAddress,
1295    ) {
1296        self.cmd_buffer.commands.push(C::DispatchIndirect {
1297            indirect_buf: buffer.raw.unwrap(),
1298            indirect_offset: offset,
1299        });
1300    }
1301
1302    unsafe fn build_acceleration_structures<'a, T>(
1303        &mut self,
1304        _descriptor_count: u32,
1305        _descriptors: T,
1306    ) where
1307        super::Api: 'a,
1308        T: IntoIterator<
1309            Item = crate::BuildAccelerationStructureDescriptor<
1310                'a,
1311                super::Buffer,
1312                super::AccelerationStructure,
1313            >,
1314        >,
1315    {
1316        unimplemented!()
1317    }
1318
1319    unsafe fn place_acceleration_structure_barrier(
1320        &mut self,
1321        _barriers: crate::AccelerationStructureBarrier,
1322    ) {
1323        unimplemented!()
1324    }
1325
1326    unsafe fn copy_acceleration_structure_to_acceleration_structure(
1327        &mut self,
1328        _src: &super::AccelerationStructure,
1329        _dst: &super::AccelerationStructure,
1330        _copy: wgt::AccelerationStructureCopy,
1331    ) {
1332        unimplemented!()
1333    }
1334
1335    unsafe fn read_acceleration_structure_compact_size(
1336        &mut self,
1337        _acceleration_structure: &super::AccelerationStructure,
1338        _buf: &super::Buffer,
1339    ) {
1340        unimplemented!()
1341    }
1342
1343    unsafe fn set_acceleration_structure_dependencies(
1344        _command_buffers: &[&super::CommandBuffer],
1345        _dependencies: &[&super::AccelerationStructure],
1346    ) {
1347        unimplemented!()
1348    }
1349
1350    unsafe fn begin_ray_tracing_pass(&mut self, _desc: &crate::RayTracingPassDescriptor) {
1351        unimplemented!()
1352    }
1353
1354    unsafe fn end_ray_tracing_pass(&mut self) {
1355        unimplemented!()
1356    }
1357
1358    unsafe fn set_ray_tracing_pipeline(
1359        &mut self,
1360        _pipeline: &<Self::A as crate::Api>::RayTracingPipeline,
1361    ) {
1362        unimplemented!()
1363    }
1364
1365    unsafe fn trace_rays(
1366        &mut self,
1367        _count: [u32; 3],
1368        _ray_generation_group_data: crate::PipelineGroupData<super::Buffer>,
1369        _miss_group_data: crate::PipelineGroupData<super::Buffer>,
1370        _intersection_group_data: crate::PipelineGroupData<super::Buffer>,
1371    ) {
1372        unimplemented!()
1373    }
1374}