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