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