wgpu_hal/gles/
device.rs

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