Skip to main content

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