Skip to main content

cvkg_render_gpu/subsystems/
gpu_capabilities.rs

1//! P1-26: GPU capability detection.
2//!
3//! Detects GPU vendor and capabilities at startup. Used by the
4//! renderer to log GPU info and (in future) select shader
5//! variants appropriate for the available hardware.
6
7use std::fmt;
8
9/// Detected GPU vendor. This is a coarse classification derived
10/// from the adapter name string. It's used to enable
11/// vendor-specific workarounds in shaders or pipeline creation.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum GpuVendor {
14    Nvidia,
15    Amd,
16    Intel,
17    Apple,
18    Qualcomm,
19    Arm,
20    ImgTec,
21    Microsoft,
22    Mesa,
23    Broadcom,
24    Unknown,
25}
26
27impl fmt::Display for GpuVendor {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        let s = match self {
30            GpuVendor::Nvidia => "NVIDIA",
31            GpuVendor::Amd => "AMD",
32            GpuVendor::Intel => "Intel",
33            GpuVendor::Apple => "Apple",
34            GpuVendor::Qualcomm => "Qualcomm",
35            GpuVendor::Arm => "ARM",
36            GpuVendor::ImgTec => "Imagination",
37            GpuVendor::Microsoft => "Microsoft",
38            GpuVendor::Mesa => "Mesa",
39            GpuVendor::Broadcom => "Broadcom",
40            GpuVendor::Unknown => "Unknown",
41        };
42        f.write_str(s)
43    }
44}
45
46/// Detect GPU vendor from an adapter name string. The name
47/// comes from `wgpu::Adapter::get_info().name`.
48///
49/// This is a best-effort heuristic. Vendors may be misidentified
50/// when the adapter name is unusual or contains a substring
51/// from another vendor (e.g., an AMD card with "NVIDIA" in
52/// the user-customized name).
53pub fn detect_gpu_vendor(adapter_name: &str) -> GpuVendor {
54    let name = adapter_name.to_lowercase();
55    if name.contains("nvidia")
56        || name.contains("geforce")
57        || name.contains("quadro")
58        || name.contains("tesla")
59    {
60        GpuVendor::Nvidia
61    } else if name.contains("amd")
62        || name.contains("radeon")
63        || name.contains("rx ")
64        || name.contains("firepro")
65    {
66        GpuVendor::Amd
67    } else if name.contains("intel")
68        || name.contains("uhd")
69        || name.contains("iris")
70        || name.contains("hd graphics")
71    {
72        GpuVendor::Intel
73    } else if name.contains("apple")
74        || name.contains("m1")
75        || name.contains("m2")
76        || name.contains("m3")
77    {
78        GpuVendor::Apple
79    } else if name.contains("qualcomm") || name.contains("adreno") {
80        GpuVendor::Qualcomm
81    } else if name.contains("arm") || name.contains("mali") {
82        GpuVendor::Arm
83    } else if name.contains("imgtec") || name.contains("powervr") {
84        GpuVendor::ImgTec
85    } else if name.contains("microsoft") || name.contains("direct3d12") {
86        GpuVendor::Microsoft
87    } else if name.contains("mesa") || name.contains("llvmpipe") || name.contains("swiftshader") {
88        GpuVendor::Mesa
89    } else if name.contains("broadcom") || name.contains("videocore") {
90        GpuVendor::Broadcom
91    } else {
92        GpuVendor::Unknown
93    }
94}
95
96/// GPU capability summary. Currently just includes the vendor;
97/// future versions will add feature flags (e.g., bindless
98/// textures, advanced compute features).
99#[derive(Debug, Clone)]
100pub struct GpuCapabilities {
101    /// Detected vendor.
102    pub vendor: GpuVendor,
103    /// The original adapter name string.
104    pub adapter_name: String,
105    /// The wgpu backend in use (Vulkan, Metal, DX12, etc.).
106    pub backend: String,
107}
108
109impl GpuCapabilities {
110    /// Detect capabilities from an adapter name and backend.
111    pub fn detect(adapter_name: &str, backend: impl Into<String>) -> Self {
112        Self {
113            vendor: detect_gpu_vendor(adapter_name),
114            adapter_name: adapter_name.to_string(),
115            backend: backend.into(),
116        }
117    }
118}
119
120impl fmt::Display for GpuCapabilities {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        write!(
123            f,
124            "{} GPU '{}' on {} backend",
125            self.vendor, self.adapter_name, self.backend
126        )
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn detects_nvidia() {
136        assert_eq!(
137            detect_gpu_vendor("NVIDIA GeForce RTX 3080"),
138            GpuVendor::Nvidia
139        );
140        assert_eq!(detect_gpu_vendor("Quadro P4000"), GpuVendor::Nvidia);
141    }
142
143    #[test]
144    fn detects_amd() {
145        assert_eq!(detect_gpu_vendor("AMD Radeon RX 6800 XT"), GpuVendor::Amd);
146        assert_eq!(detect_gpu_vendor("Radeon Pro 5500M"), GpuVendor::Amd);
147    }
148
149    #[test]
150    fn detects_intel() {
151        assert_eq!(
152            detect_gpu_vendor("Intel UHD Graphics 630"),
153            GpuVendor::Intel
154        );
155        assert_eq!(detect_gpu_vendor("Intel Iris Xe"), GpuVendor::Intel);
156    }
157
158    #[test]
159    fn detects_apple() {
160        assert_eq!(detect_gpu_vendor("Apple M1 Pro"), GpuVendor::Apple);
161        assert_eq!(detect_gpu_vendor("Apple M2 GPU"), GpuVendor::Apple);
162    }
163
164    #[test]
165    fn detects_qualcomm() {
166        assert_eq!(
167            detect_gpu_vendor("Qualcomm Adreno 660"),
168            GpuVendor::Qualcomm
169        );
170    }
171
172    #[test]
173    fn detects_arm_mali() {
174        assert_eq!(detect_gpu_vendor("ARM Mali-G78"), GpuVendor::Arm);
175    }
176
177    #[test]
178    fn detects_mesa_software() {
179        assert_eq!(detect_gpu_vendor("Mesa llvmpipe"), GpuVendor::Mesa);
180        assert_eq!(detect_gpu_vendor("Google SwiftShader"), GpuVendor::Mesa);
181    }
182
183    #[test]
184    fn unknown_for_garbage() {
185        assert_eq!(detect_gpu_vendor("Custom XYZ GPU 9000"), GpuVendor::Unknown);
186    }
187
188    #[test]
189    fn case_insensitive() {
190        assert_eq!(
191            detect_gpu_vendor("nvidia geforce gtx 1080"),
192            GpuVendor::Nvidia
193        );
194        assert_eq!(detect_gpu_vendor("AMD RADEON VII"), GpuVendor::Amd);
195    }
196
197    #[test]
198    fn capabilities_display() {
199        let caps = GpuCapabilities::detect("NVIDIA GeForce RTX 3080", "Vulkan");
200        let s = format!("{}", caps);
201        assert!(s.contains("NVIDIA"));
202        assert!(s.contains("GeForce"));
203        assert!(s.contains("Vulkan"));
204    }
205}