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