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;
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 exit(self, queue: super::Queue) {
505        let gl = &self.shared.context.lock();
506        unsafe { gl.delete_vertex_array(self.main_vao) };
507        unsafe { gl.delete_framebuffer(queue.draw_fbo) };
508        unsafe { gl.delete_framebuffer(queue.copy_fbo) };
509        unsafe { gl.delete_buffer(queue.zero_buffer) };
510    }
511
512    unsafe fn create_buffer(
513        &self,
514        desc: &crate::BufferDescriptor,
515    ) -> Result<super::Buffer, crate::DeviceError> {
516        let target = if desc.usage.contains(crate::BufferUses::INDEX) {
517            glow::ELEMENT_ARRAY_BUFFER
518        } else {
519            glow::ARRAY_BUFFER
520        };
521
522        let emulate_map = self
523            .shared
524            .workarounds
525            .contains(super::Workarounds::EMULATE_BUFFER_MAP)
526            || !self
527                .shared
528                .private_caps
529                .contains(PrivateCapabilities::BUFFER_ALLOCATION);
530
531        if emulate_map && desc.usage.intersects(crate::BufferUses::MAP_WRITE) {
532            return Ok(super::Buffer {
533                raw: None,
534                target,
535                size: desc.size,
536                map_flags: 0,
537                data: Some(Arc::new(Mutex::new(vec![0; desc.size as usize]))),
538                offset_of_current_mapping: Arc::new(Mutex::new(0)),
539            });
540        }
541
542        let gl = &self.shared.context.lock();
543
544        let target = if desc.usage.contains(crate::BufferUses::INDEX) {
545            glow::ELEMENT_ARRAY_BUFFER
546        } else {
547            glow::ARRAY_BUFFER
548        };
549
550        let is_host_visible = desc
551            .usage
552            .intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE);
553        let is_coherent = desc
554            .memory_flags
555            .contains(crate::MemoryFlags::PREFER_COHERENT);
556
557        let mut map_flags = 0;
558        if desc.usage.contains(crate::BufferUses::MAP_READ) {
559            map_flags |= glow::MAP_READ_BIT;
560        }
561        if desc.usage.contains(crate::BufferUses::MAP_WRITE) {
562            map_flags |= glow::MAP_WRITE_BIT;
563        }
564
565        let raw = Some(unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?);
566        unsafe { gl.bind_buffer(target, raw) };
567        let raw_size = desc
568            .size
569            .try_into()
570            .map_err(|_| crate::DeviceError::OutOfMemory)?;
571
572        if self
573            .shared
574            .private_caps
575            .contains(PrivateCapabilities::BUFFER_ALLOCATION)
576        {
577            if is_host_visible {
578                map_flags |= glow::MAP_PERSISTENT_BIT;
579                if is_coherent {
580                    map_flags |= glow::MAP_COHERENT_BIT;
581                }
582            }
583            // TODO: may also be required for other calls involving `buffer_sub_data_u8_slice` (e.g. copy buffer to buffer and clear buffer)
584            if desc.usage.intersects(crate::BufferUses::QUERY_RESOLVE) {
585                map_flags |= glow::DYNAMIC_STORAGE_BIT;
586            }
587            unsafe { gl.buffer_storage(target, raw_size, None, map_flags) };
588        } else {
589            assert!(!is_coherent);
590            let usage = if is_host_visible {
591                if desc.usage.contains(crate::BufferUses::MAP_READ) {
592                    glow::STREAM_READ
593                } else {
594                    glow::DYNAMIC_DRAW
595                }
596            } else {
597                // Even if the usage doesn't contain SRC_READ, we update it internally at least once
598                // Some vendors take usage very literally and STATIC_DRAW will freeze us with an empty buffer
599                // https://github.com/gfx-rs/wgpu/issues/3371
600                glow::DYNAMIC_DRAW
601            };
602            unsafe { gl.buffer_data_size(target, raw_size, usage) };
603        }
604
605        unsafe { gl.bind_buffer(target, None) };
606
607        if !is_coherent && desc.usage.contains(crate::BufferUses::MAP_WRITE) {
608            map_flags |= glow::MAP_FLUSH_EXPLICIT_BIT;
609        }
610        //TODO: do we need `glow::MAP_UNSYNCHRONIZED_BIT`?
611
612        #[cfg(native)]
613        if let Some(label) = desc.label {
614            if self
615                .shared
616                .private_caps
617                .contains(PrivateCapabilities::DEBUG_FNS)
618            {
619                let name = raw.map_or(0, |buf| buf.0.get());
620                unsafe { gl.object_label(glow::BUFFER, name, Some(label)) };
621            }
622        }
623
624        let data = if emulate_map && desc.usage.contains(crate::BufferUses::MAP_READ) {
625            Some(Arc::new(Mutex::new(vec![0; desc.size as usize])))
626        } else {
627            None
628        };
629
630        self.counters.buffers.add(1);
631
632        Ok(super::Buffer {
633            raw,
634            target,
635            size: desc.size,
636            map_flags,
637            data,
638            offset_of_current_mapping: Arc::new(Mutex::new(0)),
639        })
640    }
641
642    unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
643        if let Some(raw) = buffer.raw {
644            let gl = &self.shared.context.lock();
645            unsafe { gl.delete_buffer(raw) };
646        }
647
648        self.counters.buffers.sub(1);
649    }
650
651    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
652        self.counters.buffers.add(1);
653    }
654
655    unsafe fn map_buffer(
656        &self,
657        buffer: &super::Buffer,
658        range: crate::MemoryRange,
659    ) -> Result<crate::BufferMapping, crate::DeviceError> {
660        let is_coherent = buffer.map_flags & glow::MAP_COHERENT_BIT != 0;
661        let ptr = match buffer.raw {
662            None => {
663                let mut vec = buffer.data.as_ref().unwrap().lock().unwrap();
664                let slice = &mut vec.as_mut_slice()[range.start as usize..range.end as usize];
665                slice.as_mut_ptr()
666            }
667            Some(raw) => {
668                let gl = &self.shared.context.lock();
669                unsafe { gl.bind_buffer(buffer.target, Some(raw)) };
670                let ptr = if let Some(ref map_read_allocation) = buffer.data {
671                    let mut guard = map_read_allocation.lock().unwrap();
672                    let slice = guard.as_mut_slice();
673                    unsafe { self.shared.get_buffer_sub_data(gl, buffer.target, 0, slice) };
674                    slice.as_mut_ptr()
675                } else {
676                    *buffer.offset_of_current_mapping.lock().unwrap() = range.start;
677                    unsafe {
678                        gl.map_buffer_range(
679                            buffer.target,
680                            range.start as i32,
681                            (range.end - range.start) as i32,
682                            buffer.map_flags,
683                        )
684                    }
685                };
686                unsafe { gl.bind_buffer(buffer.target, None) };
687                ptr
688            }
689        };
690        Ok(crate::BufferMapping {
691            ptr: ptr::NonNull::new(ptr).ok_or(crate::DeviceError::Lost)?,
692            is_coherent,
693        })
694    }
695    unsafe fn unmap_buffer(&self, buffer: &super::Buffer) {
696        if let Some(raw) = buffer.raw {
697            if buffer.data.is_none() {
698                let gl = &self.shared.context.lock();
699                unsafe { gl.bind_buffer(buffer.target, Some(raw)) };
700                unsafe { gl.unmap_buffer(buffer.target) };
701                unsafe { gl.bind_buffer(buffer.target, None) };
702                *buffer.offset_of_current_mapping.lock().unwrap() = 0;
703            }
704        }
705    }
706    unsafe fn flush_mapped_ranges<I>(&self, buffer: &super::Buffer, ranges: I)
707    where
708        I: Iterator<Item = crate::MemoryRange>,
709    {
710        if let Some(raw) = buffer.raw {
711            if buffer.data.is_none() {
712                let gl = &self.shared.context.lock();
713                unsafe { gl.bind_buffer(buffer.target, Some(raw)) };
714                for range in ranges {
715                    let offset_of_current_mapping =
716                        *buffer.offset_of_current_mapping.lock().unwrap();
717                    unsafe {
718                        gl.flush_mapped_buffer_range(
719                            buffer.target,
720                            (range.start - offset_of_current_mapping) as i32,
721                            (range.end - range.start) as i32,
722                        )
723                    };
724                }
725            }
726        }
727    }
728    unsafe fn invalidate_mapped_ranges<I>(&self, _buffer: &super::Buffer, _ranges: I) {
729        //TODO: do we need to do anything?
730    }
731
732    unsafe fn create_texture(
733        &self,
734        desc: &crate::TextureDescriptor,
735    ) -> Result<super::Texture, crate::DeviceError> {
736        let gl = &self.shared.context.lock();
737
738        let render_usage = crate::TextureUses::COLOR_TARGET
739            | crate::TextureUses::DEPTH_STENCIL_WRITE
740            | crate::TextureUses::DEPTH_STENCIL_READ;
741        let format_desc = self.shared.describe_texture_format(desc.format);
742
743        let inner = if render_usage.contains(desc.usage)
744            && desc.dimension == wgt::TextureDimension::D2
745            && desc.size.depth_or_array_layers == 1
746        {
747            let raw = unsafe { gl.create_renderbuffer().unwrap() };
748            unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(raw)) };
749            if desc.sample_count > 1 {
750                unsafe {
751                    gl.renderbuffer_storage_multisample(
752                        glow::RENDERBUFFER,
753                        desc.sample_count as i32,
754                        format_desc.internal,
755                        desc.size.width as i32,
756                        desc.size.height as i32,
757                    )
758                };
759            } else {
760                unsafe {
761                    gl.renderbuffer_storage(
762                        glow::RENDERBUFFER,
763                        format_desc.internal,
764                        desc.size.width as i32,
765                        desc.size.height as i32,
766                    )
767                };
768            }
769
770            #[cfg(native)]
771            if let Some(label) = desc.label {
772                if self
773                    .shared
774                    .private_caps
775                    .contains(PrivateCapabilities::DEBUG_FNS)
776                {
777                    let name = raw.0.get();
778                    unsafe { gl.object_label(glow::RENDERBUFFER, name, Some(label)) };
779                }
780            }
781
782            unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
783            super::TextureInner::Renderbuffer { raw }
784        } else {
785            let raw = unsafe { gl.create_texture().unwrap() };
786            let target = super::Texture::get_info_from_desc(desc);
787
788            unsafe { gl.bind_texture(target, Some(raw)) };
789            //Note: this has to be done before defining the storage!
790            match desc.format.sample_type(None, Some(self.shared.features)) {
791                Some(
792                    wgt::TextureSampleType::Float { filterable: false }
793                    | wgt::TextureSampleType::Uint
794                    | wgt::TextureSampleType::Sint,
795                ) => {
796                    // reset default filtering mode
797                    unsafe {
798                        gl.tex_parameter_i32(target, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32)
799                    };
800                    unsafe {
801                        gl.tex_parameter_i32(target, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32)
802                    };
803                }
804                _ => {}
805            }
806
807            if conv::is_layered_target(target) {
808                unsafe {
809                    if self
810                        .shared
811                        .private_caps
812                        .contains(PrivateCapabilities::TEXTURE_STORAGE)
813                    {
814                        gl.tex_storage_3d(
815                            target,
816                            desc.mip_level_count as i32,
817                            format_desc.internal,
818                            desc.size.width as i32,
819                            desc.size.height as i32,
820                            desc.size.depth_or_array_layers as i32,
821                        )
822                    } else if target == glow::TEXTURE_3D {
823                        let mut width = desc.size.width;
824                        let mut height = desc.size.width;
825                        let mut depth = desc.size.depth_or_array_layers;
826                        for i in 0..desc.mip_level_count {
827                            gl.tex_image_3d(
828                                target,
829                                i as i32,
830                                format_desc.internal as i32,
831                                width as i32,
832                                height as i32,
833                                depth as i32,
834                                0,
835                                format_desc.external,
836                                format_desc.data_type,
837                                None,
838                            );
839                            width = max(1, width / 2);
840                            height = max(1, height / 2);
841                            depth = max(1, depth / 2);
842                        }
843                    } else {
844                        let mut width = desc.size.width;
845                        let mut height = desc.size.width;
846                        for i in 0..desc.mip_level_count {
847                            gl.tex_image_3d(
848                                target,
849                                i as i32,
850                                format_desc.internal as i32,
851                                width as i32,
852                                height as i32,
853                                desc.size.depth_or_array_layers as i32,
854                                0,
855                                format_desc.external,
856                                format_desc.data_type,
857                                None,
858                            );
859                            width = max(1, width / 2);
860                            height = max(1, height / 2);
861                        }
862                    }
863                };
864            } else if desc.sample_count > 1 {
865                unsafe {
866                    gl.tex_storage_2d_multisample(
867                        target,
868                        desc.sample_count as i32,
869                        format_desc.internal,
870                        desc.size.width as i32,
871                        desc.size.height as i32,
872                        true,
873                    )
874                };
875            } else {
876                unsafe {
877                    if self
878                        .shared
879                        .private_caps
880                        .contains(PrivateCapabilities::TEXTURE_STORAGE)
881                    {
882                        gl.tex_storage_2d(
883                            target,
884                            desc.mip_level_count as i32,
885                            format_desc.internal,
886                            desc.size.width as i32,
887                            desc.size.height as i32,
888                        )
889                    } else if target == glow::TEXTURE_CUBE_MAP {
890                        let mut width = desc.size.width;
891                        let mut height = desc.size.width;
892                        for i in 0..desc.mip_level_count {
893                            for face in [
894                                glow::TEXTURE_CUBE_MAP_POSITIVE_X,
895                                glow::TEXTURE_CUBE_MAP_NEGATIVE_X,
896                                glow::TEXTURE_CUBE_MAP_POSITIVE_Y,
897                                glow::TEXTURE_CUBE_MAP_NEGATIVE_Y,
898                                glow::TEXTURE_CUBE_MAP_POSITIVE_Z,
899                                glow::TEXTURE_CUBE_MAP_NEGATIVE_Z,
900                            ] {
901                                gl.tex_image_2d(
902                                    face,
903                                    i as i32,
904                                    format_desc.internal as i32,
905                                    width as i32,
906                                    height as i32,
907                                    0,
908                                    format_desc.external,
909                                    format_desc.data_type,
910                                    None,
911                                );
912                            }
913                            width = max(1, width / 2);
914                            height = max(1, height / 2);
915                        }
916                    } else {
917                        let mut width = desc.size.width;
918                        let mut height = desc.size.width;
919                        for i in 0..desc.mip_level_count {
920                            gl.tex_image_2d(
921                                target,
922                                i as i32,
923                                format_desc.internal as i32,
924                                width as i32,
925                                height as i32,
926                                0,
927                                format_desc.external,
928                                format_desc.data_type,
929                                None,
930                            );
931                            width = max(1, width / 2);
932                            height = max(1, height / 2);
933                        }
934                    }
935                };
936            }
937
938            #[cfg(native)]
939            if let Some(label) = desc.label {
940                if self
941                    .shared
942                    .private_caps
943                    .contains(PrivateCapabilities::DEBUG_FNS)
944                {
945                    let name = raw.0.get();
946                    unsafe { gl.object_label(glow::TEXTURE, name, Some(label)) };
947                }
948            }
949
950            unsafe { gl.bind_texture(target, None) };
951            super::TextureInner::Texture { raw, target }
952        };
953
954        self.counters.textures.add(1);
955
956        Ok(super::Texture {
957            inner,
958            drop_guard: None,
959            mip_level_count: desc.mip_level_count,
960            array_layer_count: desc.array_layer_count(),
961            format: desc.format,
962            format_desc,
963            copy_size: desc.copy_extent(),
964        })
965    }
966
967    unsafe fn destroy_texture(&self, texture: super::Texture) {
968        if texture.drop_guard.is_none() {
969            let gl = &self.shared.context.lock();
970            match texture.inner {
971                super::TextureInner::Renderbuffer { raw, .. } => {
972                    unsafe { gl.delete_renderbuffer(raw) };
973                }
974                super::TextureInner::DefaultRenderbuffer => {}
975                super::TextureInner::Texture { raw, .. } => {
976                    unsafe { gl.delete_texture(raw) };
977                }
978                #[cfg(webgl)]
979                super::TextureInner::ExternalFramebuffer { .. } => {}
980            }
981        }
982
983        // For clarity, we explicitly drop the drop guard. Although this has no real semantic effect as the
984        // end of the scope will drop the drop guard since this function takes ownership of the texture.
985        drop(texture.drop_guard);
986
987        self.counters.textures.sub(1);
988    }
989
990    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
991        self.counters.textures.add(1);
992    }
993
994    unsafe fn create_texture_view(
995        &self,
996        texture: &super::Texture,
997        desc: &crate::TextureViewDescriptor,
998    ) -> Result<super::TextureView, crate::DeviceError> {
999        self.counters.texture_views.add(1);
1000        Ok(super::TextureView {
1001            //TODO: use `conv::map_view_dimension(desc.dimension)`?
1002            inner: texture.inner.clone(),
1003            aspects: crate::FormatAspects::new(texture.format, desc.range.aspect),
1004            mip_levels: desc.range.mip_range(texture.mip_level_count),
1005            array_layers: desc.range.layer_range(texture.array_layer_count),
1006            format: texture.format,
1007        })
1008    }
1009
1010    unsafe fn destroy_texture_view(&self, _view: super::TextureView) {
1011        self.counters.texture_views.sub(1);
1012    }
1013
1014    unsafe fn create_sampler(
1015        &self,
1016        desc: &crate::SamplerDescriptor,
1017    ) -> Result<super::Sampler, crate::DeviceError> {
1018        let gl = &self.shared.context.lock();
1019
1020        let raw = unsafe { gl.create_sampler().unwrap() };
1021
1022        let (min, mag) =
1023            conv::map_filter_modes(desc.min_filter, desc.mag_filter, desc.mipmap_filter);
1024
1025        unsafe { gl.sampler_parameter_i32(raw, glow::TEXTURE_MIN_FILTER, min as i32) };
1026        unsafe { gl.sampler_parameter_i32(raw, glow::TEXTURE_MAG_FILTER, mag as i32) };
1027
1028        unsafe {
1029            gl.sampler_parameter_i32(
1030                raw,
1031                glow::TEXTURE_WRAP_S,
1032                conv::map_address_mode(desc.address_modes[0]) as i32,
1033            )
1034        };
1035        unsafe {
1036            gl.sampler_parameter_i32(
1037                raw,
1038                glow::TEXTURE_WRAP_T,
1039                conv::map_address_mode(desc.address_modes[1]) as i32,
1040            )
1041        };
1042        unsafe {
1043            gl.sampler_parameter_i32(
1044                raw,
1045                glow::TEXTURE_WRAP_R,
1046                conv::map_address_mode(desc.address_modes[2]) as i32,
1047            )
1048        };
1049
1050        if let Some(border_color) = desc.border_color {
1051            let border = match border_color {
1052                wgt::SamplerBorderColor::TransparentBlack | wgt::SamplerBorderColor::Zero => {
1053                    [0.0; 4]
1054                }
1055                wgt::SamplerBorderColor::OpaqueBlack => [0.0, 0.0, 0.0, 1.0],
1056                wgt::SamplerBorderColor::OpaqueWhite => [1.0; 4],
1057            };
1058            unsafe { gl.sampler_parameter_f32_slice(raw, glow::TEXTURE_BORDER_COLOR, &border) };
1059        }
1060
1061        unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MIN_LOD, desc.lod_clamp.start) };
1062        unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MAX_LOD, desc.lod_clamp.end) };
1063
1064        // If clamp is not 1, we know anisotropy is supported up to 16x
1065        if desc.anisotropy_clamp != 1 {
1066            unsafe {
1067                gl.sampler_parameter_i32(
1068                    raw,
1069                    glow::TEXTURE_MAX_ANISOTROPY,
1070                    desc.anisotropy_clamp as i32,
1071                )
1072            };
1073        }
1074
1075        //set_param_float(glow::TEXTURE_LOD_BIAS, info.lod_bias.0);
1076
1077        if let Some(compare) = desc.compare {
1078            unsafe {
1079                gl.sampler_parameter_i32(
1080                    raw,
1081                    glow::TEXTURE_COMPARE_MODE,
1082                    glow::COMPARE_REF_TO_TEXTURE as i32,
1083                )
1084            };
1085            unsafe {
1086                gl.sampler_parameter_i32(
1087                    raw,
1088                    glow::TEXTURE_COMPARE_FUNC,
1089                    conv::map_compare_func(compare) as i32,
1090                )
1091            };
1092        }
1093
1094        #[cfg(native)]
1095        if let Some(label) = desc.label {
1096            if self
1097                .shared
1098                .private_caps
1099                .contains(PrivateCapabilities::DEBUG_FNS)
1100            {
1101                let name = raw.0.get();
1102                unsafe { gl.object_label(glow::SAMPLER, name, Some(label)) };
1103            }
1104        }
1105
1106        self.counters.samplers.add(1);
1107
1108        Ok(super::Sampler { raw })
1109    }
1110
1111    unsafe fn destroy_sampler(&self, sampler: super::Sampler) {
1112        let gl = &self.shared.context.lock();
1113        unsafe { gl.delete_sampler(sampler.raw) };
1114        self.counters.samplers.sub(1);
1115    }
1116
1117    unsafe fn create_command_encoder(
1118        &self,
1119        _desc: &crate::CommandEncoderDescriptor<super::Queue>,
1120    ) -> Result<super::CommandEncoder, crate::DeviceError> {
1121        self.counters.command_encoders.add(1);
1122
1123        Ok(super::CommandEncoder {
1124            cmd_buffer: super::CommandBuffer::default(),
1125            state: Default::default(),
1126            private_caps: self.shared.private_caps,
1127        })
1128    }
1129
1130    unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {
1131        self.counters.command_encoders.sub(1);
1132    }
1133
1134    unsafe fn create_bind_group_layout(
1135        &self,
1136        desc: &crate::BindGroupLayoutDescriptor,
1137    ) -> Result<super::BindGroupLayout, crate::DeviceError> {
1138        self.counters.bind_group_layouts.add(1);
1139        Ok(super::BindGroupLayout {
1140            entries: Arc::from(desc.entries),
1141        })
1142    }
1143
1144    unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {
1145        self.counters.bind_group_layouts.sub(1);
1146    }
1147
1148    unsafe fn create_pipeline_layout(
1149        &self,
1150        desc: &crate::PipelineLayoutDescriptor<super::BindGroupLayout>,
1151    ) -> Result<super::PipelineLayout, crate::DeviceError> {
1152        use naga::back::glsl;
1153
1154        let mut group_infos = Vec::with_capacity(desc.bind_group_layouts.len());
1155        let mut num_samplers = 0u8;
1156        let mut num_textures = 0u8;
1157        let mut num_images = 0u8;
1158        let mut num_uniform_buffers = 0u8;
1159        let mut num_storage_buffers = 0u8;
1160
1161        let mut writer_flags = glsl::WriterFlags::ADJUST_COORDINATE_SPACE;
1162        writer_flags.set(
1163            glsl::WriterFlags::TEXTURE_SHADOW_LOD,
1164            self.shared
1165                .private_caps
1166                .contains(PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD),
1167        );
1168        writer_flags.set(
1169            glsl::WriterFlags::DRAW_PARAMETERS,
1170            self.shared
1171                .private_caps
1172                .contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING),
1173        );
1174        // We always force point size to be written and it will be ignored by the driver if it's not a point list primitive.
1175        // https://github.com/gfx-rs/wgpu/pull/3440/files#r1095726950
1176        writer_flags.set(glsl::WriterFlags::FORCE_POINT_SIZE, true);
1177        let mut binding_map = glsl::BindingMap::default();
1178
1179        for (group_index, bg_layout) in desc.bind_group_layouts.iter().enumerate() {
1180            // create a vector with the size enough to hold all the bindings, filled with `!0`
1181            let mut binding_to_slot = vec![
1182                !0;
1183                bg_layout
1184                    .entries
1185                    .iter()
1186                    .map(|b| b.binding)
1187                    .max()
1188                    .map_or(0, |idx| idx as usize + 1)
1189            ]
1190            .into_boxed_slice();
1191
1192            for entry in bg_layout.entries.iter() {
1193                let counter = match entry.ty {
1194                    wgt::BindingType::Sampler { .. } => &mut num_samplers,
1195                    wgt::BindingType::Texture { .. } => &mut num_textures,
1196                    wgt::BindingType::StorageTexture { .. } => &mut num_images,
1197                    wgt::BindingType::Buffer {
1198                        ty: wgt::BufferBindingType::Uniform,
1199                        ..
1200                    } => &mut num_uniform_buffers,
1201                    wgt::BindingType::Buffer {
1202                        ty: wgt::BufferBindingType::Storage { .. },
1203                        ..
1204                    } => &mut num_storage_buffers,
1205                    wgt::BindingType::AccelerationStructure => unimplemented!(),
1206                };
1207
1208                binding_to_slot[entry.binding as usize] = *counter;
1209                let br = naga::ResourceBinding {
1210                    group: group_index as u32,
1211                    binding: entry.binding,
1212                };
1213                binding_map.insert(br, *counter);
1214                *counter += entry.count.map_or(1, |c| c.get() as u8);
1215            }
1216
1217            group_infos.push(super::BindGroupLayoutInfo {
1218                entries: Arc::clone(&bg_layout.entries),
1219                binding_to_slot,
1220            });
1221        }
1222
1223        self.counters.pipeline_layouts.add(1);
1224
1225        Ok(super::PipelineLayout {
1226            group_infos: group_infos.into_boxed_slice(),
1227            naga_options: glsl::Options {
1228                version: self.shared.shading_language_version,
1229                writer_flags,
1230                binding_map,
1231                zero_initialize_workgroup_memory: true,
1232            },
1233        })
1234    }
1235
1236    unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {
1237        self.counters.pipeline_layouts.sub(1);
1238    }
1239
1240    unsafe fn create_bind_group(
1241        &self,
1242        desc: &crate::BindGroupDescriptor<
1243            super::BindGroupLayout,
1244            super::Buffer,
1245            super::Sampler,
1246            super::TextureView,
1247            super::AccelerationStructure,
1248        >,
1249    ) -> Result<super::BindGroup, crate::DeviceError> {
1250        let mut contents = Vec::new();
1251
1252        let layout_and_entry_iter = desc.entries.iter().map(|entry| {
1253            let layout = desc
1254                .layout
1255                .entries
1256                .iter()
1257                .find(|layout_entry| layout_entry.binding == entry.binding)
1258                .expect("internal error: no layout entry found with binding slot");
1259            (entry, layout)
1260        });
1261        for (entry, layout) in layout_and_entry_iter {
1262            let binding = match layout.ty {
1263                wgt::BindingType::Buffer { .. } => {
1264                    let bb = &desc.buffers[entry.resource_index as usize];
1265                    super::RawBinding::Buffer {
1266                        raw: bb.buffer.raw.unwrap(),
1267                        offset: bb.offset as i32,
1268                        size: match bb.size {
1269                            Some(s) => s.get() as i32,
1270                            None => (bb.buffer.size - bb.offset) as i32,
1271                        },
1272                    }
1273                }
1274                wgt::BindingType::Sampler { .. } => {
1275                    let sampler = desc.samplers[entry.resource_index as usize];
1276                    super::RawBinding::Sampler(sampler.raw)
1277                }
1278                wgt::BindingType::Texture { view_dimension, .. } => {
1279                    let view = desc.textures[entry.resource_index as usize].view;
1280                    if view.array_layers.start != 0 {
1281                        log::error!("Unable to create a sampled texture binding for non-zero array layer.\n{}",
1282                            "This is an implementation problem of wgpu-hal/gles backend.")
1283                    }
1284                    let (raw, target) = view.inner.as_native();
1285
1286                    super::Texture::log_failing_target_heuristics(view_dimension, target);
1287
1288                    super::RawBinding::Texture {
1289                        raw,
1290                        target,
1291                        aspects: view.aspects,
1292                        mip_levels: view.mip_levels.clone(),
1293                    }
1294                }
1295                wgt::BindingType::StorageTexture {
1296                    access,
1297                    format,
1298                    view_dimension,
1299                } => {
1300                    let view = desc.textures[entry.resource_index as usize].view;
1301                    let format_desc = self.shared.describe_texture_format(format);
1302                    let (raw, _target) = view.inner.as_native();
1303                    super::RawBinding::Image(super::ImageBinding {
1304                        raw,
1305                        mip_level: view.mip_levels.start,
1306                        array_layer: match view_dimension {
1307                            wgt::TextureViewDimension::D2Array
1308                            | wgt::TextureViewDimension::CubeArray => None,
1309                            _ => Some(view.array_layers.start),
1310                        },
1311                        access: conv::map_storage_access(access),
1312                        format: format_desc.internal,
1313                    })
1314                }
1315                wgt::BindingType::AccelerationStructure => unimplemented!(),
1316            };
1317            contents.push(binding);
1318        }
1319
1320        self.counters.bind_groups.add(1);
1321
1322        Ok(super::BindGroup {
1323            contents: contents.into_boxed_slice(),
1324        })
1325    }
1326
1327    unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {
1328        self.counters.bind_groups.sub(1);
1329    }
1330
1331    unsafe fn create_shader_module(
1332        &self,
1333        desc: &crate::ShaderModuleDescriptor,
1334        shader: crate::ShaderInput,
1335    ) -> Result<super::ShaderModule, crate::ShaderError> {
1336        self.counters.shader_modules.add(1);
1337
1338        Ok(super::ShaderModule {
1339            naga: match shader {
1340                crate::ShaderInput::SpirV(_) => {
1341                    panic!("`Features::SPIRV_SHADER_PASSTHROUGH` is not enabled")
1342                }
1343                crate::ShaderInput::Naga(naga) => naga,
1344            },
1345            label: desc.label.map(|str| str.to_string()),
1346            id: self.shared.next_shader_id.fetch_add(1, Ordering::Relaxed),
1347        })
1348    }
1349
1350    unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {
1351        self.counters.shader_modules.sub(1);
1352    }
1353
1354    unsafe fn create_render_pipeline(
1355        &self,
1356        desc: &crate::RenderPipelineDescriptor<
1357            super::PipelineLayout,
1358            super::ShaderModule,
1359            super::PipelineCache,
1360        >,
1361    ) -> Result<super::RenderPipeline, crate::PipelineError> {
1362        let gl = &self.shared.context.lock();
1363        let mut shaders = ArrayVec::new();
1364        shaders.push((naga::ShaderStage::Vertex, &desc.vertex_stage));
1365        if let Some(ref fs) = desc.fragment_stage {
1366            shaders.push((naga::ShaderStage::Fragment, fs));
1367        }
1368        let inner =
1369            unsafe { self.create_pipeline(gl, shaders, desc.layout, desc.label, desc.multiview) }?;
1370
1371        let (vertex_buffers, vertex_attributes) = {
1372            let mut buffers = Vec::new();
1373            let mut attributes = Vec::new();
1374            for (index, vb_layout) in desc.vertex_buffers.iter().enumerate() {
1375                buffers.push(super::VertexBufferDesc {
1376                    step: vb_layout.step_mode,
1377                    stride: vb_layout.array_stride as u32,
1378                });
1379                for vat in vb_layout.attributes.iter() {
1380                    let format_desc = conv::describe_vertex_format(vat.format);
1381                    attributes.push(super::AttributeDesc {
1382                        location: vat.shader_location,
1383                        offset: vat.offset as u32,
1384                        buffer_index: index as u32,
1385                        format_desc,
1386                    });
1387                }
1388            }
1389            (buffers.into_boxed_slice(), attributes.into_boxed_slice())
1390        };
1391
1392        let color_targets = {
1393            let mut targets = Vec::new();
1394            for ct in desc.color_targets.iter().filter_map(|at| at.as_ref()) {
1395                targets.push(super::ColorTargetDesc {
1396                    mask: ct.write_mask,
1397                    blend: ct.blend.as_ref().map(conv::map_blend),
1398                });
1399            }
1400            //Note: if any of the states are different, and `INDEPENDENT_BLEND` flag
1401            // is not exposed, then this pipeline will not bind correctly.
1402            targets.into_boxed_slice()
1403        };
1404
1405        self.counters.render_pipelines.add(1);
1406
1407        Ok(super::RenderPipeline {
1408            inner,
1409            primitive: desc.primitive,
1410            vertex_buffers,
1411            vertex_attributes,
1412            color_targets,
1413            depth: desc.depth_stencil.as_ref().map(|ds| super::DepthState {
1414                function: conv::map_compare_func(ds.depth_compare),
1415                mask: ds.depth_write_enabled,
1416            }),
1417            depth_bias: desc
1418                .depth_stencil
1419                .as_ref()
1420                .map(|ds| ds.bias)
1421                .unwrap_or_default(),
1422            stencil: desc
1423                .depth_stencil
1424                .as_ref()
1425                .map(|ds| conv::map_stencil(&ds.stencil)),
1426            alpha_to_coverage_enabled: desc.multisample.alpha_to_coverage_enabled,
1427        })
1428    }
1429
1430    unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) {
1431        // If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache`
1432        // This is safe to assume as long as:
1433        // - `RenderPipeline` can't be cloned
1434        // - The only place that we can get a new reference is during `program_cache.lock()`
1435        if Arc::strong_count(&pipeline.inner) == 2 {
1436            let gl = &self.shared.context.lock();
1437            let mut program_cache = self.shared.program_cache.lock();
1438            program_cache.retain(|_, v| match *v {
1439                Ok(ref p) => p.program != pipeline.inner.program,
1440                Err(_) => false,
1441            });
1442            unsafe { gl.delete_program(pipeline.inner.program) };
1443        }
1444
1445        self.counters.render_pipelines.sub(1);
1446    }
1447
1448    unsafe fn create_compute_pipeline(
1449        &self,
1450        desc: &crate::ComputePipelineDescriptor<
1451            super::PipelineLayout,
1452            super::ShaderModule,
1453            super::PipelineCache,
1454        >,
1455    ) -> Result<super::ComputePipeline, crate::PipelineError> {
1456        let gl = &self.shared.context.lock();
1457        let mut shaders = ArrayVec::new();
1458        shaders.push((naga::ShaderStage::Compute, &desc.stage));
1459        let inner = unsafe { self.create_pipeline(gl, shaders, desc.layout, desc.label, None) }?;
1460
1461        self.counters.compute_pipelines.add(1);
1462
1463        Ok(super::ComputePipeline { inner })
1464    }
1465
1466    unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) {
1467        // If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache``
1468        // This is safe to assume as long as:
1469        // - `ComputePipeline` can't be cloned
1470        // - The only place that we can get a new reference is during `program_cache.lock()`
1471        if Arc::strong_count(&pipeline.inner) == 2 {
1472            let gl = &self.shared.context.lock();
1473            let mut program_cache = self.shared.program_cache.lock();
1474            program_cache.retain(|_, v| match *v {
1475                Ok(ref p) => p.program != pipeline.inner.program,
1476                Err(_) => false,
1477            });
1478            unsafe { gl.delete_program(pipeline.inner.program) };
1479        }
1480
1481        self.counters.compute_pipelines.sub(1);
1482    }
1483
1484    unsafe fn create_pipeline_cache(
1485        &self,
1486        _: &crate::PipelineCacheDescriptor<'_>,
1487    ) -> Result<super::PipelineCache, crate::PipelineCacheError> {
1488        // Even though the cache doesn't do anything, we still return something here
1489        // as the least bad option
1490        Ok(super::PipelineCache)
1491    }
1492    unsafe fn destroy_pipeline_cache(&self, _: super::PipelineCache) {}
1493
1494    #[cfg_attr(target_arch = "wasm32", allow(unused))]
1495    unsafe fn create_query_set(
1496        &self,
1497        desc: &wgt::QuerySetDescriptor<crate::Label>,
1498    ) -> Result<super::QuerySet, crate::DeviceError> {
1499        let gl = &self.shared.context.lock();
1500
1501        let mut queries = Vec::with_capacity(desc.count as usize);
1502        for _ in 0..desc.count {
1503            let query =
1504                unsafe { gl.create_query() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
1505
1506            // We aren't really able to, in general, label queries.
1507            //
1508            // We could take a timestamp here to "initialize" the query,
1509            // but that's a bit of a hack, and we don't want to insert
1510            // random timestamps into the command stream of we don't have to.
1511
1512            queries.push(query);
1513        }
1514
1515        self.counters.query_sets.add(1);
1516
1517        Ok(super::QuerySet {
1518            queries: queries.into_boxed_slice(),
1519            target: match desc.ty {
1520                wgt::QueryType::Occlusion => glow::ANY_SAMPLES_PASSED_CONSERVATIVE,
1521                wgt::QueryType::Timestamp => glow::TIMESTAMP,
1522                _ => unimplemented!(),
1523            },
1524        })
1525    }
1526
1527    unsafe fn destroy_query_set(&self, set: super::QuerySet) {
1528        let gl = &self.shared.context.lock();
1529        for &query in set.queries.iter() {
1530            unsafe { gl.delete_query(query) };
1531        }
1532        self.counters.query_sets.sub(1);
1533    }
1534
1535    unsafe fn create_fence(&self) -> Result<super::Fence, crate::DeviceError> {
1536        self.counters.fences.add(1);
1537        Ok(super::Fence {
1538            last_completed: AtomicFenceValue::new(0),
1539            pending: Vec::new(),
1540        })
1541    }
1542
1543    unsafe fn destroy_fence(&self, fence: super::Fence) {
1544        let gl = &self.shared.context.lock();
1545        for (_, sync) in fence.pending {
1546            unsafe { gl.delete_sync(sync) };
1547        }
1548        self.counters.fences.sub(1);
1549    }
1550
1551    unsafe fn get_fence_value(
1552        &self,
1553        fence: &super::Fence,
1554    ) -> Result<crate::FenceValue, crate::DeviceError> {
1555        #[cfg_attr(target_arch = "wasm32", allow(clippy::needless_borrow))]
1556        Ok(fence.get_latest(&self.shared.context.lock()))
1557    }
1558    unsafe fn wait(
1559        &self,
1560        fence: &super::Fence,
1561        wait_value: crate::FenceValue,
1562        timeout_ms: u32,
1563    ) -> Result<bool, crate::DeviceError> {
1564        if fence.last_completed.load(Ordering::Relaxed) < wait_value {
1565            let gl = &self.shared.context.lock();
1566            let timeout_ns = if cfg!(any(webgl, Emscripten)) {
1567                0
1568            } else {
1569                (timeout_ms as u64 * 1_000_000).min(!0u32 as u64)
1570            };
1571            if let Some(&(_, sync)) = fence
1572                .pending
1573                .iter()
1574                .find(|&&(value, _)| value >= wait_value)
1575            {
1576                let signalled = match unsafe {
1577                    gl.client_wait_sync(sync, glow::SYNC_FLUSH_COMMANDS_BIT, timeout_ns as i32)
1578                } {
1579                    // for some reason firefox returns WAIT_FAILED, to investigate
1580                    #[cfg(any(webgl, Emscripten))]
1581                    glow::WAIT_FAILED => {
1582                        log::warn!("wait failed!");
1583                        false
1584                    }
1585                    glow::TIMEOUT_EXPIRED => false,
1586                    glow::CONDITION_SATISFIED | glow::ALREADY_SIGNALED => true,
1587                    _ => return Err(crate::DeviceError::Lost),
1588                };
1589                if signalled {
1590                    fence
1591                        .last_completed
1592                        .fetch_max(wait_value, Ordering::Relaxed);
1593                }
1594                return Ok(signalled);
1595            }
1596        }
1597        Ok(true)
1598    }
1599
1600    unsafe fn start_capture(&self) -> bool {
1601        #[cfg(all(native, feature = "renderdoc"))]
1602        return unsafe {
1603            self.render_doc
1604                .start_frame_capture(self.shared.context.raw_context(), ptr::null_mut())
1605        };
1606        #[allow(unreachable_code)]
1607        false
1608    }
1609    unsafe fn stop_capture(&self) {
1610        #[cfg(all(native, feature = "renderdoc"))]
1611        unsafe {
1612            self.render_doc
1613                .end_frame_capture(ptr::null_mut(), ptr::null_mut())
1614        }
1615    }
1616    unsafe fn create_acceleration_structure(
1617        &self,
1618        _desc: &crate::AccelerationStructureDescriptor,
1619    ) -> Result<super::AccelerationStructure, crate::DeviceError> {
1620        unimplemented!()
1621    }
1622    unsafe fn get_acceleration_structure_build_sizes<'a>(
1623        &self,
1624        _desc: &crate::GetAccelerationStructureBuildSizesDescriptor<'a, super::Buffer>,
1625    ) -> crate::AccelerationStructureBuildSizes {
1626        unimplemented!()
1627    }
1628    unsafe fn get_acceleration_structure_device_address(
1629        &self,
1630        _acceleration_structure: &super::AccelerationStructure,
1631    ) -> wgt::BufferAddress {
1632        unimplemented!()
1633    }
1634    unsafe fn destroy_acceleration_structure(
1635        &self,
1636        _acceleration_structure: super::AccelerationStructure,
1637    ) {
1638    }
1639
1640    fn get_internal_counters(&self) -> wgt::HalCounters {
1641        self.counters.clone()
1642    }
1643}
1644
1645#[cfg(send_sync)]
1646unsafe impl Sync for super::Device {}
1647#[cfg(send_sync)]
1648unsafe impl Send for super::Device {}