You're breathtaking!
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "CommandAllocatorPoolDX12.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
|
||||
CommandAllocatorPoolDX12::CommandAllocatorPoolDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type)
|
||||
: _type(type)
|
||||
, _device(device)
|
||||
{
|
||||
}
|
||||
|
||||
CommandAllocatorPoolDX12::~CommandAllocatorPoolDX12()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
ID3D12CommandAllocator* CommandAllocatorPoolDX12::RequestAllocator(uint64 completedFenceValue)
|
||||
{
|
||||
ScopeLock lock(_locker);
|
||||
|
||||
ID3D12CommandAllocator* allocator = nullptr;
|
||||
|
||||
if (_ready.HasItems())
|
||||
{
|
||||
PoolPair& firstPair = _ready.First();
|
||||
|
||||
if (firstPair.First <= completedFenceValue)
|
||||
{
|
||||
allocator = firstPair.Second;
|
||||
VALIDATE_DIRECTX_RESULT(allocator->Reset());
|
||||
_ready.RemoveAtKeepOrder(0);
|
||||
}
|
||||
}
|
||||
|
||||
// If no allocators were ready to be reused, create a new one
|
||||
if (allocator == nullptr)
|
||||
{
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommandAllocator(_type, IID_PPV_ARGS(&allocator)));
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
Char name[32];
|
||||
swprintf(name, 32, L"CommandAllocator %u", _pool.Count());
|
||||
allocator->SetName(name);
|
||||
#endif
|
||||
_pool.Add(allocator);
|
||||
}
|
||||
|
||||
return allocator;
|
||||
}
|
||||
|
||||
void CommandAllocatorPoolDX12::DiscardAllocator(uint64 fenceValue, ID3D12CommandAllocator* allocator)
|
||||
{
|
||||
ScopeLock lock(_locker);
|
||||
|
||||
// That fence value indicates we are free to reset the allocator
|
||||
_ready.Add(PoolPair(fenceValue, allocator));
|
||||
}
|
||||
|
||||
void CommandAllocatorPoolDX12::Release()
|
||||
{
|
||||
for (int32 i = 0; i < _pool.Count(); i++)
|
||||
{
|
||||
DX_SAFE_RELEASE_CHECK(_pool[i], 0);
|
||||
}
|
||||
|
||||
_pool.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "Engine/Core/Types/Pair.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
|
||||
class GPUDeviceDX12;
|
||||
|
||||
class CommandAllocatorPoolDX12
|
||||
{
|
||||
private:
|
||||
|
||||
typedef Pair<uint64, ID3D12CommandAllocator*> PoolPair;
|
||||
|
||||
const D3D12_COMMAND_LIST_TYPE _type;
|
||||
GPUDeviceDX12* _device;
|
||||
Array<ID3D12CommandAllocator*> _pool;
|
||||
Array<PoolPair> _ready;
|
||||
CriticalSection _locker;
|
||||
|
||||
public:
|
||||
|
||||
CommandAllocatorPoolDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type);
|
||||
~CommandAllocatorPoolDX12();
|
||||
|
||||
public:
|
||||
|
||||
FORCE_INLINE uint32 Size() const
|
||||
{
|
||||
return _pool.Count();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
ID3D12CommandAllocator* RequestAllocator(uint64 completedFenceValue);
|
||||
|
||||
void DiscardAllocator(uint64 fenceValue, ID3D12CommandAllocator* allocator);
|
||||
|
||||
void Release();
|
||||
};
|
||||
|
||||
#endif
|
||||
169
Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp
Normal file
169
Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "CommandQueueDX12.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
|
||||
FenceDX12::FenceDX12(GPUDeviceDX12* device)
|
||||
: _currentValue(1)
|
||||
, _lastSignaledValue(0)
|
||||
, _lastCompletedValue(0)
|
||||
, _device(device)
|
||||
{
|
||||
}
|
||||
|
||||
bool FenceDX12::Init()
|
||||
{
|
||||
LOG_DIRECTX_RESULT(_device->GetDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&_fence)));
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
_fence->SetName(TEXT("Fence"));
|
||||
#endif
|
||||
|
||||
_event = CreateEvent(nullptr, false, false, nullptr);
|
||||
ASSERT(_event != INVALID_HANDLE_VALUE);
|
||||
return false;
|
||||
}
|
||||
|
||||
void FenceDX12::Release()
|
||||
{
|
||||
CloseHandle(_event);
|
||||
_event = nullptr;
|
||||
|
||||
_fence->Release();
|
||||
_fence = nullptr;
|
||||
}
|
||||
|
||||
uint64 FenceDX12::Signal(CommandQueueDX12* queue)
|
||||
{
|
||||
ScopeLock lock(_locker);
|
||||
ASSERT(_lastSignaledValue != _currentValue);
|
||||
|
||||
// Insert signal into command queue
|
||||
LOG_DIRECTX_RESULT(queue->GetCommandQueue()->Signal(_fence, _currentValue));
|
||||
|
||||
// Update state
|
||||
_lastSignaledValue = _currentValue;
|
||||
_lastCompletedValue = _fence->GetCompletedValue();
|
||||
|
||||
// Increment the current value
|
||||
_currentValue++;
|
||||
|
||||
return _lastSignaledValue;
|
||||
}
|
||||
|
||||
void FenceDX12::WaitGPU(CommandQueueDX12* queue, uint64 value)
|
||||
{
|
||||
// Insert wait into command queue
|
||||
LOG_DIRECTX_RESULT(queue->GetCommandQueue()->Wait(_fence, value));
|
||||
}
|
||||
|
||||
void FenceDX12::WaitCPU(uint64 value)
|
||||
{
|
||||
if (IsFenceComplete(value))
|
||||
return;
|
||||
|
||||
ScopeLock lock(_locker);
|
||||
|
||||
_fence->SetEventOnCompletion(value, _event);
|
||||
WaitForSingleObject(_event, INFINITE);
|
||||
|
||||
_lastCompletedValue = _fence->GetCompletedValue();
|
||||
}
|
||||
|
||||
bool FenceDX12::IsFenceComplete(uint64 value)
|
||||
{
|
||||
ASSERT(value <= _currentValue);
|
||||
|
||||
if (value > _lastCompletedValue)
|
||||
{
|
||||
_lastCompletedValue = _fence->GetCompletedValue();
|
||||
}
|
||||
|
||||
return value <= _lastCompletedValue;
|
||||
}
|
||||
|
||||
CommandQueueDX12::CommandQueueDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type)
|
||||
: _device(device)
|
||||
, _commandQueue(nullptr)
|
||||
, _type(type)
|
||||
, _allocatorPool(device, type)
|
||||
, _fence(device)
|
||||
{
|
||||
}
|
||||
|
||||
CommandQueueDX12::~CommandQueueDX12()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
bool CommandQueueDX12::Init()
|
||||
{
|
||||
ASSERT(_device != nullptr);
|
||||
ASSERT(!IsReady());
|
||||
ASSERT(_allocatorPool.Size() == 0);
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC desc;
|
||||
desc.Type = _type;
|
||||
desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
|
||||
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
desc.NodeMask = 0;
|
||||
HRESULT result = _device->GetDevice()->CreateCommandQueue(&desc, IID_PPV_ARGS(&_commandQueue));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
_commandQueue->SetName(TEXT("CommandQueueDX12::CommandQueue"));
|
||||
#endif
|
||||
|
||||
if (_fence.Init())
|
||||
return true;
|
||||
|
||||
ASSERT(IsReady());
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandQueueDX12::Release()
|
||||
{
|
||||
if (_commandQueue == nullptr)
|
||||
return;
|
||||
|
||||
_allocatorPool.Release();
|
||||
_fence.Release();
|
||||
|
||||
_commandQueue->Release();
|
||||
_commandQueue = nullptr;
|
||||
}
|
||||
|
||||
void CommandQueueDX12::WaitForFence(uint64 fenceValue)
|
||||
{
|
||||
_fence.WaitCPU(fenceValue);
|
||||
}
|
||||
|
||||
void CommandQueueDX12::WaitForGPU()
|
||||
{
|
||||
const uint64 value = _fence.Signal(this);
|
||||
_fence.WaitCPU(value);
|
||||
}
|
||||
|
||||
uint64 CommandQueueDX12::ExecuteCommandList(ID3D12CommandList* list)
|
||||
{
|
||||
VALIDATE_DIRECTX_RESULT((static_cast<ID3D12GraphicsCommandList*>(list))->Close());
|
||||
|
||||
_commandQueue->ExecuteCommandLists(1, &list);
|
||||
|
||||
return _fence.Signal(this);
|
||||
}
|
||||
|
||||
ID3D12CommandAllocator* CommandQueueDX12::RequestAllocator()
|
||||
{
|
||||
const uint64 completedFence = _fence.GetLastCompletedValue();
|
||||
return _allocatorPool.RequestAllocator(completedFence);
|
||||
}
|
||||
|
||||
void CommandQueueDX12::DiscardAllocator(uint64 fenceValue, ID3D12CommandAllocator* allocator)
|
||||
{
|
||||
_allocatorPool.DiscardAllocator(fenceValue, allocator);
|
||||
}
|
||||
|
||||
#endif
|
||||
244
Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.h
Normal file
244
Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.h
Normal file
@@ -0,0 +1,244 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "CommandAllocatorPoolDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
class GPUDeviceDX12;
|
||||
class GPUContextDX12;
|
||||
class CommandQueueDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a fence object and provides functionality for common operations for GPU/CPU operations synchronization.
|
||||
/// </summary>
|
||||
class FenceDX12
|
||||
{
|
||||
private:
|
||||
|
||||
uint64 _currentValue;
|
||||
uint64 _lastSignaledValue;
|
||||
uint64 _lastCompletedValue;
|
||||
HANDLE _event;
|
||||
ID3D12Fence* _fence;
|
||||
GPUDeviceDX12* _device;
|
||||
CriticalSection _locker;
|
||||
|
||||
public:
|
||||
|
||||
FenceDX12(GPUDeviceDX12* device);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current fence value.
|
||||
/// </summary>
|
||||
/// <returns>The current fence value.</returns>
|
||||
FORCE_INLINE uint64 GetCurrentValue() const
|
||||
{
|
||||
return _currentValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last signaled fence value.
|
||||
/// </summary>
|
||||
/// <returns>The last signaled fence value.</returns>
|
||||
FORCE_INLINE uint64 GetLastSignaledValue() const
|
||||
{
|
||||
return _lastSignaledValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last completed fence value.
|
||||
/// </summary>
|
||||
/// <returns>The last completed fence value.</returns>
|
||||
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);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU commands execution sync point for DirectX 12.
|
||||
/// </summary>
|
||||
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:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="device">Graphics Device handle</param>
|
||||
/// <param name="type">Command queue type</param>
|
||||
CommandQueueDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type);
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~CommandQueueDX12();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Checks if command queue is ready for work
|
||||
/// </summary>
|
||||
/// <returns>True if is ready for work</returns>
|
||||
FORCE_INLINE bool IsReady() const
|
||||
{
|
||||
return _commandQueue != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 command queue object
|
||||
/// </summary>
|
||||
/// <returns>DirectX 12 command queue</returns>
|
||||
FORCE_INLINE ID3D12CommandQueue* GetCommandQueue() const
|
||||
{
|
||||
return _commandQueue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command lists allocator pool.
|
||||
/// </summary>
|
||||
/// <returns>The allocator.</returns>
|
||||
FORCE_INLINE CommandAllocatorPoolDX12& GetAllocatorPool()
|
||||
{
|
||||
return _allocatorPool;
|
||||
}
|
||||
|
||||
FORCE_INLINE SyncPointDX12 GetSyncPoint()
|
||||
{
|
||||
return SyncPointDX12(&_fence, _fence.GetCurrentValue());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init resources
|
||||
/// </summary>
|
||||
/// <returns>True if cannot init, otherwise false</returns>
|
||||
bool Init();
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup all stuff
|
||||
/// </summary>
|
||||
void Release();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Stalls the execution on current thread to wait for the GPU to step over given fence value.
|
||||
/// </summary>
|
||||
/// <param name="fenceValue">The fence value to wait.</param>
|
||||
void WaitForFence(uint64 fenceValue);
|
||||
|
||||
/// <summary>
|
||||
/// Stalls the execution on current thread to wait for the GPU to finish it's job.
|
||||
/// </summary>
|
||||
void WaitForGPU();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Executes a command list.
|
||||
/// </summary>
|
||||
/// <param name="list">The command list to execute.</param>
|
||||
/// <returns>The fence value after execution (can be sued to wait for it to sync parallel execution).</returns>
|
||||
uint64 ExecuteCommandList(ID3D12CommandList* list);
|
||||
|
||||
/// <summary>
|
||||
/// Requests new clean allocator to use.
|
||||
/// </summary>
|
||||
/// <returns>The allocator.</returns>
|
||||
ID3D12CommandAllocator* RequestAllocator();
|
||||
|
||||
/// <summary>
|
||||
/// Discards used allocator.
|
||||
/// </summary>
|
||||
/// <param name="fenceValueForReset">Sync fence value on reset event.</param>
|
||||
/// <param name="allocator">The allocator to discard.</param>
|
||||
void DiscardAllocator(uint64 fenceValueForReset, ID3D12CommandAllocator* allocator);
|
||||
};
|
||||
|
||||
#endif
|
||||
208
Source/Engine/GraphicsDevice/DirectX/DX12/CommandSignatureDX12.h
Normal file
208
Source/Engine/GraphicsDevice/DirectX/DX12/CommandSignatureDX12.h
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
class CommandSignatureDX12;
|
||||
|
||||
class IndirectParameterDX12
|
||||
{
|
||||
friend CommandSignatureDX12;
|
||||
|
||||
protected:
|
||||
|
||||
D3D12_INDIRECT_ARGUMENT_DESC _parameter;
|
||||
|
||||
public:
|
||||
|
||||
IndirectParameterDX12()
|
||||
{
|
||||
_parameter.Type = (D3D12_INDIRECT_ARGUMENT_TYPE)0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void Draw()
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
|
||||
}
|
||||
|
||||
void DrawIndexed()
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
|
||||
}
|
||||
|
||||
void Dispatch()
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
|
||||
}
|
||||
|
||||
void VertexBufferView(UINT slot)
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW;
|
||||
_parameter.VertexBuffer.Slot = slot;
|
||||
}
|
||||
|
||||
void IndexBufferView()
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW;
|
||||
}
|
||||
|
||||
void Constant(UINT rootParameterIndex, UINT destOffsetIn32BitValues, UINT num32BitValuesToSet)
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
|
||||
_parameter.Constant.RootParameterIndex = rootParameterIndex;
|
||||
_parameter.Constant.DestOffsetIn32BitValues = destOffsetIn32BitValues;
|
||||
_parameter.Constant.Num32BitValuesToSet = num32BitValuesToSet;
|
||||
}
|
||||
|
||||
void ConstantBufferView(UINT rootParameterIndex)
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW;
|
||||
_parameter.ConstantBufferView.RootParameterIndex = rootParameterIndex;
|
||||
}
|
||||
|
||||
void ShaderResourceView(UINT rootParameterIndex)
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW;
|
||||
_parameter.ShaderResourceView.RootParameterIndex = rootParameterIndex;
|
||||
}
|
||||
|
||||
void UnorderedAccessView(UINT rootParameterIndex)
|
||||
{
|
||||
_parameter.Type = D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW;
|
||||
_parameter.UnorderedAccessView.RootParameterIndex = rootParameterIndex;
|
||||
}
|
||||
|
||||
const D3D12_INDIRECT_ARGUMENT_DESC& GetDesc() const
|
||||
{
|
||||
return _parameter;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandSignatureDX12 : public GPUResourceDX12<GPUResource>
|
||||
{
|
||||
protected:
|
||||
|
||||
ID3D12CommandSignature* _signature;
|
||||
Array<IndirectParameterDX12, FixedAllocation<4>> _parameters;
|
||||
|
||||
public:
|
||||
|
||||
CommandSignatureDX12(GPUDeviceDX12* device, int32 numParams)
|
||||
: GPUResourceDX12<GPUResource>(device, TEXT("CommandSignatureDX12"))
|
||||
, _signature(nullptr)
|
||||
{
|
||||
_parameters.Resize(numParams);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
FORCE_INLINE IndirectParameterDX12& At(int32 entryIndex)
|
||||
{
|
||||
return _parameters[entryIndex];
|
||||
}
|
||||
|
||||
FORCE_INLINE IndirectParameterDX12& operator[](int32 entryIndex)
|
||||
{
|
||||
return _parameters[entryIndex];
|
||||
}
|
||||
|
||||
FORCE_INLINE const IndirectParameterDX12& operator[](int32 entryIndex) const
|
||||
{
|
||||
return _parameters[entryIndex];
|
||||
}
|
||||
|
||||
FORCE_INLINE ID3D12CommandSignature* GetSignature() const
|
||||
{
|
||||
return _signature;
|
||||
}
|
||||
|
||||
void Finalize(ID3D12RootSignature* rootSignature = nullptr)
|
||||
{
|
||||
if (_signature != nullptr)
|
||||
return;
|
||||
|
||||
UINT byteStride = 0;
|
||||
bool requiresRootSignature = false;
|
||||
const int32 numParameters = _parameters.Count();
|
||||
|
||||
for (int32 i = 0; i < numParameters; i++)
|
||||
{
|
||||
switch (_parameters[i].GetDesc().Type)
|
||||
{
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW:
|
||||
byteStride += sizeof(D3D12_DRAW_ARGUMENTS);
|
||||
break;
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED:
|
||||
byteStride += sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
|
||||
break;
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH:
|
||||
byteStride += sizeof(D3D12_DISPATCH_ARGUMENTS);
|
||||
break;
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT:
|
||||
byteStride += _parameters[i].GetDesc().Constant.Num32BitValuesToSet * 4;
|
||||
requiresRootSignature = true;
|
||||
break;
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW:
|
||||
byteStride += sizeof(D3D12_VERTEX_BUFFER_VIEW);
|
||||
break;
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW:
|
||||
byteStride += sizeof(D3D12_INDEX_BUFFER_VIEW);
|
||||
break;
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW:
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW:
|
||||
case D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW:
|
||||
byteStride += 8;
|
||||
requiresRootSignature = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_COMMAND_SIGNATURE_DESC desc;
|
||||
desc.ByteStride = byteStride;
|
||||
desc.NumArgumentDescs = numParameters;
|
||||
desc.pArgumentDescs = (const D3D12_INDIRECT_ARGUMENT_DESC*)_parameters.Get();
|
||||
desc.NodeMask = 1;
|
||||
|
||||
ID3D12RootSignature* rootSig = rootSignature;
|
||||
if (requiresRootSignature)
|
||||
{
|
||||
ASSERT(rootSig != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootSig = nullptr;
|
||||
}
|
||||
|
||||
const auto result = _device->GetDevice()->CreateCommandSignature(&desc, rootSig, IID_PPV_ARGS(&_signature));
|
||||
LOG_DIRECTX_RESULT(result);
|
||||
|
||||
_signature->SetName(L"CommandSignature");
|
||||
|
||||
_memoryUsage = 100;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
ResourceType GetResourceType() const override
|
||||
{
|
||||
return ResourceType::Descriptor;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() override
|
||||
{
|
||||
DX_SAFE_RELEASE_CHECK(_signature, 0);
|
||||
_parameters.Resize(0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
252
Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp
Normal file
252
Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "DescriptorHeapDX12.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHeapWithSlotsDX12::Slot::CPU() const
|
||||
{
|
||||
ASSERT_LOW_LAYER(Heap);
|
||||
return Heap->CPU(Index);
|
||||
}
|
||||
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeapWithSlotsDX12::Slot::GPU() const
|
||||
{
|
||||
ASSERT_LOW_LAYER(Heap);
|
||||
return Heap->GPU(Index);
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::Slot::CreateSRV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_SHADER_RESOURCE_VIEW_DESC* desc)
|
||||
{
|
||||
if (Heap == nullptr)
|
||||
device->Heap_CBV_SRV_UAV.AllocateSlot(Heap, Index);
|
||||
device->GetDevice()->CreateShaderResourceView(resource, desc, CPU());
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::Slot::CreateRTV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_RENDER_TARGET_VIEW_DESC* desc)
|
||||
{
|
||||
if (Heap == nullptr)
|
||||
device->Heap_RTV.AllocateSlot(Heap, Index);
|
||||
device->GetDevice()->CreateRenderTargetView(resource, desc, CPU());
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::Slot::CreateDSV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_DEPTH_STENCIL_VIEW_DESC* desc)
|
||||
{
|
||||
if (Heap == nullptr)
|
||||
device->Heap_DSV.AllocateSlot(Heap, Index);
|
||||
device->GetDevice()->CreateDepthStencilView(resource, desc, CPU());
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::Slot::CreateUAV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_UNORDERED_ACCESS_VIEW_DESC* desc, ID3D12Resource* counterResource)
|
||||
{
|
||||
if (Heap == nullptr)
|
||||
device->Heap_CBV_SRV_UAV.AllocateSlot(Heap, Index);
|
||||
device->GetDevice()->CreateUnorderedAccessView(resource, counterResource, desc, CPU());
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::Slot::Release()
|
||||
{
|
||||
if (Heap)
|
||||
{
|
||||
Heap->ReleaseSlot(Index);
|
||||
Heap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorHeapWithSlotsDX12::DescriptorHeapWithSlotsDX12(GPUDeviceDX12* device)
|
||||
: _device(device)
|
||||
, _heap(nullptr)
|
||||
, _descriptorsCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool DescriptorHeapWithSlotsDX12::Create(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible)
|
||||
{
|
||||
// Create description
|
||||
D3D12_DESCRIPTOR_HEAP_DESC desc;
|
||||
desc.Type = type;
|
||||
desc.NumDescriptors = descriptorsCount;
|
||||
desc.Flags = shaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
desc.NodeMask = 0;
|
||||
|
||||
// Create heap
|
||||
const HRESULT result = _device->GetDevice()->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), reinterpret_cast<void**>(&_heap));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
|
||||
// Setup
|
||||
_type = type;
|
||||
_shaderVisible = shaderVisible;
|
||||
_descriptorsCount = descriptorsCount;
|
||||
_beginCPU = _heap->GetCPUDescriptorHandleForHeapStart();
|
||||
if (shaderVisible)
|
||||
_beginGPU = _heap->GetGPUDescriptorHandleForHeapStart();
|
||||
else
|
||||
_beginGPU.ptr = 0;
|
||||
_incrementSize = _device->GetDevice()->GetDescriptorHandleIncrementSize(desc.Type);
|
||||
|
||||
// Setup usage cache
|
||||
_usage.Resize(static_cast<int32>(_descriptorsCount / 32), false);
|
||||
Platform::MemorySet(_usage.Get(), _usage.Count() * sizeof(uint32), 0);
|
||||
|
||||
_memoryUsage = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DescriptorHeapWithSlotsDX12::TryToGetUnusedSlot(uint32& index)
|
||||
{
|
||||
for (int32 i = 0; i < _usage.Count(); i++)
|
||||
{
|
||||
uint32& value = _usage[i];
|
||||
if (value != MAX_uint32)
|
||||
{
|
||||
// TODO: make it better?
|
||||
for (int32 bit = 0; bit < 32; bit++)
|
||||
{
|
||||
const uint32 mask = 1 << bit;
|
||||
if ((value & mask) == 0)
|
||||
{
|
||||
// Found
|
||||
index = i * 32 + bit;
|
||||
value |= mask;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::ReleaseSlot(uint32 index)
|
||||
{
|
||||
uint32& value = _usage[index / 32];
|
||||
const uint32 mask = 1 << (index & 31);
|
||||
ASSERT_LOW_LAYER((value & mask) == mask);
|
||||
value &= ~mask;
|
||||
}
|
||||
|
||||
DescriptorHeapPoolDX12::DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible)
|
||||
: _device(device)
|
||||
, _type(type)
|
||||
, _descriptorsCount(descriptorsCount)
|
||||
, _shaderVisible(shaderVisible)
|
||||
{
|
||||
}
|
||||
|
||||
void DescriptorHeapPoolDX12::Init()
|
||||
{
|
||||
// Allocate first page
|
||||
auto heap = New<DescriptorHeapWithSlotsDX12>(_device);
|
||||
if (heap->Create(_type, _descriptorsCount, _shaderVisible))
|
||||
{
|
||||
Platform::Fatal(TEXT("Failed to allocate descriptor heap."));
|
||||
}
|
||||
_heaps.Add(heap);
|
||||
}
|
||||
|
||||
void DescriptorHeapPoolDX12::AllocateSlot(DescriptorHeapWithSlotsDX12*& heap, uint32& slot)
|
||||
{
|
||||
for (int32 i = 0; i < _heaps.Count(); i++)
|
||||
{
|
||||
if (_heaps[i]->TryToGetUnusedSlot(slot))
|
||||
{
|
||||
heap = _heaps[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
heap = New<DescriptorHeapWithSlotsDX12>(_device);
|
||||
if (heap->Create(_type, _descriptorsCount, _shaderVisible))
|
||||
{
|
||||
Platform::Fatal(TEXT("Failed to allocate descriptor heap."));
|
||||
}
|
||||
_heaps.Add(heap);
|
||||
heap->TryToGetUnusedSlot(slot);
|
||||
}
|
||||
|
||||
void DescriptorHeapPoolDX12::ReleaseGPU()
|
||||
{
|
||||
for (auto heap : _heaps)
|
||||
{
|
||||
heap->ReleaseGPU();
|
||||
Delete(heap);
|
||||
}
|
||||
_heaps.Clear();
|
||||
}
|
||||
|
||||
void DescriptorHeapWithSlotsDX12::OnReleaseGPU()
|
||||
{
|
||||
_usage.SetCapacity(0, false);
|
||||
DX_SAFE_RELEASE_CHECK(_heap, 0);
|
||||
_descriptorsCount = 0;
|
||||
}
|
||||
|
||||
DescriptorHeapRingBufferDX12::DescriptorHeapRingBufferDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible)
|
||||
: _device(device)
|
||||
, _heap(nullptr)
|
||||
, _type(type)
|
||||
, _descriptorsCount(descriptorsCount)
|
||||
, _shaderVisible(shaderVisible)
|
||||
{
|
||||
}
|
||||
|
||||
bool DescriptorHeapRingBufferDX12::Init()
|
||||
{
|
||||
// Create description
|
||||
D3D12_DESCRIPTOR_HEAP_DESC desc;
|
||||
desc.Type = _type;
|
||||
desc.NumDescriptors = _descriptorsCount;
|
||||
desc.Flags = _shaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
desc.NodeMask = 0;
|
||||
|
||||
// Create heap
|
||||
const HRESULT result = _device->GetDevice()->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_heap));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
|
||||
// Setup
|
||||
_firstFree = 0;
|
||||
_beginCPU = _heap->GetCPUDescriptorHandleForHeapStart();
|
||||
if (_shaderVisible)
|
||||
_beginGPU = _heap->GetGPUDescriptorHandleForHeapStart();
|
||||
else
|
||||
_beginGPU.ptr = 0;
|
||||
_incrementSize = _device->GetDevice()->GetDescriptorHandleIncrementSize(desc.Type);
|
||||
|
||||
_memoryUsage = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DescriptorHeapRingBufferDX12::Allocation DescriptorHeapRingBufferDX12::AllocateTable(uint32 numDesc)
|
||||
{
|
||||
Allocation result;
|
||||
|
||||
// Move the ring buffer pointer
|
||||
uint32 index = _firstFree;
|
||||
_firstFree += numDesc;
|
||||
|
||||
// Check for overflow
|
||||
if (_firstFree >= _descriptorsCount)
|
||||
{
|
||||
// Move to the begin
|
||||
index = 0;
|
||||
_firstFree = numDesc;
|
||||
}
|
||||
|
||||
// Set pointers
|
||||
result.CPU.ptr = _beginCPU.ptr + static_cast<SIZE_T>(index * _incrementSize);
|
||||
result.GPU.ptr = _shaderVisible ? _beginGPU.ptr + index * _incrementSize : 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DescriptorHeapRingBufferDX12::OnReleaseGPU()
|
||||
{
|
||||
DX_SAFE_RELEASE_CHECK(_heap, 0);
|
||||
_firstFree = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
243
Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h
Normal file
243
Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "Engine/Graphics/GPUResource.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
class DescriptorHeapPoolDX12;
|
||||
class GPUDeviceDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Descriptors heap for DirectX 12 that bit array concept to implement descriptor slots allocation.
|
||||
/// </summary>
|
||||
class DescriptorHeapWithSlotsDX12 : public GPUResource
|
||||
{
|
||||
public:
|
||||
|
||||
struct Slot
|
||||
{
|
||||
DescriptorHeapWithSlotsDX12* Heap = nullptr;
|
||||
uint32 Index;
|
||||
|
||||
FORCE_INLINE bool IsValid() const
|
||||
{
|
||||
return Heap != nullptr;
|
||||
}
|
||||
|
||||
#if BUILD_DEBUG
|
||||
~Slot()
|
||||
{
|
||||
ASSERT(Heap == nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE CPU() const;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE GPU() const;
|
||||
|
||||
// Creates shader resource view
|
||||
// @param index Descriptor index in the heap
|
||||
// @param resource Shader Resource to create view for it
|
||||
// @param desc View description
|
||||
void CreateSRV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_SHADER_RESOURCE_VIEW_DESC* desc = nullptr);
|
||||
|
||||
// Creates render target view
|
||||
// @param index Descriptor index in the heap
|
||||
// @param resource Render Target to create view for it
|
||||
void CreateRTV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_RENDER_TARGET_VIEW_DESC* desc = nullptr);
|
||||
|
||||
// Creates depth stencil view
|
||||
// @param index Descriptor index in the heap
|
||||
// @param resource Render Target Depth to create view for it
|
||||
void CreateDSV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_DEPTH_STENCIL_VIEW_DESC* desc = nullptr);
|
||||
|
||||
// Creates unordered access view
|
||||
// @param index Descriptor index in the heap
|
||||
// @param resource Unordered Access to create view for it
|
||||
// @param desc View description
|
||||
void CreateUAV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_UNORDERED_ACCESS_VIEW_DESC* desc = nullptr, ID3D12Resource* counterResource = nullptr);
|
||||
|
||||
void Release();
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
ID3D12DescriptorHeap* _heap;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE _beginCPU;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE _beginGPU;
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE _type;
|
||||
uint32 _incrementSize;
|
||||
uint32 _descriptorsCount;
|
||||
bool _shaderVisible;
|
||||
Array<uint32> _usage;
|
||||
|
||||
public:
|
||||
|
||||
DescriptorHeapWithSlotsDX12(GPUDeviceDX12* device);
|
||||
|
||||
public:
|
||||
|
||||
// Get heap
|
||||
FORCE_INLINE operator ID3D12DescriptorHeap*() const
|
||||
{
|
||||
return _heap;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// Create heap data
|
||||
// @param type Heap data type
|
||||
// @param descriptorsCount Amount of descriptors to use
|
||||
// @param shaderVisible True if allow shaders to access heap data
|
||||
bool Create(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible = false);
|
||||
|
||||
public:
|
||||
|
||||
// Tries to find free descriptor slot
|
||||
// @param index Result index to use
|
||||
// @returns True if can assign descriptor to the heap
|
||||
bool TryToGetUnusedSlot(uint32& index);
|
||||
|
||||
// Release descriptor slot
|
||||
// @param index Descriptor index in the heap
|
||||
void ReleaseSlot(uint32 index);
|
||||
|
||||
public:
|
||||
|
||||
// Get handle to the CPU view at given index
|
||||
// @param index Descriptor index
|
||||
// @returns CPU address
|
||||
FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE CPU(uint32 index)
|
||||
{
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle;
|
||||
handle.ptr = _beginCPU.ptr + (SIZE_T)(index * _incrementSize);
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Get handle to the GPU view at given index
|
||||
// @param index Descriptor index
|
||||
// @returns GPU address
|
||||
FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE GPU(uint32 index)
|
||||
{
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE handle;
|
||||
handle.ptr = _beginGPU.ptr + index * _incrementSize;
|
||||
return handle;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
ResourceType GetResourceType() const final override
|
||||
{
|
||||
return ResourceType::Descriptor;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Descriptors heap pool for DirectX 12.
|
||||
/// </summary>
|
||||
class DescriptorHeapPoolDX12
|
||||
{
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE _type;
|
||||
uint32 _descriptorsCount;
|
||||
bool _shaderVisible;
|
||||
Array<DescriptorHeapWithSlotsDX12*> _heaps;
|
||||
|
||||
public:
|
||||
|
||||
DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible);
|
||||
|
||||
public:
|
||||
|
||||
void Init();
|
||||
void AllocateSlot(DescriptorHeapWithSlotsDX12*& heap, uint32& slot);
|
||||
void ReleaseGPU();
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Descriptors heap for DirectX 12 that uses a ring buffer concept to implement descriptor tables allocation.
|
||||
/// </summary>
|
||||
class DescriptorHeapRingBufferDX12 : public GPUResource
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Heap allocation info
|
||||
/// </summary>
|
||||
struct Allocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle in CPU memory
|
||||
/// </summary>
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE CPU;
|
||||
|
||||
/// <summary>
|
||||
/// Handle in GPU memory
|
||||
/// </summary>
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE GPU;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
ID3D12DescriptorHeap* _heap;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE _beginCPU;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE _beginGPU;
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE _type;
|
||||
uint32 _incrementSize;
|
||||
uint32 _descriptorsCount;
|
||||
uint32 _firstFree;
|
||||
bool _shaderVisible;
|
||||
|
||||
public:
|
||||
|
||||
DescriptorHeapRingBufferDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 heap object
|
||||
/// </summary>
|
||||
/// <returns>Heap object</returns>
|
||||
FORCE_INLINE ID3D12DescriptorHeap* GetHeap() const
|
||||
{
|
||||
return _heap;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// Setup heap
|
||||
// @returns True if cannot setup heap, otherwise false
|
||||
bool Init();
|
||||
|
||||
// Allocate memory for descriptors table
|
||||
// @param numDesc Amount of descriptors in table
|
||||
// @returns Allocated data (GPU param is valid only for shader visible heaps)
|
||||
Allocation AllocateTable(uint32 numDesc);
|
||||
|
||||
public:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
ResourceType GetResourceType() const final override
|
||||
{
|
||||
return ResourceType::Descriptor;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
245
Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp
Normal file
245
Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUBufferDX12.h"
|
||||
#include "../RenderToolsDX.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/Async/Tasks/GPUUploadBufferTask.h"
|
||||
|
||||
uint64 GPUBufferDX12::GetSizeInBytes() const
|
||||
{
|
||||
return _memoryUsage;
|
||||
}
|
||||
|
||||
D3D12_GPU_VIRTUAL_ADDRESS GPUBufferDX12::GetLocation() const
|
||||
{
|
||||
return _resource->GetGPUVirtualAddress();
|
||||
}
|
||||
|
||||
GPUBufferView* GPUBufferDX12::View() const
|
||||
{
|
||||
return (GPUBufferView*)&_view;
|
||||
}
|
||||
|
||||
void* GPUBufferDX12::Map(GPUResourceMapMode mode)
|
||||
{
|
||||
D3D12_RANGE readRange;
|
||||
D3D12_RANGE* readRangePtr;
|
||||
switch (mode)
|
||||
{
|
||||
case GPUResourceMapMode::Read:
|
||||
readRangePtr = nullptr;
|
||||
break;
|
||||
case GPUResourceMapMode::Write:
|
||||
readRange.Begin = readRange.End = 0;
|
||||
readRangePtr = &readRange;
|
||||
break;
|
||||
case GPUResourceMapMode::ReadWrite:
|
||||
readRangePtr = nullptr;
|
||||
break;
|
||||
default:
|
||||
CRASH;
|
||||
}
|
||||
_lastMapMode = mode;
|
||||
void* mapped = nullptr;
|
||||
const HRESULT result = _resource->Map(0, readRangePtr, &mapped);
|
||||
LOG_DIRECTX_RESULT(result);
|
||||
return mapped;
|
||||
}
|
||||
|
||||
void GPUBufferDX12::Unmap()
|
||||
{
|
||||
D3D12_RANGE writtenRange;
|
||||
D3D12_RANGE* writtenRangePtr;
|
||||
switch (_lastMapMode)
|
||||
{
|
||||
case GPUResourceMapMode::Read:
|
||||
writtenRange.Begin = writtenRange.End = 0;
|
||||
writtenRangePtr = &writtenRange;
|
||||
break;
|
||||
case GPUResourceMapMode::Write:
|
||||
writtenRangePtr = nullptr;
|
||||
break;
|
||||
case GPUResourceMapMode::ReadWrite:
|
||||
writtenRangePtr = nullptr;
|
||||
break;
|
||||
default:
|
||||
CRASH;
|
||||
}
|
||||
_resource->Unmap(0, writtenRangePtr);
|
||||
}
|
||||
|
||||
GPUResource* GPUBufferDX12::AsGPUResource() const
|
||||
{
|
||||
return (GPUResource*)this;
|
||||
}
|
||||
|
||||
bool GPUBufferDX12::OnInit()
|
||||
{
|
||||
const bool useSRV = IsShaderResource();
|
||||
const bool useUAV = IsUnorderedAccess();
|
||||
|
||||
// Create description
|
||||
D3D12_RESOURCE_DESC resourceDesc;
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Alignment = 0;
|
||||
resourceDesc.Width = _desc.Size;
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.SampleDesc.Quality = 0;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
if (!useSRV)
|
||||
resourceDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
|
||||
if (useUAV)
|
||||
resourceDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
if (_desc.Flags & GPUBufferFlags::Argument)
|
||||
resourceDesc.Flags |= D3D12XBOX_RESOURCE_FLAG_ALLOW_INDIRECT_BUFFER;
|
||||
#endif
|
||||
|
||||
// Create allocation description
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
switch (_desc.Usage)
|
||||
{
|
||||
case GPUResourceUsage::StagingUpload:
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
break;
|
||||
case GPUResourceUsage::StagingReadback:
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
|
||||
break;
|
||||
default:
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
}
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 1;
|
||||
heapProperties.VisibleNodeMask = 1;
|
||||
|
||||
// Create resource
|
||||
ID3D12Resource* resource;
|
||||
D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDesc,
|
||||
initialState,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&resource)));
|
||||
|
||||
// Set state
|
||||
initResource(resource, initialState, 1);
|
||||
DX_SET_DEBUG_NAME(_resource, GetName());
|
||||
_memoryUsage = _desc.Size;
|
||||
int32 numElements = _desc.GetElementsCount();
|
||||
|
||||
// Check if set initial data
|
||||
if (_desc.InitData)
|
||||
{
|
||||
// Here we have to upload initial data to the GPU.
|
||||
// If we are during rendering we can use device command list without an afford.
|
||||
// But if we are doing it during update or from the other thread we have to register resource data upload job.
|
||||
// In both cases options.InitData data have to exist for a few next frames.
|
||||
|
||||
if (_desc.Usage == GPUResourceUsage::StagingUpload)
|
||||
{
|
||||
// Modify staging resource data now
|
||||
SetData(_desc.InitData, _desc.Size);
|
||||
}
|
||||
else if (_device->IsRendering() && IsInMainThread())
|
||||
{
|
||||
// Upload resource data now
|
||||
_device->GetMainContext()->UpdateBuffer(this, _desc.InitData, _desc.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create async resource copy task
|
||||
auto copyTask = ::New<GPUUploadBufferTask>(this, 0, Span<byte>((const byte*)_desc.InitData, _desc.Size), true);
|
||||
ASSERT(copyTask->HasReference(this));
|
||||
copyTask->Start();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if need to use a counter
|
||||
if (_desc.Flags & GPUBufferFlags::Counter || _desc.Flags & GPUBufferFlags::Append)
|
||||
{
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
String name = GetName() + TEXT(".Counter");
|
||||
_counter = ::New<GPUBufferDX12>(_device, name);
|
||||
#else
|
||||
_counter = ::New<GPUBufferDX12>(_device, String::Empty);
|
||||
#endif
|
||||
if (_counter->Init(GPUBufferDescription::Raw(4, GPUBufferFlags::UnorderedAccess)))
|
||||
{
|
||||
LOG(Error, "Cannot create counter buffer.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Create views
|
||||
_view.Init(_device, this);
|
||||
if (useSRV)
|
||||
{
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
if (_desc.Flags & GPUBufferFlags::RawBuffer)
|
||||
srvDesc.Format = RenderToolsDX::ToDxgiFormat(_desc.Format);
|
||||
else
|
||||
srvDesc.Format = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindShaderResourceFormat(_desc.Format, false));
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
|
||||
srvDesc.Buffer.FirstElement = 0;
|
||||
srvDesc.Buffer.NumElements = numElements;
|
||||
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
|
||||
if (_desc.Flags & GPUBufferFlags::Structured)
|
||||
{
|
||||
srvDesc.Buffer.StructureByteStride = _desc.Stride;
|
||||
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
srvDesc.Buffer.StructureByteStride = 0;
|
||||
}
|
||||
if (_desc.Flags & GPUBufferFlags::RawBuffer)
|
||||
srvDesc.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW;
|
||||
_view.SetSRV(&srvDesc);
|
||||
}
|
||||
if (useUAV)
|
||||
{
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
|
||||
uavDesc.Buffer.FirstElement = 0;
|
||||
uavDesc.Buffer.StructureByteStride = 0;
|
||||
uavDesc.Buffer.CounterOffsetInBytes = 0;
|
||||
uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
|
||||
uavDesc.Buffer.NumElements = numElements;
|
||||
if (_desc.Flags & GPUBufferFlags::Structured)
|
||||
uavDesc.Buffer.StructureByteStride = _desc.Stride;
|
||||
if (_desc.Flags & GPUBufferFlags::RawBuffer)
|
||||
uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW;
|
||||
if (_desc.Flags & GPUBufferFlags::Structured)
|
||||
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
else
|
||||
uavDesc.Format = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindUnorderedAccessFormat(_desc.Format));
|
||||
_view.SetUAV(&uavDesc, _counter ? _counter->GetResource() : nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUBufferDX12::OnReleaseGPU()
|
||||
{
|
||||
_view.Release();
|
||||
releaseResource();
|
||||
SAFE_DELETE_GPU_RESOURCE(_counter);
|
||||
|
||||
// Base
|
||||
GPUBuffer::OnReleaseGPU();
|
||||
}
|
||||
|
||||
#endif
|
||||
211
Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h
Normal file
211
Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h
Normal file
@@ -0,0 +1,211 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/GPUBuffer.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "IShaderResourceDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
/// <summary>
|
||||
/// The buffer view for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUBufferViewDX12 : public GPUBufferView, public IShaderResourceDX12
|
||||
{
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device = nullptr;
|
||||
ResourceOwnerDX12* _owner = nullptr;
|
||||
DescriptorHeapWithSlotsDX12::Slot _srv, _uav;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUBufferViewDX12"/> class.
|
||||
/// </summary>
|
||||
GPUBufferViewDX12()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="GPUBufferViewDX12"/> class.
|
||||
/// </summary>
|
||||
~GPUBufferViewDX12()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="device">The graphics device.</param>
|
||||
/// <param name="owner">The resource owner.</param>
|
||||
void Init(GPUDeviceDX12* device, ResourceOwnerDX12* owner)
|
||||
{
|
||||
_device = device;
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the view.
|
||||
/// </summary>
|
||||
void Release()
|
||||
{
|
||||
_srv.Release();
|
||||
_uav.Release();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Sets the shader resource view.
|
||||
/// </summary>
|
||||
/// <param name="srvDesc">The SRV desc.</param>
|
||||
void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC* srvDesc)
|
||||
{
|
||||
if (srvDesc)
|
||||
{
|
||||
_srv.CreateSRV(_device, _owner->GetResource(), srvDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
_srv.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the unordered access view.
|
||||
/// </summary>
|
||||
/// <param name="uavDesc">The UAV desc.</param>
|
||||
/// <param name="counterResource">The counter buffer resource.</param>
|
||||
void SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC* uavDesc, ID3D12Resource* counterResource = nullptr)
|
||||
{
|
||||
if (uavDesc)
|
||||
{
|
||||
_uav.CreateUAV(_device, _owner->GetResource(), uavDesc, counterResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
_uav.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [GPUResourceView]
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return (void*)(IShaderResourceDX12*)this;
|
||||
}
|
||||
|
||||
// [IShaderResourceDX12]
|
||||
bool IsDepthStencilResource() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override
|
||||
{
|
||||
return _srv.CPU();
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE UAV() const override
|
||||
{
|
||||
return _uav.CPU();
|
||||
}
|
||||
|
||||
ResourceOwnerDX12* GetResourceOwner() const override
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU buffer for DirectX 12 backend.
|
||||
/// </summary>
|
||||
/// <seealso cref="GPUResourceDX12" />
|
||||
class GPUBufferDX12 : public GPUResourceDX12<GPUBuffer>, public ResourceOwnerDX12
|
||||
{
|
||||
private:
|
||||
|
||||
GPUBufferViewDX12 _view;
|
||||
GPUBufferDX12* _counter = nullptr;
|
||||
GPUResourceMapMode _lastMapMode;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUBufferDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The device.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
GPUBufferDX12(GPUDeviceDX12* device, const StringView& name)
|
||||
: GPUResourceDX12<GPUBuffer>(device, name)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets vertex buffer view descriptor. Valid only for the vertex buffers.
|
||||
/// </summary>
|
||||
FORCE_INLINE void GetVBView(D3D12_VERTEX_BUFFER_VIEW& view) const
|
||||
{
|
||||
view.StrideInBytes = GetStride();
|
||||
view.SizeInBytes = (UINT)GetSizeInBytes();
|
||||
view.BufferLocation = GetLocation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index buffer view descriptor. Valid only for the index buffers.
|
||||
/// </summary>
|
||||
FORCE_INLINE void GetIBView(D3D12_INDEX_BUFFER_VIEW& view) const
|
||||
{
|
||||
view.Format = GetStride() == 4 ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
|
||||
view.SizeInBytes = (UINT)GetSizeInBytes();
|
||||
view.BufferLocation = GetLocation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets buffer size in a GPU memory in bytes.
|
||||
/// </summary>
|
||||
/// <returns>Size in bytes.</returns>
|
||||
uint64 GetSizeInBytes() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets buffer location in a GPU memory.
|
||||
/// </summary>
|
||||
/// <returns>GPU memory location.</returns>
|
||||
D3D12_GPU_VIRTUAL_ADDRESS GetLocation() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the counter resource.
|
||||
/// </summary>
|
||||
/// <returns>The internal counter buffer.</returns>
|
||||
FORCE_INLINE GPUBufferDX12* GetCounter() const
|
||||
{
|
||||
return _counter;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [GPUBuffer]
|
||||
GPUBufferView* View() const override;
|
||||
void* Map(GPUResourceMapMode mode) override;
|
||||
void Unmap() override;
|
||||
|
||||
// [ResourceOwnerDX12]
|
||||
GPUResource* AsGPUResource() const override;
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUBuffer]
|
||||
bool OnInit() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
1254
Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
Normal file
1254
Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
Normal file
File diff suppressed because it is too large
Load Diff
204
Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h
Normal file
204
Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "IShaderResourceDX12.h"
|
||||
#include "DescriptorHeapDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
class GPUDeviceDX12;
|
||||
class GPUPipelineStateDX12;
|
||||
class GPUBufferDX12;
|
||||
class GPUConstantBufferDX12;
|
||||
class GPUTextureViewDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the resource barriers buffer size (will be flushed on overflow)
|
||||
/// </summary>
|
||||
#define DX12_RB_BUFFER_SIZE 16
|
||||
|
||||
/// <summary>
|
||||
/// GPU Commands Context implementation for DirectX 12
|
||||
/// </summary>
|
||||
class GPUContextDX12 : public GPUContext
|
||||
{
|
||||
friend GPUDeviceDX12;
|
||||
|
||||
public:
|
||||
|
||||
typedef DescriptorHeapRingBufferDX12::Allocation Descriptor;
|
||||
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
ID3D12GraphicsCommandList* _commandList;
|
||||
ID3D12CommandAllocator* _currentAllocator;
|
||||
GPUPipelineStateDX12* _currentState;
|
||||
GPUShaderProgramCS* _currentCompute;
|
||||
|
||||
int32 _swapChainsUsed;
|
||||
int32 _vbCount;
|
||||
int32 _rtCount;
|
||||
int32 _rbBufferSize;
|
||||
|
||||
uint32 _srMaskDirtyGraphics;
|
||||
uint32 _srMaskDirtyCompute;
|
||||
|
||||
uint32 _uaMaskDirtyGraphics;
|
||||
uint32 _uaMaskDirtyCompute;
|
||||
|
||||
int32 _isCompute : 1;
|
||||
int32 _rtDirtyFlag : 1;
|
||||
int32 _psDirtyFlag : 1;
|
||||
int32 _cbDirtyFlag : 1;
|
||||
|
||||
GPUTextureViewDX12* _rtDepth;
|
||||
GPUTextureViewDX12* _rtHandles[GPU_MAX_RT_BINDED];
|
||||
IShaderResourceDX12* _srHandles[GPU_MAX_SR_BINDED];
|
||||
IShaderResourceDX12* _uaHandles[GPU_MAX_UA_BINDED + 1];
|
||||
GPUBufferDX12* _ibHandle;
|
||||
GPUBufferDX12* _vbHandles[GPU_MAX_VB_BINDED];
|
||||
D3D12_INDEX_BUFFER_VIEW _ibView;
|
||||
D3D12_VERTEX_BUFFER_VIEW _vbViews[GPU_MAX_VB_BINDED];
|
||||
D3D12_RESOURCE_BARRIER _rbBuffer[DX12_RB_BUFFER_SIZE];
|
||||
GPUConstantBufferDX12* _cbHandles[GPU_MAX_CB_BINDED];
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="device">Graphics device</param>
|
||||
/// <param name="type">Context type</param>
|
||||
GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type);
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GPUContextDX12();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets command list
|
||||
/// </summary>
|
||||
/// <returns>Command list to use</returns>
|
||||
FORCE_INLINE ID3D12GraphicsCommandList* GetCommandList() const
|
||||
{
|
||||
return _commandList;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Adds the transition barrier for the given resource (or subresource). Supports batching barriers.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
/// <param name="before">The 'before' state.</param>
|
||||
/// <param name="after">The 'after' state.</param>
|
||||
/// <param name="subresourceIndex">The index of the subresource.</param>
|
||||
void AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D12_RESOURCE_STATES before, const D3D12_RESOURCE_STATES after, const int32 subresourceIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Set DirectX 12 resource state using resource barrier
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource to use</param>
|
||||
/// <param name="after">The target state to resource have.</param>
|
||||
/// <param name="subresourceIndex">The subresource index. Use -1 to apply for the whole resource.</param>
|
||||
void SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURCE_STATES after, int32 subresourceIndex = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Reset commands list and request new allocator for commands
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Flush existing commands to the GPU
|
||||
/// </summary>
|
||||
/// <param name="waitForCompletion">True if CPU should wait for GPU to wait job</param>
|
||||
/// <returns>Next fence value used for CPU/GPU work synchronization</returns>
|
||||
uint64 Execute(bool waitForCompletion = false);
|
||||
|
||||
/// <summary>
|
||||
/// External event called by swap chain after using context end
|
||||
/// </summary>
|
||||
void OnSwapChainFlush();
|
||||
|
||||
/// <summary>
|
||||
/// Flush pending resource barriers
|
||||
/// </summary>
|
||||
FORCE_INLINE void FlushResourceBarriers()
|
||||
{
|
||||
flushRBs();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void GetActiveHeapDescriptor(const D3D12_CPU_DESCRIPTOR_HANDLE& cpuHandle, Descriptor& descriptor);
|
||||
|
||||
private:
|
||||
|
||||
void flushSRVs();
|
||||
void flushRTVs();
|
||||
void flushUAVs();
|
||||
void flushCBs();
|
||||
void flushRBs();
|
||||
void flushPS();
|
||||
void onDrawCall();
|
||||
|
||||
public:
|
||||
|
||||
// [GPUContext]
|
||||
void FrameBegin() override;
|
||||
void FrameEnd() override;
|
||||
#if GPU_ALLOW_PROFILE_EVENTS
|
||||
void EventBegin(const Char* name) override;
|
||||
void EventEnd() override;
|
||||
#endif
|
||||
void* GetNativePtr() const override;
|
||||
bool IsDepthBufferBinded() override;
|
||||
void Clear(GPUTextureView* rt, const Color& color) override;
|
||||
void ClearDepth(GPUTextureView* depthBuffer, float depthValue) override;
|
||||
void ClearUA(GPUBuffer* buf, const Vector4& value) override;
|
||||
void ResetRenderTarget() override;
|
||||
void SetRenderTarget(GPUTextureView* rt) override;
|
||||
void SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt) override;
|
||||
void SetRenderTarget(GPUTextureView* depthBuffer, const Span<GPUTextureView*>& rts) override;
|
||||
void SetRenderTarget(GPUTextureView* rt, GPUBuffer* uaOutput) override;
|
||||
void ResetSR() override;
|
||||
void ResetUA() override;
|
||||
void ResetCB() override;
|
||||
void BindCB(int32 slot, GPUConstantBuffer* cb) override;
|
||||
void BindSR(int32 slot, GPUResourceView* view) override;
|
||||
void BindUA(int32 slot, GPUResourceView* view) override;
|
||||
void BindVB(const Span<GPUBuffer*>& vertexBuffers, const uint32* vertexBuffersOffsets = nullptr) override;
|
||||
void BindIB(GPUBuffer* indexBuffer) override;
|
||||
void UpdateCB(GPUConstantBuffer* cb, const void* data) override;
|
||||
void Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ) override;
|
||||
void DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
|
||||
void ResolveMultisample(GPUTexture* sourceMultisampleTexture, GPUTexture* destTexture, int32 sourceSubResource, int32 destSubResource, PixelFormat format) override;
|
||||
void DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex) override;
|
||||
void DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex) override;
|
||||
void DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
|
||||
void DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
|
||||
void SetViewport(const Viewport& viewport) override;
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
void CopyBuffer(GPUBuffer* dstBuffer, GPUBuffer* srcBuffer, uint32 size, uint32 dstOffset, uint32 srcOffset) override;
|
||||
void UpdateTexture(GPUTexture* texture, int32 arrayIndex, int32 mipIndex, const void* data, uint32 rowPitch, uint32 slicePitch) override;
|
||||
void CopyTexture(GPUTexture* dstResource, uint32 dstSubresource, uint32 dstX, uint32 dstY, uint32 dstZ, GPUTexture* srcResource, uint32 srcSubresource) override;
|
||||
void ResetCounter(GPUBuffer* buffer) override;
|
||||
void CopyCounter(GPUBuffer* dstBuffer, uint32 dstOffset, GPUBuffer* srcBuffer) override;
|
||||
void CopyResource(GPUResource* dstResource, GPUResource* srcResource) override;
|
||||
void CopySubresource(GPUResource* dstResource, uint32 dstSubresource, GPUResource* srcResource, uint32 srcSubresource) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
786
Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
Normal file
786
Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
Normal file
@@ -0,0 +1,786 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "GPUShaderDX12.h"
|
||||
#include "GPUContextDX12.h"
|
||||
#include "GPUPipelineStateDX12.h"
|
||||
#include "GPUTextureDX12.h"
|
||||
#include "GPUTimerQueryDX12.h"
|
||||
#include "GPUBufferDX12.h"
|
||||
#include "GPUSwapChainDX12.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/Async/GPUTasksExecutor.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Config/PlatformSettings.h"
|
||||
#include "UploadBufferDX12.h"
|
||||
#include "CommandQueueDX12.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "CommandSignatureDX12.h"
|
||||
|
||||
static bool CheckDX12Support(IDXGIAdapter* adapter)
|
||||
{
|
||||
// Try to create device
|
||||
if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUDevice* GPUDeviceDX12::Create()
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
IDXGIFactory4* dxgiFactory = nullptr;
|
||||
GPUAdapterDX selectedAdapter;
|
||||
selectedAdapter.Index = 0;
|
||||
selectedAdapter.MaxFeatureLevel = D3D_FEATURE_LEVEL_12_0;
|
||||
Platform::MemoryClear(&selectedAdapter.Description, sizeof(selectedAdapter.Description));
|
||||
selectedAdapter.Description.VendorId = GPU_VENDOR_ID_AMD;
|
||||
#else
|
||||
#if !USE_EDITOR && PLATFORM_WINDOWS
|
||||
auto winSettings = WindowsPlatformSettings::Instance();
|
||||
if (!winSettings->SupportDX12)
|
||||
{
|
||||
// Skip if there is no support
|
||||
LOG(Warning, "Cannot use DirectX 12 (support disabled).");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Debug Layer
|
||||
#if GPU_ENABLE_DIAGNOSTICS
|
||||
ComPtr<ID3D12Debug> debugLayer;
|
||||
D3D12GetDebugInterface(IID_PPV_ARGS(&debugLayer));
|
||||
if (debugLayer)
|
||||
{
|
||||
debugLayer->EnableDebugLayer();
|
||||
LOG(Info, "DirectX debugging layer enabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create DXGI factory (CreateDXGIFactory2 is supported on Windows 8.1 or newer)
|
||||
IDXGIFactory4* dxgiFactory;
|
||||
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
|
||||
if (hr != S_OK)
|
||||
{
|
||||
LOG(Error, "Cannot create DXGI adapter. Error code: {0:x}.", hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Enumerate the DXGIFactory's adapters
|
||||
Array<GPUAdapterDX> adapters;
|
||||
IDXGIAdapter* tempAdapter;
|
||||
for (uint32 index = 0; dxgiFactory->EnumAdapters(index, &tempAdapter) != DXGI_ERROR_NOT_FOUND; index++)
|
||||
{
|
||||
// Try to use that adapter
|
||||
GPUAdapterDX adapter;
|
||||
if (tempAdapter && CheckDX12Support(tempAdapter))
|
||||
{
|
||||
adapter.Index = index;
|
||||
adapter.MaxFeatureLevel = D3D_FEATURE_LEVEL_12_0;
|
||||
VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&adapter.Description));
|
||||
uint32 outputs = RenderToolsDX::CountAdapterOutputs(tempAdapter);
|
||||
|
||||
// Send that info to the log
|
||||
LOG(Info, "Adapter {1}: '{0}', DirectX {2}", adapter.Description.Description, index, RenderToolsDX::GetFeatureLevelString(adapter.MaxFeatureLevel));
|
||||
LOG(Info, " Dedicated Video Memory: {0}, Dedicated System Memory: {1}, Shared System Memory: {2}, Output(s): {3}", Utilities::BytesToText(adapter.Description.DedicatedVideoMemory), Utilities::BytesToText(adapter.Description.DedicatedSystemMemory), Utilities::BytesToText(adapter.Description.SharedSystemMemory), outputs);
|
||||
|
||||
adapters.Add(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
// Select the adapter to use
|
||||
GPUAdapterDX selectedAdapter = adapters[0];
|
||||
uint32 vendorId = 0;
|
||||
if (CommandLine::Options.NVIDIA)
|
||||
vendorId = GPU_VENDOR_ID_NVIDIA;
|
||||
else if (CommandLine::Options.AMD)
|
||||
vendorId = GPU_VENDOR_ID_AMD;
|
||||
else if (CommandLine::Options.Intel)
|
||||
vendorId = GPU_VENDOR_ID_INTEL;
|
||||
if (vendorId != 0)
|
||||
{
|
||||
for (const auto& adapter : adapters)
|
||||
{
|
||||
if (adapter.GetVendorId() == vendorId)
|
||||
{
|
||||
selectedAdapter = adapter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate adapter
|
||||
if (!selectedAdapter.IsValid())
|
||||
{
|
||||
LOG(Error, "Failed to choose valid DirectX adapter!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if selected adapter does not support DirectX 12
|
||||
if (!selectedAdapter.IsSupportingDX12())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create device
|
||||
auto device = New<GPUDeviceDX12>(dxgiFactory, New<GPUAdapterDX>(selectedAdapter));
|
||||
if (device->Init())
|
||||
{
|
||||
LOG(Warning, "Graphics Device init failed");
|
||||
Delete(device);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
static MSAALevel GetMaximumMultisampleCount(ID3D12Device* device, DXGI_FORMAT dxgiFormat)
|
||||
{
|
||||
int32 maxCount = 1;
|
||||
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS qualityLevels = { dxgiFormat };
|
||||
for (int32 i = 2; i <= 8; i *= 2)
|
||||
{
|
||||
qualityLevels.SampleCount = i;
|
||||
if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &qualityLevels, sizeof(qualityLevels))) && qualityLevels.NumQualityLevels > 0)
|
||||
maxCount = i;
|
||||
}
|
||||
return static_cast<MSAALevel>(maxCount);
|
||||
}
|
||||
|
||||
GPUDeviceDX12::GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter)
|
||||
: GPUDeviceDX(RendererType::DirectX12, ShaderProfile::DirectX_SM6, adapter)
|
||||
, _device(nullptr)
|
||||
, _factoryDXGI(dxgiFactory)
|
||||
, _res2Dispose(256)
|
||||
, _rootSignature(nullptr)
|
||||
, _commandQueue(nullptr)
|
||||
, _mainContext(nullptr)
|
||||
, UploadBuffer(nullptr)
|
||||
, TimestampQueryHeap(this, D3D12_QUERY_HEAP_TYPE_TIMESTAMP, DX12_BACK_BUFFER_COUNT * 1024)
|
||||
, Heap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 4 * 1024, false)
|
||||
, Heap_RTV(this, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1 * 1024, false)
|
||||
, Heap_DSV(this, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 64, false)
|
||||
, RingHeap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 64 * 1024, true)
|
||||
{
|
||||
}
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
namespace XboxScarlett
|
||||
{
|
||||
extern Action OnSuspend;
|
||||
extern Action OnResume;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GPUDeviceDX12::Init()
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
// Create DirectX device
|
||||
D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {};
|
||||
params.Version = D3D12_SDK_VERSION;
|
||||
#if GPU_ENABLE_DIAGNOSTICS
|
||||
params.ProcessDebugFlags = D3D12_PROCESS_DEBUG_FLAG_DEBUG_LAYER_ENABLED;
|
||||
#elif !BUILD_RELEASE
|
||||
params.ProcessDebugFlags = D3D12XBOX_PROCESS_DEBUG_FLAG_INSTRUMENTED;
|
||||
#endif
|
||||
params.GraphicsCommandQueueRingSizeBytes = static_cast<UINT>(D3D12XBOX_DEFAULT_SIZE_BYTES);
|
||||
params.GraphicsScratchMemorySizeBytes = static_cast<UINT>(D3D12XBOX_DEFAULT_SIZE_BYTES);
|
||||
params.ComputeScratchMemorySizeBytes = static_cast<UINT>(D3D12XBOX_DEFAULT_SIZE_BYTES);
|
||||
params.DisableDXR = TRUE;
|
||||
VALIDATE_DIRECTX_RESULT(D3D12XboxCreateDevice(nullptr, ¶ms, IID_GRAPHICS_PPV_ARGS(&_device)));
|
||||
|
||||
// Setup adapter
|
||||
D3D12XBOX_GPU_HARDWARE_CONFIGURATION hwConfig = {};
|
||||
_device->GetGpuHardwareConfigurationX(&hwConfig);
|
||||
const wchar_t* hwVer = L"Unknown";
|
||||
switch (hwConfig.HardwareVersion)
|
||||
{
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE:
|
||||
hwVer = L"Xbox One";
|
||||
break;
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE_S:
|
||||
hwVer = L"Xbox One S";
|
||||
break;
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE_X:
|
||||
hwVer = L"Xbox One X";
|
||||
break;
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE_X_DEVKIT:
|
||||
hwVer = L"Xbox One X (DevKit)";
|
||||
break;
|
||||
#ifdef _GAMING_XBOX_SCARLETT
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_SCARLETT_LOCKHART:
|
||||
hwVer = L"Scarlett Lockhart";
|
||||
break;
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_SCARLETT_ANACONDA:
|
||||
hwVer = L"Scarlett Anaconda";
|
||||
break;
|
||||
case D3D12XBOX_HARDWARE_VERSION_XBOX_SCARLETT_DEVKIT:
|
||||
hwVer = L"Scarlett Dev Kit";
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
LOG(Info, "Hardware Version: {0}", hwVer);
|
||||
updateFrameEvents();
|
||||
XboxScarlett::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
||||
XboxScarlett::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
||||
#else
|
||||
// Get DXGI adapter
|
||||
IDXGIAdapter1* adapter;
|
||||
ASSERT(_factoryDXGI);
|
||||
if (_factoryDXGI->EnumAdapters1(_adapter->Index, &adapter) == DXGI_ERROR_NOT_FOUND || adapter == nullptr)
|
||||
{
|
||||
LOG(Warning, "Cannot get the adapter.");
|
||||
return true;
|
||||
}
|
||||
UpdateOutputs(adapter);
|
||||
{
|
||||
ComPtr<IDXGIFactory5> factory5;
|
||||
_factoryDXGI->QueryInterface(IID_PPV_ARGS(&factory5));
|
||||
if (factory5)
|
||||
{
|
||||
BOOL allowTearing;
|
||||
if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))) && allowTearing)
|
||||
{
|
||||
AllowTearing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create DirectX device
|
||||
VALIDATE_DIRECTX_RESULT(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&_device)));
|
||||
|
||||
// Debug Layer
|
||||
#if GPU_ENABLE_DIAGNOSTICS
|
||||
ComPtr<ID3D12InfoQueue> infoQueue;
|
||||
VALIDATE_DIRECTX_RESULT(_device->QueryInterface(IID_PPV_ARGS(&infoQueue)));
|
||||
if (infoQueue)
|
||||
{
|
||||
D3D12_INFO_QUEUE_FILTER filter;
|
||||
Platform::MemoryClear(&filter, sizeof(filter));
|
||||
|
||||
D3D12_MESSAGE_SEVERITY denySeverity = D3D12_MESSAGE_SEVERITY_INFO;
|
||||
filter.DenyList.NumSeverities = 1;
|
||||
filter.DenyList.pSeverityList = &denySeverity;
|
||||
|
||||
D3D12_MESSAGE_ID disabledMessages[] =
|
||||
{
|
||||
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
|
||||
D3D12_MESSAGE_ID_INVALID_DESCRIPTOR_HANDLE,
|
||||
D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_PS_OUTPUT_RT_OUTPUT_MISMATCH,
|
||||
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE,
|
||||
D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE,
|
||||
D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_EMPTY_LAYOUT,
|
||||
D3D12_MESSAGE_ID_RESOURCE_BARRIER_DUPLICATE_SUBRESOURCE_TRANSITIONS,
|
||||
D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE,
|
||||
};
|
||||
|
||||
filter.DenyList.NumIDs = ARRAY_COUNT(disabledMessages);
|
||||
filter.DenyList.pIDList = disabledMessages;
|
||||
|
||||
infoQueue->AddStorageFilterEntries(&filter);
|
||||
|
||||
infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
||||
infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||
//infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Change state
|
||||
_state = DeviceState::Created;
|
||||
|
||||
// Spawn some info about the hardware
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS options;
|
||||
VALIDATE_DIRECTX_RESULT(_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)));
|
||||
LOG(Info, "Tiled Resources Tier: {0}", options.TiledResourcesTier);
|
||||
LOG(Info, "Resource Binding Tier: {0}", options.ResourceBindingTier);
|
||||
LOG(Info, "Conservative Rasterization Tier: {0}", options.ConservativeRasterizationTier);
|
||||
LOG(Info, "Resource Heap Tier: {0}", options.ResourceHeapTier);
|
||||
LOG(Info, "ROVs Supported: {0}", options.ROVsSupported != 0);
|
||||
|
||||
// Init device limits
|
||||
{
|
||||
auto& limits = Limits;
|
||||
limits.HasCompute = true;
|
||||
limits.HasTessellation = true;
|
||||
limits.HasGeometryShaders = true;
|
||||
limits.HasInstancing = true;
|
||||
limits.HasVolumeTextureRendering = true;
|
||||
limits.HasDrawIndirect = true;
|
||||
limits.HasAppendConsumeBuffers = true;
|
||||
limits.HasSeparateRenderTargetBlendState = true;
|
||||
limits.HasDepthAsSRV = true;
|
||||
limits.HasReadOnlyDepth = true;
|
||||
limits.HasMultisampleDepthAsSRV = true;
|
||||
limits.MaximumMipLevelsCount = D3D12_REQ_MIP_LEVELS;
|
||||
limits.MaximumTexture1DSize = D3D12_REQ_TEXTURE1D_U_DIMENSION;
|
||||
limits.MaximumTexture1DArraySize = D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION;
|
||||
limits.MaximumTexture2DSize = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
limits.MaximumTexture2DArraySize = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
|
||||
limits.MaximumTexture3DSize = D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION;
|
||||
limits.MaximumTextureCubeSize = D3D12_REQ_TEXTURECUBE_DIMENSION;
|
||||
|
||||
for (int32 i = 0; i < static_cast<int32>(PixelFormat::MAX); i++)
|
||||
{
|
||||
const PixelFormat format = static_cast<PixelFormat>(i);
|
||||
const DXGI_FORMAT dxgiFormat = RenderToolsDX::ToDxgiFormat(format);
|
||||
|
||||
D3D12_FEATURE_DATA_FORMAT_SUPPORT formatInfo = { dxgiFormat };
|
||||
if (FAILED(_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatInfo, sizeof(formatInfo))))
|
||||
{
|
||||
formatInfo.Support1 = D3D12_FORMAT_SUPPORT1_NONE;
|
||||
}
|
||||
const MSAALevel maximumMultisampleCount = GetMaximumMultisampleCount(_device, dxgiFormat);
|
||||
|
||||
FeaturesPerFormat[i] = FormatFeatures(format, maximumMultisampleCount, (FormatSupport)formatInfo.Support1);
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILD_DEBUG && false
|
||||
// Prevent the GPU from overclocking or under-clocking to get consistent timings
|
||||
_device->SetStablePowerState(TRUE);
|
||||
#endif
|
||||
|
||||
// Create commands queue
|
||||
_commandQueue = New<CommandQueueDX12>(this, D3D12_COMMAND_LIST_TYPE_DIRECT);
|
||||
if (_commandQueue->Init())
|
||||
return true;
|
||||
|
||||
// Create rendering main context
|
||||
_mainContext = New<GPUContextDX12>(this, D3D12_COMMAND_LIST_TYPE_DIRECT);
|
||||
|
||||
// Create descriptors heaps
|
||||
Heap_CBV_SRV_UAV.Init();
|
||||
Heap_RTV.Init();
|
||||
Heap_DSV.Init();
|
||||
if (RingHeap_CBV_SRV_UAV.Init())
|
||||
return true;
|
||||
|
||||
// Create empty views
|
||||
{
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
srvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
srvDesc.Texture2D.PlaneSlice = 0;
|
||||
srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
|
||||
_nullSrv.CreateSRV(this, nullptr, &srvDesc);
|
||||
}
|
||||
{
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
||||
uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
uavDesc.Texture2D.MipSlice = 0;
|
||||
uavDesc.Texture2D.PlaneSlice = 0;
|
||||
_nullUav.CreateUAV(this, nullptr, &uavDesc);
|
||||
}
|
||||
|
||||
// Create root signature
|
||||
// TODO: maybe create set of different root signatures? for UAVs, for compute, for simple drawing, for post fx?
|
||||
{
|
||||
// Descriptor tables
|
||||
D3D12_DESCRIPTOR_RANGE r[2];
|
||||
// TODO: separate ranges for pixel/vertex visiblity and one shared for all?
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE& range = r[0];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
range.NumDescriptors = GPU_MAX_SR_BINDED;
|
||||
range.BaseShaderRegister = 0;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE& range = r[1];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
range.NumDescriptors = GPU_MAX_UA_BINDED + 1; // the last (additional) UAV register is used as a UAV output (hidden internally)
|
||||
range.BaseShaderRegister = 0;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
static_assert(GPU_MAX_UA_BINDED == 2, "DX12 backend uses hardcoded single UAV register slot. Update code to support more.");
|
||||
|
||||
// Root parameters
|
||||
D3D12_ROOT_PARAMETER rootParameters[4];
|
||||
{
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[0];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.Descriptor.ShaderRegister = 0;
|
||||
rootParam.Descriptor.RegisterSpace = 0;
|
||||
}
|
||||
{
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[1];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.Descriptor.ShaderRegister = 1;
|
||||
rootParam.Descriptor.RegisterSpace = 0;
|
||||
}
|
||||
{
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[2];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.DescriptorTable.NumDescriptorRanges = 1;
|
||||
rootParam.DescriptorTable.pDescriptorRanges = &r[0];
|
||||
}
|
||||
{
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[3];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.DescriptorTable.NumDescriptorRanges = 1;
|
||||
rootParam.DescriptorTable.pDescriptorRanges = &r[1];
|
||||
}
|
||||
|
||||
// TODO: describe visibilities for the static samples, maybe use all pixel? or again pixel + all combo?
|
||||
|
||||
// Static samplers
|
||||
D3D12_STATIC_SAMPLER_DESC staticSamplers[6];
|
||||
// Linear Clamp
|
||||
staticSamplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[0].MipLODBias = 0.0f;
|
||||
staticSamplers[0].MaxAnisotropy = 1;
|
||||
staticSamplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[0].MinLOD = 0;
|
||||
staticSamplers[0].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[0].ShaderRegister = 0;
|
||||
staticSamplers[0].RegisterSpace = 0;
|
||||
staticSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Point Clamp
|
||||
staticSamplers[1].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
||||
staticSamplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[1].MipLODBias = 0.0f;
|
||||
staticSamplers[1].MaxAnisotropy = 1;
|
||||
staticSamplers[1].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[1].MinLOD = 0;
|
||||
staticSamplers[1].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[1].ShaderRegister = 1;
|
||||
staticSamplers[1].RegisterSpace = 0;
|
||||
staticSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Linear Wrap
|
||||
staticSamplers[2].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[2].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[2].MipLODBias = 0.0f;
|
||||
staticSamplers[2].MaxAnisotropy = 1;
|
||||
staticSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[2].MinLOD = 0;
|
||||
staticSamplers[2].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[2].ShaderRegister = 2;
|
||||
staticSamplers[2].RegisterSpace = 0;
|
||||
staticSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Point Wrap
|
||||
staticSamplers[3].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
||||
staticSamplers[3].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[3].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[3].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[3].MipLODBias = 0.0f;
|
||||
staticSamplers[3].MaxAnisotropy = 1;
|
||||
staticSamplers[3].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[3].MinLOD = 0;
|
||||
staticSamplers[3].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[3].ShaderRegister = 3;
|
||||
staticSamplers[3].RegisterSpace = 0;
|
||||
staticSamplers[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Shadow
|
||||
staticSamplers[4].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
|
||||
staticSamplers[4].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[4].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[4].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[4].MipLODBias = 0.0f;
|
||||
staticSamplers[4].MaxAnisotropy = 1;
|
||||
staticSamplers[4].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
|
||||
staticSamplers[4].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[4].MinLOD = 0;
|
||||
staticSamplers[4].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[4].ShaderRegister = 4;
|
||||
staticSamplers[4].RegisterSpace = 0;
|
||||
staticSamplers[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Shadow PCF
|
||||
staticSamplers[5].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
|
||||
staticSamplers[5].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[5].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[5].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[5].MipLODBias = 0.0f;
|
||||
staticSamplers[5].MaxAnisotropy = 1;
|
||||
staticSamplers[5].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
|
||||
staticSamplers[5].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[5].MinLOD = 0;
|
||||
staticSamplers[5].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[5].ShaderRegister = 5;
|
||||
staticSamplers[5].RegisterSpace = 0;
|
||||
staticSamplers[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
|
||||
// TODO: static samplers for the shadow pass change into bindable samplers or sth?
|
||||
|
||||
// Init
|
||||
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
||||
rootSignatureDesc.NumParameters = ARRAY_COUNT(rootParameters);
|
||||
rootSignatureDesc.pParameters = rootParameters;
|
||||
rootSignatureDesc.NumStaticSamplers = ARRAY_COUNT(staticSamplers);
|
||||
rootSignatureDesc.pStaticSamplers = staticSamplers;
|
||||
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
|
||||
// Serialize
|
||||
ComPtr<ID3DBlob> signature;
|
||||
ComPtr<ID3DBlob> error;
|
||||
VALIDATE_DIRECTX_RESULT(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
|
||||
|
||||
// Create
|
||||
VALIDATE_DIRECTX_RESULT(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature)));
|
||||
}
|
||||
|
||||
// Upload buffer
|
||||
UploadBuffer = New<UploadBufferDX12>(this);
|
||||
|
||||
if (TimestampQueryHeap.Init())
|
||||
return true;
|
||||
|
||||
// Cached command signatures
|
||||
{
|
||||
DrawIndirectCommandSignature = New<CommandSignatureDX12>(this, 1);
|
||||
DrawIndirectCommandSignature->At(0).Draw();
|
||||
DrawIndirectCommandSignature->Finalize();
|
||||
}
|
||||
{
|
||||
DrawIndexedIndirectCommandSignature = New<CommandSignatureDX12>(this, 1);
|
||||
DrawIndexedIndirectCommandSignature->At(0).DrawIndexed();
|
||||
DrawIndexedIndirectCommandSignature->Finalize();
|
||||
}
|
||||
{
|
||||
DispatchIndirectCommandSignature = New<CommandSignatureDX12>(this, 1);
|
||||
DispatchIndirectCommandSignature->At(0).Dispatch();
|
||||
DispatchIndirectCommandSignature->Finalize();
|
||||
}
|
||||
|
||||
_state = DeviceState::Ready;
|
||||
return GPUDeviceDX::Init();
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::DrawBegin()
|
||||
{
|
||||
// Wait for the GPU to have at least one backbuffer to render to
|
||||
/*{
|
||||
PROFILE_CPU_NAMED("Wait For Fence");
|
||||
const uint64 nextFenceValue = _commandQueue->GetNextFenceValue();
|
||||
if (nextFenceValue >= DX12_BACK_BUFFER_COUNT)
|
||||
{
|
||||
_commandQueue->WaitForFence(nextFenceValue - DX12_BACK_BUFFER_COUNT);
|
||||
}
|
||||
}*/
|
||||
|
||||
// Base
|
||||
GPUDeviceDX::DrawBegin();
|
||||
|
||||
updateRes2Dispose();
|
||||
UploadBuffer->BeginGeneration(Engine::FrameCount);
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::RenderEnd()
|
||||
{
|
||||
// Base
|
||||
GPUDeviceDX::RenderEnd();
|
||||
|
||||
// Resolve the timestamp queries
|
||||
TimestampQueryHeap.EndQueryBatchAndResolveQueryData(_mainContext);
|
||||
}
|
||||
|
||||
GPUDeviceDX12::~GPUDeviceDX12()
|
||||
{
|
||||
// Ensure to be disposed
|
||||
Dispose();
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullSRV() const
|
||||
{
|
||||
return _nullSrv.CPU();
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullUAV() const
|
||||
{
|
||||
return _nullUav.CPU();
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* GPUDeviceDX12::GetCommandList() const
|
||||
{
|
||||
return _mainContext->GetCommandList();
|
||||
}
|
||||
|
||||
ID3D12CommandQueue* GPUDeviceDX12::GetCommandQueueDX12() const
|
||||
{
|
||||
return _commandQueue->GetCommandQueue();
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::Dispose()
|
||||
{
|
||||
GPUDeviceLock lock(this);
|
||||
|
||||
// Check if has been disposed already
|
||||
if (_state == DeviceState::Disposed)
|
||||
return;
|
||||
|
||||
// Set current state
|
||||
_state = DeviceState::Disposing;
|
||||
|
||||
// Wait for rendering end
|
||||
WaitForGPU();
|
||||
|
||||
// Pre dispose
|
||||
preDispose();
|
||||
|
||||
// Release all late dispose resources (if state is Disposing all are released)
|
||||
updateRes2Dispose();
|
||||
|
||||
// Clear pipeline objects
|
||||
_nullSrv.Release();
|
||||
_nullUav.Release();
|
||||
TimestampQueryHeap.Destroy();
|
||||
DX_SAFE_RELEASE_CHECK(_rootSignature, 0);
|
||||
Heap_CBV_SRV_UAV.ReleaseGPU();
|
||||
Heap_RTV.ReleaseGPU();
|
||||
Heap_DSV.ReleaseGPU();
|
||||
RingHeap_CBV_SRV_UAV.ReleaseGPU();
|
||||
SAFE_DELETE(UploadBuffer);
|
||||
SAFE_DELETE(DrawIndirectCommandSignature);
|
||||
SAFE_DELETE(_mainContext);
|
||||
SAFE_DELETE(_commandQueue);
|
||||
|
||||
// Clear DirectX stuff
|
||||
SAFE_DELETE(_adapter);
|
||||
SAFE_RELEASE(_device);
|
||||
SAFE_RELEASE(_factoryDXGI);
|
||||
|
||||
// Base
|
||||
GPUDeviceDX::Dispose();
|
||||
|
||||
// Set current state
|
||||
_state = DeviceState::Disposed;
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::WaitForGPU()
|
||||
{
|
||||
_commandQueue->WaitForGPU();
|
||||
}
|
||||
|
||||
GPUTexture* GPUDeviceDX12::CreateTexture(const StringView& name)
|
||||
{
|
||||
return New<GPUTextureDX12>(this, name);
|
||||
}
|
||||
|
||||
GPUShader* GPUDeviceDX12::CreateShader(const StringView& name)
|
||||
{
|
||||
return New<GPUShaderDX12>(this, name);
|
||||
}
|
||||
|
||||
GPUPipelineState* GPUDeviceDX12::CreatePipelineState()
|
||||
{
|
||||
return New<GPUPipelineStateDX12>(this);
|
||||
}
|
||||
|
||||
GPUTimerQuery* GPUDeviceDX12::CreateTimerQuery()
|
||||
{
|
||||
return New<GPUTimerQueryDX12>(this);
|
||||
}
|
||||
|
||||
GPUBuffer* GPUDeviceDX12::CreateBuffer(const StringView& name)
|
||||
{
|
||||
return New<GPUBufferDX12>(this, name);
|
||||
}
|
||||
|
||||
GPUSwapChain* GPUDeviceDX12::CreateSwapChain(Window* window)
|
||||
{
|
||||
return New<GPUSwapChainDX12>(this, window);
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::AddResourceToLateRelease(IGraphicsUnknown* resource, uint32 safeFrameCount)
|
||||
{
|
||||
ASSERT(safeFrameCount < 32);
|
||||
if (resource == nullptr)
|
||||
return;
|
||||
|
||||
ScopeLock lock(_res2DisposeLock);
|
||||
|
||||
// Add to the list
|
||||
DisposeResourceEntry entry;
|
||||
entry.Resource = resource;
|
||||
entry.TargetFrame = Engine::FrameCount + safeFrameCount;
|
||||
_res2Dispose.Add(entry);
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::updateRes2Dispose()
|
||||
{
|
||||
uint64 currentFrame = Engine::FrameCount;
|
||||
if (_state == DeviceState::Disposing)
|
||||
{
|
||||
// During device disposing we want to remove all resources
|
||||
currentFrame = MAX_uint32;
|
||||
}
|
||||
|
||||
_res2DisposeLock.Lock();
|
||||
for (int32 i = _res2Dispose.Count() - 1; i >= 0 && i < _res2Dispose.Count(); i--)
|
||||
{
|
||||
const DisposeResourceEntry& entry = _res2Dispose[i];
|
||||
|
||||
if (entry.TargetFrame <= currentFrame)
|
||||
{
|
||||
auto refs = entry.Resource->Release();
|
||||
if (refs != 0)
|
||||
{
|
||||
LOG(Error, "Late release resource has not been fully released. References left: {0}", refs);
|
||||
}
|
||||
_res2Dispose.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
_res2DisposeLock.Unlock();
|
||||
}
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
|
||||
void GPUDeviceDX12::OnSuspend()
|
||||
{
|
||||
_commandQueue->GetCommandQueue()->SuspendX(0);
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::OnResume()
|
||||
{
|
||||
_commandQueue->GetCommandQueue()->ResumeX();
|
||||
|
||||
updateFrameEvents();
|
||||
}
|
||||
|
||||
void GPUDeviceDX12::updateFrameEvents()
|
||||
{
|
||||
ComPtr<IDXGIDevice1> dxgiDevice;
|
||||
VALIDATE_DIRECTX_RESULT(_device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxgiDevice)));
|
||||
ComPtr<IDXGIAdapter> dxgiAdapter;
|
||||
VALIDATE_DIRECTX_RESULT(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf()));
|
||||
dxgiAdapter->GetDesc(&_adapter->Description);
|
||||
ComPtr<IDXGIOutput> dxgiOutput;
|
||||
VALIDATE_DIRECTX_RESULT(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf()));
|
||||
VALIDATE_DIRECTX_RESULT(_device->SetFrameIntervalX(dxgiOutput.Get(), D3D12XBOX_FRAME_INTERVAL_60_HZ, DX12_BACK_BUFFER_COUNT - 1u, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE));
|
||||
VALIDATE_DIRECTX_RESULT(_device->ScheduleFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, 0U, nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GPUDevice* CreateGPUDeviceDX12()
|
||||
{
|
||||
return GPUDeviceDX12::Create();
|
||||
}
|
||||
|
||||
#endif
|
||||
243
Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
Normal file
243
Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "../GPUDeviceDX.h"
|
||||
#include "Engine/Graphics/GPUResource.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
#include "ResourceOwnerDX12.h"
|
||||
#include "QueryHeapDX12.h"
|
||||
#include "DescriptorHeapDX12.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#define DX12_BACK_BUFFER_COUNT 3
|
||||
#else
|
||||
#define DX12_BACK_BUFFER_COUNT 2
|
||||
#endif
|
||||
|
||||
class Engine;
|
||||
class WindowsWindow;
|
||||
class GPUContextDX12;
|
||||
class GPUSwapChainDX12;
|
||||
class UploadBufferDX12;
|
||||
class CommandQueueDX12;
|
||||
class CommandSignatureDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of Graphics Device for DirectX 12 rendering system
|
||||
/// </summary>
|
||||
class GPUDeviceDX12 : public GPUDeviceDX
|
||||
{
|
||||
friend GPUContextDX12;
|
||||
friend GPUSwapChainDX12;
|
||||
|
||||
private:
|
||||
|
||||
struct DisposeResourceEntry
|
||||
{
|
||||
IGraphicsUnknown* Resource;
|
||||
uint64 TargetFrame;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Private Stuff
|
||||
ID3D12Device* _device;
|
||||
IDXGIFactory4* _factoryDXGI;
|
||||
CriticalSection _res2DisposeLock;
|
||||
Array<DisposeResourceEntry> _res2Dispose;
|
||||
|
||||
// Pipeline
|
||||
ID3D12RootSignature* _rootSignature;
|
||||
CommandQueueDX12* _commandQueue;
|
||||
GPUContextDX12* _mainContext;
|
||||
|
||||
// Heaps
|
||||
DescriptorHeapWithSlotsDX12::Slot _nullSrv;
|
||||
DescriptorHeapWithSlotsDX12::Slot _nullUav;
|
||||
|
||||
public:
|
||||
|
||||
// Create new graphics device (returns null if failed)
|
||||
// @returns Created device or null
|
||||
static GPUDevice* Create();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUDeviceDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dxgiFactory">The DXGI factory handle.</param>
|
||||
/// <param name="adapter">The GPU device adapter.</param>
|
||||
GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="GPUDeviceDX12"/> class.
|
||||
/// </summary>
|
||||
~GPUDeviceDX12();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Upload buffer for general purpose
|
||||
/// </summary>
|
||||
UploadBufferDX12* UploadBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The timestamp queries heap.
|
||||
/// </summary>
|
||||
QueryHeapDX12 TimestampQueryHeap;
|
||||
|
||||
bool AllowTearing = false;
|
||||
CommandSignatureDX12* DispatchIndirectCommandSignature = nullptr;
|
||||
CommandSignatureDX12* DrawIndexedIndirectCommandSignature = nullptr;
|
||||
CommandSignatureDX12* DrawIndirectCommandSignature = nullptr;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE NullSRV() const;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE NullUAV() const;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets DX12 device
|
||||
/// </summary>
|
||||
/// <returns>DirectX 12 device</returns>
|
||||
FORCE_INLINE ID3D12Device* GetDevice() const
|
||||
{
|
||||
return _device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets DXGI factory
|
||||
/// </summary>
|
||||
/// <returns>DXGI factory object</returns>
|
||||
FORCE_INLINE IDXGIFactory4* GetDXGIFactory() const
|
||||
{
|
||||
return _factoryDXGI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 command list object
|
||||
/// </summary>
|
||||
/// <returns>Command list object (DirectX 12)</returns>
|
||||
ID3D12GraphicsCommandList* GetCommandList() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets command queue
|
||||
/// </summary>
|
||||
/// <returns>Command queue</returns>
|
||||
FORCE_INLINE CommandQueueDX12* GetCommandQueue() const
|
||||
{
|
||||
return _commandQueue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 command queue object
|
||||
/// </summary>
|
||||
/// <returns>Command queue object (DirectX 12)</returns>
|
||||
ID3D12CommandQueue* GetCommandQueueDX12() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets root signature of the graphics pipeline
|
||||
/// </summary>
|
||||
/// <returns>Root signature</returns>
|
||||
FORCE_INLINE ID3D12RootSignature* GetRootSignature() const
|
||||
{
|
||||
return _rootSignature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets main commands context (for DirectX 12)
|
||||
/// </summary>
|
||||
/// <returns>Main context</returns>
|
||||
FORCE_INLINE GPUContextDX12* GetMainContextDX12() const
|
||||
{
|
||||
return _mainContext;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
DescriptorHeapPoolDX12 Heap_CBV_SRV_UAV;
|
||||
DescriptorHeapPoolDX12 Heap_RTV;
|
||||
DescriptorHeapPoolDX12 Heap_DSV;
|
||||
DescriptorHeapRingBufferDX12 RingHeap_CBV_SRV_UAV;
|
||||
|
||||
public:
|
||||
|
||||
// Add resource to late release service (will be released after 'safeFrameCount' frames)
|
||||
void AddResourceToLateRelease(IGraphicsUnknown* resource, uint32 safeFrameCount = DX12_RESOURCE_DELETE_SAFE_FRAMES_COUNT);
|
||||
|
||||
static FORCE_INLINE uint32 GetMaxMSAAQuality(uint32 sampleCount)
|
||||
{
|
||||
if (sampleCount <= 8)
|
||||
{
|
||||
// 0 has better quality (a more even distribution)
|
||||
// Higher quality levels might be useful for non box filtered AA or when using weighted samples.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not supported.
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
void OnSuspend();
|
||||
void OnResume();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
void updateFrameEvents();
|
||||
#endif
|
||||
void updateRes2Dispose();
|
||||
|
||||
public:
|
||||
|
||||
// [GPUDeviceDX]
|
||||
GPUContext* GetMainContext() override
|
||||
{
|
||||
return reinterpret_cast<GPUContext*>(_mainContext);
|
||||
}
|
||||
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return _device;
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void DrawBegin() override;
|
||||
void RenderEnd() override;
|
||||
void Dispose() final override;
|
||||
void WaitForGPU() override;
|
||||
GPUTexture* CreateTexture(const StringView& name) override;
|
||||
GPUShader* CreateShader(const StringView& name) override;
|
||||
GPUPipelineState* CreatePipelineState() override;
|
||||
GPUTimerQuery* CreateTimerQuery() override;
|
||||
GPUBuffer* CreateBuffer(const StringView& name) override;
|
||||
GPUSwapChain* CreateSwapChain(Window* window) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU resource implementation for DirectX 12 backend.
|
||||
/// </summary>
|
||||
template<class BaseType>
|
||||
class GPUResourceDX12 : public GPUResourceBase<GPUDeviceDX12, BaseType>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The graphics device.</param>
|
||||
/// <param name="name">The resource name.</param>
|
||||
GPUResourceDX12(GPUDeviceDX12* device, const StringView& name)
|
||||
: GPUResourceBase(device, name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
extern GPUDevice* CreateGPUDeviceDX12();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,186 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUPipelineStateDX12.h"
|
||||
#include "GPUTextureDX12.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
|
||||
GPUPipelineStateDX12::GPUPipelineStateDX12(GPUDeviceDX12* device)
|
||||
: GPUResourceDX12(device, StringView::Empty)
|
||||
, _states(16)
|
||||
{
|
||||
}
|
||||
|
||||
bool GPUPipelineStateDX12::IsValid() const
|
||||
{
|
||||
return !!_memoryUsage;
|
||||
}
|
||||
|
||||
ID3D12PipelineState* GPUPipelineStateDX12::GetState(GPUTextureViewDX12* depth, int32 rtCount, GPUTextureViewDX12** rtHandles)
|
||||
{
|
||||
// Validate
|
||||
ASSERT(depth || rtCount);
|
||||
|
||||
// Prepare key
|
||||
GPUPipelineStateKeyDX12 key;
|
||||
key.RTsCount = rtCount;
|
||||
key.DepthFormat = depth ? depth->GetFormat() : PixelFormat::Unknown;
|
||||
key.MSAA = depth ? depth->GetMSAA() : (rtCount ? rtHandles[0]->GetMSAA() : MSAALevel::None);
|
||||
for (int32 i = 0; i < rtCount; i++)
|
||||
key.RTVsFormats[i] = rtHandles[i]->GetFormat();
|
||||
for (int32 i = rtCount; i < GPU_MAX_RT_BINDED; i++)
|
||||
key.RTVsFormats[i] = PixelFormat::Unknown;
|
||||
|
||||
// Try reuse cached version
|
||||
ID3D12PipelineState* state = nullptr;
|
||||
if (_states.TryGet(key, state))
|
||||
{
|
||||
#if BUILD_DEBUG
|
||||
// Verify
|
||||
GPUPipelineStateKeyDX12 refKey;
|
||||
_states.KeyOf(state, &refKey);
|
||||
ASSERT(refKey == key);
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
PROFILE_CPU_NAMED("Create Pipeline State");
|
||||
|
||||
// Update description to match the pipeline
|
||||
_desc.NumRenderTargets = key.RTsCount;
|
||||
for (int32 i = 0; i < GPU_MAX_RT_BINDED; i++)
|
||||
_desc.RTVFormats[i] = RenderToolsDX::ToDxgiFormat(key.RTVsFormats[i]);
|
||||
_desc.SampleDesc.Count = static_cast<UINT>(key.MSAA);
|
||||
_desc.SampleDesc.Quality = key.MSAA == MSAALevel::None ? 0 : GPUDeviceDX12::GetMaxMSAAQuality((int32)key.MSAA);
|
||||
_desc.SampleMask = D3D12_DEFAULT_SAMPLE_MASK;
|
||||
_desc.DSVFormat = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindDepthStencilFormat(key.DepthFormat));
|
||||
|
||||
// Create object
|
||||
const HRESULT result = _device->GetDevice()->CreateGraphicsPipelineState(&_desc, IID_PPV_ARGS(&state));
|
||||
LOG_DIRECTX_RESULT(result);
|
||||
if (FAILED(result))
|
||||
return nullptr;
|
||||
|
||||
// Cache it
|
||||
_states.Add(key, state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void GPUPipelineStateDX12::OnReleaseGPU()
|
||||
{
|
||||
for (auto i = _states.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
_device->AddResourceToLateRelease(i->Value);
|
||||
}
|
||||
_states.Clear();
|
||||
}
|
||||
|
||||
bool GPUPipelineStateDX12::Init(const Description& desc)
|
||||
{
|
||||
ASSERT(!IsValid());
|
||||
|
||||
// Create description
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psDesc;
|
||||
Platform::MemoryClear(&psDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
|
||||
psDesc.pRootSignature = _device->GetRootSignature();
|
||||
|
||||
// Shaders
|
||||
psDesc.InputLayout = { static_cast<D3D12_INPUT_ELEMENT_DESC*>(desc.VS->GetInputLayout()), desc.VS->GetInputLayoutSize() };
|
||||
psDesc.VS = { desc.VS->GetBufferHandle(), desc.VS->GetBufferSize() };
|
||||
if (desc.HS)
|
||||
psDesc.HS = { desc.HS->GetBufferHandle(), desc.HS->GetBufferSize() };
|
||||
if (desc.DS)
|
||||
psDesc.DS = { desc.DS->GetBufferHandle(), desc.DS->GetBufferSize() };
|
||||
if (desc.GS)
|
||||
psDesc.GS = { desc.GS->GetBufferHandle(), desc.GS->GetBufferSize() };
|
||||
if (desc.PS)
|
||||
psDesc.PS = { desc.PS->GetBufferHandle(), desc.PS->GetBufferSize() };
|
||||
const static D3D12_PRIMITIVE_TOPOLOGY_TYPE primTypes1[] =
|
||||
{
|
||||
D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED,
|
||||
D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT,
|
||||
D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,
|
||||
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||
};
|
||||
const static D3D_PRIMITIVE_TOPOLOGY primTypes2[] =
|
||||
{
|
||||
D3D_PRIMITIVE_TOPOLOGY_UNDEFINED,
|
||||
D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
|
||||
D3D_PRIMITIVE_TOPOLOGY_LINELIST,
|
||||
D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
|
||||
};
|
||||
psDesc.PrimitiveTopologyType = primTypes1[(int32)desc.PrimitiveTopologyType];
|
||||
PrimitiveTopologyType = primTypes2[(int32)desc.PrimitiveTopologyType];
|
||||
if (desc.HS)
|
||||
{
|
||||
psDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
|
||||
PrimitiveTopologyType = (D3D_PRIMITIVE_TOPOLOGY)((int32)D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (desc.HS->GetControlPointsCount() - 1));
|
||||
}
|
||||
|
||||
// Depth State
|
||||
psDesc.DepthStencilState.DepthEnable = !!desc.DepthTestEnable;
|
||||
psDesc.DepthStencilState.DepthWriteMask = desc.DepthWriteEnable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
|
||||
psDesc.DepthStencilState.DepthFunc = static_cast<D3D12_COMPARISON_FUNC>(desc.DepthFunc);
|
||||
psDesc.DepthStencilState.StencilEnable = FALSE;
|
||||
psDesc.DepthStencilState.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
|
||||
psDesc.DepthStencilState.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
|
||||
const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS };
|
||||
psDesc.DepthStencilState.FrontFace = defaultStencilOp;
|
||||
psDesc.DepthStencilState.BackFace = defaultStencilOp;
|
||||
|
||||
// Rasterizer State
|
||||
psDesc.RasterizerState.FillMode = desc.Wireframe ? D3D12_FILL_MODE_WIREFRAME : D3D12_FILL_MODE_SOLID;
|
||||
D3D12_CULL_MODE dxCullMode;
|
||||
switch (desc.CullMode)
|
||||
{
|
||||
case CullMode::Normal:
|
||||
dxCullMode = D3D12_CULL_MODE_BACK;
|
||||
break;
|
||||
case CullMode::Inverted:
|
||||
dxCullMode = D3D12_CULL_MODE_FRONT;
|
||||
break;
|
||||
case CullMode::TwoSided:
|
||||
dxCullMode = D3D12_CULL_MODE_NONE;
|
||||
break;
|
||||
}
|
||||
psDesc.RasterizerState.CullMode = dxCullMode;
|
||||
psDesc.RasterizerState.FrontCounterClockwise = FALSE;
|
||||
psDesc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
||||
psDesc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
||||
psDesc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
||||
psDesc.RasterizerState.DepthClipEnable = !!desc.DepthClipEnable;
|
||||
psDesc.RasterizerState.MultisampleEnable = TRUE;
|
||||
psDesc.RasterizerState.AntialiasedLineEnable = !!desc.Wireframe;
|
||||
psDesc.RasterizerState.ForcedSampleCount = 0;
|
||||
psDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
||||
|
||||
// Blend State
|
||||
psDesc.BlendState.AlphaToCoverageEnable = desc.BlendMode.AlphaToCoverageEnable ? TRUE : FALSE;
|
||||
psDesc.BlendState.IndependentBlendEnable = FALSE;
|
||||
psDesc.BlendState.RenderTarget[0].BlendEnable = desc.BlendMode.BlendEnable ? TRUE : FALSE;
|
||||
psDesc.BlendState.RenderTarget[0].SrcBlend = (D3D12_BLEND)desc.BlendMode.SrcBlend;
|
||||
psDesc.BlendState.RenderTarget[0].DestBlend = (D3D12_BLEND)desc.BlendMode.DestBlend;
|
||||
psDesc.BlendState.RenderTarget[0].BlendOp = (D3D12_BLEND_OP)desc.BlendMode.BlendOp;
|
||||
psDesc.BlendState.RenderTarget[0].SrcBlendAlpha = (D3D12_BLEND)desc.BlendMode.SrcBlendAlpha;
|
||||
psDesc.BlendState.RenderTarget[0].DestBlendAlpha = (D3D12_BLEND)desc.BlendMode.DestBlendAlpha;
|
||||
psDesc.BlendState.RenderTarget[0].BlendOpAlpha = (D3D12_BLEND_OP)desc.BlendMode.BlendOpAlpha;
|
||||
psDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = (UINT8)desc.BlendMode.RenderTargetWriteMask;
|
||||
#if BUILD_DEBUG
|
||||
for (byte i = 1; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
||||
psDesc.BlendState.RenderTarget[i] = psDesc.BlendState.RenderTarget[0];
|
||||
#endif
|
||||
|
||||
// Cache description
|
||||
_desc = psDesc;
|
||||
|
||||
// Set non-zero memory usage
|
||||
_memoryUsage = sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC);
|
||||
|
||||
return GPUPipelineState::Init(desc);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "Engine/Graphics/GPUPipelineState.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
class GPUTextureViewDX12;
|
||||
|
||||
struct GPUPipelineStateKeyDX12
|
||||
{
|
||||
int32 RTsCount;
|
||||
MSAALevel MSAA;
|
||||
PixelFormat DepthFormat;
|
||||
PixelFormat RTVsFormats[GPU_MAX_RT_BINDED];
|
||||
|
||||
bool operator==(const GPUPipelineStateKeyDX12& other) const
|
||||
{
|
||||
return Platform::MemoryCompare((void*)this, &other, sizeof(GPUPipelineStateKeyDX12)) == 0;
|
||||
}
|
||||
|
||||
friend inline uint32 GetHash(const GPUPipelineStateKeyDX12& key)
|
||||
{
|
||||
uint32 hash = (int32)key.MSAA * 11;
|
||||
CombineHash(hash, (uint32)key.DepthFormat * 93473262);
|
||||
CombineHash(hash, key.RTsCount * 136);
|
||||
CombineHash(hash, (uint32)key.RTVsFormats[0]);
|
||||
CombineHash(hash, (uint32)key.RTVsFormats[1]);
|
||||
CombineHash(hash, (uint32)key.RTVsFormats[2]);
|
||||
CombineHash(hash, (uint32)key.RTVsFormats[3]);
|
||||
CombineHash(hash, (uint32)key.RTVsFormats[4]);
|
||||
CombineHash(hash, (uint32)key.RTVsFormats[5]);
|
||||
static_assert(GPU_MAX_RT_BINDED == 6, "Update hash combine code to match RT count (manually inlined loop).");
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Graphics pipeline state object for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUPipelineStateDX12 : public GPUResourceDX12<GPUPipelineState>
|
||||
{
|
||||
private:
|
||||
|
||||
Dictionary<GPUPipelineStateKeyDX12, ID3D12PipelineState*> _states;
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC _desc;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="device">Graphics Device</param>
|
||||
GPUPipelineStateDX12(GPUDeviceDX12* device);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Direct3D primitive topology
|
||||
/// </summary>
|
||||
D3D_PRIMITIVE_TOPOLOGY PrimitiveTopologyType = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 graphics pipeline state object for the given rendering state. Uses depth buffer and render targets formats and multi-sample levels to setup a proper PSO. Uses caching.
|
||||
/// </summary>
|
||||
/// <param name="depth">The depth buffer (can be null).</param>
|
||||
/// <param name="rtCount">The render targets count (can be 0).</param>
|
||||
/// <param name="rtHandles">The render target handles array.</param>
|
||||
/// <returns>DirectX 12 graphics pipeline state object</returns>
|
||||
ID3D12PipelineState* GetState(GPUTextureViewDX12* depth, int32 rtCount, GPUTextureViewDX12** rtHandles);
|
||||
|
||||
public:
|
||||
|
||||
// [GPUPipelineState]
|
||||
bool IsValid() const override;
|
||||
bool Init(const Description& desc) override;
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
156
Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp
Normal file
156
Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUShaderDX12.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "GPUShaderProgramDX12.h"
|
||||
#include "../RenderToolsDX.h"
|
||||
|
||||
GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, MemoryReadStream& stream)
|
||||
{
|
||||
GPUShaderProgram* shader = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
|
||||
// Get semantic name
|
||||
const char* semanticName = nullptr;
|
||||
// TODO: maybe use enum+mapping ?
|
||||
switch (Type)
|
||||
{
|
||||
case 1:
|
||||
semanticName = "POSITION";
|
||||
break;
|
||||
case 2:
|
||||
semanticName = "COLOR";
|
||||
break;
|
||||
case 3:
|
||||
semanticName = "TEXCOORD";
|
||||
break;
|
||||
case 4:
|
||||
semanticName = "NORMAL";
|
||||
break;
|
||||
case 5:
|
||||
semanticName = "TANGENT";
|
||||
break;
|
||||
case 6:
|
||||
semanticName = "BITANGENT";
|
||||
break;
|
||||
case 7:
|
||||
semanticName = "ATTRIBUTE";
|
||||
break;
|
||||
case 8:
|
||||
semanticName = "BLENDINDICES";
|
||||
break;
|
||||
case 9:
|
||||
semanticName = "BLENDWEIGHT";
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set data
|
||||
inputLayout[a] =
|
||||
{
|
||||
semanticName,
|
||||
static_cast<UINT>(Index),
|
||||
static_cast<DXGI_FORMAT>(Format),
|
||||
static_cast<UINT>(InputSlot),
|
||||
static_cast<UINT>(AlignedByteOffset),
|
||||
static_cast<D3D12_INPUT_CLASSIFICATION>(InputSlotClass),
|
||||
static_cast<UINT>(InstanceDataStepRate)
|
||||
};
|
||||
}
|
||||
|
||||
// Create object
|
||||
shader = New<GPUShaderProgramVSDX12>(initializer, cacheBytes, cacheSize, inputLayout, inputLayoutSize);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Hull:
|
||||
{
|
||||
int32 controlPointsCount;
|
||||
stream.ReadInt32(&controlPointsCount);
|
||||
shader = New<GPUShaderProgramHSDX12>(initializer, cacheBytes, cacheSize, controlPointsCount);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Domain:
|
||||
{
|
||||
shader = New<GPUShaderProgramDSDX12>(initializer, cacheBytes, cacheSize);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Geometry:
|
||||
{
|
||||
shader = New<GPUShaderProgramGSDX12>(initializer, cacheBytes, cacheSize);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Pixel:
|
||||
{
|
||||
shader = New<GPUShaderProgramPSDX12>(initializer, cacheBytes, cacheSize);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Compute:
|
||||
{
|
||||
shader = New<GPUShaderProgramCSDX12>(_device, initializer, cacheBytes, cacheSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
GPUConstantBuffer* GPUShaderDX12::CreateCB(const String& name, uint32 size, MemoryReadStream& stream)
|
||||
{
|
||||
return new(_cbs) GPUConstantBufferDX12(_device, size);
|
||||
}
|
||||
|
||||
void GPUShaderDX12::OnReleaseGPU()
|
||||
{
|
||||
_cbs.Clear();
|
||||
|
||||
GPUShader::OnReleaseGPU();
|
||||
}
|
||||
|
||||
ID3D12PipelineState* GPUShaderProgramCSDX12::GetOrCreateState()
|
||||
{
|
||||
if (_state)
|
||||
return _state;
|
||||
|
||||
// Create description
|
||||
D3D12_COMPUTE_PIPELINE_STATE_DESC psDesc;
|
||||
Platform::MemoryClear(&psDesc, sizeof(D3D12_COMPUTE_PIPELINE_STATE_DESC));
|
||||
psDesc.pRootSignature = _device->GetRootSignature();
|
||||
|
||||
// Shader
|
||||
psDesc.CS = { GetBufferHandle(), GetBufferSize() };
|
||||
|
||||
// Create object
|
||||
const HRESULT result = _device->GetDevice()->CreateComputePipelineState(&psDesc, IID_PPV_ARGS(&_state));
|
||||
LOG_DIRECTX_RESULT(result);
|
||||
|
||||
return _state;
|
||||
}
|
||||
|
||||
#endif
|
||||
67
Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.h
Normal file
67
Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constant Buffer for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUConstantBufferDX12 : public GPUResourceDX12<GPUConstantBuffer>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUConstantBufferDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The graphics device.</param>
|
||||
/// <param name="size">The buffer size (in bytes).</param>
|
||||
GPUConstantBufferDX12(GPUDeviceDX12* device, uint32 size) noexcept
|
||||
: GPUResourceDX12(device, String::Empty)
|
||||
, GPUAddress(0)
|
||||
{
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Last uploaded data address.
|
||||
/// </summary>
|
||||
D3D12_GPU_VIRTUAL_ADDRESS GPUAddress;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderDX12 : public GPUResourceDX12<GPUShader>
|
||||
{
|
||||
private:
|
||||
|
||||
Array<GPUConstantBufferDX12, FixedAllocation<MAX_CONSTANT_BUFFER_SLOTS>> _cbs;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The device.</param>
|
||||
/// <param name="name">The resource name.</param>
|
||||
GPUShaderDX12(GPUDeviceDX12* device, const StringView& name)
|
||||
: GPUResourceDX12<GPUShader>(device, name)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUShader]
|
||||
GPUShaderProgram* CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, MemoryReadStream& stream) override;
|
||||
GPUConstantBuffer* CreateCB(const String& name, uint32 size, MemoryReadStream& stream) override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
221
Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderProgramDX12.h
Normal file
221
Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderProgramDX12.h
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
/// <summary>
|
||||
/// Shaders base class for DirectX 12 backend.
|
||||
/// </summary>
|
||||
template<typename BaseType>
|
||||
class GPUShaderProgramDX12 : public BaseType
|
||||
{
|
||||
protected:
|
||||
|
||||
Array<byte> _data;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramDX11"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheBytes">The shader data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
GPUShaderProgramDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize)
|
||||
{
|
||||
BaseType::Init(initializer);
|
||||
_data.Set(cacheBytes, cacheSize);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [BaseType]
|
||||
void* GetBufferHandle() const override
|
||||
{
|
||||
return (void*)_data.Get();
|
||||
}
|
||||
|
||||
uint32 GetBufferSize() const override
|
||||
{
|
||||
return _data.Count();
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Vertex Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramVSDX12 : public GPUShaderProgramDX12<GPUShaderProgramVS>
|
||||
{
|
||||
private:
|
||||
|
||||
byte _inputLayoutSize;
|
||||
D3D12_INPUT_ELEMENT_DESC _inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramVSDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheBytes">The shader data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
/// <param name="inputLayout">The input layout description.</param>
|
||||
/// <param name="inputLayoutSize">The input layout description size.</param>
|
||||
GPUShaderProgramVSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, D3D12_INPUT_ELEMENT_DESC* inputLayout, byte inputLayoutSize)
|
||||
: GPUShaderProgramDX12(initializer, cacheBytes, cacheSize)
|
||||
, _inputLayoutSize(inputLayoutSize)
|
||||
{
|
||||
for (byte i = 0; i < inputLayoutSize; i++)
|
||||
_inputLayout[i] = inputLayout[i];
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [GPUShaderProgramDX12]
|
||||
void* GetInputLayout() const override
|
||||
{
|
||||
return (void*)_inputLayout;
|
||||
}
|
||||
|
||||
byte GetInputLayoutSize() const override
|
||||
{
|
||||
return _inputLayoutSize;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hull Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramHSDX12 : public GPUShaderProgramDX12<GPUShaderProgramHS>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramHSDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheBytes">The shader data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
/// <param name="controlPointsCount">The control points used by the hull shader for processing.</param>
|
||||
GPUShaderProgramHSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, int32 controlPointsCount)
|
||||
: GPUShaderProgramDX12(initializer, cacheBytes, cacheSize)
|
||||
{
|
||||
_controlPointsCount = controlPointsCount;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Domain Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramDSDX12 : public GPUShaderProgramDX12<GPUShaderProgramDS>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramDSDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
GPUShaderProgramDSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize)
|
||||
: GPUShaderProgramDX12(initializer, cacheBytes, cacheSize)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Geometry Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramGSDX12 : public GPUShaderProgramDX12<GPUShaderProgramGS>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramGSDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheBytes">The shader data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
GPUShaderProgramGSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize)
|
||||
: GPUShaderProgramDX12(initializer, cacheBytes, cacheSize)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Pixel Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramPSDX12 : public GPUShaderProgramDX12<GPUShaderProgramPS>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramPSDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheBytes">The shader data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
GPUShaderProgramPSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize)
|
||||
: GPUShaderProgramDX12(initializer, cacheBytes, cacheSize)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Compute Shader for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramCSDX12 : public GPUShaderProgramDX12<GPUShaderProgramCS>
|
||||
{
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
Array<byte> _data;
|
||||
ID3D12PipelineState* _state;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUShaderProgramCSDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The graphics device.</param>
|
||||
/// <param name="initializer">The program initialization data.</param>
|
||||
/// <param name="cacheBytes">The shader data.</param>
|
||||
/// <param name="cacheSize">The shader data size.</param>
|
||||
GPUShaderProgramCSDX12(GPUDeviceDX12* device, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize)
|
||||
: GPUShaderProgramDX12(initializer, cacheBytes, cacheSize)
|
||||
, _device(device)
|
||||
, _state(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GPUShaderProgramCSDX12()
|
||||
{
|
||||
_device->AddResourceToLateRelease(_state);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 compute pipeline state object
|
||||
/// </summary>
|
||||
/// <returns>DirectX 12 compute pipeline state object</returns>
|
||||
FORCE_INLINE ID3D12PipelineState* GetState() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates compute pipeline state for that compute shader.
|
||||
/// </summary>
|
||||
/// <returns>DirectX 12 compute pipeline state object</returns>
|
||||
ID3D12PipelineState* GetOrCreateState();
|
||||
};
|
||||
|
||||
#endif
|
||||
393
Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp
Normal file
393
Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUSwapChainDX12.h"
|
||||
#include "GPUContextDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
|
||||
void BackBufferDX12::Setup(GPUSwapChainDX12* window, ID3D12Resource* backbuffer)
|
||||
{
|
||||
// Cache handle and set default initial state for the backbuffers
|
||||
initResource(backbuffer, D3D12_RESOURCE_STATE_PRESENT, 1);
|
||||
|
||||
Handle.Init(window, window->GetDevice(), this, GPU_BACK_BUFFER_PIXEL_FORMAT, MSAALevel::None);
|
||||
|
||||
// Create RTV
|
||||
{
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtDesc;
|
||||
rtDesc.Format = (DXGI_FORMAT)GPU_BACK_BUFFER_PIXEL_FORMAT;
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
rtDesc.Texture2D.MipSlice = 0;
|
||||
rtDesc.Texture2D.PlaneSlice = 0;
|
||||
Handle.SetRTV(&rtDesc);
|
||||
}
|
||||
|
||||
#if GPU_USE_WINDOW_SRV
|
||||
// Create SRV
|
||||
{
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srDesc;
|
||||
srDesc.Format = (DXGI_FORMAT)GPU_BACK_BUFFER_PIXEL_FORMAT;
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srDesc.Texture2D.MostDetailedMip = 0;
|
||||
srDesc.Texture2D.MipLevels = 1;
|
||||
srDesc.Texture2D.ResourceMinLODClamp = 0;
|
||||
srDesc.Texture2D.PlaneSlice = 0;
|
||||
Handle.SetSRV(&srDesc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BackBufferDX12::Release()
|
||||
{
|
||||
Handle.Release();
|
||||
SAFE_RELEASE(_resource);
|
||||
}
|
||||
|
||||
GPUSwapChainDX12::GPUSwapChainDX12(GPUDeviceDX12* device, Window* window)
|
||||
: GPUResourceDX12(device, StringView::Empty)
|
||||
, _windowHandle(static_cast<HWND>(window->GetNativePtr()))
|
||||
, _swapChain(nullptr)
|
||||
, _currentFrameIndex(0)
|
||||
{
|
||||
ASSERT(_windowHandle);
|
||||
_window = window;
|
||||
}
|
||||
|
||||
void GPUSwapChainDX12::OnReleaseGPU()
|
||||
{
|
||||
_device->WaitForGPU();
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
// Disable fullscreen mode
|
||||
if (_swapChain)
|
||||
{
|
||||
VALIDATE_DIRECTX_RESULT(_swapChain->SetFullscreenState(false, nullptr));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Release data
|
||||
releaseBackBuffer();
|
||||
_backBuffers.Resize(0);
|
||||
if (_swapChain)
|
||||
{
|
||||
_device->AddResourceToLateRelease(_swapChain);
|
||||
_swapChain = nullptr;
|
||||
}
|
||||
_width = _height = 0;
|
||||
}
|
||||
|
||||
void GPUSwapChainDX12::releaseBackBuffer()
|
||||
{
|
||||
for (int32 i = 0; i < _backBuffers.Count(); i++)
|
||||
{
|
||||
_backBuffers[i].Release();
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUSwapChainDX12::IsFullscreen()
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
return true;
|
||||
#else
|
||||
// Check if has no swap chain created
|
||||
if (_swapChain == nullptr)
|
||||
return false;
|
||||
|
||||
// Get state
|
||||
BOOL state;
|
||||
VALIDATE_DIRECTX_RESULT(_swapChain->GetFullscreenState(&state, nullptr));
|
||||
return state == TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPUSwapChainDX12::SetFullscreen(bool isFullscreen)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_swapChain && isFullscreen != IsFullscreen())
|
||||
{
|
||||
_device->WaitForGPU();
|
||||
GPUDeviceLock lock(_device);
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC swapChainDesc;
|
||||
_swapChain->GetDesc(&swapChainDesc);
|
||||
|
||||
// Setup target for fullscreen mode
|
||||
IDXGIOutput* output = nullptr;
|
||||
if (isFullscreen && _device->Outputs.HasItems())
|
||||
{
|
||||
const uint32 outputIdx = 0;
|
||||
auto& outputDX = _device->Outputs[outputIdx];
|
||||
output = outputDX.Output.Get();
|
||||
swapChainDesc.BufferDesc = outputDX.DesktopViewMode;
|
||||
}
|
||||
|
||||
releaseBackBuffer();
|
||||
|
||||
if (FAILED(_swapChain->ResizeTarget(&swapChainDesc.BufferDesc)))
|
||||
{
|
||||
LOG(Warning, "Swapchain resize failed.");
|
||||
}
|
||||
|
||||
if (FAILED(_swapChain->SetFullscreenState(isFullscreen, output)))
|
||||
{
|
||||
LOG(Warning, "Cannot change fullscreen mode for '{0}' to {1}.", ToString(), isFullscreen);
|
||||
}
|
||||
|
||||
VALIDATE_DIRECTX_RESULT(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, _width, _height, swapChainDesc.BufferDesc.Format, swapChainDesc.Flags));
|
||||
|
||||
getBackBuffer();
|
||||
|
||||
_isFullscreen = isFullscreen;
|
||||
}
|
||||
#else
|
||||
LOG(Info, "Cannot change fullscreen mode on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GPUSwapChainDX12::Resize(int32 width, int32 height)
|
||||
{
|
||||
// Check if window state won't change
|
||||
if (width == _width && height == _height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_device->WaitForGPU();
|
||||
GPUDeviceLock lock(_device);
|
||||
_allowTearing = _device->AllowTearing;
|
||||
_format = GPU_BACK_BUFFER_PIXEL_FORMAT;
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
ReleaseGPU();
|
||||
|
||||
_currentFrameIndex = 0;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_memoryUsage = CalculateTextureMemoryUsage(_format, _width, _height, 1) * DX12_BACK_BUFFER_COUNT;
|
||||
|
||||
getBackBuffer();
|
||||
#else
|
||||
DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
|
||||
if (_swapChain == nullptr)
|
||||
{
|
||||
ReleaseGPU();
|
||||
|
||||
// Create swap chain description
|
||||
swapChainDesc.Width = width;
|
||||
swapChainDesc.Height = height;
|
||||
swapChainDesc.Format = RenderToolsDX::ToDxgiFormat(_format);
|
||||
swapChainDesc.Stereo = FALSE;
|
||||
swapChainDesc.SampleDesc.Count = 1;
|
||||
swapChainDesc.SampleDesc.Quality = 0;
|
||||
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swapChainDesc.BufferCount = DX12_BACK_BUFFER_COUNT;
|
||||
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
|
||||
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
||||
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
#if GPU_USE_WINDOW_SRV
|
||||
swapChainDesc.BufferUsage |= DXGI_USAGE_SHADER_INPUT;
|
||||
#endif
|
||||
if (_allowTearing)
|
||||
swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreenDesc;
|
||||
if (_device->Outputs.HasItems())
|
||||
{
|
||||
const uint32 outputIdx = 0;
|
||||
auto& output = _device->Outputs[outputIdx];
|
||||
fullscreenDesc.RefreshRate = output.DesktopViewMode.RefreshRate;
|
||||
fullscreenDesc.Scaling = output.DesktopViewMode.Scaling;
|
||||
fullscreenDesc.ScanlineOrdering = output.DesktopViewMode.ScanlineOrdering;
|
||||
fullscreenDesc.Windowed = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
fullscreenDesc.RefreshRate.Numerator = 0;
|
||||
fullscreenDesc.RefreshRate.Denominator = 1;
|
||||
fullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
||||
fullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
||||
fullscreenDesc.Windowed = TRUE;
|
||||
}
|
||||
|
||||
// Create swap chain (it needs the queue so that it can force a flush on it)
|
||||
IDXGISwapChain1* swapChain;
|
||||
auto dxgiFactory = _device->GetDXGIFactory();
|
||||
VALIDATE_DIRECTX_RESULT(dxgiFactory->CreateSwapChainForHwnd(_device->GetCommandQueueDX12(), _windowHandle, &swapChainDesc, &fullscreenDesc, nullptr, &swapChain));
|
||||
_swapChain = static_cast<IDXGISwapChain3*>(swapChain);
|
||||
ASSERT(_swapChain);
|
||||
DX_SET_DEBUG_NAME_EX(_swapChain, TEXT("RenderOutput"), TEXT("SwapChain"), TEXT(""));
|
||||
_swapChain->SetBackgroundColor((const DXGI_RGBA*)Color::Black.Raw);
|
||||
_backBuffers.Resize(swapChainDesc.BufferCount);
|
||||
|
||||
// Disable DXGI changes to the window
|
||||
dxgiFactory->MakeWindowAssociation(_windowHandle, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER);
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseBackBuffer();
|
||||
|
||||
_swapChain->GetDesc1(&swapChainDesc);
|
||||
|
||||
VALIDATE_DIRECTX_RESULT(_swapChain->ResizeBuffers(swapChainDesc.BufferCount, width, height, swapChainDesc.Format, swapChainDesc.Flags));
|
||||
}
|
||||
|
||||
_currentFrameIndex = _swapChain->GetCurrentBackBufferIndex();
|
||||
_width = width;
|
||||
_height = height;
|
||||
_memoryUsage = CalculateTextureMemoryUsage(_format, _width, _height, 1) * swapChainDesc.BufferCount;
|
||||
|
||||
getBackBuffer();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUSwapChainDX12::CopyBackbuffer(GPUContext* context, GPUTexture* dst)
|
||||
{
|
||||
const auto contextDX12 = (GPUContextDX12*)context;
|
||||
|
||||
auto dstDX12 = (GPUTextureDX12*)dst;
|
||||
auto backbuffer = &_backBuffers[_currentFrameIndex];
|
||||
|
||||
contextDX12->SetResourceState(dstDX12, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
contextDX12->SetResourceState(backbuffer, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
contextDX12->FlushResourceBarriers();
|
||||
|
||||
if (dst->IsStaging())
|
||||
{
|
||||
const int32 copyOffset = dstDX12->ComputeBufferOffset(0, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLocation;
|
||||
dstLocation.pResource = dstDX12->GetResource();
|
||||
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
dstLocation.PlacedFootprint.Offset = copyOffset;
|
||||
dstLocation.PlacedFootprint.Footprint.Width = dstDX12->CalculateMipSize(dstDX12->Width(), 0);
|
||||
dstLocation.PlacedFootprint.Footprint.Height = dstDX12->CalculateMipSize(dstDX12->Height(), 0);
|
||||
dstLocation.PlacedFootprint.Footprint.Depth = dstDX12->CalculateMipSize(dstDX12->Depth(), 0);
|
||||
dstLocation.PlacedFootprint.Footprint.Format = RenderToolsDX::ToDxgiFormat(dstDX12->Format());
|
||||
dstLocation.PlacedFootprint.Footprint.RowPitch = dstDX12->ComputeRowPitch(0, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLocation;
|
||||
srcLocation.pResource = backbuffer->GetResource();
|
||||
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
srcLocation.SubresourceIndex = 0;
|
||||
|
||||
contextDX12->GetCommandList()->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
contextDX12->GetCommandList()->CopyResource(dstDX12->GetResource(), backbuffer->GetResource());
|
||||
}
|
||||
}
|
||||
|
||||
void GPUSwapChainDX12::getBackBuffer()
|
||||
{
|
||||
_backBuffers.Resize(DX12_BACK_BUFFER_COUNT);
|
||||
for (int32 i = 0; i < _backBuffers.Count(); i++)
|
||||
{
|
||||
ID3D12Resource* backbuffer;
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
D3D12_HEAP_PROPERTIES swapChainHeapProperties;
|
||||
swapChainHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
swapChainHeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
swapChainHeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
swapChainHeapProperties.CreationNodeMask = 1;
|
||||
swapChainHeapProperties.VisibleNodeMask = 1;
|
||||
D3D12_RESOURCE_DESC swapChainBufferDesc;
|
||||
swapChainBufferDesc.MipLevels = 1;
|
||||
swapChainBufferDesc.Format = RenderToolsDX::ToDxgiFormat(_format);
|
||||
swapChainBufferDesc.Width = _width;
|
||||
swapChainBufferDesc.Height = _height;
|
||||
swapChainBufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
||||
swapChainBufferDesc.DepthOrArraySize = 1;
|
||||
swapChainBufferDesc.SampleDesc.Count = 1;
|
||||
swapChainBufferDesc.SampleDesc.Quality = 0;
|
||||
swapChainBufferDesc.Alignment = 0;
|
||||
swapChainBufferDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
swapChainBufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {};
|
||||
swapChainOptimizedClearValue.Format = swapChainBufferDesc.Format;
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(
|
||||
&swapChainHeapProperties,
|
||||
D3D12_HEAP_FLAG_ALLOW_DISPLAY,
|
||||
&swapChainBufferDesc,
|
||||
D3D12_RESOURCE_STATE_PRESENT,
|
||||
&swapChainOptimizedClearValue,
|
||||
IID_GRAPHICS_PPV_ARGS(&backbuffer)));
|
||||
#else
|
||||
VALIDATE_DIRECTX_RESULT(_swapChain->GetBuffer(i, IID_PPV_ARGS(&backbuffer)));
|
||||
#endif
|
||||
DX_SET_DEBUG_NAME_EX(backbuffer, TEXT("RenderOutput"), TEXT("BackBuffer"), i);
|
||||
_backBuffers[i].Setup(this, backbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
|
||||
void GPUSwapChainDX12::Begin(RenderTask* task)
|
||||
{
|
||||
// Wait until frame start is signaled
|
||||
_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL;
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetDevice()->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &_framePipelineToken));
|
||||
|
||||
GPUSwapChain::Begin(task);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void GPUSwapChainDX12::End(RenderTask* task)
|
||||
{
|
||||
GPUSwapChain::End(task);
|
||||
|
||||
auto context = _device->GetMainContextDX12();
|
||||
|
||||
// Indicate that the back buffer will be used to present a frame
|
||||
// Note: after that we should not use this backbuffer
|
||||
context->SetResourceState(&_backBuffers[_currentFrameIndex], D3D12_RESOURCE_STATE_PRESENT);
|
||||
|
||||
// Send event
|
||||
context->OnSwapChainFlush();
|
||||
}
|
||||
|
||||
void GPUSwapChainDX12::Present(bool vsync)
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
ID3D12Resource* backBuffer = _backBuffers[_currentFrameIndex].GetResource();
|
||||
D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {};
|
||||
planeParameters.Token = _framePipelineToken;
|
||||
planeParameters.ResourceCount = 1;
|
||||
planeParameters.ppResources = &backBuffer;
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetCommandQueueDX12()->PresentX(1, &planeParameters, nullptr));
|
||||
|
||||
// Base
|
||||
GPUSwapChain::Present(vsync);
|
||||
|
||||
// Switch to next back buffer
|
||||
_currentFrameIndex = (_currentFrameIndex + 1) % DX12_BACK_BUFFER_COUNT;
|
||||
#else
|
||||
// Present frame
|
||||
UINT presentFlags = 0;
|
||||
if (!vsync && !_isFullscreen && _allowTearing)
|
||||
{
|
||||
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
|
||||
}
|
||||
const HRESULT res = _swapChain->Present(vsync ? 1 : 0, presentFlags);
|
||||
LOG_DIRECTX_RESULT(res);
|
||||
|
||||
// Base
|
||||
GPUSwapChain::Present(vsync);
|
||||
|
||||
// Switch to next back buffer
|
||||
_currentFrameIndex = _swapChain->GetCurrentBackBufferIndex();
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUTextureView* GPUSwapChainDX12::GetBackBufferView()
|
||||
{
|
||||
return &_backBuffers[_currentFrameIndex].Handle;
|
||||
}
|
||||
|
||||
#endif
|
||||
119
Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.h
Normal file
119
Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.h
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Engine/Graphics/GPUSwapChain.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
#include "ResourceOwnerDX12.h"
|
||||
#include "GPUTextureDX12.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
class GPUSwapChainDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a DirectX 12 swap chain back buffer wrapper object.
|
||||
/// </summary>
|
||||
class BackBufferDX12 : public ResourceOwnerDX12
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The render target surface handle.
|
||||
/// </summary>
|
||||
GPUTextureViewDX12 Handle;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Setup backbuffer wrapper
|
||||
/// </summary>
|
||||
/// <param name="window">Parent window</param>
|
||||
/// <param name="backbuffer">Back buffer DirectX 12 resource</param>
|
||||
void Setup(GPUSwapChainDX12* window, ID3D12Resource* backbuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Release references to the backbuffer
|
||||
/// </summary>
|
||||
void Release();
|
||||
|
||||
public:
|
||||
|
||||
// [ResourceOwnerDX12]
|
||||
GPUResource* AsGPUResource() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Graphics Device rendering output for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUSwapChainDX12 : public GPUResourceDX12<GPUSwapChain>
|
||||
{
|
||||
friend class WindowsWindow;
|
||||
friend class GPUContextDX12;
|
||||
friend GPUDeviceDX12;
|
||||
|
||||
private:
|
||||
|
||||
bool _allowTearing, _isFullscreen;
|
||||
HWND _windowHandle;
|
||||
IDXGISwapChain3* _swapChain;
|
||||
int32 _currentFrameIndex;
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
D3D12XBOX_FRAME_PIPELINE_TOKEN _framePipelineToken;
|
||||
#endif
|
||||
Array<BackBufferDX12, FixedAllocation<4>> _backBuffers;
|
||||
|
||||
public:
|
||||
|
||||
GPUSwapChainDX12(GPUDeviceDX12* device, Window* window);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets current backbuffer resource.
|
||||
/// </summary>
|
||||
/// <returns>The backbuffer resource.</returns>
|
||||
ID3D12Resource* GetBackBuffer() const
|
||||
{
|
||||
return _backBuffers[_currentFrameIndex].GetResource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets render target handle for DirectX 12 backend.
|
||||
/// </summary>
|
||||
/// <returns>The render target handle.</returns>
|
||||
const GPUTextureViewDX12* GetBackBufferHandleDX12() const
|
||||
{
|
||||
return &_backBuffers[_currentFrameIndex].Handle;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void getBackBuffer();
|
||||
void releaseBackBuffer();
|
||||
|
||||
public:
|
||||
|
||||
// [GPUSwapChain]
|
||||
bool IsFullscreen() override;
|
||||
void SetFullscreen(bool isFullscreen) override;
|
||||
GPUTextureView* GetBackBufferView() override;
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
void Begin(RenderTask* task) override;
|
||||
#endif
|
||||
void End(RenderTask* task) override;
|
||||
void Present(bool vsync) override;
|
||||
bool Resize(int32 width, int32 height) override;
|
||||
void CopyBackbuffer(GPUContext* context, GPUTexture* dst) override;
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
686
Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp
Normal file
686
Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUTextureDX12.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
|
||||
bool GPUTextureDX12::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
|
||||
{
|
||||
if (!IsStaging())
|
||||
{
|
||||
LOG(Warning, "Texture::GetData is valid only for staging resources.");
|
||||
return true;
|
||||
}
|
||||
|
||||
GPUDeviceLock lock(_device);
|
||||
|
||||
// Internally it's a buffer, so adapt resource index and offset
|
||||
const uint32 subresource = RenderToolsDX::CalcSubresourceIndex(mipMapIndex, arrayOrDepthSliceIndex, MipLevels());
|
||||
const int32 offsetInBytes = ComputeBufferOffset(subresource, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
const int32 lengthInBytes = ComputeSubresourceSize(subresource, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
const int32 rowPitch = ComputeRowPitch(mipMapIndex, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
const int32 depthPitch = ComputeSlicePitch(mipMapIndex, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
|
||||
// Map the staging resource mip map for reading
|
||||
D3D12_RANGE range;
|
||||
range.Begin = offsetInBytes;
|
||||
range.End = offsetInBytes + lengthInBytes;
|
||||
void* mapped;
|
||||
const HRESULT mapResult = _resource->Map(0, &range, &mapped);
|
||||
if (FAILED(mapResult))
|
||||
{
|
||||
LOG_DIRECTX_RESULT(mapResult);
|
||||
return true;
|
||||
}
|
||||
mapped = (byte*)mapped + offsetInBytes;
|
||||
|
||||
// Check if target row pitch is the same
|
||||
if (mipRowPitch == rowPitch || mipRowPitch == 0)
|
||||
{
|
||||
// Init mip info
|
||||
data.Lines = depthPitch / rowPitch;
|
||||
data.DepthPitch = depthPitch;
|
||||
data.RowPitch = rowPitch;
|
||||
|
||||
// Copy data
|
||||
data.Data.Copy((byte*)mapped, depthPitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Init mip info
|
||||
data.Lines = depthPitch / rowPitch;
|
||||
data.DepthPitch = mipRowPitch * data.Lines;
|
||||
data.RowPitch = mipRowPitch;
|
||||
|
||||
// Copy data
|
||||
data.Data.Allocate(data.DepthPitch);
|
||||
for (uint32 i = 0; i < data.Lines; i++)
|
||||
{
|
||||
Platform::MemoryCopy(data.Data.Get() + data.RowPitch * i, ((byte*)mapped) + rowPitch * i, data.RowPitch);
|
||||
}
|
||||
}
|
||||
|
||||
// Unmap buffer
|
||||
_resource->Unmap(0, nullptr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUTextureDX12::OnInit()
|
||||
{
|
||||
ID3D12Resource* resource;
|
||||
|
||||
// Cache formats
|
||||
const PixelFormat format = Format();
|
||||
const PixelFormat typelessFormat = PixelFormatExtensions::MakeTypeless(format);
|
||||
const DXGI_FORMAT dxgiFormat = RenderToolsDX::ToDxgiFormat(typelessFormat);
|
||||
_dxgiFormatDSV = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindDepthStencilFormat(format));
|
||||
_dxgiFormatSRV = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindShaderResourceFormat(format, _sRGB));
|
||||
_dxgiFormatRTV = _dxgiFormatSRV;
|
||||
_dxgiFormatUAV = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindUnorderedAccessFormat(format));
|
||||
|
||||
// Cache properties
|
||||
auto device = _device->GetDevice();
|
||||
bool useSRV = IsShaderResource();
|
||||
bool useDSV = IsDepthStencil();
|
||||
bool useRTV = IsRenderTarget();
|
||||
bool useUAV = IsUnorderedAccess();
|
||||
int32 depthOrArraySize = IsVolume() ? Depth() : ArraySize();
|
||||
|
||||
if (IsStaging())
|
||||
{
|
||||
// Initialize as a buffer
|
||||
const int32 totalSize = ComputeBufferTotalSize(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 1;
|
||||
heapProperties.VisibleNodeMask = 1;
|
||||
D3D12_RESOURCE_DESC resourceDesc;
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Alignment = 0;
|
||||
resourceDesc.Width = totalSize;
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.SampleDesc.Quality = 0;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
auto result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&resource));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
initResource(resource, D3D12_RESOURCE_STATE_COPY_DEST, 1);
|
||||
DX_SET_DEBUG_NAME(_resource, GetName());
|
||||
_memoryUsage = totalSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON;
|
||||
|
||||
// Create texture description
|
||||
D3D12_RESOURCE_DESC resourceDesc;
|
||||
resourceDesc.MipLevels = _desc.MipLevels;
|
||||
resourceDesc.Format = dxgiFormat;
|
||||
resourceDesc.Width = _desc.Width;
|
||||
resourceDesc.Height = _desc.Height;
|
||||
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
resourceDesc.DepthOrArraySize = depthOrArraySize;
|
||||
resourceDesc.SampleDesc.Count = static_cast<UINT>(_desc.MultiSampleLevel);
|
||||
resourceDesc.SampleDesc.Quality = IsMultiSample() ? GPUDeviceDX12::GetMaxMSAAQuality((int32)_desc.MultiSampleLevel) : 0;
|
||||
resourceDesc.Alignment = 0;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
resourceDesc.Dimension = IsVolume() ? D3D12_RESOURCE_DIMENSION_TEXTURE3D : D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
if (useRTV)
|
||||
{
|
||||
resourceDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
||||
initialState = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
}
|
||||
else if (useDSV)
|
||||
{
|
||||
resourceDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
initialState = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
|
||||
if (!useSRV)
|
||||
{
|
||||
// Only deny shader resources if it's a depth resource that will never be used as SRV
|
||||
resourceDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
|
||||
}
|
||||
}
|
||||
if (useUAV)
|
||||
{
|
||||
resourceDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
|
||||
// Create heap properties
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 1;
|
||||
heapProperties.VisibleNodeMask = 1;
|
||||
|
||||
// Create clear value (used by render targets and depth stencil buffers)
|
||||
D3D12_CLEAR_VALUE* clearValuePtr = nullptr;
|
||||
D3D12_CLEAR_VALUE clearValue;
|
||||
if (useRTV)
|
||||
{
|
||||
clearValue.Format = _dxgiFormatSRV;
|
||||
Platform::MemoryCopy(&clearValue.Color, _desc.DefaultClearColor.Raw, sizeof(Color));
|
||||
clearValuePtr = &clearValue;
|
||||
}
|
||||
else if (useDSV)
|
||||
{
|
||||
clearValue.Format = _dxgiFormatDSV;
|
||||
clearValue.DepthStencil.Depth = 1.0f;
|
||||
clearValue.DepthStencil.Stencil = 0;
|
||||
clearValuePtr = &clearValue;
|
||||
}
|
||||
|
||||
if (IsRegularTexture())
|
||||
initialState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
// Create texture
|
||||
auto result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, initialState, clearValuePtr, IID_PPV_ARGS(&resource));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
|
||||
// Set state
|
||||
bool isRead = useSRV || useUAV;
|
||||
bool isWrite = useDSV || useRTV || useUAV;
|
||||
initResource(resource, initialState, resourceDesc, isRead && isWrite);
|
||||
DX_SET_DEBUG_NAME(_resource, GetName());
|
||||
_memoryUsage = calculateMemoryUsage();
|
||||
|
||||
// Initialize handles to the resource
|
||||
if (IsRegularTexture())
|
||||
{
|
||||
// 'Regular' texture is using only one handle (texture/cubemap)
|
||||
_handlesPerSlice.Resize(1, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create all handles
|
||||
initHandles();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUTextureDX12::onResidentMipsChanged()
|
||||
{
|
||||
// We support changing resident mip maps only for regular textures (render targets and depth buffers don't use that feature at all)
|
||||
ASSERT(IsRegularTexture() && _handlesPerSlice.Count() == 1);
|
||||
ASSERT(!IsVolume());
|
||||
|
||||
// Fill description
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srDesc;
|
||||
srDesc.Format = _dxgiFormatSRV;
|
||||
srDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
if (IsCubeMap())
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
srDesc.TextureCube.MostDetailedMip = MipLevels() - ResidentMipLevels();
|
||||
srDesc.TextureCube.MipLevels = ResidentMipLevels();
|
||||
srDesc.TextureCube.ResourceMinLODClamp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srDesc.Texture2D.MostDetailedMip = MipLevels() - ResidentMipLevels();
|
||||
srDesc.Texture2D.MipLevels = ResidentMipLevels();
|
||||
srDesc.Texture2D.PlaneSlice = 0;
|
||||
srDesc.Texture2D.ResourceMinLODClamp = 0;
|
||||
}
|
||||
|
||||
// Change view
|
||||
if (_handlesPerSlice[0].GetParent() == nullptr)
|
||||
_handlesPerSlice[0].Init(this, _device, this, Format(), MultiSampleLevel());
|
||||
_handlesPerSlice[0].SetSRV(&srDesc);
|
||||
}
|
||||
|
||||
void GPUTextureDX12::OnReleaseGPU()
|
||||
{
|
||||
_handlesPerMip.Resize(0, false);
|
||||
_handlesPerSlice.Resize(0, false);
|
||||
_handleArray.Release();
|
||||
_handleVolume.Release();
|
||||
_srv.Release();
|
||||
_uav.Release();
|
||||
releaseResource();
|
||||
|
||||
// Base
|
||||
GPUTexture::OnReleaseGPU();
|
||||
}
|
||||
|
||||
void GPUTextureDX12::initHandles()
|
||||
{
|
||||
D3D12_RENDER_TARGET_VIEW_DESC rtDesc;
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srDesc;
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsDesc;
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
|
||||
|
||||
rtDesc.Format = _dxgiFormatRTV;
|
||||
srDesc.Format = _dxgiFormatSRV;
|
||||
srDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
dsDesc.Format = _dxgiFormatDSV;
|
||||
dsDesc.Flags = D3D12_DSV_FLAG_NONE;
|
||||
uavDesc.Format = _dxgiFormatUAV;
|
||||
|
||||
// Cache properties
|
||||
bool useSRV = IsShaderResource();
|
||||
bool useDSV = IsDepthStencil();
|
||||
bool useRTV = IsRenderTarget();
|
||||
bool useUAV = IsUnorderedAccess();
|
||||
int32 arraySize = ArraySize();
|
||||
int32 mipLevels = MipLevels();
|
||||
bool isArray = arraySize > 1;
|
||||
bool isCubeMap = IsCubeMap();
|
||||
bool isMsaa = IsMultiSample();
|
||||
bool isVolume = IsVolume();
|
||||
auto format = Format();
|
||||
auto msaa = MultiSampleLevel();
|
||||
|
||||
// Create resource views
|
||||
if (useUAV)
|
||||
{
|
||||
if (isVolume)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
|
||||
uavDesc.Texture3D.MipSlice = 0;
|
||||
uavDesc.Texture3D.WSize = Depth();
|
||||
uavDesc.Texture3D.FirstWSlice = 0;
|
||||
}
|
||||
else if (isArray)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
||||
uavDesc.Texture2DArray.MipSlice = 0;
|
||||
uavDesc.Texture2DArray.ArraySize = arraySize * 6;
|
||||
uavDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
uavDesc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
uavDesc.Texture2D.MipSlice = 0;
|
||||
uavDesc.Texture2D.PlaneSlice = 0;
|
||||
}
|
||||
_uav.CreateUAV(_device, _resource, &uavDesc);
|
||||
}
|
||||
if (isVolume)
|
||||
{
|
||||
// Create handle for whole 3d texture
|
||||
_handleVolume.Init(this, _device, this, format, msaa);
|
||||
if (useSRV)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
|
||||
srDesc.Texture3D.MostDetailedMip = 0;
|
||||
srDesc.Texture3D.MipLevels = MipLevels();
|
||||
srDesc.Texture3D.ResourceMinLODClamp = 0;
|
||||
_handleVolume.SetSRV(&srDesc);
|
||||
}
|
||||
if (useRTV)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
|
||||
rtDesc.Texture3D.MipSlice = 0;
|
||||
rtDesc.Texture3D.FirstWSlice = 0;
|
||||
rtDesc.Texture3D.WSize = Depth();
|
||||
_handleVolume.SetRTV(&rtDesc);
|
||||
}
|
||||
if (useUAV)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
|
||||
uavDesc.Texture2D.MipSlice = 0;
|
||||
uavDesc.Texture2D.PlaneSlice = 0;
|
||||
_handleVolume.SetUAV(&uavDesc);
|
||||
}
|
||||
|
||||
// Init per slice views
|
||||
_handlesPerSlice.Resize(Depth(), false);
|
||||
if (_desc.HasPerSliceViews() && useRTV)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
|
||||
rtDesc.Texture3D.MipSlice = 0;
|
||||
rtDesc.Texture3D.WSize = 1;
|
||||
|
||||
for (int32 sliceIndex = 0; sliceIndex < Depth(); sliceIndex++)
|
||||
{
|
||||
rtDesc.Texture3D.FirstWSlice = sliceIndex;
|
||||
_handlesPerSlice[sliceIndex].Init(this, _device, this, format, msaa);
|
||||
_handlesPerSlice[sliceIndex].SetRTV(&rtDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isArray)
|
||||
{
|
||||
// Resize handles
|
||||
_handlesPerSlice.Resize(ArraySize(), false);
|
||||
|
||||
// Create per array slice handles
|
||||
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
|
||||
{
|
||||
_handlesPerSlice[arrayIndex].Init(this, _device, this, format, msaa);
|
||||
|
||||
if (useDSV)
|
||||
{
|
||||
/*if (isCubeMap)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
||||
dsDesc.Texture2DArray.ArraySize = 6;
|
||||
dsDesc.Texture2DArray.FirstArraySlice = arrayIndex * 6;
|
||||
dsDesc.Texture2DArray.MipSlice = 0;
|
||||
}
|
||||
else*/
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
||||
dsDesc.Texture2DArray.ArraySize = 1;
|
||||
dsDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
dsDesc.Texture2DArray.MipSlice = 0;
|
||||
}
|
||||
_handlesPerSlice[arrayIndex].SetDSV(&dsDesc);
|
||||
}
|
||||
if (useRTV)
|
||||
{
|
||||
/*if (isCubeMap)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
||||
rtDesc.Texture2DArray.ArraySize = 6;
|
||||
rtDesc.Texture2DArray.FirstArraySlice = arrayIndex * 6;
|
||||
rtDesc.Texture2DArray.MipSlice = 0;
|
||||
rtDesc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
else*/
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
||||
rtDesc.Texture2DArray.ArraySize = 1;
|
||||
rtDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
rtDesc.Texture2DArray.MipSlice = 0;
|
||||
rtDesc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
_handlesPerSlice[arrayIndex].SetRTV(&rtDesc);
|
||||
}
|
||||
if (useSRV)
|
||||
{
|
||||
/*if (isCubeMap)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
||||
srDesc.TextureCubeArray.First2DArrayFace = arrayIndex * 6;
|
||||
srDesc.TextureCubeArray.NumCubes = 1;
|
||||
srDesc.TextureCubeArray.MipLevels = mipLevels;
|
||||
srDesc.TextureCubeArray.MostDetailedMip = 0;
|
||||
}
|
||||
else*/
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
srDesc.Texture2DArray.ArraySize = 1;
|
||||
srDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
srDesc.Texture2DArray.MipLevels = mipLevels;
|
||||
srDesc.Texture2DArray.MostDetailedMip = 0;
|
||||
srDesc.Texture2DArray.PlaneSlice = 0;
|
||||
srDesc.Texture2DArray.ResourceMinLODClamp = 0;
|
||||
}
|
||||
_handlesPerSlice[arrayIndex].SetSRV(&srDesc);
|
||||
}
|
||||
if (useUAV)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
||||
uavDesc.Texture2DArray.ArraySize = 1;
|
||||
uavDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
uavDesc.Texture2DArray.MipSlice = 0;
|
||||
uavDesc.Texture2DArray.PlaneSlice = 0;
|
||||
_handlesPerSlice[arrayIndex].SetSRV(&srDesc);
|
||||
}
|
||||
}
|
||||
|
||||
// Create whole array handle
|
||||
{
|
||||
_handleArray.Init(this, _device, this, format, msaa);
|
||||
if (useDSV)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
||||
dsDesc.Texture2DArray.ArraySize = arraySize;
|
||||
dsDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
dsDesc.Texture2DArray.MipSlice = 0;
|
||||
_handleArray.SetDSV(&dsDesc);
|
||||
}
|
||||
if (useRTV)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
||||
rtDesc.Texture2DArray.ArraySize = arraySize;
|
||||
rtDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
rtDesc.Texture2DArray.MipSlice = 0;
|
||||
rtDesc.Texture2DArray.PlaneSlice = 0;
|
||||
_handleArray.SetRTV(&rtDesc);
|
||||
}
|
||||
if (useSRV)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
srDesc.TextureCube.MostDetailedMip = 0;
|
||||
srDesc.TextureCube.MipLevels = mipLevels;
|
||||
srDesc.TextureCube.ResourceMinLODClamp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
srDesc.Texture2DArray.ArraySize = arraySize;
|
||||
srDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
srDesc.Texture2DArray.MipLevels = mipLevels;
|
||||
srDesc.Texture2DArray.MostDetailedMip = 0;
|
||||
srDesc.Texture2DArray.ResourceMinLODClamp = 0;
|
||||
srDesc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
_handleArray.SetSRV(&srDesc);
|
||||
}
|
||||
if (useUAV)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
||||
uavDesc.Texture2DArray.ArraySize = arraySize;
|
||||
uavDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
uavDesc.Texture2DArray.MipSlice = 0;
|
||||
uavDesc.Texture2DArray.PlaneSlice = 0;
|
||||
_handleArray.SetUAV(&uavDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create single handle for the whole texture
|
||||
_handlesPerSlice.Resize(1, false);
|
||||
_handlesPerSlice[0].Init(this, _device, this, format, msaa);
|
||||
if (useDSV)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
||||
dsDesc.Texture2DArray.MipSlice = 0;
|
||||
dsDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
dsDesc.Texture2DArray.ArraySize = arraySize * 6;
|
||||
}
|
||||
else if (isMsaa)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
dsDesc.Texture2D.MipSlice = 0;
|
||||
}
|
||||
_handlesPerSlice[0].SetDSV(&dsDesc);
|
||||
}
|
||||
if (useRTV)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
||||
rtDesc.Texture2DArray.MipSlice = 0;
|
||||
rtDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
rtDesc.Texture2DArray.ArraySize = arraySize * 6;
|
||||
rtDesc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
else if (isMsaa)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
rtDesc.Texture2D.MipSlice = 0;
|
||||
rtDesc.Texture2D.PlaneSlice = 0;
|
||||
}
|
||||
_handlesPerSlice[0].SetRTV(&rtDesc);
|
||||
}
|
||||
if (useSRV)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
srDesc.TextureCube.MostDetailedMip = 0;
|
||||
srDesc.TextureCube.MipLevels = mipLevels;
|
||||
srDesc.TextureCube.ResourceMinLODClamp = 0;
|
||||
}
|
||||
else if (isMsaa)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srDesc.Texture2D.MostDetailedMip = 0;
|
||||
srDesc.Texture2D.MipLevels = mipLevels;
|
||||
srDesc.Texture2D.ResourceMinLODClamp = 0;
|
||||
srDesc.Texture2D.PlaneSlice = 0;
|
||||
}
|
||||
_handlesPerSlice[0].SetSRV(&srDesc);
|
||||
}
|
||||
if (useUAV)
|
||||
{
|
||||
if (isCubeMap || isArray)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
||||
uavDesc.Texture2DArray.ArraySize = arraySize;
|
||||
uavDesc.Texture2DArray.MipSlice = 0;
|
||||
uavDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
uavDesc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
uavDesc.Texture2D.MipSlice = 0;
|
||||
uavDesc.Texture2D.PlaneSlice = 0;
|
||||
}
|
||||
_handlesPerSlice[0].SetUAV(&uavDesc);
|
||||
}
|
||||
}
|
||||
|
||||
// Init per mip map handles
|
||||
if (HasPerMipViews())
|
||||
{
|
||||
// Init handles
|
||||
_handlesPerMip.Resize(arraySize, false);
|
||||
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
|
||||
{
|
||||
auto& slice = _handlesPerMip[arrayIndex];
|
||||
slice.Resize(mipLevels, false);
|
||||
|
||||
for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++)
|
||||
{
|
||||
int32 subresourceIndex = arrayIndex * mipLevels + mipIndex;
|
||||
slice[mipIndex].Init(this, _device, this, format, msaa, subresourceIndex);
|
||||
|
||||
// DSV
|
||||
if (useDSV)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
||||
dsDesc.Texture2DArray.ArraySize = 1;
|
||||
dsDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
dsDesc.Texture2DArray.MipSlice = mipIndex;
|
||||
slice[mipIndex].SetDSV(&dsDesc);
|
||||
}
|
||||
|
||||
// RTV
|
||||
if (useRTV)
|
||||
{
|
||||
rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
|
||||
rtDesc.Texture2DArray.ArraySize = 1;
|
||||
rtDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
rtDesc.Texture2DArray.MipSlice = mipIndex;
|
||||
rtDesc.Texture2DArray.PlaneSlice = 0;
|
||||
slice[mipIndex].SetRTV(&rtDesc);
|
||||
}
|
||||
|
||||
// SRV
|
||||
if (useSRV)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
srDesc.Texture2DArray.ArraySize = 1;
|
||||
srDesc.Texture2DArray.FirstArraySlice = arrayIndex;
|
||||
srDesc.Texture2DArray.MipLevels = 1;
|
||||
srDesc.Texture2DArray.MostDetailedMip = mipIndex;
|
||||
srDesc.Texture2DArray.ResourceMinLODClamp = 0;
|
||||
srDesc.Texture2DArray.PlaneSlice = 0;
|
||||
slice[mipIndex].SetSRV(&srDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read-only depth-stencil
|
||||
if (_desc.Flags & GPUTextureFlags::ReadOnlyDepthView)
|
||||
{
|
||||
_handleReadOnlyDepth.Init(this, _device, this, format, msaa);
|
||||
if (useDSV)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
|
||||
dsDesc.Texture2DArray.MipSlice = 0;
|
||||
dsDesc.Texture2DArray.FirstArraySlice = 0;
|
||||
dsDesc.Texture2DArray.ArraySize = arraySize * 6;
|
||||
}
|
||||
else if (isMsaa)
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
dsDesc.Texture2D.MipSlice = 0;
|
||||
}
|
||||
dsDesc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH;
|
||||
if (PixelFormatExtensions::HasStencil(format))
|
||||
dsDesc.Flags |= D3D12_DSV_FLAG_READ_ONLY_STENCIL;
|
||||
_handleReadOnlyDepth.SetDSV(&dsDesc);
|
||||
}
|
||||
ASSERT(!useRTV);
|
||||
if (useSRV)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
srDesc.TextureCube.MostDetailedMip = 0;
|
||||
srDesc.TextureCube.MipLevels = mipLevels;
|
||||
srDesc.TextureCube.ResourceMinLODClamp = 0;
|
||||
}
|
||||
else if (isMsaa)
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srDesc.Texture2D.MostDetailedMip = 0;
|
||||
srDesc.Texture2D.MipLevels = mipLevels;
|
||||
srDesc.Texture2D.ResourceMinLODClamp = 0;
|
||||
srDesc.Texture2D.PlaneSlice = 0;
|
||||
}
|
||||
_handleReadOnlyDepth.SetSRV(&srDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
313
Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h
Normal file
313
Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h
Normal file
@@ -0,0 +1,313 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "IShaderResourceDX12.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
/// <summary>
|
||||
/// The texture view for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUTextureViewDX12 : public GPUTextureView, public IShaderResourceDX12
|
||||
{
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device = nullptr;
|
||||
ResourceOwnerDX12* _owner = nullptr;
|
||||
DescriptorHeapWithSlotsDX12::Slot _rtv, _srv, _dsv, _uav;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUTextureViewDX12"/> class.
|
||||
/// </summary>
|
||||
GPUTextureViewDX12()
|
||||
{
|
||||
}
|
||||
|
||||
GPUTextureViewDX12(const GPUTextureViewDX12& other)
|
||||
: GPUTextureViewDX12()
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
CRASH; // Not used
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUTextureViewDX12& operator=(const GPUTextureViewDX12& other)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
CRASH; // Not used
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="GPUTextureViewDX12"/> class.
|
||||
/// </summary>
|
||||
~GPUTextureViewDX12()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent resource</param>
|
||||
/// <param name="device">Graphics Device</param>
|
||||
/// <param name="owner">Resource owner</param>
|
||||
/// <param name="format">Parent texture format</param>
|
||||
/// <param name="msaa">Parent texture multi-sample level</param>
|
||||
/// <param name="subresourceIndex">Used subresource index or -1 to cover whole resource.</param>
|
||||
void Init(GPUResource* parent, GPUDeviceDX12* device, ResourceOwnerDX12* owner, PixelFormat format, MSAALevel msaa, int32 subresourceIndex = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)
|
||||
{
|
||||
GPUTextureView::Init(parent, format, msaa);
|
||||
SubresourceIndex = subresourceIndex;
|
||||
_device = device;
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the view.
|
||||
/// </summary>
|
||||
void Release()
|
||||
{
|
||||
_rtv.Release();
|
||||
_srv.Release();
|
||||
_dsv.Release();
|
||||
_uav.Release();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Sets the render target view.
|
||||
/// </summary>
|
||||
/// <param name="rtvDesc">The RTV desc.</param>
|
||||
void SetRTV(D3D12_RENDER_TARGET_VIEW_DESC* rtvDesc)
|
||||
{
|
||||
if (rtvDesc)
|
||||
{
|
||||
_rtv.CreateRTV(_device, _owner->GetResource(), rtvDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rtv.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the shader resource view.
|
||||
/// </summary>
|
||||
/// <param name="srvDesc">The SRV desc.</param>
|
||||
void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC* srvDesc)
|
||||
{
|
||||
if (srvDesc)
|
||||
{
|
||||
_srv.CreateSRV(_device, _owner->GetResource(), srvDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
_srv.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the depth stencil view.
|
||||
/// </summary>
|
||||
/// <param name="dsvDesc">The DSV desc.</param>
|
||||
void SetDSV(D3D12_DEPTH_STENCIL_VIEW_DESC* dsvDesc)
|
||||
{
|
||||
if (dsvDesc)
|
||||
{
|
||||
_dsv.CreateDSV(_device, _owner->GetResource(), dsvDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dsv.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the unordered access view.
|
||||
/// </summary>
|
||||
/// <param name="uavDesc">The UAV desc.</param>
|
||||
/// <param name="counterResource">The counter buffer resource.</param>
|
||||
void SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC* uavDesc, ID3D12Resource* counterResource = nullptr)
|
||||
{
|
||||
if (uavDesc)
|
||||
{
|
||||
_uav.CreateUAV(_device, _owner->GetResource(), uavDesc, counterResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
_uav.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CPU handle to the render target view descriptor.
|
||||
/// </summary>
|
||||
/// <returns>The CPU handle to the render target view descriptor.</returns>
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE RTV() const
|
||||
{
|
||||
return _rtv.CPU();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CPU handle to the depth stencil view descriptor.
|
||||
/// </summary>
|
||||
/// <returns>The CPU handle to the depth stencil view descriptor.</returns>
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE DSV() const
|
||||
{
|
||||
return _dsv.CPU();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [GPUResourceView]
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return (void*)(IShaderResourceDX12*)this;
|
||||
}
|
||||
|
||||
// [IShaderResourceDX12]
|
||||
bool IsDepthStencilResource() const override
|
||||
{
|
||||
return _dsv.IsValid();
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override
|
||||
{
|
||||
return _srv.CPU();
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE UAV() const override
|
||||
{
|
||||
return _uav.CPU();
|
||||
}
|
||||
|
||||
ResourceOwnerDX12* GetResourceOwner() const override
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Texture object for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUTextureDX12 : public GPUResourceDX12<GPUTexture>, public ResourceOwnerDX12, public IShaderResourceDX12
|
||||
{
|
||||
private:
|
||||
|
||||
GPUTextureViewDX12 _handleArray;
|
||||
GPUTextureViewDX12 _handleVolume;
|
||||
GPUTextureViewDX12 _handleReadOnlyDepth;
|
||||
Array<GPUTextureViewDX12> _handlesPerSlice; // [slice]
|
||||
Array<Array<GPUTextureViewDX12>> _handlesPerMip; // [slice][mip]
|
||||
|
||||
DescriptorHeapWithSlotsDX12::Slot _srv;
|
||||
DescriptorHeapWithSlotsDX12::Slot _uav;
|
||||
|
||||
DXGI_FORMAT _dxgiFormatDSV;
|
||||
DXGI_FORMAT _dxgiFormatSRV;
|
||||
DXGI_FORMAT _dxgiFormatRTV;
|
||||
DXGI_FORMAT _dxgiFormatUAV;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUTextureDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The device.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
GPUTextureDX12(GPUDeviceDX12* device, const StringView& name)
|
||||
: GPUResourceDX12<GPUTexture>(device, name)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void initHandles();
|
||||
|
||||
public:
|
||||
|
||||
// [GPUTexture]
|
||||
GPUTextureView* View(int32 arrayOrDepthIndex) const override
|
||||
{
|
||||
return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex];
|
||||
}
|
||||
|
||||
GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override
|
||||
{
|
||||
return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex];
|
||||
}
|
||||
|
||||
GPUTextureView* ViewArray() const override
|
||||
{
|
||||
ASSERT(ArraySize() > 1);
|
||||
return (GPUTextureView*)&_handleArray;
|
||||
}
|
||||
|
||||
GPUTextureView* ViewVolume() const override
|
||||
{
|
||||
ASSERT(IsVolume());
|
||||
return (GPUTextureView*)&_handleVolume;
|
||||
}
|
||||
|
||||
GPUTextureView* ViewReadOnlyDepth() const override
|
||||
{
|
||||
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
|
||||
return (GPUTextureView*)&_handleReadOnlyDepth;
|
||||
}
|
||||
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return (void*)nullptr;
|
||||
}
|
||||
|
||||
bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
|
||||
|
||||
// [ResourceOwnerDX12]
|
||||
GPUResource* AsGPUResource() const override
|
||||
{
|
||||
return (GPUResource*)this;
|
||||
}
|
||||
|
||||
// [IShaderResourceDX12]
|
||||
bool IsDepthStencilResource() const override
|
||||
{
|
||||
return (_desc.Flags & GPUTextureFlags::DepthStencil) != 0;
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override
|
||||
{
|
||||
return _srv.CPU();
|
||||
}
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE UAV() const override
|
||||
{
|
||||
return _uav.CPU();
|
||||
}
|
||||
|
||||
ResourceOwnerDX12* GetResourceOwner() const override
|
||||
{
|
||||
return (ResourceOwnerDX12*)this;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUTexture]
|
||||
bool OnInit() override;
|
||||
void onResidentMipsChanged() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "GPUTimerQueryDX12.h"
|
||||
#include "GPUContextDX12.h"
|
||||
|
||||
GPUTimerQueryDX12::GPUTimerQueryDX12(GPUDeviceDX12* device)
|
||||
: GPUResourceDX12<GPUTimerQuery>(device, String::Empty)
|
||||
{
|
||||
}
|
||||
|
||||
void GPUTimerQueryDX12::OnReleaseGPU()
|
||||
{
|
||||
_hasResult = false;
|
||||
_endCalled = false;
|
||||
_timeDelta = 0.0f;
|
||||
}
|
||||
|
||||
void GPUTimerQueryDX12::Begin()
|
||||
{
|
||||
const auto context = _device->GetMainContextDX12();
|
||||
auto& heap = _device->TimestampQueryHeap;
|
||||
heap.EndQuery(context, _begin);
|
||||
|
||||
_hasResult = false;
|
||||
_endCalled = false;
|
||||
}
|
||||
|
||||
void GPUTimerQueryDX12::End()
|
||||
{
|
||||
if (_endCalled)
|
||||
return;
|
||||
|
||||
const auto context = _device->GetMainContextDX12();
|
||||
auto& heap = _device->TimestampQueryHeap;
|
||||
heap.EndQuery(context, _end);
|
||||
|
||||
const auto queue = _device->GetCommandQueue()->GetCommandQueue();
|
||||
VALIDATE_DIRECTX_RESULT(queue->GetTimestampFrequency(&_gpuFrequency));
|
||||
|
||||
_endCalled = true;
|
||||
}
|
||||
|
||||
bool GPUTimerQueryDX12::HasResult()
|
||||
{
|
||||
if (!_endCalled)
|
||||
return false;
|
||||
if (_hasResult)
|
||||
return true;
|
||||
|
||||
auto& heap = _device->TimestampQueryHeap;
|
||||
return heap.IsReady(_end) && heap.IsReady(_begin);
|
||||
}
|
||||
|
||||
float GPUTimerQueryDX12::GetResult()
|
||||
{
|
||||
if (_hasResult)
|
||||
{
|
||||
return _timeDelta;
|
||||
}
|
||||
|
||||
const uint64 timeBegin = *(uint64*)_device->TimestampQueryHeap.ResolveQuery(_begin);
|
||||
const uint64 timeEnd = *(uint64*)_device->TimestampQueryHeap.ResolveQuery(_end);
|
||||
|
||||
// Calculate event duration in milliseconds
|
||||
if (timeEnd > timeBegin)
|
||||
{
|
||||
const uint64 delta = timeEnd - timeBegin;
|
||||
const double frequency = double(_gpuFrequency);
|
||||
_timeDelta = static_cast<float>((delta / frequency) * 1000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_timeDelta = 0.0f;
|
||||
}
|
||||
|
||||
_hasResult = true;
|
||||
return _timeDelta;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "Engine/Graphics/GPUTimerQuery.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
|
||||
/// <summary>
|
||||
/// GPU timer query object for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class GPUTimerQueryDX12 : public GPUResourceDX12<GPUTimerQuery>
|
||||
{
|
||||
private:
|
||||
|
||||
bool _hasResult = false;
|
||||
bool _endCalled = false;
|
||||
float _timeDelta = 0.0f;
|
||||
uint64 _gpuFrequency = 0;
|
||||
QueryHeapDX12::ElementHandle _begin;
|
||||
QueryHeapDX12::ElementHandle _end;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUTimerQueryDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The graphics device.</param>
|
||||
GPUTimerQueryDX12(GPUDeviceDX12* device);
|
||||
|
||||
public:
|
||||
|
||||
// [GPUTimerQuery]
|
||||
void Begin() override;
|
||||
void End() override;
|
||||
bool HasResult() override;
|
||||
float GetResult() override;
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// DirectX 12 graphics backend module.
|
||||
/// </summary>
|
||||
public class GraphicsDeviceDX12 : GraphicsDeviceBaseModule
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables using PIX events to instrument game labeling regions of CPU or GPU work and marking important occurrences.
|
||||
/// </summary>
|
||||
public bool UseWinPixEventRuntime = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PublicDefinitions.Add("GRAPHICS_API_DIRECTX12");
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
options.OutputFiles.Add("d3d12.lib");
|
||||
options.DelayLoadLibraries.Add("d3d12.dll");
|
||||
break;
|
||||
case TargetPlatform.XboxScarlett:
|
||||
options.OutputFiles.Add("d3d12_xs.lib");
|
||||
break;
|
||||
}
|
||||
|
||||
if (UseWinPixEventRuntime)
|
||||
{
|
||||
options.PrivateDefinitions.Add("USE_PIX");
|
||||
options.PrivateIncludePaths.Add(Path.Combine(Globals.EngineRoot, "Source/ThirdParty/WinPixEventRuntime"));
|
||||
options.OutputFiles.Add(Path.Combine(options.DepsFolder, "WinPixEventRuntime.lib"));
|
||||
options.DependencyFiles.Add(Path.Combine(options.DepsFolder, "WinPixEventRuntime.dll"));
|
||||
options.DelayLoadLibraries.Add("WinPixEventRuntime.dll");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ResourceOwnerDX12.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
/// <summary>
|
||||
/// Interface for objects that can be bound to the shader slots in DirectX 12.
|
||||
/// </summary>
|
||||
class IShaderResourceDX12
|
||||
{
|
||||
public:
|
||||
|
||||
IShaderResourceDX12()
|
||||
: SubresourceIndex(D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)
|
||||
{
|
||||
}
|
||||
|
||||
IShaderResourceDX12(int32 subresourceIndex)
|
||||
: SubresourceIndex(subresourceIndex)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Affected subresource index or -1 if use whole resource.
|
||||
/// </summary>
|
||||
int32 SubresourceIndex; // Note: this solves only resource states tracking per single subresource, not subresources range, if need to here should be range of subresources (for texture arrays, volume textures and cubemaps)
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this resource is depth/stencil buffer.
|
||||
/// </summary>
|
||||
/// <returns>True if this resource is depth/stencil buffer, otherwise false.</returns>
|
||||
virtual bool IsDepthStencilResource() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets CPU handle to the shader resource view descriptor.
|
||||
/// </summary>
|
||||
/// <returns>SRV</returns>
|
||||
virtual D3D12_CPU_DESCRIPTOR_HANDLE SRV() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets CPU handle to the unordered access view descriptor.
|
||||
/// </summary>
|
||||
/// <returns>UAV</returns>
|
||||
virtual D3D12_CPU_DESCRIPTOR_HANDLE UAV() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resource owner.
|
||||
/// </summary>
|
||||
/// <returns>Owner object.</returns>
|
||||
virtual ResourceOwnerDX12* GetResourceOwner() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
233
Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp
Normal file
233
Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "QueryHeapDX12.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "GPUContextDX12.h"
|
||||
#include "../RenderToolsDX.h"
|
||||
|
||||
QueryHeapDX12::QueryHeapDX12(GPUDeviceDX12* device, const D3D12_QUERY_HEAP_TYPE& queryHeapType, int32 queryHeapCount)
|
||||
: _device(device)
|
||||
, _queryHeap(nullptr)
|
||||
, _resultBuffer(nullptr)
|
||||
, _queryHeapType(queryHeapType)
|
||||
, _currentIndex(0)
|
||||
, _queryHeapCount(queryHeapCount)
|
||||
{
|
||||
if (queryHeapType == D3D12_QUERY_HEAP_TYPE_OCCLUSION)
|
||||
{
|
||||
_resultSize = sizeof(uint64);
|
||||
_queryType = D3D12_QUERY_TYPE_OCCLUSION;
|
||||
}
|
||||
else if (queryHeapType == D3D12_QUERY_HEAP_TYPE_TIMESTAMP)
|
||||
{
|
||||
_resultSize = sizeof(uint64);
|
||||
_queryType = D3D12_QUERY_TYPE_TIMESTAMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
MISSING_CODE("Not support D3D12 query heap type.");
|
||||
}
|
||||
}
|
||||
|
||||
bool QueryHeapDX12::Init()
|
||||
{
|
||||
_resultData.Resize(_resultSize * _queryHeapCount);
|
||||
|
||||
// Create the query heap
|
||||
D3D12_QUERY_HEAP_DESC heapDesc;
|
||||
heapDesc.Type = _queryHeapType;
|
||||
heapDesc.Count = _queryHeapCount;
|
||||
heapDesc.NodeMask = 0;
|
||||
HRESULT result = _device->GetDevice()->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(&_queryHeap));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
DX_SET_DEBUG_NAME(_queryHeap, "Query Heap");
|
||||
|
||||
// Create the result buffer
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 1;
|
||||
heapProperties.VisibleNodeMask = 1;
|
||||
D3D12_RESOURCE_DESC resourceDesc;
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Alignment = 0;
|
||||
resourceDesc.Width = _resultData.Count();
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.SampleDesc.Quality = 0;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
result = _device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&_resultBuffer));
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result);
|
||||
DX_SET_DEBUG_NAME(_resultBuffer, "Query Heap Result Buffer");
|
||||
|
||||
// Start out with an open query batch
|
||||
_currentBatch.Open = false;
|
||||
StartQueryBatch();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueryHeapDX12::Destroy()
|
||||
{
|
||||
SAFE_RELEASE(_resultBuffer);
|
||||
SAFE_RELEASE(_queryHeap);
|
||||
_currentBatch.Clear();
|
||||
_resultData.SetCapacity(0);
|
||||
}
|
||||
|
||||
void QueryHeapDX12::EndQueryBatchAndResolveQueryData(GPUContextDX12* context)
|
||||
{
|
||||
ASSERT(_currentBatch.Open);
|
||||
|
||||
// Discard empty batches
|
||||
if (_currentBatch.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Close the current batch
|
||||
_currentBatch.Open = false;
|
||||
|
||||
// Resolve the batch
|
||||
const int32 offset = _currentBatch.Start * _resultSize;
|
||||
const int32 size = _currentBatch.Count * _resultSize;
|
||||
context->GetCommandList()->ResolveQueryData(
|
||||
_queryHeap,
|
||||
_queryType,
|
||||
_currentBatch.Start,
|
||||
_currentBatch.Count,
|
||||
_resultBuffer,
|
||||
offset
|
||||
);
|
||||
_currentBatch.Sync = _device->GetCommandQueue()->GetSyncPoint();
|
||||
|
||||
// Begin a new query batch
|
||||
_batches.Add(_currentBatch);
|
||||
StartQueryBatch();
|
||||
}
|
||||
|
||||
void QueryHeapDX12::AllocQuery(GPUContextDX12* context, ElementHandle& handle)
|
||||
{
|
||||
ASSERT(_currentBatch.Open);
|
||||
|
||||
// Check if need to start from the buffer head
|
||||
if (_currentIndex >= GetQueryHeapCount())
|
||||
{
|
||||
// We're in the middle of a batch, but we're at the end of the heap so split the batch in two
|
||||
EndQueryBatchAndResolveQueryData(context);
|
||||
}
|
||||
|
||||
// Allocate element into the current batch
|
||||
handle = _currentIndex++;
|
||||
_currentBatch.Count++;
|
||||
}
|
||||
|
||||
void QueryHeapDX12::BeginQuery(GPUContextDX12* context, ElementHandle& handle)
|
||||
{
|
||||
AllocQuery(context, handle);
|
||||
|
||||
context->GetCommandList()->BeginQuery(_queryHeap, _queryType, handle);
|
||||
}
|
||||
|
||||
void QueryHeapDX12::EndQuery(GPUContextDX12* context, ElementHandle& handle)
|
||||
{
|
||||
AllocQuery(context, handle);
|
||||
|
||||
context->GetCommandList()->EndQuery(_queryHeap, _queryType, handle);
|
||||
}
|
||||
|
||||
bool QueryHeapDX12::IsReady(ElementHandle& handle)
|
||||
{
|
||||
// Current batch is not ready (not ended)
|
||||
if (_currentBatch.ContainsElement(handle))
|
||||
return false;
|
||||
|
||||
for (int32 i = 0; i < _batches.Count(); i++)
|
||||
{
|
||||
auto& batch = _batches[i];
|
||||
if (batch.ContainsElement(handle))
|
||||
{
|
||||
ASSERT(batch.Sync.IsValid());
|
||||
return batch.Sync.IsComplete();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* QueryHeapDX12::ResolveQuery(ElementHandle& handle)
|
||||
{
|
||||
// Prevent queries from the current batch
|
||||
ASSERT(!_currentBatch.ContainsElement(handle));
|
||||
|
||||
// Find the batch that contains this element to resolve it
|
||||
for (int32 i = 0; i < _batches.Count(); i++)
|
||||
{
|
||||
auto& batch = _batches[i];
|
||||
if (batch.ContainsElement(handle))
|
||||
{
|
||||
ASSERT(batch.Sync.IsValid());
|
||||
|
||||
// Ensure that end point has been already executed
|
||||
if (!batch.Sync.IsComplete())
|
||||
{
|
||||
if (batch.Sync.IsOpen())
|
||||
{
|
||||
// The query is on a command list that hasn't been submitted yet
|
||||
LOG(Warning, "Stalling the rendering and flushing GPU commands to wait for a query that hasn't been submitted to the GPU yet.");
|
||||
_device->WaitForGPU();
|
||||
}
|
||||
|
||||
batch.Sync.WaitForCompletion();
|
||||
}
|
||||
|
||||
// Map the query values readback buffer
|
||||
D3D12_RANGE range;
|
||||
range.Begin = batch.Start * _resultSize;
|
||||
range.End = range.Begin + batch.Count * _resultSize;
|
||||
void* mapped = nullptr;
|
||||
VALIDATE_DIRECTX_RESULT(_resultBuffer->Map(0, &range, &mapped));
|
||||
|
||||
// Copy the results data
|
||||
Platform::MemoryCopy(_resultData.Get() + range.Begin, (byte*)mapped + range.Begin, batch.Count * _resultSize);
|
||||
|
||||
// Unmap with an empty range to indicate nothing was written by the CPU
|
||||
range.Begin = range.End = 0;
|
||||
_resultBuffer->Unmap(0, &range);
|
||||
|
||||
// All elements got its results so we can remove this batch
|
||||
_batches.RemoveAt(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _resultData.Get() + handle * _resultSize;
|
||||
}
|
||||
|
||||
void QueryHeapDX12::StartQueryBatch()
|
||||
{
|
||||
ASSERT(!_currentBatch.Open);
|
||||
|
||||
// Clear the current batch
|
||||
_currentBatch.Clear();
|
||||
|
||||
// Loop active index on overflow
|
||||
if (_currentIndex >= GetQueryHeapCount())
|
||||
{
|
||||
_currentIndex = 0;
|
||||
}
|
||||
|
||||
// Start a new batch
|
||||
_currentBatch.Start = _currentIndex;
|
||||
_currentBatch.Open = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
189
Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h
Normal file
189
Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.h
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
class TimerQueryDX12;
|
||||
class GPUDeviceDX12;
|
||||
class GPUContextDX12;
|
||||
class GPUBuffer;
|
||||
|
||||
#include "CommandQueueDX12.h"
|
||||
|
||||
/// <summary>
|
||||
/// GPU queries heap for DirectX 12 backend.
|
||||
/// </summary>
|
||||
class QueryHeapDX12
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The query element handle.
|
||||
/// </summary>
|
||||
typedef int32 ElementHandle;
|
||||
|
||||
private:
|
||||
|
||||
struct QueryBatch
|
||||
{
|
||||
/// <summary>
|
||||
/// The synchronization point when query has been submitted to be executed.
|
||||
/// </summary>
|
||||
SyncPointDX12 Sync;
|
||||
|
||||
/// <summary>
|
||||
/// The first element in the batch (inclusive).
|
||||
/// </summary>
|
||||
int32 Start = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of elements added to this batch.
|
||||
/// </summary>
|
||||
int32 Count = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Is the batch still open for more begin/end queries.
|
||||
/// </summary>
|
||||
bool Open = false;
|
||||
|
||||
/// <summary>
|
||||
/// Clears this batch.
|
||||
/// </summary>
|
||||
inline void Clear()
|
||||
{
|
||||
Sync = SyncPointDX12();
|
||||
Start = 0;
|
||||
Count = 0;
|
||||
Open = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this query batch contains a given element contains the element.
|
||||
/// </summary>
|
||||
/// <param name="elementIndex">The index of the element.</param>
|
||||
/// <returns>True if element is in this query, otherwise false.</returns>
|
||||
bool ContainsElement(int32 elementIndex) const
|
||||
{
|
||||
return elementIndex >= Start && elementIndex < Start + Count;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
ID3D12QueryHeap* _queryHeap;
|
||||
ID3D12Resource* _resultBuffer;
|
||||
D3D12_QUERY_TYPE _queryType;
|
||||
D3D12_QUERY_HEAP_TYPE _queryHeapType;
|
||||
int32 _currentIndex;
|
||||
int32 _resultSize;
|
||||
int32 _queryHeapCount;
|
||||
QueryBatch _currentBatch;
|
||||
Array<QueryBatch> _batches;
|
||||
Array<byte> _resultData;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QueryHeapDX12"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The device.</param>
|
||||
/// <param name="queryHeapType">Type of the query heap.</param>
|
||||
/// <param name="queryHeapCount">The query heap count.</param>
|
||||
QueryHeapDX12(GPUDeviceDX12* device, const D3D12_QUERY_HEAP_TYPE& queryHeapType, int32 queryHeapCount);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this instance.
|
||||
/// </summary>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
bool Init();
|
||||
|
||||
/// <summary>
|
||||
/// Destroys this instance.
|
||||
/// </summary>
|
||||
void Destroy();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query heap capacity.
|
||||
/// </summary>
|
||||
/// <returns>The queries count.</returns>
|
||||
FORCE_INLINE int32 GetQueryHeapCount() const
|
||||
{
|
||||
return _queryHeapCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the result value (in bytes).
|
||||
/// </summary>
|
||||
/// <returns>The size of the query result value (in bytes).</returns>
|
||||
FORCE_INLINE int32 GetResultSize() const
|
||||
{
|
||||
return _resultSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result buffer (CPU readable via Map/Unmap).
|
||||
/// </summary>
|
||||
/// <returns>The query results buffer.</returns>
|
||||
FORCE_INLINE ID3D12Resource* GetResultBuffer() const
|
||||
{
|
||||
return _resultBuffer;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Stops tracking the current batch of begin/end query calls that will be resolved together. This implicitly starts a new batch.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
void EndQueryBatchAndResolveQueryData(GPUContextDX12* context);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates the query heap element.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="handle">The result handle.</param>
|
||||
void AllocQuery(GPUContextDX12* context, ElementHandle& handle);
|
||||
|
||||
/// <summary>
|
||||
/// Calls BeginQuery on command list for the given query heap slot.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="handle">The query handle.</param>
|
||||
void BeginQuery(GPUContextDX12* context, ElementHandle& handle);
|
||||
|
||||
/// <summary>
|
||||
/// Calls EndQuery on command list for the given query heap slot.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="handle">The query handle.</param>
|
||||
void EndQuery(GPUContextDX12* context, ElementHandle& handle);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified query handle is ready to read data (command list has been executed by the GPU).
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle.</param>
|
||||
/// <returns><c>true</c> if the specified query handle is ready; otherwise, <c>false</c>.</returns>
|
||||
bool IsReady(ElementHandle& handle);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the query (or skips if already resolved).
|
||||
/// </summary>
|
||||
/// <param name="handle">The result handle.</param>
|
||||
/// <returns>The pointer to the resolved query data.</returns>
|
||||
void* ResolveQuery(ElementHandle& handle);
|
||||
|
||||
private:
|
||||
|
||||
/// <summary>
|
||||
/// Starts tracking a new batch of begin/end query calls that will be resolved together
|
||||
/// </summary>
|
||||
void StartQueryBatch();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "ResourceOwnerDX12.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
|
||||
void ResourceOwnerDX12::initResource(ID3D12Resource* resource, const D3D12_RESOURCE_STATES initialState, const uint32 subresourceCount, bool usePerSubresourceTracking)
|
||||
{
|
||||
ASSERT(resource);
|
||||
_resource = resource;
|
||||
_subresourcesCount = subresourceCount;
|
||||
State.Initialize(subresourceCount, initialState, usePerSubresourceTracking);
|
||||
}
|
||||
|
||||
void ResourceOwnerDX12::initResource(const D3D12_RESOURCE_STATES initialState, const uint32 subresourceCount, bool usePerSubresourceTracking)
|
||||
{
|
||||
// Note: this is used by the dynamic buffers (which don't own resource but just part of other one)
|
||||
_subresourcesCount = subresourceCount;
|
||||
State.Initialize(subresourceCount, initialState, usePerSubresourceTracking);
|
||||
}
|
||||
|
||||
void ResourceOwnerDX12::releaseResource(uint32 safeFrameCount)
|
||||
{
|
||||
if (_resource)
|
||||
{
|
||||
OnRelease(this);
|
||||
|
||||
auto resource = _resource;
|
||||
_resource = nullptr;
|
||||
_subresourcesCount = 0;
|
||||
State.Release();
|
||||
|
||||
((GPUDeviceDX12*)GPUDevice::Instance)->AddResourceToLateRelease(resource, safeFrameCount);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
120
Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h
Normal file
120
Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Graphics/GPUResourceState.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
class GPUResource;
|
||||
class GPUContextDX12;
|
||||
class GPUAsyncContextDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Default amount of frames to wait until resource delete.
|
||||
/// </summary>
|
||||
#define DX12_RESOURCE_DELETE_SAFE_FRAMES_COUNT 10
|
||||
|
||||
/// <summary>
|
||||
/// Custom resource state used to indicate invalid state (useful for debugging resource tracking issues).
|
||||
/// </summary>
|
||||
#define D3D12_RESOURCE_STATE_CORRUPT (D3D12_RESOURCE_STATES)-1
|
||||
|
||||
/// <summary>
|
||||
/// Tracking of per-resource or per-subresource state for D3D12 resources that require to issue resource access barriers during rendering.
|
||||
/// </summary>
|
||||
class ResourceStateDX12 : public GPUResourceState<D3D12_RESOURCE_STATES, D3D12_RESOURCE_STATE_CORRUPT>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if resource state transition is needed in order to use resource in given state.
|
||||
/// </summary>
|
||||
/// <param name="currentState">The current resource state.</param>
|
||||
/// <param name="targeState">the destination resource state.</param>
|
||||
/// <returns>True if need to perform a transition, otherwise false.</returns>
|
||||
static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targeState)
|
||||
{
|
||||
// If 'targeState' is a subset of 'before', then there's no need for a transition
|
||||
// Note: COMMON is an oddball state that doesn't follow the RESOURE_STATE pattern of
|
||||
// having exactly one bit set so we need to special case these
|
||||
return currentState != targeState && ((currentState | targeState) != currentState || targeState == D3D12_RESOURCE_STATE_COMMON);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Base class for objects in DirectX 12 layer that can own a resource
|
||||
/// </summary>
|
||||
class ResourceOwnerDX12
|
||||
{
|
||||
friend GPUContextDX12;
|
||||
friend GPUAsyncContextDX12;
|
||||
|
||||
protected:
|
||||
|
||||
ID3D12Resource* _resource;
|
||||
uint32 _subresourcesCount;
|
||||
|
||||
ResourceOwnerDX12()
|
||||
: _resource(nullptr)
|
||||
, _subresourcesCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
~ResourceOwnerDX12()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Action called on resource release event.
|
||||
/// </summary>
|
||||
Delegate<ResourceOwnerDX12*> OnRelease;
|
||||
|
||||
/// <summary>
|
||||
/// The resource state tracking helper. Used for resource barriers.
|
||||
/// </summary>
|
||||
ResourceStateDX12 State;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the subresources count.
|
||||
/// </summary>
|
||||
/// <returns>The subresources count.</returns>
|
||||
FORCE_INLINE uint32 GetSubresourcesCount() const
|
||||
{
|
||||
return _subresourcesCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets DirectX 12 resource object handle
|
||||
/// </summary>
|
||||
/// <returns>DirectX 12 resource object handle</returns>
|
||||
FORCE_INLINE ID3D12Resource* GetResource() const
|
||||
{
|
||||
return _resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets resource owner object as a GPUResource type or returns null if cannot perform cast.
|
||||
/// </summary>
|
||||
/// <returns>GPU Resource or null if cannot cast.</returns>
|
||||
virtual GPUResource* AsGPUResource() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
FORCE_INLINE void initResource(ID3D12Resource* resource, const D3D12_RESOURCE_STATES initialState, const D3D12_RESOURCE_DESC& desc, bool usePerSubresourceTracking = false)
|
||||
{
|
||||
initResource(resource, initialState, desc.DepthOrArraySize * desc.MipLevels, usePerSubresourceTracking);
|
||||
}
|
||||
|
||||
void initResource(ID3D12Resource* resource, const D3D12_RESOURCE_STATES initialState, const uint32 subresourceCount, bool usePerSubresourceTracking = false);
|
||||
void initResource(const D3D12_RESOURCE_STATES initialState, const uint32 subresourceCount, bool usePerSubresourceTracking = false);
|
||||
void releaseResource(uint32 safeFrameCount = DX12_RESOURCE_DELETE_SAFE_FRAMES_COUNT);
|
||||
};
|
||||
|
||||
#endif
|
||||
268
Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp
Normal file
268
Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#include "UploadBufferDX12.h"
|
||||
#include "GPUTextureDX12.h"
|
||||
#include "GPUContextDX12.h"
|
||||
#include "../RenderToolsDX.h"
|
||||
|
||||
UploadBufferDX12::UploadBufferDX12(GPUDeviceDX12* device)
|
||||
: _device(device)
|
||||
, _currentPage(nullptr)
|
||||
, _currentOffset(0)
|
||||
, _currentGeneration(0)
|
||||
{
|
||||
}
|
||||
|
||||
UploadBufferDX12::~UploadBufferDX12()
|
||||
{
|
||||
_freePages.Add(_usedPages);
|
||||
for (auto page : _freePages)
|
||||
{
|
||||
page->ReleaseGPU();
|
||||
Delete(page);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicAllocation UploadBufferDX12::Allocate(uint64 size, uint64 align)
|
||||
{
|
||||
const uint64 alignmentMask = align - 1;
|
||||
ASSERT((alignmentMask & align) == 0);
|
||||
|
||||
// Check if use default or bigger page
|
||||
const bool useDefaultSize = size <= DX12_DEFAULT_UPLOAD_PAGE_SIZE;
|
||||
const uint64 pageSize = useDefaultSize ? DX12_DEFAULT_UPLOAD_PAGE_SIZE : size;
|
||||
const uint64 alignedSize = Math::AlignUpWithMask(size, alignmentMask);
|
||||
|
||||
// Align the allocation
|
||||
_currentOffset = Math::AlignUpWithMask(_currentOffset, alignmentMask);
|
||||
|
||||
// Check if there is enough space for that chunk of the data in the current page
|
||||
if (_currentPage && _currentOffset + alignedSize > _currentPage->Size)
|
||||
{
|
||||
_currentPage = nullptr;
|
||||
}
|
||||
|
||||
// Check if need to get new page
|
||||
if (_currentPage == nullptr)
|
||||
{
|
||||
_currentPage = requestPage(pageSize);
|
||||
_currentOffset = 0;
|
||||
}
|
||||
|
||||
// Mark page as used in this generation
|
||||
_currentPage->LastGen = _currentGeneration;
|
||||
|
||||
// Create allocation result
|
||||
const DynamicAllocation result(static_cast<byte*>(_currentPage->CPUAddress) + _currentOffset, _currentOffset, size, _currentPage->GPUAddress + _currentOffset, _currentPage, _currentGeneration);
|
||||
|
||||
// Move in the page
|
||||
_currentOffset += size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UploadBufferDX12::UploadBuffer(GPUContextDX12* context, ID3D12Resource* buffer, uint32 bufferOffset, const void* data, uint64 size)
|
||||
{
|
||||
// Allocate data
|
||||
const DynamicAllocation allocation = Allocate(size, 4);
|
||||
|
||||
// Check if allocation is invalid
|
||||
if (allocation.IsInvalid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
Platform::MemoryCopy(allocation.CPUAddress, data, static_cast<size_t>(size));
|
||||
|
||||
// Copy buffer region
|
||||
context->GetCommandList()->CopyBufferRegion(buffer, bufferOffset, allocation.Page->GetResource(), allocation.Offset, size);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UploadBufferDX12::UploadTexture(GPUContextDX12* context, ID3D12Resource* texture, const void* srcData, uint32 srcRowPitch, uint32 srcSlicePitch, int32 mipIndex, int32 arrayIndex)
|
||||
{
|
||||
// Cache resource info
|
||||
D3D12_RESOURCE_DESC resourceDesc = texture->GetDesc();
|
||||
const UINT subresourceIndex = RenderToolsDX::CalcSubresourceIndex(mipIndex, arrayIndex, resourceDesc.MipLevels);
|
||||
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
||||
uint32 numRows;
|
||||
uint64 rowPitchAligned, mipSizeAligned;
|
||||
_device->GetDevice()->GetCopyableFootprints(&resourceDesc, subresourceIndex, 1, 0, &footprint, &numRows, &rowPitchAligned, &mipSizeAligned);
|
||||
rowPitchAligned = footprint.Footprint.RowPitch;
|
||||
mipSizeAligned = rowPitchAligned * footprint.Footprint.Height;
|
||||
|
||||
// Destination texture copy location description
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLocation;
|
||||
dstLocation.pResource = texture;
|
||||
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstLocation.SubresourceIndex = subresourceIndex;
|
||||
|
||||
// Allocate data
|
||||
const DynamicAllocation allocation = Allocate(mipSizeAligned, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
|
||||
// Check if allocation is invalid
|
||||
if (allocation.Size != mipSizeAligned)
|
||||
return true;
|
||||
|
||||
// Check if can copy rows at once
|
||||
byte* ptr = (byte*)srcData;
|
||||
ASSERT(srcSlicePitch <= mipSizeAligned);
|
||||
if (srcRowPitch == rowPitchAligned)
|
||||
{
|
||||
// Copy data at once
|
||||
Platform::MemoryCopy(allocation.CPUAddress, ptr, srcSlicePitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use per row copy
|
||||
byte* dst = static_cast<byte*>(allocation.CPUAddress);
|
||||
ASSERT(srcRowPitch <= rowPitchAligned);
|
||||
for (uint32 i = 0; i < numRows; i++)
|
||||
{
|
||||
Platform::MemoryCopy(dst, ptr, srcRowPitch);
|
||||
|
||||
dst += rowPitchAligned;
|
||||
ptr += srcRowPitch;
|
||||
}
|
||||
}
|
||||
|
||||
// Source buffer copy location description
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLocation;
|
||||
srcLocation.pResource = allocation.Page->GetResource();
|
||||
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
srcLocation.PlacedFootprint.Offset = allocation.Offset;
|
||||
srcLocation.PlacedFootprint.Footprint = footprint.Footprint;
|
||||
|
||||
// Copy texture region
|
||||
context->GetCommandList()->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UploadBufferDX12::BeginGeneration(uint64 generation)
|
||||
{
|
||||
// Restore ready pages to be reused
|
||||
for (int32 i = 0; _usedPages.HasItems() && i < _usedPages.Count(); i++)
|
||||
{
|
||||
auto page = _usedPages[i];
|
||||
if (page->LastGen + DX12_UPLOAD_PAGE_GEN_TIMEOUT < generation)
|
||||
{
|
||||
_usedPages.RemoveAt(i);
|
||||
i--;
|
||||
_freePages.Add(page);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old pages
|
||||
for (int32 i = _freePages.Count() - 1; i >= 0 && _freePages.HasItems(); i--)
|
||||
{
|
||||
auto page = _freePages[i];
|
||||
if (page->LastGen + DX12_UPLOAD_PAGE_GEN_TIMEOUT + DX12_UPLOAD_PAGE_NOT_USED_FRAME_TIMEOUT < generation)
|
||||
{
|
||||
_freePages.RemoveAt(i);
|
||||
i--;
|
||||
page->ReleaseGPU();
|
||||
Delete(page);
|
||||
}
|
||||
}
|
||||
|
||||
// Set new generation
|
||||
_currentGeneration = generation;
|
||||
}
|
||||
|
||||
UploadBufferPageDX12* UploadBufferDX12::requestPage(uint64 size)
|
||||
{
|
||||
// Try to find valid page
|
||||
int32 freePageIndex = -1;
|
||||
for (int32 i = 0; i < _freePages.Count(); i++)
|
||||
{
|
||||
if (_freePages[i]->Size == size)
|
||||
{
|
||||
freePageIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if create a new page
|
||||
UploadBufferPageDX12* page;
|
||||
if (freePageIndex == -1)
|
||||
{
|
||||
// Get a new page to use
|
||||
page = New<UploadBufferPageDX12>(_device, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove from free pages
|
||||
page = _freePages[freePageIndex];
|
||||
_freePages.RemoveAt(freePageIndex);
|
||||
}
|
||||
|
||||
// Mark page as used
|
||||
_usedPages.Add(page);
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
UploadBufferPageDX12::UploadBufferPageDX12(GPUDeviceDX12* device, uint64 size)
|
||||
: GPUResourceDX12(device, TEXT("Upload Buffer Page"))
|
||||
, LastGen(0)
|
||||
, CPUAddress(nullptr)
|
||||
, GPUAddress(0)
|
||||
, Size(size)
|
||||
{
|
||||
// Create page buffer
|
||||
D3D12_HEAP_PROPERTIES heapProperties;
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProperties.CreationNodeMask = 1;
|
||||
heapProperties.VisibleNodeMask = 1;
|
||||
D3D12_RESOURCE_DESC resourceDesc;
|
||||
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
resourceDesc.Alignment = 0;
|
||||
resourceDesc.Width = size;
|
||||
resourceDesc.Height = 1;
|
||||
resourceDesc.DepthOrArraySize = 1;
|
||||
resourceDesc.MipLevels = 1;
|
||||
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resourceDesc.SampleDesc.Count = 1;
|
||||
resourceDesc.SampleDesc.Quality = 0;
|
||||
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
ID3D12Resource* resource;
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&resource)));
|
||||
|
||||
// Set state
|
||||
initResource(resource, D3D12_RESOURCE_STATE_GENERIC_READ, 1);
|
||||
DX_SET_DEBUG_NAME(_resource, GPUResourceDX12::GetName());
|
||||
_memoryUsage = size;
|
||||
GPUAddress = _resource->GetGPUVirtualAddress();
|
||||
|
||||
// Map buffer
|
||||
VALIDATE_DIRECTX_RESULT(_resource->Map(0, nullptr, &CPUAddress));
|
||||
}
|
||||
|
||||
void UploadBufferPageDX12::OnReleaseGPU()
|
||||
{
|
||||
// Unmap
|
||||
if (_resource && CPUAddress)
|
||||
{
|
||||
_resource->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
// Release
|
||||
releaseResource();
|
||||
}
|
||||
|
||||
#endif
|
||||
239
Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h
Normal file
239
Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "ResourceOwnerDX12.h"
|
||||
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
|
||||
#define DX12_DEFAULT_UPLOAD_PAGE_SIZE (4 * 1014 * 1024) // 4 MB
|
||||
|
||||
// Upload buffer generations timeout to dispose
|
||||
#define DX12_UPLOAD_PAGE_GEN_TIMEOUT DX12_BACK_BUFFER_COUNT
|
||||
|
||||
// Upload buffer pages that are not used for a few frames are disposed
|
||||
#define DX12_UPLOAD_PAGE_NOT_USED_FRAME_TIMEOUT 8
|
||||
|
||||
class GPUTextureDX12;
|
||||
|
||||
/// <summary>
|
||||
/// Single page for the upload buffer
|
||||
/// </summary>
|
||||
class UploadBufferPageDX12 : public GPUResourceDX12<GPUResource>, public ResourceOwnerDX12
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="device">Graphics Device</param>
|
||||
/// <param name="size">Page size</param>
|
||||
UploadBufferPageDX12(GPUDeviceDX12* device, uint64 size);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Last generation that has been using that page
|
||||
/// </summary>
|
||||
uint64 LastGen;
|
||||
|
||||
/// <summary>
|
||||
/// CPU memory address of the page
|
||||
/// </summary>
|
||||
void* CPUAddress;
|
||||
|
||||
/// <summary>
|
||||
/// GPU memory address of the page
|
||||
/// </summary>
|
||||
D3D12_GPU_VIRTUAL_ADDRESS GPUAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Page size in bytes
|
||||
/// </summary>
|
||||
uint64 Size;
|
||||
|
||||
public:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
ResourceType GetResourceType() const final override
|
||||
{
|
||||
return ResourceType::Buffer;
|
||||
}
|
||||
|
||||
// [ResourceOwnerDX12]
|
||||
GPUResource* AsGPUResource() const override
|
||||
{
|
||||
return (GPUResource*)this;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [GPUResourceDX12]
|
||||
void OnReleaseGPU() final override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Upload buffer allocation
|
||||
/// </summary>
|
||||
struct DynamicAllocation
|
||||
{
|
||||
/// <summary>
|
||||
/// CPU memory address of the allocation start.
|
||||
/// </summary>
|
||||
void* CPUAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Allocation offset in bytes (from the start of the heap buffer).
|
||||
/// </summary>
|
||||
uint64 Offset;
|
||||
|
||||
/// <summary>
|
||||
/// Allocation size in bytes
|
||||
/// </summary>
|
||||
uint64 Size;
|
||||
|
||||
/// <summary>
|
||||
/// GPU virtual memory address of the allocation start.
|
||||
/// </summary>
|
||||
D3D12_GPU_VIRTUAL_ADDRESS GPUAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Upload buffer page that owns that allocation
|
||||
/// </summary>
|
||||
UploadBufferPageDX12* Page;
|
||||
|
||||
/// <summary>
|
||||
/// Generation number of that allocation (generally allocation is invalid after one or two generations)
|
||||
/// </summary>
|
||||
uint64 Generation;
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
DynamicAllocation()
|
||||
: CPUAddress(nullptr)
|
||||
, Offset(0)
|
||||
, Size(0)
|
||||
, GPUAddress(0)
|
||||
, Page(nullptr)
|
||||
, Generation(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="address">CPU memory address</param>
|
||||
/// <param name="offset">Offset in byes</param>
|
||||
/// <param name="size">Size in byes</param>
|
||||
/// <param name="gpuAddress">GPU memory address</param>
|
||||
/// <param name="page">Parent page</param>
|
||||
/// <param name="generation">Generation</param>
|
||||
DynamicAllocation(void* address, uint64 offset, uint64 size, D3D12_GPU_VIRTUAL_ADDRESS gpuAddress, UploadBufferPageDX12* page, uint64 generation)
|
||||
: CPUAddress(address)
|
||||
, Offset(offset)
|
||||
, Size(size)
|
||||
, GPUAddress(gpuAddress)
|
||||
, Page(page)
|
||||
, Generation(generation)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if allocation is invalid
|
||||
/// </summary>
|
||||
/// <returns>True if allocation in invalid</returns>
|
||||
bool IsInvalid() const
|
||||
{
|
||||
return CPUAddress == nullptr || Size == 0 || Page == nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Uploading data to GPU buffer utility
|
||||
/// </summary>
|
||||
class UploadBufferDX12
|
||||
{
|
||||
private:
|
||||
|
||||
GPUDeviceDX12* _device;
|
||||
UploadBufferPageDX12* _currentPage;
|
||||
uint64 _currentOffset;
|
||||
uint64 _currentGeneration;
|
||||
|
||||
Array<UploadBufferPageDX12*, InlinedAllocation<64>> _freePages;
|
||||
Array<UploadBufferPageDX12*, InlinedAllocation<64>> _usedPages;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="device">Graphics Device</param>
|
||||
UploadBufferDX12(GPUDeviceDX12* device);
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~UploadBufferDX12();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current generation number.
|
||||
/// </summary>
|
||||
/// <returns>The current generation number.</returns>
|
||||
FORCE_INLINE uint64 GetCurrentGeneration() const
|
||||
{
|
||||
return _currentGeneration;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Allocates memory for custom data in the buffer.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the data in bytes</param>
|
||||
/// <param name="align">Data alignment in buffer in bytes</param>
|
||||
/// <returns>Dynamic location</returns>
|
||||
DynamicAllocation Allocate(uint64 size, uint64 align);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data to the buffer.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context to record upload command to it</param>
|
||||
/// <param name="buffer">Destination buffer</param>
|
||||
/// <param name="bufferOffset">Destination buffer offset in bytes.</param>
|
||||
/// <param name="data">Data to allocate</param>
|
||||
/// <param name="size">Size of the data in bytes</param>
|
||||
/// <returns>True if cannot upload data, otherwise false.</returns>
|
||||
bool UploadBuffer(GPUContextDX12* context, ID3D12Resource* buffer, uint32 bufferOffset, const void* data, uint64 size);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data to the texture.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context to record upload command to it</param>
|
||||
/// <param name="texture">Destination texture</param>
|
||||
/// <param name="srcData">Data to allocate</param>
|
||||
/// <param name="srcRowPitch">Source data row pitch value to upload.</param>
|
||||
/// <param name="srcSlicePitch">Source data slice pitch value to upload.</param>
|
||||
/// <param name="mipIndex">Mip map to stream index</param>
|
||||
/// <param name="arrayIndex">Texture array index</param>
|
||||
/// <returns>True if cannot upload data, otherwise false.</returns>
|
||||
bool UploadTexture(GPUContextDX12* context, ID3D12Resource* texture, const void* srcData, uint32 srcRowPitch, uint32 srcSlicePitch, int32 mipIndex, int32 arrayIndex);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Begins new generation.
|
||||
/// </summary>
|
||||
/// <param name="generation">The generation ID to begin.</param>
|
||||
void BeginGeneration(uint64 generation);
|
||||
|
||||
private:
|
||||
|
||||
UploadBufferPageDX12* requestPage(uint64 size);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user