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