Skip to main content

wgpu_hal/gles/
device.rs

1use alloc::{
2    borrow::{Cow, ToOwned},
3    format,
4    string::String,
5    string::ToString as _,
6    sync::Arc,
7    vec,
8    vec::Vec,
9};
10use core::{cmp::max, convert::TryInto, num::NonZeroU32, ptr, sync::atomic::Ordering};
11
12use arrayvec::ArrayVec;
13use glow::HasContext;
14use naga::FastHashMap;
15
16use super::{conv, lock, MaybeMutex, PrivateCapabilities};
17use crate::auxil::map_naga_stage;
18use crate::TlasInstance;
19
20type ShaderStage<'a> = (
21    naga::ShaderStage,
22    &'a crate::ProgrammableStage<'a, super::ShaderModule>,
23);
24type NameBindingMap = FastHashMap<String, (super::BindingRegister, u8)>;
25
26struct CompilationContext<'a> {
27    layout: &'a super::PipelineLayout,
28    sampler_map: &'a mut super::SamplerBindMap,
29    name_binding_map: &'a mut NameBindingMap,
30    immediates_items: &'a mut Vec<naga::back::glsl::ImmediateItem>,
31    multiview_mask: Option<NonZeroU32>,
32    clip_distance_count: &'a mut u32,
33    /// Whether the vertex stage writes `gl_PointSize`: only a pipeline that
34    /// draws points needs it, and WebGL counts it against the varyings.
35    force_point_size: bool,
36}
37
38impl CompilationContext<'_> {
39    fn consume_reflection(
40        self,
41        gl: &glow::Context,
42        module: &naga::Module,
43        ep_info: &naga::valid::FunctionInfo,
44        reflection_info: naga::back::glsl::ReflectionInfo,
45        naga_stage: naga::ShaderStage,
46        program: glow::Program,
47    ) {
48        for (handle, var) in module.global_variables.iter() {
49            if ep_info[handle].is_empty() {
50                continue;
51            }
52            let register = match var.space {
53                naga::AddressSpace::Uniform => super::BindingRegister::UniformBuffers,
54                naga::AddressSpace::Storage { .. } => super::BindingRegister::StorageBuffers,
55                _ => continue,
56            };
57
58            let br = var.binding.as_ref().unwrap();
59            let slot = self.layout.get_slot(br);
60
61            let name = match reflection_info.uniforms.get(&handle) {
62                Some(name) => name.clone(),
63                None => continue,
64            };
65            log::trace!(
66                "Rebind buffer: {:?} -> {}, register={:?}, slot={}",
67                var.name.as_ref(),
68                &name,
69                register,
70                slot
71            );
72            self.name_binding_map.insert(name, (register, slot));
73        }
74
75        for (name, mapping) in reflection_info.texture_mapping {
76            let var = &module.global_variables[mapping.texture];
77            let register = match module.types[var.ty].inner {
78                naga::TypeInner::Image {
79                    class: naga::ImageClass::Storage { .. },
80                    ..
81                } => super::BindingRegister::Images,
82                _ => super::BindingRegister::Textures,
83            };
84
85            let tex_br = var.binding.as_ref().unwrap();
86            let texture_linear_index = self.layout.get_slot(tex_br);
87
88            self.name_binding_map
89                .insert(name, (register, texture_linear_index));
90            if let Some(sampler_handle) = mapping.sampler {
91                let sam_br = module.global_variables[sampler_handle]
92                    .binding
93                    .as_ref()
94                    .unwrap();
95                let sampler_linear_index = self.layout.get_slot(sam_br);
96                self.sampler_map[texture_linear_index as usize] = Some(sampler_linear_index);
97            }
98        }
99
100        for (name, location) in reflection_info.varying {
101            match naga_stage {
102                naga::ShaderStage::Vertex => {
103                    assert_eq!(location.index, 0);
104                    unsafe { gl.bind_attrib_location(program, location.location, &name) }
105                }
106                naga::ShaderStage::Fragment => {
107                    assert_eq!(location.index, 0);
108                    unsafe { gl.bind_frag_data_location(program, location.location, &name) }
109                }
110                naga::ShaderStage::Compute => {}
111                naga::ShaderStage::Task
112                | naga::ShaderStage::Mesh
113                | naga::ShaderStage::RayGeneration
114                | naga::ShaderStage::AnyHit
115                | naga::ShaderStage::ClosestHit
116                | naga::ShaderStage::Miss => unreachable!(),
117            }
118        }
119
120        *self.immediates_items = reflection_info.immediates_items;
121
122        if naga_stage == naga::ShaderStage::Vertex {
123            *self.clip_distance_count = reflection_info.clip_distance_count;
124        }
125    }
126}
127
128impl super::Device {
129    /// # Safety
130    ///
131    /// - `name` must be created respecting `desc`
132    /// - `name` must be a texture
133    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of the texture. If
134    ///   `drop_callback` is [`Some`], the texture must be valid until the callback is called.
135    #[cfg(any(native, Emscripten))]
136    pub unsafe fn texture_from_raw(
137        &self,
138        name: NonZeroU32,
139        desc: &crate::TextureDescriptor,
140        drop_callback: Option<crate::DropCallback>,
141    ) -> super::Texture {
142        super::Texture {
143            inner: super::TextureInner::Texture {
144                raw: glow::NativeTexture(name),
145                target: super::Texture::get_info_from_desc(desc),
146            },
147            drop_guard: crate::DropGuard::from_option(drop_callback),
148            mip_level_count: desc.mip_level_count,
149            array_layer_count: desc.array_layer_count(),
150            format: desc.format,
151            format_desc: self.shared.describe_texture_format(desc.format),
152            copy_size: desc.copy_extent(),
153        }
154    }
155
156    /// # Safety
157    ///
158    /// - `name` must be created respecting `desc`
159    /// - `name` must be a renderbuffer
160    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of the renderbuffer. If
161    ///   `drop_callback` is [`Some`], the renderbuffer must be valid until the callback is called.
162    #[cfg(any(native, Emscripten))]
163    pub unsafe fn texture_from_raw_renderbuffer(
164        &self,
165        name: NonZeroU32,
166        desc: &crate::TextureDescriptor,
167        drop_callback: Option<crate::DropCallback>,
168    ) -> super::Texture {
169        super::Texture {
170            inner: super::TextureInner::Renderbuffer {
171                raw: glow::NativeRenderbuffer(name),
172            },
173            drop_guard: crate::DropGuard::from_option(drop_callback),
174            mip_level_count: desc.mip_level_count,
175            array_layer_count: desc.array_layer_count(),
176            format: desc.format,
177            format_desc: self.shared.describe_texture_format(desc.format),
178            copy_size: desc.copy_extent(),
179        }
180    }
181
182    /// # Safety
183    ///
184    /// - `name` must be a non-zero GL buffer name created respecting `desc`.
185    /// - The buffer's storage size must be at least `desc.size`.
186    /// - If `desc.usage` includes [`BufferUses::MAP_READ`](wgt::BufferUses::MAP_READ) or
187    ///   [`BufferUses::MAP_WRITE`](wgt::BufferUses::MAP_WRITE), the GL buffer must have
188    ///   been allocated with `glBufferStorage` (or equivalent) using flags compatible
189    ///   with persistent mapping (`GL_MAP_PERSISTENT_BIT` plus matching read/write/coherent
190    ///   bits). Buffers created with the legacy `glBufferData` family cannot be mapped
191    ///   through this path.
192    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of the buffer and
193    ///   call `glDeleteBuffers` on it. If `drop_callback` is [`Some`], the buffer must
194    ///   remain valid until the callback is invoked.
195    #[cfg(any(native, Emscripten))]
196    pub unsafe fn buffer_from_raw(
197        &self,
198        name: NonZeroU32,
199        desc: &crate::BufferDescriptor,
200        drop_callback: Option<crate::DropCallback>,
201    ) -> super::Buffer {
202        let target = if desc.usage.contains(wgt::BufferUses::INDEX) {
203            glow::ELEMENT_ARRAY_BUFFER
204        } else {
205            glow::ARRAY_BUFFER
206        };
207
208        let is_host_visible = desc
209            .usage
210            .intersects(wgt::BufferUses::MAP_READ | wgt::BufferUses::MAP_WRITE);
211        let is_coherent = desc
212            .memory_flags
213            .contains(crate::MemoryFlags::PREFER_COHERENT);
214
215        let mut map_flags = 0;
216        if desc.usage.contains(wgt::BufferUses::MAP_READ) {
217            map_flags |= glow::MAP_READ_BIT;
218        }
219        if desc.usage.contains(wgt::BufferUses::MAP_WRITE) {
220            map_flags |= glow::MAP_WRITE_BIT;
221        }
222        if is_host_visible {
223            map_flags |= glow::MAP_PERSISTENT_BIT;
224            if is_coherent {
225                map_flags |= glow::MAP_COHERENT_BIT;
226            }
227        }
228        if !is_coherent && desc.usage.contains(wgt::BufferUses::MAP_WRITE) {
229            map_flags |= glow::MAP_FLUSH_EXPLICIT_BIT;
230        }
231
232        self.counters.buffers.add(1);
233
234        super::Buffer {
235            raw: Some(glow::NativeBuffer(name)),
236            target,
237            size: desc.size,
238            map_flags,
239            map_state: Arc::new(MaybeMutex::new(super::BufferMapState {
240                mapped: false,
241                data: None,
242                offset_of_current_mapping: 0,
243            })),
244            drop_guard: crate::DropGuard::from_option(drop_callback).map(Arc::new),
245        }
246    }
247
248    unsafe fn compile_shader(
249        gl: &glow::Context,
250        shader: &str,
251        naga_stage: naga::ShaderStage,
252        #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>,
253    ) -> Result<glow::Shader, crate::PipelineError> {
254        let target = match naga_stage {
255            naga::ShaderStage::Vertex => glow::VERTEX_SHADER,
256            naga::ShaderStage::Fragment => glow::FRAGMENT_SHADER,
257            naga::ShaderStage::Compute => glow::COMPUTE_SHADER,
258            naga::ShaderStage::Task
259            | naga::ShaderStage::Mesh
260            | naga::ShaderStage::RayGeneration
261            | naga::ShaderStage::AnyHit
262            | naga::ShaderStage::ClosestHit
263            | naga::ShaderStage::Miss => unreachable!(),
264        };
265
266        let raw = unsafe { gl.create_shader(target) }.unwrap();
267        #[cfg(native)]
268        if gl.supports_debug() {
269            let name = raw.0.get();
270            unsafe { gl.object_label(glow::SHADER, name, label) };
271        }
272
273        unsafe { gl.shader_source(raw, shader) };
274        unsafe { gl.compile_shader(raw) };
275
276        log::debug!("\tCompiled shader {raw:?}");
277
278        let compiled_ok = unsafe { gl.get_shader_compile_status(raw) };
279        let msg = unsafe { gl.get_shader_info_log(raw) };
280        if compiled_ok {
281            if !msg.is_empty() {
282                log::debug!("\tCompile message: {msg}");
283            }
284            Ok(raw)
285        } else {
286            log::error!("\tShader compilation failed: {msg}");
287            unsafe { gl.delete_shader(raw) };
288            Err(crate::PipelineError::Linkage(
289                map_naga_stage(naga_stage),
290                msg,
291            ))
292        }
293    }
294
295    fn create_shader(
296        gl: &glow::Context,
297        naga_stage: naga::ShaderStage,
298        stage: &crate::ProgrammableStage<super::ShaderModule>,
299        context: CompilationContext,
300        program: glow::Program,
301    ) -> Result<glow::Shader, crate::PipelineError> {
302        let source = 'outer: {
303            use naga::back::glsl;
304            let pipeline_options = glsl::PipelineOptions {
305                shader_stage: naga_stage,
306                entry_point: stage.entry_point.to_owned(),
307                multiview: context
308                    .multiview_mask
309                    .map(|a| NonZeroU32::new(a.get().count_ones()).unwrap()),
310            };
311
312            let naga = match stage.module.source {
313                super::ShaderModuleSource::Naga(ref naga) => naga,
314                super::ShaderModuleSource::Passthrough { ref source } => {
315                    break 'outer Cow::Borrowed(source);
316                }
317            };
318
319            let (module, info) = naga::back::pipeline_constants::process_overrides(
320                &naga.module,
321                &naga.info,
322                Some((naga_stage, stage.entry_point)),
323                stage.constants,
324            )
325            .map_err(|e| {
326                let msg = format!("{e}");
327                crate::PipelineError::PipelineConstants(map_naga_stage(naga_stage), msg)
328            })?;
329
330            let entry_point_index = module
331                .entry_points
332                .iter()
333                .position(|ep| ep.name.as_str() == stage.entry_point)
334                .ok_or(crate::PipelineError::EntryPoint(naga_stage))?;
335
336            use naga::proc::BoundsCheckPolicy;
337            // The image bounds checks require the TEXTURE_LEVELS feature available in GL core 4.3+.
338            let version = gl.version();
339            let image_check = if !version.is_embedded && (version.major, version.minor) >= (4, 3) {
340                BoundsCheckPolicy::ReadZeroSkipWrite
341            } else {
342                BoundsCheckPolicy::Unchecked
343            };
344
345            // Other bounds check are either provided by glsl or not implemented yet.
346            let policies = naga::proc::BoundsCheckPolicies {
347                index: BoundsCheckPolicy::Unchecked,
348                buffer: BoundsCheckPolicy::Unchecked,
349                image_load: image_check,
350                binding_array: BoundsCheckPolicy::Unchecked,
351            };
352
353            let mut output = String::new();
354            let force_point_size =
355                context.force_point_size && naga_stage == naga::ShaderStage::Vertex;
356            let needs_temp_options = stage.zero_initialize_workgroup_memory
357                != context.layout.naga_options.zero_initialize_workgroup_memory
358                || force_point_size;
359            let mut temp_options;
360            let naga_options = if needs_temp_options {
361                // We use a conditional here, as cloning the naga_options could be expensive
362                // That is, we want to avoid doing that unless we cannot avoid it
363                temp_options = context.layout.naga_options.clone();
364                temp_options.zero_initialize_workgroup_memory =
365                    stage.zero_initialize_workgroup_memory;
366                temp_options
367                    .writer_flags
368                    .set(glsl::WriterFlags::FORCE_POINT_SIZE, force_point_size);
369                &temp_options
370            } else {
371                &context.layout.naga_options
372            };
373            let mut writer = glsl::Writer::new(
374                &mut output,
375                &module,
376                &info,
377                naga_options,
378                &pipeline_options,
379                policies,
380            )
381            .map_err(|e| {
382                let msg = format!("{e}");
383                crate::PipelineError::Linkage(map_naga_stage(naga_stage), msg)
384            })?;
385
386            let reflection_info = writer.write().map_err(|e| {
387                let msg = format!("{e}");
388                crate::PipelineError::Linkage(map_naga_stage(naga_stage), msg)
389            })?;
390
391            log::debug!("Naga generated shader:\n{output}");
392
393            context.consume_reflection(
394                gl,
395                &module,
396                info.get_entry_point(entry_point_index),
397                reflection_info,
398                naga_stage,
399                program,
400            );
401            Cow::Owned(output)
402        };
403
404        unsafe { Self::compile_shader(gl, &source, naga_stage, stage.module.label.as_deref()) }
405    }
406
407    unsafe fn create_pipeline<'a>(
408        &self,
409        gl: &glow::Context,
410        shaders: ArrayVec<ShaderStage<'a>, { crate::MAX_CONCURRENT_SHADER_STAGES }>,
411        layout: &super::PipelineLayout,
412        #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>,
413        multiview_mask: Option<NonZeroU32>,
414        force_point_size: bool,
415    ) -> Result<Arc<super::PipelineInner>, crate::PipelineError> {
416        let mut program_stages = ArrayVec::new();
417        let group_to_binding_to_slot = layout
418            .group_infos
419            .iter()
420            .map(|group| group.as_ref().map(|group| group.binding_to_slot.clone()))
421            .collect::<Vec<_>>();
422        for &(naga_stage, stage) in &shaders {
423            program_stages.push(super::ProgramStage {
424                naga_stage: naga_stage.to_owned(),
425                shader_id: stage.module.id,
426                entry_point: stage.entry_point.to_owned(),
427                zero_initialize_workgroup_memory: stage.zero_initialize_workgroup_memory,
428                constant_hash: Self::create_constant_hash(stage),
429            });
430        }
431        let mut guard = self
432            .shared
433            .program_cache
434            .try_lock()
435            .expect("Couldn't acquire program_cache lock");
436        // This guard ensures that we can't accidentally destroy a program whilst we're about to reuse it
437        // The only place that destroys a pipeline is also locking on `program_cache`
438        let program = guard
439            .entry(super::ProgramCacheKey {
440                stages: program_stages,
441                group_to_binding_to_slot: group_to_binding_to_slot.into_boxed_slice(),
442                force_point_size,
443            })
444            .or_insert_with(|| unsafe {
445                Self::create_program(
446                    gl,
447                    shaders,
448                    layout,
449                    label,
450                    multiview_mask,
451                    force_point_size,
452                    self.shared.shading_language_version,
453                    self.shared.private_caps,
454                )
455            })
456            .to_owned()?;
457        drop(guard);
458
459        Ok(program)
460    }
461
462    fn create_constant_hash(stage: &crate::ProgrammableStage<super::ShaderModule>) -> Vec<u8> {
463        let mut buf: Vec<u8> = Vec::new();
464
465        for (key, value) in stage.constants.iter() {
466            buf.extend_from_slice(key.as_bytes());
467            buf.extend_from_slice(&value.to_ne_bytes());
468        }
469
470        buf
471    }
472
473    unsafe fn create_program<'a>(
474        gl: &glow::Context,
475        shaders: ArrayVec<ShaderStage<'a>, { crate::MAX_CONCURRENT_SHADER_STAGES }>,
476        layout: &super::PipelineLayout,
477        #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>,
478        multiview_mask: Option<NonZeroU32>,
479        force_point_size: bool,
480        glsl_version: naga::back::glsl::Version,
481        private_caps: PrivateCapabilities,
482    ) -> Result<Arc<super::PipelineInner>, crate::PipelineError> {
483        let glsl_version = match glsl_version {
484            naga::back::glsl::Version::Embedded { version, .. } => format!("{version} es"),
485            naga::back::glsl::Version::Desktop(version) => format!("{version}"),
486        };
487        let program = unsafe { gl.create_program() }.unwrap();
488        #[cfg(native)]
489        if let Some(label) = label {
490            if private_caps.contains(PrivateCapabilities::DEBUG_FNS) {
491                let name = program.0.get();
492                unsafe { gl.object_label(glow::PROGRAM, name, Some(label)) };
493            }
494        }
495
496        let mut name_binding_map = NameBindingMap::default();
497        let mut immediates_items = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new();
498        let mut sampler_map = [None; super::MAX_TEXTURE_SLOTS];
499        let mut has_stages = wgt::ShaderStages::empty();
500        let mut shaders_to_delete = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new();
501        let mut clip_distance_count = 0;
502
503        for &(naga_stage, stage) in &shaders {
504            has_stages |= map_naga_stage(naga_stage);
505            let pc_item = {
506                immediates_items.push(Vec::new());
507                immediates_items.last_mut().unwrap()
508            };
509            let context = CompilationContext {
510                layout,
511                sampler_map: &mut sampler_map,
512                name_binding_map: &mut name_binding_map,
513                immediates_items: pc_item,
514                multiview_mask,
515                clip_distance_count: &mut clip_distance_count,
516                force_point_size,
517            };
518
519            let shader = Self::create_shader(gl, naga_stage, stage, context, program)?;
520            shaders_to_delete.push(shader);
521        }
522
523        // Create empty fragment shader if only vertex shader is present
524        if has_stages == wgt::ShaderStages::VERTEX {
525            let shader_src = format!("#version {glsl_version}\n void main(void) {{}}",);
526            log::debug!("Only vertex shader is present. Creating an empty fragment shader",);
527            let shader = unsafe {
528                Self::compile_shader(
529                    gl,
530                    &shader_src,
531                    naga::ShaderStage::Fragment,
532                    Some("(wgpu internal) dummy fragment shader"),
533                )
534            }?;
535            shaders_to_delete.push(shader);
536        }
537
538        for &shader in shaders_to_delete.iter() {
539            unsafe { gl.attach_shader(program, shader) };
540        }
541        unsafe { gl.link_program(program) };
542
543        for shader in shaders_to_delete {
544            unsafe { gl.delete_shader(shader) };
545        }
546
547        log::debug!("\tLinked program {program:?}");
548
549        let linked_ok = unsafe { gl.get_program_link_status(program) };
550        let msg = unsafe { gl.get_program_info_log(program) };
551        if !linked_ok {
552            return Err(crate::PipelineError::Linkage(has_stages, msg));
553        }
554        if !msg.is_empty() {
555            log::debug!("\tLink message: {msg}");
556        }
557
558        if !private_caps.contains(PrivateCapabilities::SHADER_BINDING_LAYOUT) {
559            // This remapping is only needed if we aren't able to put the binding layout
560            // in the shader. We can't remap storage buffers this way.
561            unsafe { gl.use_program(Some(program)) };
562            for (ref name, (register, slot)) in name_binding_map {
563                log::trace!("Get binding {name:?} from program {program:?}");
564                match register {
565                    super::BindingRegister::UniformBuffers => {
566                        let index = unsafe { gl.get_uniform_block_index(program, name) }.unwrap();
567                        log::trace!("\tBinding slot {slot} to block index {index}");
568                        unsafe { gl.uniform_block_binding(program, index, slot as _) };
569                    }
570                    super::BindingRegister::StorageBuffers => {
571                        let index =
572                            unsafe { gl.get_shader_storage_block_index(program, name) }.unwrap();
573                        log::error!("Unable to re-map shader storage block {name} to {index}");
574                        return Err(crate::DeviceError::Lost.into());
575                    }
576                    super::BindingRegister::Textures | super::BindingRegister::Images => {
577                        let location = unsafe { gl.get_uniform_location(program, name) };
578                        unsafe { gl.uniform_1_i32(location.as_ref(), slot as _) };
579                    }
580                }
581            }
582        }
583
584        let mut uniforms = ArrayVec::new();
585
586        for stage_items in immediates_items {
587            for item in stage_items {
588                let location = unsafe { gl.get_uniform_location(program, &item.access_path) };
589
590                log::trace!(
591                    "immediate data item: name={}, ty={:?}, offset={}, location={:?}",
592                    item.access_path,
593                    item.ty,
594                    item.offset,
595                    location,
596                );
597
598                if let Some(location) = location {
599                    uniforms.push(super::ImmediateDesc {
600                        location,
601                        offset: item.offset,
602                        size_bytes: item.size_bytes,
603                        ty: item.ty,
604                    });
605                }
606            }
607        }
608
609        let first_instance_location = if has_stages.contains(wgt::ShaderStages::VERTEX) {
610            // If this returns none (the uniform isn't active), that's fine, we just won't set it.
611            unsafe { gl.get_uniform_location(program, naga::back::glsl::FIRST_INSTANCE_BINDING) }
612        } else {
613            None
614        };
615
616        Ok(Arc::new(super::PipelineInner {
617            program,
618            sampler_map,
619            first_instance_location,
620            immediates_descs: uniforms,
621            clip_distance_count,
622        }))
623    }
624}
625
626impl crate::Device for super::Device {
627    type A = super::Api;
628
629    unsafe fn create_buffer(
630        &self,
631        desc: &crate::BufferDescriptor,
632    ) -> Result<super::Buffer, crate::DeviceError> {
633        let target = if desc.usage.contains(wgt::BufferUses::INDEX) {
634            glow::ELEMENT_ARRAY_BUFFER
635        } else {
636            glow::ARRAY_BUFFER
637        };
638
639        let emulate_map = self
640            .shared
641            .workarounds
642            .contains(super::Workarounds::EMULATE_BUFFER_MAP)
643            || !self
644                .shared
645                .private_caps
646                .contains(PrivateCapabilities::BUFFER_ALLOCATION);
647
648        if emulate_map && desc.usage.intersects(wgt::BufferUses::MAP_WRITE) {
649            return Ok(super::Buffer {
650                raw: None,
651                target,
652                size: desc.size,
653                map_flags: 0,
654                map_state: Arc::new(MaybeMutex::new(super::BufferMapState {
655                    mapped: false,
656                    data: Some(vec![0; desc.size as usize]),
657                    offset_of_current_mapping: 0,
658                })),
659                drop_guard: None,
660            });
661        }
662
663        let gl = &self.shared.context.lock();
664
665        let target = if desc.usage.contains(wgt::BufferUses::INDEX) {
666            glow::ELEMENT_ARRAY_BUFFER
667        } else {
668            glow::ARRAY_BUFFER
669        };
670
671        let is_host_visible = desc
672            .usage
673            .intersects(wgt::BufferUses::MAP_READ | wgt::BufferUses::MAP_WRITE);
674        let is_coherent = desc
675            .memory_flags
676            .contains(crate::MemoryFlags::PREFER_COHERENT);
677
678        let mut map_flags = 0;
679        if desc.usage.contains(wgt::BufferUses::MAP_READ) {
680            map_flags |= glow::MAP_READ_BIT;
681        }
682        if desc.usage.contains(wgt::BufferUses::MAP_WRITE) {
683            map_flags |= glow::MAP_WRITE_BIT;
684        }
685
686        let raw = Some(unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?);
687        unsafe { gl.bind_buffer(target, raw) };
688        let raw_size = desc
689            .size
690            .try_into()
691            .map_err(|_| crate::DeviceError::OutOfMemory)?;
692
693        if self
694            .shared
695            .private_caps
696            .contains(PrivateCapabilities::BUFFER_ALLOCATION)
697        {
698            if is_host_visible {
699                map_flags |= glow::MAP_PERSISTENT_BIT;
700                if is_coherent {
701                    map_flags |= glow::MAP_COHERENT_BIT;
702                }
703            }
704            // TODO: may also be required for other calls involving `buffer_sub_data_u8_slice` (e.g. copy buffer to buffer and clear buffer)
705            if desc.usage.intersects(wgt::BufferUses::QUERY_RESOLVE) {
706                map_flags |= glow::DYNAMIC_STORAGE_BIT;
707            }
708            unsafe { gl.buffer_storage(target, raw_size, None, map_flags) };
709        } else {
710            assert!(!is_coherent);
711            let usage = if is_host_visible {
712                if desc.usage.contains(wgt::BufferUses::MAP_READ) {
713                    glow::STREAM_READ
714                } else {
715                    glow::DYNAMIC_DRAW
716                }
717            } else {
718                // Even if the usage doesn't contain SRC_READ, we update it internally at least once
719                // Some vendors take usage very literally and STATIC_DRAW will freeze us with an empty buffer
720                // https://github.com/gfx-rs/wgpu/issues/3371
721                glow::DYNAMIC_DRAW
722            };
723            unsafe { gl.buffer_data_size(target, raw_size, usage) };
724        }
725
726        unsafe { gl.bind_buffer(target, None) };
727
728        if !is_coherent && desc.usage.contains(wgt::BufferUses::MAP_WRITE) {
729            map_flags |= glow::MAP_FLUSH_EXPLICIT_BIT;
730        }
731        //TODO: do we need `glow::MAP_UNSYNCHRONIZED_BIT`?
732
733        #[cfg(native)]
734        if let Some(label) = desc.label {
735            if self
736                .shared
737                .private_caps
738                .contains(PrivateCapabilities::DEBUG_FNS)
739            {
740                let name = raw.map_or(0, |buf| buf.0.get());
741                unsafe { gl.object_label(glow::BUFFER, name, Some(label)) };
742            }
743        }
744
745        let data = if emulate_map && desc.usage.contains(wgt::BufferUses::MAP_READ) {
746            Some(vec![0; desc.size as usize])
747        } else {
748            None
749        };
750
751        self.counters.buffers.add(1);
752
753        Ok(super::Buffer {
754            raw,
755            target,
756            size: desc.size,
757            map_flags,
758            map_state: Arc::new(MaybeMutex::new(super::BufferMapState {
759                mapped: false,
760                data,
761                offset_of_current_mapping: 0,
762            })),
763            drop_guard: None,
764        })
765    }
766
767    unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
768        if buffer.drop_guard.is_none() {
769            if let Some(raw) = buffer.raw {
770                let gl = &self.shared.context.lock();
771                unsafe { gl.delete_buffer(raw) };
772            }
773        }
774
775        // For clarity, we explicitly drop the drop guard. Although this has no real semantic effect as the
776        // end of the scope will drop the drop guard since this function takes ownership of the buffer.
777        drop(buffer.drop_guard);
778
779        self.counters.buffers.sub(1);
780    }
781
782    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
783        self.counters.buffers.add(1);
784    }
785
786    unsafe fn map_buffer(
787        &self,
788        buffer: &super::Buffer,
789        range: crate::MemoryRange,
790    ) -> Result<crate::BufferMapping, crate::DeviceError> {
791        let is_coherent = buffer.map_flags & glow::MAP_COHERENT_BIT != 0;
792        let ptr = match buffer.raw {
793            None => {
794                let mut map_state = lock(&buffer.map_state);
795                let vec = map_state.data.as_mut().unwrap();
796                let slice = &mut vec.as_mut_slice()[range.start as usize..range.end as usize];
797                slice.as_mut_ptr()
798            }
799            Some(raw) => {
800                let gl = &self.shared.context.lock();
801                unsafe { gl.bind_buffer(buffer.target, Some(raw)) };
802                let mut map_state = lock(&buffer.map_state);
803                let ptr = if let Some(map_read_allocation) = map_state.data.as_mut() {
804                    let slice = map_read_allocation.as_mut_slice();
805                    unsafe { self.shared.get_buffer_sub_data(gl, buffer.target, 0, slice) };
806                    slice.as_mut_ptr()
807                } else {
808                    map_state.offset_of_current_mapping = range.start;
809                    // glMapBufferRange throws an error if length is 0.
810                    // We want to allow mapping 0-sized buffer slices, so perform a workaround
811                    // if the range length is 0. The resulting pointer must never be dereferenced.
812                    let range_start: i32 = range
813                        .start
814                        .try_into()
815                        .expect("Buffer range invalid for GLES");
816                    let range_length: i32 = (range.end - range.start)
817                        .try_into()
818                        .expect("Buffer range invalid for GLES");
819                    if range_length != 0 {
820                        map_state.mapped = true;
821                        unsafe {
822                            gl.map_buffer_range(
823                                buffer.target,
824                                range_start,
825                                range_length,
826                                buffer.map_flags,
827                            )
828                        }
829                    } else {
830                        ptr::dangling_mut()
831                    }
832                };
833                unsafe { gl.bind_buffer(buffer.target, None) };
834                ptr
835            }
836        };
837        Ok(crate::BufferMapping {
838            ptr: ptr::NonNull::new(ptr).ok_or(crate::DeviceError::Lost)?,
839            is_coherent,
840        })
841    }
842    unsafe fn unmap_buffer(&self, buffer: &super::Buffer) {
843        let gl = &self.shared.context.lock();
844        let mut map_state = lock(&buffer.map_state);
845        if core::mem::replace(&mut map_state.mapped, false) {
846            if let Some(raw) = buffer.raw {
847                if map_state.data.is_none() {
848                    unsafe { gl.bind_buffer(buffer.target, Some(raw)) };
849                    unsafe { gl.unmap_buffer(buffer.target) };
850                    unsafe { gl.bind_buffer(buffer.target, None) };
851                    map_state.offset_of_current_mapping = 0;
852                }
853            }
854        }
855    }
856    unsafe fn flush_mapped_ranges<I>(&self, buffer: &super::Buffer, ranges: I)
857    where
858        I: Iterator<Item = crate::MemoryRange>,
859    {
860        let gl = &self.shared.context.lock();
861        let map_state = lock(&buffer.map_state);
862        if map_state.mapped {
863            if let Some(raw) = buffer.raw {
864                if map_state.data.is_none() {
865                    unsafe { gl.bind_buffer(buffer.target, Some(raw)) };
866                    for range in ranges {
867                        let offset_of_current_mapping = map_state.offset_of_current_mapping;
868                        unsafe {
869                            gl.flush_mapped_buffer_range(
870                                buffer.target,
871                                (range.start - offset_of_current_mapping) as i32,
872                                (range.end - range.start) as i32,
873                            )
874                        };
875                    }
876                }
877            }
878        }
879    }
880    unsafe fn invalidate_mapped_ranges<I>(&self, _buffer: &super::Buffer, _ranges: I) {
881        //TODO: do we need to do anything?
882    }
883
884    unsafe fn create_texture(
885        &self,
886        desc: &crate::TextureDescriptor,
887    ) -> Result<super::Texture, crate::DeviceError> {
888        let gl = &self.shared.context.lock();
889
890        let render_usage = wgt::TextureUses::COLOR_TARGET
891            | wgt::TextureUses::DEPTH_STENCIL_WRITE
892            | wgt::TextureUses::DEPTH_STENCIL_READ
893            | wgt::TextureUses::TRANSIENT;
894        let format_desc = self.shared.describe_texture_format(desc.format);
895
896        let inner = if render_usage.contains(desc.usage)
897            && desc.dimension == wgt::TextureDimension::D2
898            && desc.size.depth_or_array_layers == 1
899        {
900            let raw = unsafe { gl.create_renderbuffer().unwrap() };
901            unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(raw)) };
902            if desc.sample_count > 1 {
903                unsafe {
904                    gl.renderbuffer_storage_multisample(
905                        glow::RENDERBUFFER,
906                        desc.sample_count as i32,
907                        format_desc.internal,
908                        desc.size.width as i32,
909                        desc.size.height as i32,
910                    )
911                };
912            } else {
913                unsafe {
914                    gl.renderbuffer_storage(
915                        glow::RENDERBUFFER,
916                        format_desc.internal,
917                        desc.size.width as i32,
918                        desc.size.height as i32,
919                    )
920                };
921            }
922
923            #[cfg(native)]
924            if let Some(label) = desc.label {
925                if self
926                    .shared
927                    .private_caps
928                    .contains(PrivateCapabilities::DEBUG_FNS)
929                {
930                    let name = raw.0.get();
931                    unsafe { gl.object_label(glow::RENDERBUFFER, name, Some(label)) };
932                }
933            }
934
935            unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
936            super::TextureInner::Renderbuffer { raw }
937        } else {
938            let raw = unsafe { gl.create_texture().unwrap() };
939            let target = super::Texture::get_info_from_desc(desc);
940
941            unsafe { gl.bind_texture(target, Some(raw)) };
942            //Note: this has to be done before defining the storage!
943            match desc.format.sample_type(None, Some(self.shared.features)) {
944                Some(
945                    wgt::TextureSampleType::Float { filterable: false }
946                    | wgt::TextureSampleType::Uint
947                    | wgt::TextureSampleType::Sint,
948                ) => {
949                    // reset default filtering mode
950                    unsafe {
951                        gl.tex_parameter_i32(target, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32)
952                    };
953                    unsafe {
954                        gl.tex_parameter_i32(target, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32)
955                    };
956                }
957                _ => {}
958            }
959
960            if conv::is_layered_target(target) {
961                unsafe {
962                    if self
963                        .shared
964                        .private_caps
965                        .contains(PrivateCapabilities::TEXTURE_STORAGE)
966                    {
967                        gl.tex_storage_3d(
968                            target,
969                            desc.mip_level_count as i32,
970                            format_desc.internal,
971                            desc.size.width as i32,
972                            desc.size.height as i32,
973                            desc.size.depth_or_array_layers as i32,
974                        )
975                    } else if target == glow::TEXTURE_3D {
976                        let mut width = desc.size.width;
977                        let mut height = desc.size.height;
978                        let mut depth = desc.size.depth_or_array_layers;
979                        for i in 0..desc.mip_level_count {
980                            gl.tex_image_3d(
981                                target,
982                                i as i32,
983                                format_desc.internal as i32,
984                                width as i32,
985                                height as i32,
986                                depth as i32,
987                                0,
988                                format_desc.external,
989                                format_desc.data_type,
990                                glow::PixelUnpackData::Slice(None),
991                            );
992                            width = max(1, width / 2);
993                            height = max(1, height / 2);
994                            depth = max(1, depth / 2);
995                        }
996                    } else {
997                        let mut width = desc.size.width;
998                        let mut height = desc.size.height;
999                        for i in 0..desc.mip_level_count {
1000                            gl.tex_image_3d(
1001                                target,
1002                                i as i32,
1003                                format_desc.internal as i32,
1004                                width as i32,
1005                                height as i32,
1006                                desc.size.depth_or_array_layers as i32,
1007                                0,
1008                                format_desc.external,
1009                                format_desc.data_type,
1010                                glow::PixelUnpackData::Slice(None),
1011                            );
1012                            width = max(1, width / 2);
1013                            height = max(1, height / 2);
1014                        }
1015                    }
1016                };
1017            } else if desc.sample_count > 1 {
1018                unsafe {
1019                    gl.tex_storage_2d_multisample(
1020                        target,
1021                        desc.sample_count as i32,
1022                        format_desc.internal,
1023                        desc.size.width as i32,
1024                        desc.size.height as i32,
1025                        true,
1026                    )
1027                };
1028            } else {
1029                unsafe {
1030                    if self
1031                        .shared
1032                        .private_caps
1033                        .contains(PrivateCapabilities::TEXTURE_STORAGE)
1034                    {
1035                        gl.tex_storage_2d(
1036                            target,
1037                            desc.mip_level_count as i32,
1038                            format_desc.internal,
1039                            desc.size.width as i32,
1040                            desc.size.height as i32,
1041                        )
1042                    } else if target == glow::TEXTURE_CUBE_MAP {
1043                        let mut width = desc.size.width;
1044                        let mut height = desc.size.height;
1045                        for i in 0..desc.mip_level_count {
1046                            for face in [
1047                                glow::TEXTURE_CUBE_MAP_POSITIVE_X,
1048                                glow::TEXTURE_CUBE_MAP_NEGATIVE_X,
1049                                glow::TEXTURE_CUBE_MAP_POSITIVE_Y,
1050                                glow::TEXTURE_CUBE_MAP_NEGATIVE_Y,
1051                                glow::TEXTURE_CUBE_MAP_POSITIVE_Z,
1052                                glow::TEXTURE_CUBE_MAP_NEGATIVE_Z,
1053                            ] {
1054                                gl.tex_image_2d(
1055                                    face,
1056                                    i as i32,
1057                                    format_desc.internal as i32,
1058                                    width as i32,
1059                                    height as i32,
1060                                    0,
1061                                    format_desc.external,
1062                                    format_desc.data_type,
1063                                    glow::PixelUnpackData::Slice(None),
1064                                );
1065                            }
1066                            width = max(1, width / 2);
1067                            height = max(1, height / 2);
1068                        }
1069                    } else {
1070                        let mut width = desc.size.width;
1071                        let mut height = desc.size.height;
1072                        for i in 0..desc.mip_level_count {
1073                            gl.tex_image_2d(
1074                                target,
1075                                i as i32,
1076                                format_desc.internal as i32,
1077                                width as i32,
1078                                height as i32,
1079                                0,
1080                                format_desc.external,
1081                                format_desc.data_type,
1082                                glow::PixelUnpackData::Slice(None),
1083                            );
1084                            width = max(1, width / 2);
1085                            height = max(1, height / 2);
1086                        }
1087                    }
1088                };
1089            }
1090
1091            #[cfg(native)]
1092            if let Some(label) = desc.label {
1093                if self
1094                    .shared
1095                    .private_caps
1096                    .contains(PrivateCapabilities::DEBUG_FNS)
1097                {
1098                    let name = raw.0.get();
1099                    unsafe { gl.object_label(glow::TEXTURE, name, Some(label)) };
1100                }
1101            }
1102
1103            unsafe { gl.bind_texture(target, None) };
1104            super::TextureInner::Texture { raw, target }
1105        };
1106
1107        self.counters.textures.add(1);
1108
1109        Ok(super::Texture {
1110            inner,
1111            drop_guard: None,
1112            mip_level_count: desc.mip_level_count,
1113            array_layer_count: desc.array_layer_count(),
1114            format: desc.format,
1115            format_desc,
1116            copy_size: desc.copy_extent(),
1117        })
1118    }
1119
1120    unsafe fn destroy_texture(&self, texture: super::Texture) {
1121        if texture.drop_guard.is_none() {
1122            let gl = &self.shared.context.lock();
1123            match texture.inner {
1124                super::TextureInner::Renderbuffer { raw, .. } => {
1125                    unsafe { gl.delete_renderbuffer(raw) };
1126                }
1127                super::TextureInner::DefaultRenderbuffer => {}
1128                super::TextureInner::Texture { raw, .. } => {
1129                    unsafe { gl.delete_texture(raw) };
1130                }
1131                #[cfg(webgl)]
1132                super::TextureInner::ExternalFramebuffer { .. } => {}
1133                #[cfg(native)]
1134                super::TextureInner::ExternalNativeFramebuffer { .. } => {}
1135            }
1136        }
1137
1138        // For clarity, we explicitly drop the drop guard. Although this has no real semantic effect as the
1139        // end of the scope will drop the drop guard since this function takes ownership of the texture.
1140        drop(texture.drop_guard);
1141
1142        self.counters.textures.sub(1);
1143    }
1144
1145    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
1146        self.counters.textures.add(1);
1147    }
1148
1149    unsafe fn create_texture_view(
1150        &self,
1151        texture: &super::Texture,
1152        desc: &crate::TextureViewDescriptor,
1153    ) -> Result<super::TextureView, crate::DeviceError> {
1154        self.counters.texture_views.add(1);
1155        Ok(super::TextureView {
1156            //TODO: use `conv::map_view_dimension(desc.dimension)`?
1157            inner: texture.inner.clone(),
1158            aspects: crate::FormatAspects::new(texture.format, desc.range.aspect),
1159            mip_levels: desc.range.mip_range(texture.mip_level_count),
1160            array_layers: desc.range.layer_range(texture.array_layer_count),
1161            format: texture.format,
1162        })
1163    }
1164
1165    unsafe fn destroy_texture_view(&self, _view: super::TextureView) {
1166        self.counters.texture_views.sub(1);
1167    }
1168
1169    unsafe fn create_sampler(
1170        &self,
1171        desc: &crate::SamplerDescriptor,
1172    ) -> Result<super::Sampler, crate::DeviceError> {
1173        let gl = &self.shared.context.lock();
1174
1175        let raw = unsafe { gl.create_sampler().unwrap() };
1176
1177        let (min, mag) =
1178            conv::map_filter_modes(desc.min_filter, desc.mag_filter, desc.mipmap_filter);
1179
1180        unsafe { gl.sampler_parameter_i32(raw, glow::TEXTURE_MIN_FILTER, min as i32) };
1181        unsafe { gl.sampler_parameter_i32(raw, glow::TEXTURE_MAG_FILTER, mag as i32) };
1182
1183        unsafe {
1184            gl.sampler_parameter_i32(
1185                raw,
1186                glow::TEXTURE_WRAP_S,
1187                conv::map_address_mode(desc.address_modes[0]) as i32,
1188            )
1189        };
1190        unsafe {
1191            gl.sampler_parameter_i32(
1192                raw,
1193                glow::TEXTURE_WRAP_T,
1194                conv::map_address_mode(desc.address_modes[1]) as i32,
1195            )
1196        };
1197        unsafe {
1198            gl.sampler_parameter_i32(
1199                raw,
1200                glow::TEXTURE_WRAP_R,
1201                conv::map_address_mode(desc.address_modes[2]) as i32,
1202            )
1203        };
1204
1205        if let Some(border_color) = desc.border_color {
1206            let border = match border_color {
1207                wgt::SamplerBorderColor::TransparentBlack | wgt::SamplerBorderColor::Zero => {
1208                    [0.0; 4]
1209                }
1210                wgt::SamplerBorderColor::OpaqueBlack => [0.0, 0.0, 0.0, 1.0],
1211                wgt::SamplerBorderColor::OpaqueWhite => [1.0; 4],
1212            };
1213            unsafe { gl.sampler_parameter_f32_slice(raw, glow::TEXTURE_BORDER_COLOR, &border) };
1214        }
1215
1216        unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MIN_LOD, desc.lod_clamp.start) };
1217        unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MAX_LOD, desc.lod_clamp.end) };
1218
1219        // If clamp is not 1, we know anisotropy is supported up to 16x
1220        if desc.anisotropy_clamp != 1 {
1221            unsafe {
1222                gl.sampler_parameter_i32(
1223                    raw,
1224                    glow::TEXTURE_MAX_ANISOTROPY,
1225                    desc.anisotropy_clamp as i32,
1226                )
1227            };
1228        }
1229
1230        //set_param_float(glow::TEXTURE_LOD_BIAS, info.lod_bias.0);
1231
1232        if let Some(compare) = desc.compare {
1233            unsafe {
1234                gl.sampler_parameter_i32(
1235                    raw,
1236                    glow::TEXTURE_COMPARE_MODE,
1237                    glow::COMPARE_REF_TO_TEXTURE as i32,
1238                )
1239            };
1240            unsafe {
1241                gl.sampler_parameter_i32(
1242                    raw,
1243                    glow::TEXTURE_COMPARE_FUNC,
1244                    conv::map_compare_func(compare) as i32,
1245                )
1246            };
1247        }
1248
1249        #[cfg(native)]
1250        if let Some(label) = desc.label {
1251            if self
1252                .shared
1253                .private_caps
1254                .contains(PrivateCapabilities::DEBUG_FNS)
1255            {
1256                let name = raw.0.get();
1257                unsafe { gl.object_label(glow::SAMPLER, name, Some(label)) };
1258            }
1259        }
1260
1261        self.counters.samplers.add(1);
1262
1263        Ok(super::Sampler { raw })
1264    }
1265
1266    unsafe fn destroy_sampler(&self, sampler: super::Sampler) {
1267        let gl = &self.shared.context.lock();
1268        unsafe { gl.delete_sampler(sampler.raw) };
1269        self.counters.samplers.sub(1);
1270    }
1271
1272    unsafe fn create_command_encoder(
1273        &self,
1274        _desc: &crate::CommandEncoderDescriptor<super::Queue>,
1275    ) -> Result<super::CommandEncoder, crate::DeviceError> {
1276        self.counters.command_encoders.add(1);
1277
1278        Ok(super::CommandEncoder {
1279            cmd_buffer: super::CommandBuffer::default(),
1280            state: Default::default(),
1281            private_caps: self.shared.private_caps,
1282            counters: Arc::clone(&self.counters),
1283        })
1284    }
1285
1286    unsafe fn create_bind_group_layout(
1287        &self,
1288        desc: &crate::BindGroupLayoutDescriptor,
1289    ) -> Result<super::BindGroupLayout, crate::DeviceError> {
1290        self.counters.bind_group_layouts.add(1);
1291        Ok(super::BindGroupLayout {
1292            entries: Arc::from(desc.entries),
1293        })
1294    }
1295
1296    unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {
1297        self.counters.bind_group_layouts.sub(1);
1298    }
1299
1300    unsafe fn create_pipeline_layout(
1301        &self,
1302        desc: &crate::PipelineLayoutDescriptor<super::BindGroupLayout>,
1303    ) -> Result<super::PipelineLayout, crate::DeviceError> {
1304        use naga::back::glsl;
1305
1306        let mut group_infos = Vec::with_capacity(desc.bind_group_layouts.len());
1307        let mut num_samplers = 0u8;
1308        let mut num_textures = 0u8;
1309        let mut num_images = 0u8;
1310        let mut num_uniform_buffers = 0u8;
1311        let mut num_storage_buffers = 0u8;
1312
1313        let mut writer_flags = glsl::WriterFlags::ADJUST_COORDINATE_SPACE;
1314        writer_flags.set(
1315            glsl::WriterFlags::TEXTURE_SHADOW_LOD,
1316            self.shared
1317                .private_caps
1318                .contains(PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD),
1319        );
1320        writer_flags.set(
1321            glsl::WriterFlags::DRAW_PARAMETERS,
1322            self.shared
1323                .private_caps
1324                .contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING),
1325        );
1326        // A point list pipeline writes the point size (`create_shader`); no
1327        // other one does, as WebGL counts `gl_PointSize` against the 15
1328        // varying vectors a program may link with.
1329        let mut binding_map = glsl::BindingMap::default();
1330
1331        for (group_index, bg_layout) in desc.bind_group_layouts.iter().enumerate() {
1332            let Some(bg_layout) = bg_layout else {
1333                group_infos.push(None);
1334                continue;
1335            };
1336
1337            // create a vector with the size enough to hold all the bindings, filled with `!0`
1338            let mut binding_to_slot = vec![
1339                !0;
1340                bg_layout
1341                    .entries
1342                    .iter()
1343                    .map(|b| b.binding)
1344                    .max()
1345                    .map_or(0, |idx| idx as usize + 1)
1346            ]
1347            .into_boxed_slice();
1348
1349            for entry in bg_layout.entries.iter() {
1350                let counter = match entry.ty {
1351                    wgt::BindingType::Sampler { .. } => &mut num_samplers,
1352                    wgt::BindingType::Texture { .. } => &mut num_textures,
1353                    wgt::BindingType::StorageTexture { .. } => &mut num_images,
1354                    wgt::BindingType::Buffer {
1355                        ty: wgt::BufferBindingType::Uniform,
1356                        ..
1357                    } => &mut num_uniform_buffers,
1358                    wgt::BindingType::Buffer {
1359                        ty: wgt::BufferBindingType::Storage { .. },
1360                        ..
1361                    } => &mut num_storage_buffers,
1362                    wgt::BindingType::AccelerationStructure { .. } => unimplemented!(),
1363                    wgt::BindingType::ExternalTexture => unimplemented!(),
1364                };
1365
1366                binding_to_slot[entry.binding as usize] = *counter;
1367                let br = naga::ResourceBinding {
1368                    group: group_index as u32,
1369                    binding: entry.binding,
1370                };
1371                binding_map.insert(br, *counter);
1372                *counter += entry.count.map_or(1, |c| c.get() as u8);
1373            }
1374
1375            group_infos.push(Some(super::BindGroupLayoutInfo {
1376                entries: Arc::clone(&bg_layout.entries),
1377                binding_to_slot,
1378            }));
1379        }
1380
1381        self.counters.pipeline_layouts.add(1);
1382
1383        Ok(super::PipelineLayout {
1384            group_infos: group_infos.into_boxed_slice(),
1385            naga_options: glsl::Options {
1386                version: self.shared.shading_language_version,
1387                writer_flags,
1388                binding_map,
1389                zero_initialize_workgroup_memory: true,
1390            },
1391        })
1392    }
1393
1394    unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {
1395        self.counters.pipeline_layouts.sub(1);
1396    }
1397
1398    unsafe fn create_bind_group(
1399        &self,
1400        desc: &crate::BindGroupDescriptor<
1401            super::BindGroupLayout,
1402            super::Buffer,
1403            super::Sampler,
1404            super::TextureView,
1405            super::AccelerationStructure,
1406        >,
1407    ) -> Result<super::BindGroup, crate::DeviceError> {
1408        let mut contents = Vec::new();
1409
1410        let layout_and_entry_iter = desc.entries.iter().map(|entry| {
1411            let layout = desc
1412                .layout
1413                .entries
1414                .iter()
1415                .find(|layout_entry| layout_entry.binding == entry.binding)
1416                .expect("internal error: no layout entry found with binding slot");
1417            (entry, layout)
1418        });
1419        for (entry, layout) in layout_and_entry_iter {
1420            let binding = match layout.ty {
1421                wgt::BindingType::Buffer { .. } => {
1422                    let bb = &desc.buffers[entry.resource_index as usize];
1423                    super::RawBinding::Buffer {
1424                        raw: bb.buffer.raw.unwrap(),
1425                        offset: bb.offset as i32,
1426                        size: match bb.size {
1427                            Some(s) => s.get() as i32,
1428                            None => (bb.buffer.size - bb.offset) as i32,
1429                        },
1430                    }
1431                }
1432                wgt::BindingType::Sampler { .. } => {
1433                    let sampler = desc.samplers[entry.resource_index as usize];
1434                    super::RawBinding::Sampler(sampler.raw)
1435                }
1436                wgt::BindingType::Texture { view_dimension, .. } => {
1437                    let view = desc.textures[entry.resource_index as usize].view;
1438                    if view.array_layers.start != 0 {
1439                        log::error!("Unable to create a sampled texture binding for non-zero array layer.\n{}",
1440                            "This is an implementation problem of wgpu-hal/gles backend.")
1441                    }
1442                    let (raw, target) = view.inner.as_native();
1443
1444                    super::Texture::log_failing_target_heuristics(view_dimension, target);
1445
1446                    super::RawBinding::Texture {
1447                        raw,
1448                        target,
1449                        aspects: view.aspects,
1450                        mip_levels: view.mip_levels.clone(),
1451                    }
1452                }
1453                wgt::BindingType::StorageTexture {
1454                    access,
1455                    format,
1456                    view_dimension,
1457                } => {
1458                    let view = desc.textures[entry.resource_index as usize].view;
1459                    let format_desc = self.shared.describe_texture_format(format);
1460                    let (raw, _target) = view.inner.as_native();
1461                    super::RawBinding::Image(super::ImageBinding {
1462                        raw,
1463                        mip_level: view.mip_levels.start,
1464                        array_layer: match view_dimension {
1465                            wgt::TextureViewDimension::D2Array
1466                            | wgt::TextureViewDimension::CubeArray => None,
1467                            _ => Some(view.array_layers.start),
1468                        },
1469                        access: conv::map_storage_access(access),
1470                        format: format_desc.internal,
1471                    })
1472                }
1473                wgt::BindingType::AccelerationStructure { .. } => unimplemented!(),
1474                wgt::BindingType::ExternalTexture => unimplemented!(),
1475            };
1476            contents.push(binding);
1477        }
1478
1479        self.counters.bind_groups.add(1);
1480
1481        Ok(super::BindGroup {
1482            contents: contents.into_boxed_slice(),
1483        })
1484    }
1485
1486    unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {
1487        self.counters.bind_groups.sub(1);
1488    }
1489
1490    unsafe fn create_shader_module(
1491        &self,
1492        desc: &crate::ShaderModuleDescriptor,
1493        shader: crate::ShaderInput,
1494    ) -> Result<super::ShaderModule, crate::ShaderError> {
1495        self.counters.shader_modules.add(1);
1496
1497        Ok(super::ShaderModule {
1498            source: match shader {
1499                crate::ShaderInput::Naga(naga) => super::ShaderModuleSource::Naga(naga),
1500                // The backend doesn't yet expose this feature so it should be fine
1501                crate::ShaderInput::Glsl { shader, .. } => super::ShaderModuleSource::Passthrough {
1502                    source: shader.to_owned(),
1503                },
1504                crate::ShaderInput::SpirV(_)
1505                | crate::ShaderInput::MetalLib { .. }
1506                | crate::ShaderInput::Msl { .. }
1507                | crate::ShaderInput::Dxil { .. }
1508                | crate::ShaderInput::Hlsl { .. } => {
1509                    unreachable!()
1510                }
1511            },
1512            label: desc.label.map(|str| str.to_string()),
1513            id: self.shared.next_shader_id.fetch_add(1, Ordering::Relaxed),
1514        })
1515    }
1516
1517    unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {
1518        self.counters.shader_modules.sub(1);
1519    }
1520
1521    unsafe fn create_render_pipeline(
1522        &self,
1523        desc: &crate::RenderPipelineDescriptor<
1524            super::PipelineLayout,
1525            super::ShaderModule,
1526            super::PipelineCache,
1527        >,
1528    ) -> Result<super::RenderPipeline, crate::PipelineError> {
1529        let (vertex_stage, vertex_buffers) = match &desc.vertex_processor {
1530            crate::VertexProcessor::Standard {
1531                vertex_buffers,
1532                ref vertex_stage,
1533            } => (vertex_stage, vertex_buffers),
1534            crate::VertexProcessor::Mesh { .. } => unreachable!(),
1535        };
1536        let gl = &self.shared.context.lock();
1537        let mut shaders = ArrayVec::new();
1538        shaders.push((naga::ShaderStage::Vertex, vertex_stage));
1539        if let Some(ref fs) = desc.fragment_stage {
1540            shaders.push((naga::ShaderStage::Fragment, fs));
1541        }
1542        let points = desc.primitive.topology == wgt::PrimitiveTopology::PointList;
1543        let inner = unsafe {
1544            self.create_pipeline(
1545                gl,
1546                shaders,
1547                desc.layout,
1548                desc.label,
1549                desc.multiview_mask,
1550                points,
1551            )
1552        }?;
1553
1554        let (vertex_buffers, vertex_attributes) = {
1555            let mut buffers = Vec::new();
1556            let mut attributes = Vec::new();
1557            for (index, vb_layout) in vertex_buffers.iter().enumerate() {
1558                let vb_desc = if let Some(vb_layout) = vb_layout {
1559                    for vat in vb_layout.attributes.iter() {
1560                        let format_desc = conv::describe_vertex_format(vat.format);
1561                        attributes.push(super::AttributeDesc {
1562                            location: vat.shader_location,
1563                            offset: vat.offset as u32,
1564                            buffer_index: index as u32,
1565                            format_desc,
1566                        });
1567                    }
1568                    Some(super::VertexBufferDesc {
1569                        step: vb_layout.step_mode,
1570                        stride: vb_layout.array_stride as u32,
1571                    })
1572                } else {
1573                    None
1574                };
1575                buffers.push(vb_desc);
1576            }
1577            (buffers.into_boxed_slice(), attributes.into_boxed_slice())
1578        };
1579
1580        let color_targets = {
1581            let mut targets = Vec::new();
1582            for ct in desc.color_targets.iter().filter_map(|at| at.as_ref()) {
1583                targets.push(super::ColorTargetDesc {
1584                    mask: ct.write_mask,
1585                    blend: ct.blend.as_ref().map(conv::map_blend),
1586                });
1587            }
1588            //Note: if any of the states are different, and `INDEPENDENT_BLEND` flag
1589            // is not exposed, then this pipeline will not bind correctly.
1590            targets.into_boxed_slice()
1591        };
1592
1593        self.counters.render_pipelines.add(1);
1594
1595        Ok(super::RenderPipeline {
1596            inner,
1597            primitive: desc.primitive,
1598            vertex_buffers,
1599            vertex_attributes,
1600            color_targets,
1601            depth: desc.depth_stencil.as_ref().map(|ds| super::DepthState {
1602                function: conv::map_compare_func(ds.depth_compare.unwrap_or_default()),
1603                mask: ds.depth_write_enabled.unwrap_or_default(),
1604            }),
1605            depth_bias: desc
1606                .depth_stencil
1607                .as_ref()
1608                .map(|ds| ds.bias)
1609                .unwrap_or_default(),
1610            stencil: desc
1611                .depth_stencil
1612                .as_ref()
1613                .map(|ds| conv::map_stencil(&ds.stencil)),
1614            alpha_to_coverage_enabled: desc.multisample.alpha_to_coverage_enabled,
1615        })
1616    }
1617
1618    unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) {
1619        // If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache`
1620        // This is safe to assume as long as:
1621        // - `RenderPipeline` can't be cloned
1622        // - The only place that we can get a new reference is during `program_cache.lock()`
1623        if Arc::strong_count(&pipeline.inner) == 2 {
1624            let gl = &self.shared.context.lock();
1625            let mut program_cache = self.shared.program_cache.lock();
1626            program_cache.retain(|_, v| match *v {
1627                Ok(ref p) => p.program != pipeline.inner.program,
1628                Err(_) => false,
1629            });
1630            unsafe { gl.delete_program(pipeline.inner.program) };
1631        }
1632
1633        self.counters.render_pipelines.sub(1);
1634    }
1635
1636    unsafe fn create_compute_pipeline(
1637        &self,
1638        desc: &crate::ComputePipelineDescriptor<
1639            super::PipelineLayout,
1640            super::ShaderModule,
1641            super::PipelineCache,
1642        >,
1643    ) -> Result<super::ComputePipeline, crate::PipelineError> {
1644        let gl = &self.shared.context.lock();
1645        let mut shaders = ArrayVec::new();
1646        shaders.push((naga::ShaderStage::Compute, &desc.stage));
1647        let inner =
1648            unsafe { self.create_pipeline(gl, shaders, desc.layout, desc.label, None, false) }?;
1649
1650        self.counters.compute_pipelines.add(1);
1651
1652        Ok(super::ComputePipeline { inner })
1653    }
1654
1655    unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) {
1656        // If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache``
1657        // This is safe to assume as long as:
1658        // - `ComputePipeline` can't be cloned
1659        // - The only place that we can get a new reference is during `program_cache.lock()`
1660        if Arc::strong_count(&pipeline.inner) == 2 {
1661            let gl = &self.shared.context.lock();
1662            let mut program_cache = self.shared.program_cache.lock();
1663            program_cache.retain(|_, v| match *v {
1664                Ok(ref p) => p.program != pipeline.inner.program,
1665                Err(_) => false,
1666            });
1667            unsafe { gl.delete_program(pipeline.inner.program) };
1668        }
1669
1670        self.counters.compute_pipelines.sub(1);
1671    }
1672
1673    unsafe fn create_ray_tracing_pipeline(
1674        &self,
1675        _desc: &crate::RayTracingPipelineDescriptor<
1676            super::PipelineLayout,
1677            super::ShaderModule,
1678            super::PipelineCache,
1679        >,
1680    ) -> Result<super::RayTracingPipeline, crate::PipelineError> {
1681        unimplemented!("Ray tracing is unsupported on GL")
1682    }
1683
1684    unsafe fn destroy_ray_tracing_pipeline(&self, _pipeline: super::RayTracingPipeline) {
1685        unimplemented!("Ray tracing is unsupported on GL")
1686    }
1687
1688    unsafe fn get_raytracing_pipeline_group_data(
1689        &self,
1690        _pipeline: &super::RayTracingPipeline,
1691        _groups: core::ops::Range<u32>,
1692    ) -> Result<Vec<u8>, crate::DeviceError> {
1693        unimplemented!("Ray tracing is unsupported on GL")
1694    }
1695
1696    unsafe fn create_pipeline_cache(
1697        &self,
1698        _: &crate::PipelineCacheDescriptor<'_>,
1699    ) -> Result<super::PipelineCache, crate::PipelineCacheError> {
1700        // Even though the cache doesn't do anything, we still return something here
1701        // as the least bad option
1702        Ok(super::PipelineCache)
1703    }
1704    unsafe fn destroy_pipeline_cache(&self, _: super::PipelineCache) {}
1705
1706    #[cfg_attr(target_arch = "wasm32", allow(unused))]
1707    unsafe fn create_query_set(
1708        &self,
1709        desc: &wgt::QuerySetDescriptor<crate::Label>,
1710    ) -> Result<super::QuerySet, crate::DeviceError> {
1711        let gl = &self.shared.context.lock();
1712
1713        let mut queries = Vec::with_capacity(desc.count as usize);
1714        for _ in 0..desc.count {
1715            let query =
1716                unsafe { gl.create_query() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1717
1718            // We aren't really able to, in general, label queries.
1719            //
1720            // We could take a timestamp here to "initialize" the query,
1721            // but that's a bit of a hack, and we don't want to insert
1722            // random timestamps into the command stream of we don't have to.
1723
1724            queries.push(query);
1725        }
1726
1727        self.counters.query_sets.add(1);
1728
1729        Ok(super::QuerySet {
1730            queries: queries.into_boxed_slice(),
1731            target: match desc.ty {
1732                wgt::QueryType::Occlusion => glow::ANY_SAMPLES_PASSED_CONSERVATIVE,
1733                wgt::QueryType::Timestamp => glow::TIMESTAMP,
1734                _ => unimplemented!(),
1735            },
1736        })
1737    }
1738
1739    unsafe fn destroy_query_set(&self, set: super::QuerySet) {
1740        let gl = &self.shared.context.lock();
1741        for &query in set.queries.iter() {
1742            unsafe { gl.delete_query(query) };
1743        }
1744        self.counters.query_sets.sub(1);
1745    }
1746
1747    unsafe fn create_fence(&self) -> Result<super::Fence, crate::DeviceError> {
1748        self.counters.fences.add(1);
1749        Ok(super::Fence::new(&self.shared.options))
1750    }
1751
1752    unsafe fn destroy_fence(&self, fence: super::Fence) {
1753        let gl = &self.shared.context.lock();
1754        fence.destroy(gl);
1755        self.counters.fences.sub(1);
1756    }
1757
1758    unsafe fn get_fence_value(
1759        &self,
1760        fence: &super::Fence,
1761    ) -> Result<crate::FenceValue, crate::DeviceError> {
1762        #[cfg_attr(target_arch = "wasm32", allow(clippy::needless_borrow))]
1763        Ok(fence.get_latest(&self.shared.context.lock()))
1764    }
1765    unsafe fn wait(
1766        &self,
1767        fence: &super::Fence,
1768        wait_value: crate::FenceValue,
1769        timeout: Option<core::time::Duration>,
1770    ) -> Result<bool, crate::DeviceError> {
1771        if fence.satisfied(wait_value) {
1772            return Ok(true);
1773        }
1774
1775        let gl = &self.shared.context.lock();
1776        // MAX_CLIENT_WAIT_TIMEOUT_WEBGL is:
1777        // - 1s in Gecko https://searchfox.org/mozilla-central/rev/754074e05178e017ef6c3d8e30428ffa8f1b794d/dom/canvas/WebGLTypes.h#1386
1778        // - 0 in WebKit https://github.com/WebKit/WebKit/blob/4ef90d4672ca50267c0971b85db403d9684508ea/Source/WebCore/html/canvas/WebGL2RenderingContext.cpp#L110
1779        // - 0 in Chromium https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc;l=112;drc=a3cb0ac4c71ec04abfeaed199e5d63230eca2551
1780        let timeout_ns = if cfg!(any(webgl, Emscripten)) {
1781            0
1782        } else {
1783            timeout
1784                .map(|t| t.as_nanos().min(u32::MAX as u128) as u32)
1785                .unwrap_or(u32::MAX)
1786        };
1787        fence.wait(gl, wait_value, timeout_ns)
1788    }
1789
1790    unsafe fn start_graphics_debugger_capture(&self) -> bool {
1791        #[cfg(all(native, feature = "renderdoc"))]
1792        return unsafe {
1793            self.render_doc
1794                .start_frame_capture(self.shared.context.raw_context(), ptr::null_mut())
1795        };
1796        #[allow(unreachable_code)]
1797        false
1798    }
1799    unsafe fn stop_graphics_debugger_capture(&self) {
1800        #[cfg(all(native, feature = "renderdoc"))]
1801        unsafe {
1802            self.render_doc
1803                .end_frame_capture(ptr::null_mut(), ptr::null_mut())
1804        }
1805    }
1806    unsafe fn create_acceleration_structure(
1807        &self,
1808        _desc: &crate::AccelerationStructureDescriptor,
1809    ) -> Result<super::AccelerationStructure, crate::DeviceError> {
1810        unimplemented!()
1811    }
1812    unsafe fn get_acceleration_structure_build_sizes<'a>(
1813        &self,
1814        _desc: &crate::GetAccelerationStructureBuildSizesDescriptor<'a, super::Buffer>,
1815    ) -> crate::AccelerationStructureBuildSizes {
1816        unimplemented!()
1817    }
1818    unsafe fn get_acceleration_structure_device_address(
1819        &self,
1820        _acceleration_structure: &super::AccelerationStructure,
1821    ) -> wgt::BufferAddress {
1822        unimplemented!()
1823    }
1824    unsafe fn destroy_acceleration_structure(
1825        &self,
1826        _acceleration_structure: super::AccelerationStructure,
1827    ) {
1828    }
1829
1830    fn tlas_instance_to_bytes(&self, _instance: TlasInstance) -> Vec<u8> {
1831        unimplemented!()
1832    }
1833
1834    fn get_internal_counters(&self) -> wgt::HalCounters {
1835        self.counters.as_ref().clone()
1836    }
1837
1838    fn check_if_oom(&self) -> Result<(), crate::DeviceError> {
1839        Ok(())
1840    }
1841}
1842
1843#[cfg(send_sync)]
1844unsafe impl Sync for super::Device {}
1845#[cfg(send_sync)]
1846unsafe impl Send for super::Device {}