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