Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/doitsujin/dxvk.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Ashton <joshua@froggi.es>2022-11-16 19:29:17 +0300
committerJoshua Ashton <joshua@froggi.es>2022-11-17 19:36:15 +0300
commit65b33f0c396bfe2fb0d3d480d98f4311841b876d (patch)
tree81352a566f6c20a1a085aa11e6ae9622923ebc28
parentad3c316d0c96a02d19f76d6bbcb90cdcdd1a9425 (diff)
[wsi] Add getMonitorEdid functionget-edid
What an unbelievable pain this is to do on Windows... No-op on SDL2 right now, as there is nothing for that.
-rw-r--r--src/wsi/sdl2/wsi_monitor_sdl2.cpp5
-rw-r--r--src/wsi/win32/wsi_monitor_win32.cpp153
-rw-r--r--src/wsi/wsi_monitor.h17
3 files changed, 174 insertions, 1 deletions
diff --git a/src/wsi/sdl2/wsi_monitor_sdl2.cpp b/src/wsi/sdl2/wsi_monitor_sdl2.cpp
index 1ab82301..bb2fd3f8 100644
--- a/src/wsi/sdl2/wsi_monitor_sdl2.cpp
+++ b/src/wsi/sdl2/wsi_monitor_sdl2.cpp
@@ -144,4 +144,9 @@ namespace dxvk::wsi {
return true;
}
+ std::vector<uint8_t> getMonitorEdid(HMONITOR hMonitor) {
+ Logger::err("getMonitorEdid not implemented on this platform.");
+ 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 be466db5..3d7913e8 100644
--- a/src/wsi/win32/wsi_monitor_win32.cpp
+++ b/src/wsi/win32/wsi_monitor_win32.cpp
@@ -1,9 +1,14 @@
#include "../wsi_monitor.h"
#include "../../util/log/log.h"
+#include "../../util/util_string.h"
#include <cstring>
+#include <setupapi.h>
+#include <ntddvdeo.h>
+#include <cfgmgr32.h>
+
namespace dxvk::wsi {
HMONITOR getDefaultMonitor() {
@@ -131,4 +136,152 @@ namespace dxvk::wsi {
return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode);
}
+ static std::wstring getMonitorDevicePath(HMONITOR hMonitor) {
+ // Get the device name of the monitor.
+ MONITORINFOEXW monInfo;
+ monInfo.cbSize = sizeof(monInfo);
+ if (!::GetMonitorInfoW(hMonitor, &monInfo)) {
+ Logger::err("getMonitorDevicePath: Failed to get monitor info.");
+ return {};
+ }
+
+ // Try and find the monitor device path that matches
+ // the name of our HMONITOR from the monitor info.
+ LONG result = ERROR_SUCCESS;
+ std::vector<DISPLAYCONFIG_PATH_INFO> paths;
+ std::vector<DISPLAYCONFIG_MODE_INFO> modes;
+ do {
+ uint32_t pathCount = 0, modeCount = 0;
+ if ((result = ::GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount)) != ERROR_SUCCESS) {
+ Logger::err(str::format("getMonitorDevicePath: GetDisplayConfigBufferSizes failed. ret: ", result, " LastError: ", GetLastError()));
+ return {};
+ }
+ 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 != ERROR_SUCCESS) {
+ Logger::err(str::format("getMonitorDevicePath: QueryDisplayConfig failed. ret: ", result, " LastError: ", GetLastError()));
+ return {};
+ }
+
+ // Link a source name -> target name
+ for (const auto& path : paths) {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName;
+ sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ sourceName.header.size = sizeof(sourceName);
+ sourceName.header.adapterId = path.sourceInfo.adapterId;
+ sourceName.header.id = path.sourceInfo.id;
+ if ((result = ::DisplayConfigGetDeviceInfo(&sourceName.header)) != ERROR_SUCCESS) {
+ Logger::err(str::format("getMonitorDevicePath: DisplayConfigGetDeviceInfo with DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME failed. ret: ", result, " LastError: ", GetLastError()));
+ continue;
+ }
+
+ DISPLAYCONFIG_TARGET_DEVICE_NAME targetName;
+ targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+ targetName.header.size = sizeof(targetName);
+ targetName.header.adapterId = path.targetInfo.adapterId;
+ targetName.header.id = path.targetInfo.id;
+ if ((result = ::DisplayConfigGetDeviceInfo(&targetName.header)) != ERROR_SUCCESS) {
+ Logger::err(str::format("getMonitorDevicePath: DisplayConfigGetDeviceInfo with DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME failed. ret: ", result, " LastError: ", GetLastError()));
+ continue;
+ }
+
+ // Does the source match the GDI device we are looking for?
+ // If so, return the target back.
+ if (!wcscmp(sourceName.viewGdiDeviceName, monInfo.szDevice))
+ return targetName.monitorDevicePath;
+ }
+
+ Logger::err("getMonitorDevicePath: Failed to find a link from source -> target.");
+ return {};
+ }
+
+ static WsiEdidData readMonitorEdidFromKey(HKEY deviceRegKey) {
+ DWORD edidSize = 0;
+ if (::RegQueryValueExW(deviceRegKey, L"EDID", nullptr, nullptr, nullptr, &edidSize) != ERROR_SUCCESS) {
+ Logger::err("readMonitorEdidFromKey: Failed to get EDID reg key size");
+ return {};
+ }
+
+ WsiEdidData edidData(edidSize);
+ if (::RegQueryValueExW(deviceRegKey, L"EDID", nullptr, nullptr, edidData.data(), &edidSize) != ERROR_SUCCESS) {
+ Logger::err("readMonitorEdidFromKey: Failed to get EDID reg key data");
+ return {};
+ }
+
+ return edidData;
+ }
+
+ struct DxvkDeviceInterfaceDetail {
+ // SP_DEVICE_INTERFACE_DETAIL_DATA_W contains an array called
+ // "ANYSIZE_ARRAY" which is just 1 wchar_t in size.
+ // Incredible, safe, and smart API design.
+ // Allocate some chars after so it can fill these in.
+ SP_DEVICE_INTERFACE_DETAIL_DATA_W base;
+ wchar_t extraChars[MAX_DEVICE_ID_LEN];
+ };
+
+ WsiEdidData getMonitorEdid(HMONITOR hMonitor) {
+ static constexpr GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 };
+ static auto pfnSetupDiGetClassDevsW = reinterpret_cast<decltype(SetupDiGetClassDevsW)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiGetClassDevsW"));
+ static auto pfnSetupDiEnumDeviceInterfaces = reinterpret_cast<decltype(SetupDiEnumDeviceInterfaces)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiEnumDeviceInterfaces"));
+ static auto pfnSetupDiGetDeviceInterfaceDetailW = reinterpret_cast<decltype(SetupDiGetDeviceInterfaceDetailW)*>(::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiGetDeviceInterfaceDetailW"));
+ static auto pfnSetupDiOpenDevRegKey = reinterpret_cast<decltype(SetupDiOpenDevRegKey)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiOpenDevRegKey"));
+ static auto pfnSetupDiGetDeviceInstanceIdW = reinterpret_cast<decltype(SetupDiGetDeviceInstanceIdW)*> (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiGetDeviceInstanceIdW"));
+ if (!pfnSetupDiGetClassDevsW || !pfnSetupDiEnumDeviceInterfaces || !pfnSetupDiGetDeviceInterfaceDetailW || !pfnSetupDiOpenDevRegKey || !pfnSetupDiGetDeviceInstanceIdW) {
+ Logger::err("getMonitorEdid: Failed to load functions from setupapi.");
+ return {};
+ }
+
+ std::wstring monitorDevicePath = getMonitorDevicePath(hMonitor);
+ if (monitorDevicePath.empty()) {
+ Logger::err("getMonitorEdid: Failed to get monitor device path.");
+ return {};
+ }
+
+ const HDEVINFO devInfo = pfnSetupDiGetClassDevsW(&GUID_DEVINTERFACE_MONITOR, nullptr, nullptr, DIGCF_DEVICEINTERFACE);
+
+ SP_DEVICE_INTERFACE_DATA interfaceData;
+ memset(&interfaceData, 0, sizeof(interfaceData));
+ interfaceData.cbSize = sizeof(interfaceData);
+
+ for (DWORD monitorIdx = 0; pfnSetupDiEnumDeviceInterfaces(devInfo, nullptr, &GUID_DEVINTERFACE_MONITOR, monitorIdx, &interfaceData); monitorIdx++) {
+ DxvkDeviceInterfaceDetail detailData;
+ // Josh: I'm taking no chances here. I don't trust this API at all.
+ memset(&detailData, 0, sizeof(detailData));
+ detailData.base.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+
+ SP_DEVINFO_DATA devInfoData;
+ memset(&devInfoData, 0, sizeof(devInfoData));
+ devInfoData.cbSize = sizeof(devInfoData);
+
+ if (!pfnSetupDiGetDeviceInterfaceDetailW(devInfo, &interfaceData, &detailData.base, sizeof(detailData), nullptr, &devInfoData))
+ continue;
+
+ // Check that this monitor matches the same one we are looking for.
+ // Note: For some reason the casing mismatches here, because this
+ // is a well-designed API.
+ // If it isn't what we are looking for, move along.
+ if (_wcsicmp(monitorDevicePath.c_str(), detailData.base.DevicePath) != 0)
+ continue;
+
+ HKEY deviceRegKey = pfnSetupDiOpenDevRegKey(devInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
+ if (deviceRegKey == INVALID_HANDLE_VALUE) {
+ Logger::err("getMonitorEdid: Failed to open monitor device registry key.");
+ return {};
+ }
+
+ auto edidData = readMonitorEdidFromKey(deviceRegKey);
+
+ ::RegCloseKey(deviceRegKey);
+
+ return edidData;
+ }
+
+ Logger::err("getMonitorEdid: Failed to find device interface for monitor using setupapi.");
+ return {};
+ }
+
} \ No newline at end of file
diff --git a/src/wsi/wsi_monitor.h b/src/wsi/wsi_monitor.h
index c7e33578..84c00b08 100644
--- a/src/wsi/wsi_monitor.h
+++ b/src/wsi/wsi_monitor.h
@@ -3,6 +3,7 @@
#include <windows.h>
#include <array>
+#include <vector>
#include <cstdint>
namespace dxvk::wsi {
@@ -123,5 +124,19 @@ namespace dxvk::wsi {
if (pHeight)
*pHeight = rect.bottom - rect.top;
}
-
+
+ using WsiEdidData = std::vector<uint8_t>;
+
+ /**
+ * \brief Get the EDID of a monitor
+ *
+ * Helper function to grab the EDID of a monitor.
+ * This is needed to get the HDR static metadata + colorimetry
+ * info of a display for exposing HDR.
+ *
+ * \param [in] hMonitor The monitor
+ * \returns \c EDID if successful, an empty vector if failure.
+ */
+ WsiEdidData getMonitorEdid(HMONITOR hMonitor);
+
}