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