Skip to main content

wgpu_hal/gles/
mod.rs

1/*!
2# OpenGL ES3 API (aka GLES3).
3
4Designed to work on platforms with context provided by EGL or WGL, including
5Linux and Android via EGL, and Windows via WGL by default or ANGLE/EGL with
6`cfg(windows_angle)`.
7
8## Texture views
9
10GLES3 doesn't really have separate texture view objects. We have to remember the
11original texture and the sub-range into it. Problem is, however, that there is
12no way to expose a subset of array layers or mip levels of a sampled texture.
13
14## Binding model
15
16Binding model is very different from WebGPU, especially with regards to samplers.
17GLES3 has sampler objects, but they aren't separately bindable to the shaders.
18Each sampled texture is exposed to the shader as a combined texture-sampler binding.
19
20When building the pipeline layout, we linearize binding entries based on the groups
21(uniform/storage buffers, uniform/storage textures), and record the mapping into
22`BindGroupLayoutInfo`.
23When a pipeline gets created, and we track all the texture-sampler associations
24from the static use in the shader.
25We only support at most one sampler used with each texture so far. The linear index
26of this sampler is stored per texture slot in `SamplerBindMap` array.
27
28The texture-sampler pairs get potentially invalidated in 2 places:
29  - when a new pipeline is set, we update the linear indices of associated samplers
30  - when a new bind group is set, we update both the textures and the samplers
31
32We expect that the changes to sampler states between any 2 pipelines of the same layout
33will be minimal, if any.
34
35## Vertex data
36
37Generally, vertex buffers are marked as dirty and lazily bound on draw.
38
39GLES3 doesn't support `first_instance` semantics. However, it's easy to support,
40since we are forced to do late binding anyway. We just adjust the offsets
41into the vertex data.
42
43### Old path
44
45In GLES-3.0 and WebGL2, vertex buffer layout is provided
46together with the actual buffer binding.
47We invalidate the attributes on the vertex buffer change, and re-bind them.
48
49### New path
50
51In GLES-3.1 and higher, the vertex buffer layout can be declared separately
52from the vertex data itself. This mostly matches WebGPU, however there is a catch:
53`stride` needs to be specified with the data, not as a part of the layout.
54
55To address this, we invalidate the vertex buffers based on:
56  - whether or not `first_instance` is used
57  - stride has changed
58
59## Handling of `base_vertex`, `first_instance`, and `first_vertex`
60
61Between indirect, the lack of `first_instance` semantics, and the availability of `gl_BaseInstance`
62in shaders, getting buffers and builtins to work correctly is a bit tricky.
63
64We never emulate `base_vertex` and gl_VertexID behaves as `@builtin(vertex_index)` does, so we
65never need to do anything about that.
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
84mod adapter;
85mod command;
86mod conv;
87mod device;
88///cbindgen:ignore
89#[cfg(all(not(webgl), any(not(windows), windows_angle)))]
90mod egl;
91#[cfg(all(not(webgl), any(not(windows), windows_angle)))]
92pub use self::egl::{AdapterContext, AdapterContextLock, Instance, Surface};
93
94#[cfg(Emscripten)]
95mod emscripten;
96
97mod fence;
98mod queue;
99
100#[cfg(webgl)]
101mod web;
102#[cfg(webgl)]
103pub use self::web::{AdapterContext, Instance, Surface};
104
105#[cfg(all(windows, not(webgl), not(windows_angle)))]
106mod wgl;
107#[cfg(all(windows, not(webgl), not(windows_angle)))]
108pub use self::wgl::{AdapterContext, AdapterContextLock, Instance, Surface};
109
110pub use fence::Fence;
111
112use alloc::{boxed::Box, string::String, string::ToString as _, sync::Arc, vec::Vec};
113use core::{fmt, ops::Range};
114use wgpu_sync::{
115    atomic::{AtomicU32, AtomicU8},
116    Mutex,
117};
118
119use arrayvec::ArrayVec;
120use glow::HasContext;
121use naga::FastHashMap;
122
123use crate::{CopyExtent, TextureDescriptor};
124
125#[derive(Clone, Debug)]
126pub struct Api;
127
128//Note: we can support more samplers if not every one of them is used at a time,
129// but it probably doesn't worth it.
130const MAX_TEXTURE_SLOTS: usize = 16;
131const MAX_SAMPLERS: usize = 16;
132const MAX_VERTEX_ATTRIBUTES: usize = 16;
133const ZERO_BUFFER_SIZE: usize = 256 << 10;
134const MAX_IMMEDIATES: usize = 64;
135// We have to account for each immediate data may need to be set for every shader.
136const MAX_IMMEDIATES_COMMANDS: usize = MAX_IMMEDIATES * crate::MAX_CONCURRENT_SHADER_STAGES;
137
138impl crate::Api for Api {
139    const VARIANT: wgt::Backend = wgt::Backend::Gl;
140
141    type Instance = Instance;
142    type Surface = Surface;
143    type Adapter = Adapter;
144    type Device = Device;
145
146    type Queue = Queue;
147    type CommandEncoder = CommandEncoder;
148    type CommandBuffer = CommandBuffer;
149
150    type Buffer = Buffer;
151    type Texture = Texture;
152    type SurfaceTexture = Texture;
153    type TextureView = TextureView;
154    type Sampler = Sampler;
155    type QuerySet = QuerySet;
156    type Fence = Fence;
157    type AccelerationStructure = AccelerationStructure;
158    type PipelineCache = PipelineCache;
159
160    type BindGroupLayout = BindGroupLayout;
161    type BindGroup = BindGroup;
162    type PipelineLayout = PipelineLayout;
163    type ShaderModule = ShaderModule;
164    type RenderPipeline = RenderPipeline;
165    type ComputePipeline = ComputePipeline;
166    type RayTracingPipeline = RayTracingPipeline;
167}
168
169crate::impl_dyn_resource!(
170    Adapter,
171    AccelerationStructure,
172    BindGroup,
173    BindGroupLayout,
174    Buffer,
175    CommandBuffer,
176    CommandEncoder,
177    ComputePipeline,
178    Device,
179    Fence,
180    Instance,
181    PipelineCache,
182    PipelineLayout,
183    QuerySet,
184    Queue,
185    RenderPipeline,
186    RayTracingPipeline,
187    Sampler,
188    ShaderModule,
189    Surface,
190    Texture,
191    TextureView
192);
193
194bitflags::bitflags! {
195    /// Flags that affect internal code paths but do not
196    /// change the exposed feature set.
197    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
198    struct PrivateCapabilities: u32 {
199        /// Indicates support for `glBufferStorage` allocation.
200        const BUFFER_ALLOCATION = 1 << 0;
201        /// Support explicit layouts in shader.
202        const SHADER_BINDING_LAYOUT = 1 << 1;
203        /// Support extended shadow sampling instructions.
204        const SHADER_TEXTURE_SHADOW_LOD = 1 << 2;
205        /// Support memory barriers.
206        const MEMORY_BARRIERS = 1 << 3;
207        /// Vertex buffer layouts separate from the data.
208        const VERTEX_BUFFER_LAYOUT = 1 << 4;
209        /// Indicates that buffers used as `GL_ELEMENT_ARRAY_BUFFER` may be created / initialized / used
210        /// as other targets, if not present they must not be mixed with other targets.
211        const INDEX_BUFFER_ROLE_CHANGE = 1 << 5;
212        /// Supports `glGetBufferSubData`
213        const GET_BUFFER_SUB_DATA = 1 << 7;
214        /// Supports `f16` color buffers
215        const COLOR_BUFFER_HALF_FLOAT = 1 << 8;
216        /// Supports `f11/f10` and `f32` color buffers
217        const COLOR_BUFFER_FLOAT = 1 << 9;
218        /// Supports query buffer objects.
219        const QUERY_BUFFERS = 1 << 11;
220        /// Supports 64 bit queries via `glGetQueryObjectui64v`
221        const QUERY_64BIT = 1 << 12;
222        /// Supports `glTexStorage2D`, etc.
223        const TEXTURE_STORAGE = 1 << 13;
224        /// Supports `push_debug_group`, `pop_debug_group` and `debug_message_insert`.
225        const DEBUG_FNS = 1 << 14;
226        /// Supports framebuffer invalidation.
227        const INVALIDATE_FRAMEBUFFER = 1 << 15;
228        /// Indicates support for `glDrawElementsInstancedBaseVertexBaseInstance` and `ARB_shader_draw_parameters`
229        ///
230        /// When this is true, instance offset emulation via vertex buffer rebinding and a shader uniform will be disabled.
231        const FULLY_FEATURED_INSTANCING = 1 << 16;
232        /// Supports direct multisampled rendering to a texture without needing a resolve texture.
233        const MULTISAMPLED_RENDER_TO_TEXTURE = 1 << 17;
234        /// Supports norm16 sized internal formats as filterable sampled
235        /// textures, with UNORM variants also color-renderable. SNORM
236        /// renderability is gated on `TEXTURE_FORMAT_SNORM16_RENDERABLE`.
237        const TEXTURE_FORMAT_NORM16 = 1 << 18;
238        /// Supports SNORM 16-bit formats as color attachments. Requires
239        /// `GL_EXT_render_snorm` (in addition to `GL_EXT_texture_norm16`
240        /// on GLES) - desktop GL alone only "optionally" renders SNORM 16.
241        const TEXTURE_FORMAT_SNORM16_RENDERABLE = 1 << 19;
242        /// Supports norm16 sized internal formats as image-load/store targets.
243        /// Desktop GL >= 4.2 (core image-format list) or pre-4.2 with
244        /// `GL_ARB_shader_image_load_store`; GLES needs `GL_NV_image_formats`
245        /// (which itself depends on `GL_EXT_texture_norm16`).
246        const TEXTURE_FORMAT_NORM16_STORAGE = 1 << 20;
247    }
248}
249
250bitflags::bitflags! {
251    /// Flags that indicate necessary workarounds for specific devices or driver bugs
252    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
253    struct Workarounds: u32 {
254        // Needs workaround for Intel Mesa bug:
255        // https://gitlab.freedesktop.org/mesa/mesa/-/issues/2565.
256        //
257        // This comment
258        // (https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4972/diffs?diff_id=75888#22f5d1004713c9bbf857988c7efb81631ab88f99_323_327)
259        // seems to indicate all skylake models are effected.
260        const MESA_I915_SRGB_SHADER_CLEAR = 1 << 0;
261        /// Buffer map must emulated because it is not supported natively
262        const EMULATE_BUFFER_MAP = 1 << 1;
263    }
264}
265
266type BindTarget = u32;
267
268#[derive(Debug, Default, Clone, Copy)]
269enum VertexAttribKind {
270    #[default]
271    Float, // glVertexAttribPointer
272    Integer, // glVertexAttribIPointer
273             //Double,  // glVertexAttribLPointer
274}
275
276#[derive(Clone, Debug)]
277pub struct TextureFormatDesc {
278    pub internal: u32,
279    pub external: u32,
280    pub data_type: u32,
281}
282
283struct AdapterShared {
284    context: AdapterContext,
285    private_caps: PrivateCapabilities,
286    features: wgt::Features,
287    limits: wgt::Limits,
288    workarounds: Workarounds,
289    options: wgt::GlBackendOptions,
290    shading_language_version: naga::back::glsl::Version,
291    next_shader_id: AtomicU32,
292    program_cache: Mutex<ProgramCache>,
293    es: bool,
294
295    /// Result of `gl.get_parameter_i32(glow::MAX_SAMPLES)`.
296    /// Cached here so it doesn't need to be queried every time texture format capabilities are requested.
297    /// (this has been shown to be a significant enough overhead)
298    max_msaa_samples: i32,
299}
300
301impl fmt::Debug for AdapterShared {
302    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303        let Self {
304            context: _, // may or may not implement Debug depending on platform
305            private_caps,
306            features,
307            limits,
308            workarounds,
309            options,
310            shading_language_version,
311            next_shader_id,
312            program_cache: _,
313            es,
314            max_msaa_samples,
315        } = self;
316        f.debug_struct("AdapterShared")
317            .field("private_caps", private_caps)
318            .field("features", features)
319            .field("limits", limits)
320            .field("workarounds", workarounds)
321            .field("options", options)
322            .field("shading_language_version", shading_language_version)
323            .field("next_shader_id", next_shader_id)
324            .field("es", es)
325            .field("max_msaa_samples", max_msaa_samples)
326            .finish_non_exhaustive()
327    }
328}
329
330#[derive(Debug)]
331pub struct Adapter {
332    shared: Arc<AdapterShared>,
333}
334
335#[derive(Debug)]
336pub struct Device {
337    shared: Arc<AdapterShared>,
338    main_vao: glow::VertexArray,
339    #[cfg(all(native, feature = "renderdoc"))]
340    render_doc: crate::auxil::renderdoc::RenderDoc,
341    counters: Arc<wgt::HalCounters>,
342}
343
344impl Drop for Device {
345    fn drop(&mut self) {
346        let gl = &self.shared.context.lock();
347        unsafe { gl.delete_vertex_array(self.main_vao) };
348    }
349}
350
351#[derive(Debug)]
352pub struct ShaderClearProgram {
353    pub program: glow::Program,
354    pub color_uniform_location: glow::UniformLocation,
355}
356
357#[derive(Debug)]
358pub struct Queue {
359    shared: Arc<AdapterShared>,
360    features: wgt::Features,
361    draw_fbo: glow::Framebuffer,
362    copy_fbo: glow::Framebuffer,
363    /// Shader program used to clear the screen for [`Workarounds::MESA_I915_SRGB_SHADER_CLEAR`]
364    /// devices.
365    shader_clear_program: Option<ShaderClearProgram>,
366    /// Keep a reasonably large buffer filled with zeroes, so that we can implement `ClearBuffer` of
367    /// zeroes by copying from it.
368    zero_buffer: glow::Buffer,
369    temp_query_results: Mutex<Vec<u64>>,
370    draw_buffer_count: AtomicU8,
371    current_index_buffer: Mutex<Option<glow::Buffer>>,
372}
373
374impl Drop for Queue {
375    fn drop(&mut self) {
376        let gl = &self.shared.context.lock();
377        unsafe { gl.delete_framebuffer(self.draw_fbo) };
378        unsafe { gl.delete_framebuffer(self.copy_fbo) };
379        unsafe { gl.delete_buffer(self.zero_buffer) };
380    }
381}
382
383#[derive(Clone, Debug)]
384pub struct Buffer {
385    raw: Option<glow::Buffer>,
386    target: BindTarget,
387    /// Flags to use within calls to [`Device::map_buffer`](crate::Device::map_buffer).
388    map_flags: u32,
389    /// Buffer mapping state.
390    ///
391    /// If locked concurrently with the GL context, the GL context should be locked first.
392    map_state: Arc<Mutex<BufferMapState>>,
393    /// Set when the buffer wraps an externally-owned GL name created via
394    /// [`Device::buffer_from_raw`](crate::gles::Device::buffer_from_raw).
395    ///
396    /// `Buffer` is `Clone`, so the guard is shared via `Arc`
397    /// and only fires its callback once every clone is dropped.
398    drop_guard: Option<Arc<crate::DropGuard>>,
399}
400
401#[derive(Clone, Debug)]
402struct BufferMapState {
403    /// True if the GL buffer is actually mapped, i.e. not "fake-mapped" with
404    /// an empty slice
405    mapped: bool,
406    data: Option<Vec<u8>>,
407    offset_of_current_mapping: wgt::BufferAddress,
408}
409
410#[cfg(send_sync)]
411static_assertions::assert_impl_all!(Buffer: Send, Sync);
412
413impl crate::DynBuffer for Buffer {}
414
415#[derive(Clone, Debug)]
416pub enum TextureInner {
417    Renderbuffer {
418        raw: glow::Renderbuffer,
419    },
420    DefaultRenderbuffer,
421    Texture {
422        raw: glow::Texture,
423        target: BindTarget,
424    },
425    #[cfg(webgl)]
426    /// Render to a `WebGLFramebuffer`
427    ///
428    /// This is a web feature
429    ExternalFramebuffer {
430        inner: web_sys::WebGlFramebuffer,
431    },
432    #[cfg(native)]
433    /// Render to a `glow::NativeFramebuffer`
434    /// Useful when the framebuffer to draw to
435    /// has a non-zero framebuffer ID
436    ///
437    /// This is a native feature
438    ExternalNativeFramebuffer {
439        inner: glow::NativeFramebuffer,
440    },
441}
442
443#[cfg(send_sync)]
444unsafe impl Sync for TextureInner {}
445#[cfg(send_sync)]
446unsafe impl Send for TextureInner {}
447
448impl TextureInner {
449    fn as_native(&self) -> (glow::Texture, BindTarget) {
450        match *self {
451            Self::Renderbuffer { .. } | Self::DefaultRenderbuffer => {
452                panic!("Unexpected renderbuffer");
453            }
454            Self::Texture { raw, target } => (raw, target),
455            #[cfg(webgl)]
456            Self::ExternalFramebuffer { .. } => panic!("Unexpected external framebuffer"),
457            #[cfg(native)]
458            Self::ExternalNativeFramebuffer { .. } => panic!("unexpected external framebuffer"),
459        }
460    }
461}
462
463#[derive(Debug)]
464pub struct Texture {
465    pub inner: TextureInner,
466    pub mip_level_count: u32,
467    pub array_layer_count: u32,
468    pub format: wgt::TextureFormat,
469    pub format_desc: TextureFormatDesc,
470    pub copy_size: CopyExtent,
471
472    /// `Some` marks the underlying GL object as externally owned: wgpu-hal
473    /// never deletes it (the guard's callback, if any, fires instead).
474    ///
475    /// On WebGL every handle also holds a slot in glow's resource tracker.
476    /// `destroy_texture` always releases that slot: by deleting the texture
477    /// when we own it, or by `unregister_external_texture` when we don't.
478    ///
479    /// The `drop_guard` field must be the last field of this struct so it is
480    /// dropped last. Do not add new fields after it.
481    pub drop_guard: Option<crate::DropGuard>,
482}
483
484impl crate::DynTexture for Texture {}
485impl crate::DynSurfaceTexture for Texture {}
486
487impl core::borrow::Borrow<dyn crate::DynTexture> for Texture {
488    fn borrow(&self) -> &dyn crate::DynTexture {
489        self
490    }
491}
492
493impl Texture {
494    pub fn default_framebuffer(format: wgt::TextureFormat) -> Self {
495        Self {
496            inner: TextureInner::DefaultRenderbuffer,
497            drop_guard: None,
498            mip_level_count: 1,
499            array_layer_count: 1,
500            format,
501            format_desc: TextureFormatDesc {
502                internal: 0,
503                external: 0,
504                data_type: 0,
505            },
506            copy_size: CopyExtent {
507                width: 0,
508                height: 0,
509                depth: 0,
510            },
511        }
512    }
513
514    /// Returns the `target`, whether the image is 3d and whether the image is a cubemap.
515    fn get_info_from_desc(desc: &TextureDescriptor) -> u32 {
516        match desc.dimension {
517            // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are
518            // doing `TEXTURE_2D` instead
519            wgt::TextureDimension::D1 => glow::TEXTURE_2D,
520            wgt::TextureDimension::D2 => {
521                // HACK: detect a cube map; forces cube compatible textures to be cube textures
522                match (desc.is_cube_compatible(), desc.size.depth_or_array_layers) {
523                    (false, 1) => glow::TEXTURE_2D,
524                    (false, _) => glow::TEXTURE_2D_ARRAY,
525                    (true, 6) => glow::TEXTURE_CUBE_MAP,
526                    (true, _) => glow::TEXTURE_CUBE_MAP_ARRAY,
527                }
528            }
529            wgt::TextureDimension::D3 => glow::TEXTURE_3D,
530        }
531    }
532
533    /// GL bind target corresponding to a view dimension.
534    ///
535    /// 1D collapses to `TEXTURE_2D`: WebGL (1 and 2) as well as some GLES
536    /// versions do not have 1D textures.
537    fn target_for_view_dimension(view_dimension: wgt::TextureViewDimension) -> BindTarget {
538        match view_dimension {
539            wgt::TextureViewDimension::D1 | wgt::TextureViewDimension::D2 => glow::TEXTURE_2D,
540            wgt::TextureViewDimension::D2Array => glow::TEXTURE_2D_ARRAY,
541            wgt::TextureViewDimension::Cube => glow::TEXTURE_CUBE_MAP,
542            wgt::TextureViewDimension::CubeArray => glow::TEXTURE_CUBE_MAP_ARRAY,
543            wgt::TextureViewDimension::D3 => glow::TEXTURE_3D,
544        }
545    }
546
547    /// More information can be found in issues #1614 and #1574
548    fn log_failing_target_heuristics(view_dimension: wgt::TextureViewDimension, target: u32) {
549        let expected_target = Self::target_for_view_dimension(view_dimension);
550
551        if expected_target == target {
552            return;
553        }
554
555        let buffer;
556        let got = match target {
557            glow::TEXTURE_2D => "D2",
558            glow::TEXTURE_2D_ARRAY => "D2Array",
559            glow::TEXTURE_CUBE_MAP => "Cube",
560            glow::TEXTURE_CUBE_MAP_ARRAY => "CubeArray",
561            glow::TEXTURE_3D => "D3",
562            target => {
563                buffer = target.to_string();
564                &buffer
565            }
566        };
567
568        log::error!(
569            concat!(
570                "wgpu-hal heuristics assumed that ",
571                "the view dimension will be equal to `{}` rather than `{:?}`.\n",
572                "`D2` textures with ",
573                "`depth_or_array_layers == 1` ",
574                "are assumed to have view dimension `D2`\n",
575                "`D2` textures with ",
576                "`depth_or_array_layers > 1` ",
577                "are assumed to have view dimension `D2Array`\n",
578                "`D2` textures with ",
579                "`depth_or_array_layers == 6` ",
580                "are assumed to have view dimension `Cube`\n",
581                "`D2` textures with ",
582                "`depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` ",
583                "are assumed to have view dimension `CubeArray`\n",
584            ),
585            got,
586            view_dimension,
587        );
588    }
589}
590
591#[derive(Clone, Debug)]
592pub struct TextureView {
593    inner: TextureInner,
594    aspects: crate::FormatAspects,
595    mip_levels: Range<u32>,
596    array_layers: Range<u32>,
597    format: wgt::TextureFormat,
598}
599
600impl crate::DynTextureView for TextureView {}
601
602#[derive(Debug)]
603pub struct Sampler {
604    raw: glow::Sampler,
605}
606
607impl crate::DynSampler for Sampler {}
608
609#[derive(Debug)]
610pub struct BindGroupLayout {
611    entries: Arc<[wgt::BindGroupLayoutEntry]>,
612}
613
614impl crate::DynBindGroupLayout for BindGroupLayout {}
615
616#[derive(Debug)]
617struct BindGroupLayoutInfo {
618    entries: Arc<[wgt::BindGroupLayoutEntry]>,
619    /// Mapping of resources, indexed by `binding`, into the whole layout space.
620    /// For texture resources, the value is the texture slot index.
621    /// For sampler resources, the value is the index of the sampler in the whole layout.
622    /// For buffers, the value is the uniform or storage slot index.
623    /// For unused bindings, the value is `!0`
624    binding_to_slot: Box<[u8]>,
625}
626
627#[derive(Debug)]
628pub struct PipelineLayout {
629    group_infos: Box<[Option<BindGroupLayoutInfo>]>,
630    naga_options: naga::back::glsl::Options,
631}
632
633impl crate::DynPipelineLayout for PipelineLayout {}
634
635impl PipelineLayout {
636    /// # Panics
637    /// If the pipeline layout does not contain a bind group layout used by
638    /// the resource binding.
639    fn get_slot(&self, br: &naga::ResourceBinding) -> u8 {
640        let group_info = self.group_infos[br.group as usize].as_ref().unwrap();
641        group_info.binding_to_slot[br.binding as usize]
642    }
643}
644
645#[derive(Debug)]
646enum BindingRegister {
647    UniformBuffers,
648    StorageBuffers,
649    Textures,
650    Images,
651}
652
653#[derive(Debug)]
654enum RawBinding {
655    Buffer {
656        raw: glow::Buffer,
657        offset: i32,
658        size: i32,
659    },
660    Texture {
661        raw: glow::Texture,
662        target: BindTarget,
663        aspects: crate::FormatAspects,
664        mip_levels: Range<u32>,
665        //TODO: array layers
666    },
667    Image(ImageBinding),
668    Sampler(glow::Sampler),
669}
670
671#[derive(Debug)]
672pub struct BindGroup {
673    contents: Box<[RawBinding]>,
674}
675
676impl crate::DynBindGroup for BindGroup {}
677
678type ShaderId = u32;
679
680#[derive(Debug)]
681pub enum ShaderModuleSource {
682    Naga(crate::NagaShader),
683    Passthrough { source: String },
684}
685
686#[derive(Debug)]
687pub struct ShaderModule {
688    source: ShaderModuleSource,
689    label: Option<String>,
690    id: ShaderId,
691}
692
693impl crate::DynShaderModule for ShaderModule {}
694
695#[derive(Clone, Debug, Default)]
696struct VertexFormatDesc {
697    element_count: i32,
698    element_format: u32,
699    attrib_kind: VertexAttribKind,
700}
701
702#[derive(Clone, Debug, Default)]
703struct AttributeDesc {
704    location: u32,
705    offset: u32,
706    buffer_index: u32,
707    format_desc: VertexFormatDesc,
708}
709
710#[derive(Clone, Debug)]
711struct BufferBinding {
712    raw: glow::Buffer,
713    offset: wgt::BufferAddress,
714}
715
716#[derive(Clone, Debug)]
717struct ImageBinding {
718    raw: glow::Texture,
719    mip_level: u32,
720    array_layer: Option<u32>,
721    access: u32,
722    format: u32,
723}
724
725#[derive(Clone, Debug, Default, PartialEq)]
726struct VertexBufferDesc {
727    step: wgt::VertexStepMode,
728    stride: u32,
729}
730
731#[derive(Clone, Debug)]
732struct ImmediateDesc {
733    location: glow::UniformLocation,
734    ty: nt::glsl::GlslUniformType,
735    offset: u32,
736    size_bytes: u32,
737}
738
739#[cfg(send_sync)]
740unsafe impl Sync for ImmediateDesc {}
741#[cfg(send_sync)]
742unsafe impl Send for ImmediateDesc {}
743
744/// For each texture in the pipeline layout, store the index of the only
745/// sampler (in this layout) that the texture is used with.
746type SamplerBindMap = [Option<u8>; MAX_TEXTURE_SLOTS];
747
748#[derive(Debug)]
749struct PipelineInner {
750    program: glow::Program,
751    sampler_map: SamplerBindMap,
752    first_instance_location: Option<glow::UniformLocation>,
753    immediates_descs: ArrayVec<ImmediateDesc, MAX_IMMEDIATES_COMMANDS>,
754    clip_distance_count: u32,
755}
756
757#[cfg(send_sync)]
758unsafe impl Sync for PipelineInner {}
759#[cfg(send_sync)]
760unsafe impl Send for PipelineInner {}
761
762#[derive(Clone, Debug)]
763struct DepthState {
764    function: u32,
765    mask: bool,
766}
767
768#[derive(Clone, Debug, PartialEq)]
769struct BlendComponent {
770    src: u32,
771    dst: u32,
772    equation: u32,
773}
774
775#[derive(Clone, Debug, PartialEq)]
776struct BlendDesc {
777    alpha: BlendComponent,
778    color: BlendComponent,
779}
780
781#[derive(Clone, Debug, Default, PartialEq)]
782struct ColorTargetDesc {
783    mask: wgt::ColorWrites,
784    blend: Option<BlendDesc>,
785}
786
787#[derive(Debug, PartialEq, Eq, Hash)]
788struct ProgramStage {
789    naga_stage: naga::ShaderStage,
790    shader_id: ShaderId,
791    entry_point: String,
792    zero_initialize_workgroup_memory: bool,
793    constant_hash: Vec<u8>,
794}
795
796#[derive(Debug, PartialEq, Eq, Hash)]
797struct ProgramCacheKey {
798    stages: ArrayVec<ProgramStage, 3>,
799    group_to_binding_to_slot: Box<[Option<Box<[u8]>>]>,
800}
801
802type ProgramCache = FastHashMap<ProgramCacheKey, Result<Arc<PipelineInner>, crate::PipelineError>>;
803
804#[derive(Debug)]
805pub struct RenderPipeline {
806    inner: Arc<PipelineInner>,
807    primitive: wgt::PrimitiveState,
808    vertex_buffers: Box<[Option<VertexBufferDesc>]>,
809    vertex_attributes: Box<[AttributeDesc]>,
810    color_targets: Box<[ColorTargetDesc]>,
811    depth: Option<DepthState>,
812    depth_bias: wgt::DepthBiasState,
813    stencil: Option<StencilState>,
814    alpha_to_coverage_enabled: bool,
815}
816
817impl crate::DynRenderPipeline for RenderPipeline {}
818
819#[cfg(send_sync)]
820static_assertions::assert_impl_all!(RenderPipeline: Send, Sync);
821
822#[derive(Debug)]
823pub struct ComputePipeline {
824    inner: Arc<PipelineInner>,
825}
826
827impl crate::DynComputePipeline for ComputePipeline {}
828
829#[derive(Debug)]
830pub struct RayTracingPipeline {}
831
832impl crate::DynRayTracingPipeline for RayTracingPipeline {}
833
834#[cfg(send_sync)]
835static_assertions::assert_impl_all!(ComputePipeline: Send, Sync);
836
837#[derive(Debug)]
838pub struct QuerySet {
839    queries: Box<[glow::Query]>,
840    target: BindTarget,
841}
842
843impl crate::DynQuerySet for QuerySet {}
844
845#[derive(Debug)]
846pub struct AccelerationStructure;
847
848impl crate::DynAccelerationStructure for AccelerationStructure {}
849
850#[derive(Debug)]
851pub struct PipelineCache;
852
853impl crate::DynPipelineCache for PipelineCache {}
854
855#[derive(Clone, Debug, PartialEq)]
856struct StencilOps {
857    pass: u32,
858    fail: u32,
859    depth_fail: u32,
860}
861
862impl Default for StencilOps {
863    fn default() -> Self {
864        Self {
865            pass: glow::KEEP,
866            fail: glow::KEEP,
867            depth_fail: glow::KEEP,
868        }
869    }
870}
871
872#[derive(Clone, Debug, PartialEq)]
873struct StencilSide {
874    function: u32,
875    mask_read: u32,
876    mask_write: u32,
877    reference: u32,
878    ops: StencilOps,
879}
880
881impl Default for StencilSide {
882    fn default() -> Self {
883        Self {
884            function: glow::ALWAYS,
885            mask_read: 0xFF,
886            mask_write: 0xFF,
887            reference: 0,
888            ops: StencilOps::default(),
889        }
890    }
891}
892
893#[derive(Debug, Clone, Default)]
894struct StencilState {
895    front: StencilSide,
896    back: StencilSide,
897}
898
899#[derive(Clone, Debug, Default, PartialEq)]
900struct PrimitiveState {
901    front_face: u32,
902    cull_face: u32,
903    unclipped_depth: bool,
904    polygon_mode: u32,
905}
906
907type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>;
908
909#[derive(Debug)]
910enum Command {
911    Draw {
912        topology: u32,
913        first_vertex: u32,
914        vertex_count: u32,
915        first_instance: u32,
916        instance_count: u32,
917        first_instance_location: Option<glow::UniformLocation>,
918    },
919    DrawIndexed {
920        topology: u32,
921        index_type: u32,
922        index_count: u32,
923        index_offset: wgt::BufferAddress,
924        base_vertex: i32,
925        first_instance: u32,
926        instance_count: u32,
927        first_instance_location: Option<glow::UniformLocation>,
928    },
929    DrawIndirect {
930        topology: u32,
931        indirect_buf: glow::Buffer,
932        indirect_offset: wgt::BufferAddress,
933        first_instance_location: Option<glow::UniformLocation>,
934    },
935    DrawIndexedIndirect {
936        topology: u32,
937        index_type: u32,
938        indirect_buf: glow::Buffer,
939        indirect_offset: wgt::BufferAddress,
940        first_instance_location: Option<glow::UniformLocation>,
941    },
942    Dispatch([u32; 3]),
943    DispatchIndirect {
944        indirect_buf: glow::Buffer,
945        indirect_offset: wgt::BufferAddress,
946    },
947    ClearBuffer {
948        dst: Buffer,
949        dst_target: BindTarget,
950        range: crate::MemoryRange,
951    },
952    CopyBufferToBuffer {
953        src: Buffer,
954        src_target: BindTarget,
955        dst: Buffer,
956        dst_target: BindTarget,
957        copy: crate::BufferCopy,
958    },
959    #[cfg(webgl)]
960    CopyExternalImageToTexture {
961        src: wgt::CopyExternalImageSourceInfo,
962        dst: glow::Texture,
963        dst_target: BindTarget,
964        dst_format: wgt::TextureFormat,
965        dst_premultiplication: bool,
966        copy: crate::TextureCopy,
967    },
968    CopyTextureToTexture {
969        src: glow::Texture,
970        src_target: BindTarget,
971        dst: glow::Texture,
972        dst_target: BindTarget,
973        copy: crate::TextureCopy,
974    },
975    CopyBufferToTexture {
976        src: Buffer,
977        #[allow(unused)]
978        src_target: BindTarget,
979        dst: glow::Texture,
980        dst_target: BindTarget,
981        dst_format: wgt::TextureFormat,
982        copy: crate::BufferTextureCopy,
983    },
984    CopyTextureToBuffer {
985        src: glow::Texture,
986        src_target: BindTarget,
987        src_format: wgt::TextureFormat,
988        dst: Buffer,
989        #[allow(unused)]
990        dst_target: BindTarget,
991        copy: crate::BufferTextureCopy,
992    },
993    SetIndexBuffer(glow::Buffer),
994    BeginQuery(glow::Query, BindTarget),
995    EndQuery(BindTarget),
996    TimestampQuery(glow::Query),
997    CopyQueryResults {
998        query_range: Range<u32>,
999        dst: Buffer,
1000        dst_target: BindTarget,
1001        dst_offset: wgt::BufferAddress,
1002    },
1003    ResetFramebuffer {
1004        is_default: bool,
1005    },
1006    BindAttachment {
1007        attachment: u32,
1008        view: TextureView,
1009        depth_slice: Option<u32>,
1010        sample_count: u32,
1011    },
1012    ResolveAttachment {
1013        attachment: u32,
1014        dst: TextureView,
1015        size: wgt::Extent3d,
1016    },
1017    InvalidateAttachments(InvalidatedAttachments),
1018    SetDrawColorBuffers(u8),
1019    ClearColorF {
1020        draw_buffer: u32,
1021        color: [f32; 4],
1022        is_srgb: bool,
1023    },
1024    ClearColorU(u32, [u32; 4]),
1025    ClearColorI(u32, [i32; 4]),
1026    ClearDepth(f32),
1027    ClearStencil(u32),
1028    // Clearing both the depth and stencil buffer individually appears to
1029    // result in the stencil buffer failing to clear, atleast in WebGL.
1030    // It is also more efficient to emit a single command instead of two for
1031    // this.
1032    ClearDepthAndStencil(f32, u32),
1033    BufferBarrier(glow::Buffer, wgt::BufferUses),
1034    TextureBarrier(wgt::TextureUses),
1035    SetViewport {
1036        rect: crate::Rect<i32>,
1037        depth: Range<f32>,
1038    },
1039    SetScissor(crate::Rect<i32>),
1040    SetStencilFunc {
1041        face: u32,
1042        function: u32,
1043        reference: u32,
1044        read_mask: u32,
1045    },
1046    SetStencilOps {
1047        face: u32,
1048        write_mask: u32,
1049        ops: StencilOps,
1050    },
1051    SetDepth(DepthState),
1052    SetDepthBias(wgt::DepthBiasState),
1053    ConfigureDepthStencil(crate::FormatAspects),
1054    SetAlphaToCoverage(bool),
1055    SetVertexAttribute {
1056        buffer: Option<glow::Buffer>,
1057        buffer_desc: VertexBufferDesc,
1058        attribute_desc: AttributeDesc,
1059    },
1060    UnsetVertexAttribute(u32),
1061    SetVertexBuffer {
1062        index: u32,
1063        buffer: BufferBinding,
1064        buffer_desc: VertexBufferDesc,
1065    },
1066    SetProgram(glow::Program),
1067    SetPrimitive(PrimitiveState),
1068    SetBlendConstant([f32; 4]),
1069    SetColorTarget {
1070        draw_buffer_index: Option<u32>,
1071        desc: ColorTargetDesc,
1072    },
1073    BindBuffer {
1074        target: BindTarget,
1075        slot: u32,
1076        buffer: glow::Buffer,
1077        offset: i32,
1078        size: i32,
1079    },
1080    BindSampler(u32, Option<glow::Sampler>),
1081    BindTexture {
1082        slot: u32,
1083        texture: glow::Texture,
1084        target: BindTarget,
1085        aspects: crate::FormatAspects,
1086        mip_levels: Range<u32>,
1087    },
1088    BindImage {
1089        slot: u32,
1090        binding: ImageBinding,
1091    },
1092    InsertDebugMarker(Range<u32>),
1093    PushDebugGroup(Range<u32>),
1094    PopDebugGroup,
1095    SetImmediates {
1096        uniform: ImmediateDesc,
1097        /// Offset from the start of the `data_bytes`
1098        offset: u32,
1099    },
1100    SetClipDistances {
1101        old_count: u32,
1102        new_count: u32,
1103    },
1104}
1105
1106#[derive(Default)]
1107pub struct CommandBuffer {
1108    label: Option<String>,
1109    commands: Vec<Command>,
1110    data_bytes: Vec<u8>,
1111    queries: Vec<glow::Query>,
1112}
1113
1114impl crate::DynCommandBuffer for CommandBuffer {}
1115
1116impl fmt::Debug for CommandBuffer {
1117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1118        let mut builder = f.debug_struct("CommandBuffer");
1119        if let Some(ref label) = self.label {
1120            builder.field("label", label);
1121        }
1122        builder.finish()
1123    }
1124}
1125
1126#[cfg(send_sync)]
1127unsafe impl Sync for CommandBuffer {}
1128#[cfg(send_sync)]
1129unsafe impl Send for CommandBuffer {}
1130
1131//TODO: we would have something like `Arc<typed_arena::Arena>`
1132// here and in the command buffers. So that everything grows
1133// inside the encoder and stays there until `reset_all`.
1134
1135pub struct CommandEncoder {
1136    cmd_buffer: CommandBuffer,
1137    state: command::State,
1138    private_caps: PrivateCapabilities,
1139    counters: Arc<wgt::HalCounters>,
1140}
1141
1142impl fmt::Debug for CommandEncoder {
1143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1144        f.debug_struct("CommandEncoder")
1145            .field("cmd_buffer", &self.cmd_buffer)
1146            .finish()
1147    }
1148}
1149
1150#[cfg(send_sync)]
1151unsafe impl Sync for CommandEncoder {}
1152#[cfg(send_sync)]
1153unsafe impl Send for CommandEncoder {}
1154
1155#[cfg(not(webgl))]
1156fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
1157    let source_str = match source {
1158        glow::DEBUG_SOURCE_API => "API",
1159        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
1160        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
1161        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
1162        glow::DEBUG_SOURCE_APPLICATION => "Application",
1163        glow::DEBUG_SOURCE_OTHER => "Other",
1164        _ => unreachable!(),
1165    };
1166
1167    let log_severity = match severity {
1168        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
1169        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
1170        glow::DEBUG_SEVERITY_LOW => log::Level::Debug,
1171        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
1172        _ => unreachable!(),
1173    };
1174
1175    let type_str = match gltype {
1176        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
1177        glow::DEBUG_TYPE_ERROR => "Error",
1178        glow::DEBUG_TYPE_MARKER => "Marker",
1179        glow::DEBUG_TYPE_OTHER => "Other",
1180        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
1181        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
1182        glow::DEBUG_TYPE_PORTABILITY => "Portability",
1183        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
1184        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
1185        _ => unreachable!(),
1186    };
1187
1188    let _ = std::panic::catch_unwind(|| {
1189        log::log!(
1190            log_severity,
1191            "GLES: [{source_str}/{type_str}] ID {id} : {message}"
1192        );
1193    });
1194
1195    #[cfg(feature = "validation_canary")]
1196    if cfg!(debug_assertions) && log_severity == log::Level::Error {
1197        // Set canary and continue
1198        crate::VALIDATION_CANARY.add(message.to_string());
1199    }
1200}