//----------------------------------------------------------------------------------
// File:        CDx12Sync.cpp
// 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.
//
//----------------------------------------------------------------------------------

///////////////////////////////////////////
// CDx12Sync.cpp
//
// Implementation of DX12 Sync Object wrapping DX12 Fence
//

#include <d3d12.h>

#include "CDX1x_Common.h"
#include "CDx12Sync.h"
#include "d3dx12.h"

//////////////////////////////////////////////////////////////////////////////////////
CDx12SyncObject::CDx12SyncObject(ID3D12Device* pD3D12Device)
    : m_fenceSignaledValue(0)
    , m_fence(nullptr)
{
    pD3D12Device->CreateFence(m_fenceSignaledValue, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), (void**)(&m_fence));
}

//////////////////////////////////////////////////////////////////////////////////////
CDx12SyncObject::~CDx12SyncObject()
{
    SafeRelease(m_fence);
}

//////////////////////////////////////////////////////////////////////////////////////
UINT64 CDx12SyncObject::SignalFence(ID3D12CommandQueue* pCmdQ)
{
    // Signal and increment the fence value.
    pCmdQ->Signal(m_fence, ++m_fenceSignaledValue);
    return m_fenceSignaledValue;
}


//////////////////////////////////////////////////////////////////////////////////////
void CDx12SyncObject::WaitForFence(ID3D12CommandQueue* pCmdQ)
{
    if (m_fence->GetCompletedValue() < m_fenceSignaledValue)
    {
        pCmdQ->Wait(m_fence, m_fenceSignaledValue);
    }
}


//////////////////////////////////////////////////////////////////////////////////////
void CDx12SyncObject::WaitForCPUFence()
{
    if (m_fence->GetCompletedValue() < m_fenceSignaledValue)
    {
        m_fence->SetEventOnCompletion(m_fenceSignaledValue, nullptr);       // returns when value is reached
    }
}

//////////////////////////////////////////////////////////////////////////////////////
void CDx12SyncObject::WaitForFenceValue(ID3D12CommandQueue* pCmdQ, UINT64 waitValue)
{
    if (m_fence->GetCompletedValue() < waitValue)
    {
        pCmdQ->Wait(m_fence, waitValue);
    }
}

//////////////////////////////////////////////////////////////////////////////////////
void CDx12SyncObject::WaitForCPUFenceValue(UINT64 waitValue)
{
    if (m_fence->GetCompletedValue() < waitValue)
    {
        m_fence->SetEventOnCompletion(m_fenceSignaledValue, nullptr);       // returns when value is reached
    }
}


//////////////////////////////////////////////////////////////////////////////////////
// CDx12SyncObjectArray
//////////////////////////////////////////////////////////////////////////////////////
CDx12SyncObjectArray::CDx12SyncObjectArray(ID3D12Device* pD3D12Device, UINT maxIndex)
    : m_aSyncObjects(nullptr)
    , m_TotalIndexed(maxIndex)
{
    m_aSyncObjects = new PCDx12SyncObject[m_TotalIndexed];
    for (UINT n = 0; n < m_TotalIndexed; n++)
    {
        m_aSyncObjects[n] = new CDx12SyncObject(pD3D12Device);
    }
}

//////////////////////////////////////////////////////////////////////////////////////
CDx12SyncObjectArray::~CDx12SyncObjectArray()
{
    if (m_aSyncObjects)
    {
        for (UINT n = 0; n < m_TotalIndexed; n++)
        {
            SafeDelete(m_aSyncObjects[n]);
            if (m_aSyncObjects[n])
                delete m_aSyncObjects[n];
        }
        SafeDeleteA(m_aSyncObjects);
    }
}



//////////////////////////////////////////////////////////////////////////////////////
UINT64 CDx12SyncObjectArray::SignalFenceIndexed(ID3D12CommandQueue* pCmdQ, UINT index)
{
    if (m_aSyncObjects && index < m_TotalIndexed && m_aSyncObjects[index])
        return m_aSyncObjects[index]->SignalFence(pCmdQ);
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////
void CDx12SyncObjectArray::WaitForFenceIndexed(ID3D12CommandQueue* pCmdQ, UINT index)
{
    if (m_aSyncObjects && index < m_TotalIndexed && m_aSyncObjects[index])
        m_aSyncObjects[index]->WaitForFence(pCmdQ);
}

//////////////////////////////////////////////////////////////////////////////////////
void CDx12SyncObjectArray::WaitForCPUFenceIndexed(UINT index)
{
    if (m_aSyncObjects && index < m_TotalIndexed && m_aSyncObjects[index])
        m_aSyncObjects[index]->WaitForCPUFence();
}

//////////////////////////////////////////////////////////////////////////////////////
CDx12SyncObject* CDx12SyncObjectArray::GetFenceIndexed(UINT index)
{
    if (m_aSyncObjects && index < m_TotalIndexed && m_aSyncObjects[index])
        return m_aSyncObjects[index];
    return nullptr;
}

