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