Skip to content

Commit bfbad64

Browse files
committed
HelloMeshNodes v1.0.0
0 parents  commit bfbad64

15 files changed

+1409
-0
lines changed

.gitattributes

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
d3d12/lib/x64/D3D12Core.dll filter=lfs diff=lfs merge=lfs -text
2+
d3d12/lib/x64/d3d12SDKLayers.dll filter=lfs diff=lfs merge=lfs -text
3+
d3d12/lib/x64/dxcompiler.dll filter=lfs diff=lfs merge=lfs -text

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.user
2+
.vs/
3+
x64/
4+
!/d3d12/lib/x64/
5+
packages/

D3D12Helper.cpp

+383
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
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("\nPress 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

Comments
 (0)