blob: 48064679f7866117134784ffc51b429b4a9a858a [file] [log] [blame]
//
// Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "D3D12MemAlloc.h"
#include "Common.h"
#include "Tests.h"
#include <atomic>
namespace VS
{
#include "Shaders\VS_Compiled.h"
}
namespace PS
{
#include "Shaders\PS_Compiled.h"
}
static const wchar_t * const CLASS_NAME = L"D3D12MemAllocSample";
static const wchar_t * const WINDOW_TITLE = L"D3D12 Memory Allocator Sample";
static const int SIZE_X = 1024;
static const int SIZE_Y = 576;
static const bool FULLSCREEN = false;
static const UINT PRESENT_SYNC_INTERVAL = 1;
static const DXGI_FORMAT RENDER_TARGET_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
static const DXGI_FORMAT DEPTH_STENCIL_FORMAT = DXGI_FORMAT_D32_FLOAT;
static const size_t FRAME_BUFFER_COUNT = 3; // number of buffers we want, 2 for double buffering, 3 for tripple buffering
static const D3D_FEATURE_LEVEL MY_D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_12_0;
static const bool ENABLE_DEBUG_LAYER = true;
static const bool ENABLE_CPU_ALLOCATION_CALLBACKS = true;
static const bool ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT = false;
static constexpr D3D12MA::ALLOCATOR_FLAGS g_AllocatorFlags = D3D12MA::ALLOCATOR_FLAG_NONE;
static D3D12MA::ALLOCATION_CALLBACKS g_AllocationCallbacks = {}; // Used only when ENABLE_CPU_ALLOCATION_CALLBACKS
static HINSTANCE g_Instance;
static HWND g_Wnd;
static UINT64 g_TimeOffset; // In ms.
static UINT64 g_TimeValue; // Time since g_TimeOffset, in ms.
static float g_Time; // g_TimeValue converted to float, in seconds.
static float g_TimeDelta;
static CComPtr<ID3D12Device> g_Device;
static D3D12MA::Allocator* g_Allocator;
static CComPtr<IDXGISwapChain3> g_SwapChain; // swapchain used to switch between render targets
static CComPtr<ID3D12CommandQueue> g_CommandQueue; // container for command lists
static CComPtr<ID3D12DescriptorHeap> g_RtvDescriptorHeap; // a descriptor heap to hold resources like the render targets
static CComPtr<ID3D12Resource> g_RenderTargets[FRAME_BUFFER_COUNT]; // number of render targets equal to buffer count
static CComPtr<ID3D12CommandAllocator> g_CommandAllocators[FRAME_BUFFER_COUNT]; // we want enough allocators for each buffer * number of threads (we only have one thread)
static CComPtr<ID3D12GraphicsCommandList> g_CommandList; // a command list we can record commands into, then execute them to render the frame
static CComPtr<ID3D12Fence> g_Fences[FRAME_BUFFER_COUNT]; // an object that is locked while our command list is being executed by the gpu. We need as many
//as we have allocators (more if we want to know when the gpu is finished with an asset)
static HANDLE g_FenceEvent; // a handle to an event when our g_Fences is unlocked by the gpu
static UINT64 g_FenceValues[FRAME_BUFFER_COUNT]; // this value is incremented each frame. each g_Fences will have its own value
static UINT g_FrameIndex; // current rtv we are on
static UINT g_RtvDescriptorSize; // size of the rtv descriptor on the g_Device (all front and back buffers will be the same size)
static CComPtr<ID3D12PipelineState> g_PipelineStateObject;
static CComPtr<ID3D12RootSignature> g_RootSignature;
static CComPtr<ID3D12Resource> g_VertexBuffer;
static D3D12MA::Allocation* g_VertexBufferAllocation;
static CComPtr<ID3D12Resource> g_IndexBuffer;
static D3D12MA::Allocation* g_IndexBufferAllocation;
static D3D12_VERTEX_BUFFER_VIEW g_VertexBufferView;
static D3D12_INDEX_BUFFER_VIEW g_IndexBufferView;
static CComPtr<ID3D12Resource> g_DepthStencilBuffer;
static D3D12MA::Allocation* g_DepthStencilAllocation;
static CComPtr<ID3D12DescriptorHeap> g_DepthStencilDescriptorHeap;
struct Vertex {
vec3 pos;
vec2 texCoord;
Vertex() { }
Vertex(float x, float y, float z, float tx, float ty) :
pos(x, y, z),
texCoord(tx, ty)
{
}
};
struct ConstantBuffer0_PS
{
vec4 Color;
};
struct ConstantBuffer1_VS
{
mat4 WorldViewProj;
};
static const size_t ConstantBufferPerObjectAlignedSize = AlignUp<size_t>(sizeof(ConstantBuffer1_VS), 256);
static D3D12MA::Allocation* g_CbPerObjectUploadHeapAllocations[FRAME_BUFFER_COUNT];
static CComPtr<ID3D12Resource> g_CbPerObjectUploadHeaps[FRAME_BUFFER_COUNT];
static void* g_CbPerObjectAddress[FRAME_BUFFER_COUNT];
static uint32_t g_CubeIndexCount;
static CComPtr<ID3D12DescriptorHeap> g_MainDescriptorHeap[FRAME_BUFFER_COUNT];
static CComPtr<ID3D12Resource> g_ConstantBufferUploadHeap[FRAME_BUFFER_COUNT];
static D3D12MA::Allocation* g_ConstantBufferUploadAllocation[FRAME_BUFFER_COUNT];
static void* g_ConstantBufferAddress[FRAME_BUFFER_COUNT];
static CComPtr<ID3D12Resource> g_Texture;
static D3D12MA::Allocation* g_TextureAllocation;
static void* const CUSTOM_ALLOCATION_USER_DATA = (void*)(uintptr_t)0xDEADC0DE;
static std::atomic<size_t> g_CpuAllocationCount{0};
static void* CustomAllocate(size_t Size, size_t Alignment, void* pUserData)
{
assert(pUserData == CUSTOM_ALLOCATION_USER_DATA);
void* memory = _aligned_malloc(Size, Alignment);
if(ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT)
{
wprintf(L"Allocate Size=%llu Alignment=%llu -> %p\n", Size, Alignment, memory);
}
++g_CpuAllocationCount;
return memory;
}
static void CustomFree(void* pMemory, void* pUserData)
{
assert(pUserData == CUSTOM_ALLOCATION_USER_DATA);
if(pMemory)
{
--g_CpuAllocationCount;
if(ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT)
{
wprintf(L"Free %p\n", pMemory);
}
_aligned_free(pMemory);
}
}
static void SetDefaultRasterizerDesc(D3D12_RASTERIZER_DESC& outDesc)
{
outDesc.FillMode = D3D12_FILL_MODE_SOLID;
outDesc.CullMode = D3D12_CULL_MODE_BACK;
outDesc.FrontCounterClockwise = FALSE;
outDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
outDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
outDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
outDesc.DepthClipEnable = TRUE;
outDesc.MultisampleEnable = FALSE;
outDesc.AntialiasedLineEnable = FALSE;
outDesc.ForcedSampleCount = 0;
outDesc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
}
static void SetDefaultBlendDesc(D3D12_BLEND_DESC& outDesc)
{
outDesc.AlphaToCoverageEnable = FALSE;
outDesc.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
FALSE,FALSE,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL };
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
outDesc.RenderTarget[i] = defaultRenderTargetBlendDesc;
}
static void SetDefaultDepthStencilDesc(D3D12_DEPTH_STENCIL_DESC& outDesc)
{
outDesc.DepthEnable = TRUE;
outDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
outDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
outDesc.StencilEnable = FALSE;
outDesc.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
outDesc.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 };
outDesc.FrontFace = defaultStencilOp;
outDesc.BackFace = defaultStencilOp;
}
void WaitForFrame(size_t frameIndex) // wait until gpu is finished with command list
{
// if the current g_Fences value is still less than "g_FenceValues", then we know the GPU has not finished executing
// the command queue since it has not reached the "g_CommandQueue->Signal(g_Fences, g_FenceValues)" command
if (g_Fences[frameIndex]->GetCompletedValue() < g_FenceValues[frameIndex])
{
// we have the g_Fences create an event which is signaled once the g_Fences's current value is "g_FenceValues"
CHECK_HR( g_Fences[frameIndex]->SetEventOnCompletion(g_FenceValues[frameIndex], g_FenceEvent) );
// We will wait until the g_Fences has triggered the event that it's current value has reached "g_FenceValues". once it's value
// has reached "g_FenceValues", we know the command queue has finished executing
WaitForSingleObject(g_FenceEvent, INFINITE);
}
}
void WaitGPUIdle(size_t frameIndex)
{
g_FenceValues[frameIndex]++;
CHECK_HR( g_CommandQueue->Signal(g_Fences[frameIndex], g_FenceValues[frameIndex]) );
WaitForFrame(frameIndex);
}
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
// Row-by-row memcpy
inline void MemcpySubresource(
_In_ const D3D12_MEMCPY_DEST* pDest,
_In_ const D3D12_SUBRESOURCE_DATA* pSrc,
SIZE_T RowSizeInBytes,
UINT NumRows,
UINT NumSlices)
{
for (UINT z = 0; z < NumSlices; ++z)
{
BYTE* pDestSlice = reinterpret_cast<BYTE*>(pDest->pData) + pDest->SlicePitch * z;
const BYTE* pSrcSlice = reinterpret_cast<const BYTE*>(pSrc->pData) + pSrc->SlicePitch * z;
for (UINT y = 0; y < NumRows; ++y)
{
memcpy(pDestSlice + pDest->RowPitch * y,
pSrcSlice + pSrc->RowPitch * y,
RowSizeInBytes);
}
}
}
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
inline UINT64 UpdateSubresources(
_In_ ID3D12GraphicsCommandList* pCmdList,
_In_ ID3D12Resource* pDestinationResource,
_In_ ID3D12Resource* pIntermediate,
_In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
_In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources,
UINT64 RequiredSize,
_In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts,
_In_reads_(NumSubresources) const UINT* pNumRows,
_In_reads_(NumSubresources) const UINT64* pRowSizesInBytes,
_In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData)
{
// Minor validation
D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc();
D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc();
if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER ||
IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset ||
RequiredSize > (SIZE_T)-1 ||
(DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER &&
(FirstSubresource != 0 || NumSubresources != 1)))
{
return 0;
}
BYTE* pData;
HRESULT hr = pIntermediate->Map(0, &EMPTY_RANGE, reinterpret_cast<void**>(&pData));
if (FAILED(hr))
{
return 0;
}
for (UINT i = 0; i < NumSubresources; ++i)
{
if (pRowSizesInBytes[i] > (SIZE_T)-1) return 0;
D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, pLayouts[i].Footprint.RowPitch * pNumRows[i] };
MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth);
}
pIntermediate->Unmap(0, NULL);
if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
D3D12_BOX SrcBox = {
UINT( pLayouts[0].Offset ), 0, 0,
UINT( pLayouts[0].Offset + pLayouts[0].Footprint.Width ), 0, 0 };
pCmdList->CopyBufferRegion(
pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width);
}
else
{
for (UINT i = 0; i < NumSubresources; ++i)
{
D3D12_TEXTURE_COPY_LOCATION Dst = {};
Dst.pResource = pDestinationResource;
Dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
Dst.SubresourceIndex = i + FirstSubresource;
D3D12_TEXTURE_COPY_LOCATION Src = {};
Src.pResource = pIntermediate;
Src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
Src.PlacedFootprint = pLayouts[i];
pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
}
}
return RequiredSize;
}
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
inline UINT64 UpdateSubresources(
_In_ ID3D12GraphicsCommandList* pCmdList,
_In_ ID3D12Resource* pDestinationResource,
_In_ ID3D12Resource* pIntermediate,
UINT64 IntermediateOffset,
_In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
_In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources,
_In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData)
{
UINT64 RequiredSize = 0;
UINT64 MemToAlloc = static_cast<UINT64>(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources;
if (MemToAlloc > SIZE_MAX)
{
return 0;
}
void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast<SIZE_T>(MemToAlloc));
if (pMem == NULL)
{
return 0;
}
D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts = reinterpret_cast<D3D12_PLACED_SUBRESOURCE_FOOTPRINT*>(pMem);
UINT64* pRowSizesInBytes = reinterpret_cast<UINT64*>(pLayouts + NumSubresources);
UINT* pNumRows = reinterpret_cast<UINT*>(pRowSizesInBytes + NumSubresources);
D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc();
ID3D12Device* pDevice;
pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize);
pDevice->Release();
UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData);
HeapFree(GetProcessHeap(), 0, pMem);
return Result;
}
void InitD3D() // initializes direct3d 12
{
IDXGIFactory4* dxgiFactory;
CHECK_HR( CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) );
IDXGIAdapter1* adapter = nullptr; // adapters are the graphics card (this includes the embedded graphics on the motherboard)
int adapterIndex = 0; // we'll start looking for directx 12 compatible graphics devices starting at index 0
bool adapterFound = false; // set this to true when a good one was found
// find first hardware gpu that supports d3d 12
while (dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if ((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0)
{
HRESULT hr = D3D12CreateDevice(adapter, MY_D3D_FEATURE_LEVEL, _uuidof(ID3D12Device), nullptr);
if (SUCCEEDED(hr))
{
adapterFound = true;
break;
}
}
adapter->Release();
adapterIndex++;
}
assert(adapterFound);
// Must be done before D3D12 device is created.
if(ENABLE_DEBUG_LAYER)
{
CComPtr<ID3D12Debug> debug;
if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug))))
debug->EnableDebugLayer();
}
// Create the g_Device
ID3D12Device* device = nullptr;
CHECK_HR( D3D12CreateDevice(
adapter,
MY_D3D_FEATURE_LEVEL,
IID_PPV_ARGS(&device)) );
g_Device.Attach(device);
// Create allocator
{
D3D12MA::ALLOCATOR_DESC desc = {};
desc.Flags = g_AllocatorFlags;
desc.pDevice = device;
desc.pAdapter = adapter;
if(ENABLE_CPU_ALLOCATION_CALLBACKS)
{
g_AllocationCallbacks.pAllocate = &CustomAllocate;
g_AllocationCallbacks.pFree = &CustomFree;
g_AllocationCallbacks.pUserData = CUSTOM_ALLOCATION_USER_DATA;
desc.pAllocationCallbacks = &g_AllocationCallbacks;
}
CHECK_HR( D3D12MA::CreateAllocator(&desc, &g_Allocator) );
switch(g_Allocator->GetD3D12Options().ResourceHeapTier)
{
case D3D12_RESOURCE_HEAP_TIER_1:
wprintf(L"ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1\n");
break;
case D3D12_RESOURCE_HEAP_TIER_2:
wprintf(L"ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_2\n");
break;
default:
assert(0);
}
}
// -- Create the Command Queue -- //
D3D12_COMMAND_QUEUE_DESC cqDesc = {}; // we will be using all the default values
ID3D12CommandQueue* commandQueue = nullptr;
CHECK_HR( g_Device->CreateCommandQueue(&cqDesc, IID_PPV_ARGS(&commandQueue)) ); // create the command queue
g_CommandQueue.Attach(commandQueue);
// -- Create the Swap Chain (double/tripple buffering) -- //
DXGI_MODE_DESC backBufferDesc = {}; // this is to describe our display mode
backBufferDesc.Width = SIZE_X; // buffer width
backBufferDesc.Height = SIZE_Y; // buffer height
backBufferDesc.Format = RENDER_TARGET_FORMAT; // format of the buffer (rgba 32 bits, 8 bits for each chanel)
// describe our multi-sampling. We are not multi-sampling, so we set the count to 1 (we need at least one sample of course)
DXGI_SAMPLE_DESC sampleDesc = {};
sampleDesc.Count = 1; // multisample count (no multisampling, so we just put 1, since we still need 1 sample)
// Describe and create the swap chain.
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferCount = FRAME_BUFFER_COUNT; // number of buffers we have
swapChainDesc.BufferDesc = backBufferDesc; // our back buffer description
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // this says the pipeline will render to this swap chain
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // dxgi will discard the buffer (data) after we call present
swapChainDesc.OutputWindow = g_Wnd; // handle to our window
swapChainDesc.SampleDesc = sampleDesc; // our multi-sampling description
swapChainDesc.Windowed = !FULLSCREEN; // set to true, then if in fullscreen must call SetFullScreenState with true for full screen to get uncapped fps
IDXGISwapChain* tempSwapChain;
CHECK_HR( dxgiFactory->CreateSwapChain(
g_CommandQueue, // the queue will be flushed once the swap chain is created
&swapChainDesc, // give it the swap chain description we created above
&tempSwapChain // store the created swap chain in a temp IDXGISwapChain interface
) );
g_SwapChain.Attach(static_cast<IDXGISwapChain3*>(tempSwapChain));
g_FrameIndex = g_SwapChain->GetCurrentBackBufferIndex();
// -- Create the Back Buffers (render target views) Descriptor Heap -- //
// describe an rtv descriptor heap and create
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = FRAME_BUFFER_COUNT; // number of descriptors for this heap.
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // this heap is a render target view heap
// This heap will not be directly referenced by the shaders (not shader visible), as this will store the output from the pipeline
// otherwise we would set the heap's flag to D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ID3D12DescriptorHeap* rtvDescriptorHeap = nullptr;
CHECK_HR( g_Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvDescriptorHeap)) );
g_RtvDescriptorHeap.Attach(rtvDescriptorHeap);
// get the size of a descriptor in this heap (this is a rtv heap, so only rtv descriptors should be stored in it.
// descriptor sizes may vary from g_Device to g_Device, which is why there is no set size and we must ask the
// g_Device to give us the size. we will use this size to increment a descriptor handle offset
g_RtvDescriptorSize = g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// get a handle to the first descriptor in the descriptor heap. a handle is basically a pointer,
// but we cannot literally use it like a c++ pointer.
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle { g_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart() };
// Create a RTV for each buffer (double buffering is two buffers, tripple buffering is 3).
for (int i = 0; i < FRAME_BUFFER_COUNT; i++)
{
// first we get the n'th buffer in the swap chain and store it in the n'th
// position of our ID3D12Resource array
ID3D12Resource* res = nullptr;
CHECK_HR( g_SwapChain->GetBuffer(i, IID_PPV_ARGS(&res)) );
g_RenderTargets[i].Attach(res);
// the we "create" a render target view which binds the swap chain buffer (ID3D12Resource[n]) to the rtv handle
g_Device->CreateRenderTargetView(g_RenderTargets[i], nullptr, rtvHandle);
// we increment the rtv handle by the rtv descriptor size we got above
rtvHandle.ptr += g_RtvDescriptorSize;
}
// -- Create the Command Allocators -- //
for (int i = 0; i < FRAME_BUFFER_COUNT; i++)
{
ID3D12CommandAllocator* commandAllocator = nullptr;
CHECK_HR( g_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)) );
g_CommandAllocators[i].Attach(commandAllocator);
}
// create the command list with the first allocator
CHECK_HR( g_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_CommandAllocators[0], NULL, IID_PPV_ARGS(&g_CommandList)) );
// command lists are created in the recording state. our main loop will set it up for recording again so close it now
g_CommandList->Close();
// create a depth stencil descriptor heap so we can get a pointer to the depth stencil buffer
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
CHECK_HR( g_Device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&g_DepthStencilDescriptorHeap)) );
D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
depthOptimizedClearValue.Format = DEPTH_STENCIL_FORMAT;
depthOptimizedClearValue.DepthStencil.Depth = 1.0f;
depthOptimizedClearValue.DepthStencil.Stencil = 0;
D3D12MA::ALLOCATION_DESC depthStencilAllocDesc = {};
depthStencilAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC depthStencilResourceDesc = {};
depthStencilResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depthStencilResourceDesc.Alignment = 0;
depthStencilResourceDesc.Width = SIZE_X;
depthStencilResourceDesc.Height = SIZE_Y;
depthStencilResourceDesc.DepthOrArraySize = 1;
depthStencilResourceDesc.MipLevels = 1;
depthStencilResourceDesc.Format = DEPTH_STENCIL_FORMAT;
depthStencilResourceDesc.SampleDesc.Count = 1;
depthStencilResourceDesc.SampleDesc.Quality = 0;
depthStencilResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depthStencilResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
CHECK_HR( g_Allocator->CreateResource(
&depthStencilAllocDesc,
&depthStencilResourceDesc,
D3D12_RESOURCE_STATE_DEPTH_WRITE,
&depthOptimizedClearValue,
&g_DepthStencilAllocation,
IID_PPV_ARGS(&g_DepthStencilBuffer)
) );
CHECK_HR( g_DepthStencilBuffer->SetName(L"Depth/Stencil Resource Heap") );
D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
depthStencilDesc.Format = DEPTH_STENCIL_FORMAT;
depthStencilDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE;
g_Device->CreateDepthStencilView(g_DepthStencilBuffer, &depthStencilDesc, g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
// -- Create a Fence & Fence Event -- //
// create the fences
for (int i = 0; i < FRAME_BUFFER_COUNT; i++)
{
ID3D12Fence* fence = nullptr;
CHECK_HR( g_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)) );
g_Fences[i].Attach(fence);
g_FenceValues[i] = 0; // set the initial g_Fences value to 0
}
// create a handle to a g_Fences event
g_FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
assert(g_FenceEvent);
D3D12_DESCRIPTOR_RANGE cbDescriptorRange;
cbDescriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
cbDescriptorRange.NumDescriptors = 1;
cbDescriptorRange.BaseShaderRegister = 0;
cbDescriptorRange.RegisterSpace = 0;
cbDescriptorRange.OffsetInDescriptorsFromTableStart = 0;
D3D12_DESCRIPTOR_RANGE textureDescRange;
textureDescRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
textureDescRange.NumDescriptors = 1;
textureDescRange.BaseShaderRegister = 0;
textureDescRange.RegisterSpace = 0;
textureDescRange.OffsetInDescriptorsFromTableStart = 1;
D3D12_ROOT_PARAMETER rootParameters[3];
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameters[0].DescriptorTable = {1, &cbDescriptorRange};
rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
rootParameters[1].Descriptor = {1, 0};
rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameters[2].DescriptorTable = {1, &textureDescRange};
rootParameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
// create root signature
// create a static sampler
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
rootSignatureDesc.NumParameters = _countof(rootParameters);
rootSignatureDesc.pParameters = rootParameters;
rootSignatureDesc.NumStaticSamplers = 1;
rootSignatureDesc.pStaticSamplers = &sampler;
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
CComPtr<ID3DBlob> signatureBlob;
ID3DBlob* signatureBlobPtr;
CHECK_HR( D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signatureBlobPtr, nullptr) );
signatureBlob.Attach(signatureBlobPtr);
ID3D12RootSignature* rootSignature = nullptr;
CHECK_HR( device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&rootSignature)) );
g_RootSignature.Attach(rootSignature);
for (int i = 0; i < FRAME_BUFFER_COUNT; ++i)
{
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = 2;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
CHECK_HR( g_Device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&g_MainDescriptorHeap[i])) );
}
// # CONSTANT BUFFER
for (int i = 0; i < FRAME_BUFFER_COUNT; ++i)
{
D3D12MA::ALLOCATION_DESC constantBufferUploadAllocDesc = {};
constantBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC constantBufferResourceDesc = {};
constantBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
constantBufferResourceDesc.Alignment = 0;
constantBufferResourceDesc.Width = 1024 * 64;
constantBufferResourceDesc.Height = 1;
constantBufferResourceDesc.DepthOrArraySize = 1;
constantBufferResourceDesc.MipLevels = 1;
constantBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
constantBufferResourceDesc.SampleDesc.Count = 1;
constantBufferResourceDesc.SampleDesc.Quality = 0;
constantBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
constantBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
CHECK_HR( g_Allocator->CreateResource(
&constantBufferUploadAllocDesc,
&constantBufferResourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
&g_ConstantBufferUploadAllocation[i],
IID_PPV_ARGS(&g_ConstantBufferUploadHeap[i])) );
g_ConstantBufferUploadHeap[i]->SetName(L"Constant Buffer Upload Resource Heap");
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = g_ConstantBufferUploadHeap[i]->GetGPUVirtualAddress();
cbvDesc.SizeInBytes = AlignUp<UINT>(sizeof(ConstantBuffer0_PS), 256);
g_Device->CreateConstantBufferView(&cbvDesc, g_MainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart());
CHECK_HR( g_ConstantBufferUploadHeap[i]->Map(0, &EMPTY_RANGE, &g_ConstantBufferAddress[i]) );
}
// create input layout
// The input layout is used by the Input Assembler so that it knows
// how to read the vertex data bound to it.
const D3D12_INPUT_ELEMENT_DESC inputLayout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
// create a pipeline state object (PSO)
// In a real application, you will have many pso's. for each different shader
// or different combinations of shaders, different blend states or different rasterizer states,
// different topology types (point, line, triangle, patch), or a different number
// of render targets you will need a pso
// VS is the only required shader for a pso. You might be wondering when a case would be where
// you only set the VS. It's possible that you have a pso that only outputs data with the stream
// output, and not on a render target, which means you would not need anything after the stream
// output.
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; // a structure to define a pso
psoDesc.InputLayout.NumElements = _countof(inputLayout);
psoDesc.InputLayout.pInputElementDescs = inputLayout;
psoDesc.pRootSignature = g_RootSignature; // the root signature that describes the input data this pso needs
psoDesc.VS.BytecodeLength = sizeof(VS::g_main);
psoDesc.VS.pShaderBytecode = VS::g_main;
psoDesc.PS.BytecodeLength = sizeof(PS::g_main);
psoDesc.PS.pShaderBytecode = PS::g_main;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // type of topology we are drawing
psoDesc.RTVFormats[0] = RENDER_TARGET_FORMAT; // format of the render target
psoDesc.DSVFormat = DEPTH_STENCIL_FORMAT;
psoDesc.SampleDesc = sampleDesc; // must be the same sample description as the swapchain and depth/stencil buffer
psoDesc.SampleMask = 0xffffffff; // sample mask has to do with multi-sampling. 0xffffffff means point sampling is done
SetDefaultRasterizerDesc(psoDesc.RasterizerState);
SetDefaultBlendDesc(psoDesc.BlendState);
psoDesc.NumRenderTargets = 1; // we are only binding one render target
SetDefaultDepthStencilDesc(psoDesc.DepthStencilState);
// create the pso
ID3D12PipelineState* pipelineStateObject;
CHECK_HR( device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineStateObject)) );
g_PipelineStateObject.Attach(pipelineStateObject);
// Create vertex buffer
// a triangle
Vertex vList[] = {
// front face
{ -0.5f, 0.5f, -0.5f, 0.f, 0.f },
{ 0.5f, -0.5f, -0.5f, 1.f, 1.f },
{ -0.5f, -0.5f, -0.5f, 0.f, 1.f },
{ 0.5f, 0.5f, -0.5f, 1.f, 0.f },
// right side face
{ 0.5f, -0.5f, -0.5f, 0.f, 1.f },
{ 0.5f, 0.5f, 0.5f, 1.f, 0.f },
{ 0.5f, -0.5f, 0.5f, 1.f, 1.f },
{ 0.5f, 0.5f, -0.5f, 0.f, 0.f },
// left side face
{ -0.5f, 0.5f, 0.5f, 0.f, 0.f },
{ -0.5f, -0.5f, -0.5f, 1.f, 1.f },
{ -0.5f, -0.5f, 0.5f, 0.f, 1.f },
{ -0.5f, 0.5f, -0.5f, 1.f, 0.f },
// back face
{ 0.5f, 0.5f, 0.5f, 0.f, 0.f },
{ -0.5f, -0.5f, 0.5f, 1.f, 1.f },
{ 0.5f, -0.5f, 0.5f, 0.f, 1.f },
{ -0.5f, 0.5f, 0.5f, 1.f, 0.f },
// top face
{ -0.5f, 0.5f, -0.5f, 0.f, 0.f },
{ 0.5f, 0.5f, 0.5f, 1.f, 1.f },
{ 0.5f, 0.5f, -0.5f, 0.f, 1.f },
{ -0.5f, 0.5f, 0.5f, 1.f, 0.f },
// bottom face
{ 0.5f, -0.5f, 0.5f, 0.f, 0.f },
{ -0.5f, -0.5f, -0.5f, 1.f, 1.f },
{ 0.5f, -0.5f, -0.5f, 0.f, 1.f },
{ -0.5f, -0.5f, 0.5f, 1.f, 0.f },
};
const uint32_t vBufferSize = sizeof(vList);
// create default heap
// default heap is memory on the GPU. Only the GPU has access to this memory
// To get data into this heap, we will have to upload the data using
// an upload heap
D3D12MA::ALLOCATION_DESC vertexBufferAllocDesc = {};
vertexBufferAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC vertexBufferResourceDesc = {};
vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
vertexBufferResourceDesc.Alignment = 0;
vertexBufferResourceDesc.Width = vBufferSize;
vertexBufferResourceDesc.Height = 1;
vertexBufferResourceDesc.DepthOrArraySize = 1;
vertexBufferResourceDesc.MipLevels = 1;
vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
vertexBufferResourceDesc.SampleDesc.Count = 1;
vertexBufferResourceDesc.SampleDesc.Quality = 0;
vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* vertexBufferPtr;
CHECK_HR( g_Allocator->CreateResource(
&vertexBufferAllocDesc,
&vertexBufferResourceDesc, // resource description for a buffer
D3D12_RESOURCE_STATE_COPY_DEST, // we will start this heap in the copy destination state since we will copy data
// from the upload heap to this heap
nullptr, // optimized clear value must be null for this type of resource. used for render targets and depth/stencil buffers
&g_VertexBufferAllocation,
IID_PPV_ARGS(&vertexBufferPtr)) );
g_VertexBuffer.Attach(vertexBufferPtr);
// we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at
g_VertexBuffer->SetName(L"Vertex Buffer Resource Heap");
// create upload heap
// upload heaps are used to upload data to the GPU. CPU can write to it, GPU can read from it
// We will upload the vertex buffer using this heap to the default heap
D3D12MA::ALLOCATION_DESC vBufferUploadAllocDesc = {};
vBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC vertexBufferUploadResourceDesc = {};
vertexBufferUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
vertexBufferUploadResourceDesc.Alignment = 0;
vertexBufferUploadResourceDesc.Width = vBufferSize;
vertexBufferUploadResourceDesc.Height = 1;
vertexBufferUploadResourceDesc.DepthOrArraySize = 1;
vertexBufferUploadResourceDesc.MipLevels = 1;
vertexBufferUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
vertexBufferUploadResourceDesc.SampleDesc.Count = 1;
vertexBufferUploadResourceDesc.SampleDesc.Quality = 0;
vertexBufferUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
vertexBufferUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
CComPtr<ID3D12Resource> vBufferUploadHeap;
D3D12MA::Allocation* vBufferUploadHeapAllocation = nullptr;
CHECK_HR( g_Allocator->CreateResource(
&vBufferUploadAllocDesc,
&vertexBufferUploadResourceDesc, // resource description for a buffer
D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap
nullptr,
&vBufferUploadHeapAllocation,
IID_PPV_ARGS(&vBufferUploadHeap)) );
vBufferUploadHeap->SetName(L"Vertex Buffer Upload Resource Heap");
// store vertex buffer in upload heap
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = reinterpret_cast<BYTE*>(vList); // pointer to our vertex array
vertexData.RowPitch = vBufferSize; // size of all our triangle vertex data
vertexData.SlicePitch = vBufferSize; // also the size of our triangle vertex data
CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex], NULL) );
// we are now creating a command with the command list to copy the data from
// the upload heap to the default heap
UINT64 r = UpdateSubresources(g_CommandList, g_VertexBuffer, vBufferUploadHeap, 0, 0, 1, &vertexData);
assert(r);
// transition the vertex buffer data from copy destination state to vertex buffer state
D3D12_RESOURCE_BARRIER vbBarrier = {};
vbBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
vbBarrier.Transition.pResource = g_VertexBuffer;
vbBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
vbBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
vbBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
g_CommandList->ResourceBarrier(1, &vbBarrier);
// Create index buffer
// a quad (2 triangles)
uint16_t iList[] = {
// ffront face
0, 1, 2, // first triangle
0, 3, 1, // second triangle
// left face
4, 5, 6, // first triangle
4, 7, 5, // second triangle
// right face
8, 9, 10, // first triangle
8, 11, 9, // second triangle
// back face
12, 13, 14, // first triangle
12, 15, 13, // second triangle
// top face
16, 17, 18, // first triangle
16, 19, 17, // second triangle
// bottom face
20, 21, 22, // first triangle
20, 23, 21, // second triangle
};
g_CubeIndexCount = (uint32_t)_countof(iList);
size_t iBufferSize = sizeof(iList);
// create default heap to hold index buffer
D3D12MA::ALLOCATION_DESC indexBufferAllocDesc = {};
indexBufferAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC indexBufferResourceDesc = {};
indexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
indexBufferResourceDesc.Alignment = 0;
indexBufferResourceDesc.Width = iBufferSize;
indexBufferResourceDesc.Height = 1;
indexBufferResourceDesc.DepthOrArraySize = 1;
indexBufferResourceDesc.MipLevels = 1;
indexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
indexBufferResourceDesc.SampleDesc.Count = 1;
indexBufferResourceDesc.SampleDesc.Quality = 0;
indexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
indexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
CHECK_HR( g_Allocator->CreateResource(
&indexBufferAllocDesc,
&indexBufferResourceDesc, // resource description for a buffer
D3D12_RESOURCE_STATE_COPY_DEST, // start in the copy destination state
nullptr, // optimized clear value must be null for this type of resource
&g_IndexBufferAllocation,
IID_PPV_ARGS(&g_IndexBuffer)) );
// we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at
g_IndexBuffer->SetName(L"Index Buffer Resource Heap");
// create upload heap to upload index buffer
D3D12MA::ALLOCATION_DESC iBufferUploadAllocDesc = {};
iBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC indexBufferUploadResourceDesc = {};
indexBufferUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
indexBufferUploadResourceDesc.Alignment = 0;
indexBufferUploadResourceDesc.Width = iBufferSize;
indexBufferUploadResourceDesc.Height = 1;
indexBufferUploadResourceDesc.DepthOrArraySize = 1;
indexBufferUploadResourceDesc.MipLevels = 1;
indexBufferUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
indexBufferUploadResourceDesc.SampleDesc.Count = 1;
indexBufferUploadResourceDesc.SampleDesc.Quality = 0;
indexBufferUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
indexBufferUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
CComPtr<ID3D12Resource> iBufferUploadHeap;
D3D12MA::Allocation* iBufferUploadHeapAllocation = nullptr;
CHECK_HR( g_Allocator->CreateResource(
&iBufferUploadAllocDesc,
&indexBufferUploadResourceDesc, // resource description for a buffer
D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap
nullptr,
&iBufferUploadHeapAllocation,
IID_PPV_ARGS(&iBufferUploadHeap)) );
CHECK_HR( iBufferUploadHeap->SetName(L"Index Buffer Upload Resource Heap") );
// store vertex buffer in upload heap
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = iList; // pointer to our index array
indexData.RowPitch = iBufferSize; // size of all our index buffer
indexData.SlicePitch = iBufferSize; // also the size of our index buffer
// we are now creating a command with the command list to copy the data from
// the upload heap to the default heap
r = UpdateSubresources(g_CommandList, g_IndexBuffer, iBufferUploadHeap, 0, 0, 1, &indexData);
assert(r);
// transition the index buffer data from copy destination state to vertex buffer state
D3D12_RESOURCE_BARRIER ibBarrier = {};
ibBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
ibBarrier.Transition.pResource = g_IndexBuffer;
ibBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
ibBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER;
ibBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
g_CommandList->ResourceBarrier(1, &ibBarrier);
// create a vertex buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method
g_VertexBufferView.BufferLocation = g_VertexBuffer->GetGPUVirtualAddress();
g_VertexBufferView.StrideInBytes = sizeof(Vertex);
g_VertexBufferView.SizeInBytes = vBufferSize;
// create a index buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method
g_IndexBufferView.BufferLocation = g_IndexBuffer->GetGPUVirtualAddress();
g_IndexBufferView.Format = DXGI_FORMAT_R16_UINT;
g_IndexBufferView.SizeInBytes = (UINT)iBufferSize;
D3D12MA::ALLOCATION_DESC cbPerObjectUploadAllocDesc = {};
cbPerObjectUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC cbPerObjectUploadResourceDesc = {};
cbPerObjectUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
cbPerObjectUploadResourceDesc.Alignment = 0;
cbPerObjectUploadResourceDesc.Width = 1024 * 64;
cbPerObjectUploadResourceDesc.Height = 1;
cbPerObjectUploadResourceDesc.DepthOrArraySize = 1;
cbPerObjectUploadResourceDesc.MipLevels = 1;
cbPerObjectUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
cbPerObjectUploadResourceDesc.SampleDesc.Count = 1;
cbPerObjectUploadResourceDesc.SampleDesc.Quality = 0;
cbPerObjectUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
cbPerObjectUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i)
{
// create resource for cube 1
CHECK_HR( g_Allocator->CreateResource(
&cbPerObjectUploadAllocDesc,
&cbPerObjectUploadResourceDesc, // size of the resource heap. Must be a multiple of 64KB for single-textures and constant buffers
D3D12_RESOURCE_STATE_GENERIC_READ, // will be data that is read from so we keep it in the generic read state
nullptr, // we do not have use an optimized clear value for constant buffers
&g_CbPerObjectUploadHeapAllocations[i],
IID_PPV_ARGS(&g_CbPerObjectUploadHeaps[i])) );
g_CbPerObjectUploadHeaps[i]->SetName(L"Constant Buffer Upload Resource Heap");
CHECK_HR( g_CbPerObjectUploadHeaps[i]->Map(0, &EMPTY_RANGE, &g_CbPerObjectAddress[i]) );
}
// # TEXTURE
D3D12_RESOURCE_DESC textureDesc;
size_t imageBytesPerRow;
size_t imageSize = SIZE_MAX;
std::vector<char> imageData;
{
const UINT sizeX = 256;
const UINT sizeY = 256;
const DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
const UINT bytesPerPixel = 4;
imageBytesPerRow = sizeX * bytesPerPixel;
imageSize = sizeY * imageBytesPerRow;
imageData.resize(imageSize);
char* rowPtr = (char*)imageData.data();
for(UINT y = 0; y < sizeY; ++y)
{
char* pixelPtr = rowPtr;
for(UINT x = 0; x < sizeX; ++x)
{
*(UINT8*)(pixelPtr ) = (UINT8)x; // R
*(UINT8*)(pixelPtr + 1) = (UINT8)y; // G
*(UINT8*)(pixelPtr + 2) = 0x00; // B
*(UINT8*)(pixelPtr + 3) = 0xFF; // A
*(UINT8*)(pixelPtr ) = x > 128 ? 0xFF : 00;
*(UINT8*)(pixelPtr + 1) = y > 128 ? 0xFF : 00;
pixelPtr += bytesPerPixel;
}
rowPtr += imageBytesPerRow;
}
textureDesc = {};
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
textureDesc.Alignment = 0;
textureDesc.Width = sizeX;
textureDesc.Height = sizeY;
textureDesc.DepthOrArraySize = 1;
textureDesc.MipLevels = 1;
textureDesc.Format = format;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
}
D3D12MA::ALLOCATION_DESC textureAllocDesc = {};
textureAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
CHECK_HR( g_Allocator->CreateResource(
&textureAllocDesc,
&textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr, // pOptimizedClearValue
&g_TextureAllocation,
IID_PPV_ARGS(&g_Texture)) );
g_Texture->SetName(L"g_Texture");
UINT64 textureUploadBufferSize;
device->GetCopyableFootprints(
&textureDesc,
0, // FirstSubresource
1, // NumSubresources
0, // BaseOffset
nullptr, // pLayouts
nullptr, // pNumRows
nullptr, // pRowSizeInBytes
&textureUploadBufferSize); // pTotalBytes
D3D12MA::ALLOCATION_DESC textureUploadAllocDesc = {};
textureUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
D3D12_RESOURCE_DESC textureUploadResourceDesc = {};
textureUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
textureUploadResourceDesc.Alignment = 0;
textureUploadResourceDesc.Width = textureUploadBufferSize;
textureUploadResourceDesc.Height = 1;
textureUploadResourceDesc.DepthOrArraySize = 1;
textureUploadResourceDesc.MipLevels = 1;
textureUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
textureUploadResourceDesc.SampleDesc.Count = 1;
textureUploadResourceDesc.SampleDesc.Quality = 0;
textureUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
textureUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
CComPtr<ID3D12Resource> textureUpload;
D3D12MA::Allocation* textureUploadAllocation;
CHECK_HR( g_Allocator->CreateResource(
&textureUploadAllocDesc,
&textureUploadResourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr, // pOptimizedClearValue
&textureUploadAllocation,
IID_PPV_ARGS(&textureUpload)) );
textureUpload->SetName(L"textureUpload");
D3D12_SUBRESOURCE_DATA textureSubresourceData = {};
textureSubresourceData.pData = imageData.data();
textureSubresourceData.RowPitch = imageBytesPerRow;
textureSubresourceData.SlicePitch = imageBytesPerRow * textureDesc.Height;
UpdateSubresources(g_CommandList, g_Texture, textureUpload, 0, 0, 1, &textureSubresourceData);
D3D12_RESOURCE_BARRIER textureBarrier = {};
textureBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
textureBarrier.Transition.pResource = g_Texture;
textureBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
textureBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
textureBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
g_CommandList->ResourceBarrier(1, &textureBarrier);
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = textureDesc.Format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i)
{
D3D12_CPU_DESCRIPTOR_HANDLE descHandle = {
g_MainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart().ptr +
g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)};
g_Device->CreateShaderResourceView(g_Texture, &srvDesc, descHandle);
}
// # END OF INITIAL COMMAND LIST
// Now we execute the command list to upload the initial assets (triangle data)
g_CommandList->Close();
ID3D12CommandList* ppCommandLists[] = { g_CommandList };
commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// increment the fence value now, otherwise the buffer might not be uploaded by the time we start drawing
WaitGPUIdle(g_FrameIndex);
textureUploadAllocation->Release();
iBufferUploadHeapAllocation->Release();
vBufferUploadHeapAllocation->Release();
}
void Update()
{
{
const float f = sin(g_Time * (PI * 2.f)) * 0.5f + 0.5f;
ConstantBuffer0_PS cb;
cb.Color = vec4(f, f, f, 1.f);
memcpy(g_ConstantBufferAddress[g_FrameIndex], &cb, sizeof(cb));
}
{
const mat4 projection = mat4::Perspective(
45.f * (PI / 180.f), // fovY
(float)SIZE_X / (float)SIZE_Y, // aspectRatio
0.1f, // zNear
1000.0f); // zFar
const mat4 view = mat4::LookAt(
vec3(0.f, 0.f, 0.f), // at
vec3(-.4f, 1.7f, -3.5f), // eye
vec3(0.f, 1.f, 0.f)); // up
const mat4 viewProjection = view * projection;
mat4 cube1World = mat4::RotationZ(g_Time);
ConstantBuffer1_VS cb;
mat4 worldViewProjection = cube1World * viewProjection;
cb.WorldViewProj = worldViewProjection.Transposed();
memcpy(g_CbPerObjectAddress[g_FrameIndex], &cb, sizeof(cb));
mat4 cube2World = mat4::Scaling(0.5f) *
mat4::RotationX(g_Time * 2.0f) *
mat4::Translation(vec3(-1.2f, 0.f, 0.f)) *
cube1World;
worldViewProjection = cube2World * viewProjection;
cb.WorldViewProj = worldViewProjection.Transposed();
memcpy((char*)g_CbPerObjectAddress[g_FrameIndex] + ConstantBufferPerObjectAlignedSize, &cb, sizeof(cb));
}
}
void Render() // execute the command list
{
// # Here was UpdatePipeline function.
// swap the current rtv buffer index so we draw on the correct buffer
g_FrameIndex = g_SwapChain->GetCurrentBackBufferIndex();
// We have to wait for the gpu to finish with the command allocator before we reset it
WaitForFrame(g_FrameIndex);
// increment g_FenceValues for next frame
g_FenceValues[g_FrameIndex]++;
// we can only reset an allocator once the gpu is done with it
// resetting an allocator frees the memory that the command list was stored in
CHECK_HR( g_CommandAllocators[g_FrameIndex]->Reset() );
// reset the command list. by resetting the command list we are putting it into
// a recording state so we can start recording commands into the command allocator.
// the command allocator that we reference here may have multiple command lists
// associated with it, but only one can be recording at any time. Make sure
// that any other command lists associated to this command allocator are in
// the closed state (not recording).
// Here you will pass an initial pipeline state object as the second parameter,
// but in this tutorial we are only clearing the rtv, and do not actually need
// anything but an initial default pipeline, which is what we get by setting
// the second parameter to NULL
CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex], NULL) );
// here we start recording commands into the g_CommandList (which all the commands will be stored in the g_CommandAllocators)
// transition the "g_FrameIndex" render target from the present state to the render target state so the command list draws to it starting from here
D3D12_RESOURCE_BARRIER presentToRenderTargetBarrier = {};
presentToRenderTargetBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
presentToRenderTargetBarrier.Transition.pResource = g_RenderTargets[g_FrameIndex];
presentToRenderTargetBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
presentToRenderTargetBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
presentToRenderTargetBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
g_CommandList->ResourceBarrier(1, &presentToRenderTargetBarrier);
// here we again get the handle to our current render target view so we can set it as the render target in the output merger stage of the pipeline
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = {
g_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + g_FrameIndex * g_RtvDescriptorSize};
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle =
g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
// set the render target for the output merger stage (the output of the pipeline)
g_CommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
g_CommandList->ClearDepthStencilView(g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
// Clear the render target by using the ClearRenderTargetView command
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
g_CommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
g_CommandList->SetPipelineState(g_PipelineStateObject);
g_CommandList->SetGraphicsRootSignature(g_RootSignature);
ID3D12DescriptorHeap* descriptorHeaps[] = { g_MainDescriptorHeap[g_FrameIndex] };
g_CommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
g_CommandList->SetGraphicsRootDescriptorTable(0, g_MainDescriptorHeap[g_FrameIndex]->GetGPUDescriptorHandleForHeapStart());
g_CommandList->SetGraphicsRootDescriptorTable(2, g_MainDescriptorHeap[g_FrameIndex]->GetGPUDescriptorHandleForHeapStart());
D3D12_VIEWPORT viewport{0.f, 0.f, (float)SIZE_X, (float)SIZE_Y, 0.f, 1.f};
g_CommandList->RSSetViewports(1, &viewport); // set the viewports
D3D12_RECT scissorRect{0, 0, SIZE_X, SIZE_Y};
g_CommandList->RSSetScissorRects(1, &scissorRect); // set the scissor rects
g_CommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // set the primitive topology
g_CommandList->IASetVertexBuffers(0, 1, &g_VertexBufferView); // set the vertex buffer (using the vertex buffer view)
g_CommandList->IASetIndexBuffer(&g_IndexBufferView);
g_CommandList->SetGraphicsRootConstantBufferView(1,
g_CbPerObjectUploadHeaps[g_FrameIndex]->GetGPUVirtualAddress());
g_CommandList->DrawIndexedInstanced(g_CubeIndexCount, 1, 0, 0, 0);
g_CommandList->SetGraphicsRootConstantBufferView(1,
g_CbPerObjectUploadHeaps[g_FrameIndex]->GetGPUVirtualAddress() + ConstantBufferPerObjectAlignedSize);
g_CommandList->DrawIndexedInstanced(g_CubeIndexCount, 1, 0, 0, 0);
// transition the "g_FrameIndex" render target from the render target state to the present state. If the debug layer is enabled, you will receive a
// warning if present is called on the render target when it's not in the present state
D3D12_RESOURCE_BARRIER renderTargetToPresentBarrier = {};
renderTargetToPresentBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
renderTargetToPresentBarrier.Transition.pResource = g_RenderTargets[g_FrameIndex];
renderTargetToPresentBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
renderTargetToPresentBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
renderTargetToPresentBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
g_CommandList->ResourceBarrier(1, &renderTargetToPresentBarrier);
CHECK_HR( g_CommandList->Close() );
// ================
// create an array of command lists (only one command list here)
ID3D12CommandList* ppCommandLists[] = { g_CommandList };
// execute the array of command lists
g_CommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// this command goes in at the end of our command queue. we will know when our command queue
// has finished because the g_Fences value will be set to "g_FenceValues" from the GPU since the command
// queue is being executed on the GPU
CHECK_HR( g_CommandQueue->Signal(g_Fences[g_FrameIndex], g_FenceValues[g_FrameIndex]) );
// present the current backbuffer
CHECK_HR( g_SwapChain->Present(PRESENT_SYNC_INTERVAL, 0) );
}
void Cleanup() // release com ojects and clean up memory
{
// wait for the gpu to finish all frames
for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i)
{
WaitForFrame(i);
CHECK_HR( g_CommandQueue->Wait(g_Fences[i], g_FenceValues[i]) );
}
// get swapchain out of full screen before exiting
BOOL fs = false;
CHECK_HR( g_SwapChain->GetFullscreenState(&fs, NULL) );
if (fs)
g_SwapChain->SetFullscreenState(false, NULL);
WaitGPUIdle(0);
g_Texture.Release();
g_TextureAllocation->Release(); g_TextureAllocation = nullptr;
g_IndexBuffer.Release();
g_IndexBufferAllocation->Release(); g_IndexBufferAllocation = nullptr;
g_VertexBuffer.Release();
g_VertexBufferAllocation->Release(); g_VertexBufferAllocation = nullptr;
g_PipelineStateObject.Release();
g_RootSignature.Release();
CloseHandle(g_FenceEvent);
g_CommandList.Release();
g_CommandQueue.Release();
for (size_t i = FRAME_BUFFER_COUNT; i--; )
{
g_CbPerObjectUploadHeaps[i].Release();
g_CbPerObjectUploadHeapAllocations[i]->Release(); g_CbPerObjectUploadHeapAllocations[i] = nullptr;
g_MainDescriptorHeap[i].Release();
g_ConstantBufferUploadHeap[i].Release();
g_ConstantBufferUploadAllocation[i]->Release(); g_ConstantBufferUploadAllocation[i] = nullptr;
}
g_DepthStencilDescriptorHeap.Release();
g_DepthStencilBuffer.Release();
g_DepthStencilAllocation->Release(); g_DepthStencilAllocation = nullptr;
g_RtvDescriptorHeap.Release();
for (size_t i = FRAME_BUFFER_COUNT; i--; )
{
g_RenderTargets[i].Release();
g_CommandAllocators[i].Release();
g_Fences[i].Release();
}
g_Allocator->Release(); g_Allocator = nullptr;
if(ENABLE_CPU_ALLOCATION_CALLBACKS)
{
assert(g_CpuAllocationCount.load() == 0);
}
g_Device.Release();
g_SwapChain.Release();
}
static void ExecuteTests()
{
try
{
TestContext ctx = {};
ctx.allocationCallbacks = &g_AllocationCallbacks;
ctx.device = g_Device;
ctx.allocator = g_Allocator;
ctx.allocatorFlags = g_AllocatorFlags;
Test(ctx);
}
catch(const std::exception& ex)
{
wprintf(L"ERROR: %hs\n", ex.what());
}
}
static void OnKeyDown(WPARAM key)
{
switch (key)
{
case 'T':
ExecuteTests();
break;
case 'J':
{
WCHAR* statsString = NULL;
g_Allocator->BuildStatsString(&statsString, TRUE);
wprintf(L"%s\n", statsString);
g_Allocator->FreeStatsString(statsString);
}
break;
case VK_ESCAPE:
PostMessage(g_Wnd, WM_CLOSE, 0, 0);
break;
}
}
static LRESULT WINAPI WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
g_Wnd = wnd;
InitD3D();
g_TimeOffset = GetTickCount64();
return 0;
case WM_DESTROY:
Cleanup();
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
OnKeyDown(wParam);
return 0;
}
return DefWindowProc(wnd, msg, wParam, lParam);
}
ID3D12GraphicsCommandList* BeginCommandList()
{
CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex], NULL) );
return g_CommandList;
}
void EndCommandList(ID3D12GraphicsCommandList* cmdList)
{
cmdList->Close();
ID3D12CommandList* genericCmdList = cmdList;
g_CommandQueue->ExecuteCommandLists(1, &genericCmdList);
WaitGPUIdle(g_FrameIndex);
}
int main()
{
g_Instance = (HINSTANCE)GetModuleHandle(NULL);
CoInitialize(NULL);
WNDCLASSEX wndClass;
ZeroMemory(&wndClass, sizeof(wndClass));
wndClass.cbSize = sizeof(wndClass);
wndClass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
wndClass.hbrBackground = NULL;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = g_Instance;
wndClass.lpfnWndProc = &WndProc;
wndClass.lpszClassName = CLASS_NAME;
ATOM classR = RegisterClassEx(&wndClass);
assert(classR);
DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE;
DWORD exStyle = 0;
RECT rect = { 0, 0, SIZE_X, SIZE_Y };
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
g_Wnd = CreateWindowEx(
exStyle,
CLASS_NAME,
WINDOW_TITLE,
style,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
NULL,
NULL,
g_Instance,
0);
assert(g_Wnd);
MSG msg;
for (;;)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
const UINT64 newTimeValue = GetTickCount64() - g_TimeOffset;
g_TimeDelta = (float)(newTimeValue - g_TimeValue) * 0.001f;
g_TimeValue = newTimeValue;
g_Time = (float)newTimeValue * 0.001f;
Update();
Render();
}
}
return (int)msg.wParam;
}