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