aboutsummaryrefslogtreecommitdiff
path: root/hello/main.cc
diff options
context:
space:
mode:
authorMarc Sunet <marc.sunet@amd.com>2025-11-21 09:41:06 -0800
committerMarc Sunet <marc.sunet@amd.com>2025-11-21 09:41:06 -0800
commit0b5491e0a2f1a9a4023e2c4eb171287bede41388 (patch)
treee82e93313f1fcfcc5622bae706aea9335dbc43ef /hello/main.cc
parentb5697421bbc73ed17ef3a8bc003571d1b6351b5c (diff)
Switch to plain C
Diffstat (limited to 'hello/main.cc')
-rw-r--r--hello/main.cc433
1 files changed, 0 insertions, 433 deletions
diff --git a/hello/main.cc b/hello/main.cc
deleted file mode 100644
index d9040b4..0000000
--- a/hello/main.cc
+++ /dev/null
@@ -1,433 +0,0 @@
1#include <dxcommon.h>
2
3#include <cassert>
4#include <cstdio>
5
6import dxcommon;
7import dxwindow;
8
9using namespace dx;
10using Microsoft::WRL::ComPtr;
11
12struct D3DSettings
13{
14 int width = 0;
15 int height = 0;
16};
17
18class D3D
19{
20public:
21 void Initialise(Window* window, const D3DSettings& settings)
22 {
23 m_window = window;
24 m_settings = settings;
25
26 UINT dxgiFactoryFlags = 0;
27#ifdef DEBUG
28 {
29 ComPtr<ID3D12Debug> debug;
30 D3D12GetDebugInterface(IID_PPV_ARGS(&debug));
31 debug->EnableDebugLayer();
32 dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
33 }
34#endif
35 ThrowIfFailed(CreateDXGIFactory2(
36 dxgiFactoryFlags, IID_PPV_ARGS(&m_dxgi_factory)));
37
38 // Prevent Alt+Enter from going into fullscreen.
39 ThrowIfFailed(m_dxgi_factory->MakeWindowAssociation(
40 m_window->GetWindowHandle(),
41 DXGI_MWA_NO_ALT_ENTER));
42
43 ThrowIfFailed(D3D12CreateDevice(
44 /*pAdapter=*/nullptr, // Default adapter.
45 D3D_FEATURE_LEVEL_11_0,
46 IID_PPV_ARGS(&m_device)));
47
48 m_rtv_descriptor_size = m_device->GetDescriptorHandleIncrementSize(
49 D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
50 m_dsv_descriptor_size = m_device->GetDescriptorHandleIncrementSize(
51 D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
52 m_cbv_descriptor_size = m_device->GetDescriptorHandleIncrementSize(
53 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
54
55 const D3D12_COMMAND_QUEUE_DESC queue_desc
56 {
57 .Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
58 .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
59 };
60 ThrowIfFailed(m_device->CreateCommandQueue(
61 &queue_desc,
62 IID_PPV_ARGS(&m_command_queue)));
63
64 // The command allocator is the memory backing for the command list.
65 // It is in the allocator's memory where the commands are stored.
66 ThrowIfFailed(m_device->CreateCommandAllocator(
67 queue_desc.Type,
68 IID_PPV_ARGS(&m_command_allocator)));
69
70 ThrowIfFailed(m_device->CreateCommandList(
71 /*nodeMask=*/0,
72 queue_desc.Type,
73 m_command_allocator.Get(),
74 /*pInitialState=*/nullptr, // Pipeline state.
75 IID_PPV_ARGS(&m_command_list)));
76
77 // Command lists are in the "open" state after they are created. It is
78 // easier to assume that they start in the "closed" state at each
79 // iteration of the main loop, however. The Reset() method, which we'll
80 // use later, also expects the command list to be closed.
81 ThrowIfFailed(m_command_list->Close());
82
83 CreateDescriptorHeaps();
84
85 CreateSwapChain();
86 CreateSwapChainBufferRenderTargetViews();
87 CreateDepthStencilBufferAndView();
88
89 ThrowIfFailed(m_device->CreateFence(
90 /*InitialValue=*/m_fence_value,
91 D3D12_FENCE_FLAG_NONE,
92 IID_PPV_ARGS(&m_fence)));
93
94 if ((m_fence_event = CreateEvent(
95 /*lpEventAttributes=*/nullptr,
96 /*bManualReset=*/FALSE,
97 /*bInitialState=*/FALSE,
98 /*lpName=*/nullptr)) == 0)
99 {
100 ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
101 }
102 }
103
104 void Render()
105 {
106 PopulateCommandList();
107
108 ID3D12CommandList* command_lists[] { m_command_list.Get() };
109 m_command_queue->ExecuteCommandLists(
110 _countof(command_lists), command_lists);
111
112 ThrowIfFailed(m_swap_chain->Present(/*SyncInterval=*/1, /*Flags=*/0));
113
114 // It is not efficient to wait for the frame to complete here, but it
115 // is simple and sufficient for this application.
116 WaitForPreviousFrame();
117 }
118
119private:
120 void PopulateCommandList()
121 {
122 /// Note that we skip the following two items:
123 ///
124 /// 1. RSSetViewports()
125 /// 2. OMSetRenderTargets()
126 ///
127 /// This application does not render anything useful, it simply clears
128 /// the back buffer and depth/stencil view. Clearing both resources
129 /// does not require a viewport to be set or the OM (output-merger
130 /// stage) to be configured.
131
132 // A command allocator can only be reset when its associated command
133 // lists are finished executing on the GPU. This requires
134 // synchronisation.
135 ThrowIfFailed(m_command_allocator->Reset());
136
137 // A command list can be reset as soon as it is executed with
138 // ExecuteCommandList(). Reset() does require that the command list is
139 // in a "closed" state, however, which is why we Close() it right away
140 // after creation.
141 ThrowIfFailed(m_command_list->Reset(
142 m_command_allocator.Get(),
143 /*pInitialState=*/nullptr));
144
145 // Indicate that we intend to use the back buffer as a render target.
146 const auto render_barrier = CD3DX12_RESOURCE_BARRIER::Transition(
147 GetCurrentBackBuffer(),
148 D3D12_RESOURCE_STATE_PRESENT,
149 D3D12_RESOURCE_STATE_RENDER_TARGET);
150 m_command_list->ResourceBarrier(1, &render_barrier);
151
152 // Record commands.
153 const float clear_colour[] { 0.0f, 0.502f, 0.494f, 0.0f };
154 m_command_list->ClearRenderTargetView(
155 GetCurrentBackBufferView(),
156 clear_colour,
157 0, // Number of rectangles in the following array.
158 nullptr); // No rectangles; clear the entire resource.
159
160 m_command_list->ClearDepthStencilView(
161 GetDepthStencilView(),
162 D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,
163 1.0f, // Depth.
164 0, // Stencil.
165 0, // Number of rectangles in the following array.
166 nullptr); // No rectangles; clear the entire resource view.
167
168 // Indicate that we now intend to use the back buffer to present.
169 const auto present_barrier = CD3DX12_RESOURCE_BARRIER::Transition(
170 GetCurrentBackBuffer(),
171 D3D12_RESOURCE_STATE_RENDER_TARGET,
172 D3D12_RESOURCE_STATE_PRESENT);
173 m_command_list->ResourceBarrier(1, &present_barrier);
174
175 // A command list must be closed before it can be executed.
176 ThrowIfFailed(m_command_list->Close());
177 }
178
179 void WaitForPreviousFrame()
180 {
181 // Advance the fence value to mark commands up to this fence point.
182 m_fence_value++;
183
184 // The command queue will signal the new fence value when all commands
185 // up to this point have finished execution.
186 ThrowIfFailed(m_command_queue->Signal(m_fence.Get(), m_fence_value));
187
188 // Wait for commands to finish execution.
189 // It is possible that execution has already finished by the time we
190 // get here, so first check the fence's completed value.
191 if (m_fence->GetCompletedValue() < m_fence_value)
192 {
193 // Commands are still being executed. Configure a Windows event
194 // and wait for it. The event fires when the commands have finished
195 // execution.
196
197 // Indicate that |m_fence_event| is to be fired when |m_fence|
198 // reaches the new fence value.
199 ThrowIfFailed(m_fence->SetEventOnCompletion(
200 m_fence_value, m_fence_event));
201
202 // Will wake up when the fence takes on the new fence value.
203 WaitForSingleObject(m_fence_event, INFINITE);
204 }
205 }
206
207 /// Creates RTV and DSV descriptor heaps.
208 void CreateDescriptorHeaps()
209 {
210 assert(m_device);
211
212 // The RTV heap must hold as many descriptors as we have buffers in the
213 // swap chain.
214 const D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc
215 {
216 .Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
217 .NumDescriptors = SWAP_CHAIN_BUFFER_COUNT,
218 .Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
219 .NodeMask = 0,
220 };
221 ThrowIfFailed(m_device->CreateDescriptorHeap(
222 &rtv_heap_desc, IID_PPV_ARGS(&m_rtv_heap)));
223
224 // For the depth/stencil buffer, we just need one view.
225 const D3D12_DESCRIPTOR_HEAP_DESC dsv_heap_desc
226 {
227 .Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
228 .NumDescriptors = 1,
229 .Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
230 .NodeMask = 0,
231 };
232 ThrowIfFailed(m_device->CreateDescriptorHeap(
233 &dsv_heap_desc, IID_PPV_ARGS(&m_dsv_heap)));
234 }
235
236 /// Creates the application's swap chain.
237 ///
238 /// This method can be called multiple times to re-create the swap chain.
239 void CreateSwapChain()
240 {
241 assert(m_dxgi_factory);
242 assert(m_command_queue);
243
244 SafeRelease(m_swap_chain);
245
246 DXGI_SWAP_CHAIN_DESC1 desc
247 {
248 .Width = static_cast<UINT>(m_settings.width),
249 .Height = static_cast<UINT>(m_settings.height),
250 .Format = DXGI_FORMAT_R8G8B8A8_UNORM,
251 .SampleDesc = DXGI_SAMPLE_DESC
252 {
253 .Count = 1,
254 .Quality = 0,
255 },
256 .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
257 .BufferCount = SWAP_CHAIN_BUFFER_COUNT,
258 .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
259 };
260 ComPtr<IDXGISwapChain1> swap_chain;
261 ThrowIfFailed(m_dxgi_factory->CreateSwapChainForHwnd(
262 m_command_queue.Get(), // Swap chain uses queue to perform flush.
263 m_window->GetWindowHandle(),
264 &desc,
265 /*pFullScreenDesc=*/nullptr, // Running in windowed mode.
266 /*pRestrictToOutput=*/nullptr,
267 &swap_chain));
268 ThrowIfFailed(swap_chain.As(&m_swap_chain));
269 }
270
271 /// Creates RTVs for all of the swap chain's buffers.
272 void CreateSwapChainBufferRenderTargetViews()
273 {
274 assert(m_device);
275 assert(m_swap_chain);
276 assert(m_rtv_heap);
277
278 // Create the new buffer views.
279 CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_heap_handle(
280 m_rtv_heap->GetCPUDescriptorHandleForHeapStart());
281 for (int i = 0; i < SWAP_CHAIN_BUFFER_COUNT; ++i)
282 {
283 ThrowIfFailed(m_swap_chain->GetBuffer(
284 i, IID_PPV_ARGS(&m_swap_chain_buffer[i])));
285
286 m_device->CreateRenderTargetView(
287 m_swap_chain_buffer[i].Get(), /*pDesc=*/nullptr, rtv_heap_handle);
288
289 rtv_heap_handle.Offset(1, m_rtv_descriptor_size);
290 }
291 }
292
293 /// Creates a depth/stencil buffer and its view.
294 void CreateDepthStencilBufferAndView()
295 {
296 assert(m_device);
297
298 const D3D12_RESOURCE_DESC depth_stencil_desc
299 {
300 .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
301 .Alignment = 0,
302 .Width = static_cast<UINT64>(m_settings.width),
303 .Height = static_cast<UINT>(m_settings.height),
304 .DepthOrArraySize = 1,
305 .MipLevels = 1,
306 .Format = DXGI_FORMAT_D24_UNORM_S8_UINT,
307 .SampleDesc = DXGI_SAMPLE_DESC
308 {
309 .Count = 1,
310 .Quality = 0,
311 },
312 .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
313 .Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL,
314 };
315 const D3D12_CLEAR_VALUE opt_clear_value
316 {
317 .Format = depth_stencil_desc.Format,
318 .DepthStencil = D3D12_DEPTH_STENCIL_VALUE
319 {
320 .Depth = 1.0f,
321 .Stencil = 0,
322 },
323 };
324 const CD3DX12_HEAP_PROPERTIES depth_stencil_heap_properties(
325 D3D12_HEAP_TYPE_DEFAULT);
326 ThrowIfFailed(m_device->CreateCommittedResource(
327 &depth_stencil_heap_properties,
328 D3D12_HEAP_FLAG_NONE,
329 &depth_stencil_desc,
330 D3D12_RESOURCE_STATE_COMMON,
331 &opt_clear_value,
332 IID_PPV_ARGS(&m_depth_stencil_buffer)));
333
334 m_device->CreateDepthStencilView(
335 m_depth_stencil_buffer.Get(),
336 /*pDesc=*/nullptr,
337 GetDepthStencilView());
338 }
339
340 ID3D12Resource* GetCurrentBackBuffer() const
341 {
342 return m_swap_chain_buffer[m_swap_chain->GetCurrentBackBufferIndex()].Get();
343 }
344
345 D3D12_CPU_DESCRIPTOR_HANDLE GetCurrentBackBufferView() const
346 {
347 assert(m_rtv_heap);
348 assert(m_rtv_descriptor_size > 0);
349 return CD3DX12_CPU_DESCRIPTOR_HANDLE(
350 m_rtv_heap->GetCPUDescriptorHandleForHeapStart(),
351 m_swap_chain->GetCurrentBackBufferIndex(),
352 m_rtv_descriptor_size);
353 }
354
355 D3D12_CPU_DESCRIPTOR_HANDLE GetDepthStencilView() const
356 {
357 assert(m_dsv_heap);
358 return m_dsv_heap->GetCPUDescriptorHandleForHeapStart();
359 }
360
361private:
362 static constexpr int SWAP_CHAIN_BUFFER_COUNT = 2; // Double-buffering.
363
364 Window* m_window = nullptr;
365 D3DSettings m_settings;
366
367 ComPtr<IDXGIFactory4> m_dxgi_factory;
368 ComPtr<ID3D12Device> m_device;
369
370 ComPtr<ID3D12CommandQueue> m_command_queue;
371 ComPtr<ID3D12CommandAllocator> m_command_allocator;
372 ComPtr<ID3D12GraphicsCommandList> m_command_list;
373
374 ComPtr<IDXGISwapChain3> m_swap_chain;
375
376 ComPtr<ID3D12DescriptorHeap> m_rtv_heap;
377 ComPtr<ID3D12DescriptorHeap> m_dsv_heap;
378
379 ComPtr<ID3D12Resource> m_swap_chain_buffer[SWAP_CHAIN_BUFFER_COUNT];
380 ComPtr<ID3D12Resource> m_depth_stencil_buffer;
381
382 ComPtr<ID3D12Fence> m_fence;
383 HANDLE m_fence_event = 0;
384 UINT64 m_fence_value = 0;
385
386 UINT m_rtv_descriptor_size = 0;
387 UINT m_dsv_descriptor_size = 0;
388 UINT m_cbv_descriptor_size = 0;
389};
390
391int main()
392{
393 try
394 {
395 const D3DSettings settings =
396 {
397 // TODO: use 960x600 or 1920x1200 depending on native resolution.
398 .width = 1920,
399 .height = 1200,
400 };
401
402 if (!WindowInitialise())
403 {
404 THROW("Failed to initialise the window subsystem");
405 }
406 {
407 Window window;
408 if (!window.Initialise(
409 settings.width, settings.height, /*title=*/"D3D Application"))
410 {
411 THROW(GetWindowError());
412 }
413
414 D3D d3d;
415 d3d.Initialise(&window, settings);
416
417 while (!window.ShouldClose())
418 {
419 window.Update();
420 d3d.Render();
421 Sleep(10);
422 }
423 }
424 WindowTerminate();
425
426 return 0;
427 }
428 catch (const std::exception& e)
429 {
430 fprintf(stderr, "Exception caught: %s\n", e.what());
431 return 1;
432 }
433}