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    size: wgt::BufferAddress,
388    /// Flags to use within calls to [`Device::map_buffer`](crate::Device::map_buffer).
389    map_flags: u32,
390    /// Buffer mapping state.
391    ///
392    /// If locked concurrently with the GL context, the GL context should be locked first.
393    map_state: Arc<Mutex<BufferMapState>>,
394    /// Set when the buffer wraps an externally-owned GL name created via
395    /// [`Device::buffer_from_raw`](crate::gles::Device::buffer_from_raw).
396    ///
397    /// `Buffer` is `Clone`, so the guard is shared via `Arc`
398    /// and only fires its callback once every clone is dropped.
399    drop_guard: Option<Arc<crate::DropGuard>>,
400}
401
402#[derive(Clone, Debug)]
403struct BufferMapState {
404    /// True if the GL buffer is actually mapped, i.e. not "fake-mapped" with
405    /// an empty slice
406    mapped: bool,
407    data: Option<Vec<u8>>,
408    offset_of_current_mapping: wgt::BufferAddress,
409}
410
411#[cfg(send_sync)]
412static_assertions::assert_impl_all!(Buffer: Send, Sync);
413
414impl crate::DynBuffer for Buffer {}
415
416#[derive(Clone, Debug)]
417pub enum TextureInner {
418    Renderbuffer {
419        raw: glow::Renderbuffer,
420    },
421    DefaultRenderbuffer,
422    Texture {
423        raw: glow::Texture,
424        target: BindTarget,
425    },
426    #[cfg(webgl)]
427    /// Render to a `WebGLFramebuffer`
428    ///
429    /// This is a web feature
430    ExternalFramebuffer {
431        inner: web_sys::WebGlFramebuffer,
432    },
433    #[cfg(native)]
434    /// Render to a `glow::NativeFramebuffer`
435    /// Useful when the framebuffer to draw to
436    /// has a non-zero framebuffer ID
437    ///
438    /// This is a native feature
439    ExternalNativeFramebuffer {
440        inner: glow::NativeFramebuffer,
441    },
442}
443
444#[cfg(send_sync)]
445unsafe impl Sync for TextureInner {}
446#[cfg(send_sync)]
447unsafe impl Send for TextureInner {}
448
449impl TextureInner {
450    fn as_native(&self) -> (glow::Texture, BindTarget) {
451        match *self {
452            Self::Renderbuffer { .. } | Self::DefaultRenderbuffer => {
453                panic!("Unexpected renderbuffer");
454            }
455            Self::Texture { raw, target } => (raw, target),
456            #[cfg(webgl)]
457            Self::ExternalFramebuffer { .. } => panic!("Unexpected external framebuffer"),
458            #[cfg(native)]
459            Self::ExternalNativeFramebuffer { .. } => panic!("unexpected external framebuffer"),
460        }
461    }
462}
463
464#[derive(Debug)]
465pub struct Texture {
466    pub inner: TextureInner,
467    pub mip_level_count: u32,
468    pub array_layer_count: u32,
469    pub format: wgt::TextureFormat,
470    pub format_desc: TextureFormatDesc,
471    pub copy_size: CopyExtent,
472
473    /// `Some` marks the underlying GL object as externally owned: wgpu-hal
474    /// never deletes it (the guard's callback, if any, fires instead).
475    ///
476    /// On WebGL every handle also holds a slot in glow's resource tracker.
477    /// `destroy_texture` always releases that slot: by deleting the texture
478    /// when we own it, or by `unregister_external_texture` when we don't.
479    ///
480    /// The `drop_guard` field must be the last field of this struct so it is
481    /// dropped last. Do not add new fields after it.
482    pub drop_guard: Option<crate::DropGuard>,
483}
484
485impl crate::DynTexture for Texture {}
486impl crate::DynSurfaceTexture for Texture {}
487
488impl core::borrow::Borrow<dyn crate::DynTexture> for Texture {
489    fn borrow(&self) -> &dyn crate::DynTexture {
490        self
491    }
492}
493
494impl Texture {
495    pub fn default_framebuffer(format: wgt::TextureFormat) -> Self {
496        Self {
497            inner: TextureInner::DefaultRenderbuffer,
498            drop_guard: None,
499            mip_level_count: 1,
500            array_layer_count: 1,
501            format,
502            format_desc: TextureFormatDesc {
503                internal: 0,
504                external: 0,
505                data_type: 0,
506            },
507            copy_size: CopyExtent {
508                width: 0,
509                height: 0,
510                depth: 0,
511            },
512        }
513    }
514
515    /// Returns the `target`, whether the image is 3d and whether the image is a cubemap.
516    fn get_info_from_desc(desc: &TextureDescriptor) -> u32 {
517        match desc.dimension {
518            // WebGL (1 and 2) as well as some GLES versions do not have 1D textures, so we are
519            // doing `TEXTURE_2D` instead
520            wgt::TextureDimension::D1 => glow::TEXTURE_2D,
521            wgt::TextureDimension::D2 => {
522                // HACK: detect a cube map; forces cube compatible textures to be cube textures
523                match (desc.is_cube_compatible(), desc.size.depth_or_array_layers) {
524                    (false, 1) => glow::TEXTURE_2D,
525                    (false, _) => glow::TEXTURE_2D_ARRAY,
526                    (true, 6) => glow::TEXTURE_CUBE_MAP,
527                    (true, _) => glow::TEXTURE_CUBE_MAP_ARRAY,
528                }
529            }
530            wgt::TextureDimension::D3 => glow::TEXTURE_3D,
531        }
532    }
533
534    /// GL bind target corresponding to a view dimension.
535    ///
536    /// 1D collapses to `TEXTURE_2D`: WebGL (1 and 2) as well as some GLES
537    /// versions do not have 1D textures.
538    fn target_for_view_dimension(view_dimension: wgt::TextureViewDimension) -> BindTarget {
539        match view_dimension {
540            wgt::TextureViewDimension::D1 | wgt::TextureViewDimension::D2 => glow::TEXTURE_2D,
541            wgt::TextureViewDimension::D2Array => glow::TEXTURE_2D_ARRAY,
542            wgt::TextureViewDimension::Cube => glow::TEXTURE_CUBE_MAP,
543            wgt::TextureViewDimension::CubeArray => glow::TEXTURE_CUBE_MAP_ARRAY,
544            wgt::TextureViewDimension::D3 => glow::TEXTURE_3D,
545        }
546    }
547
548    /// More information can be found in issues #1614 and #1574
549    fn log_failing_target_heuristics(view_dimension: wgt::TextureViewDimension, target: u32) {
550        let expected_target = Self::target_for_view_dimension(view_dimension);
551
552        if expected_target == target {
553            return;
554        }
555
556        let buffer;
557        let got = match target {
558            glow::TEXTURE_2D => "D2",
559            glow::TEXTURE_2D_ARRAY => "D2Array",
560            glow::TEXTURE_CUBE_MAP => "Cube",
561            glow::TEXTURE_CUBE_MAP_ARRAY => "CubeArray",
562            glow::TEXTURE_3D => "D3",
563            target => {
564                buffer = target.to_string();
565                &buffer
566            }
567        };
568
569        log::error!(
570            concat!(
571                "wgpu-hal heuristics assumed that ",
572                "the view dimension will be equal to `{}` rather than `{:?}`.\n",
573                "`D2` textures with ",
574                "`depth_or_array_layers == 1` ",
575                "are assumed to have view dimension `D2`\n",
576                "`D2` textures with ",
577                "`depth_or_array_layers > 1` ",
578                "are assumed to have view dimension `D2Array`\n",
579                "`D2` textures with ",
580                "`depth_or_array_layers == 6` ",
581                "are assumed to have view dimension `Cube`\n",
582                "`D2` textures with ",
583                "`depth_or_array_layers > 6 && depth_or_array_layers % 6 == 0` ",
584                "are assumed to have view dimension `CubeArray`\n",
585            ),
586            got,
587            view_dimension,
588        );
589    }
590}
591
592#[derive(Clone, Debug)]
593pub struct TextureView {
594    inner: TextureInner,
595    aspects: crate::FormatAspects,
596    mip_levels: Range<u32>,
597    array_layers: Range<u32>,
598    format: wgt::TextureFormat,
599}
600
601impl crate::DynTextureView for TextureView {}
602
603#[derive(Debug)]
604pub struct Sampler {
605    raw: glow::Sampler,
606}
607
608impl crate::DynSampler for Sampler {}
609
610#[derive(Debug)]
611pub struct BindGroupLayout {
612    entries: Arc<[wgt::BindGroupLayoutEntry]>,
613}
614
615impl crate::DynBindGroupLayout for BindGroupLayout {}
616
617#[derive(Debug)]
618struct BindGroupLayoutInfo {
619    entries: Arc<[wgt::BindGroupLayoutEntry]>,
620    /// Mapping of resources, indexed by `binding`, into the whole layout space.
621    /// For texture resources, the value is the texture slot index.
622    /// For sampler resources, the value is the index of the sampler in the whole layout.
623    /// For buffers, the value is the uniform or storage slot index.
624    /// For unused bindings, the value is `!0`
625    binding_to_slot: Box<[u8]>,
626}
627
628#[derive(Debug)]
629pub struct PipelineLayout {
630    group_infos: Box<[Option<BindGroupLayoutInfo>]>,
631    naga_options: naga::back::glsl::Options,
632}
633
634impl crate::DynPipelineLayout for PipelineLayout {}
635
636impl PipelineLayout {
637    /// # Panics
638    /// If the pipeline layout does not contain a bind group layout used by
639    /// the resource binding.
640    fn get_slot(&self, br: &naga::ResourceBinding) -> u8 {
641        let group_info = self.group_infos[br.group as usize].as_ref().unwrap();
642        group_info.binding_to_slot[br.binding as usize]
643    }
644}
645
646#[derive(Debug)]
647enum BindingRegister {
648    UniformBuffers,
649    StorageBuffers,
650    Textures,
651    Images,
652}
653
654#[derive(Debug)]
655enum RawBinding {
656    Buffer {
657        raw: glow::Buffer,
658        offset: i32,
659        size: i32,
660    },
661    Texture {
662        raw: glow::Texture,
663        target: BindTarget,
664        aspects: crate::FormatAspects,
665        mip_levels: Range<u32>,
666        //TODO: array layers
667    },
668    Image(ImageBinding),
669    Sampler(glow::Sampler),
670}
671
672#[derive(Debug)]
673pub struct BindGroup {
674    contents: Box<[RawBinding]>,
675}
676
677impl crate::DynBindGroup for BindGroup {}
678
679type ShaderId = u32;
680
681#[derive(Debug)]
682pub enum ShaderModuleSource {
683    Naga(crate::NagaShader),
684    Passthrough { source: String },
685}
686
687#[derive(Debug)]
688pub struct ShaderModule {
689    source: ShaderModuleSource,
690    label: Option<String>,
691    id: ShaderId,
692}
693
694impl crate::DynShaderModule for ShaderModule {}
695
696#[derive(Clone, Debug, Default)]
697struct VertexFormatDesc {
698    element_count: i32,
699    element_format: u32,
700    attrib_kind: VertexAttribKind,
701}
702
703#[derive(Clone, Debug, Default)]
704struct AttributeDesc {
705    location: u32,
706    offset: u32,
707    buffer_index: u32,
708    format_desc: VertexFormatDesc,
709}
710
711#[derive(Clone, Debug)]
712struct BufferBinding {
713    raw: glow::Buffer,
714    offset: wgt::BufferAddress,
715}
716
717#[derive(Clone, Debug)]
718struct ImageBinding {
719    raw: glow::Texture,
720    mip_level: u32,
721    array_layer: Option<u32>,
722    access: u32,
723    format: u32,
724}
725
726#[derive(Clone, Debug, Default, PartialEq)]
727struct VertexBufferDesc {
728    step: wgt::VertexStepMode,
729    stride: u32,
730}
731
732#[derive(Clone, Debug)]
733struct ImmediateDesc {
734    location: glow::UniformLocation,
735    ty: nt::glsl::GlslUniformType,
736    offset: u32,
737    size_bytes: u32,
738}
739
740#[cfg(send_sync)]
741unsafe impl Sync for ImmediateDesc {}
742#[cfg(send_sync)]
743unsafe impl Send for ImmediateDesc {}
744
745/// For each texture in the pipeline layout, store the index of the only
746/// sampler (in this layout) that the texture is used with.
747type SamplerBindMap = [Option<u8>; MAX_TEXTURE_SLOTS];
748
749#[derive(Debug)]
750struct PipelineInner {
751    program: glow::Program,
752    sampler_map: SamplerBindMap,
753    first_instance_location: Option<glow::UniformLocation>,
754    immediates_descs: ArrayVec<ImmediateDesc, MAX_IMMEDIATES_COMMANDS>,
755    clip_distance_count: u32,
756}
757
758#[cfg(send_sync)]
759unsafe impl Sync for PipelineInner {}
760#[cfg(send_sync)]
761unsafe impl Send for PipelineInner {}
762
763#[derive(Clone, Debug)]
764struct DepthState {
765    function: u32,
766    mask: bool,
767}
768
769#[derive(Clone, Debug, PartialEq)]
770struct BlendComponent {
771    src: u32,
772    dst: u32,
773    equation: u32,
774}
775
776#[derive(Clone, Debug, PartialEq)]
777struct BlendDesc {
778    alpha: BlendComponent,
779    color: BlendComponent,
780}
781
782#[derive(Clone, Debug, Default, PartialEq)]
783struct ColorTargetDesc {
784    mask: wgt::ColorWrites,
785    blend: Option<BlendDesc>,
786}
787
788#[derive(Debug, PartialEq, Eq, Hash)]
789struct ProgramStage {
790    naga_stage: naga::ShaderStage,
791    shader_id: ShaderId,
792    entry_point: String,
793    zero_initialize_workgroup_memory: bool,
794    constant_hash: Vec<u8>,
795}
796
797#[derive(Debug, PartialEq, Eq, Hash)]
798struct ProgramCacheKey {
799    stages: ArrayVec<ProgramStage, 3>,
800    group_to_binding_to_slot: Box<[Option<Box<[u8]>>]>,
801}
802
803type ProgramCache = FastHashMap<ProgramCacheKey, Result<Arc<PipelineInner>, crate::PipelineError>>;
804
805#[derive(Debug)]
806pub struct RenderPipeline {
807    inner: Arc<PipelineInner>,
808    primitive: wgt::PrimitiveState,
809    vertex_buffers: Box<[Option<VertexBufferDesc>]>,
810    vertex_attributes: Box<[AttributeDesc]>,
811    color_targets: Box<[ColorTargetDesc]>,
812    depth: Option<DepthState>,
813    depth_bias: wgt::DepthBiasState,
814    stencil: Option<StencilState>,
815    alpha_to_coverage_enabled: bool,
816}
817
818impl crate::DynRenderPipeline for RenderPipeline {}
819
820#[cfg(send_sync)]
821static_assertions::assert_impl_all!(RenderPipeline: Send, Sync);
822
823#[derive(Debug)]
824pub struct ComputePipeline {
825    inner: Arc<PipelineInner>,
826}
827
828impl crate::DynComputePipeline for ComputePipeline {}
829
830#[derive(Debug)]
831pub struct RayTracingPipeline {}
832
833impl crate::DynRayTracingPipeline for RayTracingPipeline {}
834
835#[cfg(send_sync)]
836static_assertions::assert_impl_all!(ComputePipeline: Send, Sync);
837
838#[derive(Debug)]
839pub struct QuerySet {
840    queries: Box<[glow::Query]>,
841    target: BindTarget,
842}
843
844impl crate::DynQuerySet for QuerySet {}
845
846#[derive(Debug)]
847pub struct AccelerationStructure;
848
849impl crate::DynAccelerationStructure for AccelerationStructure {}
850
851#[derive(Debug)]
852pub struct PipelineCache;
853
854impl crate::DynPipelineCache for PipelineCache {}
855
856#[derive(Clone, Debug, PartialEq)]
857struct StencilOps {
858    pass: u32,
859    fail: u32,
860    depth_fail: u32,
861}
862
863impl Default for StencilOps {
864    fn default() -> Self {
865        Self {
866            pass: glow::KEEP,
867            fail: glow::KEEP,
868            depth_fail: glow::KEEP,
869        }
870    }
871}
872
873#[derive(Clone, Debug, PartialEq)]
874struct StencilSide {
875    function: u32,
876    mask_read: u32,
877    mask_write: u32,
878    reference: u32,
879    ops: StencilOps,
880}
881
882impl Default for StencilSide {
883    fn default() -> Self {
884        Self {
885            function: glow::ALWAYS,
886            mask_read: 0xFF,
887            mask_write: 0xFF,
888            reference: 0,
889            ops: StencilOps::default(),
890        }
891    }
892}
893
894#[derive(Debug, Clone, Default)]
895struct StencilState {
896    front: StencilSide,
897    back: StencilSide,
898}
899
900#[derive(Clone, Debug, Default, PartialEq)]
901struct PrimitiveState {
902    front_face: u32,
903    cull_face: u32,
904    unclipped_depth: bool,
905    polygon_mode: u32,
906}
907
908type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>;
909
910#[derive(Debug)]
911enum Command {
912    Draw {
913        topology: u32,
914        first_vertex: u32,
915        vertex_count: u32,
916        first_instance: u32,
917        instance_count: u32,
918        first_instance_location: Option<glow::UniformLocation>,
919    },
920    DrawIndexed {
921        topology: u32,
922        index_type: u32,
923        index_count: u32,
924        index_offset: wgt::BufferAddress,
925        base_vertex: i32,
926        first_instance: u32,
927        instance_count: u32,
928        first_instance_location: Option<glow::UniformLocation>,
929    },
930    DrawIndirect {
931        topology: u32,
932        indirect_buf: glow::Buffer,
933        indirect_offset: wgt::BufferAddress,
934        first_instance_location: Option<glow::UniformLocation>,
935    },
936    DrawIndexedIndirect {
937        topology: u32,
938        index_type: u32,
939        indirect_buf: glow::Buffer,
940        indirect_offset: wgt::BufferAddress,
941        first_instance_location: Option<glow::UniformLocation>,
942    },
943    Dispatch([u32; 3]),
944    DispatchIndirect {
945        indirect_buf: glow::Buffer,
946        indirect_offset: wgt::BufferAddress,
947    },
948    ClearBuffer {
949        dst: Buffer,
950        dst_target: BindTarget,
951        range: crate::MemoryRange,
952    },
953    CopyBufferToBuffer {
954        src: Buffer,
955        src_target: BindTarget,
956        dst: Buffer,
957        dst_target: BindTarget,
958        copy: crate::BufferCopy,
959    },
960    #[cfg(webgl)]
961    CopyExternalImageToTexture {
962        src: wgt::CopyExternalImageSourceInfo,
963        dst: glow::Texture,
964        dst_target: BindTarget,
965        dst_format: wgt::TextureFormat,
966        dst_premultiplication: bool,
967        copy: crate::TextureCopy,
968    },
969    CopyTextureToTexture {
970        src: glow::Texture,
971        src_target: BindTarget,
972        dst: glow::Texture,
973        dst_target: BindTarget,
974        copy: crate::TextureCopy,
975    },
976    CopyBufferToTexture {
977        src: Buffer,
978        #[allow(unused)]
979        src_target: BindTarget,
980        dst: glow::Texture,
981        dst_target: BindTarget,
982        dst_format: wgt::TextureFormat,
983        copy: crate::BufferTextureCopy,
984    },
985    CopyTextureToBuffer {
986        src: glow::Texture,
987        src_target: BindTarget,
988        src_format: wgt::TextureFormat,
989        dst: Buffer,
990        #[allow(unused)]
991        dst_target: BindTarget,
992        copy: crate::BufferTextureCopy,
993    },
994    SetIndexBuffer(glow::Buffer),
995    BeginQuery(glow::Query, BindTarget),
996    EndQuery(BindTarget),
997    TimestampQuery(glow::Query),
998    CopyQueryResults {
999        query_range: Range<u32>,
1000        dst: Buffer,
1001        dst_target: BindTarget,
1002        dst_offset: wgt::BufferAddress,
1003    },
1004    ResetFramebuffer {
1005        is_default: bool,
1006    },
1007    BindAttachment {
1008        attachment: u32,
1009        view: TextureView,
1010        depth_slice: Option<u32>,
1011        sample_count: u32,
1012    },
1013    ResolveAttachment {
1014        attachment: u32,
1015        dst: TextureView,
1016        size: wgt::Extent3d,
1017    },
1018    InvalidateAttachments(InvalidatedAttachments),
1019    SetDrawColorBuffers(u8),
1020    ClearColorF {
1021        draw_buffer: u32,
1022        color: [f32; 4],
1023        is_srgb: bool,
1024    },
1025    ClearColorU(u32, [u32; 4]),
1026    ClearColorI(u32, [i32; 4]),
1027    ClearDepth(f32),
1028    ClearStencil(u32),
1029    // Clearing both the depth and stencil buffer individually appears to
1030    // result in the stencil buffer failing to clear, atleast in WebGL.
1031    // It is also more efficient to emit a single command instead of two for
1032    // this.
1033    ClearDepthAndStencil(f32, u32),
1034    BufferBarrier(glow::Buffer, wgt::BufferUses),
1035    TextureBarrier(wgt::TextureUses),
1036    SetViewport {
1037        rect: crate::Rect<i32>,
1038        depth: Range<f32>,
1039    },
1040    SetScissor(crate::Rect<i32>),
1041    SetStencilFunc {
1042        face: u32,
1043        function: u32,
1044        reference: u32,
1045        read_mask: u32,
1046    },
1047    SetStencilOps {
1048        face: u32,
1049        write_mask: u32,
1050        ops: StencilOps,
1051    },
1052    SetDepth(DepthState),
1053    SetDepthBias(wgt::DepthBiasState),
1054    ConfigureDepthStencil(crate::FormatAspects),
1055    SetAlphaToCoverage(bool),
1056    SetVertexAttribute {
1057        buffer: Option<glow::Buffer>,
1058        buffer_desc: VertexBufferDesc,
1059        attribute_desc: AttributeDesc,
1060    },
1061    UnsetVertexAttribute(u32),
1062    SetVertexBuffer {
1063        index: u32,
1064        buffer: BufferBinding,
1065        buffer_desc: VertexBufferDesc,
1066    },
1067    SetProgram(glow::Program),
1068    SetPrimitive(PrimitiveState),
1069    SetBlendConstant([f32; 4]),
1070    SetColorTarget {
1071        draw_buffer_index: Option<u32>,
1072        desc: ColorTargetDesc,
1073    },
1074    BindBuffer {
1075        target: BindTarget,
1076        slot: u32,
1077        buffer: glow::Buffer,
1078        offset: i32,
1079        size: i32,
1080    },
1081    BindSampler(u32, Option<glow::Sampler>),
1082    BindTexture {
1083        slot: u32,
1084        texture: glow::Texture,
1085        target: BindTarget,
1086        aspects: crate::FormatAspects,
1087        mip_levels: Range<u32>,
1088    },
1089    BindImage {
1090        slot: u32,
1091        binding: ImageBinding,
1092    },
1093    InsertDebugMarker(Range<u32>),
1094    PushDebugGroup(Range<u32>),
1095    PopDebugGroup,
1096    SetImmediates {
1097        uniform: ImmediateDesc,
1098        /// Offset from the start of the `data_bytes`
1099        offset: u32,
1100    },
1101    SetClipDistances {
1102        old_count: u32,
1103        new_count: u32,
1104    },
1105}
1106
1107#[derive(Default)]
1108pub struct CommandBuffer {
1109    label: Option<String>,
1110    commands: Vec<Command>,
1111    data_bytes: Vec<u8>,
1112    queries: Vec<glow::Query>,
1113}
1114
1115impl crate::DynCommandBuffer for CommandBuffer {}
1116
1117impl fmt::Debug for CommandBuffer {
1118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1119        let mut builder = f.debug_struct("CommandBuffer");
1120        if let Some(ref label) = self.label {
1121            builder.field("label", label);
1122        }
1123        builder.finish()
1124    }
1125}
1126
1127#[cfg(send_sync)]
1128unsafe impl Sync for CommandBuffer {}
1129#[cfg(send_sync)]
1130unsafe impl Send for CommandBuffer {}
1131
1132//TODO: we would have something like `Arc<typed_arena::Arena>`
1133// here and in the command buffers. So that everything grows
1134// inside the encoder and stays there until `reset_all`.
1135
1136pub struct CommandEncoder {
1137    cmd_buffer: CommandBuffer,
1138    state: command::State,
1139    private_caps: PrivateCapabilities,
1140    counters: Arc<wgt::HalCounters>,
1141}
1142
1143impl fmt::Debug for CommandEncoder {
1144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1145        f.debug_struct("CommandEncoder")
1146            .field("cmd_buffer", &self.cmd_buffer)
1147            .finish()
1148    }
1149}
1150
1151#[cfg(send_sync)]
1152unsafe impl Sync for CommandEncoder {}
1153#[cfg(send_sync)]
1154unsafe impl Send for CommandEncoder {}
1155
1156#[cfg(not(webgl))]
1157fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
1158    let source_str = match source {
1159        glow::DEBUG_SOURCE_API => "API",
1160        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
1161        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
1162        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
1163        glow::DEBUG_SOURCE_APPLICATION => "Application",
1164        glow::DEBUG_SOURCE_OTHER => "Other",
1165        _ => unreachable!(),
1166    };
1167
1168    let log_severity = match severity {
1169        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
1170        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
1171        glow::DEBUG_SEVERITY_LOW => log::Level::Debug,
1172        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
1173        _ => unreachable!(),
1174    };
1175
1176    let type_str = match gltype {
1177        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
1178        glow::DEBUG_TYPE_ERROR => "Error",
1179        glow::DEBUG_TYPE_MARKER => "Marker",
1180        glow::DEBUG_TYPE_OTHER => "Other",
1181        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
1182        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
1183        glow::DEBUG_TYPE_PORTABILITY => "Portability",
1184        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
1185        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
1186        _ => unreachable!(),
1187    };
1188
1189    let _ = std::panic::catch_unwind(|| {
1190        log::log!(
1191            log_severity,
1192            "GLES: [{source_str}/{type_str}] ID {id} : {message}"
1193        );
1194    });
1195
1196    #[cfg(feature = "validation_canary")]
1197    if cfg!(debug_assertions) && log_severity == log::Level::Error {
1198        // Set canary and continue
1199        crate::VALIDATION_CANARY.add(message.to_string());
1200    }
1201}