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