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.
This commit is contained in:
parent
e7cfaa0def
commit
83c89d06b5
@ -997,6 +997,180 @@ static bool GetMonitorTarget(const MONITORINFOEX &info,
|
|||||||
return found;
|
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)
|
static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
|
||||||
{
|
{
|
||||||
UINT i;
|
UINT i;
|
||||||
@ -1030,18 +1204,36 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
|
|||||||
target.monitorFriendlyDeviceName[0] = 0;
|
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 RECT &rect = desc.DesktopCoordinates;
|
||||||
|
const ULONG nits = GetSdrWhiteNits(desc.Monitor);
|
||||||
blog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"\t output %u: "
|
"\t output %u: "
|
||||||
"pos={%d, %d}, "
|
"pos={%d, %d}, "
|
||||||
"size={%d, %d}, "
|
"size={%d, %d}, "
|
||||||
"attached=%s, "
|
"attached=%s, "
|
||||||
|
"space=%s, "
|
||||||
|
"sdr_white_nits=%lu, "
|
||||||
"refresh=%u, "
|
"refresh=%u, "
|
||||||
"name=%ls",
|
"name=%ls",
|
||||||
i, rect.left, rect.top, rect.right - rect.left,
|
i, rect.left, rect.top, rect.right - rect.left,
|
||||||
rect.bottom - rect.top,
|
rect.bottom - rect.top,
|
||||||
desc.AttachedToDesktop ? "true" : "false", refresh,
|
desc.AttachedToDesktop ? "true" : "false", space, nits,
|
||||||
target.monitorFriendlyDeviceName);
|
refresh, target.monitorFriendlyDeviceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <dxgi1_5.h>
|
#include <dxgi1_6.h>
|
||||||
#include <d3d11_1.h>
|
#include <d3d11_1.h>
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user