Skip to main content

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