You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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, &params, 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

View 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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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");
}
}
}

View File

@@ -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

View 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

View 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

View File

@@ -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

View 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

View 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

View 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