• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &copy_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