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