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