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.master
parent
e7cfaa0def
commit
83c89d06b5
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <d3dcompiler.h>
|
||||
|
||||
|
|
Loading…
Reference in New Issue