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