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