Skip to content

Commit cac3767

Browse files
Adapter selection: allow LUID resolution by PCI bus
1 parent 498c37b commit cac3767

1 file changed

Lines changed: 96 additions & 2 deletions

File tree

Common/Include/AdapterOption.h

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
#include <wrl/client.h> // For ComPtr
44
#include <dxgi.h> // For IDXGIAdapter, IDXGIFactory1
55
#include <algorithm> // For sort
6+
#include <setupapi.h>
7+
#include <devguid.h>
8+
#include <devpkey.h>
9+
#include <cstdint>
10+
#include <optional>
11+
#include <string>
12+
#include <vector>
13+
#include <fstream>
614

715
using namespace std;
816
using namespace Microsoft::WRL;
@@ -49,6 +57,68 @@ vector<GPUInfo> getAvailableGPUs() {
4957
return gpus;
5058
}
5159

60+
// Resolve an adapter LUID from a PCI bus number by enumerating display devices (SetupAPI).
61+
// Returns nullopt if no match is found or if the system doesn't expose the LUID property.
62+
inline std::optional<LUID> ResolveAdapterLuidFromPciBus(uint32_t targetBusIndex) {
63+
HDEVINFO devInfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, nullptr, nullptr, DIGCF_PRESENT);
64+
if (devInfo == INVALID_HANDLE_VALUE) {
65+
return std::nullopt;
66+
}
67+
68+
SP_DEVINFO_DATA devData = {};
69+
devData.cbSize = sizeof(devData);
70+
71+
std::optional<LUID> result = std::nullopt;
72+
73+
for (DWORD i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devData); ++i) {
74+
DWORD currentBus = 0;
75+
if (!SetupDiGetDeviceRegistryPropertyW(
76+
devInfo,
77+
&devData,
78+
SPDRP_BUSNUMBER,
79+
nullptr,
80+
reinterpret_cast<PBYTE>(&currentBus),
81+
sizeof(currentBus),
82+
nullptr)) {
83+
continue;
84+
}
85+
86+
if (static_cast<uint32_t>(currentBus) != targetBusIndex) {
87+
continue;
88+
}
89+
90+
// DEVPKEY_Device_Luid is exposed as a UINT64 on Windows; convert into LUID.
91+
DEVPROPTYPE propType = 0;
92+
ULONG propSize = 0;
93+
ULONGLONG luid64 = 0;
94+
95+
if (!SetupDiGetDevicePropertyW(
96+
devInfo,
97+
&devData,
98+
&DEVPKEY_Device_Luid,
99+
&propType,
100+
reinterpret_cast<PBYTE>(&luid64),
101+
sizeof(luid64),
102+
&propSize,
103+
0)) {
104+
continue;
105+
}
106+
107+
if (propType != DEVPROP_TYPE_UINT64 || propSize != sizeof(luid64)) {
108+
continue;
109+
}
110+
111+
LUID luid{};
112+
luid.LowPart = static_cast<DWORD>(luid64 & 0xFFFFFFFFull);
113+
luid.HighPart = static_cast<LONG>((luid64 >> 32) & 0xFFFFFFFFull);
114+
result = luid;
115+
break;
116+
}
117+
118+
SetupDiDestroyDeviceInfoList(devInfo);
119+
return result;
120+
}
121+
52122
class AdapterOption {
53123
public:
54124
bool hasTargetAdapter{}; // Indicates if a target adapter is selected
@@ -110,8 +180,32 @@ class AdapterOption {
110180
}
111181

112182
private:
113-
// Find and set the adapter by its name
114-
bool findAndSetAdapter(const wstring& adapterName) {
183+
// Find and set the adapter by name, optionally using "name,bus" where bus is the PCI bus number.
184+
bool findAndSetAdapter(const wstring& adapterSpec) {
185+
// If user provides "name,bus", use bus to resolve LUID (more deterministic on multi-GPU setups).
186+
const size_t comma = adapterSpec.find(L',');
187+
if (comma != wstring::npos) {
188+
const wstring namePart = adapterSpec.substr(0, comma);
189+
wstring busPart = adapterSpec.substr(comma + 1);
190+
// Trim whitespace in bus part
191+
busPart.erase(remove_if(busPart.begin(), busPart.end(), iswspace), busPart.end());
192+
193+
wchar_t* end = nullptr;
194+
const unsigned long busUl = wcstoul(busPart.c_str(), &end, 10);
195+
const bool parsedOk = (end != nullptr) && (*end == L'\0') && (end != busPart.c_str());
196+
if (parsedOk && busUl <= 0xFFFFFFFFul) {
197+
if (auto luidOpt = ResolveAdapterLuidFromPciBus(static_cast<uint32_t>(busUl)); luidOpt.has_value()) {
198+
adapterLuid = luidOpt.value();
199+
hasTargetAdapter = true;
200+
return true;
201+
}
202+
}
203+
204+
// Fall through to name matching using the name portion.
205+
return findAndSetAdapter(namePart);
206+
}
207+
208+
const wstring& adapterName = adapterSpec;
115209
auto gpus = getAvailableGPUs();
116210

117211
// Iterate through all available GPUs

0 commit comments

Comments
 (0)