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