Skip to main content

wgpu_hal/gles/
device.rs

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