1 //
2 // Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "D3D12MemAlloc.h"
24 #include "Common.h"
25 #include "Tests.h"
26 #include <atomic>
27
28 namespace VS
29 {
30 #include "Shaders\VS_Compiled.h"
31 }
32 namespace PS
33 {
34 #include "Shaders\PS_Compiled.h"
35 }
36
37 static const wchar_t * const CLASS_NAME = L"D3D12MemAllocSample";
38 static const wchar_t * const WINDOW_TITLE = L"D3D12 Memory Allocator Sample";
39 static const int SIZE_X = 1024;
40 static const int SIZE_Y = 576;
41 static const bool FULLSCREEN = false;
42 static const UINT PRESENT_SYNC_INTERVAL = 1;
43 static const DXGI_FORMAT RENDER_TARGET_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
44 static const DXGI_FORMAT DEPTH_STENCIL_FORMAT = DXGI_FORMAT_D32_FLOAT;
45 static const size_t FRAME_BUFFER_COUNT = 3; // number of buffers we want, 2 for double buffering, 3 for tripple buffering
46 static const D3D_FEATURE_LEVEL MY_D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_12_0;
47
48 static const bool ENABLE_DEBUG_LAYER = true;
49 static const bool ENABLE_CPU_ALLOCATION_CALLBACKS = true;
50 static const bool ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT = false;
51 static constexpr D3D12MA::ALLOCATOR_FLAGS g_AllocatorFlags = D3D12MA::ALLOCATOR_FLAG_NONE;
52 static D3D12MA::ALLOCATION_CALLBACKS g_AllocationCallbacks = {}; // Used only when ENABLE_CPU_ALLOCATION_CALLBACKS
53
54 static HINSTANCE g_Instance;
55 static HWND g_Wnd;
56
57 static UINT64 g_TimeOffset; // In ms.
58 static UINT64 g_TimeValue; // Time since g_TimeOffset, in ms.
59 static float g_Time; // g_TimeValue converted to float, in seconds.
60 static float g_TimeDelta;
61
62 static CComPtr<ID3D12Device> g_Device;
63 static D3D12MA::Allocator* g_Allocator;
64
65 static CComPtr<IDXGISwapChain3> g_SwapChain; // swapchain used to switch between render targets
66 static CComPtr<ID3D12CommandQueue> g_CommandQueue; // container for command lists
67 static CComPtr<ID3D12DescriptorHeap> g_RtvDescriptorHeap; // a descriptor heap to hold resources like the render targets
68 static CComPtr<ID3D12Resource> g_RenderTargets[FRAME_BUFFER_COUNT]; // number of render targets equal to buffer count
69 static CComPtr<ID3D12CommandAllocator> g_CommandAllocators[FRAME_BUFFER_COUNT]; // we want enough allocators for each buffer * number of threads (we only have one thread)
70 static CComPtr<ID3D12GraphicsCommandList> g_CommandList; // a command list we can record commands into, then execute them to render the frame
71 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
72 //as we have allocators (more if we want to know when the gpu is finished with an asset)
73 static HANDLE g_FenceEvent; // a handle to an event when our g_Fences is unlocked by the gpu
74 static UINT64 g_FenceValues[FRAME_BUFFER_COUNT]; // this value is incremented each frame. each g_Fences will have its own value
75 static UINT g_FrameIndex; // current rtv we are on
76 static UINT g_RtvDescriptorSize; // size of the rtv descriptor on the g_Device (all front and back buffers will be the same size)
77
78 static CComPtr<ID3D12PipelineState> g_PipelineStateObject;
79 static CComPtr<ID3D12RootSignature> g_RootSignature;
80 static CComPtr<ID3D12Resource> g_VertexBuffer;
81 static D3D12MA::Allocation* g_VertexBufferAllocation;
82 static CComPtr<ID3D12Resource> g_IndexBuffer;
83 static D3D12MA::Allocation* g_IndexBufferAllocation;
84 static D3D12_VERTEX_BUFFER_VIEW g_VertexBufferView;
85 static D3D12_INDEX_BUFFER_VIEW g_IndexBufferView;
86 static CComPtr<ID3D12Resource> g_DepthStencilBuffer;
87 static D3D12MA::Allocation* g_DepthStencilAllocation;
88 static CComPtr<ID3D12DescriptorHeap> g_DepthStencilDescriptorHeap;
89
90 struct Vertex {
91 vec3 pos;
92 vec2 texCoord;
93
VertexVertex94 Vertex() { }
VertexVertex95 Vertex(float x, float y, float z, float tx, float ty) :
96 pos(x, y, z),
97 texCoord(tx, ty)
98 {
99 }
100 };
101
102 struct ConstantBuffer0_PS
103 {
104 vec4 Color;
105 };
106 struct ConstantBuffer1_VS
107 {
108 mat4 WorldViewProj;
109 };
110
111 static const size_t ConstantBufferPerObjectAlignedSize = AlignUp<size_t>(sizeof(ConstantBuffer1_VS), 256);
112 static D3D12MA::Allocation* g_CbPerObjectUploadHeapAllocations[FRAME_BUFFER_COUNT];
113 static CComPtr<ID3D12Resource> g_CbPerObjectUploadHeaps[FRAME_BUFFER_COUNT];
114 static void* g_CbPerObjectAddress[FRAME_BUFFER_COUNT];
115 static uint32_t g_CubeIndexCount;
116
117 static CComPtr<ID3D12DescriptorHeap> g_MainDescriptorHeap[FRAME_BUFFER_COUNT];
118 static CComPtr<ID3D12Resource> g_ConstantBufferUploadHeap[FRAME_BUFFER_COUNT];
119 static D3D12MA::Allocation* g_ConstantBufferUploadAllocation[FRAME_BUFFER_COUNT];
120 static void* g_ConstantBufferAddress[FRAME_BUFFER_COUNT];
121
122 static CComPtr<ID3D12Resource> g_Texture;
123 static D3D12MA::Allocation* g_TextureAllocation;
124
125 static void* const CUSTOM_ALLOCATION_USER_DATA = (void*)(uintptr_t)0xDEADC0DE;
126
127 static std::atomic<size_t> g_CpuAllocationCount{0};
128
CustomAllocate(size_t Size,size_t Alignment,void * pUserData)129 static void* CustomAllocate(size_t Size, size_t Alignment, void* pUserData)
130 {
131 assert(pUserData == CUSTOM_ALLOCATION_USER_DATA);
132 void* memory = _aligned_malloc(Size, Alignment);
133 if(ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT)
134 {
135 wprintf(L"Allocate Size=%llu Alignment=%llu -> %p\n", Size, Alignment, memory);
136 }
137 ++g_CpuAllocationCount;
138 return memory;
139 }
140
CustomFree(void * pMemory,void * pUserData)141 static void CustomFree(void* pMemory, void* pUserData)
142 {
143 assert(pUserData == CUSTOM_ALLOCATION_USER_DATA);
144 if(pMemory)
145 {
146 --g_CpuAllocationCount;
147 if(ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT)
148 {
149 wprintf(L"Free %p\n", pMemory);
150 }
151 _aligned_free(pMemory);
152 }
153 }
154
SetDefaultRasterizerDesc(D3D12_RASTERIZER_DESC & outDesc)155 static void SetDefaultRasterizerDesc(D3D12_RASTERIZER_DESC& outDesc)
156 {
157 outDesc.FillMode = D3D12_FILL_MODE_SOLID;
158 outDesc.CullMode = D3D12_CULL_MODE_BACK;
159 outDesc.FrontCounterClockwise = FALSE;
160 outDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
161 outDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
162 outDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
163 outDesc.DepthClipEnable = TRUE;
164 outDesc.MultisampleEnable = FALSE;
165 outDesc.AntialiasedLineEnable = FALSE;
166 outDesc.ForcedSampleCount = 0;
167 outDesc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
168 }
169
SetDefaultBlendDesc(D3D12_BLEND_DESC & outDesc)170 static void SetDefaultBlendDesc(D3D12_BLEND_DESC& outDesc)
171 {
172 outDesc.AlphaToCoverageEnable = FALSE;
173 outDesc.IndependentBlendEnable = FALSE;
174 const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
175 FALSE,FALSE,
176 D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
177 D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
178 D3D12_LOGIC_OP_NOOP,
179 D3D12_COLOR_WRITE_ENABLE_ALL };
180 for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
181 outDesc.RenderTarget[i] = defaultRenderTargetBlendDesc;
182 }
183
SetDefaultDepthStencilDesc(D3D12_DEPTH_STENCIL_DESC & outDesc)184 static void SetDefaultDepthStencilDesc(D3D12_DEPTH_STENCIL_DESC& outDesc)
185 {
186 outDesc.DepthEnable = TRUE;
187 outDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
188 outDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
189 outDesc.StencilEnable = FALSE;
190 outDesc.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
191 outDesc.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
192 const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = {
193 D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS };
194 outDesc.FrontFace = defaultStencilOp;
195 outDesc.BackFace = defaultStencilOp;
196 }
197
WaitForFrame(size_t frameIndex)198 void WaitForFrame(size_t frameIndex) // wait until gpu is finished with command list
199 {
200 // if the current g_Fences value is still less than "g_FenceValues", then we know the GPU has not finished executing
201 // the command queue since it has not reached the "g_CommandQueue->Signal(g_Fences, g_FenceValues)" command
202 if (g_Fences[frameIndex]->GetCompletedValue() < g_FenceValues[frameIndex])
203 {
204 // we have the g_Fences create an event which is signaled once the g_Fences's current value is "g_FenceValues"
205 CHECK_HR( g_Fences[frameIndex]->SetEventOnCompletion(g_FenceValues[frameIndex], g_FenceEvent) );
206
207 // We will wait until the g_Fences has triggered the event that it's current value has reached "g_FenceValues". once it's value
208 // has reached "g_FenceValues", we know the command queue has finished executing
209 WaitForSingleObject(g_FenceEvent, INFINITE);
210 }
211 }
212
WaitGPUIdle(size_t frameIndex)213 void WaitGPUIdle(size_t frameIndex)
214 {
215 g_FenceValues[frameIndex]++;
216 CHECK_HR( g_CommandQueue->Signal(g_Fences[frameIndex], g_FenceValues[frameIndex]) );
217 WaitForFrame(frameIndex);
218 }
219
220 //*********************************************************
221 //
222 // Copyright (c) Microsoft. All rights reserved.
223 // This code is licensed under the MIT License (MIT).
224 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
225 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
226 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
227 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
228 //
229 //*********************************************************
230 // Row-by-row memcpy
MemcpySubresource(_In_ const D3D12_MEMCPY_DEST * pDest,_In_ const D3D12_SUBRESOURCE_DATA * pSrc,SIZE_T RowSizeInBytes,UINT NumRows,UINT NumSlices)231 inline void MemcpySubresource(
232 _In_ const D3D12_MEMCPY_DEST* pDest,
233 _In_ const D3D12_SUBRESOURCE_DATA* pSrc,
234 SIZE_T RowSizeInBytes,
235 UINT NumRows,
236 UINT NumSlices)
237 {
238 for (UINT z = 0; z < NumSlices; ++z)
239 {
240 BYTE* pDestSlice = reinterpret_cast<BYTE*>(pDest->pData) + pDest->SlicePitch * z;
241 const BYTE* pSrcSlice = reinterpret_cast<const BYTE*>(pSrc->pData) + pSrc->SlicePitch * z;
242 for (UINT y = 0; y < NumRows; ++y)
243 {
244 memcpy(pDestSlice + pDest->RowPitch * y,
245 pSrcSlice + pSrc->RowPitch * y,
246 RowSizeInBytes);
247 }
248 }
249 }
250
251 //*********************************************************
252 //
253 // Copyright (c) Microsoft. All rights reserved.
254 // This code is licensed under the MIT License (MIT).
255 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
256 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
257 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
258 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
259 //
260 //*********************************************************
261 inline UINT64 UpdateSubresources(
262 _In_ ID3D12GraphicsCommandList* pCmdList,
263 _In_ ID3D12Resource* pDestinationResource,
264 _In_ ID3D12Resource* pIntermediate,
265 _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
266 _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources,
267 UINT64 RequiredSize,
268 _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts,
269 _In_reads_(NumSubresources) const UINT* pNumRows,
270 _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes,
271 _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData)
272 {
273 // Minor validation
274 D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc();
275 D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc();
276 if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER ||
277 IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset ||
278 RequiredSize > (SIZE_T)-1 ||
279 (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER &&
280 (FirstSubresource != 0 || NumSubresources != 1)))
281 {
282 return 0;
283 }
284
285 BYTE* pData;
286 HRESULT hr = pIntermediate->Map(0, &EMPTY_RANGE, reinterpret_cast<void**>(&pData));
287 if (FAILED(hr))
288 {
289 return 0;
290 }
291
292 for (UINT i = 0; i < NumSubresources; ++i)
293 {
294 if (pRowSizesInBytes[i] > (SIZE_T)-1) return 0;
295 D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, pLayouts[i].Footprint.RowPitch * pNumRows[i] };
296 MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth);
297 }
298 pIntermediate->Unmap(0, NULL);
299
300 if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
301 {
302 D3D12_BOX SrcBox = {
303 UINT( pLayouts[0].Offset ), 0, 0,
304 UINT( pLayouts[0].Offset + pLayouts[0].Footprint.Width ), 0, 0 };
305 pCmdList->CopyBufferRegion(
306 pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width);
307 }
308 else
309 {
310 for (UINT i = 0; i < NumSubresources; ++i)
311 {
312 D3D12_TEXTURE_COPY_LOCATION Dst = {};
313 Dst.pResource = pDestinationResource;
314 Dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
315 Dst.SubresourceIndex = i + FirstSubresource;
316 D3D12_TEXTURE_COPY_LOCATION Src = {};
317 Src.pResource = pIntermediate;
318 Src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
319 Src.PlacedFootprint = pLayouts[i];
320 pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
321 }
322 }
323 return RequiredSize;
324 }
325
326 //*********************************************************
327 //
328 // Copyright (c) Microsoft. All rights reserved.
329 // This code is licensed under the MIT License (MIT).
330 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
331 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
332 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
333 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
334 //
335 //*********************************************************
336 inline UINT64 UpdateSubresources(
337 _In_ ID3D12GraphicsCommandList* pCmdList,
338 _In_ ID3D12Resource* pDestinationResource,
339 _In_ ID3D12Resource* pIntermediate,
340 UINT64 IntermediateOffset,
341 _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
342 _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources,
343 _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData)
344 {
345 UINT64 RequiredSize = 0;
346 UINT64 MemToAlloc = static_cast<UINT64>(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources;
347 if (MemToAlloc > SIZE_MAX)
348 {
349 return 0;
350 }
351 void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast<SIZE_T>(MemToAlloc));
352 if (pMem == NULL)
353 {
354 return 0;
355 }
356 D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts = reinterpret_cast<D3D12_PLACED_SUBRESOURCE_FOOTPRINT*>(pMem);
357 UINT64* pRowSizesInBytes = reinterpret_cast<UINT64*>(pLayouts + NumSubresources);
358 UINT* pNumRows = reinterpret_cast<UINT*>(pRowSizesInBytes + NumSubresources);
359
360 D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc();
361 ID3D12Device* pDevice;
362 pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
363 pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize);
364 pDevice->Release();
365
366 UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData);
367 HeapFree(GetProcessHeap(), 0, pMem);
368 return Result;
369 }
370
InitD3D()371 void InitD3D() // initializes direct3d 12
372 {
373 IDXGIFactory4* dxgiFactory;
374 CHECK_HR( CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) );
375
376 IDXGIAdapter1* adapter = nullptr; // adapters are the graphics card (this includes the embedded graphics on the motherboard)
377
378 int adapterIndex = 0; // we'll start looking for directx 12 compatible graphics devices starting at index 0
379
380 bool adapterFound = false; // set this to true when a good one was found
381
382 // find first hardware gpu that supports d3d 12
383 while (dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
384 {
385 DXGI_ADAPTER_DESC1 desc;
386 adapter->GetDesc1(&desc);
387
388 if ((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0)
389 {
390 HRESULT hr = D3D12CreateDevice(adapter, MY_D3D_FEATURE_LEVEL, _uuidof(ID3D12Device), nullptr);
391 if (SUCCEEDED(hr))
392 {
393 adapterFound = true;
394 break;
395 }
396 }
397 adapter->Release();
398 adapterIndex++;
399 }
400 assert(adapterFound);
401
402 // Must be done before D3D12 device is created.
403 if(ENABLE_DEBUG_LAYER)
404 {
405 CComPtr<ID3D12Debug> debug;
406 if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug))))
407 debug->EnableDebugLayer();
408 }
409
410 // Create the g_Device
411 ID3D12Device* device = nullptr;
412 CHECK_HR( D3D12CreateDevice(
413 adapter,
414 MY_D3D_FEATURE_LEVEL,
415 IID_PPV_ARGS(&device)) );
416 g_Device.Attach(device);
417
418 // Create allocator
419
420 {
421 D3D12MA::ALLOCATOR_DESC desc = {};
422 desc.Flags = g_AllocatorFlags;
423 desc.pDevice = device;
424 desc.pAdapter = adapter;
425
426 if(ENABLE_CPU_ALLOCATION_CALLBACKS)
427 {
428 g_AllocationCallbacks.pAllocate = &CustomAllocate;
429 g_AllocationCallbacks.pFree = &CustomFree;
430 g_AllocationCallbacks.pUserData = CUSTOM_ALLOCATION_USER_DATA;
431 desc.pAllocationCallbacks = &g_AllocationCallbacks;
432 }
433
434 CHECK_HR( D3D12MA::CreateAllocator(&desc, &g_Allocator) );
435
436 switch(g_Allocator->GetD3D12Options().ResourceHeapTier)
437 {
438 case D3D12_RESOURCE_HEAP_TIER_1:
439 wprintf(L"ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1\n");
440 break;
441 case D3D12_RESOURCE_HEAP_TIER_2:
442 wprintf(L"ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_2\n");
443 break;
444 default:
445 assert(0);
446 }
447 }
448
449 // -- Create the Command Queue -- //
450
451 D3D12_COMMAND_QUEUE_DESC cqDesc = {}; // we will be using all the default values
452
453 ID3D12CommandQueue* commandQueue = nullptr;
454 CHECK_HR( g_Device->CreateCommandQueue(&cqDesc, IID_PPV_ARGS(&commandQueue)) ); // create the command queue
455 g_CommandQueue.Attach(commandQueue);
456
457 // -- Create the Swap Chain (double/tripple buffering) -- //
458
459 DXGI_MODE_DESC backBufferDesc = {}; // this is to describe our display mode
460 backBufferDesc.Width = SIZE_X; // buffer width
461 backBufferDesc.Height = SIZE_Y; // buffer height
462 backBufferDesc.Format = RENDER_TARGET_FORMAT; // format of the buffer (rgba 32 bits, 8 bits for each chanel)
463
464 // describe our multi-sampling. We are not multi-sampling, so we set the count to 1 (we need at least one sample of course)
465 DXGI_SAMPLE_DESC sampleDesc = {};
466 sampleDesc.Count = 1; // multisample count (no multisampling, so we just put 1, since we still need 1 sample)
467
468 // Describe and create the swap chain.
469 DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
470 swapChainDesc.BufferCount = FRAME_BUFFER_COUNT; // number of buffers we have
471 swapChainDesc.BufferDesc = backBufferDesc; // our back buffer description
472 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // this says the pipeline will render to this swap chain
473 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // dxgi will discard the buffer (data) after we call present
474 swapChainDesc.OutputWindow = g_Wnd; // handle to our window
475 swapChainDesc.SampleDesc = sampleDesc; // our multi-sampling description
476 swapChainDesc.Windowed = !FULLSCREEN; // set to true, then if in fullscreen must call SetFullScreenState with true for full screen to get uncapped fps
477
478 IDXGISwapChain* tempSwapChain;
479
480 CHECK_HR( dxgiFactory->CreateSwapChain(
481 g_CommandQueue, // the queue will be flushed once the swap chain is created
482 &swapChainDesc, // give it the swap chain description we created above
483 &tempSwapChain // store the created swap chain in a temp IDXGISwapChain interface
484 ) );
485
486 g_SwapChain.Attach(static_cast<IDXGISwapChain3*>(tempSwapChain));
487
488 g_FrameIndex = g_SwapChain->GetCurrentBackBufferIndex();
489
490 // -- Create the Back Buffers (render target views) Descriptor Heap -- //
491
492 // describe an rtv descriptor heap and create
493 D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
494 rtvHeapDesc.NumDescriptors = FRAME_BUFFER_COUNT; // number of descriptors for this heap.
495 rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // this heap is a render target view heap
496
497 // This heap will not be directly referenced by the shaders (not shader visible), as this will store the output from the pipeline
498 // otherwise we would set the heap's flag to D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE
499 rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
500 ID3D12DescriptorHeap* rtvDescriptorHeap = nullptr;
501 CHECK_HR( g_Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvDescriptorHeap)) );
502 g_RtvDescriptorHeap.Attach(rtvDescriptorHeap);
503
504 // get the size of a descriptor in this heap (this is a rtv heap, so only rtv descriptors should be stored in it.
505 // descriptor sizes may vary from g_Device to g_Device, which is why there is no set size and we must ask the
506 // g_Device to give us the size. we will use this size to increment a descriptor handle offset
507 g_RtvDescriptorSize = g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
508
509 // get a handle to the first descriptor in the descriptor heap. a handle is basically a pointer,
510 // but we cannot literally use it like a c++ pointer.
511 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle { g_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart() };
512
513 // Create a RTV for each buffer (double buffering is two buffers, tripple buffering is 3).
514 for (int i = 0; i < FRAME_BUFFER_COUNT; i++)
515 {
516 // first we get the n'th buffer in the swap chain and store it in the n'th
517 // position of our ID3D12Resource array
518 ID3D12Resource* res = nullptr;
519 CHECK_HR( g_SwapChain->GetBuffer(i, IID_PPV_ARGS(&res)) );
520 g_RenderTargets[i].Attach(res);
521
522 // the we "create" a render target view which binds the swap chain buffer (ID3D12Resource[n]) to the rtv handle
523 g_Device->CreateRenderTargetView(g_RenderTargets[i], nullptr, rtvHandle);
524
525 // we increment the rtv handle by the rtv descriptor size we got above
526 rtvHandle.ptr += g_RtvDescriptorSize;
527 }
528
529 // -- Create the Command Allocators -- //
530
531 for (int i = 0; i < FRAME_BUFFER_COUNT; i++)
532 {
533 ID3D12CommandAllocator* commandAllocator = nullptr;
534 CHECK_HR( g_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)) );
535 g_CommandAllocators[i].Attach(commandAllocator);
536 }
537
538 // create the command list with the first allocator
539 CHECK_HR( g_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_CommandAllocators[0], NULL, IID_PPV_ARGS(&g_CommandList)) );
540
541 // command lists are created in the recording state. our main loop will set it up for recording again so close it now
542 g_CommandList->Close();
543
544 // create a depth stencil descriptor heap so we can get a pointer to the depth stencil buffer
545 D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
546 dsvHeapDesc.NumDescriptors = 1;
547 dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
548 dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
549 CHECK_HR( g_Device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&g_DepthStencilDescriptorHeap)) );
550
551 D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
552 depthOptimizedClearValue.Format = DEPTH_STENCIL_FORMAT;
553 depthOptimizedClearValue.DepthStencil.Depth = 1.0f;
554 depthOptimizedClearValue.DepthStencil.Stencil = 0;
555
556 D3D12MA::ALLOCATION_DESC depthStencilAllocDesc = {};
557 depthStencilAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
558 D3D12_RESOURCE_DESC depthStencilResourceDesc = {};
559 depthStencilResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
560 depthStencilResourceDesc.Alignment = 0;
561 depthStencilResourceDesc.Width = SIZE_X;
562 depthStencilResourceDesc.Height = SIZE_Y;
563 depthStencilResourceDesc.DepthOrArraySize = 1;
564 depthStencilResourceDesc.MipLevels = 1;
565 depthStencilResourceDesc.Format = DEPTH_STENCIL_FORMAT;
566 depthStencilResourceDesc.SampleDesc.Count = 1;
567 depthStencilResourceDesc.SampleDesc.Quality = 0;
568 depthStencilResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
569 depthStencilResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
570 CHECK_HR( g_Allocator->CreateResource(
571 &depthStencilAllocDesc,
572 &depthStencilResourceDesc,
573 D3D12_RESOURCE_STATE_DEPTH_WRITE,
574 &depthOptimizedClearValue,
575 &g_DepthStencilAllocation,
576 IID_PPV_ARGS(&g_DepthStencilBuffer)
577 ) );
578 CHECK_HR( g_DepthStencilBuffer->SetName(L"Depth/Stencil Resource Heap") );
579
580 D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
581 depthStencilDesc.Format = DEPTH_STENCIL_FORMAT;
582 depthStencilDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
583 depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE;
584 g_Device->CreateDepthStencilView(g_DepthStencilBuffer, &depthStencilDesc, g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
585
586 // -- Create a Fence & Fence Event -- //
587
588 // create the fences
589 for (int i = 0; i < FRAME_BUFFER_COUNT; i++)
590 {
591 ID3D12Fence* fence = nullptr;
592 CHECK_HR( g_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)) );
593 g_Fences[i].Attach(fence);
594 g_FenceValues[i] = 0; // set the initial g_Fences value to 0
595 }
596
597 // create a handle to a g_Fences event
598 g_FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
599 assert(g_FenceEvent);
600
601 D3D12_DESCRIPTOR_RANGE cbDescriptorRange;
602 cbDescriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
603 cbDescriptorRange.NumDescriptors = 1;
604 cbDescriptorRange.BaseShaderRegister = 0;
605 cbDescriptorRange.RegisterSpace = 0;
606 cbDescriptorRange.OffsetInDescriptorsFromTableStart = 0;
607
608 D3D12_DESCRIPTOR_RANGE textureDescRange;
609 textureDescRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
610 textureDescRange.NumDescriptors = 1;
611 textureDescRange.BaseShaderRegister = 0;
612 textureDescRange.RegisterSpace = 0;
613 textureDescRange.OffsetInDescriptorsFromTableStart = 1;
614
615 D3D12_ROOT_PARAMETER rootParameters[3];
616
617 rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
618 rootParameters[0].DescriptorTable = {1, &cbDescriptorRange};
619 rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
620
621 rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
622 rootParameters[1].Descriptor = {1, 0};
623 rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
624
625 rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
626 rootParameters[2].DescriptorTable = {1, &textureDescRange};
627 rootParameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
628
629 // create root signature
630
631 // create a static sampler
632 D3D12_STATIC_SAMPLER_DESC sampler = {};
633 sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
634 sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
635 sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
636 sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
637 sampler.MipLODBias = 0;
638 sampler.MaxAnisotropy = 0;
639 sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
640 sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
641 sampler.MinLOD = 0.0f;
642 sampler.MaxLOD = D3D12_FLOAT32_MAX;
643 sampler.ShaderRegister = 0;
644 sampler.RegisterSpace = 0;
645 sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
646
647 D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
648 rootSignatureDesc.NumParameters = _countof(rootParameters);
649 rootSignatureDesc.pParameters = rootParameters;
650 rootSignatureDesc.NumStaticSamplers = 1;
651 rootSignatureDesc.pStaticSamplers = &sampler;
652 rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
653 D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
654 D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
655 D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
656
657 CComPtr<ID3DBlob> signatureBlob;
658 ID3DBlob* signatureBlobPtr;
659 CHECK_HR( D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signatureBlobPtr, nullptr) );
660 signatureBlob.Attach(signatureBlobPtr);
661
662 ID3D12RootSignature* rootSignature = nullptr;
663 CHECK_HR( device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&rootSignature)) );
664 g_RootSignature.Attach(rootSignature);
665
666 for (int i = 0; i < FRAME_BUFFER_COUNT; ++i)
667 {
668 D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
669 heapDesc.NumDescriptors = 2;
670 heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
671 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
672 CHECK_HR( g_Device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&g_MainDescriptorHeap[i])) );
673 }
674
675 // # CONSTANT BUFFER
676
677 for (int i = 0; i < FRAME_BUFFER_COUNT; ++i)
678 {
679 D3D12MA::ALLOCATION_DESC constantBufferUploadAllocDesc = {};
680 constantBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
681 D3D12_RESOURCE_DESC constantBufferResourceDesc = {};
682 constantBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
683 constantBufferResourceDesc.Alignment = 0;
684 constantBufferResourceDesc.Width = 1024 * 64;
685 constantBufferResourceDesc.Height = 1;
686 constantBufferResourceDesc.DepthOrArraySize = 1;
687 constantBufferResourceDesc.MipLevels = 1;
688 constantBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
689 constantBufferResourceDesc.SampleDesc.Count = 1;
690 constantBufferResourceDesc.SampleDesc.Quality = 0;
691 constantBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
692 constantBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
693 CHECK_HR( g_Allocator->CreateResource(
694 &constantBufferUploadAllocDesc,
695 &constantBufferResourceDesc,
696 D3D12_RESOURCE_STATE_GENERIC_READ,
697 nullptr,
698 &g_ConstantBufferUploadAllocation[i],
699 IID_PPV_ARGS(&g_ConstantBufferUploadHeap[i])) );
700 g_ConstantBufferUploadHeap[i]->SetName(L"Constant Buffer Upload Resource Heap");
701
702 D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
703 cbvDesc.BufferLocation = g_ConstantBufferUploadHeap[i]->GetGPUVirtualAddress();
704 cbvDesc.SizeInBytes = AlignUp<UINT>(sizeof(ConstantBuffer0_PS), 256);
705 g_Device->CreateConstantBufferView(&cbvDesc, g_MainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart());
706
707 CHECK_HR( g_ConstantBufferUploadHeap[i]->Map(0, &EMPTY_RANGE, &g_ConstantBufferAddress[i]) );
708 }
709
710 // create input layout
711
712 // The input layout is used by the Input Assembler so that it knows
713 // how to read the vertex data bound to it.
714
715 const D3D12_INPUT_ELEMENT_DESC inputLayout[] =
716 {
717 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
718 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
719 };
720
721 // create a pipeline state object (PSO)
722
723 // In a real application, you will have many pso's. for each different shader
724 // or different combinations of shaders, different blend states or different rasterizer states,
725 // different topology types (point, line, triangle, patch), or a different number
726 // of render targets you will need a pso
727
728 // VS is the only required shader for a pso. You might be wondering when a case would be where
729 // you only set the VS. It's possible that you have a pso that only outputs data with the stream
730 // output, and not on a render target, which means you would not need anything after the stream
731 // output.
732
733 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; // a structure to define a pso
734 psoDesc.InputLayout.NumElements = _countof(inputLayout);
735 psoDesc.InputLayout.pInputElementDescs = inputLayout;
736 psoDesc.pRootSignature = g_RootSignature; // the root signature that describes the input data this pso needs
737 psoDesc.VS.BytecodeLength = sizeof(VS::g_main);
738 psoDesc.VS.pShaderBytecode = VS::g_main;
739 psoDesc.PS.BytecodeLength = sizeof(PS::g_main);
740 psoDesc.PS.pShaderBytecode = PS::g_main;
741 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // type of topology we are drawing
742 psoDesc.RTVFormats[0] = RENDER_TARGET_FORMAT; // format of the render target
743 psoDesc.DSVFormat = DEPTH_STENCIL_FORMAT;
744 psoDesc.SampleDesc = sampleDesc; // must be the same sample description as the swapchain and depth/stencil buffer
745 psoDesc.SampleMask = 0xffffffff; // sample mask has to do with multi-sampling. 0xffffffff means point sampling is done
746 SetDefaultRasterizerDesc(psoDesc.RasterizerState);
747 SetDefaultBlendDesc(psoDesc.BlendState);
748 psoDesc.NumRenderTargets = 1; // we are only binding one render target
749 SetDefaultDepthStencilDesc(psoDesc.DepthStencilState);
750
751 // create the pso
752 ID3D12PipelineState* pipelineStateObject;
753 CHECK_HR( device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineStateObject)) );
754 g_PipelineStateObject.Attach(pipelineStateObject);
755
756 // Create vertex buffer
757
758 // a triangle
759 Vertex vList[] = {
760 // front face
761 { -0.5f, 0.5f, -0.5f, 0.f, 0.f },
762 { 0.5f, -0.5f, -0.5f, 1.f, 1.f },
763 { -0.5f, -0.5f, -0.5f, 0.f, 1.f },
764 { 0.5f, 0.5f, -0.5f, 1.f, 0.f },
765
766 // right side face
767 { 0.5f, -0.5f, -0.5f, 0.f, 1.f },
768 { 0.5f, 0.5f, 0.5f, 1.f, 0.f },
769 { 0.5f, -0.5f, 0.5f, 1.f, 1.f },
770 { 0.5f, 0.5f, -0.5f, 0.f, 0.f },
771
772 // left side face
773 { -0.5f, 0.5f, 0.5f, 0.f, 0.f },
774 { -0.5f, -0.5f, -0.5f, 1.f, 1.f },
775 { -0.5f, -0.5f, 0.5f, 0.f, 1.f },
776 { -0.5f, 0.5f, -0.5f, 1.f, 0.f },
777
778 // back face
779 { 0.5f, 0.5f, 0.5f, 0.f, 0.f },
780 { -0.5f, -0.5f, 0.5f, 1.f, 1.f },
781 { 0.5f, -0.5f, 0.5f, 0.f, 1.f },
782 { -0.5f, 0.5f, 0.5f, 1.f, 0.f },
783
784 // top face
785 { -0.5f, 0.5f, -0.5f, 0.f, 0.f },
786 { 0.5f, 0.5f, 0.5f, 1.f, 1.f },
787 { 0.5f, 0.5f, -0.5f, 0.f, 1.f },
788 { -0.5f, 0.5f, 0.5f, 1.f, 0.f },
789
790 // bottom face
791 { 0.5f, -0.5f, 0.5f, 0.f, 0.f },
792 { -0.5f, -0.5f, -0.5f, 1.f, 1.f },
793 { 0.5f, -0.5f, -0.5f, 0.f, 1.f },
794 { -0.5f, -0.5f, 0.5f, 1.f, 0.f },
795 };
796 const uint32_t vBufferSize = sizeof(vList);
797
798 // create default heap
799 // default heap is memory on the GPU. Only the GPU has access to this memory
800 // To get data into this heap, we will have to upload the data using
801 // an upload heap
802 D3D12MA::ALLOCATION_DESC vertexBufferAllocDesc = {};
803 vertexBufferAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
804 D3D12_RESOURCE_DESC vertexBufferResourceDesc = {};
805 vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
806 vertexBufferResourceDesc.Alignment = 0;
807 vertexBufferResourceDesc.Width = vBufferSize;
808 vertexBufferResourceDesc.Height = 1;
809 vertexBufferResourceDesc.DepthOrArraySize = 1;
810 vertexBufferResourceDesc.MipLevels = 1;
811 vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
812 vertexBufferResourceDesc.SampleDesc.Count = 1;
813 vertexBufferResourceDesc.SampleDesc.Quality = 0;
814 vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
815 vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
816 ID3D12Resource* vertexBufferPtr;
817 CHECK_HR( g_Allocator->CreateResource(
818 &vertexBufferAllocDesc,
819 &vertexBufferResourceDesc, // resource description for a buffer
820 D3D12_RESOURCE_STATE_COPY_DEST, // we will start this heap in the copy destination state since we will copy data
821 // from the upload heap to this heap
822 nullptr, // optimized clear value must be null for this type of resource. used for render targets and depth/stencil buffers
823 &g_VertexBufferAllocation,
824 IID_PPV_ARGS(&vertexBufferPtr)) );
825 g_VertexBuffer.Attach(vertexBufferPtr);
826
827 // we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at
828 g_VertexBuffer->SetName(L"Vertex Buffer Resource Heap");
829
830 // create upload heap
831 // upload heaps are used to upload data to the GPU. CPU can write to it, GPU can read from it
832 // We will upload the vertex buffer using this heap to the default heap
833 D3D12MA::ALLOCATION_DESC vBufferUploadAllocDesc = {};
834 vBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
835 D3D12_RESOURCE_DESC vertexBufferUploadResourceDesc = {};
836 vertexBufferUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
837 vertexBufferUploadResourceDesc.Alignment = 0;
838 vertexBufferUploadResourceDesc.Width = vBufferSize;
839 vertexBufferUploadResourceDesc.Height = 1;
840 vertexBufferUploadResourceDesc.DepthOrArraySize = 1;
841 vertexBufferUploadResourceDesc.MipLevels = 1;
842 vertexBufferUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
843 vertexBufferUploadResourceDesc.SampleDesc.Count = 1;
844 vertexBufferUploadResourceDesc.SampleDesc.Quality = 0;
845 vertexBufferUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
846 vertexBufferUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
847 CComPtr<ID3D12Resource> vBufferUploadHeap;
848 D3D12MA::Allocation* vBufferUploadHeapAllocation = nullptr;
849 CHECK_HR( g_Allocator->CreateResource(
850 &vBufferUploadAllocDesc,
851 &vertexBufferUploadResourceDesc, // resource description for a buffer
852 D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap
853 nullptr,
854 &vBufferUploadHeapAllocation,
855 IID_PPV_ARGS(&vBufferUploadHeap)) );
856 vBufferUploadHeap->SetName(L"Vertex Buffer Upload Resource Heap");
857
858 // store vertex buffer in upload heap
859 D3D12_SUBRESOURCE_DATA vertexData = {};
860 vertexData.pData = reinterpret_cast<BYTE*>(vList); // pointer to our vertex array
861 vertexData.RowPitch = vBufferSize; // size of all our triangle vertex data
862 vertexData.SlicePitch = vBufferSize; // also the size of our triangle vertex data
863
864 CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex], NULL) );
865
866 // we are now creating a command with the command list to copy the data from
867 // the upload heap to the default heap
868 UINT64 r = UpdateSubresources(g_CommandList, g_VertexBuffer, vBufferUploadHeap, 0, 0, 1, &vertexData);
869 assert(r);
870
871 // transition the vertex buffer data from copy destination state to vertex buffer state
872 D3D12_RESOURCE_BARRIER vbBarrier = {};
873 vbBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
874 vbBarrier.Transition.pResource = g_VertexBuffer;
875 vbBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
876 vbBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
877 vbBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
878 g_CommandList->ResourceBarrier(1, &vbBarrier);
879
880 // Create index buffer
881
882 // a quad (2 triangles)
883 uint16_t iList[] = {
884 // ffront face
885 0, 1, 2, // first triangle
886 0, 3, 1, // second triangle
887
888 // left face
889 4, 5, 6, // first triangle
890 4, 7, 5, // second triangle
891
892 // right face
893 8, 9, 10, // first triangle
894 8, 11, 9, // second triangle
895
896 // back face
897 12, 13, 14, // first triangle
898 12, 15, 13, // second triangle
899
900 // top face
901 16, 17, 18, // first triangle
902 16, 19, 17, // second triangle
903
904 // bottom face
905 20, 21, 22, // first triangle
906 20, 23, 21, // second triangle
907 };
908
909 g_CubeIndexCount = (uint32_t)_countof(iList);
910
911 size_t iBufferSize = sizeof(iList);
912
913 // create default heap to hold index buffer
914 D3D12MA::ALLOCATION_DESC indexBufferAllocDesc = {};
915 indexBufferAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
916 D3D12_RESOURCE_DESC indexBufferResourceDesc = {};
917 indexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
918 indexBufferResourceDesc.Alignment = 0;
919 indexBufferResourceDesc.Width = iBufferSize;
920 indexBufferResourceDesc.Height = 1;
921 indexBufferResourceDesc.DepthOrArraySize = 1;
922 indexBufferResourceDesc.MipLevels = 1;
923 indexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
924 indexBufferResourceDesc.SampleDesc.Count = 1;
925 indexBufferResourceDesc.SampleDesc.Quality = 0;
926 indexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
927 indexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
928 CHECK_HR( g_Allocator->CreateResource(
929 &indexBufferAllocDesc,
930 &indexBufferResourceDesc, // resource description for a buffer
931 D3D12_RESOURCE_STATE_COPY_DEST, // start in the copy destination state
932 nullptr, // optimized clear value must be null for this type of resource
933 &g_IndexBufferAllocation,
934 IID_PPV_ARGS(&g_IndexBuffer)) );
935
936 // we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at
937 g_IndexBuffer->SetName(L"Index Buffer Resource Heap");
938
939 // create upload heap to upload index buffer
940 D3D12MA::ALLOCATION_DESC iBufferUploadAllocDesc = {};
941 iBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
942 D3D12_RESOURCE_DESC indexBufferUploadResourceDesc = {};
943 indexBufferUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
944 indexBufferUploadResourceDesc.Alignment = 0;
945 indexBufferUploadResourceDesc.Width = iBufferSize;
946 indexBufferUploadResourceDesc.Height = 1;
947 indexBufferUploadResourceDesc.DepthOrArraySize = 1;
948 indexBufferUploadResourceDesc.MipLevels = 1;
949 indexBufferUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
950 indexBufferUploadResourceDesc.SampleDesc.Count = 1;
951 indexBufferUploadResourceDesc.SampleDesc.Quality = 0;
952 indexBufferUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
953 indexBufferUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
954 CComPtr<ID3D12Resource> iBufferUploadHeap;
955 D3D12MA::Allocation* iBufferUploadHeapAllocation = nullptr;
956 CHECK_HR( g_Allocator->CreateResource(
957 &iBufferUploadAllocDesc,
958 &indexBufferUploadResourceDesc, // resource description for a buffer
959 D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap
960 nullptr,
961 &iBufferUploadHeapAllocation,
962 IID_PPV_ARGS(&iBufferUploadHeap)) );
963 CHECK_HR( iBufferUploadHeap->SetName(L"Index Buffer Upload Resource Heap") );
964
965 // store vertex buffer in upload heap
966 D3D12_SUBRESOURCE_DATA indexData = {};
967 indexData.pData = iList; // pointer to our index array
968 indexData.RowPitch = iBufferSize; // size of all our index buffer
969 indexData.SlicePitch = iBufferSize; // also the size of our index buffer
970
971 // we are now creating a command with the command list to copy the data from
972 // the upload heap to the default heap
973 r = UpdateSubresources(g_CommandList, g_IndexBuffer, iBufferUploadHeap, 0, 0, 1, &indexData);
974 assert(r);
975
976 // transition the index buffer data from copy destination state to vertex buffer state
977 D3D12_RESOURCE_BARRIER ibBarrier = {};
978 ibBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
979 ibBarrier.Transition.pResource = g_IndexBuffer;
980 ibBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
981 ibBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER;
982 ibBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
983 g_CommandList->ResourceBarrier(1, &ibBarrier);
984
985 // create a vertex buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method
986 g_VertexBufferView.BufferLocation = g_VertexBuffer->GetGPUVirtualAddress();
987 g_VertexBufferView.StrideInBytes = sizeof(Vertex);
988 g_VertexBufferView.SizeInBytes = vBufferSize;
989
990 // create a index buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method
991 g_IndexBufferView.BufferLocation = g_IndexBuffer->GetGPUVirtualAddress();
992 g_IndexBufferView.Format = DXGI_FORMAT_R16_UINT;
993 g_IndexBufferView.SizeInBytes = (UINT)iBufferSize;
994
995 D3D12MA::ALLOCATION_DESC cbPerObjectUploadAllocDesc = {};
996 cbPerObjectUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
997 D3D12_RESOURCE_DESC cbPerObjectUploadResourceDesc = {};
998 cbPerObjectUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
999 cbPerObjectUploadResourceDesc.Alignment = 0;
1000 cbPerObjectUploadResourceDesc.Width = 1024 * 64;
1001 cbPerObjectUploadResourceDesc.Height = 1;
1002 cbPerObjectUploadResourceDesc.DepthOrArraySize = 1;
1003 cbPerObjectUploadResourceDesc.MipLevels = 1;
1004 cbPerObjectUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
1005 cbPerObjectUploadResourceDesc.SampleDesc.Count = 1;
1006 cbPerObjectUploadResourceDesc.SampleDesc.Quality = 0;
1007 cbPerObjectUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1008 cbPerObjectUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
1009 for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i)
1010 {
1011 // create resource for cube 1
1012 CHECK_HR( g_Allocator->CreateResource(
1013 &cbPerObjectUploadAllocDesc,
1014 &cbPerObjectUploadResourceDesc, // size of the resource heap. Must be a multiple of 64KB for single-textures and constant buffers
1015 D3D12_RESOURCE_STATE_GENERIC_READ, // will be data that is read from so we keep it in the generic read state
1016 nullptr, // we do not have use an optimized clear value for constant buffers
1017 &g_CbPerObjectUploadHeapAllocations[i],
1018 IID_PPV_ARGS(&g_CbPerObjectUploadHeaps[i])) );
1019 g_CbPerObjectUploadHeaps[i]->SetName(L"Constant Buffer Upload Resource Heap");
1020
1021 CHECK_HR( g_CbPerObjectUploadHeaps[i]->Map(0, &EMPTY_RANGE, &g_CbPerObjectAddress[i]) );
1022 }
1023
1024 // # TEXTURE
1025
1026 D3D12_RESOURCE_DESC textureDesc;
1027 size_t imageBytesPerRow;
1028 size_t imageSize = SIZE_MAX;
1029 std::vector<char> imageData;
1030 {
1031 const UINT sizeX = 256;
1032 const UINT sizeY = 256;
1033 const DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
1034 const UINT bytesPerPixel = 4;
1035
1036 imageBytesPerRow = sizeX * bytesPerPixel;
1037 imageSize = sizeY * imageBytesPerRow;
1038
1039 imageData.resize(imageSize);
1040 char* rowPtr = (char*)imageData.data();
1041 for(UINT y = 0; y < sizeY; ++y)
1042 {
1043 char* pixelPtr = rowPtr;
1044 for(UINT x = 0; x < sizeX; ++x)
1045 {
1046 *(UINT8*)(pixelPtr ) = (UINT8)x; // R
1047 *(UINT8*)(pixelPtr + 1) = (UINT8)y; // G
1048 *(UINT8*)(pixelPtr + 2) = 0x00; // B
1049 *(UINT8*)(pixelPtr + 3) = 0xFF; // A
1050
1051 *(UINT8*)(pixelPtr ) = x > 128 ? 0xFF : 00;
1052 *(UINT8*)(pixelPtr + 1) = y > 128 ? 0xFF : 00;
1053 pixelPtr += bytesPerPixel;
1054 }
1055 rowPtr += imageBytesPerRow;
1056 }
1057
1058 textureDesc = {};
1059 textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
1060 textureDesc.Alignment = 0;
1061 textureDesc.Width = sizeX;
1062 textureDesc.Height = sizeY;
1063 textureDesc.DepthOrArraySize = 1;
1064 textureDesc.MipLevels = 1;
1065 textureDesc.Format = format;
1066 textureDesc.SampleDesc.Count = 1;
1067 textureDesc.SampleDesc.Quality = 0;
1068 textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
1069 textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
1070 }
1071
1072 D3D12MA::ALLOCATION_DESC textureAllocDesc = {};
1073 textureAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
1074 CHECK_HR( g_Allocator->CreateResource(
1075 &textureAllocDesc,
1076 &textureDesc,
1077 D3D12_RESOURCE_STATE_COPY_DEST,
1078 nullptr, // pOptimizedClearValue
1079 &g_TextureAllocation,
1080 IID_PPV_ARGS(&g_Texture)) );
1081 g_Texture->SetName(L"g_Texture");
1082
1083 UINT64 textureUploadBufferSize;
1084 device->GetCopyableFootprints(
1085 &textureDesc,
1086 0, // FirstSubresource
1087 1, // NumSubresources
1088 0, // BaseOffset
1089 nullptr, // pLayouts
1090 nullptr, // pNumRows
1091 nullptr, // pRowSizeInBytes
1092 &textureUploadBufferSize); // pTotalBytes
1093
1094 D3D12MA::ALLOCATION_DESC textureUploadAllocDesc = {};
1095 textureUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
1096 D3D12_RESOURCE_DESC textureUploadResourceDesc = {};
1097 textureUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
1098 textureUploadResourceDesc.Alignment = 0;
1099 textureUploadResourceDesc.Width = textureUploadBufferSize;
1100 textureUploadResourceDesc.Height = 1;
1101 textureUploadResourceDesc.DepthOrArraySize = 1;
1102 textureUploadResourceDesc.MipLevels = 1;
1103 textureUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
1104 textureUploadResourceDesc.SampleDesc.Count = 1;
1105 textureUploadResourceDesc.SampleDesc.Quality = 0;
1106 textureUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1107 textureUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
1108 CComPtr<ID3D12Resource> textureUpload;
1109 D3D12MA::Allocation* textureUploadAllocation;
1110 CHECK_HR( g_Allocator->CreateResource(
1111 &textureUploadAllocDesc,
1112 &textureUploadResourceDesc,
1113 D3D12_RESOURCE_STATE_GENERIC_READ,
1114 nullptr, // pOptimizedClearValue
1115 &textureUploadAllocation,
1116 IID_PPV_ARGS(&textureUpload)) );
1117 textureUpload->SetName(L"textureUpload");
1118
1119 D3D12_SUBRESOURCE_DATA textureSubresourceData = {};
1120 textureSubresourceData.pData = imageData.data();
1121 textureSubresourceData.RowPitch = imageBytesPerRow;
1122 textureSubresourceData.SlicePitch = imageBytesPerRow * textureDesc.Height;
1123
1124 UpdateSubresources(g_CommandList, g_Texture, textureUpload, 0, 0, 1, &textureSubresourceData);
1125
1126 D3D12_RESOURCE_BARRIER textureBarrier = {};
1127 textureBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1128 textureBarrier.Transition.pResource = g_Texture;
1129 textureBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
1130 textureBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
1131 textureBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1132 g_CommandList->ResourceBarrier(1, &textureBarrier);
1133
1134 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
1135 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
1136 srvDesc.Format = textureDesc.Format;
1137 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
1138 srvDesc.Texture2D.MipLevels = 1;
1139 for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i)
1140 {
1141 D3D12_CPU_DESCRIPTOR_HANDLE descHandle = {
1142 g_MainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart().ptr +
1143 g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)};
1144 g_Device->CreateShaderResourceView(g_Texture, &srvDesc, descHandle);
1145 }
1146
1147 // # END OF INITIAL COMMAND LIST
1148
1149 // Now we execute the command list to upload the initial assets (triangle data)
1150 g_CommandList->Close();
1151 ID3D12CommandList* ppCommandLists[] = { g_CommandList };
1152 commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
1153
1154 // increment the fence value now, otherwise the buffer might not be uploaded by the time we start drawing
1155 WaitGPUIdle(g_FrameIndex);
1156
1157 textureUploadAllocation->Release();
1158 iBufferUploadHeapAllocation->Release();
1159 vBufferUploadHeapAllocation->Release();
1160 }
1161
Update()1162 void Update()
1163 {
1164 {
1165 const float f = sin(g_Time * (PI * 2.f)) * 0.5f + 0.5f;
1166 ConstantBuffer0_PS cb;
1167 cb.Color = vec4(f, f, f, 1.f);
1168 memcpy(g_ConstantBufferAddress[g_FrameIndex], &cb, sizeof(cb));
1169 }
1170
1171 {
1172 const mat4 projection = mat4::Perspective(
1173 45.f * (PI / 180.f), // fovY
1174 (float)SIZE_X / (float)SIZE_Y, // aspectRatio
1175 0.1f, // zNear
1176 1000.0f); // zFar
1177 const mat4 view = mat4::LookAt(
1178 vec3(0.f, 0.f, 0.f), // at
1179 vec3(-.4f, 1.7f, -3.5f), // eye
1180 vec3(0.f, 1.f, 0.f)); // up
1181 const mat4 viewProjection = view * projection;
1182
1183 mat4 cube1World = mat4::RotationZ(g_Time);
1184
1185 ConstantBuffer1_VS cb;
1186 mat4 worldViewProjection = cube1World * viewProjection;
1187 cb.WorldViewProj = worldViewProjection.Transposed();
1188 memcpy(g_CbPerObjectAddress[g_FrameIndex], &cb, sizeof(cb));
1189
1190 mat4 cube2World = mat4::Scaling(0.5f) *
1191 mat4::RotationX(g_Time * 2.0f) *
1192 mat4::Translation(vec3(-1.2f, 0.f, 0.f)) *
1193 cube1World;
1194
1195 worldViewProjection = cube2World * viewProjection;
1196 cb.WorldViewProj = worldViewProjection.Transposed();
1197 memcpy((char*)g_CbPerObjectAddress[g_FrameIndex] + ConstantBufferPerObjectAlignedSize, &cb, sizeof(cb));
1198 }
1199 }
1200
Render()1201 void Render() // execute the command list
1202 {
1203 // # Here was UpdatePipeline function.
1204
1205 // swap the current rtv buffer index so we draw on the correct buffer
1206 g_FrameIndex = g_SwapChain->GetCurrentBackBufferIndex();
1207 // We have to wait for the gpu to finish with the command allocator before we reset it
1208 WaitForFrame(g_FrameIndex);
1209 // increment g_FenceValues for next frame
1210 g_FenceValues[g_FrameIndex]++;
1211
1212 // we can only reset an allocator once the gpu is done with it
1213 // resetting an allocator frees the memory that the command list was stored in
1214 CHECK_HR( g_CommandAllocators[g_FrameIndex]->Reset() );
1215
1216 // reset the command list. by resetting the command list we are putting it into
1217 // a recording state so we can start recording commands into the command allocator.
1218 // the command allocator that we reference here may have multiple command lists
1219 // associated with it, but only one can be recording at any time. Make sure
1220 // that any other command lists associated to this command allocator are in
1221 // the closed state (not recording).
1222 // Here you will pass an initial pipeline state object as the second parameter,
1223 // but in this tutorial we are only clearing the rtv, and do not actually need
1224 // anything but an initial default pipeline, which is what we get by setting
1225 // the second parameter to NULL
1226 CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex], NULL) );
1227
1228 // here we start recording commands into the g_CommandList (which all the commands will be stored in the g_CommandAllocators)
1229
1230 // 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
1231 D3D12_RESOURCE_BARRIER presentToRenderTargetBarrier = {};
1232 presentToRenderTargetBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1233 presentToRenderTargetBarrier.Transition.pResource = g_RenderTargets[g_FrameIndex];
1234 presentToRenderTargetBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
1235 presentToRenderTargetBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
1236 presentToRenderTargetBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1237 g_CommandList->ResourceBarrier(1, &presentToRenderTargetBarrier);
1238
1239 // 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
1240 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = {
1241 g_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + g_FrameIndex * g_RtvDescriptorSize};
1242 D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle =
1243 g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
1244
1245 // set the render target for the output merger stage (the output of the pipeline)
1246 g_CommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
1247
1248 g_CommandList->ClearDepthStencilView(g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
1249
1250 // Clear the render target by using the ClearRenderTargetView command
1251 const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
1252 g_CommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
1253
1254 g_CommandList->SetPipelineState(g_PipelineStateObject);
1255
1256 g_CommandList->SetGraphicsRootSignature(g_RootSignature);
1257
1258 ID3D12DescriptorHeap* descriptorHeaps[] = { g_MainDescriptorHeap[g_FrameIndex] };
1259 g_CommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
1260
1261 g_CommandList->SetGraphicsRootDescriptorTable(0, g_MainDescriptorHeap[g_FrameIndex]->GetGPUDescriptorHandleForHeapStart());
1262 g_CommandList->SetGraphicsRootDescriptorTable(2, g_MainDescriptorHeap[g_FrameIndex]->GetGPUDescriptorHandleForHeapStart());
1263
1264 D3D12_VIEWPORT viewport{0.f, 0.f, (float)SIZE_X, (float)SIZE_Y, 0.f, 1.f};
1265 g_CommandList->RSSetViewports(1, &viewport); // set the viewports
1266
1267 D3D12_RECT scissorRect{0, 0, SIZE_X, SIZE_Y};
1268 g_CommandList->RSSetScissorRects(1, &scissorRect); // set the scissor rects
1269
1270 g_CommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // set the primitive topology
1271 g_CommandList->IASetVertexBuffers(0, 1, &g_VertexBufferView); // set the vertex buffer (using the vertex buffer view)
1272 g_CommandList->IASetIndexBuffer(&g_IndexBufferView);
1273
1274 g_CommandList->SetGraphicsRootConstantBufferView(1,
1275 g_CbPerObjectUploadHeaps[g_FrameIndex]->GetGPUVirtualAddress());
1276 g_CommandList->DrawIndexedInstanced(g_CubeIndexCount, 1, 0, 0, 0);
1277
1278 g_CommandList->SetGraphicsRootConstantBufferView(1,
1279 g_CbPerObjectUploadHeaps[g_FrameIndex]->GetGPUVirtualAddress() + ConstantBufferPerObjectAlignedSize);
1280 g_CommandList->DrawIndexedInstanced(g_CubeIndexCount, 1, 0, 0, 0);
1281
1282 // 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
1283 // warning if present is called on the render target when it's not in the present state
1284 D3D12_RESOURCE_BARRIER renderTargetToPresentBarrier = {};
1285 renderTargetToPresentBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1286 renderTargetToPresentBarrier.Transition.pResource = g_RenderTargets[g_FrameIndex];
1287 renderTargetToPresentBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
1288 renderTargetToPresentBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
1289 renderTargetToPresentBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1290 g_CommandList->ResourceBarrier(1, &renderTargetToPresentBarrier);
1291
1292 CHECK_HR( g_CommandList->Close() );
1293
1294 // ================
1295
1296 // create an array of command lists (only one command list here)
1297 ID3D12CommandList* ppCommandLists[] = { g_CommandList };
1298
1299 // execute the array of command lists
1300 g_CommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
1301
1302 // this command goes in at the end of our command queue. we will know when our command queue
1303 // has finished because the g_Fences value will be set to "g_FenceValues" from the GPU since the command
1304 // queue is being executed on the GPU
1305 CHECK_HR( g_CommandQueue->Signal(g_Fences[g_FrameIndex], g_FenceValues[g_FrameIndex]) );
1306
1307 // present the current backbuffer
1308 CHECK_HR( g_SwapChain->Present(PRESENT_SYNC_INTERVAL, 0) );
1309 }
1310
Cleanup()1311 void Cleanup() // release com ojects and clean up memory
1312 {
1313 // wait for the gpu to finish all frames
1314 for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i)
1315 {
1316 WaitForFrame(i);
1317 CHECK_HR( g_CommandQueue->Wait(g_Fences[i], g_FenceValues[i]) );
1318 }
1319
1320 // get swapchain out of full screen before exiting
1321 BOOL fs = false;
1322 CHECK_HR( g_SwapChain->GetFullscreenState(&fs, NULL) );
1323 if (fs)
1324 g_SwapChain->SetFullscreenState(false, NULL);
1325
1326 WaitGPUIdle(0);
1327
1328 g_Texture.Release();
1329 g_TextureAllocation->Release(); g_TextureAllocation = nullptr;
1330 g_IndexBuffer.Release();
1331 g_IndexBufferAllocation->Release(); g_IndexBufferAllocation = nullptr;
1332 g_VertexBuffer.Release();
1333 g_VertexBufferAllocation->Release(); g_VertexBufferAllocation = nullptr;
1334 g_PipelineStateObject.Release();
1335 g_RootSignature.Release();
1336
1337 CloseHandle(g_FenceEvent);
1338 g_CommandList.Release();
1339 g_CommandQueue.Release();
1340
1341 for (size_t i = FRAME_BUFFER_COUNT; i--; )
1342 {
1343 g_CbPerObjectUploadHeaps[i].Release();
1344 g_CbPerObjectUploadHeapAllocations[i]->Release(); g_CbPerObjectUploadHeapAllocations[i] = nullptr;
1345 g_MainDescriptorHeap[i].Release();
1346 g_ConstantBufferUploadHeap[i].Release();
1347 g_ConstantBufferUploadAllocation[i]->Release(); g_ConstantBufferUploadAllocation[i] = nullptr;
1348 }
1349
1350 g_DepthStencilDescriptorHeap.Release();
1351 g_DepthStencilBuffer.Release();
1352 g_DepthStencilAllocation->Release(); g_DepthStencilAllocation = nullptr;
1353 g_RtvDescriptorHeap.Release();
1354 for (size_t i = FRAME_BUFFER_COUNT; i--; )
1355 {
1356 g_RenderTargets[i].Release();
1357 g_CommandAllocators[i].Release();
1358 g_Fences[i].Release();
1359 }
1360
1361 g_Allocator->Release(); g_Allocator = nullptr;
1362 if(ENABLE_CPU_ALLOCATION_CALLBACKS)
1363 {
1364 assert(g_CpuAllocationCount.load() == 0);
1365 }
1366
1367 g_Device.Release();
1368 g_SwapChain.Release();
1369 }
1370
ExecuteTests()1371 static void ExecuteTests()
1372 {
1373 try
1374 {
1375 TestContext ctx = {};
1376 ctx.allocationCallbacks = &g_AllocationCallbacks;
1377 ctx.device = g_Device;
1378 ctx.allocator = g_Allocator;
1379 ctx.allocatorFlags = g_AllocatorFlags;
1380 Test(ctx);
1381 }
1382 catch(const std::exception& ex)
1383 {
1384 wprintf(L"ERROR: %hs\n", ex.what());
1385 }
1386 }
1387
OnKeyDown(WPARAM key)1388 static void OnKeyDown(WPARAM key)
1389 {
1390 switch (key)
1391 {
1392 case 'T':
1393 ExecuteTests();
1394 break;
1395
1396 case 'J':
1397 {
1398 WCHAR* statsString = NULL;
1399 g_Allocator->BuildStatsString(&statsString, TRUE);
1400 wprintf(L"%s\n", statsString);
1401 g_Allocator->FreeStatsString(statsString);
1402 }
1403 break;
1404
1405 case VK_ESCAPE:
1406 PostMessage(g_Wnd, WM_CLOSE, 0, 0);
1407 break;
1408 }
1409 }
1410
WndProc(HWND wnd,UINT msg,WPARAM wParam,LPARAM lParam)1411 static LRESULT WINAPI WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
1412 {
1413 switch(msg)
1414 {
1415 case WM_CREATE:
1416 g_Wnd = wnd;
1417 InitD3D();
1418 g_TimeOffset = GetTickCount64();
1419 return 0;
1420
1421 case WM_DESTROY:
1422 Cleanup();
1423 PostQuitMessage(0);
1424 return 0;
1425
1426 case WM_KEYDOWN:
1427 OnKeyDown(wParam);
1428 return 0;
1429 }
1430
1431 return DefWindowProc(wnd, msg, wParam, lParam);
1432 }
1433
BeginCommandList()1434 ID3D12GraphicsCommandList* BeginCommandList()
1435 {
1436 CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex], NULL) );
1437
1438 return g_CommandList;
1439 }
1440
EndCommandList(ID3D12GraphicsCommandList * cmdList)1441 void EndCommandList(ID3D12GraphicsCommandList* cmdList)
1442 {
1443 cmdList->Close();
1444
1445 ID3D12CommandList* genericCmdList = cmdList;
1446 g_CommandQueue->ExecuteCommandLists(1, &genericCmdList);
1447
1448 WaitGPUIdle(g_FrameIndex);
1449 }
1450
main()1451 int main()
1452 {
1453 g_Instance = (HINSTANCE)GetModuleHandle(NULL);
1454
1455 CoInitialize(NULL);
1456
1457 WNDCLASSEX wndClass;
1458 ZeroMemory(&wndClass, sizeof(wndClass));
1459 wndClass.cbSize = sizeof(wndClass);
1460 wndClass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
1461 wndClass.hbrBackground = NULL;
1462 wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
1463 wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
1464 wndClass.hInstance = g_Instance;
1465 wndClass.lpfnWndProc = &WndProc;
1466 wndClass.lpszClassName = CLASS_NAME;
1467
1468 ATOM classR = RegisterClassEx(&wndClass);
1469 assert(classR);
1470
1471 DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE;
1472 DWORD exStyle = 0;
1473
1474 RECT rect = { 0, 0, SIZE_X, SIZE_Y };
1475 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
1476 g_Wnd = CreateWindowEx(
1477 exStyle,
1478 CLASS_NAME,
1479 WINDOW_TITLE,
1480 style,
1481 CW_USEDEFAULT, CW_USEDEFAULT,
1482 rect.right - rect.left, rect.bottom - rect.top,
1483 NULL,
1484 NULL,
1485 g_Instance,
1486 0);
1487 assert(g_Wnd);
1488
1489 MSG msg;
1490 for (;;)
1491 {
1492 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
1493 {
1494 if (msg.message == WM_QUIT)
1495 break;
1496 TranslateMessage(&msg);
1497 DispatchMessage(&msg);
1498 }
1499 else
1500 {
1501 const UINT64 newTimeValue = GetTickCount64() - g_TimeOffset;
1502 g_TimeDelta = (float)(newTimeValue - g_TimeValue) * 0.001f;
1503 g_TimeValue = newTimeValue;
1504 g_Time = (float)newTimeValue * 0.001f;
1505
1506 Update();
1507 Render();
1508 }
1509 }
1510 return (int)msg.wParam;
1511 }
1512