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