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