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