1use std::collections::BTreeMap;
2use {allowable_result, allowable_result_fallback};
3
4use nvapi::{self,
5 ClockTable, VfpCurve, VfpEntry, Sensor, Cooler, ThermalInfo, PowerInfoEntry,
6 ClockFrequencyType, ClockEntry,
7 BaseVoltage, PStates, ClockRange, ThermalLimit,
8};
9pub use nvapi::{
10 PhysicalGpu,
11 Vendor, SystemType, RamType, RamMaker, Foundry,
12 ClockFrequencies, ClockDomain, VoltageDomain, UtilizationDomain, Utilizations, ClockLockMode, ClockLockEntry,
13 CoolerType, CoolerController, CoolerControl, CoolerPolicy, CoolerTarget, CoolerLevel,
14 VoltageStatus, VoltageTable,
15 PerfInfo, PerfStatus,
16 ThermalController, ThermalTarget,
17 MemoryInfo, PciIdentifiers, DriverModel,
18 Percentage, Celsius,
19 Range,
20 Kibibytes, Microvolts, MicrovoltsDelta, Kilohertz, KilohertzDelta,
21 PState,
22};
23
24pub struct Gpu {
25 gpu: PhysicalGpu,
26}
27
28#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
29#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
30pub struct GpuInfo {
31 pub name: String,
32 pub codename: String,
33 pub bios_version: String,
34 pub driver_model: DriverModel,
35 pub vendor: Vendor,
36 pub pci: PciIdentifiers,
37 pub memory: MemoryInfo,
38 pub system_type: SystemType,
39 pub ram_type: RamType,
40 pub ram_maker: RamMaker,
41 pub ram_bus_width: u32,
42 pub ram_bank_count: u32,
43 pub ram_partition_count: u32,
44 pub foundry: Foundry,
45 pub core_count: u32,
46 pub shader_pipe_count: u32,
47 pub shader_sub_pipe_count: u32,
48 pub base_clocks: ClockFrequencies,
49 pub boost_clocks: ClockFrequencies,
50 pub sensors: Vec<SensorDesc>,
51 pub coolers: Vec<CoolerDesc>,
52 pub perf: PerfInfo,
53 pub sensor_limits: Vec<SensorLimit>,
54 pub power_limits: Vec<PowerLimit>,
55 pub pstate_limits: BTreeMap<PState, BTreeMap<ClockDomain, PStateLimit>>,
56 pub overvolt_limits: Vec<OvervoltLimit>,
58 pub vfp_limits: BTreeMap<ClockDomain, VfpRange>,
59 pub vfp_locks: Vec<usize>,
60}
61
62#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
63#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
64pub struct VfpRange {
65 pub range: Range<KilohertzDelta>,
66 pub temperature: Celsius,
67}
68
69impl From<ClockRange> for VfpRange {
70 fn from(c: ClockRange) -> Self {
71 VfpRange {
72 range: Range::range_from(c.range),
73 temperature: c.temp_max.into(),
74 }
75 }
76}
77
78#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
79#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
80pub struct GpuStatus {
81 pub pstate: PState,
82 pub clocks: ClockFrequencies,
83 pub memory: MemoryInfo,
84 pub voltage: Option<Microvolts>,
85 pub voltage_domains: Option<VoltageStatus>,
86 pub voltage_step: Option<VoltageStatus>,
87 pub voltage_table: Option<VoltageTable>,
88 pub tachometer: Option<u32>,
89 pub utilization: Utilizations,
90 pub power: Vec<Percentage>,
91 pub sensors: Vec<(SensorDesc, Celsius)>,
92 pub coolers: Vec<(CoolerDesc, CoolerStatus)>,
93 pub perf: PerfStatus,
94 pub vfp: Option<VfpTable>,
95 pub vfp_locks: BTreeMap<usize, Microvolts>,
96}
97
98#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
99#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
100pub struct GpuSettings {
101 pub voltage_boost: Option<Percentage>,
102 pub sensor_limits: Vec<Celsius>,
103 pub power_limits: Vec<Percentage>,
104 pub coolers: Vec<(CoolerDesc, CoolerStatus)>,
105 pub vfp: Option<VfpDeltas>,
106 pub pstate_deltas: BTreeMap<PState, BTreeMap<ClockDomain, KilohertzDelta>>,
107 pub overvolt: Vec<MicrovoltsDelta>,
108 pub vfp_locks: BTreeMap<usize, ClockLockEntry>,
109}
110
111impl Gpu {
112 pub fn new(gpu: PhysicalGpu) -> Self {
113 Gpu {
114 gpu: gpu,
115 }
116 }
117
118 pub fn into_inner(self) -> PhysicalGpu {
119 self.gpu
120 }
121
122 pub fn inner(&self) -> &PhysicalGpu {
123 &self.gpu
124 }
125
126 pub fn enumerate() -> nvapi::Result<Vec<Self>> {
127 PhysicalGpu::enumerate().map(|v| v.into_iter().map(Gpu::new).collect())
128 }
129
130 pub fn info(&self) -> nvapi::Result<GpuInfo> {
131 let pstates = allowable_result(self.gpu.pstates())?;
132 let (pstates, ov) = match pstates {
133 Ok(PStates { editable: _editable, pstates, overvolt }) => (pstates, overvolt),
134 Err(..) => (Default::default(), Default::default()),
135 };
136 let pci = self.gpu.pci_identifiers()?;
137
138 Ok(GpuInfo {
139 name: self.gpu.full_name()?,
140 codename: self.gpu.short_name()?,
141 bios_version: self.gpu.vbios_version_string()?,
142 driver_model: self.gpu.driver_model()?,
143 vendor: allowable_result_fallback(pci.vendor().map_err(From::from), Vendor::Unknown)?,
144 pci: pci,
145 memory: self.gpu.memory_info()?,
146 system_type: allowable_result_fallback(self.gpu.system_type(), SystemType::Unknown)?,
147 ram_type: allowable_result_fallback(self.gpu.ram_type(), RamType::Unknown)?,
148 ram_maker: allowable_result_fallback(self.gpu.ram_maker(), RamMaker::Unknown)?,
149 ram_bus_width: allowable_result_fallback(self.gpu.ram_bus_width(), 0)?,
150 ram_bank_count: allowable_result_fallback(self.gpu.ram_bank_count(), 0)?,
151 ram_partition_count: allowable_result_fallback(self.gpu.ram_partition_count(), 0)?,
152 foundry: allowable_result_fallback(self.gpu.foundry(), Foundry::Unknown)?,
153 core_count: self.gpu.core_count()?,
154 shader_pipe_count: self.gpu.shader_pipe_count()?,
155 shader_sub_pipe_count: self.gpu.shader_sub_pipe_count()?,
156 base_clocks: self.gpu.clock_frequencies(ClockFrequencyType::Base)?,
157 boost_clocks: self.gpu.clock_frequencies(ClockFrequencyType::Boost)?,
158 sensors: match allowable_result(self.gpu.thermal_settings(None))? {
159 Ok(s) => s.into_iter().map(From::from).collect(),
160 Err(..) => Default::default(),
161 },
162 coolers: match allowable_result(self.gpu.cooler_settings(None))? {
163 Ok(c) => c.into_iter().map(From::from).collect(),
164 Err(..) => Default::default(),
165 },
166 perf: self.gpu.perf_info()?,
167 sensor_limits: match allowable_result(self.gpu.thermal_limit_info())? {
168 Ok((_, l)) => l.into_iter().map(From::from).collect(),
169 Err(..) => Default::default(),
170 },
171 power_limits: match allowable_result(self.gpu.power_limit_info())? {
172 Ok(p) => p.entries.into_iter().map(From::from).collect(),
173 Err(..) => Default::default(),
174 },
175 pstate_limits: pstates.into_iter().map(|p| (p.id, p.clocks.into_iter().map(|p| (p.domain(), p.into())).collect())).collect(),
176 overvolt_limits: ov.into_iter().map(From::from).collect(),
177 vfp_limits: match allowable_result(self.gpu.vfp_ranges())? {
178 Ok(l) => l.into_iter().map(|v| (v.domain, v.into())).collect(),
179 Err(..) => Default::default(),
180 },
181 vfp_locks: match allowable_result(self.gpu.vfp_locks())? {
182 Ok(v) => v.into_iter().map(|(id, _)| id).collect(),
183 Err(..) => Default::default(),
184 },
185 })
186 }
187
188 pub fn status(&self) -> nvapi::Result<GpuStatus> {
189 let mask = allowable_result(self.gpu.vfp_mask())?;
190
191 Ok(GpuStatus {
192 pstate: self.gpu.current_pstate()?,
193 clocks: self.gpu.clock_frequencies(ClockFrequencyType::Current)?,
194 memory: self.gpu.memory_info()?,
195 voltage: allowable_result(self.gpu.core_voltage())?.ok(),
196 voltage_domains: allowable_result(self.gpu.voltage_domains_status())?.ok(),
197 voltage_step: allowable_result(self.gpu.voltage_step())?.ok(),
198 voltage_table: allowable_result(self.gpu.voltage_table())?.ok(),
199 tachometer: allowable_result(self.gpu.tachometer())?.ok(),
200 utilization: self.gpu.dynamic_pstates_info()?,
201 power: self.gpu.power_usage()?.into_iter().map(From::from).collect(),
202 sensors: match allowable_result(self.gpu.thermal_settings(None))? {
203 Ok(s) => s.into_iter().map(|s| (From::from(s), s.current_temperature)).collect(),
204 Err(..) => Default::default(),
205 },
206 coolers: match allowable_result(self.gpu.cooler_settings(None))? {
207 Ok(c) => c.into_iter().map(|c| (From::from(c), From::from(c))).collect(),
208 Err(..) => Default::default(),
209 },
210 perf: self.gpu.perf_status()?,
211 vfp: match mask {
212 Ok(mask) => allowable_result(self.gpu.vfp_curve(mask.mask))?.map(From::from).ok(),
213 Err(..) => None,
214 },
215 vfp_locks: match allowable_result(self.gpu.vfp_locks())? {
216 Ok(l) => l.into_iter().filter_map(|(id, e)| if e.mode == ClockLockMode::Manual {
217 Some((id, e.voltage))
218 } else {
219 None
220 }).collect(),
221 Err(..) => Default::default(),
222 },
223 })
224 }
225
226 pub fn settings(&self) -> nvapi::Result<GpuSettings> {
227 let mask = allowable_result(self.gpu.vfp_mask())?;
228 let pstates = allowable_result(self.gpu.pstates())?;
229 let (pstates, ov) = match pstates {
230 Ok(PStates { editable: _editable, pstates, overvolt }) => (pstates, overvolt),
231 Err(..) => (Default::default(), Default::default()),
232 };
233
234 Ok(GpuSettings {
235 voltage_boost: allowable_result(self.gpu.core_voltage_boost())?.ok(),
236 sensor_limits: match allowable_result(self.gpu.thermal_limit())? {
237 Ok(l) => l.into_iter().map(|l| l.value.into()).collect(),
238 Err(..) => Default::default(),
239 },
240 power_limits: match allowable_result(self.gpu.power_limit())? {
241 Ok(l) => l.into_iter().map(|l| l.into()).collect(),
242 Err(..) => Default::default(),
243 },
244 coolers: match allowable_result(self.gpu.cooler_settings(None))? {
245 Ok(c) => c.into_iter().map(|c| (From::from(c), From::from(c))).collect(),
246 Err(..) => Default::default(),
247 },
248 vfp: match mask {
249 Ok(mask) => allowable_result(self.gpu.vfp_table(mask.mask))?.map(From::from).ok(),
250 Err(..) => None,
251 },
252 vfp_locks: match allowable_result(self.gpu.vfp_locks())? {
253 Ok(l) => l,
254 Err(..) => Default::default(),
255 },
256 pstate_deltas: pstates.into_iter().filter(|p| p.editable)
257 .map(|p| (p.id, p.clocks.into_iter().filter(|p| p.editable())
258 .map(|p| (p.domain(), p.frequency_delta().value)).collect())
259 ).collect(),
260 overvolt: ov.into_iter().filter(|v| v.editable).map(|v| v.voltage_delta.value).collect(),
261 })
262 }
263
264 pub fn set_voltage_boost(&self, boost: Percentage) -> nvapi::Result<()> {
265 self.gpu.set_core_voltage_boost(boost)
266 }
267
268 pub fn set_power_limits<I: Iterator<Item=Percentage>>(&self, limits: I) -> nvapi::Result<()> {
269 self.gpu.set_power_limit(limits.map(From::from))
271 }
272
273 pub fn set_sensor_limits<I: Iterator<Item=Celsius>>(&self, limits: I) -> nvapi::Result<()> {
274 self.gpu.thermal_limit_info().and_then(|(_, info)| self.gpu.set_thermal_limit(
275 limits.zip(info.into_iter()).map(|(limit, info)| ThermalLimit {
276 controller: info.controller,
277 flags: info.default_flags,
278 value: limit.into(),
279 })
280 ))
281 }
282
283 pub fn set_cooler_levels<I: Iterator<Item=CoolerLevel>>(&self, levels: I) -> nvapi::Result<()> {
284 self.gpu.set_cooler_levels(None, levels)
285 }
286
287 pub fn reset_cooler_levels(&self) -> nvapi::Result<()> {
288 self.gpu.restore_cooler_settings(&[])
289 }
290
291 pub fn set_vfp<I: Iterator<Item=(usize, KilohertzDelta)>, M: Iterator<Item=(usize, KilohertzDelta)>>(&self, clock_deltas: I, mem_deltas: M) -> nvapi::Result<()> {
292 self.gpu.set_vfp_table([0, 0, 0, 0], clock_deltas.map(|(i, d)| (i, d.into())), mem_deltas.map(|(i, d)| (i, d.into())))
293 }
294
295 pub fn set_vfp_lock(&self, voltage: Microvolts) -> nvapi::Result<()> {
296 self.gpu.set_vfp_locks(self.gpu.vfp_locks()?
297 .into_iter().max_by_key(|&(id, _)| id).into_iter()
298 .map(|(id, entry)| (id, Some(voltage)))
299 )
300 }
301
302 pub fn reset_vfp_lock(&self) -> nvapi::Result<()> {
303 self.gpu.set_vfp_locks(self.gpu.vfp_locks()?.into_iter().map(|(id, _)| (id, None)))
304 }
305
306 pub fn reset_vfp(&self) -> nvapi::Result<()> {
307 use std::iter;
308
309 let mask = self.gpu.vfp_mask()?;
310 self.gpu.set_vfp_table(mask.mask, iter::empty(), iter::empty())
311 }
312}
313
314#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
315#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
316pub struct OvervoltLimit {
317 pub domain: VoltageDomain,
318 pub voltage: Microvolts,
319 pub range: Option<Range<MicrovoltsDelta>>,
320}
321
322impl From<BaseVoltage> for OvervoltLimit {
323 fn from(v: BaseVoltage) -> Self {
324 OvervoltLimit {
325 domain: v.voltage_domain,
326 voltage: v.voltage,
327 range: if v.editable {
328 Some(v.voltage_delta.range)
329 } else {
330 None
331 },
332 }
333 }
334}
335
336#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
337#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
338pub struct PStateLimit {
339 pub frequency_delta: Option<Range<KilohertzDelta>>,
340 pub frequency: Range<Kilohertz>,
341 pub voltage: Range<Microvolts>,
342 pub voltage_domain: VoltageDomain,
343}
344
345impl From<ClockEntry> for PStateLimit {
346 fn from(s: ClockEntry) -> Self {
347 match s {
348 ClockEntry::Range { domain: _, editable, frequency_delta, frequency_range, voltage_domain, voltage_range } => PStateLimit {
349 frequency_delta: if editable { Some(frequency_delta.range) } else { None },
350 frequency: frequency_range,
351 voltage: voltage_range,
352 voltage_domain: voltage_domain,
353 },
354 ClockEntry::Single { domain: _, editable, frequency_delta, frequency } => PStateLimit {
355 frequency_delta: if editable { Some(frequency_delta.range) } else { None },
356 frequency: Range::from_scalar(frequency),
357 voltage: Default::default(),
358 voltage_domain: VoltageDomain::Undefined,
359 },
360 }
361 }
362}
363
364#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
365#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
366pub struct PowerLimit {
367 pub range: Range<Percentage>,
368 pub default: Percentage,
369}
370
371impl From<PowerInfoEntry> for PowerLimit {
372 fn from(info: PowerInfoEntry) -> Self {
373 PowerLimit {
374 range: Range::range_from(info.range),
375 default: info.default_limit.into(),
376 }
377 }
378}
379
380#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
381#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
382pub struct SensorLimit {
383 pub range: Range<Celsius>,
384 pub default: Celsius,
385 pub flags: u32,
386}
387
388impl From<ThermalInfo> for SensorLimit {
389 fn from(info: ThermalInfo) -> Self {
390 SensorLimit {
391 range: Range::range_from(info.temperature_range),
392 default: info.default_temperature.into(),
393 flags: info.default_flags,
394 }
395 }
396}
397
398#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
399#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
400pub struct SensorDesc {
401 pub controller: ThermalController,
402 pub target: ThermalTarget,
403 pub range: Range<Celsius>,
404}
405
406impl From<Sensor> for SensorDesc {
407 fn from(sensor: Sensor) -> Self {
408 SensorDesc {
409 controller: sensor.controller,
410 target: sensor.target,
411 range: sensor.default_temperature_range,
412 }
413 }
414}
415
416#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
417#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
418pub struct CoolerDesc {
419 pub kind: CoolerType,
420 pub controller: CoolerController,
421 pub range: Range<Percentage>,
422 pub default_policy: CoolerPolicy,
423 pub target: CoolerTarget,
424 pub control: CoolerControl,
425}
426
427impl From<Cooler> for CoolerDesc {
428 fn from(cooler: Cooler) -> Self {
429 CoolerDesc {
430 kind: cooler.kind,
431 controller: cooler.controller,
432 range: cooler.default_level_range,
433 default_policy: cooler.default_policy,
434 target: cooler.target,
435 control: cooler.control,
436 }
437 }
438}
439
440#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
441#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
442pub struct CoolerStatus {
443 pub range: Range<Percentage>,
444 pub level: Percentage,
445 pub policy: CoolerPolicy,
446 pub active: bool,
447}
448
449impl From<Cooler> for CoolerStatus {
450 fn from(cooler: Cooler) -> Self {
451 CoolerStatus {
452 range: cooler.current_level_range,
453 level: cooler.current_level,
454 policy: cooler.current_policy,
455 active: cooler.active,
456 }
457 }
458}
459
460#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
461#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
462pub struct VfpPoint {
463 pub frequency: Kilohertz,
464 pub voltage: Microvolts,
465}
466
467impl<T> From<VfpEntry<T>> for VfpPoint where Kilohertz: From<T> {
468 fn from(v: VfpEntry<T>) -> Self {
469 VfpPoint {
470 frequency: v.frequency.into(),
471 voltage: v.voltage,
472 }
473 }
474}
475
476#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
477#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
478pub struct VfpTable {
479 pub graphics: BTreeMap<usize, VfpPoint>,
480 pub memory: BTreeMap<usize, VfpPoint>,
481}
482
483impl From<VfpCurve> for VfpTable {
484 fn from(v: VfpCurve) -> Self {
485 VfpTable {
486 graphics: v.graphics.into_iter().map(|(i, e)| (i, e.into())).collect(),
487 memory: v.memory.into_iter().map(|(i, e)| (i, e.into())).collect(),
488 }
489 }
490}
491
492#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
493#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
494pub struct VfpDeltas {
495 pub graphics: BTreeMap<usize, KilohertzDelta>,
496 pub memory: BTreeMap<usize, KilohertzDelta>,
497}
498
499impl From<ClockTable> for VfpDeltas {
500 fn from(c: ClockTable) -> Self {
501 VfpDeltas {
502 graphics: c.gpu_delta.into_iter().map(|(i, d)| (i, d.into())).collect(),
503 memory: c.mem_delta.into_iter().map(|(i, d)| (i, d.into())).collect(),
504 }
505 }
506}
507
508#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
509#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
510pub struct VfPoint {
511 pub voltage: Microvolts,
512 pub frequency: Kilohertz,
513 pub delta: KilohertzDelta,
514}
515
516impl VfPoint {
517 pub fn new(point: VfpPoint, delta: KilohertzDelta) -> Self {
518 VfPoint {
519 voltage: point.voltage,
520 frequency: point.frequency,
521 delta: delta,
522 }
523 }
524}