diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index 1badca8e4..1094e43b4 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -148,12 +148,35 @@ namespace dxvk { if (ppOutput == nullptr) return E_INVALIDARG; - - HMONITOR monitor = wsi::enumMonitors(Output); - + + const auto& deviceId = m_adapter->devicePropertiesExt().vk11; + + std::array adapterLUIDs = { }; + uint32_t numLUIDs = 0; + + if (m_adapter->isLinkedToDGPU()) + return DXGI_ERROR_NOT_FOUND; + + if (deviceId.deviceLUIDValid) + adapterLUIDs[numLUIDs++] = reinterpret_cast(deviceId.deviceLUID); + + auto linkedAdapter = m_adapter->linkedIGPUAdapter(); + + /* If either LUID is not valid enumerate all monitors. */ + if (numLUIDs && linkedAdapter != nullptr) { + const auto& deviceId = linkedAdapter->devicePropertiesExt().vk11; + + if (deviceId.deviceLUIDValid) + adapterLUIDs[numLUIDs++] = reinterpret_cast(deviceId.deviceLUID); + else + numLUIDs = 0; + } + + HMONITOR monitor = wsi::enumMonitors(adapterLUIDs.data(), numLUIDs, Output); + if (monitor == nullptr) return DXGI_ERROR_NOT_FOUND; - + *ppOutput = ref(new DxgiOutput(m_factory, this, monitor)); return S_OK; } diff --git a/src/dxvk/dxvk_adapter.h b/src/dxvk/dxvk_adapter.h index 48b54e169..ad3ca23ce 100644 --- a/src/dxvk/dxvk_adapter.h +++ b/src/dxvk/dxvk_adapter.h @@ -274,7 +274,34 @@ namespace dxvk { * \returns \c true if the system has unified memory. */ bool isUnifiedMemoryArchitecture() const; - + + /** + * \brief Registers a relationship with another GPU + * + * Used for display enumeration purposes. + * \param [in] dgpu Dedicated GPU adatper + */ + void linkToDGPU(Rc dgpu) { + dgpu->m_linkedIGPUAdapter = this; + m_linkedToDGPU = true; + } + + /** + * \brief Retrieves linked integrated GPU + * \returns Integrated GPU adapter + */ + Rc linkedIGPUAdapter() const { + return m_linkedIGPUAdapter; + } + + /** + * \brief Checks whether the GPU is linked + * \returns \c true if the GPU is linked + */ + bool isLinkedToDGPU() const { + return m_linkedToDGPU; + } + private: Rc m_vki; @@ -286,7 +313,10 @@ namespace dxvk { DxvkDeviceFeatures m_deviceFeatures; bool m_hasMemoryBudget; - + + Rc m_linkedIGPUAdapter; + bool m_linkedToDGPU = false; + std::vector m_queueFamilies; std::array, VK_MAX_MEMORY_HEAPS> m_memoryAllocated = { }; diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index c5fdeabc9..56bfb0d54 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -252,9 +252,18 @@ namespace dxvk { DxvkDeviceFilter filter(filterFlags); std::vector> result; + uint32_t numDGPU = 0; + uint32_t numIGPU = 0; + for (uint32_t i = 0; i < numAdapters; i++) { - if (filter.testAdapter(deviceProperties[i])) + if (filter.testAdapter(deviceProperties[i])) { result.push_back(new DxvkAdapter(m_vki, adapters[i])); + + if (deviceProperties[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + numDGPU += 1; + else if (deviceProperties[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) + numIGPU += 1; + } } std::stable_sort(result.begin(), result.end(), @@ -276,9 +285,11 @@ namespace dxvk { return aRank < bRank; }); - if (result.size() == 0) { + if (result.empty()) { Logger::warn("DXVK: No adapters found. Please check your " "device filter settings and Vulkan setup."); + } else if (numDGPU == 1 && numIGPU == 1) { + result[1]->linkToDGPU(result[0]); } return result; diff --git a/src/wsi/glfw/wsi_monitor_glfw.cpp b/src/wsi/glfw/wsi_monitor_glfw.cpp index 7715118be..e450f83cb 100644 --- a/src/wsi/glfw/wsi_monitor_glfw.cpp +++ b/src/wsi/glfw/wsi_monitor_glfw.cpp @@ -22,6 +22,10 @@ namespace dxvk::wsi { : nullptr; } + HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + return enumMonitors(index); + } + bool getDisplayName( HMONITOR hMonitor, WCHAR (&Name)[32]) { @@ -160,4 +164,4 @@ namespace dxvk::wsi { return {}; } -} \ No newline at end of file +} diff --git a/src/wsi/sdl2/wsi_monitor_sdl2.cpp b/src/wsi/sdl2/wsi_monitor_sdl2.cpp index bb2fd3f86..f0de7cd64 100644 --- a/src/wsi/sdl2/wsi_monitor_sdl2.cpp +++ b/src/wsi/sdl2/wsi_monitor_sdl2.cpp @@ -23,6 +23,10 @@ namespace dxvk::wsi { : nullptr; } + HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + return enumMonitors(index); + } + bool getDisplayName( HMONITOR hMonitor, WCHAR (&Name)[32]) { @@ -149,4 +153,4 @@ namespace dxvk::wsi { return {}; } -} \ No newline at end of file +} diff --git a/src/wsi/win32/wsi_monitor_win32.cpp b/src/wsi/win32/wsi_monitor_win32.cpp index 3d7913e87..c9c2bef9b 100644 --- a/src/wsi/win32/wsi_monitor_win32.cpp +++ b/src/wsi/win32/wsi_monitor_win32.cpp @@ -1,9 +1,11 @@ #include "../wsi_monitor.h" +#include "../../util/util_string.h" #include "../../util/log/log.h" #include "../../util/util_string.h" #include +#include #include #include @@ -16,6 +18,7 @@ namespace dxvk::wsi { } struct MonitorEnumInfo { + const WCHAR *gdiDeviceName; UINT iMonitorId; HMONITOR oMonitor; }; @@ -27,17 +30,26 @@ namespace dxvk::wsi { LPARAM lp) { auto data = reinterpret_cast(lp); - if (data->iMonitorId--) - return TRUE; /* continue */ + if (data->gdiDeviceName) + { + MONITORINFOEXW monitorInfo; + monitorInfo.cbSize = sizeof(monitorInfo); + GetMonitorInfoW(hmon, (MONITORINFO *)&monitorInfo); + if (wcscmp(data->gdiDeviceName, monitorInfo.szDevice)) + return TRUE; + } + if (data->iMonitorId--) + return TRUE; data->oMonitor = hmon; - return FALSE; /* stop */ + return FALSE; } HMONITOR enumMonitors(uint32_t index) { MonitorEnumInfo info; info.iMonitorId = index; info.oMonitor = nullptr; + info.gdiDeviceName = nullptr; ::EnumDisplayMonitors( nullptr, nullptr, &MonitorEnumProc, @@ -46,6 +58,81 @@ namespace dxvk::wsi { return info.oMonitor; } + HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + if (!numLUIDs) + return enumMonitors(index); + + std::vector paths; + std::vector modes; + std::set> sources; + UINT32 pathCount = 0; + UINT32 modeCount = 0; + LONG result; + + do { + if ((result = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount))) { + Logger::err(str::format("GetDisplayConfigBufferSizes failed, result ", result)); + return enumMonitors(index); + } + + paths.resize(pathCount); + modes.resize(modeCount); + + result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, + &pathCount, paths.data(), &modeCount, modes.data(), nullptr); + } while (result == ERROR_INSUFFICIENT_BUFFER); + + if (result) { + Logger::err(str::format("QueryDisplayConfig failed, result ", result)); + return enumMonitors(index); + } + + paths.resize(pathCount); + modes.resize(modeCount); + + MonitorEnumInfo info; + info.iMonitorId = index; + info.oMonitor = nullptr; + + for (const auto &path : paths) { + uint32_t i; + + for (i = 0; i < numLUIDs; ++i) { + if (!std::memcmp(&path.sourceInfo.adapterId, adapterLUID[i], sizeof(path.sourceInfo.adapterId))) + break; + } + + if (i == numLUIDs) + continue; + + /* Mirrored displays appear as multiple paths with the same + * GDI device name, that comes as single dxgi output. */ + if (!sources.insert(std::pair(i, path.sourceInfo.id)).second) + continue; + + DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = { }; + deviceName.header.adapterId = path.sourceInfo.adapterId; + deviceName.header.id = path.sourceInfo.id; + deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + deviceName.header.size = sizeof(deviceName); + + if ((result = DisplayConfigGetDeviceInfo(&deviceName.header))) { + Logger::err(str::format("DisplayConfigGetDeviceInfo failed, result ", result)); + return enumMonitors(index); + } + + info.gdiDeviceName = deviceName.viewGdiDeviceName; + + ::EnumDisplayMonitors( + nullptr, nullptr, &MonitorEnumProc, + reinterpret_cast(&info)); + + if (info.oMonitor != nullptr) + return info.oMonitor; + } + return nullptr; + } + bool getDisplayName( HMONITOR hMonitor, @@ -284,4 +371,4 @@ namespace dxvk::wsi { return {}; } -} \ No newline at end of file +} diff --git a/src/wsi/wsi_monitor.h b/src/wsi/wsi_monitor.h index ef542fed4..29cd9ef78 100644 --- a/src/wsi/wsi_monitor.h +++ b/src/wsi/wsi_monitor.h @@ -43,6 +43,16 @@ namespace dxvk::wsi { */ HMONITOR enumMonitors(uint32_t index); + /** + * \brief Enumerators monitors on the system + * \param [in] adapterLUID array of adapters' LUIDs + * \param [in] numLUIDs adapterLUID array size (0 for all monitors) + * \param [in] index Monitor index within enumeration + * + * \returns The monitor of given index + */ + HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index); + /** * \brief Get the GDI name of a HMONITOR *