fix(gpu): use epsilon comparison instead of == for floating-point guards
This commit is contained in:
+118
-77
@@ -17,26 +17,31 @@ namespace ZShell::services {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr const char* kTypeDetectScript =
|
constexpr const char* kTypeDetectScript =
|
||||||
"if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1; then echo NVIDIA;"
|
"if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null "
|
||||||
" elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC;"
|
"2>&1; then echo NVIDIA;"
|
||||||
|
" elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep "
|
||||||
|
"-q .; then echo GENERIC;"
|
||||||
" else echo NONE; fi";
|
" else echo NONE; fi";
|
||||||
|
|
||||||
constexpr const char* kNameDetectScript = "nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null"
|
constexpr const char* kNameDetectScript =
|
||||||
" || glxinfo -B 2>/dev/null | grep 'Device:' | cut -d':' -f2 | cut -d'(' -f1"
|
"nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null"
|
||||||
" || lspci 2>/dev/null | grep -i 'vga\\|3d controller\\|display' | head -1";
|
" || glxinfo -B 2>/dev/null | grep 'Device:' | cut -d':' -f2 | cut -d'(' "
|
||||||
|
"-f1"
|
||||||
|
" || lspci 2>/dev/null | grep -i 'vga\\|3d controller\\|display' | head -1";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Gpu::Gpu(QObject* parent)
|
Gpu::Gpu(QObject* parent) : TickingService(parent) {
|
||||||
: TickingService(parent) {
|
QString configPath =
|
||||||
QString configPath = QDir::homePath() + QStringLiteral("/.config/zshell/config.json");
|
QDir::homePath() + QStringLiteral("/.config/zshell/config.json");
|
||||||
|
|
||||||
auto reloadConfig = [this, configPath]() {
|
auto reloadConfig = [this, configPath]() {
|
||||||
QFile file(configPath);
|
QFile file(configPath);
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
||||||
if (!doc.isNull()) {
|
if (!doc.isNull()) {
|
||||||
QJsonObject services = doc.object().value("services").toObject();
|
QJsonObject services =
|
||||||
|
doc.object().value("services").toObject();
|
||||||
setUserType(parseType(services.value("gpuType").toString()));
|
setUserType(parseType(services.value("gpuType").toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,18 +51,25 @@ Gpu::Gpu(QObject* parent)
|
|||||||
|
|
||||||
static QFileSystemWatcher* watcher = new QFileSystemWatcher();
|
static QFileSystemWatcher* watcher = new QFileSystemWatcher();
|
||||||
if (!watcher->files().contains(configPath)) {
|
if (!watcher->files().contains(configPath)) {
|
||||||
QObject::connect(watcher, &QFileSystemWatcher::fileChanged, this, [this, configPath]() {
|
QObject::connect(
|
||||||
QTimer::singleShot(100, this, [this, configPath]() {
|
watcher,
|
||||||
QFile file(configPath);
|
&QFileSystemWatcher::fileChanged,
|
||||||
if (file.exists()) {
|
this,
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
[this, configPath]() {
|
||||||
if (!doc.isNull()) {
|
QTimer::singleShot(100, this, [this, configPath]() {
|
||||||
QJsonObject services = doc.object().value("services").toObject();
|
QFile file(configPath);
|
||||||
setUserType(parseType(services.value("gpuType").toString()));
|
if (file.exists()) {
|
||||||
|
QJsonDocument doc =
|
||||||
|
QJsonDocument::fromJson(file.readAll());
|
||||||
|
if (!doc.isNull()) {
|
||||||
|
QJsonObject services =
|
||||||
|
doc.object().value("services").toObject();
|
||||||
|
setUserType(parseType(
|
||||||
|
services.value("gpuType").toString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
watcher->addPath(configPath);
|
watcher->addPath(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,13 +146,13 @@ void Gpu::setName(QString value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::setMemoryUsed(qreal value) {
|
void Gpu::setMemoryUsed(qreal value) {
|
||||||
if (m_memoryUsed == value) return;
|
if (std::abs(m_memoryUsed - value) < 0.001) return;
|
||||||
m_memoryUsed = value;
|
m_memoryUsed = value;
|
||||||
Q_EMIT memoryUsedChanged();
|
Q_EMIT memoryUsedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::setMemoryTotal(qreal value) {
|
void Gpu::setMemoryTotal(qreal value) {
|
||||||
if (m_memoryTotal == value) return;
|
if (std::abs(m_memoryTotal - value) < 0.001) return;
|
||||||
m_memoryTotal = value;
|
m_memoryTotal = value;
|
||||||
Q_EMIT memoryTotalChanged();
|
Q_EMIT memoryTotalChanged();
|
||||||
}
|
}
|
||||||
@@ -169,15 +181,22 @@ void Gpu::detectTypeOnce() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_typeProc = new QProcess(this);
|
m_typeProc = new QProcess(this);
|
||||||
QObject::connect(m_typeProc, &QProcess::finished, this, [this](int, QProcess::ExitStatus) {
|
QObject::connect(
|
||||||
const QByteArray out = m_typeProc->readAllStandardOutput().trimmed();
|
m_typeProc,
|
||||||
|
&QProcess::finished,
|
||||||
|
this,
|
||||||
|
[this](int, QProcess::ExitStatus) {
|
||||||
|
const QByteArray out =
|
||||||
|
m_typeProc->readAllStandardOutput().trimmed();
|
||||||
if (!out.isEmpty()) {
|
if (!out.isEmpty()) {
|
||||||
setAutoType(parseType(QString::fromLatin1(out)));
|
setAutoType(parseType(QString::fromLatin1(out)));
|
||||||
}
|
}
|
||||||
m_typeProc->deleteLater();
|
m_typeProc->deleteLater();
|
||||||
m_typeProc = nullptr;
|
m_typeProc = nullptr;
|
||||||
});
|
});
|
||||||
m_typeProc->start(QStringLiteral("sh"), { QStringLiteral("-c"), QString::fromLatin1(kTypeDetectScript) });
|
m_typeProc->start(
|
||||||
|
QStringLiteral("sh"),
|
||||||
|
{QStringLiteral("-c"), QString::fromLatin1(kTypeDetectScript)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::detectNameOnce() {
|
void Gpu::detectNameOnce() {
|
||||||
@@ -185,21 +204,30 @@ void Gpu::detectNameOnce() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_nameProc = new QProcess(this);
|
m_nameProc = new QProcess(this);
|
||||||
QObject::connect(m_nameProc, &QProcess::finished, this, [this](int, QProcess::ExitStatus) {
|
QObject::connect(
|
||||||
const QString output = QString::fromUtf8(m_nameProc->readAllStandardOutput()).trimmed();
|
m_nameProc,
|
||||||
|
&QProcess::finished,
|
||||||
|
this,
|
||||||
|
[this](int, QProcess::ExitStatus) {
|
||||||
|
const QString output =
|
||||||
|
QString::fromUtf8(m_nameProc->readAllStandardOutput()).trimmed();
|
||||||
if (!output.isEmpty()) {
|
if (!output.isEmpty()) {
|
||||||
const QString lower = output.toLower();
|
const QString lower = output.toLower();
|
||||||
if (lower.contains(QStringLiteral("nvidia")) || lower.contains(QStringLiteral("geforce")) ||
|
if (lower.contains(QStringLiteral("nvidia")) ||
|
||||||
lower.contains(QStringLiteral("rtx")) || lower.contains(QStringLiteral("gtx")) ||
|
lower.contains(QStringLiteral("geforce")) ||
|
||||||
lower.contains(QStringLiteral("rx"))) {
|
lower.contains(QStringLiteral("rtx")) ||
|
||||||
|
lower.contains(QStringLiteral("gtx")) ||
|
||||||
|
lower.contains(QStringLiteral("rx"))) {
|
||||||
setName(cleanName(output));
|
setName(cleanName(output));
|
||||||
} else {
|
} else {
|
||||||
static const QRegularExpression bracketRe(QStringLiteral("\\[([^\\]]+)\\][^\\[]*$"));
|
static const QRegularExpression bracketRe(
|
||||||
|
QStringLiteral("\\[([^\\]]+)\\][^\\[]*$"));
|
||||||
const auto bracket = bracketRe.match(output);
|
const auto bracket = bracketRe.match(output);
|
||||||
if (bracket.hasMatch()) {
|
if (bracket.hasMatch()) {
|
||||||
setName(cleanName(bracket.captured(1)));
|
setName(cleanName(bracket.captured(1)));
|
||||||
} else {
|
} else {
|
||||||
static const QRegularExpression colonRe(QStringLiteral(":\\s*(.+)"));
|
static const QRegularExpression colonRe(
|
||||||
|
QStringLiteral(":\\s*(.+)"));
|
||||||
const auto colon = colonRe.match(output);
|
const auto colon = colonRe.match(output);
|
||||||
if (colon.hasMatch()) {
|
if (colon.hasMatch()) {
|
||||||
setName(cleanName(colon.captured(1)));
|
setName(cleanName(colon.captured(1)));
|
||||||
@@ -210,17 +238,21 @@ void Gpu::detectNameOnce() {
|
|||||||
m_nameProc->deleteLater();
|
m_nameProc->deleteLater();
|
||||||
m_nameProc = nullptr;
|
m_nameProc = nullptr;
|
||||||
});
|
});
|
||||||
m_nameProc->start(QStringLiteral("sh"), { QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript) });
|
m_nameProc->start(
|
||||||
|
QStringLiteral("sh"),
|
||||||
|
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::readGenericUsage() {
|
void Gpu::readGenericUsage() {
|
||||||
const QStringList paths =
|
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
||||||
QDir(QStringLiteral("/sys/class/drm"))
|
.entryList(
|
||||||
.entryList(QStringList() << QStringLiteral("card*"), QDir::Dirs | QDir::NoDotAndDotDot);
|
QStringList() << QStringLiteral("card*"),
|
||||||
|
QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
qreal sum = 0.0;
|
qreal sum = 0.0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (const QString& card : paths) {
|
for (const QString& card : paths) {
|
||||||
QFile f(QStringLiteral("/sys/class/drm/%1/device/gpu_busy_percent").arg(card));
|
QFile f(QStringLiteral("/sys/class/drm/%1/device/gpu_busy_percent")
|
||||||
|
.arg(card));
|
||||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -244,57 +276,65 @@ void Gpu::startNvidiaUsage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_nvidiaProc = new QProcess(this);
|
m_nvidiaProc = new QProcess(this);
|
||||||
QObject::connect(m_nvidiaProc, &QProcess::readyReadStandardOutput, this, [this]() {
|
QObject::connect(
|
||||||
while (m_nvidiaProc->canReadLine()) {
|
m_nvidiaProc, &QProcess::readyReadStandardOutput, this, [this]() {
|
||||||
const QByteArray out = m_nvidiaProc->readLine();
|
while (m_nvidiaProc->canReadLine()) {
|
||||||
const QString output = QString::fromUtf8(out).trimmed();
|
const QByteArray out = m_nvidiaProc->readLine();
|
||||||
if (output.isEmpty())
|
const QString output = QString::fromUtf8(out).trimmed();
|
||||||
continue;
|
if (output.isEmpty()) continue;
|
||||||
|
|
||||||
const QList<QString> parts = output.split(',');
|
const QList<QString> parts = output.split(',');
|
||||||
if (parts.size() < 4)
|
if (parts.size() < 4) return;
|
||||||
return;
|
|
||||||
|
|
||||||
bool ok1 = false;
|
bool ok1 = false;
|
||||||
bool ok2 = false;
|
bool ok2 = false;
|
||||||
bool ok3 = false;
|
bool ok3 = false;
|
||||||
bool ok4 = false;
|
bool ok4 = false;
|
||||||
const qreal usage = parts.at(0).trimmed().toDouble(&ok1) / 100.0;
|
const qreal usage =
|
||||||
const qreal temp = parts.at(1).trimmed().toDouble(&ok2);
|
parts.at(0).trimmed().toDouble(&ok1) / 100.0;
|
||||||
const qreal memUsed = parts.at(2).trimmed().toDouble(&ok3);
|
const qreal temp = parts.at(1).trimmed().toDouble(&ok2);
|
||||||
const qreal memTotal = parts.at(3).trimmed().toDouble(&ok4);
|
const qreal memUsed = parts.at(2).trimmed().toDouble(&ok3);
|
||||||
|
const qreal memTotal = parts.at(3).trimmed().toDouble(&ok4);
|
||||||
|
|
||||||
if (ok1 && std::abs(usage - m_percentage) > 0.0001) {
|
if (ok1 && std::abs(usage - m_percentage) > 0.0001) {
|
||||||
m_percentage = usage;
|
m_percentage = usage;
|
||||||
Q_EMIT percentageChanged();
|
Q_EMIT percentageChanged();
|
||||||
|
}
|
||||||
|
if (ok2 && std::abs(temp - m_temperature) > 0.05) {
|
||||||
|
m_temperature = temp;
|
||||||
|
Q_EMIT temperatureChanged();
|
||||||
|
}
|
||||||
|
if (ok3) {
|
||||||
|
setMemoryUsed(memUsed);
|
||||||
|
}
|
||||||
|
if (ok4) {
|
||||||
|
setMemoryTotal(memTotal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ok2 && std::abs(temp - m_temperature) > 0.05) {
|
});
|
||||||
m_temperature = temp;
|
|
||||||
Q_EMIT temperatureChanged();
|
|
||||||
}
|
|
||||||
if (ok3) {
|
|
||||||
setMemoryUsed(memUsed);
|
|
||||||
}
|
|
||||||
if (ok4) {
|
|
||||||
setMemoryTotal(memTotal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
QObject::connect(m_nvidiaProc, &QProcess::finished, this, [this]() {
|
QObject::connect(m_nvidiaProc, &QProcess::finished, this, [this]() {
|
||||||
m_nvidiaProc->deleteLater();
|
m_nvidiaProc->deleteLater();
|
||||||
m_nvidiaProc = nullptr;
|
m_nvidiaProc = nullptr;
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(m_nvidiaProc, &QProcess::errorOccurred, this, [this](QProcess::ProcessError) {
|
QObject::connect(
|
||||||
m_nvidiaProc->deleteLater();
|
m_nvidiaProc,
|
||||||
m_nvidiaProc = nullptr;
|
&QProcess::errorOccurred,
|
||||||
});
|
this,
|
||||||
|
[this](QProcess::ProcessError) {
|
||||||
|
m_nvidiaProc->deleteLater();
|
||||||
|
m_nvidiaProc = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
m_nvidiaProc->start(QStringLiteral("nvidia-smi"), { QStringLiteral("--query-gpu=utilization.gpu,temperature.gpu,memory.used,memory.total"),
|
m_nvidiaProc->start(
|
||||||
QStringLiteral("--format=csv,noheader,nounits"),
|
QStringLiteral("nvidia-smi"),
|
||||||
QStringLiteral("-lms"),
|
{QStringLiteral(
|
||||||
QString::number(updateInterval()) });
|
"--query-gpu=utilization.gpu,temperature.gpu,"
|
||||||
|
"memory.used,memory.total"),
|
||||||
|
QStringLiteral("--format=csv,noheader,nounits"),
|
||||||
|
QStringLiteral("-lms"),
|
||||||
|
QString::number(updateInterval())});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::readGpuTemperature() {
|
void Gpu::readGpuTemperature() {
|
||||||
@@ -322,7 +362,8 @@ Gpu::Type Gpu::parseType(const QString& s) {
|
|||||||
|
|
||||||
QString Gpu::cleanName(QString s) {
|
QString Gpu::cleanName(QString s) {
|
||||||
static const QRegularExpression noise(
|
static const QRegularExpression noise(
|
||||||
QStringLiteral("\\(R\\)|\\(TM\\)|Graphics"), QRegularExpression::CaseInsensitiveOption);
|
QStringLiteral("\\(R\\)|\\(TM\\)|Graphics"),
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
static const QRegularExpression spaces(QStringLiteral("\\s+"));
|
static const QRegularExpression spaces(QStringLiteral("\\s+"));
|
||||||
s.replace(noise, QString());
|
s.replace(noise, QString());
|
||||||
s.replace(spaces, QStringLiteral(" "));
|
s.replace(spaces, QStringLiteral(" "));
|
||||||
|
|||||||
Reference in New Issue
Block a user