// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Platform/CriticalSection.h"
#include "CommandAllocatorPoolDX12.h"
#include "../IncludeDirectXHeaders.h"
#if GRAPHICS_API_DIRECTX12
class GPUDeviceDX12;
class GPUContextDX12;
class CommandQueueDX12;
///
/// Wraps a fence object and provides functionality for common operations for GPU/CPU operations synchronization.
///
class FenceDX12
{
private:
uint64 _currentValue;
uint64 _lastSignaledValue;
uint64 _lastCompletedValue;
HANDLE _event;
ID3D12Fence* _fence;
GPUDeviceDX12* _device;
CriticalSection _locker;
public:
FenceDX12(GPUDeviceDX12* device);
public:
FORCE_INLINE uint64 GetCurrentValue() const
{
return _currentValue;
}
FORCE_INLINE uint64 GetLastSignaledValue() const
{
return _lastSignaledValue;
}
FORCE_INLINE uint64 GetLastCompletedValue() const
{
return _lastCompletedValue;
}
public:
bool Init();
void Release();
uint64 Signal(CommandQueueDX12* queue);
void WaitGPU(CommandQueueDX12* queue, uint64 value);
void WaitCPU(uint64 value);
bool IsFenceComplete(uint64 value);
};
///
/// GPU commands execution sync point for DirectX 12.
///
struct SyncPointDX12
{
FenceDX12* Fence;
uint64 Value;
SyncPointDX12()
: Fence(nullptr)
, Value(0)
{
}
SyncPointDX12(FenceDX12* fence, uint64 value)
: Fence(fence)
, Value(value)
{
}
SyncPointDX12(const SyncPointDX12& other)
: Fence(other.Fence)
, Value(other.Value)
{
}
SyncPointDX12& operator=(const SyncPointDX12& other)
{
Fence = other.Fence;
Value = other.Value;
return *this;
}
bool operator!() const
{
return Fence == nullptr;
}
bool IsValid() const
{
return Fence != nullptr;
}
bool IsOpen() const
{
return Value == Fence->GetCurrentValue();
}
bool IsComplete() const
{
return Fence->IsFenceComplete(Value);
}
void WaitForCompletion() const
{
Fence->WaitCPU(Value);
}
};
class CommandQueueDX12
{
friend GPUDeviceDX12;
friend GPUContextDX12;
private:
GPUDeviceDX12* _device;
ID3D12CommandQueue* _commandQueue;
const D3D12_COMMAND_LIST_TYPE _type;
CommandAllocatorPoolDX12 _allocatorPool;
FenceDX12 _fence;
public:
CommandQueueDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type);
~CommandQueueDX12();
public:
FORCE_INLINE bool IsReady() const
{
return _commandQueue != nullptr;
}
FORCE_INLINE ID3D12CommandQueue* GetCommandQueue() const
{
return _commandQueue;
}
FORCE_INLINE CommandAllocatorPoolDX12& GetAllocatorPool()
{
return _allocatorPool;
}
FORCE_INLINE SyncPointDX12 GetSyncPoint()
{
return SyncPointDX12(&_fence, _fence.GetCurrentValue());
}
public:
///
/// Init resources
///
/// True if cannot init, otherwise false
bool Init();
///
/// Cleanup all stuff
///
void Release();
public:
///
/// Stalls the execution on current thread to wait for the GPU to step over given fence value.
///
/// The fence value to wait.
void WaitForFence(uint64 fenceValue);
///
/// Stalls the execution on current thread to wait for the GPU to finish it's job.
///
void WaitForGPU();
public:
///
/// Executes a command list.
///
/// The command list to execute.
/// The fence value after execution (can be used to wait for it to sync parallel execution).
uint64 ExecuteCommandList(ID3D12CommandList* list);
///
/// Requests new clean allocator to use.
///
/// The allocator.
ID3D12CommandAllocator* RequestAllocator();
///
/// Discards used allocator.
///
/// Sync fence value on reset event.
/// The allocator to discard.
void DiscardAllocator(uint64 fenceValueForReset, ID3D12CommandAllocator* allocator);
};
#endif