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