wgpu_hal/gles/
mod.rs

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