use crate::bitmasks::device::FbcFlags;
use crate::enum_wrappers::device::{BridgeChip, EncoderType, FbcSessionType, SampleValueType};
use crate::enums::device::{FirmwareVersion, SampleValue, UsedGpuMemory};
use crate::error::{nvml_try, Bits, NvmlError};
use crate::ffi::bindings::*;
use crate::structs::device::FieldId;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
ffi::{CStr, CString},
};
use std::{
convert::{TryFrom, TryInto},
os::raw::c_char,
};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PciInfo {
pub bus: u32,
pub bus_id: String,
pub device: u32,
pub domain: u32,
pub pci_device_id: u32,
pub pci_sub_system_id: Option<u32>,
}
impl PciInfo {
pub fn try_from(struct_: nvmlPciInfo_t, sub_sys_id_present: bool) -> Result<Self, NvmlError> {
unsafe {
let bus_id_raw = CStr::from_ptr(struct_.busId.as_ptr());
Ok(Self {
bus: struct_.bus,
bus_id: bus_id_raw.to_str()?.into(),
device: struct_.device,
domain: struct_.domain,
pci_device_id: struct_.pciDeviceId,
pci_sub_system_id: if sub_sys_id_present {
Some(struct_.pciSubSystemId)
} else {
None
},
})
}
}
}
impl TryInto<nvmlPciInfo_t> for PciInfo {
type Error = NvmlError;
fn try_into(self) -> Result<nvmlPciInfo_t, Self::Error> {
const fn buf_size() -> usize {
NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE as usize
}
let mut bus_id_c: [c_char; buf_size()] = [0; buf_size()];
let mut bus_id = CString::new(self.bus_id)?.into_bytes_with_nul();
match bus_id.len().cmp(&buf_size()) {
Ordering::Less => {
while bus_id.len() != buf_size() {
bus_id.push(0);
}
}
Ordering::Equal => {
}
Ordering::Greater => {
return Err(NvmlError::StringTooLong {
max_len: buf_size(),
actual_len: bus_id.len(),
})
}
}
bus_id_c.clone_from_slice(&bus_id.into_iter().map(|b| b as c_char).collect::<Vec<_>>());
Ok(nvmlPciInfo_t {
busIdLegacy: [0; NVML_DEVICE_PCI_BUS_ID_BUFFER_V2_SIZE as usize],
domain: self.domain,
bus: self.bus,
device: self.device,
pciDeviceId: self.pci_device_id,
pciSubSystemId: self.pci_sub_system_id.unwrap_or(0),
busId: bus_id_c,
})
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BAR1MemoryInfo {
pub free: u64,
pub total: u64,
pub used: u64,
}
impl From<nvmlBAR1Memory_t> for BAR1MemoryInfo {
fn from(struct_: nvmlBAR1Memory_t) -> Self {
Self {
free: struct_.bar1Free,
total: struct_.bar1Total,
used: struct_.bar1Used,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BridgeChipInfo {
pub fw_version: FirmwareVersion,
pub chip_type: BridgeChip,
}
impl TryFrom<nvmlBridgeChipInfo_t> for BridgeChipInfo {
type Error = NvmlError;
fn try_from(value: nvmlBridgeChipInfo_t) -> Result<Self, Self::Error> {
let fw_version = FirmwareVersion::from(value.fwVersion);
let chip_type = BridgeChip::try_from(value.type_)?;
Ok(Self {
fw_version,
chip_type,
})
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BridgeChipHierarchy {
pub chips_hierarchy: Vec<BridgeChipInfo>,
pub chip_count: u8,
}
impl TryFrom<nvmlBridgeChipHierarchy_t> for BridgeChipHierarchy {
type Error = NvmlError;
fn try_from(value: nvmlBridgeChipHierarchy_t) -> Result<Self, Self::Error> {
let chips_hierarchy = value
.bridgeChipInfo
.iter()
.map(|bci| BridgeChipInfo::try_from(*bci))
.collect::<Result<_, NvmlError>>()?;
Ok(Self {
chips_hierarchy,
chip_count: value.bridgeCount,
})
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ProcessInfo {
pub pid: u32,
pub used_gpu_memory: UsedGpuMemory,
pub gpu_instance_id: Option<u32>,
pub compute_instance_id: Option<u32>,
}
impl From<nvmlProcessInfo_t> for ProcessInfo {
fn from(struct_: nvmlProcessInfo_t) -> Self {
const NO_VALUE: u32 = 0xFFFFFFFF;
let gpu_instance_id = Some(struct_.gpuInstanceId).filter(|id| *id != NO_VALUE);
let compute_instance_id = Some(struct_.computeInstanceId).filter(|id| *id != NO_VALUE);
Self {
pid: struct_.pid,
used_gpu_memory: UsedGpuMemory::from(struct_.usedGpuMemory),
gpu_instance_id,
compute_instance_id,
}
}
}
#[cfg(feature = "legacy-functions")]
impl From<nvmlProcessInfo_v2_t> for ProcessInfo {
fn from(struct_: nvmlProcessInfo_v2_t) -> Self {
let unversioned_struct = nvmlProcessInfo_t {
pid: struct_.pid,
usedGpuMemory: struct_.usedGpuMemory,
gpuInstanceId: struct_.gpuInstanceId,
computeInstanceId: struct_.computeInstanceId,
};
Self::from(unversioned_struct)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EccErrorCounts {
pub device_memory: u64,
pub l1_cache: u64,
pub l2_cache: u64,
pub register_file: u64,
}
impl From<nvmlEccErrorCounts_t> for EccErrorCounts {
fn from(struct_: nvmlEccErrorCounts_t) -> Self {
Self {
device_memory: struct_.deviceMemory,
l1_cache: struct_.l1Cache,
l2_cache: struct_.l2Cache,
register_file: struct_.registerFile,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MemoryInfo {
pub free: u64,
pub total: u64,
pub used: u64,
}
impl From<nvmlMemory_t> for MemoryInfo {
fn from(struct_: nvmlMemory_t) -> Self {
Self {
free: struct_.free,
total: struct_.total,
used: struct_.used,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Utilization {
pub gpu: u32,
pub memory: u32,
}
impl From<nvmlUtilization_t> for Utilization {
fn from(struct_: nvmlUtilization_t) -> Self {
Self {
gpu: struct_.gpu,
memory: struct_.memory,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ViolationTime {
pub reference_time: u64,
pub violation_time: u64,
}
impl From<nvmlViolationTime_t> for ViolationTime {
fn from(struct_: nvmlViolationTime_t) -> Self {
Self {
reference_time: struct_.referenceTime,
violation_time: struct_.violationTime,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AccountingStats {
pub gpu_utilization: Option<u32>,
pub is_running: bool,
pub max_memory_usage: Option<u64>,
pub memory_utilization: Option<u32>,
pub start_time: u64,
pub time: u64,
}
impl From<nvmlAccountingStats_t> for AccountingStats {
fn from(struct_: nvmlAccountingStats_t) -> Self {
let not_avail_u64 = (NVML_VALUE_NOT_AVAILABLE) as u64;
let not_avail_u32 = (NVML_VALUE_NOT_AVAILABLE) as u32;
#[allow(clippy::match_like_matches_macro)]
Self {
gpu_utilization: match struct_.gpuUtilization {
v if v == not_avail_u32 => None,
_ => Some(struct_.gpuUtilization),
},
is_running: match struct_.isRunning {
0 => false,
_ => true,
},
max_memory_usage: match struct_.maxMemoryUsage {
v if v == not_avail_u64 => None,
_ => Some(struct_.maxMemoryUsage),
},
memory_utilization: match struct_.memoryUtilization {
v if v == not_avail_u32 => None,
_ => Some(struct_.memoryUtilization),
},
start_time: struct_.startTime,
time: struct_.time,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EncoderSessionInfo {
pub session_id: u32,
pub pid: u32,
pub vgpu_instance: Option<u32>,
pub codec_type: EncoderType,
pub hres: u32,
pub vres: u32,
pub average_fps: u32,
pub average_latency: u32,
}
impl TryFrom<nvmlEncoderSessionInfo_t> for EncoderSessionInfo {
type Error = NvmlError;
fn try_from(value: nvmlEncoderSessionInfo_t) -> Result<Self, Self::Error> {
Ok(Self {
session_id: value.sessionId,
pid: value.pid,
vgpu_instance: match value.vgpuInstance {
0 => None,
other => Some(other),
},
codec_type: EncoderType::try_from(value.codecType)?,
hres: value.hResolution,
vres: value.vResolution,
average_fps: value.averageFps,
average_latency: value.averageLatency,
})
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Sample {
pub timestamp: u64,
pub value: SampleValue,
}
impl Sample {
pub fn from_tag_and_struct(tag: &SampleValueType, struct_: nvmlSample_t) -> Self {
Self {
timestamp: struct_.timeStamp,
value: SampleValue::from_tag_and_union(tag, struct_.sampleValue),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ProcessUtilizationSample {
pub pid: u32,
pub timestamp: u64,
pub sm_util: u32,
pub mem_util: u32,
pub enc_util: u32,
pub dec_util: u32,
}
impl From<nvmlProcessUtilizationSample_t> for ProcessUtilizationSample {
fn from(struct_: nvmlProcessUtilizationSample_t) -> Self {
Self {
pid: struct_.pid,
timestamp: struct_.timeStamp,
sm_util: struct_.smUtil,
mem_util: struct_.memUtil,
enc_util: struct_.encUtil,
dec_util: struct_.decUtil,
}
}
}
#[derive(Debug)]
pub struct FieldValueSample {
pub field: FieldId,
pub timestamp: i64,
pub latency: i64,
pub value: Result<SampleValue, NvmlError>,
}
impl TryFrom<nvmlFieldValue_t> for FieldValueSample {
type Error = NvmlError;
fn try_from(value: nvmlFieldValue_t) -> Result<Self, Self::Error> {
Ok(Self {
field: FieldId(value.fieldId),
timestamp: value.timestamp,
latency: value.latencyUsec,
value: match nvml_try(value.nvmlReturn) {
Ok(_) => Ok(SampleValue::from_tag_and_union(
&SampleValueType::try_from(value.valueType)?,
value.value,
)),
Err(e) => Err(e),
},
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FbcStats {
pub sessions_count: u32,
pub average_fps: u32,
pub average_latency: u32,
}
impl From<nvmlFBCStats_t> for FbcStats {
fn from(struct_: nvmlFBCStats_t) -> Self {
Self {
sessions_count: struct_.sessionsCount,
average_fps: struct_.averageFPS,
average_latency: struct_.averageLatency,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FbcSessionInfo {
pub session_id: u32,
pub pid: u32,
pub vgpu_instance: Option<u32>,
pub display_ordinal: u32,
pub session_type: FbcSessionType,
pub session_flags: FbcFlags,
pub hres_max: u32,
pub vres_max: u32,
pub hres: u32,
pub vres: u32,
pub average_fps: u32,
pub average_latency: u32,
}
impl TryFrom<nvmlFBCSessionInfo_t> for FbcSessionInfo {
type Error = NvmlError;
fn try_from(value: nvmlFBCSessionInfo_t) -> Result<Self, Self::Error> {
Ok(Self {
session_id: value.sessionId,
pid: value.pid,
vgpu_instance: match value.vgpuInstance {
0 => None,
other => Some(other),
},
display_ordinal: value.displayOrdinal,
session_type: FbcSessionType::try_from(value.sessionType)?,
session_flags: FbcFlags::from_bits(value.sessionFlags)
.ok_or(NvmlError::IncorrectBits(Bits::U32(value.sessionFlags)))?,
hres_max: value.hMaxResolution,
vres_max: value.vMaxResolution,
hres: value.hResolution,
vres: value.vResolution,
average_fps: value.averageFPS,
average_latency: value.averageLatency,
})
}
}
#[cfg(test)]
#[allow(unused_variables, unused_imports)]
mod tests {
use crate::error::*;
use crate::ffi::bindings::*;
use crate::test_utils::*;
use std::convert::TryInto;
use std::mem;
#[test]
fn pci_info_from_to_c() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let converted: nvmlPciInfo_t = device
.pci_info()
.expect("wrapped pci info")
.try_into()
.expect("converted c pci info");
let sym = nvml_sym(nvml.lib.nvmlDeviceGetPciInfo_v3.as_ref())?;
let raw = unsafe {
let mut pci_info: nvmlPciInfo_t = mem::zeroed();
nvml_try(sym(device.handle(), &mut pci_info)).expect("raw pci info");
pci_info
};
assert_eq!(converted.busId, raw.busId);
assert_eq!(converted.domain, raw.domain);
assert_eq!(converted.bus, raw.bus);
assert_eq!(converted.device, raw.device);
assert_eq!(converted.pciDeviceId, raw.pciDeviceId);
assert_eq!(converted.pciSubSystemId, raw.pciSubSystemId);
Ok(())
})
}
}