1 /*
2 * Copyright 2016 Henri Verbeet for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 /*
20 * This application contains code derived from Microsoft's "HelloTriangle"
21 * demo, the license for which follows:
22 *
23 * Copyright (c) 2015 Microsoft
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a
26 * copy of this software and associated documentation files (the "Software"),
27 * to deal in the Software without restriction, including without limitation
28 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
29 * and/or sell copies of the Software, and to permit persons to whom the
30 * Software is furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
40 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
41 * DEALINGS IN THE SOFTWARE.
42 */
43
44 #define INITGUID
45 #include <assert.h>
46 #include "demo.h"
47
48 #include "triangle_vs.h"
49 #include "triangle_ps.h"
50
51 struct cxt_fence
52 {
53 ID3D12Fence *fence;
54 UINT64 value;
55 HANDLE event;
56 };
57
58 struct cx_triangle
59 {
60 struct demo demo;
61
62 struct demo_window *window;
63
64 unsigned int width;
65 unsigned int height;
66 float aspect_ratio;
67
68 D3D12_VIEWPORT vp;
69 D3D12_RECT scissor_rect;
70
71 ID3D12Device *device;
72 ID3D12CommandQueue *command_queue;
73 struct demo_swapchain *swapchain;
74 ID3D12DescriptorHeap *rtv_heap;
75 unsigned int rtv_descriptor_size;
76 ID3D12Resource *render_targets[3];
77 ID3D12CommandAllocator *command_allocator;
78
79 ID3D12RootSignature *root_signature;
80 ID3D12PipelineState *pipeline_state;
81 ID3D12GraphicsCommandList *command_list;
82 ID3D12Resource *vb;
83 D3D12_VERTEX_BUFFER_VIEW vbv;
84
85 unsigned int frame_idx;
86 struct cxt_fence fence;
87 };
88
cxt_populate_command_list(struct cx_triangle * cxt)89 static void cxt_populate_command_list(struct cx_triangle *cxt)
90 {
91 static const float clear_colour[] = {0.0f, 0.2f, 0.4f, 1.0f};
92
93 D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
94 D3D12_RESOURCE_BARRIER barrier;
95 HRESULT hr;
96
97 hr = ID3D12CommandAllocator_Reset(cxt->command_allocator);
98 assert(SUCCEEDED(hr));
99
100 hr = ID3D12GraphicsCommandList_Reset(cxt->command_list, cxt->command_allocator, cxt->pipeline_state);
101 assert(SUCCEEDED(hr));
102
103 ID3D12GraphicsCommandList_SetGraphicsRootSignature(cxt->command_list, cxt->root_signature);
104 ID3D12GraphicsCommandList_RSSetViewports(cxt->command_list, 1, &cxt->vp);
105 ID3D12GraphicsCommandList_RSSetScissorRects(cxt->command_list, 1, &cxt->scissor_rect);
106
107 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
108 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
109 barrier.Transition.pResource = cxt->render_targets[cxt->frame_idx];
110 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
111 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
112 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
113 ID3D12GraphicsCommandList_ResourceBarrier(cxt->command_list, 1, &barrier);
114
115 rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cxt->rtv_heap);
116 rtv_handle.ptr += cxt->frame_idx * cxt->rtv_descriptor_size;
117 ID3D12GraphicsCommandList_OMSetRenderTargets(cxt->command_list, 1, &rtv_handle, FALSE, NULL);
118
119 ID3D12GraphicsCommandList_ClearRenderTargetView(cxt->command_list, rtv_handle, clear_colour, 0, NULL);
120 ID3D12GraphicsCommandList_IASetPrimitiveTopology(cxt->command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
121 ID3D12GraphicsCommandList_IASetVertexBuffers(cxt->command_list, 0, 1, &cxt->vbv);
122 ID3D12GraphicsCommandList_DrawInstanced(cxt->command_list, 3, 1, 0, 0);
123
124 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
125 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
126 ID3D12GraphicsCommandList_ResourceBarrier(cxt->command_list, 1, &barrier);
127
128 hr = ID3D12GraphicsCommandList_Close(cxt->command_list);
129 assert(SUCCEEDED(hr));
130 }
131
cxt_wait_for_previous_frame(struct cx_triangle * cxt)132 static void cxt_wait_for_previous_frame(struct cx_triangle *cxt)
133 {
134 struct cxt_fence *fence = &cxt->fence;
135 const UINT64 v = fence->value;
136 HRESULT hr;
137
138 hr = ID3D12CommandQueue_Signal(cxt->command_queue, fence->fence, v);
139 assert(SUCCEEDED(hr));
140
141 ++fence->value;
142
143 if (ID3D12Fence_GetCompletedValue(fence->fence) < v)
144 {
145 hr = ID3D12Fence_SetEventOnCompletion(fence->fence, v, fence->event);
146 assert(SUCCEEDED(hr));
147 demo_wait_event(fence->event, INFINITE);
148 }
149
150 cxt->frame_idx = demo_swapchain_get_current_back_buffer_index(cxt->swapchain);
151 }
152
cxt_render_frame(struct demo_window * window,void * user_data)153 static void cxt_render_frame(struct demo_window *window, void *user_data)
154 {
155 struct cx_triangle *cxt = user_data;
156
157 cxt_populate_command_list(cxt);
158 ID3D12CommandQueue_ExecuteCommandLists(cxt->command_queue, 1, (ID3D12CommandList **)&cxt->command_list);
159 demo_swapchain_present(cxt->swapchain);
160 cxt_wait_for_previous_frame(cxt);
161 }
162
cxt_destroy_pipeline(struct cx_triangle * cxt)163 static void cxt_destroy_pipeline(struct cx_triangle *cxt)
164 {
165 unsigned int i;
166
167 ID3D12CommandAllocator_Release(cxt->command_allocator);
168 for (i = 0; i < ARRAY_SIZE(cxt->render_targets); ++i)
169 {
170 ID3D12Resource_Release(cxt->render_targets[i]);
171 }
172 ID3D12DescriptorHeap_Release(cxt->rtv_heap);
173 demo_swapchain_destroy(cxt->swapchain);
174 ID3D12CommandQueue_Release(cxt->command_queue);
175 ID3D12Device_Release(cxt->device);
176 }
177
cxt_load_pipeline(struct cx_triangle * cxt)178 static void cxt_load_pipeline(struct cx_triangle *cxt)
179 {
180 struct demo_swapchain_desc swapchain_desc;
181 D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc;
182 D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
183 D3D12_COMMAND_QUEUE_DESC queue_desc;
184 unsigned int i;
185 HRESULT hr;
186
187 hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&cxt->device);
188 assert(SUCCEEDED(hr));
189
190 memset(&queue_desc, 0, sizeof(queue_desc));
191 queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
192 queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
193 hr = ID3D12Device_CreateCommandQueue(cxt->device, &queue_desc,
194 &IID_ID3D12CommandQueue, (void **)&cxt->command_queue);
195 assert(SUCCEEDED(hr));
196
197 swapchain_desc.buffer_count = ARRAY_SIZE(cxt->render_targets);
198 swapchain_desc.format = DXGI_FORMAT_B8G8R8A8_UNORM;
199 swapchain_desc.width = cxt->width;
200 swapchain_desc.height = cxt->height;
201 cxt->swapchain = demo_swapchain_create(cxt->command_queue, cxt->window, &swapchain_desc);
202 assert(cxt->swapchain);
203 cxt->frame_idx = demo_swapchain_get_current_back_buffer_index(cxt->swapchain);
204
205 memset(&rtv_heap_desc, 0, sizeof(rtv_heap_desc));
206 rtv_heap_desc.NumDescriptors = ARRAY_SIZE(cxt->render_targets);
207 rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
208 rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
209 hr = ID3D12Device_CreateDescriptorHeap(cxt->device, &rtv_heap_desc,
210 &IID_ID3D12DescriptorHeap, (void **)&cxt->rtv_heap);
211 assert(SUCCEEDED(hr));
212
213 cxt->rtv_descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(cxt->device,
214 D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
215 rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cxt->rtv_heap);
216 for (i = 0; i < ARRAY_SIZE(cxt->render_targets); ++i)
217 {
218 cxt->render_targets[i] = demo_swapchain_get_back_buffer(cxt->swapchain, i);
219 ID3D12Device_CreateRenderTargetView(cxt->device, cxt->render_targets[i], NULL, rtv_handle);
220 rtv_handle.ptr += cxt->rtv_descriptor_size;
221 }
222
223 hr = ID3D12Device_CreateCommandAllocator(cxt->device, D3D12_COMMAND_LIST_TYPE_DIRECT,
224 &IID_ID3D12CommandAllocator, (void **)&cxt->command_allocator);
225 assert(SUCCEEDED(hr));
226 }
227
cxt_fence_destroy(struct cxt_fence * cxt_fence)228 static void cxt_fence_destroy(struct cxt_fence *cxt_fence)
229 {
230 ID3D12Fence_Release(cxt_fence->fence);
231 demo_destroy_event(cxt_fence->event);
232 }
233
cxt_destroy_assets(struct cx_triangle * cxt)234 static void cxt_destroy_assets(struct cx_triangle *cxt)
235 {
236 cxt_fence_destroy(&cxt->fence);
237 ID3D12Resource_Release(cxt->vb);
238 ID3D12GraphicsCommandList_Release(cxt->command_list);
239 ID3D12PipelineState_Release(cxt->pipeline_state);
240 ID3D12RootSignature_Release(cxt->root_signature);
241 }
242
cxt_fence_create(struct cxt_fence * fence,ID3D12Device * device)243 static void cxt_fence_create(struct cxt_fence *fence, ID3D12Device *device)
244 {
245 HRESULT hr;
246
247 hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
248 &IID_ID3D12Fence, (void **)&fence->fence);
249 assert(SUCCEEDED(hr));
250 fence->value = 1;
251 fence->event = demo_create_event();
252 assert(fence->event);
253 }
254
cxt_load_assets(struct cx_triangle * cxt)255 static void cxt_load_assets(struct cx_triangle *cxt)
256 {
257 static const D3D12_INPUT_ELEMENT_DESC il_desc[] =
258 {
259 {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
260 {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
261 };
262
263 const struct
264 {
265 struct demo_vec3 position;
266 struct demo_vec4 colour;
267 }
268 vertices[] =
269 {
270 {{ 0.0f, 0.25f * cxt->aspect_ratio, 0.0f}, {1.0f, 0.0f, 0.0f, 1.0f}},
271 {{ 0.25f, -0.25f * cxt->aspect_ratio, 0.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
272 {{-0.25f, -0.25f * cxt->aspect_ratio, 0.0f}, {0.0f, 0.0f, 1.0f, 1.0f}},
273 };
274
275 D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
276 D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
277 D3D12_RESOURCE_DESC resource_desc;
278 D3D12_HEAP_PROPERTIES heap_desc;
279 D3D12_RANGE read_range = {0, 0};
280 HRESULT hr;
281 void *data;
282
283 memset(&root_signature_desc, 0, sizeof(root_signature_desc));
284 root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
285 hr = demo_create_root_signature(cxt->device, &root_signature_desc, &cxt->root_signature);
286 assert(SUCCEEDED(hr));
287
288 memset(&pso_desc, 0, sizeof(pso_desc));
289 pso_desc.InputLayout.pInputElementDescs = il_desc;
290 pso_desc.InputLayout.NumElements = ARRAY_SIZE(il_desc);
291 pso_desc.pRootSignature = cxt->root_signature;
292 pso_desc.VS.pShaderBytecode = g_vs_main;
293 pso_desc.VS.BytecodeLength = sizeof(g_vs_main);
294 pso_desc.PS.pShaderBytecode = g_ps_main;
295 pso_desc.PS.BytecodeLength = sizeof(g_ps_main);
296 demo_rasterizer_desc_init_default(&pso_desc.RasterizerState);
297 demo_blend_desc_init_default(&pso_desc.BlendState);
298 pso_desc.DepthStencilState.DepthEnable = FALSE;
299 pso_desc.DepthStencilState.StencilEnable = FALSE;
300 pso_desc.SampleMask = UINT_MAX;
301 pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
302 pso_desc.NumRenderTargets = 1;
303 pso_desc.RTVFormats[0] = DXGI_FORMAT_B8G8R8A8_UNORM;
304 pso_desc.SampleDesc.Count = 1;
305 hr = ID3D12Device_CreateGraphicsPipelineState(cxt->device, &pso_desc,
306 &IID_ID3D12PipelineState, (void **)&cxt->pipeline_state);
307 assert(SUCCEEDED(hr));
308
309 hr = ID3D12Device_CreateCommandList(cxt->device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, cxt->command_allocator,
310 cxt->pipeline_state, &IID_ID3D12GraphicsCommandList, (void **)&cxt->command_list);
311 assert(SUCCEEDED(hr));
312
313 hr = ID3D12GraphicsCommandList_Close(cxt->command_list);
314 assert(SUCCEEDED(hr));
315
316 heap_desc.Type = D3D12_HEAP_TYPE_UPLOAD;
317 heap_desc.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
318 heap_desc.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
319 heap_desc.CreationNodeMask = 1;
320 heap_desc.VisibleNodeMask = 1;
321
322 resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
323 resource_desc.Alignment = 0;
324 resource_desc.Width = sizeof(vertices);
325 resource_desc.Height = 1;
326 resource_desc.DepthOrArraySize = 1;
327 resource_desc.MipLevels = 1;
328 resource_desc.Format = DXGI_FORMAT_UNKNOWN;
329 resource_desc.SampleDesc.Count = 1;
330 resource_desc.SampleDesc.Quality = 0;
331 resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
332 resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
333
334 hr = ID3D12Device_CreateCommittedResource(cxt->device, &heap_desc, D3D12_HEAP_FLAG_NONE, &resource_desc,
335 D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&cxt->vb);
336 assert(SUCCEEDED(hr));
337
338 hr = ID3D12Resource_Map(cxt->vb, 0, &read_range, &data);
339 assert(SUCCEEDED(hr));
340 memcpy(data, vertices, sizeof(vertices));
341 ID3D12Resource_Unmap(cxt->vb, 0, NULL);
342
343 cxt->vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(cxt->vb);
344 cxt->vbv.StrideInBytes = sizeof(*vertices);
345 cxt->vbv.SizeInBytes = sizeof(vertices);
346
347 cxt_fence_create(&cxt->fence, cxt->device);
348 cxt_wait_for_previous_frame(cxt);
349 }
350
cxt_key_press(struct demo_window * window,demo_key key,void * user_data)351 static void cxt_key_press(struct demo_window *window, demo_key key, void *user_data)
352 {
353 if (key == DEMO_KEY_ESCAPE)
354 demo_window_destroy(window);
355 }
356
cxt_main(void)357 static int cxt_main(void)
358 {
359 unsigned int width = 640, height = 480;
360 struct cx_triangle cxt;
361
362 memset(&cxt, 0, sizeof(cxt));
363
364 if (!demo_init(&cxt.demo, NULL))
365 return EXIT_FAILURE;
366
367 cxt.window = demo_window_create(&cxt.demo, "Vkd3d Triangle", width, height, &cxt);
368 demo_window_set_expose_func(cxt.window, cxt_render_frame);
369 demo_window_set_key_press_func(cxt.window, cxt_key_press);
370
371 cxt.width = width;
372 cxt.height = height;
373 cxt.aspect_ratio = (float)width / (float)height;
374
375 cxt.vp.Width = (float)width;
376 cxt.vp.Height = (float)height;
377 cxt.vp.MaxDepth = 1.0f;
378
379 cxt.scissor_rect.right = width;
380 cxt.scissor_rect.bottom = height;
381
382 cxt_load_pipeline(&cxt);
383 cxt_load_assets(&cxt);
384
385 demo_process_events(&cxt.demo);
386
387 cxt_wait_for_previous_frame(&cxt);
388 cxt_destroy_assets(&cxt);
389 cxt_destroy_pipeline(&cxt);
390 demo_cleanup(&cxt.demo);
391
392 return EXIT_SUCCESS;
393 }
394
395 #ifdef _WIN32
wmain(void)396 int wmain(void)
397 #else
398 int main(void)
399 #endif
400 {
401 return cxt_main();
402 }
403