1
+ /* *********************************************************************
2
+ Copyright (c) 2024 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 " HelloMeshNodes.h"
24
+
25
+ #include < string>
26
+ #include < algorithm>
27
+
28
+ #define ERROR_QUIT (value, ...) if (!(value)) { printf (" ERROR: " ); printf (__VA_ARGS__); printf (" \n Press any key to terminate...\n " ); _getch (); throw 0 ; }
29
+
30
+ namespace {
31
+ // function GetHardwareAdapter() copy-pasted from the publicly distributed sample provided at: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-d3d12createdevice
32
+ void GetHardwareAdapter (IDXGIFactory4* pFactory, IDXGIAdapter1** ppAdapter)
33
+ {
34
+ *ppAdapter = nullptr ;
35
+ for (UINT adapterIndex = 0 ; ; ++adapterIndex)
36
+ {
37
+ IDXGIAdapter1* pAdapter = nullptr ;
38
+ if (DXGI_ERROR_NOT_FOUND == pFactory->EnumAdapters1 (adapterIndex, &pAdapter))
39
+ {
40
+ // No more adapters to enumerate.
41
+ break ;
42
+ }
43
+
44
+ // Check to see if the adapter supports Direct3D 12, but don't create the
45
+ // actual device yet.
46
+ if (SUCCEEDED (D3D12CreateDevice (pAdapter, D3D_FEATURE_LEVEL_11_0, _uuidof (ID3D12Device), nullptr )))
47
+ {
48
+ *ppAdapter = pAdapter;
49
+ return ;
50
+ }
51
+ pAdapter->Release ();
52
+ }
53
+ }
54
+ }
55
+
56
+ HelloMeshNodes::~HelloMeshNodes ()
57
+ {
58
+ if (device_) {
59
+ WaitForPreviousFrame ();
60
+ CloseHandle (fenceEvent_);
61
+ }
62
+ }
63
+
64
+ void HelloMeshNodes::InitializeDirectX (HWND hwnd)
65
+ {
66
+ HRESULT hresult;
67
+
68
+ device_ = nullptr ;
69
+
70
+ CComPtr<IDXGIFactory4> factory;
71
+ hresult = CreateDXGIFactory2 (0 , IID_PPV_ARGS (&factory));
72
+ ERROR_QUIT (hresult == S_OK, " Failed to create IDXGIFactory4." );
73
+
74
+ CComPtr<IDXGIAdapter1> hardwareAdapter;
75
+ GetHardwareAdapter (factory, &hardwareAdapter);
76
+ hresult = D3D12CreateDevice (hardwareAdapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS (&device_));
77
+ ERROR_QUIT (hresult == S_OK, " Failed to create ID3D12Device." );
78
+
79
+ // Create the command queue.
80
+ D3D12_COMMAND_QUEUE_DESC queueDesc = {};
81
+ queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
82
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
83
+
84
+ hresult = device_->CreateCommandQueue (&queueDesc, IID_PPV_ARGS (&commandQueue_));
85
+ ERROR_QUIT (hresult == S_OK, " Failed to create ID3D12CommandQueue." );
86
+
87
+ // Create the swap chain.
88
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
89
+ swapChainDesc.BufferCount = FrameCount;
90
+ swapChainDesc.Width = WindowSize;
91
+ swapChainDesc.Height = WindowSize;
92
+ swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
93
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
94
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
95
+ swapChainDesc.SampleDesc .Count = 1 ;
96
+
97
+ CComPtr<IDXGISwapChain1> swapChain;
98
+ hresult = factory->CreateSwapChainForHwnd (
99
+ commandQueue_,
100
+ hwnd,
101
+ &swapChainDesc,
102
+ nullptr ,
103
+ nullptr ,
104
+ &swapChain
105
+ );
106
+ ERROR_QUIT (hresult == S_OK, " Failed to create IDXGISwapChain1." );
107
+
108
+ hresult = factory->MakeWindowAssociation (hwnd, DXGI_MWA_NO_ALT_ENTER);
109
+ ERROR_QUIT (hresult == S_OK, " Failed to make window association." );
110
+
111
+ hresult = swapChain.QueryInterface (&swapChain_);
112
+ ERROR_QUIT (hresult == S_OK, " Failed to query IDXGISwapChain3." );
113
+
114
+ frameIndex_ = swapChain_->GetCurrentBackBufferIndex ();
115
+
116
+ // Create render target view (RTV) descriptor heaps.
117
+ {
118
+ D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
119
+ rtvHeapDesc.NumDescriptors = FrameCount;
120
+ rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
121
+ rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
122
+ hresult = device_->CreateDescriptorHeap (&rtvHeapDesc, IID_PPV_ARGS (&renderViewDescriptorHeap_));
123
+ ERROR_QUIT (hresult == S_OK, " Failed to create RTV descriptor heap." );
124
+
125
+ descriptorSize_ = device_->GetDescriptorHandleIncrementSize (D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
126
+ }
127
+
128
+ // Create frame resources.
129
+ {
130
+ CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle (renderViewDescriptorHeap_->GetCPUDescriptorHandleForHeapStart ());
131
+
132
+ // Create a RTV for each frame.
133
+ for (UINT n = 0 ; n < FrameCount; n++)
134
+ {
135
+ hresult = swapChain_->GetBuffer (n, IID_PPV_ARGS (&renderTargets_[n]));
136
+ ERROR_QUIT (hresult == S_OK, " Failed to access render target of swap chain." );
137
+ device_->CreateRenderTargetView (renderTargets_[n].p , nullptr , rtvHandle);
138
+ rtvHandle.Offset (1 , descriptorSize_);
139
+ }
140
+ }
141
+
142
+ // Create a depth-stencil view (DSV) descriptor heap and depth buffer
143
+ {
144
+ D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
145
+ dsvHeapDesc.NumDescriptors = 1 ;
146
+ dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
147
+ dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
148
+ hresult = device_->CreateDescriptorHeap (&dsvHeapDesc, IID_PPV_ARGS (&depthDescriptorHeap_));
149
+ ERROR_QUIT (hresult == S_OK, " Failed to create DSV descriptor heap." );
150
+
151
+ depthDescriptorHeap_->SetName (L" Depth/Stencil Resource Heap" );
152
+
153
+ D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
154
+ depthStencilDesc.Format = DXGI_FORMAT_D32_FLOAT;
155
+ depthStencilDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
156
+ depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE;
157
+
158
+ D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
159
+ depthOptimizedClearValue.Format = DXGI_FORMAT_D32_FLOAT;
160
+ depthOptimizedClearValue.DepthStencil .Depth = 1 .0f ;
161
+ depthOptimizedClearValue.DepthStencil .Stencil = 0 ;
162
+
163
+ CD3DX12_HEAP_PROPERTIES depthHeapProperties (D3D12_HEAP_TYPE_DEFAULT);
164
+ CD3DX12_RESOURCE_DESC depthResourceDescription = CD3DX12_RESOURCE_DESC::Tex2D (
165
+ DXGI_FORMAT_D32_FLOAT,
166
+ WindowSize, WindowSize,
167
+ 1 , 0 , 1 , 0 ,
168
+ D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
169
+ hresult = device_->CreateCommittedResource (
170
+ &depthHeapProperties,
171
+ D3D12_HEAP_FLAG_NONE,
172
+ &depthResourceDescription,
173
+ D3D12_RESOURCE_STATE_DEPTH_WRITE,
174
+ &depthOptimizedClearValue,
175
+ IID_PPV_ARGS (&depthBuffer_)
176
+ );
177
+ ERROR_QUIT (hresult == S_OK, " Failed to create depth buffer." );
178
+
179
+ device_->CreateDepthStencilView (depthBuffer_, &depthStencilDesc, depthDescriptorHeap_->GetCPUDescriptorHandleForHeapStart ());
180
+ }
181
+
182
+ hresult = device_->CreateCommandAllocator (D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS (&commandAllocator_));
183
+ ERROR_QUIT (hresult == S_OK, " Failed to create ID3D12CommandAllocator." );
184
+
185
+ // Create the command list.
186
+ hresult = device_->CreateCommandList (0 , D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator_.p , nullptr , IID_PPV_ARGS (&commandList_));
187
+ ERROR_QUIT (hresult == S_OK, " Failed to create ID3D12GraphicsCommandList." );
188
+
189
+ // Command lists are created in the recording state, but there is nothing
190
+ // to record yet. The main loop expects it to be closed, so close it now.
191
+ hresult = commandList_->Close ();
192
+ ERROR_QUIT (hresult == S_OK, " Failed to close ID3D12GraphicsCommandList." );
193
+
194
+ // Create sync objects
195
+ hresult = device_->CreateFence (0 , D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS (&fence_));
196
+ ERROR_QUIT (hresult == S_OK, " Failed to create ID3D12Fence." );
197
+ fenceValue_ = 1 ;
198
+
199
+ // Create an event handle to use for frame synchronization.
200
+ fenceEvent_ = CreateEvent (nullptr , FALSE , FALSE , nullptr );
201
+ ERROR_QUIT (fenceEvent_ != nullptr , " Failed to create synchronization event." );
202
+
203
+ // Create empty root signature
204
+ {
205
+ CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
206
+ rootSignatureDesc.Init (0 , nullptr , 0 , nullptr , D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
207
+
208
+ CComPtr<ID3DBlob> signature;
209
+ CComPtr<ID3DBlob> error;
210
+ hresult = D3D12SerializeRootSignature (&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
211
+ ERROR_QUIT (hresult == S_OK, " Failed to serialize RootSignature." );
212
+ hresult = device_->CreateRootSignature (0 , signature->GetBufferPointer (), signature->GetBufferSize (), IID_PPV_ARGS (&globalRootSignature_));
213
+ ERROR_QUIT (hresult == S_OK, " Failed to create RootSignature." );
214
+ }
215
+ }
216
+
217
+ void HelloMeshNodes::Render ()
218
+ {
219
+ HRESULT hresult;
220
+ // Reset allocator and list
221
+ hresult = commandAllocator_->Reset ();
222
+ ERROR_QUIT (hresult == S_OK, " Failed to reset ID3D12CommandAllocator." );
223
+
224
+ hresult = commandList_->Reset (commandAllocator_.p , pipelineState_.p );
225
+ ERROR_QUIT (hresult == S_OK, " Failed to reset ID3D12GraphicsCommandList." );
226
+
227
+ RecordCommandList ();
228
+
229
+ hresult = commandList_->Close ();
230
+ ERROR_QUIT (hresult == S_OK, " Failed to close ID3D12CommandAllocator." );
231
+
232
+ // Execute the command list.
233
+ commandQueue_->ExecuteCommandLists (1 , CommandListCast (&commandList_.p ));
234
+
235
+ // Present the frame.
236
+ hresult = swapChain_->Present (1 , 0 );
237
+ ERROR_QUIT (hresult == S_OK, " Failed to present frame." );
238
+
239
+ WaitForPreviousFrame ();
240
+ }
241
+
242
+ void HelloMeshNodes::WaitForPreviousFrame ()
243
+ {
244
+ HRESULT hresult;
245
+ // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
246
+ // This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
247
+ // sample from Microsoft illustrates how to use fences for efficient resource
248
+ // usage and to maximize GPU utilization.
249
+
250
+ // Signal and increment the fence value.
251
+ const UINT64 fence = fenceValue_;
252
+ hresult = commandQueue_->Signal (fence_.p , fence);
253
+ ERROR_QUIT (hresult == S_OK, " Failed to signal fence." );
254
+ fenceValue_++;
255
+
256
+ // Wait until the previous frame is finished.
257
+ if (fence_->GetCompletedValue () < fence)
258
+ {
259
+ hresult = fence_->SetEventOnCompletion (fence, fenceEvent_);
260
+ ERROR_QUIT (hresult == S_OK, " Failed to set up fence event." );
261
+ WaitForSingleObject (fenceEvent_, INFINITE);
262
+ }
263
+
264
+ frameIndex_ = swapChain_->GetCurrentBackBufferIndex ();
265
+ }
266
+
267
+ namespace d3d12 {
268
+ HMODULE sDxCompilerDLL = nullptr ;
269
+ void LoadCompiler ()
270
+ {
271
+ // load compiler
272
+ sDxCompilerDLL = LoadLibrary (L" dxcompiler.dll" );
273
+
274
+ ERROR_QUIT (sDxCompilerDLL , " Failed to initialize compiler." );
275
+ }
276
+
277
+ void ReleaseCompiler ()
278
+ {
279
+ if (sDxCompilerDLL )
280
+ {
281
+ FreeLibrary (sDxCompilerDLL );
282
+ sDxCompilerDLL = nullptr ;
283
+ }
284
+ }
285
+
286
+ ID3D12Resource* AllocateBuffer (CComPtr<ID3D12Device9> pDevice, UINT64 Size , D3D12_RESOURCE_FLAGS ResourceFlags, D3D12_HEAP_TYPE HeapType)
287
+ {
288
+ ID3D12Resource* pResource;
289
+
290
+ CD3DX12_HEAP_PROPERTIES HeapProperties (HeapType);
291
+ CD3DX12_RESOURCE_DESC ResourceDesc = CD3DX12_RESOURCE_DESC::Buffer (Size , ResourceFlags);
292
+ HRESULT hr = pDevice->CreateCommittedResource (&HeapProperties, D3D12_HEAP_FLAG_NONE, &ResourceDesc, D3D12_RESOURCE_STATE_COMMON, NULL , IID_PPV_ARGS (&pResource));
293
+ ERROR_QUIT (SUCCEEDED (hr), " Failed to allocate buffer." );
294
+
295
+ return pResource;
296
+ }
297
+
298
+ void TransitionBarrier (ID3D12GraphicsCommandList* commandList, ID3D12Resource* resource, D3D12_RESOURCE_STATES stateBefore, D3D12_RESOURCE_STATES stateAfter)
299
+ {
300
+ CD3DX12_RESOURCE_BARRIER transition = CD3DX12_RESOURCE_BARRIER::Transition (resource, stateBefore, stateAfter);
301
+ commandList->ResourceBarrier (1 , &transition);
302
+ }
303
+ }
304
+
305
+ namespace window {
306
+ HWND Initialize (HelloMeshNodes* ctx)
307
+ {
308
+ const HINSTANCE hInstance = GetModuleHandleA (NULL );
309
+
310
+ WNDCLASSEX windowClass = { 0 };
311
+ windowClass.cbSize = sizeof (WNDCLASSEX);
312
+ windowClass.style = CS_HREDRAW | CS_VREDRAW;
313
+ windowClass.lpfnWndProc = Proc;
314
+ windowClass.hInstance = hInstance;
315
+ windowClass.hCursor = LoadCursor (NULL , IDC_ARROW);
316
+ windowClass.lpszClassName = L" Hello Mesh Nodes" ;
317
+ RegisterClassEx (&windowClass);
318
+
319
+ RECT windowRect = { 0 , 0 , WindowSize, WindowSize };
320
+ DWORD style = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU;
321
+ AdjustWindowRect (&windowRect, style, FALSE );
322
+
323
+ HWND hwnd = CreateWindow (windowClass.lpszClassName ,
324
+ windowClass.lpszClassName ,
325
+ style,
326
+ CW_USEDEFAULT,
327
+ CW_USEDEFAULT,
328
+ windowRect.right - windowRect.left ,
329
+ windowRect.bottom - windowRect.top ,
330
+ nullptr , // We have no parent window.
331
+ nullptr , // We are not using menus.
332
+ hInstance,
333
+ static_cast <void *>(ctx));
334
+
335
+ return hwnd;
336
+ }
337
+
338
+
339
+ LRESULT CALLBACK Proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
340
+ HelloMeshNodes* ctx = reinterpret_cast <HelloMeshNodes*>(GetWindowLongPtr (hWnd, GWLP_USERDATA));
341
+
342
+ switch (message)
343
+ {
344
+ case WM_CREATE:
345
+ {
346
+ // Save the WindowContext* passed in to CreateWindow.
347
+ LPCREATESTRUCT pCreateStruct = reinterpret_cast <LPCREATESTRUCT>(lParam);
348
+ SetWindowLongPtr (hWnd, GWLP_USERDATA, reinterpret_cast <LONG_PTR>(pCreateStruct->lpCreateParams ));
349
+ return 0 ;
350
+ }
351
+ case WM_PAINT:
352
+ ctx->Render ();
353
+ return 0 ;
354
+ case WM_KEYDOWN: {
355
+ if (lParam == VK_ESCAPE) {
356
+ PostQuitMessage (0 );
357
+ return 0 ;
358
+ }
359
+ }
360
+ case WM_DESTROY:
361
+ PostQuitMessage (0 );
362
+ return 0 ;
363
+ }
364
+
365
+ // Handle any messages the switch statement didn't.
366
+ return DefWindowProc (hWnd, message, wParam, lParam);
367
+ }
368
+
369
+
370
+ void MessageLoop ()
371
+ {
372
+ MSG msg = {};
373
+ while (msg.message != WM_QUIT)
374
+ {
375
+ // Process any messages in the queue.
376
+ if (PeekMessage (&msg, NULL , 0 , 0 , PM_REMOVE))
377
+ {
378
+ TranslateMessage (&msg);
379
+ DispatchMessage (&msg);
380
+ }
381
+ }
382
+ }
383
+ }
0 commit comments