1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package d3d12_triangle
17
18import "core:fmt"
19import "core:mem"
20import "core:sys/windows"
21import "core:os"
22import sdl "vendor:sdl2"
23import d3d12 "vendor:directx/d3d12"
24import dxgi "vendor:directx/dxgi"
25import d3dc "vendor:directx/d3d_compiler"
26
27NUM_RENDERTARGETS :: 2
28
29check :: proc(res: d3d12.HRESULT, message: string) {
30 if (res >= 0) {
31 return
32 }
33
34 fmt.printf("%v. Error code: %0x\n", message, u32(res))
35 os.exit(-1)
36}
37
38main :: proc() {
39
40
41 if err := sdl.Init({.VIDEO}); err != 0 {
42 fmt.eprintln(err)
43 return
44 }
45
46 defer sdl.Quit()
47 wx := i32(640)
48 wy := i32(480)
49 window := sdl.CreateWindow("d3d12 triangle", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, wx, wy, { .ALLOW_HIGHDPI, .SHOWN, .RESIZABLE })
50
51 if window == nil {
52 fmt.eprintln(sdl.GetError())
53 return
54 }
55
56 defer sdl.DestroyWindow(window)
57 hr: d3d12.HRESULT
58
59
60 factory: ^dxgi.IFactory4
61
62 {
63 flags: dxgi.CREATE_FACTORY
64
65 when ODIN_DEBUG {
66 flags += { .DEBUG }
67 }
68
69 hr = dxgi.CreateDXGIFactory2(flags, dxgi.IFactory4_UUID, cast(^rawptr)&factory)
70 check(hr, "Failed creating factory")
71 }
72
73
74 adapter: ^dxgi.IAdapter1
75 error_not_found := dxgi.HRESULT(-142213123)
76
77 for i: u32 = 0; factory->EnumAdapters1(i, &adapter) != error_not_found; i += 1 {
78 desc: dxgi.ADAPTER_DESC1
79 adapter->GetDesc1(&desc)
80 if .SOFTWARE in desc.Flags {
81 continue
82 }
83
84 if d3d12.CreateDevice((^dxgi.IUnknown)(adapter), ._12_0, d3d12.IDevice_UUID, nil) >= 0 {
85 break
86 } else {
87 fmt.println("Failed to create device")
88 }
89 }
90
91 if adapter == nil {
92 fmt.println("Could not find hardware adapter")
93 return
94 }
95
96
97 device: ^d3d12.IDevice
98 hr = d3d12.CreateDevice((^dxgi.IUnknown)(adapter), ._12_0, d3d12.IDevice_UUID, (^rawptr)(&device))
99 check(hr, "Failed to create device")
100 queue: ^d3d12.ICommandQueue
101
102 {
103 desc := d3d12.COMMAND_QUEUE_DESC {
104 Type = .DIRECT,
105 }
106
107 hr = device->CreateCommandQueue(&desc, d3d12.ICommandQueue_UUID, (^rawptr)(&queue))
108 check(hr, "Failed creating command queue")
109 }
110
111
112 window_info: sdl.SysWMinfo
113 sdl.GetWindowWMInfo(window, &window_info)
114 window_handle := dxgi.HWND(window_info.info.win.window)
115
116
117 swapchain: ^dxgi.ISwapChain3
118
119 {
120 desc := dxgi.SWAP_CHAIN_DESC1 {
121 Width = u32(wx),
122 Height = u32(wy),
123 Format = .R8G8B8A8_UNORM,
124 SampleDesc = {
125 Count = 1,
126 Quality = 0,
127 },
128 BufferUsage = {.RENDER_TARGET_OUTPUT},
129 BufferCount = NUM_RENDERTARGETS,
130 Scaling = .NONE,
131 SwapEffect = .FLIP_DISCARD,
132 AlphaMode = .UNSPECIFIED,
133 }
134
135 hr = factory->CreateSwapChainForHwnd((^dxgi.IUnknown)(queue), window_handle, &desc, nil, nil, (^^dxgi.ISwapChain1)(&swapchain))
136 check(hr, "Failed to create swap chain")
137 }
138
139 frame_index := swapchain->GetCurrentBackBufferIndex()
140
141
142 rtv_descriptor_heap: ^d3d12.IDescriptorHeap
143
144 {
145 desc := d3d12.DESCRIPTOR_HEAP_DESC {
146 NumDescriptors = NUM_RENDERTARGETS,
147 Type = .RTV,
148 Flags = {},
149 }
150
151 hr = device->CreateDescriptorHeap(&desc, d3d12.IDescriptorHeap_UUID, (^rawptr)(&rtv_descriptor_heap))
152 check(hr, "Failed creating descriptor heap")
153 }
154
155
156 targets: [NUM_RENDERTARGETS]^d3d12.IResource
157
158 {
159 rtv_descriptor_size: u32 = device->GetDescriptorHandleIncrementSize(.RTV)
160
161 rtv_descriptor_handle: d3d12.CPU_DESCRIPTOR_HANDLE
162 rtv_descriptor_heap->GetCPUDescriptorHandleForHeapStart(&rtv_descriptor_handle)
163
164 for i :u32= 0; i < NUM_RENDERTARGETS; i += 1 {
165 hr = swapchain->GetBuffer(i, d3d12.IResource_UUID, (^rawptr)(&targets[i]))
166 check(hr, "Failed getting render target")
167 device->CreateRenderTargetView(targets[i], nil, rtv_descriptor_handle)
168 rtv_descriptor_handle.ptr += uint(rtv_descriptor_size)
169 }
170 }
171
172
173 command_allocator: ^d3d12.ICommandAllocator
174 hr = device->CreateCommandAllocator(.DIRECT, d3d12.ICommandAllocator_UUID, (^rawptr)(&command_allocator))
175 check(hr, "Failed creating command allocator")
176
177
178
179
180
181
182
183
184 root_signature: ^d3d12.IRootSignature
185
186 {
187 desc := d3d12.VERSIONED_ROOT_SIGNATURE_DESC {
188 Version = ._1_0,
189 }
190
191 desc.Desc_1_0.Flags = {.ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT}
192 serialized_desc: ^d3d12.IBlob
193 hr = d3d12.SerializeVersionedRootSignature(&desc, &serialized_desc, nil)
194 check(hr, "Failed to serialize root signature")
195 hr = device->CreateRootSignature(0, serialized_desc->GetBufferPointer(), serialized_desc->GetBufferSize(), d3d12.IRootSignature_UUID, (^rawptr)(&root_signature))
196 check(hr, "Failed creating root signature")
197 serialized_desc->Release()
198 }
199
200
201 pipeline: ^d3d12.IPipelineState
202
203 {
204
205 data :cstring=
206 `struct PSInput {
207 float4 position : SV_POSITION;
208 float4 color : COLOR;
209 };
210 PSInput VSMain(float4 position : POSITION0, float4 color : COLOR0) {
211 PSInput result;
212 result.position = position;
213 result.color = color;
214 return result;
215 }
216 float4 PSMain(PSInput input) : SV_TARGET {
217 return input.color;
218 };`
219
220 data_size: uint = len(data)
221
222 compile_flags: u32 = 0
223 when ODIN_DEBUG {
224 compile_flags |= u32(d3dc.D3DCOMPILE.DEBUG)
225 compile_flags |= u32(d3dc.D3DCOMPILE.SKIP_OPTIMIZATION)
226 }
227
228 vs: ^d3d12.IBlob = nil
229 ps: ^d3d12.IBlob = nil
230
231 hr = d3dc.Compile(rawptr(data), data_size, nil, nil, nil, "VSMain", "vs_4_0", compile_flags, 0, &vs, nil)
232 check(hr, "Failed to compile vertex shader")
233
234 hr =d3dc.Compile(rawptr(data), data_size, nil, nil, nil, "PSMain", "ps_4_0", compile_flags, 0, &ps, nil)
235 check(hr, "Failed to compile pixel shader")
236
237
238 vertex_format: []d3d12.INPUT_ELEMENT_DESC = {
239 {
240 SemanticName = "POSITION",
241 Format = .R32G32B32_FLOAT,
242 InputSlotClass = .PER_VERTEX_DATA,
243 },
244 {
245 SemanticName = "COLOR",
246 Format = .R32G32B32A32_FLOAT,
247 AlignedByteOffset = size_of(f32) * 3,
248 InputSlotClass = .PER_VERTEX_DATA,
249 },
250 }
251
252 default_blend_state := d3d12.RENDER_TARGET_BLEND_DESC {
253 BlendEnable = false,
254 LogicOpEnable = false,
255
256 SrcBlend = .ONE,
257 DestBlend = .ZERO,
258 BlendOp = .ADD,
259
260 SrcBlendAlpha = .ONE,
261 DestBlendAlpha = .ZERO,
262 BlendOpAlpha = .ADD,
263
264 LogicOp = .NOOP,
265 RenderTargetWriteMask = u8(d3d12.COLOR_WRITE_ENABLE_ALL),
266 }
267
268 pipeline_state_desc := d3d12.GRAPHICS_PIPELINE_STATE_DESC {
269 pRootSignature = root_signature,
270 VS = {
271 pShaderBytecode = vs->GetBufferPointer(),
272 BytecodeLength = vs->GetBufferSize(),
273 },
274 PS = {
275 pShaderBytecode = ps->GetBufferPointer(),
276 BytecodeLength = ps->GetBufferSize(),
277 },
278 StreamOutput = {},
279 BlendState = {
280 AlphaToCoverageEnable = false,
281 IndependentBlendEnable = false,
282 RenderTarget = { 0 = default_blend_state, 1..<7 = {} },
283 },
284 SampleMask = 0xFFFFFFFF,
285 RasterizerState = {
286 FillMode = .SOLID,
287 CullMode = .BACK,
288 FrontCounterClockwise = false,
289 DepthBias = 0,
290 DepthBiasClamp = 0,
291 SlopeScaledDepthBias = 0,
292 DepthClipEnable = true,
293 MultisampleEnable = false,
294 AntialiasedLineEnable = false,
295 ForcedSampleCount = 0,
296 ConservativeRaster = .OFF,
297 },
298 DepthStencilState = {
299 DepthEnable = false,
300 StencilEnable = false,
301 },
302 InputLayout = {
303 pInputElementDescs = &vertex_format[0],
304 NumElements = u32(len(vertex_format)),
305 },
306 PrimitiveTopologyType = .TRIANGLE,
307 NumRenderTargets = 1,
308 RTVFormats = { 0 = .R8G8B8A8_UNORM, 1..<7 = .UNKNOWN },
309 DSVFormat = .UNKNOWN,
310 SampleDesc = {
311 Count = 1,
312 Quality = 0,
313 },
314 }
315
316 hr = device->CreateGraphicsPipelineState(&pipeline_state_desc, d3d12.IPipelineState_UUID, (^rawptr)(&pipeline))
317 check(hr, "Pipeline creation failed")
318
319 vs->Release()
320 ps->Release()
321 }
322
323
324 cmdlist: ^d3d12.IGraphicsCommandList
325 hr = device->CreateCommandList(0, .DIRECT, command_allocator, pipeline, d3d12.ICommandList_UUID, (^rawptr)(&cmdlist))
326 check(hr, "Failed to create command list")
327 hr = cmdlist->Close()
328 check(hr, "Failed to close command list")
329
330 vertex_buffer: ^d3d12.IResource
331 vertex_buffer_view: d3d12.VERTEX_BUFFER_VIEW
332
333 {
334
335 vertices := [?]f32 {
336
337 0.0 , 0.5, 0.0, 1,0,0,0,
338 0.5, -0.5, 0.0, 0,1,0,0,
339 -0.5, -0.5, 0.0, 0,0,1,0,
340 }
341
342 heap_props := d3d12.HEAP_PROPERTIES {
343 Type = .UPLOAD,
344 }
345
346 vertex_buffer_size := len(vertices) * size_of(vertices[0])
347
348 resource_desc := d3d12.RESOURCE_DESC {
349 Dimension = .BUFFER,
350 Alignment = 0,
351 Width = u64(vertex_buffer_size),
352 Height = 1,
353 DepthOrArraySize = 1,
354 MipLevels = 1,
355 Format = .UNKNOWN,
356 SampleDesc = { Count = 1, Quality = 0 },
357 Layout = .ROW_MAJOR,
358 Flags = {},
359 }
360
361 hr = device->CreateCommittedResource(&heap_props, {}, &resource_desc, d3d12.RESOURCE_STATE_GENERIC_READ, nil, d3d12.IResource_UUID, (^rawptr)(&vertex_buffer))
362 check(hr, "Failed creating vertex buffer")
363
364 gpu_data: rawptr
365 read_range: d3d12.RANGE
366
367 hr = vertex_buffer->Map(0, &read_range, &gpu_data)
368 check(hr, "Failed creating verex buffer resource")
369
370 mem.copy(gpu_data, &vertices[0], vertex_buffer_size)
371 vertex_buffer->Unmap(0, nil)
372
373 vertex_buffer_view = d3d12.VERTEX_BUFFER_VIEW {
374 BufferLocation = vertex_buffer->GetGPUVirtualAddress(),
375 StrideInBytes = u32(vertex_buffer_size/3),
376 SizeInBytes = u32(vertex_buffer_size),
377 }
378 }
379
380
381 fence_value: u64
382 fence: ^d3d12.IFence
383 fence_event: windows.HANDLE
384
385 {
386 hr = device->CreateFence(fence_value, {}, d3d12.IFence_UUID, (^rawptr)(&fence))
387 check(hr, "Failed to create fence")
388 fence_value += 1
389 manual_reset: windows.BOOL = false
390 initial_state: windows.BOOL = false
391 fence_event = windows.CreateEventW(nil, manual_reset, initial_state, nil)
392 if fence_event == nil {
393 fmt.println("Failed to create fence event")
394 return
395 }
396 }
397
398 main_loop: for {
399 for e: sdl.Event; sdl.PollEvent(&e); {
400 #partial switch e.type {
401 case .QUIT:
402 break main_loop
403 case .WINDOWEVENT:
404
405 if e.window.event == .EXPOSED {
406 hr = command_allocator->Reset()
407 check(hr, "Failed resetting command allocator")
408
409 hr = cmdlist->Reset(command_allocator, pipeline)
410 check(hr, "Failed to reset command list")
411
412 viewport := d3d12.VIEWPORT {
413 Width = f32(wx),
414 Height = f32(wy),
415 MinDepth = 0,
416 MaxDepth = 1,
417 }
418
419 scissor_rect := d3d12.RECT {
420 left = 0, right = wx,
421 top = 0, bottom = wy,
422 }
423
424
425 cmdlist->SetGraphicsRootSignature(root_signature)
426 cmdlist->RSSetViewports(1, &viewport)
427 cmdlist->RSSetScissorRects(1, &scissor_rect)
428
429 to_render_target_barrier := d3d12.RESOURCE_BARRIER {
430 Type = .TRANSITION,
431 Flags = {},
432 }
433
434 to_render_target_barrier.Transition = {
435 pResource = targets[frame_index],
436 StateBefore = d3d12.RESOURCE_STATE_PRESENT,
437 StateAfter = {.RENDER_TARGET},
438 Subresource = d3d12.RESOURCE_BARRIER_ALL_SUBRESOURCES,
439 }
440
441 cmdlist->ResourceBarrier(1, &to_render_target_barrier)
442
443 rtv_handle: d3d12.CPU_DESCRIPTOR_HANDLE
444 rtv_descriptor_heap->GetCPUDescriptorHandleForHeapStart(&rtv_handle)
445
446 if (frame_index > 0) {
447 s := device->GetDescriptorHandleIncrementSize(.RTV)
448 rtv_handle.ptr += uint(frame_index * s)
449 }
450
451 cmdlist->OMSetRenderTargets(1, &rtv_handle, false, nil)
452
453
454 clearcolor := [?]f32 { 0.05, 0.05, 0.05, 1.0 }
455 cmdlist->ClearRenderTargetView(rtv_handle, &clearcolor, 0, nil)
456
457
458 cmdlist->IASetPrimitiveTopology(.TRIANGLELIST)
459 cmdlist->IASetVertexBuffers(0, 1, &vertex_buffer_view)
460 cmdlist->DrawInstanced(3, 1, 0, 0)
461
462 to_present_barrier := to_render_target_barrier
463 to_present_barrier.Transition.StateBefore = {.RENDER_TARGET}
464 to_present_barrier.Transition.StateAfter = d3d12.RESOURCE_STATE_PRESENT
465
466 cmdlist->ResourceBarrier(1, &to_present_barrier)
467
468 hr = cmdlist->Close()
469 check(hr, "Failed to close command list")
470
471
472 cmdlists := [?]^d3d12.IGraphicsCommandList { cmdlist }
473 queue->ExecuteCommandLists(len(cmdlists), (^^d3d12.ICommandList)(&cmdlists[0]))
474
475
476 {
477 flags: dxgi.PRESENT
478 params: dxgi.PRESENT_PARAMETERS
479 hr = swapchain->Present1(1, flags, ¶ms)
480 check(hr, "Present failed")
481 }
482
483
484 {
485 current_fence_value := fence_value
486
487 hr = queue->Signal(fence, current_fence_value)
488 check(hr, "Failed to signal fence")
489
490 fence_value += 1
491 completed := fence->GetCompletedValue()
492
493 if completed < current_fence_value {
494 hr = fence->SetEventOnCompletion(current_fence_value, fence_event)
495 check(hr, "Failed to set event on completion flag")
496 windows.WaitForSingleObject(fence_event, windows.INFINITE)
497 }
498
499 frame_index = swapchain->GetCurrentBackBufferIndex()
500 }
501 }
502 }
503 }
504 }
505}