Skip to main content

wgpu_hal/gles/
command.rs

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