1 /*
2 * Copyright (c) 2015-2016 The Khronos Group Inc.
3 * Copyright (c) 2015-2016 Valve Corporation
4 * Copyright (c) 2015-2016 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and/or associated documentation files (the "Materials"), to
8 * deal in the Materials without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Materials, and to permit persons to whom the Materials are
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice(s) and this permission notice shall be included in
14 * all copies or substantial portions of the Materials.
15 *
16 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 *
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23 * USE OR OTHER DEALINGS IN THE MATERIALS.
24 *
25 * Author: Chia-I Wu <olvaffe@gmail.com>
26 * Author: Cody Northrop <cody@lunarg.com>
27 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
28 * Author: Ian Elliott <ian@LunarG.com>
29 * Author: Jon Ashburn <jon@lunarg.com>
30 * Author: Piers Daniell <pdaniell@nvidia.com>
31 */
32 /*
33 * Draw a textured triangle with depth testing. This is written against Intel
34 * ICD. It does not do state transition nor object memory binding like it
35 * should. It also does no error checking.
36 */
37
38 #ifndef _MSC_VER
39 #define _ISOC11_SOURCE /* for aligned_alloc() */
40 #endif
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <stdbool.h>
46 #include <assert.h>
47
48 #ifdef _WIN32
49 #pragma comment(linker, "/subsystem:windows")
50 #define APP_NAME_STR_LEN 80
51 #endif // _WIN32
52
53 #include <vulkan/vulkan.h>
54
55 #define DEMO_TEXTURE_COUNT 1
56 #define VERTEX_BUFFER_BIND_ID 0
57 #define APP_SHORT_NAME "tri"
58 #define APP_LONG_NAME "The Vulkan Triangle Demo Program"
59
60 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
61
62 #if defined(NDEBUG) && defined(__GNUC__)
63 #define U_ASSERT_ONLY __attribute__((unused))
64 #else
65 #define U_ASSERT_ONLY
66 #endif
67
68 #ifdef _WIN32
69 #define ERR_EXIT(err_msg, err_class) \
70 do { \
71 MessageBox(NULL, err_msg, err_class, MB_OK); \
72 exit(1); \
73 } while (0)
74 #else // _WIN32
75
76 #define ERR_EXIT(err_msg, err_class) \
77 do { \
78 printf(err_msg); \
79 fflush(stdout); \
80 exit(1); \
81 } while (0)
82 #endif // _WIN32
83
84 #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
85 { \
86 demo->fp##entrypoint = \
87 (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \
88 if (demo->fp##entrypoint == NULL) { \
89 ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \
90 "vkGetInstanceProcAddr Failure"); \
91 } \
92 }
93
94 #define GET_DEVICE_PROC_ADDR(dev, entrypoint) \
95 { \
96 demo->fp##entrypoint = \
97 (PFN_vk##entrypoint)vkGetDeviceProcAddr(dev, "vk" #entrypoint); \
98 if (demo->fp##entrypoint == NULL) { \
99 ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \
100 "vkGetDeviceProcAddr Failure"); \
101 } \
102 }
103
104 struct texture_object {
105 VkSampler sampler;
106
107 VkImage image;
108 VkImageLayout imageLayout;
109
110 VkDeviceMemory mem;
111 VkImageView view;
112 int32_t tex_width, tex_height;
113 };
114
115 VKAPI_ATTR VkBool32 VKAPI_CALL
dbgFunc(VkFlags msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)116 dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
117 uint64_t srcObject, size_t location, int32_t msgCode,
118 const char *pLayerPrefix, const char *pMsg, void *pUserData) {
119 char *message = (char *)malloc(strlen(pMsg) + 100);
120
121 assert(message);
122
123 if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
124 sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode,
125 pMsg);
126 } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
127 sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode,
128 pMsg);
129 } else {
130 return false;
131 }
132
133 #ifdef _WIN32
134 MessageBox(NULL, message, "Alert", MB_OK);
135 #else
136 printf("%s\n", message);
137 fflush(stdout);
138 #endif
139 free(message);
140
141 /*
142 * false indicates that layer should not bail-out of an
143 * API call that had validation failures. This may mean that the
144 * app dies inside the driver due to invalid parameter(s).
145 * That's what would happen without validation layers, so we'll
146 * keep that behavior here.
147 */
148 return false;
149 }
150
151 typedef struct _SwapchainBuffers {
152 VkImage image;
153 VkCommandBuffer cmd;
154 VkImageView view;
155 } SwapchainBuffers;
156
157 struct demo {
158 #ifdef _WIN32
159 #define APP_NAME_STR_LEN 80
160 HINSTANCE connection; // hInstance - Windows Instance
161 char name[APP_NAME_STR_LEN]; // Name to put on the window/icon
162 HWND window; // hWnd - window handle
163 #else // _WIN32
164 xcb_connection_t *connection;
165 xcb_screen_t *screen;
166 xcb_window_t window;
167 xcb_intern_atom_reply_t *atom_wm_delete_window;
168 #endif // _WIN32
169 VkSurfaceKHR surface;
170 bool prepared;
171 bool use_staging_buffer;
172
173 VkInstance inst;
174 VkPhysicalDevice gpu;
175 VkDevice device;
176 VkQueue queue;
177 VkPhysicalDeviceProperties gpu_props;
178 VkQueueFamilyProperties *queue_props;
179 uint32_t graphics_queue_node_index;
180
181 uint32_t enabled_extension_count;
182 uint32_t enabled_layer_count;
183 char *extension_names[64];
184 char *device_validation_layers[64];
185
186 int width, height;
187 VkFormat format;
188 VkColorSpaceKHR color_space;
189
190 PFN_vkGetPhysicalDeviceSurfaceSupportKHR
191 fpGetPhysicalDeviceSurfaceSupportKHR;
192 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
193 fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
194 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR
195 fpGetPhysicalDeviceSurfaceFormatsKHR;
196 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
197 fpGetPhysicalDeviceSurfacePresentModesKHR;
198 PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
199 PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
200 PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
201 PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
202 PFN_vkQueuePresentKHR fpQueuePresentKHR;
203 uint32_t swapchainImageCount;
204 VkSwapchainKHR swapchain;
205 SwapchainBuffers *buffers;
206
207 VkCommandPool cmd_pool;
208
209 struct {
210 VkFormat format;
211
212 VkImage image;
213 VkDeviceMemory mem;
214 VkImageView view;
215 } depth;
216
217 struct texture_object textures[DEMO_TEXTURE_COUNT];
218
219 struct {
220 VkBuffer buf;
221 VkDeviceMemory mem;
222
223 VkPipelineVertexInputStateCreateInfo vi;
224 VkVertexInputBindingDescription vi_bindings[1];
225 VkVertexInputAttributeDescription vi_attrs[2];
226 } vertices;
227
228 VkCommandBuffer setup_cmd; // Command Buffer for initialization commands
229 VkCommandBuffer draw_cmd; // Command Buffer for drawing commands
230 VkPipelineLayout pipeline_layout;
231 VkDescriptorSetLayout desc_layout;
232 VkPipelineCache pipelineCache;
233 VkRenderPass render_pass;
234 VkPipeline pipeline;
235
236 VkShaderModule vert_shader_module;
237 VkShaderModule frag_shader_module;
238
239 VkDescriptorPool desc_pool;
240 VkDescriptorSet desc_set;
241
242 VkFramebuffer *framebuffers;
243
244 VkPhysicalDeviceMemoryProperties memory_properties;
245
246 bool validate;
247 PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback;
248 PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback;
249 VkDebugReportCallbackEXT msg_callback;
250 PFN_vkDebugReportMessageEXT DebugReportMessage;
251
252 float depthStencil;
253 float depthIncrement;
254
255 bool quit;
256 uint32_t current_buffer;
257 uint32_t queue_count;
258 };
259
260 // Forward declaration:
261 static void demo_resize(struct demo *demo);
262
memory_type_from_properties(struct demo * demo,uint32_t typeBits,VkFlags requirements_mask,uint32_t * typeIndex)263 static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits,
264 VkFlags requirements_mask,
265 uint32_t *typeIndex) {
266 // Search memtypes to find first index with those properties
267 for (uint32_t i = 0; i < 32; i++) {
268 if ((typeBits & 1) == 1) {
269 // Type is available, does it match user properties?
270 if ((demo->memory_properties.memoryTypes[i].propertyFlags &
271 requirements_mask) == requirements_mask) {
272 *typeIndex = i;
273 return true;
274 }
275 }
276 typeBits >>= 1;
277 }
278 // No memory types matched, return failure
279 return false;
280 }
281
demo_flush_init_cmd(struct demo * demo)282 static void demo_flush_init_cmd(struct demo *demo) {
283 VkResult U_ASSERT_ONLY err;
284
285 if (demo->setup_cmd == VK_NULL_HANDLE)
286 return;
287
288 err = vkEndCommandBuffer(demo->setup_cmd);
289 assert(!err);
290
291 const VkCommandBuffer cmd_bufs[] = {demo->setup_cmd};
292 VkFence nullFence = {VK_NULL_HANDLE};
293 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
294 .pNext = NULL,
295 .waitSemaphoreCount = 0,
296 .pWaitSemaphores = NULL,
297 .pWaitDstStageMask = NULL,
298 .commandBufferCount = 1,
299 .pCommandBuffers = cmd_bufs,
300 .signalSemaphoreCount = 0,
301 .pSignalSemaphores = NULL};
302
303 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
304 assert(!err);
305
306 err = vkQueueWaitIdle(demo->queue);
307 assert(!err);
308
309 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs);
310 demo->setup_cmd = VK_NULL_HANDLE;
311 }
312
demo_set_image_layout(struct demo * demo,VkImage image,VkImageAspectFlags aspectMask,VkImageLayout old_image_layout,VkImageLayout new_image_layout,VkAccessFlagBits srcAccessMask)313 static void demo_set_image_layout(struct demo *demo, VkImage image,
314 VkImageAspectFlags aspectMask,
315 VkImageLayout old_image_layout,
316 VkImageLayout new_image_layout,
317 VkAccessFlagBits srcAccessMask) {
318
319 VkResult U_ASSERT_ONLY err;
320
321 if (demo->setup_cmd == VK_NULL_HANDLE) {
322 const VkCommandBufferAllocateInfo cmd = {
323 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
324 .pNext = NULL,
325 .commandPool = demo->cmd_pool,
326 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
327 .commandBufferCount = 1,
328 };
329
330 err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->setup_cmd);
331 assert(!err);
332
333 VkCommandBufferInheritanceInfo cmd_buf_hinfo = {
334 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
335 .pNext = NULL,
336 .renderPass = VK_NULL_HANDLE,
337 .subpass = 0,
338 .framebuffer = VK_NULL_HANDLE,
339 .occlusionQueryEnable = VK_FALSE,
340 .queryFlags = 0,
341 .pipelineStatistics = 0,
342 };
343 VkCommandBufferBeginInfo cmd_buf_info = {
344 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
345 .pNext = NULL,
346 .flags = 0,
347 .pInheritanceInfo = &cmd_buf_hinfo,
348 };
349 err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info);
350 assert(!err);
351 }
352
353 VkImageMemoryBarrier image_memory_barrier = {
354 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
355 .pNext = NULL,
356 .srcAccessMask = srcAccessMask,
357 .dstAccessMask = 0,
358 .oldLayout = old_image_layout,
359 .newLayout = new_image_layout,
360 .image = image,
361 .subresourceRange = {aspectMask, 0, 1, 0, 1}};
362
363 if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
364 /* Make sure anything that was copying from this image has completed */
365 image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
366 }
367
368 if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
369 image_memory_barrier.dstAccessMask =
370 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
371 }
372
373 if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
374 image_memory_barrier.dstAccessMask =
375 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
376 }
377
378 if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
379 /* Make sure any Copy or CPU writes to image are flushed */
380 image_memory_barrier.dstAccessMask =
381 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
382 }
383
384 VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
385
386 VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
387 VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
388
389 vkCmdPipelineBarrier(demo->setup_cmd, src_stages, dest_stages, 0, 0, NULL,
390 0, NULL, 1, pmemory_barrier);
391 }
392
demo_draw_build_cmd(struct demo * demo)393 static void demo_draw_build_cmd(struct demo *demo) {
394 const VkCommandBufferInheritanceInfo cmd_buf_hinfo = {
395 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
396 .pNext = NULL,
397 .renderPass = VK_NULL_HANDLE,
398 .subpass = 0,
399 .framebuffer = VK_NULL_HANDLE,
400 .occlusionQueryEnable = VK_FALSE,
401 .queryFlags = 0,
402 .pipelineStatistics = 0,
403 };
404 const VkCommandBufferBeginInfo cmd_buf_info = {
405 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
406 .pNext = NULL,
407 .flags = 0,
408 .pInheritanceInfo = &cmd_buf_hinfo,
409 };
410 const VkClearValue clear_values[2] = {
411 [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}},
412 [1] = {.depthStencil = {demo->depthStencil, 0}},
413 };
414 const VkRenderPassBeginInfo rp_begin = {
415 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
416 .pNext = NULL,
417 .renderPass = demo->render_pass,
418 .framebuffer = demo->framebuffers[demo->current_buffer],
419 .renderArea.offset.x = 0,
420 .renderArea.offset.y = 0,
421 .renderArea.extent.width = demo->width,
422 .renderArea.extent.height = demo->height,
423 .clearValueCount = 2,
424 .pClearValues = clear_values,
425 };
426 VkResult U_ASSERT_ONLY err;
427
428 err = vkBeginCommandBuffer(demo->draw_cmd, &cmd_buf_info);
429 assert(!err);
430
431 vkCmdBeginRenderPass(demo->draw_cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
432 vkCmdBindPipeline(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
433 demo->pipeline);
434 vkCmdBindDescriptorSets(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
435 demo->pipeline_layout, 0, 1, &demo->desc_set, 0,
436 NULL);
437
438 VkViewport viewport;
439 memset(&viewport, 0, sizeof(viewport));
440 viewport.height = (float)demo->height;
441 viewport.width = (float)demo->width;
442 viewport.minDepth = (float)0.0f;
443 viewport.maxDepth = (float)1.0f;
444 vkCmdSetViewport(demo->draw_cmd, 0, 1, &viewport);
445
446 VkRect2D scissor;
447 memset(&scissor, 0, sizeof(scissor));
448 scissor.extent.width = demo->width;
449 scissor.extent.height = demo->height;
450 scissor.offset.x = 0;
451 scissor.offset.y = 0;
452 vkCmdSetScissor(demo->draw_cmd, 0, 1, &scissor);
453
454 VkDeviceSize offsets[1] = {0};
455 vkCmdBindVertexBuffers(demo->draw_cmd, VERTEX_BUFFER_BIND_ID, 1,
456 &demo->vertices.buf, offsets);
457
458 vkCmdDraw(demo->draw_cmd, 3, 1, 0, 0);
459 vkCmdEndRenderPass(demo->draw_cmd);
460
461 VkImageMemoryBarrier prePresentBarrier = {
462 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
463 .pNext = NULL,
464 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
465 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
466 .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
467 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
468 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
469 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
470 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
471
472 prePresentBarrier.image = demo->buffers[demo->current_buffer].image;
473 VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier;
474 vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
475 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0,
476 NULL, 1, pmemory_barrier);
477
478 err = vkEndCommandBuffer(demo->draw_cmd);
479 assert(!err);
480 }
481
demo_draw(struct demo * demo)482 static void demo_draw(struct demo *demo) {
483 VkResult U_ASSERT_ONLY err;
484 VkSemaphore presentCompleteSemaphore;
485 VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = {
486 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
487 .pNext = NULL,
488 .flags = 0,
489 };
490
491 err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo,
492 NULL, &presentCompleteSemaphore);
493 assert(!err);
494
495 // Get the index of the next available swapchain image:
496 err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX,
497 presentCompleteSemaphore,
498 (VkFence)0, // TODO: Show use of fence
499 &demo->current_buffer);
500 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
501 // demo->swapchain is out of date (e.g. the window was resized) and
502 // must be recreated:
503 demo_resize(demo);
504 demo_draw(demo);
505 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL);
506 return;
507 } else if (err == VK_SUBOPTIMAL_KHR) {
508 // demo->swapchain is not as optimal as it could be, but the platform's
509 // presentation engine will still present the image correctly.
510 } else {
511 assert(!err);
512 }
513
514 // Assume the command buffer has been run on current_buffer before so
515 // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL
516 demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image,
517 VK_IMAGE_ASPECT_COLOR_BIT,
518 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
519 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
520 0);
521 demo_flush_init_cmd(demo);
522
523 // Wait for the present complete semaphore to be signaled to ensure
524 // that the image won't be rendered to until the presentation
525 // engine has fully released ownership to the application, and it is
526 // okay to render to the image.
527
528 // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
529 demo_draw_build_cmd(demo);
530 VkFence nullFence = VK_NULL_HANDLE;
531 VkPipelineStageFlags pipe_stage_flags =
532 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
533 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
534 .pNext = NULL,
535 .waitSemaphoreCount = 1,
536 .pWaitSemaphores = &presentCompleteSemaphore,
537 .pWaitDstStageMask = &pipe_stage_flags,
538 .commandBufferCount = 1,
539 .pCommandBuffers = &demo->draw_cmd,
540 .signalSemaphoreCount = 0,
541 .pSignalSemaphores = NULL};
542
543 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
544 assert(!err);
545
546 VkPresentInfoKHR present = {
547 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
548 .pNext = NULL,
549 .swapchainCount = 1,
550 .pSwapchains = &demo->swapchain,
551 .pImageIndices = &demo->current_buffer,
552 };
553
554 // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER?
555 err = demo->fpQueuePresentKHR(demo->queue, &present);
556 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
557 // demo->swapchain is out of date (e.g. the window was resized) and
558 // must be recreated:
559 demo_resize(demo);
560 } else if (err == VK_SUBOPTIMAL_KHR) {
561 // demo->swapchain is not as optimal as it could be, but the platform's
562 // presentation engine will still present the image correctly.
563 } else {
564 assert(!err);
565 }
566
567 err = vkQueueWaitIdle(demo->queue);
568 assert(err == VK_SUCCESS);
569
570 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL);
571 }
572
demo_prepare_buffers(struct demo * demo)573 static void demo_prepare_buffers(struct demo *demo) {
574 VkResult U_ASSERT_ONLY err;
575 VkSwapchainKHR oldSwapchain = demo->swapchain;
576
577 // Check the surface capabilities and formats
578 VkSurfaceCapabilitiesKHR surfCapabilities;
579 err = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR(
580 demo->gpu, demo->surface, &surfCapabilities);
581 assert(!err);
582
583 uint32_t presentModeCount;
584 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(
585 demo->gpu, demo->surface, &presentModeCount, NULL);
586 assert(!err);
587 VkPresentModeKHR *presentModes =
588 (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
589 assert(presentModes);
590 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(
591 demo->gpu, demo->surface, &presentModeCount, presentModes);
592 assert(!err);
593
594 VkExtent2D swapchainExtent;
595 // width and height are either both -1, or both not -1.
596 if (surfCapabilities.currentExtent.width == (uint32_t)-1) {
597 // If the surface size is undefined, the size is set to
598 // the size of the images requested.
599 swapchainExtent.width = demo->width;
600 swapchainExtent.height = demo->height;
601 } else {
602 // If the surface size is defined, the swap chain size must match
603 swapchainExtent = surfCapabilities.currentExtent;
604 demo->width = surfCapabilities.currentExtent.width;
605 demo->height = surfCapabilities.currentExtent.height;
606 }
607
608 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
609
610 // Determine the number of VkImage's to use in the swap chain (we desire to
611 // own only 1 image at a time, besides the images being displayed and
612 // queued for display):
613 uint32_t desiredNumberOfSwapchainImages =
614 surfCapabilities.minImageCount + 1;
615 if ((surfCapabilities.maxImageCount > 0) &&
616 (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) {
617 // Application must settle for fewer images than desired:
618 desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount;
619 }
620
621 VkSurfaceTransformFlagsKHR preTransform;
622 if (surfCapabilities.supportedTransforms &
623 VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
624 preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
625 } else {
626 preTransform = surfCapabilities.currentTransform;
627 }
628
629 const VkSwapchainCreateInfoKHR swapchain = {
630 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
631 .pNext = NULL,
632 .surface = demo->surface,
633 .minImageCount = desiredNumberOfSwapchainImages,
634 .imageFormat = demo->format,
635 .imageColorSpace = demo->color_space,
636 .imageExtent =
637 {
638 .width = swapchainExtent.width, .height = swapchainExtent.height,
639 },
640 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
641 .preTransform = preTransform,
642 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
643 .imageArrayLayers = 1,
644 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
645 .queueFamilyIndexCount = 0,
646 .pQueueFamilyIndices = NULL,
647 .presentMode = swapchainPresentMode,
648 .oldSwapchain = oldSwapchain,
649 .clipped = true,
650 };
651 uint32_t i;
652
653 err = demo->fpCreateSwapchainKHR(demo->device, &swapchain, NULL,
654 &demo->swapchain);
655 assert(!err);
656
657 // If we just re-created an existing swapchain, we should destroy the old
658 // swapchain at this point.
659 // Note: destroying the swapchain also cleans up all its associated
660 // presentable images once the platform is done with them.
661 if (oldSwapchain != VK_NULL_HANDLE) {
662 demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL);
663 }
664
665 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain,
666 &demo->swapchainImageCount, NULL);
667 assert(!err);
668
669 VkImage *swapchainImages =
670 (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage));
671 assert(swapchainImages);
672 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain,
673 &demo->swapchainImageCount,
674 swapchainImages);
675 assert(!err);
676
677 demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) *
678 demo->swapchainImageCount);
679 assert(demo->buffers);
680
681 for (i = 0; i < demo->swapchainImageCount; i++) {
682 VkImageViewCreateInfo color_attachment_view = {
683 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
684 .pNext = NULL,
685 .format = demo->format,
686 .components =
687 {
688 .r = VK_COMPONENT_SWIZZLE_R,
689 .g = VK_COMPONENT_SWIZZLE_G,
690 .b = VK_COMPONENT_SWIZZLE_B,
691 .a = VK_COMPONENT_SWIZZLE_A,
692 },
693 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
694 .baseMipLevel = 0,
695 .levelCount = 1,
696 .baseArrayLayer = 0,
697 .layerCount = 1},
698 .viewType = VK_IMAGE_VIEW_TYPE_2D,
699 .flags = 0,
700 };
701
702 demo->buffers[i].image = swapchainImages[i];
703
704 // Render loop will expect image to have been used before and in
705 // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
706 // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image
707 // to that state
708 demo_set_image_layout(
709 demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT,
710 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
711 0);
712
713 color_attachment_view.image = demo->buffers[i].image;
714
715 err = vkCreateImageView(demo->device, &color_attachment_view, NULL,
716 &demo->buffers[i].view);
717 assert(!err);
718 }
719
720 demo->current_buffer = 0;
721
722 if (NULL != presentModes) {
723 free(presentModes);
724 }
725 }
726
demo_prepare_depth(struct demo * demo)727 static void demo_prepare_depth(struct demo *demo) {
728 const VkFormat depth_format = VK_FORMAT_D16_UNORM;
729 const VkImageCreateInfo image = {
730 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
731 .pNext = NULL,
732 .imageType = VK_IMAGE_TYPE_2D,
733 .format = depth_format,
734 .extent = {demo->width, demo->height, 1},
735 .mipLevels = 1,
736 .arrayLayers = 1,
737 .samples = VK_SAMPLE_COUNT_1_BIT,
738 .tiling = VK_IMAGE_TILING_OPTIMAL,
739 .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
740 .flags = 0,
741 };
742 VkMemoryAllocateInfo mem_alloc = {
743 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
744 .pNext = NULL,
745 .allocationSize = 0,
746 .memoryTypeIndex = 0,
747 };
748 VkImageViewCreateInfo view = {
749 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
750 .pNext = NULL,
751 .image = VK_NULL_HANDLE,
752 .format = depth_format,
753 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
754 .baseMipLevel = 0,
755 .levelCount = 1,
756 .baseArrayLayer = 0,
757 .layerCount = 1},
758 .flags = 0,
759 .viewType = VK_IMAGE_VIEW_TYPE_2D,
760 };
761
762 VkMemoryRequirements mem_reqs;
763 VkResult U_ASSERT_ONLY err;
764 bool U_ASSERT_ONLY pass;
765
766 demo->depth.format = depth_format;
767
768 /* create image */
769 err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image);
770 assert(!err);
771
772 /* get memory requirements for this object */
773 vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs);
774
775 /* select memory size and type */
776 mem_alloc.allocationSize = mem_reqs.size;
777 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
778 0, /* No requirements */
779 &mem_alloc.memoryTypeIndex);
780 assert(pass);
781
782 /* allocate memory */
783 err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->depth.mem);
784 assert(!err);
785
786 /* bind memory */
787 err =
788 vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0);
789 assert(!err);
790
791 demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT,
792 VK_IMAGE_LAYOUT_UNDEFINED,
793 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
794 0);
795
796 /* create image view */
797 view.image = demo->depth.image;
798 err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view);
799 assert(!err);
800 }
801
802 static void
demo_prepare_texture_image(struct demo * demo,const uint32_t * tex_colors,struct texture_object * tex_obj,VkImageTiling tiling,VkImageUsageFlags usage,VkFlags required_props)803 demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors,
804 struct texture_object *tex_obj, VkImageTiling tiling,
805 VkImageUsageFlags usage, VkFlags required_props) {
806 const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
807 const int32_t tex_width = 2;
808 const int32_t tex_height = 2;
809 VkResult U_ASSERT_ONLY err;
810 bool U_ASSERT_ONLY pass;
811
812 tex_obj->tex_width = tex_width;
813 tex_obj->tex_height = tex_height;
814
815 const VkImageCreateInfo image_create_info = {
816 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
817 .pNext = NULL,
818 .imageType = VK_IMAGE_TYPE_2D,
819 .format = tex_format,
820 .extent = {tex_width, tex_height, 1},
821 .mipLevels = 1,
822 .arrayLayers = 1,
823 .samples = VK_SAMPLE_COUNT_1_BIT,
824 .tiling = tiling,
825 .usage = usage,
826 .flags = 0,
827 .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
828 };
829 VkMemoryAllocateInfo mem_alloc = {
830 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
831 .pNext = NULL,
832 .allocationSize = 0,
833 .memoryTypeIndex = 0,
834 };
835
836 VkMemoryRequirements mem_reqs;
837
838 err =
839 vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image);
840 assert(!err);
841
842 vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs);
843
844 mem_alloc.allocationSize = mem_reqs.size;
845 pass =
846 memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
847 required_props, &mem_alloc.memoryTypeIndex);
848 assert(pass);
849
850 /* allocate memory */
851 err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &tex_obj->mem);
852 assert(!err);
853
854 /* bind memory */
855 err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0);
856 assert(!err);
857
858 if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
859 const VkImageSubresource subres = {
860 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
861 .mipLevel = 0,
862 .arrayLayer = 0,
863 };
864 VkSubresourceLayout layout;
865 void *data;
866 int32_t x, y;
867
868 vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres,
869 &layout);
870
871 err = vkMapMemory(demo->device, tex_obj->mem, 0,
872 mem_alloc.allocationSize, 0, &data);
873 assert(!err);
874
875 for (y = 0; y < tex_height; y++) {
876 uint32_t *row = (uint32_t *)((char *)data + layout.rowPitch * y);
877 for (x = 0; x < tex_width; x++)
878 row[x] = tex_colors[(x & 1) ^ (y & 1)];
879 }
880
881 vkUnmapMemory(demo->device, tex_obj->mem);
882 }
883
884 tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
885 demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT,
886 VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout,
887 VK_ACCESS_HOST_WRITE_BIT);
888 /* setting the image layout does not reference the actual memory so no need
889 * to add a mem ref */
890 }
891
demo_destroy_texture_image(struct demo * demo,struct texture_object * tex_obj)892 static void demo_destroy_texture_image(struct demo *demo,
893 struct texture_object *tex_obj) {
894 /* clean up staging resources */
895 vkDestroyImage(demo->device, tex_obj->image, NULL);
896 vkFreeMemory(demo->device, tex_obj->mem, NULL);
897 }
898
demo_prepare_textures(struct demo * demo)899 static void demo_prepare_textures(struct demo *demo) {
900 const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
901 VkFormatProperties props;
902 const uint32_t tex_colors[DEMO_TEXTURE_COUNT][2] = {
903 {0xffff0000, 0xff00ff00},
904 };
905 uint32_t i;
906 VkResult U_ASSERT_ONLY err;
907
908 vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props);
909
910 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
911 if ((props.linearTilingFeatures &
912 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) &&
913 !demo->use_staging_buffer) {
914 /* Device can texture using linear textures */
915 demo_prepare_texture_image(demo, tex_colors[i], &demo->textures[i],
916 VK_IMAGE_TILING_LINEAR,
917 VK_IMAGE_USAGE_SAMPLED_BIT,
918 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
919 } else if (props.optimalTilingFeatures &
920 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) {
921 /* Must use staging buffer to copy linear texture to optimized */
922 struct texture_object staging_texture;
923
924 memset(&staging_texture, 0, sizeof(staging_texture));
925 demo_prepare_texture_image(demo, tex_colors[i], &staging_texture,
926 VK_IMAGE_TILING_LINEAR,
927 VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
928 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
929
930 demo_prepare_texture_image(
931 demo, tex_colors[i], &demo->textures[i],
932 VK_IMAGE_TILING_OPTIMAL,
933 (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
934 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
935
936 demo_set_image_layout(demo, staging_texture.image,
937 VK_IMAGE_ASPECT_COLOR_BIT,
938 staging_texture.imageLayout,
939 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
940 0);
941
942 demo_set_image_layout(demo, demo->textures[i].image,
943 VK_IMAGE_ASPECT_COLOR_BIT,
944 demo->textures[i].imageLayout,
945 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
946 0);
947
948 VkImageCopy copy_region = {
949 .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
950 .srcOffset = {0, 0, 0},
951 .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
952 .dstOffset = {0, 0, 0},
953 .extent = {staging_texture.tex_width,
954 staging_texture.tex_height, 1},
955 };
956 vkCmdCopyImage(
957 demo->setup_cmd, staging_texture.image,
958 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image,
959 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
960
961 demo_set_image_layout(demo, demo->textures[i].image,
962 VK_IMAGE_ASPECT_COLOR_BIT,
963 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
964 demo->textures[i].imageLayout,
965 0);
966
967 demo_flush_init_cmd(demo);
968
969 demo_destroy_texture_image(demo, &staging_texture);
970 } else {
971 /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */
972 assert(!"No support for B8G8R8A8_UNORM as texture image format");
973 }
974
975 const VkSamplerCreateInfo sampler = {
976 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
977 .pNext = NULL,
978 .magFilter = VK_FILTER_NEAREST,
979 .minFilter = VK_FILTER_NEAREST,
980 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
981 .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
982 .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
983 .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
984 .mipLodBias = 0.0f,
985 .anisotropyEnable = VK_FALSE,
986 .maxAnisotropy = 1,
987 .compareOp = VK_COMPARE_OP_NEVER,
988 .minLod = 0.0f,
989 .maxLod = 0.0f,
990 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
991 .unnormalizedCoordinates = VK_FALSE,
992 };
993 VkImageViewCreateInfo view = {
994 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
995 .pNext = NULL,
996 .image = VK_NULL_HANDLE,
997 .viewType = VK_IMAGE_VIEW_TYPE_2D,
998 .format = tex_format,
999 .components =
1000 {
1001 VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
1002 VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
1003 },
1004 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
1005 .flags = 0,
1006 };
1007
1008 /* create sampler */
1009 err = vkCreateSampler(demo->device, &sampler, NULL,
1010 &demo->textures[i].sampler);
1011 assert(!err);
1012
1013 /* create image view */
1014 view.image = demo->textures[i].image;
1015 err = vkCreateImageView(demo->device, &view, NULL,
1016 &demo->textures[i].view);
1017 assert(!err);
1018 }
1019 }
1020
demo_prepare_vertices(struct demo * demo)1021 static void demo_prepare_vertices(struct demo *demo) {
1022 // clang-format off
1023 const float vb[3][5] = {
1024 /* position texcoord */
1025 { -1.0f, -1.0f, 0.25f, 0.0f, 0.0f },
1026 { 1.0f, -1.0f, 0.25f, 1.0f, 0.0f },
1027 { 0.0f, 1.0f, 1.0f, 0.5f, 1.0f },
1028 };
1029 // clang-format on
1030 const VkBufferCreateInfo buf_info = {
1031 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
1032 .pNext = NULL,
1033 .size = sizeof(vb),
1034 .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1035 .flags = 0,
1036 };
1037 VkMemoryAllocateInfo mem_alloc = {
1038 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
1039 .pNext = NULL,
1040 .allocationSize = 0,
1041 .memoryTypeIndex = 0,
1042 };
1043 VkMemoryRequirements mem_reqs;
1044 VkResult U_ASSERT_ONLY err;
1045 bool U_ASSERT_ONLY pass;
1046 void *data;
1047
1048 memset(&demo->vertices, 0, sizeof(demo->vertices));
1049
1050 err = vkCreateBuffer(demo->device, &buf_info, NULL, &demo->vertices.buf);
1051 assert(!err);
1052
1053 vkGetBufferMemoryRequirements(demo->device, demo->vertices.buf, &mem_reqs);
1054 assert(!err);
1055
1056 mem_alloc.allocationSize = mem_reqs.size;
1057 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
1058 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
1059 &mem_alloc.memoryTypeIndex);
1060 assert(pass);
1061
1062 err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->vertices.mem);
1063 assert(!err);
1064
1065 err = vkMapMemory(demo->device, demo->vertices.mem, 0,
1066 mem_alloc.allocationSize, 0, &data);
1067 assert(!err);
1068
1069 memcpy(data, vb, sizeof(vb));
1070
1071 vkUnmapMemory(demo->device, demo->vertices.mem);
1072
1073 err = vkBindBufferMemory(demo->device, demo->vertices.buf,
1074 demo->vertices.mem, 0);
1075 assert(!err);
1076
1077 demo->vertices.vi.sType =
1078 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1079 demo->vertices.vi.pNext = NULL;
1080 demo->vertices.vi.vertexBindingDescriptionCount = 1;
1081 demo->vertices.vi.pVertexBindingDescriptions = demo->vertices.vi_bindings;
1082 demo->vertices.vi.vertexAttributeDescriptionCount = 2;
1083 demo->vertices.vi.pVertexAttributeDescriptions = demo->vertices.vi_attrs;
1084
1085 demo->vertices.vi_bindings[0].binding = VERTEX_BUFFER_BIND_ID;
1086 demo->vertices.vi_bindings[0].stride = sizeof(vb[0]);
1087 demo->vertices.vi_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1088
1089 demo->vertices.vi_attrs[0].binding = VERTEX_BUFFER_BIND_ID;
1090 demo->vertices.vi_attrs[0].location = 0;
1091 demo->vertices.vi_attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1092 demo->vertices.vi_attrs[0].offset = 0;
1093
1094 demo->vertices.vi_attrs[1].binding = VERTEX_BUFFER_BIND_ID;
1095 demo->vertices.vi_attrs[1].location = 1;
1096 demo->vertices.vi_attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
1097 demo->vertices.vi_attrs[1].offset = sizeof(float) * 3;
1098 }
1099
demo_prepare_descriptor_layout(struct demo * demo)1100 static void demo_prepare_descriptor_layout(struct demo *demo) {
1101 const VkDescriptorSetLayoutBinding layout_binding = {
1102 .binding = 0,
1103 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1104 .descriptorCount = DEMO_TEXTURE_COUNT,
1105 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
1106 .pImmutableSamplers = NULL,
1107 };
1108 const VkDescriptorSetLayoutCreateInfo descriptor_layout = {
1109 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1110 .pNext = NULL,
1111 .bindingCount = 1,
1112 .pBindings = &layout_binding,
1113 };
1114 VkResult U_ASSERT_ONLY err;
1115
1116 err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL,
1117 &demo->desc_layout);
1118 assert(!err);
1119
1120 const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
1121 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
1122 .pNext = NULL,
1123 .setLayoutCount = 1,
1124 .pSetLayouts = &demo->desc_layout,
1125 };
1126
1127 err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL,
1128 &demo->pipeline_layout);
1129 assert(!err);
1130 }
1131
demo_prepare_render_pass(struct demo * demo)1132 static void demo_prepare_render_pass(struct demo *demo) {
1133 const VkAttachmentDescription attachments[2] = {
1134 [0] =
1135 {
1136 .format = demo->format,
1137 .samples = VK_SAMPLE_COUNT_1_BIT,
1138 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1139 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
1140 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1141 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1142 .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1143 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1144 },
1145 [1] =
1146 {
1147 .format = demo->depth.format,
1148 .samples = VK_SAMPLE_COUNT_1_BIT,
1149 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1150 .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1151 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1152 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1153 .initialLayout =
1154 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1155 .finalLayout =
1156 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1157 },
1158 };
1159 const VkAttachmentReference color_reference = {
1160 .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1161 };
1162 const VkAttachmentReference depth_reference = {
1163 .attachment = 1,
1164 .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1165 };
1166 const VkSubpassDescription subpass = {
1167 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
1168 .flags = 0,
1169 .inputAttachmentCount = 0,
1170 .pInputAttachments = NULL,
1171 .colorAttachmentCount = 1,
1172 .pColorAttachments = &color_reference,
1173 .pResolveAttachments = NULL,
1174 .pDepthStencilAttachment = &depth_reference,
1175 .preserveAttachmentCount = 0,
1176 .pPreserveAttachments = NULL,
1177 };
1178 const VkRenderPassCreateInfo rp_info = {
1179 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
1180 .pNext = NULL,
1181 .attachmentCount = 2,
1182 .pAttachments = attachments,
1183 .subpassCount = 1,
1184 .pSubpasses = &subpass,
1185 .dependencyCount = 0,
1186 .pDependencies = NULL,
1187 };
1188 VkResult U_ASSERT_ONLY err;
1189
1190 err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass);
1191 assert(!err);
1192 }
1193
1194 static VkShaderModule
demo_prepare_shader_module(struct demo * demo,const void * code,size_t size)1195 demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) {
1196 VkShaderModuleCreateInfo moduleCreateInfo;
1197 VkShaderModule module;
1198 VkResult U_ASSERT_ONLY err;
1199
1200 moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1201 moduleCreateInfo.pNext = NULL;
1202
1203 moduleCreateInfo.codeSize = size;
1204 moduleCreateInfo.pCode = code;
1205 moduleCreateInfo.flags = 0;
1206 err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module);
1207 assert(!err);
1208
1209 return module;
1210 }
1211
demo_read_spv(const char * filename,size_t * psize)1212 char *demo_read_spv(const char *filename, size_t *psize) {
1213 long int size;
1214 void *shader_code;
1215 size_t retVal;
1216
1217 FILE *fp = fopen(filename, "rb");
1218 if (!fp)
1219 return NULL;
1220
1221 fseek(fp, 0L, SEEK_END);
1222 size = ftell(fp);
1223
1224 fseek(fp, 0L, SEEK_SET);
1225
1226 shader_code = malloc(size);
1227 retVal = fread(shader_code, size, 1, fp);
1228 if (!retVal)
1229 return NULL;
1230
1231 *psize = size;
1232
1233 fclose(fp);
1234 return shader_code;
1235 }
1236
demo_prepare_vs(struct demo * demo)1237 static VkShaderModule demo_prepare_vs(struct demo *demo) {
1238 void *vertShaderCode;
1239 size_t size;
1240
1241 vertShaderCode = demo_read_spv("tri-vert.spv", &size);
1242
1243 demo->vert_shader_module =
1244 demo_prepare_shader_module(demo, vertShaderCode, size);
1245
1246 free(vertShaderCode);
1247
1248 return demo->vert_shader_module;
1249 }
1250
demo_prepare_fs(struct demo * demo)1251 static VkShaderModule demo_prepare_fs(struct demo *demo) {
1252 void *fragShaderCode;
1253 size_t size;
1254
1255 fragShaderCode = demo_read_spv("tri-frag.spv", &size);
1256
1257 demo->frag_shader_module =
1258 demo_prepare_shader_module(demo, fragShaderCode, size);
1259
1260 free(fragShaderCode);
1261
1262 return demo->frag_shader_module;
1263 }
1264
demo_prepare_pipeline(struct demo * demo)1265 static void demo_prepare_pipeline(struct demo *demo) {
1266 VkGraphicsPipelineCreateInfo pipeline;
1267 VkPipelineCacheCreateInfo pipelineCache;
1268
1269 VkPipelineVertexInputStateCreateInfo vi;
1270 VkPipelineInputAssemblyStateCreateInfo ia;
1271 VkPipelineRasterizationStateCreateInfo rs;
1272 VkPipelineColorBlendStateCreateInfo cb;
1273 VkPipelineDepthStencilStateCreateInfo ds;
1274 VkPipelineViewportStateCreateInfo vp;
1275 VkPipelineMultisampleStateCreateInfo ms;
1276 VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE];
1277 VkPipelineDynamicStateCreateInfo dynamicState;
1278
1279 VkResult U_ASSERT_ONLY err;
1280
1281 memset(dynamicStateEnables, 0, sizeof dynamicStateEnables);
1282 memset(&dynamicState, 0, sizeof dynamicState);
1283 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1284 dynamicState.pDynamicStates = dynamicStateEnables;
1285
1286 memset(&pipeline, 0, sizeof(pipeline));
1287 pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1288 pipeline.layout = demo->pipeline_layout;
1289
1290 vi = demo->vertices.vi;
1291
1292 memset(&ia, 0, sizeof(ia));
1293 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1294 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1295
1296 memset(&rs, 0, sizeof(rs));
1297 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1298 rs.polygonMode = VK_POLYGON_MODE_FILL;
1299 rs.cullMode = VK_CULL_MODE_BACK_BIT;
1300 rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
1301 rs.depthClampEnable = VK_FALSE;
1302 rs.rasterizerDiscardEnable = VK_FALSE;
1303 rs.depthBiasEnable = VK_FALSE;
1304
1305 memset(&cb, 0, sizeof(cb));
1306 cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1307 VkPipelineColorBlendAttachmentState att_state[1];
1308 memset(att_state, 0, sizeof(att_state));
1309 att_state[0].colorWriteMask = 0xf;
1310 att_state[0].blendEnable = VK_FALSE;
1311 cb.attachmentCount = 1;
1312 cb.pAttachments = att_state;
1313
1314 memset(&vp, 0, sizeof(vp));
1315 vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1316 vp.viewportCount = 1;
1317 dynamicStateEnables[dynamicState.dynamicStateCount++] =
1318 VK_DYNAMIC_STATE_VIEWPORT;
1319 vp.scissorCount = 1;
1320 dynamicStateEnables[dynamicState.dynamicStateCount++] =
1321 VK_DYNAMIC_STATE_SCISSOR;
1322
1323 memset(&ds, 0, sizeof(ds));
1324 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1325 ds.depthTestEnable = VK_TRUE;
1326 ds.depthWriteEnable = VK_TRUE;
1327 ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
1328 ds.depthBoundsTestEnable = VK_FALSE;
1329 ds.back.failOp = VK_STENCIL_OP_KEEP;
1330 ds.back.passOp = VK_STENCIL_OP_KEEP;
1331 ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
1332 ds.stencilTestEnable = VK_FALSE;
1333 ds.front = ds.back;
1334
1335 memset(&ms, 0, sizeof(ms));
1336 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1337 ms.pSampleMask = NULL;
1338 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1339
1340 // Two stages: vs and fs
1341 pipeline.stageCount = 2;
1342 VkPipelineShaderStageCreateInfo shaderStages[2];
1343 memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo));
1344
1345 shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1346 shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
1347 shaderStages[0].module = demo_prepare_vs(demo);
1348 shaderStages[0].pName = "main";
1349
1350 shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1351 shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1352 shaderStages[1].module = demo_prepare_fs(demo);
1353 shaderStages[1].pName = "main";
1354
1355 pipeline.pVertexInputState = &vi;
1356 pipeline.pInputAssemblyState = &ia;
1357 pipeline.pRasterizationState = &rs;
1358 pipeline.pColorBlendState = &cb;
1359 pipeline.pMultisampleState = &ms;
1360 pipeline.pViewportState = &vp;
1361 pipeline.pDepthStencilState = &ds;
1362 pipeline.pStages = shaderStages;
1363 pipeline.renderPass = demo->render_pass;
1364 pipeline.pDynamicState = &dynamicState;
1365
1366 memset(&pipelineCache, 0, sizeof(pipelineCache));
1367 pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
1368
1369 err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL,
1370 &demo->pipelineCache);
1371 assert(!err);
1372 err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1,
1373 &pipeline, NULL, &demo->pipeline);
1374 assert(!err);
1375
1376 vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
1377
1378 vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL);
1379 vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL);
1380 }
1381
demo_prepare_descriptor_pool(struct demo * demo)1382 static void demo_prepare_descriptor_pool(struct demo *demo) {
1383 const VkDescriptorPoolSize type_count = {
1384 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1385 .descriptorCount = DEMO_TEXTURE_COUNT,
1386 };
1387 const VkDescriptorPoolCreateInfo descriptor_pool = {
1388 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
1389 .pNext = NULL,
1390 .maxSets = 1,
1391 .poolSizeCount = 1,
1392 .pPoolSizes = &type_count,
1393 };
1394 VkResult U_ASSERT_ONLY err;
1395
1396 err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL,
1397 &demo->desc_pool);
1398 assert(!err);
1399 }
1400
demo_prepare_descriptor_set(struct demo * demo)1401 static void demo_prepare_descriptor_set(struct demo *demo) {
1402 VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT];
1403 VkWriteDescriptorSet write;
1404 VkResult U_ASSERT_ONLY err;
1405 uint32_t i;
1406
1407 VkDescriptorSetAllocateInfo alloc_info = {
1408 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
1409 .pNext = NULL,
1410 .descriptorPool = demo->desc_pool,
1411 .descriptorSetCount = 1,
1412 .pSetLayouts = &demo->desc_layout};
1413 err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set);
1414 assert(!err);
1415
1416 memset(&tex_descs, 0, sizeof(tex_descs));
1417 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1418 tex_descs[i].sampler = demo->textures[i].sampler;
1419 tex_descs[i].imageView = demo->textures[i].view;
1420 tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
1421 }
1422
1423 memset(&write, 0, sizeof(write));
1424 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1425 write.dstSet = demo->desc_set;
1426 write.descriptorCount = DEMO_TEXTURE_COUNT;
1427 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1428 write.pImageInfo = tex_descs;
1429
1430 vkUpdateDescriptorSets(demo->device, 1, &write, 0, NULL);
1431 }
1432
demo_prepare_framebuffers(struct demo * demo)1433 static void demo_prepare_framebuffers(struct demo *demo) {
1434 VkImageView attachments[2];
1435 attachments[1] = demo->depth.view;
1436
1437 const VkFramebufferCreateInfo fb_info = {
1438 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
1439 .pNext = NULL,
1440 .renderPass = demo->render_pass,
1441 .attachmentCount = 2,
1442 .pAttachments = attachments,
1443 .width = demo->width,
1444 .height = demo->height,
1445 .layers = 1,
1446 };
1447 VkResult U_ASSERT_ONLY err;
1448 uint32_t i;
1449
1450 demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount *
1451 sizeof(VkFramebuffer));
1452 assert(demo->framebuffers);
1453
1454 for (i = 0; i < demo->swapchainImageCount; i++) {
1455 attachments[0] = demo->buffers[i].view;
1456 err = vkCreateFramebuffer(demo->device, &fb_info, NULL,
1457 &demo->framebuffers[i]);
1458 assert(!err);
1459 }
1460 }
1461
demo_prepare(struct demo * demo)1462 static void demo_prepare(struct demo *demo) {
1463 VkResult U_ASSERT_ONLY err;
1464
1465 const VkCommandPoolCreateInfo cmd_pool_info = {
1466 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
1467 .pNext = NULL,
1468 .queueFamilyIndex = demo->graphics_queue_node_index,
1469 .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
1470 };
1471 err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL,
1472 &demo->cmd_pool);
1473 assert(!err);
1474
1475 const VkCommandBufferAllocateInfo cmd = {
1476 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
1477 .pNext = NULL,
1478 .commandPool = demo->cmd_pool,
1479 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1480 .commandBufferCount = 1,
1481 };
1482 err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->draw_cmd);
1483 assert(!err);
1484
1485 demo_prepare_buffers(demo);
1486 demo_prepare_depth(demo);
1487 demo_prepare_textures(demo);
1488 demo_prepare_vertices(demo);
1489 demo_prepare_descriptor_layout(demo);
1490 demo_prepare_render_pass(demo);
1491 demo_prepare_pipeline(demo);
1492
1493 demo_prepare_descriptor_pool(demo);
1494 demo_prepare_descriptor_set(demo);
1495
1496 demo_prepare_framebuffers(demo);
1497
1498 demo->prepared = true;
1499 }
1500
1501 #ifdef _WIN32
demo_run(struct demo * demo)1502 static void demo_run(struct demo *demo) {
1503 if (!demo->prepared)
1504 return;
1505 demo_draw(demo);
1506
1507 if (demo->depthStencil > 0.99f)
1508 demo->depthIncrement = -0.001f;
1509 if (demo->depthStencil < 0.8f)
1510 demo->depthIncrement = 0.001f;
1511
1512 demo->depthStencil += demo->depthIncrement;
1513 }
1514
1515 // On MS-Windows, make this a global, so it's available to WndProc()
1516 struct demo demo;
1517
1518 // MS-Windows event handling function:
WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1519 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1520 char tmp_str[] = APP_LONG_NAME;
1521
1522 switch (uMsg) {
1523 case WM_CREATE:
1524 return 0;
1525 case WM_CLOSE:
1526 PostQuitMessage(0);
1527 return 0;
1528 case WM_PAINT:
1529 if (demo.prepared) {
1530 demo_run(&demo);
1531 break;
1532 }
1533 case WM_SIZE:
1534 // Resize the application to the new window size, except when
1535 // it was minimized. Vulkan doesn't support images or swapchains
1536 // with width=0 and height=0.
1537 if (wParam != SIZE_MINIMIZED) {
1538 demo.width = lParam & 0xffff;
1539 demo.height = lParam & 0xffff0000 >> 16;
1540 demo_resize(&demo);
1541 }
1542 break;
1543 default:
1544 break;
1545 }
1546 return (DefWindowProc(hWnd, uMsg, wParam, lParam));
1547 }
1548
demo_create_window(struct demo * demo)1549 static void demo_create_window(struct demo *demo) {
1550 WNDCLASSEX win_class;
1551
1552 // Initialize the window class structure:
1553 win_class.cbSize = sizeof(WNDCLASSEX);
1554 win_class.style = CS_HREDRAW | CS_VREDRAW;
1555 win_class.lpfnWndProc = WndProc;
1556 win_class.cbClsExtra = 0;
1557 win_class.cbWndExtra = 0;
1558 win_class.hInstance = demo->connection; // hInstance
1559 win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
1560 win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
1561 win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
1562 win_class.lpszMenuName = NULL;
1563 win_class.lpszClassName = demo->name;
1564 win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
1565 // Register window class:
1566 if (!RegisterClassEx(&win_class)) {
1567 // It didn't work, so try to give a useful error:
1568 printf("Unexpected error trying to start the application!\n");
1569 fflush(stdout);
1570 exit(1);
1571 }
1572 // Create window with the registered class:
1573 RECT wr = {0, 0, demo->width, demo->height};
1574 AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
1575 demo->window = CreateWindowEx(0,
1576 demo->name, // class name
1577 demo->name, // app name
1578 WS_OVERLAPPEDWINDOW | // window style
1579 WS_VISIBLE | WS_SYSMENU,
1580 100, 100, // x/y coords
1581 wr.right - wr.left, // width
1582 wr.bottom - wr.top, // height
1583 NULL, // handle to parent
1584 NULL, // handle to menu
1585 demo->connection, // hInstance
1586 NULL); // no extra parameters
1587 if (!demo->window) {
1588 // It didn't work, so try to give a useful error:
1589 printf("Cannot create a window in which to draw!\n");
1590 fflush(stdout);
1591 exit(1);
1592 }
1593 }
1594 #else // _WIN32
1595
demo_handle_event(struct demo * demo,const xcb_generic_event_t * event)1596 static void demo_handle_event(struct demo *demo,
1597 const xcb_generic_event_t *event) {
1598 switch (event->response_type & 0x7f) {
1599 case XCB_EXPOSE:
1600 demo_draw(demo);
1601 break;
1602 case XCB_CLIENT_MESSAGE:
1603 if ((*(xcb_client_message_event_t *)event).data.data32[0] ==
1604 (*demo->atom_wm_delete_window).atom) {
1605 demo->quit = true;
1606 }
1607 break;
1608 case XCB_KEY_RELEASE: {
1609 const xcb_key_release_event_t *key =
1610 (const xcb_key_release_event_t *)event;
1611
1612 if (key->detail == 0x9)
1613 demo->quit = true;
1614 } break;
1615 case XCB_DESTROY_NOTIFY:
1616 demo->quit = true;
1617 break;
1618 case XCB_CONFIGURE_NOTIFY: {
1619 const xcb_configure_notify_event_t *cfg =
1620 (const xcb_configure_notify_event_t *)event;
1621 if ((demo->width != cfg->width) || (demo->height != cfg->height)) {
1622 demo->width = cfg->width;
1623 demo->height = cfg->height;
1624 demo_resize(demo);
1625 }
1626 } break;
1627 default:
1628 break;
1629 }
1630 }
1631
demo_run(struct demo * demo)1632 static void demo_run(struct demo *demo) {
1633 xcb_flush(demo->connection);
1634
1635 while (!demo->quit) {
1636 xcb_generic_event_t *event;
1637
1638 event = xcb_poll_for_event(demo->connection);
1639 if (event) {
1640 demo_handle_event(demo, event);
1641 free(event);
1642 }
1643
1644 demo_draw(demo);
1645
1646 if (demo->depthStencil > 0.99f)
1647 demo->depthIncrement = -0.001f;
1648 if (demo->depthStencil < 0.8f)
1649 demo->depthIncrement = 0.001f;
1650
1651 demo->depthStencil += demo->depthIncrement;
1652
1653 // Wait for work to finish before updating MVP.
1654 vkDeviceWaitIdle(demo->device);
1655 }
1656 }
1657
demo_create_window(struct demo * demo)1658 static void demo_create_window(struct demo *demo) {
1659 uint32_t value_mask, value_list[32];
1660
1661 demo->window = xcb_generate_id(demo->connection);
1662
1663 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
1664 value_list[0] = demo->screen->black_pixel;
1665 value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE |
1666 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
1667
1668 xcb_create_window(demo->connection, XCB_COPY_FROM_PARENT, demo->window,
1669 demo->screen->root, 0, 0, demo->width, demo->height, 0,
1670 XCB_WINDOW_CLASS_INPUT_OUTPUT, demo->screen->root_visual,
1671 value_mask, value_list);
1672
1673 /* Magic code that will send notification when window is destroyed */
1674 xcb_intern_atom_cookie_t cookie =
1675 xcb_intern_atom(demo->connection, 1, 12, "WM_PROTOCOLS");
1676 xcb_intern_atom_reply_t *reply =
1677 xcb_intern_atom_reply(demo->connection, cookie, 0);
1678
1679 xcb_intern_atom_cookie_t cookie2 =
1680 xcb_intern_atom(demo->connection, 0, 16, "WM_DELETE_WINDOW");
1681 demo->atom_wm_delete_window =
1682 xcb_intern_atom_reply(demo->connection, cookie2, 0);
1683
1684 xcb_change_property(demo->connection, XCB_PROP_MODE_REPLACE, demo->window,
1685 (*reply).atom, 4, 32, 1,
1686 &(*demo->atom_wm_delete_window).atom);
1687 free(reply);
1688
1689 xcb_map_window(demo->connection, demo->window);
1690 }
1691 #endif // _WIN32
1692
1693 /*
1694 * Return 1 (true) if all layer names specified in check_names
1695 * can be found in given layer properties.
1696 */
demo_check_layers(uint32_t check_count,char ** check_names,uint32_t layer_count,VkLayerProperties * layers)1697 static VkBool32 demo_check_layers(uint32_t check_count, char **check_names,
1698 uint32_t layer_count,
1699 VkLayerProperties *layers) {
1700 for (uint32_t i = 0; i < check_count; i++) {
1701 VkBool32 found = 0;
1702 for (uint32_t j = 0; j < layer_count; j++) {
1703 if (!strcmp(check_names[i], layers[j].layerName)) {
1704 found = 1;
1705 break;
1706 }
1707 }
1708 if (!found) {
1709 fprintf(stderr, "Cannot find layer: %s\n", check_names[i]);
1710 return 0;
1711 }
1712 }
1713 return 1;
1714 }
1715
demo_init_vk(struct demo * demo)1716 static void demo_init_vk(struct demo *demo) {
1717 VkResult err;
1718 uint32_t instance_extension_count = 0;
1719 uint32_t instance_layer_count = 0;
1720 uint32_t device_validation_layer_count = 0;
1721 char **instance_validation_layers = NULL;
1722 demo->enabled_extension_count = 0;
1723 demo->enabled_layer_count = 0;
1724
1725 char *instance_validation_layers_alt1[] = {
1726 "VK_LAYER_LUNARG_standard_validation"
1727 };
1728
1729 char *instance_validation_layers_alt2[] = {
1730 "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation",
1731 "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_object_tracker",
1732 "VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation",
1733 "VK_LAYER_LUNARG_swapchain", "VK_LAYER_GOOGLE_unique_objects"
1734 };
1735
1736 /* Look for validation layers */
1737 VkBool32 validation_found = 0;
1738 if (demo->validate) {
1739
1740 err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
1741 assert(!err);
1742
1743 instance_validation_layers = instance_validation_layers_alt1;
1744 if (instance_layer_count > 0) {
1745 VkLayerProperties *instance_layers =
1746 malloc(sizeof (VkLayerProperties) * instance_layer_count);
1747 err = vkEnumerateInstanceLayerProperties(&instance_layer_count,
1748 instance_layers);
1749 assert(!err);
1750
1751
1752 validation_found = demo_check_layers(
1753 ARRAY_SIZE(instance_validation_layers_alt1),
1754 instance_validation_layers, instance_layer_count,
1755 instance_layers);
1756 if (validation_found) {
1757 demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1);
1758 demo->device_validation_layers[0] = "VK_LAYER_LUNARG_standard_validation";
1759 device_validation_layer_count = 1;
1760 } else {
1761 // use alternative set of validation layers
1762 instance_validation_layers = instance_validation_layers_alt2;
1763 demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2);
1764 validation_found = demo_check_layers(
1765 ARRAY_SIZE(instance_validation_layers_alt2),
1766 instance_validation_layers, instance_layer_count,
1767 instance_layers);
1768 device_validation_layer_count =
1769 ARRAY_SIZE(instance_validation_layers_alt2);
1770 for (uint32_t i = 0; i < device_validation_layer_count; i++) {
1771 demo->device_validation_layers[i] =
1772 instance_validation_layers[i];
1773 }
1774 }
1775 free(instance_layers);
1776 }
1777
1778 if (!validation_found) {
1779 ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find"
1780 "required validation layer.\n\n"
1781 "Please look at the Getting Started guide for additional "
1782 "information.\n",
1783 "vkCreateInstance Failure");
1784 }
1785 }
1786
1787 /* Look for instance extensions */
1788 VkBool32 surfaceExtFound = 0;
1789 VkBool32 platformSurfaceExtFound = 0;
1790 memset(demo->extension_names, 0, sizeof(demo->extension_names));
1791
1792 err = vkEnumerateInstanceExtensionProperties(
1793 NULL, &instance_extension_count, NULL);
1794 assert(!err);
1795
1796 if (instance_extension_count > 0) {
1797 VkExtensionProperties *instance_extensions =
1798 malloc(sizeof(VkExtensionProperties) * instance_extension_count);
1799 err = vkEnumerateInstanceExtensionProperties(
1800 NULL, &instance_extension_count, instance_extensions);
1801 assert(!err);
1802 for (uint32_t i = 0; i < instance_extension_count; i++) {
1803 if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME,
1804 instance_extensions[i].extensionName)) {
1805 surfaceExtFound = 1;
1806 demo->extension_names[demo->enabled_extension_count++] =
1807 VK_KHR_SURFACE_EXTENSION_NAME;
1808 }
1809 #ifdef _WIN32
1810 if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
1811 instance_extensions[i].extensionName)) {
1812 platformSurfaceExtFound = 1;
1813 demo->extension_names[demo->enabled_extension_count++] =
1814 VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
1815 }
1816 #else // _WIN32
1817 if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME,
1818 instance_extensions[i].extensionName)) {
1819 platformSurfaceExtFound = 1;
1820 demo->extension_names[demo->enabled_extension_count++] =
1821 VK_KHR_XCB_SURFACE_EXTENSION_NAME;
1822 }
1823 #endif // _WIN32
1824 if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
1825 instance_extensions[i].extensionName)) {
1826 if (demo->validate) {
1827 demo->extension_names[demo->enabled_extension_count++] =
1828 VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
1829 }
1830 }
1831 assert(demo->enabled_extension_count < 64);
1832 }
1833
1834 free(instance_extensions);
1835 }
1836
1837 if (!surfaceExtFound) {
1838 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
1839 "the " VK_KHR_SURFACE_EXTENSION_NAME
1840 " extension.\n\nDo you have a compatible "
1841 "Vulkan installable client driver (ICD) installed?\nPlease "
1842 "look at the Getting Started guide for additional "
1843 "information.\n",
1844 "vkCreateInstance Failure");
1845 }
1846 if (!platformSurfaceExtFound) {
1847 #ifdef _WIN32
1848 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
1849 "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME
1850 " extension.\n\nDo you have a compatible "
1851 "Vulkan installable client driver (ICD) installed?\nPlease "
1852 "look at the Getting Started guide for additional "
1853 "information.\n",
1854 "vkCreateInstance Failure");
1855 #else // _WIN32
1856 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
1857 "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME
1858 " extension.\n\nDo you have a compatible "
1859 "Vulkan installable client driver (ICD) installed?\nPlease "
1860 "look at the Getting Started guide for additional "
1861 "information.\n",
1862 "vkCreateInstance Failure");
1863 #endif // _WIN32
1864 }
1865 const VkApplicationInfo app = {
1866 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
1867 .pNext = NULL,
1868 .pApplicationName = APP_SHORT_NAME,
1869 .applicationVersion = 0,
1870 .pEngineName = APP_SHORT_NAME,
1871 .engineVersion = 0,
1872 .apiVersion = VK_API_VERSION_1_0,
1873 };
1874 VkInstanceCreateInfo inst_info = {
1875 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
1876 .pNext = NULL,
1877 .pApplicationInfo = &app,
1878 .enabledLayerCount = demo->enabled_layer_count,
1879 .ppEnabledLayerNames = (const char *const *)instance_validation_layers,
1880 .enabledExtensionCount = demo->enabled_extension_count,
1881 .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
1882 };
1883
1884 uint32_t gpu_count;
1885
1886 err = vkCreateInstance(&inst_info, NULL, &demo->inst);
1887 if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
1888 ERR_EXIT("Cannot find a compatible Vulkan installable client driver "
1889 "(ICD).\n\nPlease look at the Getting Started guide for "
1890 "additional information.\n",
1891 "vkCreateInstance Failure");
1892 } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) {
1893 ERR_EXIT("Cannot find a specified extension library"
1894 ".\nMake sure your layers path is set appropriately\n",
1895 "vkCreateInstance Failure");
1896 } else if (err) {
1897 ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan "
1898 "installable client driver (ICD) installed?\nPlease look at "
1899 "the Getting Started guide for additional information.\n",
1900 "vkCreateInstance Failure");
1901 }
1902
1903 /* Make initial call to query gpu_count, then second call for gpu info*/
1904 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
1905 assert(!err && gpu_count > 0);
1906
1907 if (gpu_count > 0) {
1908 VkPhysicalDevice *physical_devices =
1909 malloc(sizeof(VkPhysicalDevice) * gpu_count);
1910 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count,
1911 physical_devices);
1912 assert(!err);
1913 /* For tri demo we just grab the first physical device */
1914 demo->gpu = physical_devices[0];
1915 free(physical_devices);
1916 } else {
1917 ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices."
1918 "\n\nDo you have a compatible Vulkan installable client"
1919 " driver (ICD) installed?\nPlease look at the Getting Started"
1920 " guide for additional information.\n",
1921 "vkEnumeratePhysicalDevices Failure");
1922 }
1923
1924 /* Look for validation layers */
1925 if (demo->validate) {
1926 validation_found = 0;
1927 demo->enabled_layer_count = 0;
1928 uint32_t device_layer_count = 0;
1929 err =
1930 vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL);
1931 assert(!err);
1932
1933 if (device_layer_count > 0) {
1934 VkLayerProperties *device_layers =
1935 malloc(sizeof (VkLayerProperties) * device_layer_count);
1936 err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count,
1937 device_layers);
1938 assert(!err);
1939
1940
1941 validation_found = demo_check_layers(device_validation_layer_count,
1942 demo->device_validation_layers,
1943 device_layer_count,
1944 device_layers);
1945 demo->enabled_layer_count = device_validation_layer_count;
1946
1947 free(device_layers);
1948 }
1949
1950 if (!validation_found) {
1951 ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find "
1952 "a required validation layer.\n\n"
1953 "Please look at the Getting Started guide for additional "
1954 "information.\n",
1955 "vkCreateDevice Failure");
1956 }
1957 }
1958
1959 /* Look for device extensions */
1960 uint32_t device_extension_count = 0;
1961 VkBool32 swapchainExtFound = 0;
1962 demo->enabled_extension_count = 0;
1963 memset(demo->extension_names, 0, sizeof(demo->extension_names));
1964
1965 err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL,
1966 &device_extension_count, NULL);
1967 assert(!err);
1968
1969 if (device_extension_count > 0) {
1970 VkExtensionProperties *device_extensions =
1971 malloc(sizeof(VkExtensionProperties) * device_extension_count);
1972 err = vkEnumerateDeviceExtensionProperties(
1973 demo->gpu, NULL, &device_extension_count, device_extensions);
1974 assert(!err);
1975
1976 for (uint32_t i = 0; i < device_extension_count; i++) {
1977 if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
1978 device_extensions[i].extensionName)) {
1979 swapchainExtFound = 1;
1980 demo->extension_names[demo->enabled_extension_count++] =
1981 VK_KHR_SWAPCHAIN_EXTENSION_NAME;
1982 }
1983 assert(demo->enabled_extension_count < 64);
1984 }
1985
1986 free(device_extensions);
1987 }
1988
1989 if (!swapchainExtFound) {
1990 ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find "
1991 "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME
1992 " extension.\n\nDo you have a compatible "
1993 "Vulkan installable client driver (ICD) installed?\nPlease "
1994 "look at the Getting Started guide for additional "
1995 "information.\n",
1996 "vkCreateInstance Failure");
1997 }
1998
1999 if (demo->validate) {
2000 demo->CreateDebugReportCallback =
2001 (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(
2002 demo->inst, "vkCreateDebugReportCallbackEXT");
2003 demo->DestroyDebugReportCallback =
2004 (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(
2005 demo->inst, "vkDestroyDebugReportCallbackEXT");
2006 if (!demo->CreateDebugReportCallback) {
2007 ERR_EXIT(
2008 "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n",
2009 "vkGetProcAddr Failure");
2010 }
2011 if (!demo->DestroyDebugReportCallback) {
2012 ERR_EXIT(
2013 "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n",
2014 "vkGetProcAddr Failure");
2015 }
2016 demo->DebugReportMessage =
2017 (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(
2018 demo->inst, "vkDebugReportMessageEXT");
2019 if (!demo->DebugReportMessage) {
2020 ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n",
2021 "vkGetProcAddr Failure");
2022 }
2023
2024 VkDebugReportCallbackCreateInfoEXT dbgCreateInfo;
2025 dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
2026 dbgCreateInfo.flags =
2027 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
2028 dbgCreateInfo.pfnCallback = dbgFunc;
2029 dbgCreateInfo.pUserData = NULL;
2030 dbgCreateInfo.pNext = NULL;
2031 err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL,
2032 &demo->msg_callback);
2033 switch (err) {
2034 case VK_SUCCESS:
2035 break;
2036 case VK_ERROR_OUT_OF_HOST_MEMORY:
2037 ERR_EXIT("CreateDebugReportCallback: out of host memory\n",
2038 "CreateDebugReportCallback Failure");
2039 break;
2040 default:
2041 ERR_EXIT("CreateDebugReportCallback: unknown failure\n",
2042 "CreateDebugReportCallback Failure");
2043 break;
2044 }
2045 }
2046
2047 // Having these GIPA queries of device extension entry points both
2048 // BEFORE and AFTER vkCreateDevice is a good test for the loader
2049 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR);
2050 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR);
2051 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR);
2052 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR);
2053 GET_INSTANCE_PROC_ADDR(demo->inst, CreateSwapchainKHR);
2054 GET_INSTANCE_PROC_ADDR(demo->inst, DestroySwapchainKHR);
2055 GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR);
2056 GET_INSTANCE_PROC_ADDR(demo->inst, AcquireNextImageKHR);
2057 GET_INSTANCE_PROC_ADDR(demo->inst, QueuePresentKHR);
2058
2059 vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props);
2060
2061 // Query with NULL data to get count
2062 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
2063 NULL);
2064
2065 demo->queue_props = (VkQueueFamilyProperties *)malloc(
2066 demo->queue_count * sizeof(VkQueueFamilyProperties));
2067 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
2068 demo->queue_props);
2069 assert(demo->queue_count >= 1);
2070
2071 VkPhysicalDeviceFeatures features;
2072 vkGetPhysicalDeviceFeatures(demo->gpu, &features);
2073
2074 if (!features.shaderClipDistance) {
2075 ERR_EXIT("Required device feature `shaderClipDistance` not supported\n",
2076 "GetPhysicalDeviceFeatures failure");
2077 }
2078
2079 // Graphics queue and MemMgr queue can be separate.
2080 // TODO: Add support for separate queues, including synchronization,
2081 // and appropriate tracking for QueueSubmit
2082 }
2083
demo_init_device(struct demo * demo)2084 static void demo_init_device(struct demo *demo) {
2085 VkResult U_ASSERT_ONLY err;
2086
2087 float queue_priorities[1] = {0.0};
2088 const VkDeviceQueueCreateInfo queue = {
2089 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
2090 .pNext = NULL,
2091 .queueFamilyIndex = demo->graphics_queue_node_index,
2092 .queueCount = 1,
2093 .pQueuePriorities = queue_priorities};
2094
2095 VkPhysicalDeviceFeatures features = {
2096 .shaderClipDistance = VK_TRUE,
2097 };
2098
2099 VkDeviceCreateInfo device = {
2100 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
2101 .pNext = NULL,
2102 .queueCreateInfoCount = 1,
2103 .pQueueCreateInfos = &queue,
2104 .enabledLayerCount = demo->enabled_layer_count,
2105 .ppEnabledLayerNames =
2106 (const char *const *)((demo->validate)
2107 ? demo->device_validation_layers
2108 : NULL),
2109 .enabledExtensionCount = demo->enabled_extension_count,
2110 .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
2111 .pEnabledFeatures = &features,
2112 };
2113
2114 err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device);
2115 assert(!err);
2116 }
2117
demo_init_vk_swapchain(struct demo * demo)2118 static void demo_init_vk_swapchain(struct demo *demo) {
2119 VkResult U_ASSERT_ONLY err;
2120 uint32_t i;
2121
2122 // Create a WSI surface for the window:
2123 #ifdef _WIN32
2124 VkWin32SurfaceCreateInfoKHR createInfo;
2125 createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
2126 createInfo.pNext = NULL;
2127 createInfo.flags = 0;
2128 createInfo.hinstance = demo->connection;
2129 createInfo.hwnd = demo->window;
2130
2131 err =
2132 vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
2133
2134 #else // _WIN32
2135 VkXcbSurfaceCreateInfoKHR createInfo;
2136 createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
2137 createInfo.pNext = NULL;
2138 createInfo.flags = 0;
2139 createInfo.connection = demo->connection;
2140 createInfo.window = demo->window;
2141
2142 err = vkCreateXcbSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
2143 #endif // _WIN32
2144
2145 // Iterate over each queue to learn whether it supports presenting:
2146 VkBool32 *supportsPresent =
2147 (VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32));
2148 for (i = 0; i < demo->queue_count; i++) {
2149 demo->fpGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface,
2150 &supportsPresent[i]);
2151 }
2152
2153 // Search for a graphics and a present queue in the array of queue
2154 // families, try to find one that supports both
2155 uint32_t graphicsQueueNodeIndex = UINT32_MAX;
2156 uint32_t presentQueueNodeIndex = UINT32_MAX;
2157 for (i = 0; i < demo->queue_count; i++) {
2158 if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
2159 if (graphicsQueueNodeIndex == UINT32_MAX) {
2160 graphicsQueueNodeIndex = i;
2161 }
2162
2163 if (supportsPresent[i] == VK_TRUE) {
2164 graphicsQueueNodeIndex = i;
2165 presentQueueNodeIndex = i;
2166 break;
2167 }
2168 }
2169 }
2170 if (presentQueueNodeIndex == UINT32_MAX) {
2171 // If didn't find a queue that supports both graphics and present, then
2172 // find a separate present queue.
2173 for (uint32_t i = 0; i < demo->queue_count; ++i) {
2174 if (supportsPresent[i] == VK_TRUE) {
2175 presentQueueNodeIndex = i;
2176 break;
2177 }
2178 }
2179 }
2180 free(supportsPresent);
2181
2182 // Generate error if could not find both a graphics and a present queue
2183 if (graphicsQueueNodeIndex == UINT32_MAX ||
2184 presentQueueNodeIndex == UINT32_MAX) {
2185 ERR_EXIT("Could not find a graphics and a present queue\n",
2186 "Swapchain Initialization Failure");
2187 }
2188
2189 // TODO: Add support for separate queues, including presentation,
2190 // synchronization, and appropriate tracking for QueueSubmit.
2191 // NOTE: While it is possible for an application to use a separate graphics
2192 // and a present queues, this demo program assumes it is only using
2193 // one:
2194 if (graphicsQueueNodeIndex != presentQueueNodeIndex) {
2195 ERR_EXIT("Could not find a common graphics and a present queue\n",
2196 "Swapchain Initialization Failure");
2197 }
2198
2199 demo->graphics_queue_node_index = graphicsQueueNodeIndex;
2200
2201 demo_init_device(demo);
2202
2203 vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0,
2204 &demo->queue);
2205
2206 // Get the list of VkFormat's that are supported:
2207 uint32_t formatCount;
2208 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
2209 &formatCount, NULL);
2210 assert(!err);
2211 VkSurfaceFormatKHR *surfFormats =
2212 (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
2213 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
2214 &formatCount, surfFormats);
2215 assert(!err);
2216 // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
2217 // the surface has no preferred format. Otherwise, at least one
2218 // supported format will be returned.
2219 if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
2220 demo->format = VK_FORMAT_B8G8R8A8_UNORM;
2221 } else {
2222 assert(formatCount >= 1);
2223 demo->format = surfFormats[0].format;
2224 }
2225 demo->color_space = surfFormats[0].colorSpace;
2226
2227 // Get Memory information and properties
2228 vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
2229 }
2230
demo_init_connection(struct demo * demo)2231 static void demo_init_connection(struct demo *demo) {
2232 #ifndef _WIN32
2233 const xcb_setup_t *setup;
2234 xcb_screen_iterator_t iter;
2235 int scr;
2236
2237 demo->connection = xcb_connect(NULL, &scr);
2238 if (demo->connection == NULL) {
2239 printf("Cannot find a compatible Vulkan installable client driver "
2240 "(ICD).\nExiting ...\n");
2241 fflush(stdout);
2242 exit(1);
2243 }
2244
2245 setup = xcb_get_setup(demo->connection);
2246 iter = xcb_setup_roots_iterator(setup);
2247 while (scr-- > 0)
2248 xcb_screen_next(&iter);
2249
2250 demo->screen = iter.data;
2251 #endif // _WIN32
2252 }
2253
2254 #ifdef _WIN32
demo_init(struct demo * demo,HINSTANCE hInstance,LPSTR pCmdLine)2255 static void demo_init(struct demo *demo, HINSTANCE hInstance, LPSTR pCmdLine)
2256 #else // _WIN32
2257 static void demo_init(struct demo *demo, const int argc, const char *argv[])
2258 #endif // _WIN32
2259 {
2260 bool argv_error = false;
2261
2262 memset(demo, 0, sizeof(*demo));
2263
2264 #ifdef _WIN32
2265 demo->connection = hInstance;
2266 strncpy(demo->name, APP_SHORT_NAME, APP_NAME_STR_LEN);
2267
2268 if (strncmp(pCmdLine, "--use_staging", strlen("--use_staging")) == 0)
2269 demo->use_staging_buffer = true;
2270 else if (strncmp(pCmdLine, "--validate", strlen("--validate")) == 0)
2271 demo->validate = true;
2272 else if (strlen(pCmdLine) != 0) {
2273 fprintf(stderr, "Do not recognize argument \"%s\".\n", pCmdLine);
2274 argv_error = true;
2275 }
2276 #else // _WIN32
2277 for (int i = 0; i < argc; i++) {
2278 if (strncmp(argv[i], "--use_staging", strlen("--use_staging")) == 0)
2279 demo->use_staging_buffer = true;
2280 if (strncmp(argv[i], "--validate", strlen("--validate")) == 0)
2281 demo->validate = true;
2282 }
2283 #endif // _WIN32
2284 if (argv_error) {
2285 fprintf(stderr, "Usage:\n %s [--use_staging] [--validate]\n", APP_SHORT_NAME);
2286 fflush(stderr);
2287 exit(1);
2288 }
2289
2290 demo_init_connection(demo);
2291 demo_init_vk(demo);
2292
2293 demo->width = 300;
2294 demo->height = 300;
2295 demo->depthStencil = 1.0;
2296 demo->depthIncrement = -0.01f;
2297 }
2298
demo_cleanup(struct demo * demo)2299 static void demo_cleanup(struct demo *demo) {
2300 uint32_t i;
2301
2302 demo->prepared = false;
2303
2304 for (i = 0; i < demo->swapchainImageCount; i++) {
2305 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
2306 }
2307 free(demo->framebuffers);
2308 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
2309
2310 if (demo->setup_cmd) {
2311 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd);
2312 }
2313 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd);
2314 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
2315
2316 vkDestroyPipeline(demo->device, demo->pipeline, NULL);
2317 vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
2318 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
2319 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
2320
2321 vkDestroyBuffer(demo->device, demo->vertices.buf, NULL);
2322 vkFreeMemory(demo->device, demo->vertices.mem, NULL);
2323
2324 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
2325 vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
2326 vkDestroyImage(demo->device, demo->textures[i].image, NULL);
2327 vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
2328 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
2329 }
2330
2331 for (i = 0; i < demo->swapchainImageCount; i++) {
2332 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
2333 }
2334
2335 vkDestroyImageView(demo->device, demo->depth.view, NULL);
2336 vkDestroyImage(demo->device, demo->depth.image, NULL);
2337 vkFreeMemory(demo->device, demo->depth.mem, NULL);
2338
2339 demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL);
2340 free(demo->buffers);
2341
2342 vkDestroyDevice(demo->device, NULL);
2343 if (demo->validate) {
2344 demo->DestroyDebugReportCallback(demo->inst, demo->msg_callback, NULL);
2345 }
2346 vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
2347 vkDestroyInstance(demo->inst, NULL);
2348
2349 free(demo->queue_props);
2350
2351 #ifndef _WIN32
2352 xcb_destroy_window(demo->connection, demo->window);
2353 xcb_disconnect(demo->connection);
2354 free(demo->atom_wm_delete_window);
2355 #endif // _WIN32
2356 }
2357
demo_resize(struct demo * demo)2358 static void demo_resize(struct demo *demo) {
2359 uint32_t i;
2360
2361 // Don't react to resize until after first initialization.
2362 if (!demo->prepared) {
2363 return;
2364 }
2365 // In order to properly resize the window, we must re-create the swapchain
2366 // AND redo the command buffers, etc.
2367 //
2368 // First, perform part of the demo_cleanup() function:
2369 demo->prepared = false;
2370
2371 for (i = 0; i < demo->swapchainImageCount; i++) {
2372 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
2373 }
2374 free(demo->framebuffers);
2375 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
2376
2377 if (demo->setup_cmd) {
2378 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd);
2379 }
2380 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd);
2381 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
2382
2383 vkDestroyPipeline(demo->device, demo->pipeline, NULL);
2384 vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
2385 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
2386 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
2387
2388 vkDestroyBuffer(demo->device, demo->vertices.buf, NULL);
2389 vkFreeMemory(demo->device, demo->vertices.mem, NULL);
2390
2391 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
2392 vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
2393 vkDestroyImage(demo->device, demo->textures[i].image, NULL);
2394 vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
2395 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
2396 }
2397
2398 for (i = 0; i < demo->swapchainImageCount; i++) {
2399 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
2400 }
2401
2402 vkDestroyImageView(demo->device, demo->depth.view, NULL);
2403 vkDestroyImage(demo->device, demo->depth.image, NULL);
2404 vkFreeMemory(demo->device, demo->depth.mem, NULL);
2405
2406 free(demo->buffers);
2407
2408 // Second, re-perform the demo_prepare() function, which will re-create the
2409 // swapchain:
2410 demo_prepare(demo);
2411 }
2412
2413 #ifdef _WIN32
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR pCmdLine,int nCmdShow)2414 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
2415 LPSTR pCmdLine, int nCmdShow) {
2416 MSG msg; // message
2417 bool done; // flag saying when app is complete
2418
2419 demo_init(&demo, hInstance, pCmdLine);
2420 demo_create_window(&demo);
2421 demo_init_vk_swapchain(&demo);
2422
2423 demo_prepare(&demo);
2424
2425 done = false; // initialize loop condition variable
2426 /* main message loop*/
2427 while (!done) {
2428 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
2429 if (msg.message == WM_QUIT) // check for a quit message
2430 {
2431 done = true; // if found, quit app
2432 } else {
2433 /* Translate and dispatch to event queue*/
2434 TranslateMessage(&msg);
2435 DispatchMessage(&msg);
2436 }
2437 RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT);
2438 }
2439
2440 demo_cleanup(&demo);
2441
2442 return (int)msg.wParam;
2443 }
2444 #else // _WIN32
main(const int argc,const char * argv[])2445 int main(const int argc, const char *argv[]) {
2446 struct demo demo;
2447
2448 demo_init(&demo, argc, argv);
2449 demo_create_window(&demo);
2450 demo_init_vk_swapchain(&demo);
2451
2452 demo_prepare(&demo);
2453 demo_run(&demo);
2454
2455 demo_cleanup(&demo);
2456
2457 return 0;
2458 }
2459 #endif // _WIN32
2460