////----------------------------------------------------------------------------------
// File:        CDX1x_Common.cpp
// SDK Version: 1.0.2
//
// SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.
//
//----------------------------------------------------------------------------------

///////////////////////////////////////////
// CDX1x_Common.cpp
//
// convenience items used by DX11 and DX12 classes
//

#pragma once

#include "framework.h"

#include "CDX1x_Common.h"
#include <vector>

#pragma comment( lib, "dxgi" )
#pragma comment( lib, "user32" ) 
#pragma comment( lib, "shell32" )
#pragma comment( lib, "Advapi32" )

////////////////////////////////////////////////////
// NOTE: In the SDK Samples it uses ../../Include and ../../Lib/X64
//       instead of setting $(NV_RTX_VIDEO_SDK)
//       To add lib and include api files, save the SDK in the 
//       desired directory and point $(NV_RTX_VIDEO_SDK) to it
//       add to include path: $(NV_RTX_VIDEO_SDK)\include
//       add to lib path: $(NV_RTX_VIDEO_SDK)\lib\x64
#if defined(NDEBUG)
#pragma comment( lib, "nvsdk_ngx_s.lib" ) // ngx sdk
#else
#pragma comment( lib, "nvsdk_ngx_s_dbg.lib" ) // ngx sdk
#endif

UINT CDx1xCommon::GetMonitorMaxLuminance()
{
    return m_uMaxLuminance;
}

RECT CDx1xCommon::GetMonitorRect()
{
    return m_rcMonitor;
}

bool CDx1xCommon::IsMonitorHDR()
{
    bool bHDREnabled = false;
    if (m_colorInfo.header.id)
    {
        LONG status = DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)&m_colorInfo);
        if (status == ERROR_SUCCESS)
        {
            bHDREnabled = m_colorInfo.advancedColorEnabled;
        }
    }
    return bHDREnabled;
}

//////////////////////////////////////////////////////////////////////////////////////
// get the rect of the monitor on the adapter/head so window can be created on it
bool CDx1xCommon::FindMonitorRect(UINT head, RECT* pMonRect)
{
    IDXGIFactory7* factory = nullptr;
    IDXGIAdapter4* adapter = nullptr;

    HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&factory));
    if (FAILED(hr)) return false;

    bool bFoundMonitor = false;
    for (UINT adapterIndex = 0; !bFoundMonitor; ++adapterIndex)
    {
        SafeRelease(adapter);
        hr = factory->EnumAdapters(adapterIndex, (IDXGIAdapter**)&adapter);
        if (FAILED(hr)) break;

        DXGI_ADAPTER_DESC desc;
        adapter->GetDesc(&desc);
        if (desc.VendorId == 0x10de)
        {
            *pMonRect = {0, 0, 0, 0};
            IDXGIOutput* dxgiOutput = nullptr;
            hr = adapter->EnumOutputs(head, &dxgiOutput);
            if (FAILED(hr)) break;

            DXGI_OUTPUT_DESC dxgiOutputDesc;
            hr = dxgiOutput->GetDesc(&dxgiOutputDesc);
            SafeRelease(dxgiOutput);
            if (FAILED(hr)) break;

            MONITORINFOEX mi = {};
            mi.cbSize = sizeof(mi);
            GetMonitorInfo(dxgiOutputDesc.Monitor, &mi);
            *pMonRect = mi.rcMonitor;
            bFoundMonitor = true;
        }
    }
    SafeRelease(adapter);
    SafeRelease(factory);
    m_bWindowOnNonNV = !bFoundMonitor;
    return bFoundMonitor;
}


//////////////////////////////////////////////////////////////////////////////////////
// find the adapter the window is on
IDXGIAdapter4* CDx1xCommon::GetDxgiAdapterForWnd(HWND hWndDisplay)
{
    IDXGIFactory7* factory = nullptr;
    IDXGIAdapter4* adapter = nullptr;
    IDXGIAdapter4* NVadapter = nullptr;

    HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&factory));
    if (FAILED(hr)) return nullptr;
    HMONITOR hMon = MonitorFromWindow(hWndDisplay, MONITOR_DEFAULTTONEAREST);
    bool bFoundMonitor = false;

    if (m_bWindowOnNonNV)
    {
        for (UINT adapterIndex = 0; !bFoundMonitor; ++adapterIndex)
        {
            hr = factory->EnumAdapters(adapterIndex, (IDXGIAdapter**)&adapter);
            if (FAILED(hr)) break;

            DXGI_ADAPTER_DESC desc;
            adapter->GetDesc(&desc);
            if (desc.VendorId == 0x10de)
            {
                NVadapter = adapter;
                break;
            }
            SafeRelease(adapter);
        }
    }
    for (UINT adapterIndex = 0; !bFoundMonitor; ++adapterIndex)
    {
        hr = factory->EnumAdapters(adapterIndex, (IDXGIAdapter**)&adapter);
        if (FAILED(hr)) break;

        for (UINT output_index = 0; !bFoundMonitor; ++output_index)
        {
            IDXGIOutput* output = nullptr;
            hr = adapter->EnumOutputs(output_index, &output);
            if (FAILED(hr)) break;

            DXGI_OUTPUT_DESC outputDesc;
            if (FAILED(output->GetDesc(&outputDesc))) continue;

            if (outputDesc.Monitor == hMon)
            {
                MONITORINFOEXW MonInfoW = {};
                MonInfoW.cbSize = sizeof(MonInfoW);
                GetMonitorInfoW(hMon, &MonInfoW);
                m_colorInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
                m_colorInfo.header.size = sizeof(DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO);
                UpdateMonitorIDs(MonInfoW.szDevice, &m_colorInfo.header.adapterId, &m_colorInfo.header.id);
                m_uMaxLuminance = GetMonitorStaticInfo(hMon, &m_rcMonitor);
                m_hMonitor = hMon;
                bFoundMonitor = true;
            }
            SafeRelease(output);
        }
        if (!bFoundMonitor || m_bWindowOnNonNV)
            SafeRelease(adapter);
    }
    SafeRelease(factory);
    return NVadapter ? NVadapter : adapter;
}

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
DXGI_COLOR_SPACE_TYPE CDx1xCommon::UpdateSwapChainColorSpace(HWND hWndDisplay, IDXGISwapChain4* pSwapChain, DXGI_FORMAT SwapFormat, bool bSupportHDR)
{
    HMONITOR hMon = MonitorFromWindow(hWndDisplay, MONITOR_DEFAULTTONEAREST);
    if (hMon != m_hMonitor)
    {
        // get info dynamically to handle hot plub
        MONITORINFOEXW MonInfoW = {};
        MonInfoW.cbSize = sizeof(MonInfoW);
        GetMonitorInfoW(hMon, &MonInfoW);
        m_colorInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
        m_colorInfo.header.size = sizeof(DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO);
        UpdateMonitorIDs(MonInfoW.szDevice, &m_colorInfo.header.adapterId, &m_colorInfo.header.id);
        m_uMaxLuminance = GetMonitorStaticInfo(hMon, &m_rcMonitor);
        m_hMonitor = hMon;
    }
    bool bSetHDRSpace = bSupportHDR && IsMonitorHDR();

    DXGI_COLOR_SPACE_TYPE cspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
    switch (SwapFormat)
    {
    case DXGI_FORMAT_R16G16B16A16_FLOAT:
        cspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
        break;
    case DXGI_FORMAT_R10G10B10A2_UNORM:
        cspace = (bSetHDRSpace ? DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
        break;
    case DXGI_FORMAT_NV12:
        cspace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
        break;
    case DXGI_FORMAT_P010:
        cspace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020;
        break;
    case DXGI_FORMAT_B8G8R8A8_UNORM:
    case DXGI_FORMAT_R8G8B8A8_UNORM:
        cspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
        break;
    }
    if (pSwapChain) pSwapChain->SetColorSpace1(cspace);
    return cspace;
}


//////////////////////////////////////////////////////////////////////////////
// get info on monitor - handles hot plug
bool CDx1xCommon::UpdateMonitorIDs(WCHAR* szMonitor, LUID* pLUID, UINT32* pID)
{
    LONG result;
    uint32_t num_path_array_elements = 0;
    uint32_t num_mode_info_array_elements = 0;
    DISPLAYCONFIG_PATH_INFO* path_infos = nullptr;
    DISPLAYCONFIG_MODE_INFO* mode_infos = nullptr;
    // Get all path infos.
    if (GetDisplayConfigBufferSizes(
        QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements,
        &num_mode_info_array_elements) != ERROR_SUCCESS)
    {
        return false;
    }
    path_infos = new DISPLAYCONFIG_PATH_INFO[num_path_array_elements];
    mode_infos = new DISPLAYCONFIG_MODE_INFO[num_mode_info_array_elements];
    result = QueryDisplayConfig(
        QDC_ONLY_ACTIVE_PATHS, &num_path_array_elements, path_infos,
        &num_mode_info_array_elements, mode_infos, nullptr);
    // Iterate of the path infos and see if we find one with a matching name.
    if (result == ERROR_SUCCESS)
    {
        for (uint32_t p = 0; p < num_path_array_elements; p++)
        {
            DISPLAYCONFIG_SOURCE_DEVICE_NAME device_name;
            device_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
            device_name.header.size = sizeof(device_name);
            device_name.header.adapterId = path_infos[p].sourceInfo.adapterId;
            device_name.header.id = path_infos[p].sourceInfo.id;
            if (DisplayConfigGetDeviceInfo(&device_name.header) == ERROR_SUCCESS)
            {
                if (wcscmp(szMonitor, device_name.viewGdiDeviceName) == 0)
                {
                    *pLUID = path_infos[p].targetInfo.adapterId;
                    *pID = path_infos[p].targetInfo.id;
                    delete[] path_infos;
                    delete[] mode_infos;
                    return true;
                }
            }
        }
    }
    if (path_infos)
        delete[] path_infos;
    if (mode_infos)
        delete[] mode_infos;
    return false;
}

UINT CDx1xCommon::GetMonitorStaticInfo(HMONITOR hMon, RECT* pRcMon)
{
    UINT            maxLuminance = 650;
    bool            bMonitorFound = false;
    IDXGIFactory7* factory = nullptr;
    IDXGIAdapter4* adapter = nullptr;
    IDXGIOutput6* dxgiOutput6 = nullptr;
    IDXGIOutput* dxgiOutput = nullptr;

    HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&factory));
    if (SUCCEEDED(hr))
    {
        for (UINT adapterIndex = 0; !bMonitorFound; ++adapterIndex)
        {
            hr = factory->EnumAdapters(adapterIndex, (IDXGIAdapter**)&adapter);
            if (FAILED(hr)) break;

            for (int monIndx = 0; !bMonitorFound; monIndx++)
            {
                hr = adapter->EnumOutputs(monIndx, &dxgiOutput);
                if (FAILED(hr)) break;

                hr = dxgiOutput->QueryInterface(__uuidof(IDXGIOutput6), (LPVOID*)&dxgiOutput6);
                if (FAILED(hr)) break;

                DXGI_OUTPUT_DESC1 DXGIOutputDesc = {};
                dxgiOutput6->GetDesc1(&DXGIOutputDesc);
                if (DXGIOutputDesc.Monitor == hMon)
                {
                    if (DXGIOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
                    {
                        maxLuminance = (UINT)DXGIOutputDesc.MaxLuminance;
                    }
                    MONITORINFOEX mi = {};
                    mi.cbSize = sizeof(mi);
                    GetMonitorInfo(DXGIOutputDesc.Monitor, &mi);
                    *pRcMon = mi.rcMonitor;
                    bMonitorFound = true;
                }
                if (dxgiOutput6) dxgiOutput6->Release();
                if (dxgiOutput) dxgiOutput->Release();
                dxgiOutput6 = nullptr;
                dxgiOutput = nullptr;
            }
            if (adapter) adapter->Release();
            adapter = nullptr;
        }
    }
    return maxLuminance;
}

