wgpu_hal/gles/
device.rs

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