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