瀏覽代碼

libobs-d3d11: Log display color space info

Add DXGI_COLOR_SPACE_TYPE and SDR white level when available for HDR
characteristics.

GetPathInfo/IsInternalVideoOutput functions were copied from MS docs.
jpark37 3 年之前
父節點
當前提交
83c89d06b5
共有 2 個文件被更改,包括 195 次插入3 次删除
  1. 194 2
      libobs-d3d11/d3d11-subsystem.cpp
  2. 1 1
      libobs-d3d11/d3d11-subsystem.hpp

+ 194 - 2
libobs-d3d11/d3d11-subsystem.cpp

@@ -997,6 +997,180 @@ static bool GetMonitorTarget(const MONITORINFOEX &info,
 	return found;
 }
 
+static DXGI_COLOR_SPACE_TYPE GetColorSpace(IDXGIOutput *const output)
+{
+	DXGI_COLOR_SPACE_TYPE space = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
+
+	ComPtr<IDXGIOutput6> output6;
+	HRESULT hr = output->QueryInterface(IID_PPV_ARGS(output6.Assign()));
+	if (SUCCEEDED(hr)) {
+		DXGI_OUTPUT_DESC1 desc1;
+		hr = output6->GetDesc1(&desc1);
+		if (SUCCEEDED(hr)) {
+			space = desc1.ColorSpace;
+		} else {
+			blog(LOG_WARNING,
+			     "IDXGIOutput6::GetDesc1 failed: 0x%08lX", hr);
+		}
+	}
+
+	return space;
+}
+
+// Returns true if this is an integrated display panel e.g. the screen attached to tablets or laptops.
+static bool IsInternalVideoOutput(
+	const DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY VideoOutputTechnologyType)
+{
+	switch (VideoOutputTechnologyType) {
+	case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL:
+	case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED:
+	case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED:
+		return TRUE;
+
+	default:
+		return FALSE;
+	}
+}
+
+// Note: Since an hmon can represent multiple monitors while in clone, this function as written will return
+//  the value for the internal monitor if one exists, and otherwise the highest clone-path priority.
+static HRESULT GetPathInfo(_In_ PCWSTR pszDeviceName,
+			   _Out_ DISPLAYCONFIG_PATH_INFO *pPathInfo)
+{
+	HRESULT hr = S_OK;
+	UINT32 NumPathArrayElements = 0;
+	UINT32 NumModeInfoArrayElements = 0;
+	DISPLAYCONFIG_PATH_INFO *PathInfoArray = nullptr;
+	DISPLAYCONFIG_MODE_INFO *ModeInfoArray = nullptr;
+
+	do {
+		// In case this isn't the first time through the loop, delete the buffers allocated
+		delete[] PathInfoArray;
+		PathInfoArray = nullptr;
+
+		delete[] ModeInfoArray;
+		ModeInfoArray = nullptr;
+
+		hr = HRESULT_FROM_WIN32(GetDisplayConfigBufferSizes(
+			QDC_ONLY_ACTIVE_PATHS, &NumPathArrayElements,
+			&NumModeInfoArrayElements));
+		if (FAILED(hr)) {
+			break;
+		}
+
+		PathInfoArray = new (std::nothrow)
+			DISPLAYCONFIG_PATH_INFO[NumPathArrayElements];
+		if (PathInfoArray == nullptr) {
+			hr = E_OUTOFMEMORY;
+			break;
+		}
+
+		ModeInfoArray = new (std::nothrow)
+			DISPLAYCONFIG_MODE_INFO[NumModeInfoArrayElements];
+		if (ModeInfoArray == nullptr) {
+			hr = E_OUTOFMEMORY;
+			break;
+		}
+
+		hr = HRESULT_FROM_WIN32(QueryDisplayConfig(
+			QDC_ONLY_ACTIVE_PATHS, &NumPathArrayElements,
+			PathInfoArray, &NumModeInfoArrayElements, ModeInfoArray,
+			nullptr));
+	} while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
+
+	INT DesiredPathIdx = -1;
+
+	if (SUCCEEDED(hr)) {
+		// Loop through all sources until the one which matches the 'monitor' is found.
+		for (UINT PathIdx = 0; PathIdx < NumPathArrayElements;
+		     ++PathIdx) {
+			DISPLAYCONFIG_SOURCE_DEVICE_NAME SourceName = {};
+			SourceName.header.type =
+				DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+			SourceName.header.size = sizeof(SourceName);
+			SourceName.header.adapterId =
+				PathInfoArray[PathIdx].sourceInfo.adapterId;
+			SourceName.header.id =
+				PathInfoArray[PathIdx].sourceInfo.id;
+
+			hr = HRESULT_FROM_WIN32(
+				DisplayConfigGetDeviceInfo(&SourceName.header));
+			if (SUCCEEDED(hr)) {
+				if (wcscmp(pszDeviceName,
+					   SourceName.viewGdiDeviceName) == 0) {
+					// Found the source which matches this hmonitor. The paths are given in path-priority order
+					// so the first found is the most desired, unless we later find an internal.
+					if (DesiredPathIdx == -1 ||
+					    IsInternalVideoOutput(
+						    PathInfoArray[PathIdx]
+							    .targetInfo
+							    .outputTechnology)) {
+						DesiredPathIdx = PathIdx;
+					}
+				}
+			}
+		}
+	}
+
+	if (DesiredPathIdx != -1) {
+		*pPathInfo = PathInfoArray[DesiredPathIdx];
+	} else {
+		hr = E_INVALIDARG;
+	}
+
+	delete[] PathInfoArray;
+	PathInfoArray = nullptr;
+
+	delete[] ModeInfoArray;
+	ModeInfoArray = nullptr;
+
+	return hr;
+}
+
+// Overloaded function accepts an HMONITOR and converts to DeviceName
+static HRESULT GetPathInfo(HMONITOR hMonitor,
+			   _Out_ DISPLAYCONFIG_PATH_INFO *pPathInfo)
+{
+	HRESULT hr = S_OK;
+
+	// Get the name of the 'monitor' being requested
+	MONITORINFOEXW ViewInfo;
+	RtlZeroMemory(&ViewInfo, sizeof(ViewInfo));
+	ViewInfo.cbSize = sizeof(ViewInfo);
+	if (!GetMonitorInfoW(hMonitor, &ViewInfo)) {
+		// Error condition, likely invalid monitor handle, could log error
+		hr = HRESULT_FROM_WIN32(GetLastError());
+	}
+
+	if (SUCCEEDED(hr)) {
+		hr = GetPathInfo(ViewInfo.szDevice, pPathInfo);
+	}
+
+	return hr;
+}
+
+static ULONG GetSdrWhiteNits(HMONITOR monitor)
+{
+	ULONG nits = 0;
+
+	DISPLAYCONFIG_PATH_INFO info;
+	if (SUCCEEDED(GetPathInfo(monitor, &info))) {
+		const DISPLAYCONFIG_PATH_TARGET_INFO &targetInfo =
+			info.targetInfo;
+
+		DISPLAYCONFIG_SDR_WHITE_LEVEL level;
+		DISPLAYCONFIG_DEVICE_INFO_HEADER &header = level.header;
+		header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
+		header.size = sizeof(level);
+		header.adapterId = targetInfo.adapterId;
+		header.id = targetInfo.id;
+		if (DisplayConfigGetDeviceInfo(&header) == ERROR_SUCCESS)
+			nits = (level.SDRWhiteLevel * 80) / 1000;
+	}
+
+	return nits;
+}
+
 static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
 {
 	UINT i;
@@ -1030,18 +1204,36 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
 			target.monitorFriendlyDeviceName[0] = 0;
 		}
 
+		const char *space = "Unknown";
+		const DXGI_COLOR_SPACE_TYPE type = GetColorSpace(output);
+		switch (type) {
+		case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:
+			space = "RGB_FULL_G22_NONE_P709";
+			break;
+		case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
+			space = "RGB_FULL_G2084_NONE_P2020";
+			break;
+		default:
+			blog(LOG_WARNING,
+			     "Unexpected DXGI_COLOR_SPACE_TYPE: %u",
+			     (unsigned)type);
+		}
+
 		const RECT &rect = desc.DesktopCoordinates;
+		const ULONG nits = GetSdrWhiteNits(desc.Monitor);
 		blog(LOG_INFO,
 		     "\t  output %u: "
 		     "pos={%d, %d}, "
 		     "size={%d, %d}, "
 		     "attached=%s, "
+		     "space=%s, "
+		     "sdr_white_nits=%lu, "
 		     "refresh=%u, "
 		     "name=%ls",
 		     i, rect.left, rect.top, rect.right - rect.left,
 		     rect.bottom - rect.top,
-		     desc.AttachedToDesktop ? "true" : "false", refresh,
-		     target.monitorFriendlyDeviceName);
+		     desc.AttachedToDesktop ? "true" : "false", space, nits,
+		     refresh, target.monitorFriendlyDeviceName);
 	}
 }
 

+ 1 - 1
libobs-d3d11/d3d11-subsystem.hpp

@@ -25,7 +25,7 @@
 #include <memory>
 
 #include <windows.h>
-#include <dxgi1_5.h>
+#include <dxgi1_6.h>
 #include <d3d11_1.h>
 #include <d3dcompiler.h>