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