forked from ge9/IddSampleDriver
-
-
Notifications
You must be signed in to change notification settings - Fork 366
Expand file tree
/
Copy pathAdapterOption.h
More file actions
231 lines (194 loc) · 7.59 KB
/
AdapterOption.h
File metadata and controls
231 lines (194 loc) · 7.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#pragma once
#include <wrl/client.h> // For ComPtr
#include <dxgi.h> // For IDXGIAdapter, IDXGIFactory1
#include <algorithm> // For sort
#include <setupapi.h>
#include <devguid.h>
#include <devpropdef.h>
#include <devpkey.h>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
using namespace Microsoft::WRL;
// DEVPKEY_Device_Luid: {60b193cb-5276-4d0f-96fc-f173ab17af69}, 2
// Define it ourselves to avoid SDK/WDK header differences where DEVPKEY_Device_Luid may not be declared.
static const DEVPROPKEY DEVPKEY_Device_Luid_Custom = {
{ 0x60b193cb, 0x5276, 0x4d0f, { 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6 } },
2
};
// Structure to vector gpus
struct GPUInfo {
wstring name; // GPU name
ComPtr<IDXGIAdapter> adapter;// COM pointer to the adapter
DXGI_ADAPTER_DESC desc; // Adapter description
};
// Sort function for GPUs by dedicated video memory
bool CompareGPUs(const GPUInfo& a, const GPUInfo& b) {
return a.desc.DedicatedVideoMemory > b.desc.DedicatedVideoMemory;
}
// Get a enumerate list of available GPUs
vector<GPUInfo> getAvailableGPUs() {
vector<GPUInfo> gpus; // Vector to hold all GPU's information
ComPtr<IDXGIFactory1> factory;
if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) {
return gpus;
}
// Enumerate all adapters (GPUs)
for (UINT i = 0;; i++) {
ComPtr<IDXGIAdapter> adapter;
if (!SUCCEEDED(factory->EnumAdapters(i, &adapter))) {
break;
}
DXGI_ADAPTER_DESC desc;
if (!SUCCEEDED(adapter->GetDesc(&desc))) {
continue;
}
// Add the adapter information to the list
GPUInfo info{ desc.Description, adapter, desc };
gpus.push_back(info);
}
return gpus;
}
// Resolve an adapter LUID from a PCI bus number by enumerating display devices (SetupAPI).
// Returns nullopt if no match is found or if the system doesn't expose the LUID property.
inline std::optional<LUID> ResolveAdapterLuidFromPciBus(uint32_t targetBusIndex) {
HDEVINFO devInfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, nullptr, nullptr, DIGCF_PRESENT);
if (devInfo == INVALID_HANDLE_VALUE) {
return std::nullopt;
}
SP_DEVINFO_DATA devData = {};
devData.cbSize = sizeof(devData);
std::optional<LUID> result = std::nullopt;
for (DWORD i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devData); ++i) {
DWORD currentBus = 0;
if (!SetupDiGetDeviceRegistryPropertyW(
devInfo,
&devData,
SPDRP_BUSNUMBER,
nullptr,
reinterpret_cast<PBYTE>(¤tBus),
sizeof(currentBus),
nullptr)) {
continue;
}
if (static_cast<uint32_t>(currentBus) != targetBusIndex) {
continue;
}
// DEVPKEY_Device_Luid is exposed as a UINT64 on Windows; convert into LUID.
DEVPROPTYPE propType = 0;
ULONG propSize = 0;
ULONGLONG luid64 = 0;
if (!SetupDiGetDevicePropertyW(
devInfo,
&devData,
&DEVPKEY_Device_Luid_Custom,
&propType,
reinterpret_cast<PBYTE>(&luid64),
sizeof(luid64),
&propSize,
0)) {
continue;
}
if (propType != DEVPROP_TYPE_UINT64 || propSize != sizeof(luid64)) {
continue;
}
LUID luid{};
luid.LowPart = static_cast<DWORD>(luid64 & 0xFFFFFFFFull);
luid.HighPart = static_cast<LONG>((luid64 >> 32) & 0xFFFFFFFFull);
result = luid;
break;
}
SetupDiDestroyDeviceInfoList(devInfo);
return result;
}
class AdapterOption {
public:
bool hasTargetAdapter{}; // Indicates if a target adapter is selected
LUID adapterLuid{}; // Adapter's unique identifier (LUID)
wstring target_name{}; // Target adapter name
// Select the best GPU based on dedicated video memory
wstring selectBestGPU() {
auto gpus = getAvailableGPUs();
if (gpus.empty()) {
return L""; // Error check for headless / vm
}
// Sort GPUs by dedicated video memory in descending order
sort(gpus.begin(), gpus.end(), CompareGPUs);
auto bestGPU = gpus.front(); // Get the GPU with the most memory
return bestGPU.name;
}
// Load friendlyname from a file OR select the best GPU
void load(const wchar_t* path) {
ifstream ifs{ path };
if (!ifs.is_open()) {
target_name = selectBestGPU();
}
else {
string line;
getline(ifs, line);
target_name.assign(line.begin(), line.end());
}
// Find and set the adapter based on the target name
if (!findAndSetAdapter(target_name)) {
// If the adapter is not found, select the best GPU and retry
target_name = selectBestGPU();
findAndSetAdapter(target_name);
}
}
// Set the target adapter from a given name and validate it
void xmlprovide(const wstring& xtarg) {
target_name = xtarg;
if (!findAndSetAdapter(target_name)) {
// If the adapter is not found, select the best GPU and retry
target_name = selectBestGPU();
findAndSetAdapter(target_name);
}
}
// Apply the adapter settings to the specified adapter
void apply(const IDDCX_ADAPTER& adapter) {
if (hasTargetAdapter && IDD_IS_FUNCTION_AVAILABLE(IddCxAdapterSetRenderAdapter)) {
IDARG_IN_ADAPTERSETRENDERADAPTER arg{};
arg.PreferredRenderAdapter = adapterLuid;
IddCxAdapterSetRenderAdapter(adapter, &arg);
}
}
private:
// Find and set the adapter by name, optionally using "name,bus" where bus is the PCI bus number.
bool findAndSetAdapter(const wstring& adapterSpec) {
// If user provides "name,bus", use bus to resolve LUID (more deterministic on multi-GPU setups).
const size_t comma = adapterSpec.find(L',');
if (comma != wstring::npos) {
const wstring namePart = adapterSpec.substr(0, comma);
wstring busPart = adapterSpec.substr(comma + 1);
// Trim whitespace in bus part
busPart.erase(remove_if(busPart.begin(), busPart.end(), iswspace), busPart.end());
wchar_t* end = nullptr;
const unsigned long busUl = wcstoul(busPart.c_str(), &end, 10);
const bool parsedOk = (end != nullptr) && (*end == L'\0') && (end != busPart.c_str());
if (parsedOk && busUl <= 0xFFFFFFFFul) {
if (auto luidOpt = ResolveAdapterLuidFromPciBus(static_cast<uint32_t>(busUl)); luidOpt.has_value()) {
adapterLuid = luidOpt.value();
hasTargetAdapter = true;
return true;
}
}
// Fall through to name matching using the name portion.
return findAndSetAdapter(namePart);
}
const wstring& adapterName = adapterSpec;
auto gpus = getAvailableGPUs();
// Iterate through all available GPUs
for (const auto& gpu : gpus) {
if (_wcsicmp(gpu.name.c_str(), adapterName.c_str()) == 0) {
adapterLuid = gpu.desc.AdapterLuid; // Set the adapter LUID
hasTargetAdapter = true; // Indicate that a target adapter is selected
return true;
}
}
hasTargetAdapter = false; // Indicate that no target adapter is selected
return false;
}
};