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