li_wgpu_hal/gles/
mod.rs

1/*!
2# OpenGL ES3 API (aka GLES3).
3
4Designed to work on Linux and Android, with context provided by EGL.
5
6## Texture views
7
8GLES3 doesn't really have separate texture view objects. We have to remember the
9original texture and the sub-range into it. Problem is, however, that there is
10no way to expose a subset of array layers or mip levels of a sampled texture.
11
12## Binding model
13
14Binding model is very different from WebGPU, especially with regards to samplers.
15GLES3 has sampler objects, but they aren't separately bindable to the shaders.
16Each sampled texture is exposed to the shader as a combined texture-sampler binding.
17
18When building the pipeline layout, we linearize binding entries based on the groups
19(uniform/storage buffers, uniform/storage textures), and record the mapping into
20`BindGroupLayoutInfo`.
21When a pipeline gets created, and we track all the texture-sampler associations
22from the static use in the shader.
23We only support at most one sampler used with each texture so far. The linear index
24of this sampler is stored per texture slot in `SamplerBindMap` array.
25
26The texture-sampler pairs get potentially invalidated in 2 places:
27  - when a new pipeline is set, we update the linear indices of associated samplers
28  - when a new bind group is set, we update both the textures and the samplers
29
30We expect that the changes to sampler states between any 2 pipelines of the same layout
31will be minimal, if any.
32
33## Vertex data
34
35Generally, vertex buffers are marked as dirty and lazily bound on draw.
36
37GLES3 doesn't support "base instance" semantics. However, it's easy to support,
38since we are forced to do late binding anyway. We just adjust the offsets
39into the vertex data.
40
41### Old path
42
43In GLES-3.0 and WebGL2, vertex buffer layout is provided
44together with the actual buffer binding.
45We invalidate the attributes on the vertex buffer change, and re-bind them.
46
47### New path
48
49In GLES-3.1 and higher, the vertex buffer layout can be declared separately
50from the vertex data itself. This mostly matches WebGPU, however there is a catch:
51`stride` needs to be specified with the data, not as a part of the layout.
52
53To address this, we invalidate the vertex buffers based on:
54  - whether or not `start_instance` is used
55  - stride has changed
56
57*/
58
59///cbindgen:ignore
60#[cfg(not(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten")))))]
61mod egl;
62#[cfg(target_os = "emscripten")]
63mod emscripten;
64#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
65mod web;
66#[cfg(windows)]
67mod wgl;
68
69mod adapter;
70mod command;
71mod conv;
72mod device;
73mod queue;
74
75use crate::{CopyExtent, TextureDescriptor};
76
77#[cfg(not(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten")))))]
78pub use self::egl::{AdapterContext, AdapterContextLock};
79#[cfg(not(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten")))))]
80use self::egl::{Instance, Surface};
81
82#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
83pub use self::web::AdapterContext;
84#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
85use self::web::{Instance, Surface};
86
87#[cfg(windows)]
88use self::wgl::AdapterContext;
89#[cfg(windows)]
90use self::wgl::{Instance, Surface};
91
92use arrayvec::ArrayVec;
93
94use glow::HasContext;
95
96use naga::FastHashMap;
97use parking_lot::Mutex;
98use std::sync::atomic::AtomicU32;
99use std::{fmt, ops::Range, sync::Arc};
100
101#[derive(Clone, Debug)]
102pub struct Api;
103
104//Note: we can support more samplers if not every one of them is used at a time,
105// but it probably doesn't worth it.
106const MAX_TEXTURE_SLOTS: usize = 16;
107const MAX_SAMPLERS: usize = 16;
108const MAX_VERTEX_ATTRIBUTES: usize = 16;
109const ZERO_BUFFER_SIZE: usize = 256 << 10;
110const MAX_PUSH_CONSTANTS: usize = 64;
111
112impl crate::Api for Api {
113    type Instance = Instance;
114    type Surface = Surface;
115    type Adapter = Adapter;
116    type Device = Device;
117
118    type Queue = Queue;
119    type CommandEncoder = CommandEncoder;
120    type CommandBuffer = CommandBuffer;
121
122    type Buffer = Buffer;
123    type Texture = Texture;
124    type SurfaceTexture = Texture;
125    type TextureView = TextureView;
126    type Sampler = Sampler;
127    type QuerySet = QuerySet;
128    type Fence = Fence;
129
130    type BindGroupLayout = BindGroupLayout;
131    type BindGroup = BindGroup;
132    type PipelineLayout = PipelineLayout;
133    type ShaderModule = ShaderModule;
134    type RenderPipeline = RenderPipeline;
135    type ComputePipeline = ComputePipeline;
136}
137
138bitflags::bitflags! {
139    /// Flags that affect internal code paths but do not
140    /// change the exposed feature set.
141    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
142    struct PrivateCapabilities: u32 {
143        /// Indicates support for `glBufferStorage` allocation.
144        const BUFFER_ALLOCATION = 1 << 0;
145        /// Support explicit layouts in shader.
146        const SHADER_BINDING_LAYOUT = 1 << 1;
147        /// Support extended shadow sampling instructions.
148        const SHADER_TEXTURE_SHADOW_LOD = 1 << 2;
149        /// Support memory barriers.
150        const MEMORY_BARRIERS = 1 << 3;
151        /// Vertex buffer layouts separate from the data.
152        const VERTEX_BUFFER_LAYOUT = 1 << 4;
153        /// Indicates that buffers used as `GL_ELEMENT_ARRAY_BUFFER` may be created / initialized / used
154        /// as other targets, if not present they must not be mixed with other targets.
155        const INDEX_BUFFER_ROLE_CHANGE = 1 << 5;
156        /// Supports `glGetBufferSubData`
157        const GET_BUFFER_SUB_DATA = 1 << 7;
158        /// Supports `f16` color buffers
159        const COLOR_BUFFER_HALF_FLOAT = 1 << 8;
160        /// Supports `f11/f10` and `f32` color buffers
161        const COLOR_BUFFER_FLOAT = 1 << 9;
162        /// Supports linear flitering `f32` textures.
163        const TEXTURE_FLOAT_LINEAR = 1 << 10;
164        /// Supports query buffer objects.
165        const QUERY_BUFFERS = 1 << 11;
166    }
167}
168
169bitflags::bitflags! {
170    /// Flags that indicate necessary workarounds for specific devices or driver bugs
171    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
172    struct Workarounds: u32 {
173        // Needs workaround for Intel Mesa bug:
174        // https://gitlab.freedesktop.org/mesa/mesa/-/issues/2565.
175        //
176        // This comment
177        // (https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4972/diffs?diff_id=75888#22f5d1004713c9bbf857988c7efb81631ab88f99_323_327)
178        // seems to indicate all skylake models are effected.
179        const MESA_I915_SRGB_SHADER_CLEAR = 1 << 0;
180        /// Buffer map must emulated becuase it is not supported natively
181        const EMULATE_BUFFER_MAP = 1 << 1;
182    }
183}
184
185type BindTarget = u32;
186
187#[derive(Debug, Clone, Copy)]
188enum VertexAttribKind {
189    Float, // glVertexAttribPointer
190    Integer, // glVertexAttribIPointer
191           //Double,  // glVertexAttribLPointer
192}
193
194impl Default for VertexAttribKind {
195    fn default() -> Self {
196        Self::Float
197    }
198}
199
200#[derive(Clone, Debug)]
201pub struct TextureFormatDesc {
202    pub internal: u32,
203    pub external: u32,
204    pub data_type: u32,
205}
206
207struct AdapterShared {
208    context: AdapterContext,
209    private_caps: PrivateCapabilities,
210    features: wgt::Features,
211    workarounds: Workarounds,
212    shading_language_version: naga::back::glsl::Version,
213    max_texture_size: u32,
214    next_shader_id: AtomicU32,
215    program_cache: Mutex<ProgramCache>,
216    es: bool,
217}
218
219pub struct Adapter {
220    shared: Arc<AdapterShared>,
221}
222
223pub struct Device {
224    shared: Arc<AdapterShared>,
225    main_vao: glow::VertexArray,
226    #[cfg(all(not(target_arch = "wasm32"), feature = "renderdoc"))]
227    render_doc: crate::auxil::renderdoc::RenderDoc,
228}
229
230pub struct Queue {
231    shared: Arc<AdapterShared>,
232    features: wgt::Features,
233    draw_fbo: glow::Framebuffer,
234    copy_fbo: glow::Framebuffer,
235    /// Shader program used to clear the screen for [`Workarounds::MESA_I915_SRGB_SHADER_CLEAR`]
236    /// devices.
237    shader_clear_program: glow::Program,
238    /// The uniform location of the color uniform in the shader clear program
239    shader_clear_program_color_uniform_location: glow::UniformLocation,
240    /// Keep a reasonably large buffer filled with zeroes, so that we can implement `ClearBuffer` of
241    /// zeroes by copying from it.
242    zero_buffer: glow::Buffer,
243    temp_query_results: Vec<u64>,
244    draw_buffer_count: u8,
245    current_index_buffer: Option<glow::Buffer>,
246}
247
248#[derive(Clone, Debug)]
249pub struct Buffer {
250    raw: Option<glow::Buffer>,
251    target: BindTarget,
252    size: wgt::BufferAddress,
253    map_flags: u32,
254    data: Option<Arc<std::sync::Mutex<Vec<u8>>>>,
255}
256
257#[cfg(all(
258    target_arch = "wasm32",
259    feature = "fragile-send-sync-non-atomic-wasm",
260    not(target_feature = "atomics")
261))]
262unsafe impl Sync for Buffer {}
263#[cfg(all(
264    target_arch = "wasm32",
265    feature = "fragile-send-sync-non-atomic-wasm",
266    not(target_feature = "atomics")
267))]
268unsafe impl Send for Buffer {}
269
270#[derive(Clone, Debug)]
271pub enum TextureInner {
272    Renderbuffer {
273        raw: glow::Renderbuffer,
274    },
275    DefaultRenderbuffer,
276    Texture {
277        raw: glow::Texture,
278        target: BindTarget,
279    },
280    #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
281    ExternalFramebuffer {
282        inner: web_sys::WebGlFramebuffer,
283    },
284}
285
286#[cfg(all(
287    target_arch = "wasm32",
288    feature = "fragile-send-sync-non-atomic-wasm",
289    not(target_feature = "atomics")
290))]
291unsafe impl Sync for TextureInner {}
292#[cfg(all(
293    target_arch = "wasm32",
294    feature = "fragile-send-sync-non-atomic-wasm",
295    not(target_feature = "atomics")
296))]
297unsafe impl Send for TextureInner {}
298
299impl TextureInner {
300    fn as_native(&self) -> (glow::Texture, BindTarget) {
301        match *self {
302            Self::Renderbuffer { .. } | Self::DefaultRenderbuffer => {
303                panic!("Unexpected renderbuffer");
304            }
305            Self::Texture { raw, target } => (raw, target),
306            #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
307            Self::ExternalFramebuffer { .. } => panic!("Unexpected external framebuffer"),
308        }
309    }
310}
311
312#[derive(Debug)]
313pub struct Texture {
314    pub inner: TextureInner,
315    pub drop_guard: Option<crate::DropGuard>,
316    pub mip_level_count: u32,
317    pub array_layer_count: u32,
318    pub format: wgt::TextureFormat,
319    #[allow(unused)]
320    pub format_desc: TextureFormatDesc,
321    pub copy_size: CopyExtent,
322}
323
324impl Texture {
325    pub fn default_framebuffer(format: wgt::TextureFormat) -> Self {
326        Self {
327            inner: TextureInner::DefaultRenderbuffer,
328            drop_guard: None,
329            mip_level_count: 1,
330            array_layer_count: 1,
331            format,
332            format_desc: TextureFormatDesc {
333                internal: 0,
334                external: 0,
335                data_type: 0,
336            },
337            copy_size: CopyExtent {
338                width: 0,
339                height: 0,
340                depth: 0,
341            },
342        }
343    }
344
345    /// Returns the `target`, whether the image is 3d and whether the image is a cubemap.
346    fn get_info_from_desc(desc: &TextureDescriptor) -> u32 {
347        match desc.dimension {
348            wgt::TextureDimension::D1 => glow::TEXTURE_2D,
349            wgt::TextureDimension::D2 => {
350                // HACK: detect a cube map; forces cube compatible textures to be cube textures
351                match (desc.is_cube_compatible(), desc.size.depth_or_array_layers) {
352                    (false, 1) => glow::TEXTURE_2D,
353                    (false, _) => glow::TEXTURE_2D_ARRAY,
354                    (true, 6) => glow::TEXTURE_CUBE_MAP,
355                    (true, _) => glow::TEXTURE_CUBE_MAP_ARRAY,
356                }
357            }
358            wgt::TextureDimension::D3 => glow::TEXTURE_3D,
359        }
360    }
361}
362
363#[derive(Clone, Debug)]
364pub struct TextureView {
365    inner: TextureInner,
366    aspects: crate::FormatAspects,
367    mip_levels: Range<u32>,
368    array_layers: Range<u32>,
369    format: wgt::TextureFormat,
370}
371
372#[derive(Debug)]
373pub struct Sampler {
374    raw: glow::Sampler,
375}
376
377#[derive(Debug)]
378pub struct BindGroupLayout {
379    entries: Arc<[wgt::BindGroupLayoutEntry]>,
380}
381
382struct BindGroupLayoutInfo {
383    entries: Arc<[wgt::BindGroupLayoutEntry]>,
384    /// Mapping of resources, indexed by `binding`, into the whole layout space.
385    /// For texture resources, the value is the texture slot index.
386    /// For sampler resources, the value is the index of the sampler in the whole layout.
387    /// For buffers, the value is the uniform or storage slot index.
388    /// For unused bindings, the value is `!0`
389    binding_to_slot: Box<[u8]>,
390}
391
392pub struct PipelineLayout {
393    group_infos: Box<[BindGroupLayoutInfo]>,
394    naga_options: naga::back::glsl::Options,
395}
396
397impl PipelineLayout {
398    fn get_slot(&self, br: &naga::ResourceBinding) -> u8 {
399        let group_info = &self.group_infos[br.group as usize];
400        group_info.binding_to_slot[br.binding as usize]
401    }
402}
403
404#[derive(Debug)]
405enum BindingRegister {
406    UniformBuffers,
407    StorageBuffers,
408    Textures,
409    Images,
410}
411
412#[derive(Debug)]
413enum RawBinding {
414    Buffer {
415        raw: glow::Buffer,
416        offset: i32,
417        size: i32,
418    },
419    Texture {
420        raw: glow::Texture,
421        target: BindTarget,
422        aspects: crate::FormatAspects,
423        //TODO: mip levels, array layers
424    },
425    Image(ImageBinding),
426    Sampler(glow::Sampler),
427}
428
429#[derive(Debug)]
430pub struct BindGroup {
431    contents: Box<[RawBinding]>,
432}
433
434type ShaderId = u32;
435
436#[derive(Debug)]
437pub struct ShaderModule {
438    naga: crate::NagaShader,
439    label: Option<String>,
440    id: ShaderId,
441}
442
443#[derive(Clone, Debug, Default)]
444struct VertexFormatDesc {
445    element_count: i32,
446    element_format: u32,
447    attrib_kind: VertexAttribKind,
448}
449
450#[derive(Clone, Debug, Default)]
451struct AttributeDesc {
452    location: u32,
453    offset: u32,
454    buffer_index: u32,
455    format_desc: VertexFormatDesc,
456}
457
458#[derive(Clone, Debug)]
459struct BufferBinding {
460    raw: glow::Buffer,
461    offset: wgt::BufferAddress,
462}
463
464#[derive(Clone, Debug)]
465struct ImageBinding {
466    raw: glow::Texture,
467    mip_level: u32,
468    array_layer: Option<u32>,
469    access: u32,
470    format: u32,
471}
472
473#[derive(Clone, Debug, Default, PartialEq)]
474struct VertexBufferDesc {
475    step: wgt::VertexStepMode,
476    stride: u32,
477}
478
479#[derive(Clone, Debug, Default)]
480struct UniformDesc {
481    location: Option<glow::UniformLocation>,
482    size: u32,
483    utype: u32,
484}
485
486#[cfg(all(
487    target_arch = "wasm32",
488    feature = "fragile-send-sync-non-atomic-wasm",
489    not(target_feature = "atomics")
490))]
491unsafe impl Sync for UniformDesc {}
492#[cfg(all(
493    target_arch = "wasm32",
494    feature = "fragile-send-sync-non-atomic-wasm",
495    not(target_feature = "atomics")
496))]
497unsafe impl Send for UniformDesc {}
498
499/// For each texture in the pipeline layout, store the index of the only
500/// sampler (in this layout) that the texture is used with.
501type SamplerBindMap = [Option<u8>; MAX_TEXTURE_SLOTS];
502
503struct PipelineInner {
504    program: glow::Program,
505    sampler_map: SamplerBindMap,
506    uniforms: [UniformDesc; MAX_PUSH_CONSTANTS],
507}
508
509#[derive(Clone, Debug)]
510struct DepthState {
511    function: u32,
512    mask: bool,
513}
514
515#[derive(Clone, Debug, PartialEq)]
516struct BlendComponent {
517    src: u32,
518    dst: u32,
519    equation: u32,
520}
521
522#[derive(Clone, Debug, PartialEq)]
523struct BlendDesc {
524    alpha: BlendComponent,
525    color: BlendComponent,
526}
527
528#[derive(Clone, Debug, Default, PartialEq)]
529struct ColorTargetDesc {
530    mask: wgt::ColorWrites,
531    blend: Option<BlendDesc>,
532}
533
534#[derive(PartialEq, Eq, Hash)]
535struct ProgramStage {
536    naga_stage: naga::ShaderStage,
537    shader_id: ShaderId,
538    entry_point: String,
539}
540
541#[derive(PartialEq, Eq, Hash)]
542struct ProgramCacheKey {
543    stages: ArrayVec<ProgramStage, 3>,
544    group_to_binding_to_slot: Box<[Box<[u8]>]>,
545}
546
547type ProgramCache = FastHashMap<ProgramCacheKey, Result<Arc<PipelineInner>, crate::PipelineError>>;
548
549pub struct RenderPipeline {
550    inner: Arc<PipelineInner>,
551    primitive: wgt::PrimitiveState,
552    vertex_buffers: Box<[VertexBufferDesc]>,
553    vertex_attributes: Box<[AttributeDesc]>,
554    color_targets: Box<[ColorTargetDesc]>,
555    depth: Option<DepthState>,
556    depth_bias: wgt::DepthBiasState,
557    stencil: Option<StencilState>,
558    alpha_to_coverage_enabled: bool,
559}
560
561#[cfg(all(
562    target_arch = "wasm32",
563    feature = "fragile-send-sync-non-atomic-wasm",
564    not(target_feature = "atomics")
565))]
566unsafe impl Sync for RenderPipeline {}
567#[cfg(all(
568    target_arch = "wasm32",
569    feature = "fragile-send-sync-non-atomic-wasm",
570    not(target_feature = "atomics")
571))]
572unsafe impl Send for RenderPipeline {}
573
574pub struct ComputePipeline {
575    inner: Arc<PipelineInner>,
576}
577
578#[cfg(all(
579    target_arch = "wasm32",
580    feature = "fragile-send-sync-non-atomic-wasm",
581    not(target_feature = "atomics")
582))]
583unsafe impl Sync for ComputePipeline {}
584#[cfg(all(
585    target_arch = "wasm32",
586    feature = "fragile-send-sync-non-atomic-wasm",
587    not(target_feature = "atomics")
588))]
589unsafe impl Send for ComputePipeline {}
590
591#[derive(Debug)]
592pub struct QuerySet {
593    queries: Box<[glow::Query]>,
594    target: BindTarget,
595}
596
597#[derive(Debug)]
598pub struct Fence {
599    last_completed: crate::FenceValue,
600    pending: Vec<(crate::FenceValue, glow::Fence)>,
601}
602
603#[cfg(any(
604    not(target_arch = "wasm32"),
605    all(
606        feature = "fragile-send-sync-non-atomic-wasm",
607        not(target_feature = "atomics")
608    )
609))]
610unsafe impl Send for Fence {}
611#[cfg(any(
612    not(target_arch = "wasm32"),
613    all(
614        feature = "fragile-send-sync-non-atomic-wasm",
615        not(target_feature = "atomics")
616    )
617))]
618unsafe impl Sync for Fence {}
619
620impl Fence {
621    fn get_latest(&self, gl: &glow::Context) -> crate::FenceValue {
622        let mut max_value = self.last_completed;
623        for &(value, sync) in self.pending.iter() {
624            let status = unsafe { gl.get_sync_status(sync) };
625            if status == glow::SIGNALED {
626                max_value = value;
627            }
628        }
629        max_value
630    }
631
632    fn maintain(&mut self, gl: &glow::Context) {
633        let latest = self.get_latest(gl);
634        for &(value, sync) in self.pending.iter() {
635            if value <= latest {
636                unsafe {
637                    gl.delete_sync(sync);
638                }
639            }
640        }
641        self.pending.retain(|&(value, _)| value > latest);
642        self.last_completed = latest;
643    }
644}
645
646#[derive(Clone, Debug, PartialEq)]
647struct StencilOps {
648    pass: u32,
649    fail: u32,
650    depth_fail: u32,
651}
652
653impl Default for StencilOps {
654    fn default() -> Self {
655        Self {
656            pass: glow::KEEP,
657            fail: glow::KEEP,
658            depth_fail: glow::KEEP,
659        }
660    }
661}
662
663#[derive(Clone, Debug, PartialEq)]
664struct StencilSide {
665    function: u32,
666    mask_read: u32,
667    mask_write: u32,
668    reference: u32,
669    ops: StencilOps,
670}
671
672impl Default for StencilSide {
673    fn default() -> Self {
674        Self {
675            function: glow::ALWAYS,
676            mask_read: 0xFF,
677            mask_write: 0xFF,
678            reference: 0,
679            ops: StencilOps::default(),
680        }
681    }
682}
683
684#[derive(Clone, Default)]
685struct StencilState {
686    front: StencilSide,
687    back: StencilSide,
688}
689
690#[derive(Clone, Debug, Default, PartialEq)]
691struct PrimitiveState {
692    front_face: u32,
693    cull_face: u32,
694    unclipped_depth: bool,
695}
696
697type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>;
698
699#[derive(Debug)]
700enum Command {
701    Draw {
702        topology: u32,
703        start_vertex: u32,
704        vertex_count: u32,
705        instance_count: u32,
706    },
707    DrawIndexed {
708        topology: u32,
709        index_type: u32,
710        index_count: u32,
711        index_offset: wgt::BufferAddress,
712        base_vertex: i32,
713        instance_count: u32,
714    },
715    DrawIndirect {
716        topology: u32,
717        indirect_buf: glow::Buffer,
718        indirect_offset: wgt::BufferAddress,
719    },
720    DrawIndexedIndirect {
721        topology: u32,
722        index_type: u32,
723        indirect_buf: glow::Buffer,
724        indirect_offset: wgt::BufferAddress,
725    },
726    Dispatch([u32; 3]),
727    DispatchIndirect {
728        indirect_buf: glow::Buffer,
729        indirect_offset: wgt::BufferAddress,
730    },
731    ClearBuffer {
732        dst: Buffer,
733        dst_target: BindTarget,
734        range: crate::MemoryRange,
735    },
736    CopyBufferToBuffer {
737        src: Buffer,
738        src_target: BindTarget,
739        dst: Buffer,
740        dst_target: BindTarget,
741        copy: crate::BufferCopy,
742    },
743    #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
744    CopyExternalImageToTexture {
745        src: wgt::ImageCopyExternalImage,
746        dst: glow::Texture,
747        dst_target: BindTarget,
748        dst_format: wgt::TextureFormat,
749        dst_premultiplication: bool,
750        copy: crate::TextureCopy,
751    },
752    CopyTextureToTexture {
753        src: glow::Texture,
754        src_target: BindTarget,
755        dst: glow::Texture,
756        dst_target: BindTarget,
757        copy: crate::TextureCopy,
758    },
759    CopyBufferToTexture {
760        src: Buffer,
761        #[allow(unused)]
762        src_target: BindTarget,
763        dst: glow::Texture,
764        dst_target: BindTarget,
765        dst_format: wgt::TextureFormat,
766        copy: crate::BufferTextureCopy,
767    },
768    CopyTextureToBuffer {
769        src: glow::Texture,
770        src_target: BindTarget,
771        src_format: wgt::TextureFormat,
772        dst: Buffer,
773        #[allow(unused)]
774        dst_target: BindTarget,
775        copy: crate::BufferTextureCopy,
776    },
777    SetIndexBuffer(glow::Buffer),
778    BeginQuery(glow::Query, BindTarget),
779    EndQuery(BindTarget),
780    TimestampQuery(glow::Query),
781    CopyQueryResults {
782        query_range: Range<u32>,
783        dst: Buffer,
784        dst_target: BindTarget,
785        dst_offset: wgt::BufferAddress,
786    },
787    ResetFramebuffer {
788        is_default: bool,
789    },
790    BindAttachment {
791        attachment: u32,
792        view: TextureView,
793    },
794    ResolveAttachment {
795        attachment: u32,
796        dst: TextureView,
797        size: wgt::Extent3d,
798    },
799    InvalidateAttachments(InvalidatedAttachments),
800    SetDrawColorBuffers(u8),
801    ClearColorF {
802        draw_buffer: u32,
803        color: [f32; 4],
804        is_srgb: bool,
805    },
806    ClearColorU(u32, [u32; 4]),
807    ClearColorI(u32, [i32; 4]),
808    ClearDepth(f32),
809    ClearStencil(u32),
810    // Clearing both the depth and stencil buffer individually appears to
811    // result in the stencil buffer failing to clear, atleast in WebGL.
812    // It is also more efficient to emit a single command instead of two for
813    // this.
814    ClearDepthAndStencil(f32, u32),
815    BufferBarrier(glow::Buffer, crate::BufferUses),
816    TextureBarrier(crate::TextureUses),
817    SetViewport {
818        rect: crate::Rect<i32>,
819        depth: Range<f32>,
820    },
821    SetScissor(crate::Rect<i32>),
822    SetStencilFunc {
823        face: u32,
824        function: u32,
825        reference: u32,
826        read_mask: u32,
827    },
828    SetStencilOps {
829        face: u32,
830        write_mask: u32,
831        ops: StencilOps,
832    },
833    SetDepth(DepthState),
834    SetDepthBias(wgt::DepthBiasState),
835    ConfigureDepthStencil(crate::FormatAspects),
836    SetAlphaToCoverage(bool),
837    SetVertexAttribute {
838        buffer: Option<glow::Buffer>,
839        buffer_desc: VertexBufferDesc,
840        attribute_desc: AttributeDesc,
841    },
842    UnsetVertexAttribute(u32),
843    SetVertexBuffer {
844        index: u32,
845        buffer: BufferBinding,
846        buffer_desc: VertexBufferDesc,
847    },
848    SetProgram(glow::Program),
849    SetPrimitive(PrimitiveState),
850    SetBlendConstant([f32; 4]),
851    SetColorTarget {
852        draw_buffer_index: Option<u32>,
853        desc: ColorTargetDesc,
854    },
855    BindBuffer {
856        target: BindTarget,
857        slot: u32,
858        buffer: glow::Buffer,
859        offset: i32,
860        size: i32,
861    },
862    BindSampler(u32, Option<glow::Sampler>),
863    BindTexture {
864        slot: u32,
865        texture: glow::Texture,
866        target: BindTarget,
867        aspects: crate::FormatAspects,
868    },
869    BindImage {
870        slot: u32,
871        binding: ImageBinding,
872    },
873    InsertDebugMarker(Range<u32>),
874    PushDebugGroup(Range<u32>),
875    PopDebugGroup,
876    SetPushConstants {
877        uniform: UniformDesc,
878        /// Offset from the start of the `data_bytes`
879        offset: u32,
880    },
881}
882
883#[derive(Default)]
884pub struct CommandBuffer {
885    label: Option<String>,
886    commands: Vec<Command>,
887    data_bytes: Vec<u8>,
888    queries: Vec<glow::Query>,
889}
890
891impl fmt::Debug for CommandBuffer {
892    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
893        let mut builder = f.debug_struct("CommandBuffer");
894        if let Some(ref label) = self.label {
895            builder.field("label", label);
896        }
897        builder.finish()
898    }
899}
900
901//TODO: we would have something like `Arc<typed_arena::Arena>`
902// here and in the command buffers. So that everything grows
903// inside the encoder and stays there until `reset_all`.
904
905pub struct CommandEncoder {
906    cmd_buffer: CommandBuffer,
907    state: command::State,
908    private_caps: PrivateCapabilities,
909}
910
911impl fmt::Debug for CommandEncoder {
912    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
913        f.debug_struct("CommandEncoder")
914            .field("cmd_buffer", &self.cmd_buffer)
915            .finish()
916    }
917}
918
919#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
920fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
921    let source_str = match source {
922        glow::DEBUG_SOURCE_API => "API",
923        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
924        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
925        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
926        glow::DEBUG_SOURCE_APPLICATION => "Application",
927        glow::DEBUG_SOURCE_OTHER => "Other",
928        _ => unreachable!(),
929    };
930
931    let log_severity = match severity {
932        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
933        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
934        glow::DEBUG_SEVERITY_LOW => log::Level::Info,
935        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
936        _ => unreachable!(),
937    };
938
939    let type_str = match gltype {
940        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
941        glow::DEBUG_TYPE_ERROR => "Error",
942        glow::DEBUG_TYPE_MARKER => "Marker",
943        glow::DEBUG_TYPE_OTHER => "Other",
944        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
945        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
946        glow::DEBUG_TYPE_PORTABILITY => "Portability",
947        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
948        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
949        _ => unreachable!(),
950    };
951
952    let _ = std::panic::catch_unwind(|| {
953        log::log!(
954            log_severity,
955            "GLES: [{}/{}] ID {} : {}",
956            source_str,
957            type_str,
958            id,
959            message
960        );
961    });
962
963    if cfg!(debug_assertions) && log_severity == log::Level::Error {
964        // Set canary and continue
965        crate::VALIDATION_CANARY.set();
966    }
967}