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 #[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 #[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 #[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 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 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 #[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 #[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 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 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 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 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 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 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 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 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 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 #[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 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 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 }
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 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 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 #[cfg(webgl)]
1201 if let super::TextureInner::Texture { raw, .. } = texture.inner {
1202 self.shared.context.lock().unregister_external_texture(raw);
1203 }
1204 }
1205
1206 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 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 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 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 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 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 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 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 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 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 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 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 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 {}