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_IMMEDIATES: usize = 64;
140// We have to account for each immediate data may need to be set for every shader.
141const MAX_IMMEDIATES_COMMANDS: usize = MAX_IMMEDIATES * 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        /// Supports direct multisampled rendering to a texture without needing a resolve texture.
236        const MULTISAMPLED_RENDER_TO_TEXTURE = 1 << 17;
237    }
238}
239
240bitflags::bitflags! {
241    /// Flags that indicate necessary workarounds for specific devices or driver bugs
242    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
243    struct Workarounds: u32 {
244        // Needs workaround for Intel Mesa bug:
245        // https://gitlab.freedesktop.org/mesa/mesa/-/issues/2565.
246        //
247        // This comment
248        // (https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4972/diffs?diff_id=75888#22f5d1004713c9bbf857988c7efb81631ab88f99_323_327)
249        // seems to indicate all skylake models are effected.
250        const MESA_I915_SRGB_SHADER_CLEAR = 1 << 0;
251        /// Buffer map must emulated because it is not supported natively
252        const EMULATE_BUFFER_MAP = 1 << 1;
253    }
254}
255
256type BindTarget = u32;
257
258#[derive(Debug, Default, Clone, Copy)]
259enum VertexAttribKind {
260    #[default]
261    Float, // glVertexAttribPointer
262    Integer, // glVertexAttribIPointer
263             //Double,  // glVertexAttribLPointer
264}
265
266#[derive(Clone, Debug)]
267pub struct TextureFormatDesc {
268    pub internal: u32,
269    pub external: u32,
270    pub data_type: u32,
271}
272
273struct AdapterShared {
274    context: AdapterContext,
275    private_caps: PrivateCapabilities,
276    features: wgt::Features,
277    limits: wgt::Limits,
278    workarounds: Workarounds,
279    options: wgt::GlBackendOptions,
280    shading_language_version: naga::back::glsl::Version,
281    next_shader_id: AtomicU32,
282    program_cache: Mutex<ProgramCache>,
283    es: bool,
284
285    /// Result of `gl.get_parameter_i32(glow::MAX_SAMPLES)`.
286    /// Cached here so it doesn't need to be queried every time texture format capabilities are requested.
287    /// (this has been shown to be a significant enough overhead)
288    max_msaa_samples: i32,
289}
290
291pub struct Adapter {
292    shared: Arc<AdapterShared>,
293}
294
295pub struct Device {
296    shared: Arc<AdapterShared>,
297    main_vao: glow::VertexArray,
298    #[cfg(all(native, feature = "renderdoc"))]
299    render_doc: crate::auxil::renderdoc::RenderDoc,
300    counters: Arc<wgt::HalCounters>,
301}
302
303impl Drop for Device {
304    fn drop(&mut self) {
305        let gl = &self.shared.context.lock();
306        unsafe { gl.delete_vertex_array(self.main_vao) };
307    }
308}
309
310pub struct ShaderClearProgram {
311    pub program: glow::Program,
312    pub color_uniform_location: glow::UniformLocation,
313}
314
315pub struct Queue {
316    shared: Arc<AdapterShared>,
317    features: wgt::Features,
318    draw_fbo: glow::Framebuffer,
319    copy_fbo: glow::Framebuffer,
320    /// Shader program used to clear the screen for [`Workarounds::MESA_I915_SRGB_SHADER_CLEAR`]
321    /// devices.
322    shader_clear_program: Option<ShaderClearProgram>,
323    /// Keep a reasonably large buffer filled with zeroes, so that we can implement `ClearBuffer` of
324    /// zeroes by copying from it.
325    zero_buffer: glow::Buffer,
326    temp_query_results: Mutex<Vec<u64>>,
327    draw_buffer_count: AtomicU8,
328    current_index_buffer: Mutex<Option<glow::Buffer>>,
329}
330
331impl Drop for Queue {
332    fn drop(&mut self) {
333        let gl = &self.shared.context.lock();
334        unsafe { gl.delete_framebuffer(self.draw_fbo) };
335        unsafe { gl.delete_framebuffer(self.copy_fbo) };
336        unsafe { gl.delete_buffer(self.zero_buffer) };
337    }
338}
339
340#[derive(Clone, Debug)]
341pub struct Buffer {
342    raw: Option<glow::Buffer>,
343    target: BindTarget,
344    size: wgt::BufferAddress,
345    /// Flags to use within calls to [`Device::map_buffer`](crate::Device::map_buffer).
346    map_flags: u32,
347    data: Option<Arc<MaybeMutex<Vec<u8>>>>,
348    offset_of_current_mapping: Arc<MaybeMutex<wgt::BufferAddress>>,
349}
350
351#[cfg(send_sync)]
352unsafe impl Sync for Buffer {}
353#[cfg(send_sync)]
354unsafe impl Send for Buffer {}
355
356impl crate::DynBuffer for Buffer {}
357
358#[derive(Clone, Debug)]
359pub enum TextureInner {
360    Renderbuffer {
361        raw: glow::Renderbuffer,
362    },
363    DefaultRenderbuffer,
364    Texture {
365        raw: glow::Texture,
366        target: BindTarget,
367    },
368    #[cfg(webgl)]
369    /// Render to a `WebGLFramebuffer`
370    ///
371    /// This is a web feature
372    ExternalFramebuffer {
373        inner: web_sys::WebGlFramebuffer,
374    },
375    #[cfg(native)]
376    /// Render to a `glow::NativeFramebuffer`
377    /// Useful when the framebuffer to draw to
378    /// has a non-zero framebuffer ID
379    ///
380    /// This is a native feature
381    ExternalNativeFramebuffer {
382        inner: glow::NativeFramebuffer,
383    },
384}
385
386#[cfg(send_sync)]
387unsafe impl Sync for TextureInner {}
388#[cfg(send_sync)]
389unsafe impl Send for TextureInner {}
390
391impl TextureInner {
392    fn as_native(&self) -> (glow::Texture, BindTarget) {
393        match *self {
394            Self::Renderbuffer { .. } | Self::DefaultRenderbuffer => {
395                panic!("Unexpected renderbuffer");
396            }
397            Self::Texture { raw, target } => (raw, target),
398            #[cfg(webgl)]
399            Self::ExternalFramebuffer { .. } => panic!("Unexpected external framebuffer"),
400            #[cfg(native)]
401            Self::ExternalNativeFramebuffer { .. } => panic!("unexpected external framebuffer"),
402        }
403    }
404}
405
406#[derive(Debug)]
407pub struct Texture {
408    pub inner: TextureInner,
409    pub mip_level_count: u32,
410    pub array_layer_count: u32,
411    pub format: wgt::TextureFormat,
412    pub format_desc: TextureFormatDesc,
413    pub copy_size: CopyExtent,
414
415    // The `drop_guard` field must be the last field of this struct so it is dropped last.
416    // Do not add new fields after it.
417    pub drop_guard: Option<crate::DropGuard>,
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<[Option<BindGroupLayoutInfo>]>,
559    naga_options: naga::back::glsl::Options,
560}
561
562impl crate::DynPipelineLayout for PipelineLayout {}
563
564impl PipelineLayout {
565    /// # Panics
566    /// If the pipeline layout does not contain a bind group layout used by
567    /// the resource binding.
568    fn get_slot(&self, br: &naga::ResourceBinding) -> u8 {
569        let group_info = self.group_infos[br.group as usize].as_ref().unwrap();
570        group_info.binding_to_slot[br.binding as usize]
571    }
572}
573
574#[derive(Debug)]
575enum BindingRegister {
576    UniformBuffers,
577    StorageBuffers,
578    Textures,
579    Images,
580}
581
582#[derive(Debug)]
583enum RawBinding {
584    Buffer {
585        raw: glow::Buffer,
586        offset: i32,
587        size: i32,
588    },
589    Texture {
590        raw: glow::Texture,
591        target: BindTarget,
592        aspects: crate::FormatAspects,
593        mip_levels: Range<u32>,
594        //TODO: array layers
595    },
596    Image(ImageBinding),
597    Sampler(glow::Sampler),
598}
599
600#[derive(Debug)]
601pub struct BindGroup {
602    contents: Box<[RawBinding]>,
603}
604
605impl crate::DynBindGroup for BindGroup {}
606
607type ShaderId = u32;
608
609#[derive(Debug)]
610pub enum ShaderModuleSource {
611    Naga(crate::NagaShader),
612    Passthrough { source: String },
613}
614
615#[derive(Debug)]
616pub struct ShaderModule {
617    source: ShaderModuleSource,
618    label: Option<String>,
619    id: ShaderId,
620}
621
622impl crate::DynShaderModule for ShaderModule {}
623
624#[derive(Clone, Debug, Default)]
625struct VertexFormatDesc {
626    element_count: i32,
627    element_format: u32,
628    attrib_kind: VertexAttribKind,
629}
630
631#[derive(Clone, Debug, Default)]
632struct AttributeDesc {
633    location: u32,
634    offset: u32,
635    buffer_index: u32,
636    format_desc: VertexFormatDesc,
637}
638
639#[derive(Clone, Debug)]
640struct BufferBinding {
641    raw: glow::Buffer,
642    offset: wgt::BufferAddress,
643}
644
645#[derive(Clone, Debug)]
646struct ImageBinding {
647    raw: glow::Texture,
648    mip_level: u32,
649    array_layer: Option<u32>,
650    access: u32,
651    format: u32,
652}
653
654#[derive(Clone, Debug, Default, PartialEq)]
655struct VertexBufferDesc {
656    step: wgt::VertexStepMode,
657    stride: u32,
658}
659
660#[derive(Clone, Debug)]
661struct ImmediateDesc {
662    location: glow::UniformLocation,
663    ty: naga::TypeInner,
664    offset: u32,
665    size_bytes: u32,
666}
667
668#[cfg(send_sync)]
669unsafe impl Sync for ImmediateDesc {}
670#[cfg(send_sync)]
671unsafe impl Send for ImmediateDesc {}
672
673/// For each texture in the pipeline layout, store the index of the only
674/// sampler (in this layout) that the texture is used with.
675type SamplerBindMap = [Option<u8>; MAX_TEXTURE_SLOTS];
676
677#[derive(Debug)]
678struct PipelineInner {
679    program: glow::Program,
680    sampler_map: SamplerBindMap,
681    first_instance_location: Option<glow::UniformLocation>,
682    immediates_descs: ArrayVec<ImmediateDesc, MAX_IMMEDIATES_COMMANDS>,
683    clip_distance_count: u32,
684}
685
686#[derive(Clone, Debug)]
687struct DepthState {
688    function: u32,
689    mask: bool,
690}
691
692#[derive(Clone, Debug, PartialEq)]
693struct BlendComponent {
694    src: u32,
695    dst: u32,
696    equation: u32,
697}
698
699#[derive(Clone, Debug, PartialEq)]
700struct BlendDesc {
701    alpha: BlendComponent,
702    color: BlendComponent,
703}
704
705#[derive(Clone, Debug, Default, PartialEq)]
706struct ColorTargetDesc {
707    mask: wgt::ColorWrites,
708    blend: Option<BlendDesc>,
709}
710
711#[derive(PartialEq, Eq, Hash)]
712struct ProgramStage {
713    naga_stage: naga::ShaderStage,
714    shader_id: ShaderId,
715    entry_point: String,
716    zero_initialize_workgroup_memory: bool,
717    constant_hash: Vec<u8>,
718}
719
720#[derive(PartialEq, Eq, Hash)]
721struct ProgramCacheKey {
722    stages: ArrayVec<ProgramStage, 3>,
723    group_to_binding_to_slot: Box<[Option<Box<[u8]>>]>,
724}
725
726type ProgramCache = FastHashMap<ProgramCacheKey, Result<Arc<PipelineInner>, crate::PipelineError>>;
727
728#[derive(Debug)]
729pub struct RenderPipeline {
730    inner: Arc<PipelineInner>,
731    primitive: wgt::PrimitiveState,
732    vertex_buffers: Box<[VertexBufferDesc]>,
733    vertex_attributes: Box<[AttributeDesc]>,
734    color_targets: Box<[ColorTargetDesc]>,
735    depth: Option<DepthState>,
736    depth_bias: wgt::DepthBiasState,
737    stencil: Option<StencilState>,
738    alpha_to_coverage_enabled: bool,
739}
740
741impl crate::DynRenderPipeline for RenderPipeline {}
742
743#[cfg(send_sync)]
744unsafe impl Sync for RenderPipeline {}
745#[cfg(send_sync)]
746unsafe impl Send for RenderPipeline {}
747
748#[derive(Debug)]
749pub struct ComputePipeline {
750    inner: Arc<PipelineInner>,
751}
752
753impl crate::DynComputePipeline for ComputePipeline {}
754
755#[cfg(send_sync)]
756unsafe impl Sync for ComputePipeline {}
757#[cfg(send_sync)]
758unsafe impl Send for ComputePipeline {}
759
760#[derive(Debug)]
761pub struct QuerySet {
762    queries: Box<[glow::Query]>,
763    target: BindTarget,
764}
765
766impl crate::DynQuerySet for QuerySet {}
767
768#[derive(Debug)]
769pub struct AccelerationStructure;
770
771impl crate::DynAccelerationStructure for AccelerationStructure {}
772
773#[derive(Debug)]
774pub struct PipelineCache;
775
776impl crate::DynPipelineCache for PipelineCache {}
777
778#[derive(Clone, Debug, PartialEq)]
779struct StencilOps {
780    pass: u32,
781    fail: u32,
782    depth_fail: u32,
783}
784
785impl Default for StencilOps {
786    fn default() -> Self {
787        Self {
788            pass: glow::KEEP,
789            fail: glow::KEEP,
790            depth_fail: glow::KEEP,
791        }
792    }
793}
794
795#[derive(Clone, Debug, PartialEq)]
796struct StencilSide {
797    function: u32,
798    mask_read: u32,
799    mask_write: u32,
800    reference: u32,
801    ops: StencilOps,
802}
803
804impl Default for StencilSide {
805    fn default() -> Self {
806        Self {
807            function: glow::ALWAYS,
808            mask_read: 0xFF,
809            mask_write: 0xFF,
810            reference: 0,
811            ops: StencilOps::default(),
812        }
813    }
814}
815
816#[derive(Debug, Clone, Default)]
817struct StencilState {
818    front: StencilSide,
819    back: StencilSide,
820}
821
822#[derive(Clone, Debug, Default, PartialEq)]
823struct PrimitiveState {
824    front_face: u32,
825    cull_face: u32,
826    unclipped_depth: bool,
827    polygon_mode: u32,
828}
829
830type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>;
831
832#[derive(Debug)]
833enum Command {
834    Draw {
835        topology: u32,
836        first_vertex: u32,
837        vertex_count: u32,
838        first_instance: u32,
839        instance_count: u32,
840        first_instance_location: Option<glow::UniformLocation>,
841    },
842    DrawIndexed {
843        topology: u32,
844        index_type: u32,
845        index_count: u32,
846        index_offset: wgt::BufferAddress,
847        base_vertex: i32,
848        first_instance: u32,
849        instance_count: u32,
850        first_instance_location: Option<glow::UniformLocation>,
851    },
852    DrawIndirect {
853        topology: u32,
854        indirect_buf: glow::Buffer,
855        indirect_offset: wgt::BufferAddress,
856        first_instance_location: Option<glow::UniformLocation>,
857    },
858    DrawIndexedIndirect {
859        topology: u32,
860        index_type: u32,
861        indirect_buf: glow::Buffer,
862        indirect_offset: wgt::BufferAddress,
863        first_instance_location: Option<glow::UniformLocation>,
864    },
865    Dispatch([u32; 3]),
866    DispatchIndirect {
867        indirect_buf: glow::Buffer,
868        indirect_offset: wgt::BufferAddress,
869    },
870    ClearBuffer {
871        dst: Buffer,
872        dst_target: BindTarget,
873        range: crate::MemoryRange,
874    },
875    CopyBufferToBuffer {
876        src: Buffer,
877        src_target: BindTarget,
878        dst: Buffer,
879        dst_target: BindTarget,
880        copy: crate::BufferCopy,
881    },
882    #[cfg(webgl)]
883    CopyExternalImageToTexture {
884        src: wgt::CopyExternalImageSourceInfo,
885        dst: glow::Texture,
886        dst_target: BindTarget,
887        dst_format: wgt::TextureFormat,
888        dst_premultiplication: bool,
889        copy: crate::TextureCopy,
890    },
891    CopyTextureToTexture {
892        src: glow::Texture,
893        src_target: BindTarget,
894        dst: glow::Texture,
895        dst_target: BindTarget,
896        copy: crate::TextureCopy,
897    },
898    CopyBufferToTexture {
899        src: Buffer,
900        #[allow(unused)]
901        src_target: BindTarget,
902        dst: glow::Texture,
903        dst_target: BindTarget,
904        dst_format: wgt::TextureFormat,
905        copy: crate::BufferTextureCopy,
906    },
907    CopyTextureToBuffer {
908        src: glow::Texture,
909        src_target: BindTarget,
910        src_format: wgt::TextureFormat,
911        dst: Buffer,
912        #[allow(unused)]
913        dst_target: BindTarget,
914        copy: crate::BufferTextureCopy,
915    },
916    SetIndexBuffer(glow::Buffer),
917    BeginQuery(glow::Query, BindTarget),
918    EndQuery(BindTarget),
919    TimestampQuery(glow::Query),
920    CopyQueryResults {
921        query_range: Range<u32>,
922        dst: Buffer,
923        dst_target: BindTarget,
924        dst_offset: wgt::BufferAddress,
925    },
926    ResetFramebuffer {
927        is_default: bool,
928    },
929    BindAttachment {
930        attachment: u32,
931        view: TextureView,
932        depth_slice: Option<u32>,
933        sample_count: u32,
934    },
935    ResolveAttachment {
936        attachment: u32,
937        dst: TextureView,
938        size: wgt::Extent3d,
939    },
940    InvalidateAttachments(InvalidatedAttachments),
941    SetDrawColorBuffers(u8),
942    ClearColorF {
943        draw_buffer: u32,
944        color: [f32; 4],
945        is_srgb: bool,
946    },
947    ClearColorU(u32, [u32; 4]),
948    ClearColorI(u32, [i32; 4]),
949    ClearDepth(f32),
950    ClearStencil(u32),
951    // Clearing both the depth and stencil buffer individually appears to
952    // result in the stencil buffer failing to clear, atleast in WebGL.
953    // It is also more efficient to emit a single command instead of two for
954    // this.
955    ClearDepthAndStencil(f32, u32),
956    BufferBarrier(glow::Buffer, wgt::BufferUses),
957    TextureBarrier(wgt::TextureUses),
958    SetViewport {
959        rect: crate::Rect<i32>,
960        depth: Range<f32>,
961    },
962    SetScissor(crate::Rect<i32>),
963    SetStencilFunc {
964        face: u32,
965        function: u32,
966        reference: u32,
967        read_mask: u32,
968    },
969    SetStencilOps {
970        face: u32,
971        write_mask: u32,
972        ops: StencilOps,
973    },
974    SetDepth(DepthState),
975    SetDepthBias(wgt::DepthBiasState),
976    ConfigureDepthStencil(crate::FormatAspects),
977    SetAlphaToCoverage(bool),
978    SetVertexAttribute {
979        buffer: Option<glow::Buffer>,
980        buffer_desc: VertexBufferDesc,
981        attribute_desc: AttributeDesc,
982    },
983    UnsetVertexAttribute(u32),
984    SetVertexBuffer {
985        index: u32,
986        buffer: BufferBinding,
987        buffer_desc: VertexBufferDesc,
988    },
989    SetProgram(glow::Program),
990    SetPrimitive(PrimitiveState),
991    SetBlendConstant([f32; 4]),
992    SetColorTarget {
993        draw_buffer_index: Option<u32>,
994        desc: ColorTargetDesc,
995    },
996    BindBuffer {
997        target: BindTarget,
998        slot: u32,
999        buffer: glow::Buffer,
1000        offset: i32,
1001        size: i32,
1002    },
1003    BindSampler(u32, Option<glow::Sampler>),
1004    BindTexture {
1005        slot: u32,
1006        texture: glow::Texture,
1007        target: BindTarget,
1008        aspects: crate::FormatAspects,
1009        mip_levels: Range<u32>,
1010    },
1011    BindImage {
1012        slot: u32,
1013        binding: ImageBinding,
1014    },
1015    InsertDebugMarker(Range<u32>),
1016    PushDebugGroup(Range<u32>),
1017    PopDebugGroup,
1018    SetImmediates {
1019        uniform: ImmediateDesc,
1020        /// Offset from the start of the `data_bytes`
1021        offset: u32,
1022    },
1023    SetClipDistances {
1024        old_count: u32,
1025        new_count: u32,
1026    },
1027}
1028
1029#[derive(Default)]
1030pub struct CommandBuffer {
1031    label: Option<String>,
1032    commands: Vec<Command>,
1033    data_bytes: Vec<u8>,
1034    queries: Vec<glow::Query>,
1035}
1036
1037impl crate::DynCommandBuffer for CommandBuffer {}
1038
1039impl fmt::Debug for CommandBuffer {
1040    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1041        let mut builder = f.debug_struct("CommandBuffer");
1042        if let Some(ref label) = self.label {
1043            builder.field("label", label);
1044        }
1045        builder.finish()
1046    }
1047}
1048
1049#[cfg(send_sync)]
1050unsafe impl Sync for CommandBuffer {}
1051#[cfg(send_sync)]
1052unsafe impl Send for CommandBuffer {}
1053
1054//TODO: we would have something like `Arc<typed_arena::Arena>`
1055// here and in the command buffers. So that everything grows
1056// inside the encoder and stays there until `reset_all`.
1057
1058pub struct CommandEncoder {
1059    cmd_buffer: CommandBuffer,
1060    state: command::State,
1061    private_caps: PrivateCapabilities,
1062    counters: Arc<wgt::HalCounters>,
1063}
1064
1065impl fmt::Debug for CommandEncoder {
1066    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1067        f.debug_struct("CommandEncoder")
1068            .field("cmd_buffer", &self.cmd_buffer)
1069            .finish()
1070    }
1071}
1072
1073#[cfg(send_sync)]
1074unsafe impl Sync for CommandEncoder {}
1075#[cfg(send_sync)]
1076unsafe impl Send for CommandEncoder {}
1077
1078#[cfg(not(webgl))]
1079fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
1080    let source_str = match source {
1081        glow::DEBUG_SOURCE_API => "API",
1082        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
1083        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
1084        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
1085        glow::DEBUG_SOURCE_APPLICATION => "Application",
1086        glow::DEBUG_SOURCE_OTHER => "Other",
1087        _ => unreachable!(),
1088    };
1089
1090    let log_severity = match severity {
1091        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
1092        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
1093        glow::DEBUG_SEVERITY_LOW => log::Level::Debug,
1094        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
1095        _ => unreachable!(),
1096    };
1097
1098    let type_str = match gltype {
1099        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
1100        glow::DEBUG_TYPE_ERROR => "Error",
1101        glow::DEBUG_TYPE_MARKER => "Marker",
1102        glow::DEBUG_TYPE_OTHER => "Other",
1103        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
1104        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
1105        glow::DEBUG_TYPE_PORTABILITY => "Portability",
1106        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
1107        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
1108        _ => unreachable!(),
1109    };
1110
1111    let _ = std::panic::catch_unwind(|| {
1112        log::log!(
1113            log_severity,
1114            "GLES: [{source_str}/{type_str}] ID {id} : {message}"
1115        );
1116    });
1117
1118    #[cfg(feature = "validation_canary")]
1119    if cfg!(debug_assertions) && log_severity == log::Level::Error {
1120        // Set canary and continue
1121        crate::VALIDATION_CANARY.add(message.to_string());
1122    }
1123}
1124
1125// If we are using `std`, then use `Mutex` to provide `Send` and `Sync`
1126cfg_if::cfg_if! {
1127    if #[cfg(gles_with_std)] {
1128        type MaybeMutex<T> = std::sync::Mutex<T>;
1129
1130        fn lock<T>(mutex: &MaybeMutex<T>) -> std::sync::MutexGuard<'_, T> {
1131            mutex.lock().unwrap()
1132        }
1133    } else {
1134        // It should be impossible for any build configuration to trigger this error
1135        // It is intended only as a guard against changes elsewhere causing the use of
1136        // `RefCell` here to become unsound.
1137        #[cfg(all(send_sync, not(feature = "fragile-send-sync-non-atomic-wasm")))]
1138        compile_error!("cannot provide non-fragile Send+Sync without std");
1139
1140        type MaybeMutex<T> = core::cell::RefCell<T>;
1141
1142        fn lock<T>(mutex: &MaybeMutex<T>) -> core::cell::RefMut<'_, T> {
1143            mutex.borrow_mut()
1144        }
1145    }
1146}