1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2023 Dave Vasilevsky <dave@vasilevsky.ca> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only |
6 | */ |
7 | |
8 | #include "gpudetection_p.h" |
9 | |
10 | #ifdef WITH_QTDBUS |
11 | #include <QDBusArgument> |
12 | #include <QDBusConnection> |
13 | #include <QDBusInterface> |
14 | #include <QDBusReply> |
15 | #include <QDBusVariant> |
16 | #endif |
17 | #include <QMap> |
18 | #include <QString> |
19 | |
20 | #include <KProcess> |
21 | |
22 | static void checkGpu(); |
23 | // Returns true if switcheroo present |
24 | static bool checkGpuWithSwitcheroo(); |
25 | static void checkGpuWithSolid(); |
26 | |
27 | // TODO: GPUs are hot-swappable, watch for changes using dbus PropertiesChanged |
28 | enum class GpuCheck { |
29 | NotChecked, |
30 | Present, |
31 | Absent, |
32 | }; |
33 | static GpuCheck s_gpuCheck = GpuCheck::NotChecked; |
34 | static QProcessEnvironment s_gpuEnv; |
35 | |
36 | static void checkGpu() |
37 | { |
38 | if (s_gpuCheck == GpuCheck::NotChecked) { |
39 | if (!checkGpuWithSwitcheroo()) { |
40 | checkGpuWithSolid(); |
41 | } |
42 | } |
43 | } |
44 | |
45 | static bool checkGpuWithSwitcheroo() |
46 | { |
47 | #ifdef WITH_QTDBUS |
48 | QDBusInterface switcheroo(QStringLiteral("net.hadess.SwitcherooControl" ), |
49 | QStringLiteral("/net/hadess/SwitcherooControl" ), |
50 | QStringLiteral("org.freedesktop.DBus.Properties" ), |
51 | QDBusConnection::systemBus()); |
52 | if (!switcheroo.isValid()) { |
53 | return false; |
54 | } |
55 | |
56 | QDBusReply<QDBusVariant> reply = switcheroo.call(QStringLiteral("Get" ), QStringLiteral("net.hadess.SwitcherooControl" ), QStringLiteral("GPUs" )); |
57 | if (!reply.isValid()) { |
58 | return false; |
59 | } |
60 | |
61 | QDBusArgument arg = qvariant_cast<QDBusArgument>(v: reply.value().variant()); |
62 | QList<QVariantMap> gpus; |
63 | arg >> gpus; |
64 | |
65 | if (gpus.isEmpty()) { |
66 | // No GPU(s) found |
67 | return false; |
68 | } else if (gpus.size() == 1) { |
69 | // There is only one GPU, no need to check for others |
70 | s_gpuCheck = GpuCheck::Absent; |
71 | return true; |
72 | } |
73 | |
74 | QVariantMap defaultGpu; |
75 | QVariantMap firstDiscreteGpu; |
76 | QVariantMap firstNonDefaultGpu; |
77 | |
78 | for (const auto &gpu : std::as_const(t&: gpus)) { |
79 | if (defaultGpu.isEmpty() && gpu[QStringLiteral("Default" )].toBool()) { |
80 | defaultGpu = gpu; |
81 | } else if (firstNonDefaultGpu.isEmpty()) { |
82 | firstNonDefaultGpu = gpu; |
83 | } |
84 | if (firstDiscreteGpu.isEmpty() && gpu[QStringLiteral("Discrete" )].toBool()) { |
85 | firstDiscreteGpu = gpu; |
86 | } |
87 | } |
88 | |
89 | if (!defaultGpu.isEmpty() && defaultGpu[QStringLiteral("Discrete" )].toBool()) { |
90 | // If the default GPU is discrete there is no need to do anything special |
91 | s_gpuCheck = GpuCheck::Absent; |
92 | return true; |
93 | } |
94 | |
95 | // Otherwise prefer the discrete GPU over any other random non-default GPU (legacy behavior) |
96 | for (const auto &gpu : {firstDiscreteGpu, firstNonDefaultGpu}) { |
97 | if (gpu.isEmpty()) { |
98 | continue; |
99 | } |
100 | s_gpuCheck = GpuCheck::Present; |
101 | auto envList = gpu[QStringLiteral("Environment" )].toStringList(); |
102 | for (int i = 0; i + 1 < envList.size(); i += 2) { |
103 | s_gpuEnv.insert(name: envList[i], value: envList[i + 1]); |
104 | } |
105 | return true; |
106 | } |
107 | #endif |
108 | |
109 | // No discrete or non-default GPU found |
110 | s_gpuCheck = GpuCheck::Absent; |
111 | return true; |
112 | } |
113 | |
114 | static void checkGpuWithSolid() |
115 | { |
116 | #ifdef WITH_QTDBUS |
117 | // TODO: Consider moving this check into kio, instead of using Solid |
118 | QDBusInterface iface(QStringLiteral("org.kde.Solid.PowerManagement" ), |
119 | QStringLiteral("/org/kde/Solid/PowerManagement" ), |
120 | QStringLiteral("org.kde.Solid.PowerManagement" ), |
121 | QDBusConnection::sessionBus()); |
122 | if (iface.isValid()) { |
123 | QDBusReply<bool> reply = iface.call(QStringLiteral("hasDualGpu" )); |
124 | if (reply.isValid() && reply.value()) { |
125 | s_gpuCheck = GpuCheck::Present; |
126 | s_gpuEnv.insert(QStringLiteral("DRI_PRIME" ), QStringLiteral("1" )); |
127 | return; |
128 | } |
129 | } |
130 | |
131 | s_gpuCheck = GpuCheck::Absent; |
132 | #endif |
133 | } |
134 | |
135 | namespace KIO |
136 | { |
137 | |
138 | bool hasDiscreteGpu() |
139 | { |
140 | checkGpu(); |
141 | return s_gpuCheck == GpuCheck::Present; |
142 | } |
143 | |
144 | QProcessEnvironment discreteGpuEnvironment() |
145 | { |
146 | checkGpu(); |
147 | return s_gpuEnv; |
148 | } |
149 | |
150 | } |
151 | |