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