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