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    Templates(&'static [crate::GlslTemplate]),
678}
679
680#[derive(Debug)]
681pub struct ShaderModule {
682    source: ShaderModuleSource,
683    label: Option<String>,
684    id: ShaderId,
685}
686
687impl crate::DynShaderModule for ShaderModule {}
688
689#[derive(Clone, Debug, Default)]
690struct VertexFormatDesc {
691    element_count: i32,
692    element_format: u32,
693    attrib_kind: VertexAttribKind,
694}
695
696#[derive(Clone, Debug, Default)]
697struct AttributeDesc {
698    location: u32,
699    offset: u32,
700    buffer_index: u32,
701    format_desc: VertexFormatDesc,
702}
703
704#[derive(Clone, Debug)]
705struct BufferBinding {
706    raw: glow::Buffer,
707    offset: wgt::BufferAddress,
708}
709
710#[derive(Clone, Debug)]
711struct ImageBinding {
712    raw: glow::Texture,
713    mip_level: u32,
714    array_layer: Option<u32>,
715    access: u32,
716    format: u32,
717}
718
719#[derive(Clone, Debug, Default, PartialEq)]
720struct VertexBufferDesc {
721    step: wgt::VertexStepMode,
722    stride: u32,
723}
724
725#[derive(Clone, Debug)]
726struct ImmediateDesc {
727    location: glow::UniformLocation,
728    ty: nt::glsl::GlslUniformType,
729    offset: 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    clip_distance_count: u32,
746    /// The program's link, whose state the queue reads when a command
747    /// buffer first uses it (see [`PipelineInner::linked`]).
748    link: Mutex<ProgramLink>,
749}
750
751/// A program's link: pending until its first use, then what it tells.
752#[derive(Debug)]
753struct ProgramLink {
754    pending: Option<Box<device::PendingLink>>,
755    linked: Option<Arc<LinkedProgram>>,
756}
757
758/// What only the linked program tells: the uniforms its draws set.
759#[derive(Debug, Default)]
760struct LinkedProgram {
761    first_instance_location: Option<glow::UniformLocation>,
762    immediates_descs: ArrayVec<ImmediateDesc, MAX_IMMEDIATES_COMMANDS>,
763}
764
765#[cfg(send_sync)]
766unsafe impl Sync for PipelineInner {}
767#[cfg(send_sync)]
768unsafe impl Send for PipelineInner {}
769
770#[derive(Clone, Debug)]
771struct DepthState {
772    function: u32,
773    mask: bool,
774}
775
776#[derive(Clone, Debug, PartialEq)]
777struct BlendComponent {
778    src: u32,
779    dst: u32,
780    equation: u32,
781}
782
783#[derive(Clone, Debug, PartialEq)]
784struct BlendDesc {
785    alpha: BlendComponent,
786    color: BlendComponent,
787}
788
789#[derive(Clone, Debug, Default, PartialEq)]
790struct ColorTargetDesc {
791    mask: wgt::ColorWrites,
792    blend: Option<BlendDesc>,
793}
794
795#[derive(Debug, PartialEq, Eq, Hash)]
796struct ProgramStage {
797    naga_stage: naga::ShaderStage,
798    shader_id: ShaderId,
799    entry_point: String,
800    zero_initialize_workgroup_memory: bool,
801    constant_hash: Vec<u8>,
802}
803
804#[derive(Debug, PartialEq, Eq, Hash)]
805struct ProgramCacheKey {
806    stages: ArrayVec<ProgramStage, 3>,
807    group_to_binding_to_slot: Box<[Option<Box<[u8]>>]>,
808    force_point_size: bool,
809}
810
811type ProgramCache = FastHashMap<ProgramCacheKey, Result<Arc<PipelineInner>, crate::PipelineError>>;
812
813#[derive(Debug)]
814pub struct RenderPipeline {
815    inner: Arc<PipelineInner>,
816    primitive: wgt::PrimitiveState,
817    vertex_buffers: Box<[Option<VertexBufferDesc>]>,
818    vertex_attributes: Box<[AttributeDesc]>,
819    color_targets: Box<[ColorTargetDesc]>,
820    depth: Option<DepthState>,
821    depth_bias: wgt::DepthBiasState,
822    stencil: Option<StencilState>,
823    alpha_to_coverage_enabled: bool,
824}
825
826impl crate::DynRenderPipeline for RenderPipeline {}
827
828#[cfg(send_sync)]
829static_assertions::assert_impl_all!(RenderPipeline: Send, Sync);
830
831#[derive(Debug)]
832pub struct ComputePipeline {
833    inner: Arc<PipelineInner>,
834}
835
836impl crate::DynComputePipeline for ComputePipeline {}
837
838#[derive(Debug)]
839pub struct RayTracingPipeline {}
840
841impl crate::DynRayTracingPipeline for RayTracingPipeline {}
842
843#[cfg(send_sync)]
844static_assertions::assert_impl_all!(ComputePipeline: Send, Sync);
845
846#[derive(Debug)]
847pub struct QuerySet {
848    queries: Box<[glow::Query]>,
849    target: BindTarget,
850}
851
852impl crate::DynQuerySet for QuerySet {}
853
854#[derive(Debug)]
855pub struct AccelerationStructure;
856
857impl crate::DynAccelerationStructure for AccelerationStructure {}
858
859#[derive(Debug)]
860pub struct PipelineCache;
861
862impl crate::DynPipelineCache for PipelineCache {}
863
864#[derive(Clone, Debug, PartialEq)]
865struct StencilOps {
866    pass: u32,
867    fail: u32,
868    depth_fail: u32,
869}
870
871impl Default for StencilOps {
872    fn default() -> Self {
873        Self {
874            pass: glow::KEEP,
875            fail: glow::KEEP,
876            depth_fail: glow::KEEP,
877        }
878    }
879}
880
881#[derive(Clone, Debug, PartialEq)]
882struct StencilSide {
883    function: u32,
884    mask_read: u32,
885    mask_write: u32,
886    reference: u32,
887    ops: StencilOps,
888}
889
890impl Default for StencilSide {
891    fn default() -> Self {
892        Self {
893            function: glow::ALWAYS,
894            mask_read: 0xFF,
895            mask_write: 0xFF,
896            reference: 0,
897            ops: StencilOps::default(),
898        }
899    }
900}
901
902#[derive(Debug, Clone, Default)]
903struct StencilState {
904    front: StencilSide,
905    back: StencilSide,
906}
907
908#[derive(Clone, Debug, Default, PartialEq)]
909struct PrimitiveState {
910    front_face: u32,
911    cull_face: u32,
912    unclipped_depth: bool,
913    polygon_mode: u32,
914}
915
916type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>;
917
918#[derive(Debug)]
919struct ClearBufferOp {
920    dst: Buffer,
921    dst_target: BindTarget,
922    range: crate::MemoryRange,
923}
924
925#[derive(Debug)]
926struct BufferToBufferCopy {
927    src: Buffer,
928    src_target: BindTarget,
929    dst: Buffer,
930    dst_target: BindTarget,
931    copy: crate::BufferCopy,
932}
933
934#[cfg(webgl)]
935#[derive(Debug)]
936struct ExternalImageToTextureCopy {
937    src: wgt::CopyExternalImageSourceInfo,
938    dst: glow::Texture,
939    dst_target: BindTarget,
940    dst_format: wgt::TextureFormat,
941    dst_premultiplication: bool,
942    copy: crate::TextureCopy,
943}
944
945#[derive(Debug)]
946struct TextureToTextureCopy {
947    src: glow::Texture,
948    src_target: BindTarget,
949    dst: glow::Texture,
950    dst_target: BindTarget,
951    copy: crate::TextureCopy,
952}
953
954#[derive(Debug)]
955struct BufferToTextureCopy {
956    src: Buffer,
957    #[allow(unused)]
958    src_target: BindTarget,
959    dst: glow::Texture,
960    dst_target: BindTarget,
961    dst_format: wgt::TextureFormat,
962    copy: crate::BufferTextureCopy,
963}
964
965#[derive(Debug)]
966struct TextureToBufferCopy {
967    src: glow::Texture,
968    src_target: BindTarget,
969    src_format: wgt::TextureFormat,
970    dst: Buffer,
971    #[allow(unused)]
972    dst_target: BindTarget,
973    copy: crate::BufferTextureCopy,
974}
975
976#[derive(Debug)]
977struct QueryResultsCopy {
978    query_range: Range<u32>,
979    dst: Buffer,
980    dst_target: BindTarget,
981    dst_offset: wgt::BufferAddress,
982}
983
984#[derive(Debug)]
985struct AttachmentBinding {
986    attachment: u32,
987    view: TextureView,
988    depth_slice: Option<u32>,
989    sample_count: u32,
990}
991
992#[derive(Debug)]
993struct AttachmentResolve {
994    attachment: u32,
995    dst: TextureView,
996    size: wgt::Extent3d,
997}
998
999#[derive(Debug)]
1000enum Command {
1001    Draw {
1002        topology: u32,
1003        first_vertex: u32,
1004        vertex_count: u32,
1005        first_instance: u32,
1006        instance_count: u32,
1007    },
1008    DrawIndexed {
1009        topology: u32,
1010        index_type: u32,
1011        index_count: u32,
1012        index_offset: wgt::BufferAddress,
1013        base_vertex: i32,
1014        first_instance: u32,
1015        instance_count: u32,
1016    },
1017    DrawIndirect {
1018        topology: u32,
1019        indirect_buf: glow::Buffer,
1020        indirect_offset: wgt::BufferAddress,
1021    },
1022    DrawIndexedIndirect {
1023        topology: u32,
1024        index_type: u32,
1025        indirect_buf: glow::Buffer,
1026        indirect_offset: wgt::BufferAddress,
1027    },
1028    Dispatch([u32; 3]),
1029    DispatchIndirect {
1030        indirect_buf: glow::Buffer,
1031        indirect_offset: wgt::BufferAddress,
1032    },
1033    ClearBuffer(Box<ClearBufferOp>),
1034    CopyBufferToBuffer(Box<BufferToBufferCopy>),
1035    #[cfg(webgl)]
1036    CopyExternalImageToTexture(Box<ExternalImageToTextureCopy>),
1037    CopyTextureToTexture(Box<TextureToTextureCopy>),
1038    CopyBufferToTexture(Box<BufferToTextureCopy>),
1039    CopyTextureToBuffer(Box<TextureToBufferCopy>),
1040    SetIndexBuffer(glow::Buffer),
1041    BeginQuery(glow::Query, BindTarget),
1042    EndQuery(BindTarget),
1043    TimestampQuery(glow::Query),
1044    CopyQueryResults(Box<QueryResultsCopy>),
1045    ResetFramebuffer {
1046        is_default: bool,
1047    },
1048    BindAttachment(Box<AttachmentBinding>),
1049    ResolveAttachment(Box<AttachmentResolve>),
1050    InvalidateAttachments(InvalidatedAttachments),
1051    SetDrawColorBuffers(u8),
1052    ClearColorF {
1053        draw_buffer: u32,
1054        color: [f32; 4],
1055        is_srgb: bool,
1056    },
1057    ClearColorU(u32, [u32; 4]),
1058    ClearColorI(u32, [i32; 4]),
1059    ClearDepth(f32),
1060    ClearStencil(u32),
1061    // Clearing both the depth and stencil buffer individually appears to
1062    // result in the stencil buffer failing to clear, atleast in WebGL.
1063    // It is also more efficient to emit a single command instead of two for
1064    // this.
1065    ClearDepthAndStencil(f32, u32),
1066    BufferBarrier(glow::Buffer, wgt::BufferUses),
1067    TextureBarrier(wgt::TextureUses),
1068    SetViewport {
1069        rect: crate::Rect<i32>,
1070        depth: Range<f32>,
1071    },
1072    SetScissor(crate::Rect<i32>),
1073    SetStencilFunc {
1074        face: u32,
1075        function: u32,
1076        reference: u32,
1077        read_mask: u32,
1078    },
1079    SetStencilOps {
1080        face: u32,
1081        write_mask: u32,
1082        ops: StencilOps,
1083    },
1084    SetDepth(DepthState),
1085    SetDepthBias(wgt::DepthBiasState),
1086    ConfigureDepthStencil(crate::FormatAspects),
1087    SetAlphaToCoverage(bool),
1088    SetVertexAttribute {
1089        buffer: Option<glow::Buffer>,
1090        buffer_desc: VertexBufferDesc,
1091        attribute_desc: AttributeDesc,
1092    },
1093    UnsetVertexAttribute(u32),
1094    SetVertexBuffer {
1095        index: u32,
1096        buffer: BufferBinding,
1097        buffer_desc: VertexBufferDesc,
1098    },
1099    SetProgram(Arc<PipelineInner>),
1100    SetPrimitive(PrimitiveState),
1101    SetBlendConstant([f32; 4]),
1102    SetColorTarget {
1103        draw_buffer_index: Option<u32>,
1104        desc: ColorTargetDesc,
1105    },
1106    BindBuffer {
1107        target: BindTarget,
1108        slot: u32,
1109        buffer: glow::Buffer,
1110        offset: i32,
1111        size: i32,
1112    },
1113    BindSampler(u32, Option<glow::Sampler>),
1114    BindTexture {
1115        slot: u32,
1116        texture: glow::Texture,
1117        target: BindTarget,
1118        aspects: crate::FormatAspects,
1119        mip_levels: Range<u32>,
1120    },
1121    BindImage {
1122        slot: u32,
1123        binding: ImageBinding,
1124    },
1125    InsertDebugMarker(Range<u32>),
1126    PushDebugGroup(Range<u32>),
1127    PopDebugGroup,
1128    SetImmediates {
1129        /// The whole immediate data block in `data_bytes`, as the encoder
1130        /// last set it.
1131        data: Range<u32>,
1132    },
1133    SetClipDistances {
1134        old_count: u32,
1135        new_count: u32,
1136    },
1137}
1138
1139#[derive(Default)]
1140pub struct CommandBuffer {
1141    label: Option<String>,
1142    commands: Vec<Command>,
1143    data_bytes: Vec<u8>,
1144    queries: Vec<glow::Query>,
1145}
1146
1147impl crate::DynCommandBuffer for CommandBuffer {}
1148
1149impl fmt::Debug for CommandBuffer {
1150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151        let mut builder = f.debug_struct("CommandBuffer");
1152        if let Some(ref label) = self.label {
1153            builder.field("label", label);
1154        }
1155        builder.finish()
1156    }
1157}
1158
1159#[cfg(send_sync)]
1160unsafe impl Sync for CommandBuffer {}
1161#[cfg(send_sync)]
1162unsafe impl Send for CommandBuffer {}
1163
1164//TODO: we would have something like `Arc<typed_arena::Arena>`
1165// here and in the command buffers. So that everything grows
1166// inside the encoder and stays there until `reset_all`.
1167
1168pub struct CommandEncoder {
1169    cmd_buffer: CommandBuffer,
1170    state: command::State,
1171    private_caps: PrivateCapabilities,
1172    counters: Arc<wgt::HalCounters>,
1173}
1174
1175impl fmt::Debug for CommandEncoder {
1176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1177        f.debug_struct("CommandEncoder")
1178            .field("cmd_buffer", &self.cmd_buffer)
1179            .finish()
1180    }
1181}
1182
1183#[cfg(send_sync)]
1184unsafe impl Sync for CommandEncoder {}
1185#[cfg(send_sync)]
1186unsafe impl Send for CommandEncoder {}
1187
1188#[cfg(not(webgl))]
1189fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
1190    let source_str = match source {
1191        glow::DEBUG_SOURCE_API => "API",
1192        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
1193        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
1194        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
1195        glow::DEBUG_SOURCE_APPLICATION => "Application",
1196        glow::DEBUG_SOURCE_OTHER => "Other",
1197        _ => unreachable!(),
1198    };
1199
1200    let log_severity = match severity {
1201        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
1202        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
1203        glow::DEBUG_SEVERITY_LOW => log::Level::Debug,
1204        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
1205        _ => unreachable!(),
1206    };
1207
1208    let type_str = match gltype {
1209        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
1210        glow::DEBUG_TYPE_ERROR => "Error",
1211        glow::DEBUG_TYPE_MARKER => "Marker",
1212        glow::DEBUG_TYPE_OTHER => "Other",
1213        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
1214        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
1215        glow::DEBUG_TYPE_PORTABILITY => "Portability",
1216        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
1217        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
1218        _ => unreachable!(),
1219    };
1220
1221    let _ = std::panic::catch_unwind(|| {
1222        log::log!(
1223            log_severity,
1224            "GLES: [{source_str}/{type_str}] ID {id} : {message}"
1225        );
1226    });
1227
1228    #[cfg(feature = "validation_canary")]
1229    if cfg!(debug_assertions) && log_severity == log::Level::Error {
1230        // Set canary and continue
1231        crate::VALIDATION_CANARY.add(message.to_string());
1232    }
1233}
1234
1235// If we are using `std`, then use `Mutex` to provide `Send` and `Sync`
1236cfg_if::cfg_if! {
1237    if #[cfg(gles_with_std)] {
1238        type MaybeMutex<T> = std::sync::Mutex<T>;
1239
1240        fn lock<T>(mutex: &MaybeMutex<T>) -> std::sync::MutexGuard<'_, T> {
1241            mutex.lock().unwrap()
1242        }
1243    } else {
1244        // It should be impossible for any build configuration to trigger this error
1245        // It is intended only as a guard against changes elsewhere causing the use of
1246        // `RefCell` here to become unsound.
1247        #[cfg(all(send_sync, not(feature = "fragile-send-sync-non-atomic-wasm")))]
1248        compile_error!("cannot provide non-fragile Send+Sync without std");
1249
1250        type MaybeMutex<T> = core::cell::RefCell<T>;
1251
1252        fn lock<T>(mutex: &MaybeMutex<T>) -> core::cell::RefMut<'_, T> {
1253            mutex.borrow_mut()
1254        }
1255    }
1256}