//----------------------------------------------------------------------------------
// File:        CDx12NGXTrueHDR.h
// SDK Version: 1.0.2
//
// SPDX-FileCopyrightText: Copyright (c) 2023 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.
//
//----------------------------------------------------------------------------------

///////////////////////////////////////////
// CDx12NGXTrueHDR.h
// This is a wrapper class to simplify adding NGX TrueHDR to a DX12 app

// This class can be used as is or the code can be incorporated into the app.
// There is a sync class the needs to be adjusted. See below.
// 
// Unzip the RTX_Video_SDK. SDK files are in Include and Lib
// In the SDK Samples it uses ../../Include, and ../../Lib/X64 to access the SDK
//
// normal dxgi present
//      video decode->vpblt to RGB->TrueHDR to HDR swap back buffer->Present
//      app render RGB->TrueHDR to HDR swap back buffer->Present
// 
// dx12 sync requirement
//      DX12 requires fences be used to sync between resources.
//      An example of a CDx12SyncObject class supporting generalized fence usage is at the end of CDx12NGXTrueHDR.cpp
//      Usage:
//          Insert wait for resource fences before starting decode/rendering
//          Signal on the command queue when done with decode/rendering
//          Pass the source/dest fences into Evaluate
//          It will insert waits before starting evaluate, and signals when done.
//          Insert wait before present and signal after doing present.

#pragma once

#include "CDx1xNGX_common.h"

class CDx12SyncObject;

class CDx12NGXTrueHDR
{
public:
    CDx12NGXTrueHDR() {}

    HRESULT     CreateFeature(ID3D12Device* pD3DDevice, UINT uGPUNodeMask, UINT uGPUVisibleNodeMask);
    HRESULT     EvaluateFeature(ID3D12Resource* Output, CDx12SyncObject* pDstSyncObj, RECT RectOutput,
                                ID3D12Resource* Input,  CDx12SyncObject* pSrcSyncObj, RECT RectInput,
                                UINT Contrast,      // 0 to 200 for HDR Contrast   (default 100)
                                UINT Saturation,    // 0 to 200 for HDR Saturation (default 100)
                                UINT MiddleGray,    // 10 to 100 for HDR MiddleGray (default  50
                                UINT MaxLuminance); // 400 to 2000 for Monitor MaxLuminance (default 1000)
    void        ReleaseFeature();

private:
    ID3D12Device*               m_D3D12Device           = nullptr;
    ID3D12CommandAllocator*     m_commandAllocatorNGX   = nullptr;
    ID3D12CommandQueue*         m_commandQueueNGX       = nullptr;
    ID3D12GraphicsCommandList*  m_commandListNGX        = nullptr;
    CDx12SyncObject*            m_CmdQSyncObjNGX        = nullptr;
    UINT                        m_uGPUNodeMask          = 1;
    UINT                        m_uGPUVisibleNodeMask   = 1;

    bool                        m_bNGXInitialized       = false;
    NVSDK_NGX_Parameter*        m_ngxParameters         = nullptr;
    NVSDK_NGX_Handle*           m_TrueHDRFeature        = nullptr;
    RECT                        m_NGXSrcRect            = {};
    RECT                        m_NGXDstRect            = {};

    bool                        m_bNGXInitializedDstTmp = false;
    ID3D12Resource*             m_pDstTmpNGX            = nullptr;
    UINT64                      m_uDstTmpWidth          = 0;
    UINT64                      m_uDstTmpHeight         = 0;
};

/////////////////////////////////////////////////////////////////
// Sample App for DX12 DXGI Present
//
// Initialization:
//      Create ID3D12Device and determine GPU Node Mask and Visible Node Mask
//      Call CreateFeature to verify it is supported
//      Create DXGI_FORMAT_R8G8B8A8_UNORM ID3D12Resource for source input to feature
//          For an array, create one resource for each entry in the array (array of textures)
//          Set an input rect to define you source size.
//      Create a swap chain in HDR, either in DXGI_FORMAT_R10G10B10A2_UNORM or DXGI_FORMAT_R16G16B16A16_FLOAT.
//          Have several backbuffers, use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL or DXGI_SWAP_EFFECT_FLIP_DISCARD.
//          Set an output rect to define you destination size.
//      If possible create output buffers with D3D11_BIND_UNORDERED_ACCESS.
//
// Execution:
//      Render into source RGB.
//          For Video apps with YUV surfaces, use VPBlt from the YUV to the source RGB
//      In the commandQueue used for rendering, issue a signal.
//      Call EvaluateFeature with DX12 pointers, 
//          source RGB, source sync object, source rect, 
//          dest HDR, dest sync object, dest rect,
//          other TrueHDR parameters,
//          NOTE: Evaluate will insert a wait into its CommandQueue for both source and dest sync objects,
//                then insert a signal into both.
//      Present dest HDR back buffer.
//          In the commandQueue used for present, issue a wait on both sync objects before the present.
//          Then issue a wait on both sync objects after the Present.
//
// Shutdown:
//      Call ReleaseFeature from a shut down function, not the class destructor.
//          There is a critical section used by NGX that the runtime deletes in a destructor.
/////////////////////////////////////////////////////////////////
// Using both DLVSR and TrueHDR
//      DLVSR and TrueHDR can be run sequentially in that order.
//      TrueHDR must be last as it outputs to HDR.
//      Create an array of RGB textures for output of DLVSR and input to TrueHDR
/////////////////////////////////////////////////////////////////

#if 0
/////////////////////////////////////////////////////////////////
// Examples of code to use
// 
//////////////////////
// In .h
    CDx12NGXTrueHDR*                                m_CNGXTrueHDR               = nullptr;
    RECT                                            m_rcNGXInput                = {};
    RECT                                            m_rcNGXOutput = {};
    ID3D12Resource*                                 m_NGXInputBuffer            = nullptr;
    CDx12SyncObject*                                m_NGXInputSyncObj           = nullptr;

//////////////////////
// In Initialization:
// create feature after creating your D3D12Device
m_pCNGXTrueHDR = new CDx12NGXTrueHDR;
hr = m_pCNGXTrueHDR->CreateFeature(m_pD3D12Device, m_GPUNodeMask, m_GPUVisibleNodeMask);
if (FAILED(hr))
{
    SafeDelete(m_pCNGXTrueHDR);
    if (FAILED(hr)) return hr;
}
// create sync object
m_NGXInputSyncObj = new CDx12SyncObject(m_D3D12Device);

// create swap chain with HDR format
scd.Format = (bUseABGR10) ? DXGI_FORMAT_R10G10B10A2_UNORM : DXGI_FORMAT_R16G16B16A16_FLOAT;
// set swap chain color space
    if (scd.Format == DXGI_FORMAT_R16G16B16A16_FLOAT) m_pSwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709);
    if (scd.Format == DXGI_FORMAT_R10G10B10A2_UNORM) m_pSwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);

// set input size for NGX
m_rcNGXInput = { 0, 0, (LONG)scd.Width, (LONG)scd.Height };
// set output size for NGX
m_rcNGXOutput = { 0, 0, (LONG)scd.Width, (LONG)scd.Height };
// set vpBlt output size to input size of NGX 
m_rcVpOutput = m_rcNGXInput;
// set vpBlt output format to DXGI_FORMAT_R8G8B8A8_UNORM 
m_FmtVpOutput = DXGI_FORMAT_R8G8B8A8_UNORM;

// create a resource for output from vpBlt for input to NGX. Use format DXGI_FORMAT_R8G8B8A8_UNORM and D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
D3D12_RESOURCE_DESC textureDesc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM, m_rcNGXInput.right, m_rcNGXInput.bottom, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
CD3DX12_HEAP_PROPERTIES heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT, m_GPUNodeMask, m_GPUVisibleNodeMask);
hr = m_D3D12Device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&m_NGXInputBuffer));

// optionally get monitor max luminance and verify it is in hdr mode
DXGI_OUTPUT_DESC1 DxgiOutputDesc = {};
pDXGIOutput6->GetDesc1(&DxgiOutputDesc);
m_uMaxLuminance = (UINT)((DxgiOutputDesc.MaxLuminance >= 400.0f) ? DxgiOutputDesc.MaxLuminance : 1000);
if (DxgiOutputDesc.ColorSpace != DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
    return E_FAIL; //MonitorNotInHDRMode



//////////////////////
// In Update BackBuffer for Present
// VpBlt from decode output to NGX input buffer, RGB apps skip this.
hr = VpBlt(pDecodeRTInUse, pSrcSyncObj, m_pNGXInputBuffer, m_NGXInputSyncObj);
// Apply TrueHDR with EvaluateFeature
hr = m_pCNGXTrueHDR->EvaluateFeature(pSwapBackBuffer, pSwapSyncObj, m_rcNGXOutput,
    m_pNGXInputBuffer, m_NGXInputSyncObj, m_rcNGXInput,
    Contrast, Saturation, MiddleGray, MaxLuminance);


//////////////////////
// In Deinitialization (call prior to destructor):
if (m_pCNGXTrueHDR)
{
    m_pCNGXTrueHDR->ReleaseFeature();
    SafeDelete(m_pCNGXTrueHDR);
}
/////////////////////////////////////////////////////////////////
#endif