1#![allow(missing_docs, missing_copy_implementations)]
24
25use std::{
26 cell::Cell,
27 collections::HashMap,
28 fmt,
29 hash::BuildHasherDefault,
30 ops::{Deref, Range},
31 sync::{Arc, Weak},
32 thread,
33};
34
35use hal::{adapter, buffer, display, image, memory, queue as q};
36
37pub use self::device::Device;
38pub use self::info::{Info, PlatformName, Version};
39
40mod command;
41mod conv;
42mod device;
43mod info;
44mod native;
45mod pool;
46mod queue;
47mod state;
48mod window;
49
50#[cfg(target_arch = "wasm32")]
52pub use window::web::{Instance, Surface, Swapchain};
53
54#[cfg(not(target_arch = "wasm32"))]
55pub use window::egl::{Instance, Surface, Swapchain};
56
57pub use glow::Context as GlContext;
58use glow::HasContext;
59
60type ColorSlot = u8;
61type FastHashMap<K, V> = HashMap<K, V, BuildHasherDefault<fxhash::FxHasher>>;
62
63const MAX_SAMPLERS: usize = 16;
66const MAX_TEXTURE_SLOTS: usize = 16;
68const MAX_COLOR_ATTACHMENTS: usize = 16;
69
70struct GlContainer {
71 context: GlContext,
72}
73
74impl Deref for GlContainer {
75 type Target = GlContext;
76 fn deref(&self) -> &GlContext {
77 &self.context
78 }
79}
80
81#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
82pub enum Backend {}
83
84impl hal::Backend for Backend {
85 type Instance = Instance;
86
87 type PhysicalDevice = PhysicalDevice;
88 type Device = Device;
89 type Surface = Surface;
90
91 type QueueFamily = QueueFamily;
92 type Queue = queue::Queue;
93 type CommandBuffer = command::CommandBuffer;
94
95 type Memory = native::Memory;
96 type CommandPool = pool::CommandPool;
97
98 type ShaderModule = native::ShaderModule;
99 type RenderPass = native::RenderPass;
100 type Framebuffer = native::Framebuffer;
101
102 type Buffer = native::Buffer;
103 type BufferView = native::BufferView;
104 type Image = native::Image;
105 type ImageView = native::ImageView;
106 type Sampler = native::FatSampler;
107
108 type ComputePipeline = native::ComputePipeline;
109 type GraphicsPipeline = native::GraphicsPipeline;
110 type PipelineLayout = native::PipelineLayout;
111 type PipelineCache = ();
112 type DescriptorSetLayout = native::DescriptorSetLayout;
113 type DescriptorPool = native::DescriptorPool;
114 type DescriptorSet = native::DescriptorSet;
115
116 type Fence = native::Fence;
117 type Semaphore = native::Semaphore;
118 type Event = ();
119 type QueryPool = ();
120
121 type Display = ();
122 type DisplayMode = ();
123}
124
125#[derive(Copy, Clone, Eq, PartialEq, Debug)]
126pub enum Error {
127 NoError,
128 InvalidEnum,
129 InvalidValue,
130 InvalidOperation,
131 InvalidFramebufferOperation,
132 OutOfMemory,
133 UnknownError,
134}
135
136impl Error {
137 pub fn from_error_code(error_code: u32) -> Error {
138 match error_code {
139 glow::NO_ERROR => Error::NoError,
140 glow::INVALID_ENUM => Error::InvalidEnum,
141 glow::INVALID_VALUE => Error::InvalidValue,
142 glow::INVALID_OPERATION => Error::InvalidOperation,
143 glow::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
144 glow::OUT_OF_MEMORY => Error::OutOfMemory,
145 _ => Error::UnknownError,
146 }
147 }
148}
149
150fn debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
151 let source_str = match source {
152 glow::DEBUG_SOURCE_API => "API",
153 glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
154 glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
155 glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
156 glow::DEBUG_SOURCE_APPLICATION => "Application",
157 glow::DEBUG_SOURCE_OTHER => "Other",
158 _ => unreachable!(),
159 };
160
161 let log_severity = match severity {
162 glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
163 glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
164 glow::DEBUG_SEVERITY_LOW => log::Level::Info,
165 glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
166 _ => unreachable!(),
167 };
168
169 let type_str = match gltype {
170 glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
171 glow::DEBUG_TYPE_ERROR => "Error",
172 glow::DEBUG_TYPE_MARKER => "Marker",
173 glow::DEBUG_TYPE_OTHER => "Other",
174 glow::DEBUG_TYPE_PERFORMANCE => "Performance",
175 glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
176 glow::DEBUG_TYPE_PORTABILITY => "Portability",
177 glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
178 glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
179 _ => unreachable!(),
180 };
181
182 log::log!(
183 log_severity,
184 "[{}/{}] ID {} : {}",
185 source_str,
186 type_str,
187 id,
188 message
189 );
190}
191
192const DEVICE_LOCAL_HEAP: usize = 0;
193const CPU_VISIBLE_HEAP: usize = 1;
194
195#[derive(Copy, Clone, Debug)]
198enum MemoryUsage {
199 Buffer(buffer::Usage),
200 Image,
201}
202
203struct Share {
205 context: GlContainer,
206 info: Info,
207 supported_features: hal::Features,
208 legacy_features: info::LegacyFeatures,
209 public_caps: hal::PhysicalDeviceProperties,
210 private_caps: info::PrivateCaps,
211 open: Cell<bool>,
213 memory_types: Vec<(adapter::MemoryType, MemoryUsage)>,
214 texture_format_filter: info::TextureFormatFilter,
215}
216
217impl Share {
218 fn check(&self) -> Result<(), Error> {
220 if cfg!(debug_assertions) {
221 let gl = &self.context;
222 let err = Error::from_error_code(unsafe { gl.get_error() });
223 if err != Error::NoError {
224 return Err(err);
225 }
226 }
227 Ok(())
228 }
229
230 fn buffer_memory_type_mask(&self, usage: buffer::Usage) -> u32 {
231 let mut type_mask = 0;
232 for (type_index, &(_, kind)) in self.memory_types.iter().enumerate() {
233 match kind {
234 MemoryUsage::Buffer(buffer_usage) => {
235 if buffer_usage.contains(usage) {
236 type_mask |= 1 << type_index;
237 }
238 }
239 MemoryUsage::Image => {}
240 }
241 }
242 if type_mask == 0 {
243 log::error!(
244 "gl backend capability does not allow a buffer with usage {:?}",
245 usage
246 );
247 }
248 type_mask
249 }
250
251 fn image_memory_type_mask(&self) -> u32 {
252 let mut type_mask = 0;
253 for (type_index, &(_, kind)) in self.memory_types.iter().enumerate() {
254 match kind {
255 MemoryUsage::Buffer(_) => {}
256 MemoryUsage::Image => {
257 type_mask |= 1 << type_index;
258 }
259 }
260 }
261 assert_ne!(type_mask, 0);
262 type_mask
263 }
264}
265
266pub struct Starc<T: ?Sized> {
270 arc: Arc<T>,
271 thread: thread::ThreadId,
272}
273
274impl<T: ?Sized> Clone for Starc<T> {
275 fn clone(&self) -> Self {
276 Self {
277 arc: self.arc.clone(),
278 thread: self.thread,
279 }
280 }
281}
282
283impl<T: ?Sized> fmt::Debug for Starc<T> {
284 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
285 write!(fmt, "{:p}@{:?}", self.arc, self.thread)
286 }
287}
288
289impl<T> Starc<T> {
290 #[inline]
291 fn new(value: T) -> Self {
292 Starc {
293 arc: Arc::new(value),
294 thread: thread::current().id(),
295 }
296 }
297 #[inline]
298 pub fn try_unwrap(self) -> Result<T, Self> {
299 let a = Arc::try_unwrap(self.arc);
300 let thread = self.thread;
301 a.map_err(|a| Starc {
302 arc: a,
303 thread: thread,
304 })
305 }
306}
307
308impl<T> Starc<T>
309where
310 T: ?Sized,
311{
312 #[inline]
313 pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
314 Wstarc {
315 weak: Arc::downgrade(&this.arc),
316 thread: this.thread,
317 }
318 }
319
320 #[inline]
321 pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
322 Arc::get_mut(&mut this.arc)
323 }
324}
325
326unsafe impl<T: ?Sized> Send for Starc<T> {}
327unsafe impl<T: ?Sized> Sync for Starc<T> {}
328
329impl<T: ?Sized> Deref for Starc<T> {
330 type Target = T;
331 fn deref(&self) -> &T {
332 assert_eq!(thread::current().id(), self.thread);
333 &*self.arc
334 }
335}
336
337pub struct Wstarc<T: ?Sized> {
341 weak: Weak<T>,
342 thread: thread::ThreadId,
343}
344impl<T> Wstarc<T> {
345 pub fn upgrade(&self) -> Option<Starc<T>> {
346 let thread = self.thread;
347 self.weak.upgrade().map(|arc| Starc { arc, thread })
348 }
349}
350unsafe impl<T: ?Sized> Send for Wstarc<T> {}
351unsafe impl<T: ?Sized> Sync for Wstarc<T> {}
352
353#[derive(Debug)]
354pub struct PhysicalDevice(Starc<Share>);
355
356impl PhysicalDevice {
357 fn new_adapter(context: GlContext) -> adapter::Adapter<Backend> {
358 let gl = GlContainer { context };
359 let (
361 info,
362 supported_features,
363 legacy_features,
364 public_caps,
365 private_caps,
366 texture_format_filter,
367 ) = info::query_all(&gl);
368 log::info!("Vendor: {:?}", info.platform_name.vendor);
369 log::info!("Renderer: {:?}", info.platform_name.renderer);
370 log::info!("Version: {:?}", info.version);
371 log::info!("Shading Language: {:?}", info.shading_language);
372 log::info!("Supported Features: {:?}", supported_features);
373 log::info!("Legacy Features: {:?}", legacy_features);
374 log::debug!("Public capabilities: {:#?}", public_caps);
375 log::debug!("Private capabilities: {:#?}", private_caps);
376 log::debug!("Texture format filter: {:#?}", texture_format_filter);
377 log::debug!("Loaded Extensions:");
378 for extension in info.extensions.iter() {
379 log::debug!("- {}", *extension);
380 }
381 let name = info.platform_name.renderer.clone();
382 let vendor: std::string::String = info.platform_name.vendor.clone();
383 let renderer: std::string::String = info.platform_name.renderer.clone();
384
385 let mut memory_types = Vec::new();
386
387 let mut add_buffer_memory_type = |memory_type: adapter::MemoryType| {
388 if private_caps.index_buffer_role_change {
389 memory_types.push((memory_type, MemoryUsage::Buffer(buffer::Usage::all())));
391 } else {
392 memory_types.push((
396 memory_type,
397 MemoryUsage::Buffer(buffer::Usage::INDEX | buffer::Usage::TRANSFER_DST),
398 ));
399 memory_types.push((
400 memory_type,
401 MemoryUsage::Buffer(buffer::Usage::all() - buffer::Usage::INDEX),
402 ));
403 }
404 };
405
406 let coherent_flag = if private_caps.buffer_storage {
408 memory::Properties::COHERENT
409 } else {
410 memory::Properties::empty()
411 };
412 add_buffer_memory_type(adapter::MemoryType {
414 properties: coherent_flag
415 | memory::Properties::CPU_VISIBLE
416 | memory::Properties::CPU_CACHED,
417 heap_index: CPU_VISIBLE_HEAP,
418 });
419 add_buffer_memory_type(adapter::MemoryType {
420 properties: coherent_flag | memory::Properties::CPU_VISIBLE,
421 heap_index: CPU_VISIBLE_HEAP,
422 });
423
424 add_buffer_memory_type(adapter::MemoryType {
425 properties: memory::Properties::DEVICE_LOCAL,
426 heap_index: DEVICE_LOCAL_HEAP,
427 });
428
429 memory_types.push((
431 adapter::MemoryType {
432 properties: memory::Properties::DEVICE_LOCAL,
433 heap_index: DEVICE_LOCAL_HEAP,
434 },
435 MemoryUsage::Image,
436 ));
437
438 assert!(memory_types.len() <= 64);
439
440 log::info!("Memory types: {:#?}", memory_types);
441
442 let share = Share {
444 context: gl,
445 info,
446 supported_features,
447 legacy_features,
448 public_caps,
449 texture_format_filter,
450 private_caps,
451 open: Cell::new(false),
452 memory_types,
453 };
454 if let Err(err) = share.check() {
455 panic!("Error querying info: {:?}", err);
456 }
457
458 let vendor_lower = vendor.to_lowercase();
460 let renderer_lower = renderer.to_lowercase();
461 let strings_that_imply_integrated = [
462 " xpress", "radeon hd 4200",
464 "radeon hd 4250",
465 "radeon hd 4290",
466 "radeon hd 4270",
467 "radeon hd 4225",
468 "radeon hd 3100",
469 "radeon hd 3200",
470 "radeon hd 3000",
471 "radeon hd 3300",
472 "radeon(tm) r4 graphics",
473 "radeon(tm) r5 graphics",
474 "radeon(tm) r6 graphics",
475 "radeon(tm) r7 graphics",
476 "radeon r7 graphics",
477 "nforce", "tegra", "shield", "igp",
481 "mali",
482 "intel",
483 ];
484 let strings_that_imply_cpu = ["mesa offscreen", "swiftshader"];
485 let inferred_device_type = if vendor_lower.contains("qualcomm")
487 || vendor_lower.contains("intel")
488 || strings_that_imply_integrated
489 .iter()
490 .any(|&s| renderer_lower.contains(s))
491 {
492 hal::adapter::DeviceType::IntegratedGpu
493 } else if strings_that_imply_cpu
494 .iter()
495 .any(|&s| renderer_lower.contains(s))
496 {
497 hal::adapter::DeviceType::Cpu
498 } else {
499 hal::adapter::DeviceType::DiscreteGpu
500 };
501
502 let vendor_id = if vendor_lower.contains("amd") {
504 0x1002
505 } else if vendor_lower.contains("imgtec") {
506 0x1010
507 } else if vendor_lower.contains("nvidia") {
508 0x10DE
509 } else if vendor_lower.contains("arm") {
510 0x13B5
511 } else if vendor_lower.contains("qualcomm") {
512 0x5143
513 } else if vendor_lower.contains("intel") {
514 0x8086
515 } else {
516 0
517 };
518
519 adapter::Adapter {
520 info: adapter::AdapterInfo {
521 name,
522 vendor: vendor_id,
523 device: 0,
524 device_type: inferred_device_type,
525 },
526 physical_device: PhysicalDevice(Starc::new(share)),
527 queue_families: vec![QueueFamily],
528 }
529 }
530
531 pub fn legacy_features(&self) -> &info::LegacyFeatures {
533 &self.0.legacy_features
534 }
535}
536
537impl adapter::PhysicalDevice<Backend> for PhysicalDevice {
538 unsafe fn open(
539 &self,
540 families: &[(&QueueFamily, &[q::QueuePriority])],
541 requested_features: hal::Features,
542 ) -> Result<adapter::Gpu<Backend>, hal::device::CreationError> {
543 if self.0.open.get() {
546 return Err(hal::device::CreationError::TooManyObjects);
547 }
548 self.0.open.set(true);
549
550 if !self.features().contains(requested_features) {
552 return Err(hal::device::CreationError::MissingFeature);
553 }
554
555 let gl = &self.0.context;
557
558 if cfg!(debug_assertions) && !cfg!(target_arch = "wasm32") && gl.supports_debug() {
559 log::info!("Debug output is enabled");
560 gl.enable(glow::DEBUG_OUTPUT);
561 gl.debug_message_callback(debug_message_callback);
562 }
563
564 if self
565 .0
566 .legacy_features
567 .contains(info::LegacyFeatures::SRGB_COLOR)
568 && !self.0.info.version.is_embedded
569 {
570 gl.enable(glow::FRAMEBUFFER_SRGB);
573 }
574
575 gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
576
577 let mut vao = None;
579 if self.0.private_caps.vertex_array {
580 vao = Some(gl.create_vertex_array().unwrap());
581 gl.bind_vertex_array(vao);
582 }
583
584 if let Err(err) = self.0.check() {
585 panic!("Error opening adapter: {:?}", err);
586 }
587
588 Ok(adapter::Gpu {
589 device: Device::new(self.0.clone(), requested_features),
590 queue_groups: families
591 .iter()
592 .map(|&(_family, priorities)| {
593 assert_eq!(priorities.len(), 1);
594 let mut family = q::QueueGroup::new(q::QueueFamilyId(0));
595 let queue = queue::Queue::new(&self.0, requested_features, vao);
596 family.add_queue(queue);
597 family
598 })
599 .collect(),
600 })
601 }
602
603 fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
604 use hal::format::{BufferFeature as Bf, ImageFeature as If};
605
606 hal::format::Properties {
608 linear_tiling: If::TRANSFER_SRC | If::TRANSFER_DST | If::empty(),
609 optimal_tiling: If::TRANSFER_SRC | If::TRANSFER_DST | If::SAMPLED,
610 buffer_features: Bf::VERTEX,
611 drm_format_properties: Vec::new(),
612 }
613 }
614
615 fn image_format_properties(
616 &self,
617 format: hal::format::Format,
618 _dimensions: u8,
619 _tiling: image::Tiling,
620 _usage: image::Usage,
621 _view_caps: image::ViewCapabilities,
622 ) -> Option<image::FormatProperties> {
623 let conv::FormatDescription {
624 tex_external,
625 tex_internal,
626 data_type,
627 ..
628 } = conv::describe_format(format)?;
629
630 if !self
631 .0
632 .texture_format_filter
633 .check(tex_internal, tex_external, data_type)
634 {
635 return None;
637 }
638
639 Some(image::FormatProperties {
640 max_extent: image::Extent {
641 width: !0,
642 height: !0,
643 depth: !0,
644 },
645 max_levels: !0,
646 max_layers: !0,
647 sample_count_mask: 127,
648 max_resource_size: !0,
649 })
650 }
651
652 fn memory_properties(&self) -> adapter::MemoryProperties {
653 adapter::MemoryProperties {
654 memory_types: self
655 .0
656 .memory_types
657 .iter()
658 .map(|(mem_type, _)| *mem_type)
659 .collect(),
660 memory_heaps: vec![
661 adapter::MemoryHeap {
662 size: !0,
663 flags: memory::HeapFlags::DEVICE_LOCAL,
664 },
665 adapter::MemoryHeap {
666 size: !0,
667 flags: memory::HeapFlags::empty(),
668 },
669 ],
670 }
671 }
672
673 fn external_buffer_properties(
674 &self,
675 _usage: hal::buffer::Usage,
676 _sparse: hal::memory::SparseFlags,
677 _memory_type: hal::external_memory::ExternalMemoryType,
678 ) -> hal::external_memory::ExternalMemoryProperties {
679 unimplemented!()
680 }
681
682 fn external_image_properties(
683 &self,
684 _format: hal::format::Format,
685 _dimensions: u8,
686 _tiling: image::Tiling,
687 _usage: image::Usage,
688 _view_caps: image::ViewCapabilities,
689 _memory_type: hal::external_memory::ExternalMemoryType,
690 ) -> Result<
691 hal::external_memory::ExternalMemoryProperties,
692 hal::external_memory::ExternalImagePropertiesError,
693 > {
694 unimplemented!()
695 }
696
697 fn features(&self) -> hal::Features {
698 self.0.supported_features
699 }
700
701 fn properties(&self) -> hal::PhysicalDeviceProperties {
702 self.0.public_caps
703 }
704
705 unsafe fn enumerate_displays(&self) -> Vec<display::Display<crate::Backend>> {
706 unimplemented!();
707 }
708
709 unsafe fn enumerate_compatible_planes(
710 &self,
711 _display: &display::Display<crate::Backend>,
712 ) -> Vec<display::Plane> {
713 unimplemented!();
714 }
715
716 unsafe fn create_display_mode(
717 &self,
718 _display: &display::Display<crate::Backend>,
719 _resolution: (u32, u32),
720 _refresh_rate: u32,
721 ) -> Result<display::DisplayMode<crate::Backend>, display::DisplayModeError> {
722 unimplemented!();
723 }
724
725 unsafe fn create_display_plane<'a>(
726 &self,
727 _display: &'a display::DisplayMode<crate::Backend>,
728 _plane: &'a display::Plane,
729 ) -> Result<display::DisplayPlane<'a, crate::Backend>, hal::device::OutOfMemory> {
730 unimplemented!();
731 }
732}
733
734#[derive(Debug, Clone, Copy)]
735pub struct QueueFamily;
736
737impl q::QueueFamily for QueueFamily {
738 fn queue_type(&self) -> q::QueueType {
739 q::QueueType::General
740 }
741 fn max_queues(&self) -> usize {
742 1
743 }
744 fn id(&self) -> q::QueueFamilyId {
745 q::QueueFamilyId(0)
746 }
747 fn supports_sparse_binding(&self) -> bool {
748 false
749 }
750}
751
752fn resolve_sub_range(
753 sub: &buffer::SubRange,
754 whole: Range<buffer::Offset>,
755) -> Range<buffer::Offset> {
756 let end = sub.size.map_or(whole.end, |s| whole.start + sub.offset + s);
757 whole.start + sub.offset..end
758}
759
760const fn is_webgl() -> bool {
761 cfg!(target_arch = "wasm32")
762}