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