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