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  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Author: Chia-I Wu <olv@lunarg.com>
19  * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
20  * Author: Ian Elliott <ian@LunarG.com>
21  * Author: Ian Elliott <ianelliott@google.com>
22  * Author: Jon Ashburn <jon@lunarg.com>
23  * Author: Gwan-gyeong Mun <elongbug@gmail.com>
24  * Author: Tony Barbour <tony@LunarG.com>
25  * Author: Bill Hollings <bill.hollings@brenwill.com>
26  */
27 
28  #define _GNU_SOURCE
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdbool.h>
34 #include <assert.h>
35 #include <signal.h>
36 #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
37 #include <X11/Xutil.h>
38 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
39 #include <linux/input.h>
40 #endif
41 
42 #ifdef _WIN32
43 #pragma comment(linker, "/subsystem:windows")
44 #define APP_NAME_STR_LEN 80
45 #endif  // _WIN32
46 
47 #ifdef ANDROID
48 #include "vulkan_wrapper.h"
49 #include <android/trace.h>
50 #else
51 #include <vulkan/vulkan.h>
52 
53 // The following 2 macros stub-out the Android systrace interface:
54 #define ATrace_beginSection(...) ((void)0)
55 #define ATrace_endSection(...) ((void)0)
56 #endif
57 
58 #include <vulkan/vk_platform.h>
59 #include "linmath.h"
60 #include "object_type_string_helper.h"
61 
62 #include <swappyVk/SwappyVk.h>
63 
64 #include "gettime.h"
65 #include "inttypes.h"
66 #define MILLION 1000000L
67 #define BILLION 1000000000L
68 
69 #define DEMO_TEXTURE_COUNT 1
70 #define APP_SHORT_NAME "cube"
71 #define APP_LONG_NAME "The Vulkan Cube Demo Program"
72 
73 // Allow a maximum of two outstanding presentation operations.
74 #define FRAME_LAG 2
75 
76 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
77 
78 #if defined(NDEBUG) && defined(__GNUC__)
79 #define U_ASSERT_ONLY __attribute__((unused))
80 #else
81 #define U_ASSERT_ONLY
82 #endif
83 
84 #if defined(__GNUC__)
85 #define UNUSED __attribute__((unused))
86 #else
87 #define UNUSED
88 #endif
89 
90 #ifdef _WIN32
91 bool in_callback = false;
92 #define ERR_EXIT(err_msg, err_class)                                             \
93     do {                                                                         \
94         if (!demo->suppress_popups) MessageBox(NULL, err_msg, err_class, MB_OK); \
95         exit(1);                                                                 \
96     } while (0)
DbgMsg(char * fmt,...)97 void DbgMsg(char *fmt, ...) {
98     va_list va;
99     va_start(va, fmt);
100     printf(fmt, va);
101     fflush(stdout);
102     va_end(va);
103 }
104 
105 #elif defined __ANDROID__
106 #include <android/log.h>
107 #define ERR_EXIT(err_msg, err_class)                                    \
108     do {                                                                \
109         ((void)__android_log_print(ANDROID_LOG_INFO, "Cube", err_msg)); \
110         exit(1);                                                        \
111     } while (0)
112 #ifdef VARARGS_WORKS_ON_ANDROID
DbgMsg(const char * fmt,...)113 void DbgMsg(const char *fmt, ...) {
114     va_list va;
115     va_start(va, fmt);
116     __android_log_print(ANDROID_LOG_INFO, "Cube", fmt, va);
117     va_end(va);
118 }
119 #else  // VARARGS_WORKS_ON_ANDROID
120 #define DbgMsg(fmt, ...)                                                           \
121     do {                                                                           \
122         ((void)__android_log_print(ANDROID_LOG_INFO, "Cube", fmt, ##__VA_ARGS__)); \
123     } while (0)
124 #endif  // VARARGS_WORKS_ON_ANDROID
125 #else
126 #define ERR_EXIT(err_msg, err_class) \
127     do {                             \
128         printf("%s\n", err_msg);     \
129         fflush(stdout);              \
130         exit(1);                     \
131     } while (0)
DbgMsg(char * fmt,...)132 void DbgMsg(char *fmt, ...) {
133     va_list va;
134     va_start(va, fmt);
135     printf(fmt, va);
136     fflush(stdout);
137     va_end(va);
138 }
139 #endif
140 
141 // Uncomment the following line in order to log events for the first 10 frames:
142 //#define ANDROID_LOG_PRESENT_EVENTS
143 typedef enum TimingEvent {
144     EVENT_CALLING_ANI = 1,
145     EVENT_CALLED_ANI  = 2,
146     EVENT_CALLING_QS  = 3,
147     EVENT_CALLED_QS   = 4,
148     EVENT_CALLING_QP  = 5,
149     EVENT_CALLED_QP   = 6,
150     EVENT_CALLING_WFF = 7,
151     EVENT_CALLED_WFF  = 8,
152 } TimingEvent;
153 struct TrackTiming {
154     uint64_t now;
155     uint64_t lastEvent;
156     uint64_t lastANI;
157     uint32_t frame;
158 };
159 struct TrackTiming timing;
InitializeTrackTiming()160 void InitializeTrackTiming() {
161     timing.now = 0;
162     timing.lastEvent = 0;
163     timing.lastANI = 0;
164     timing.frame = 0;
165 }
logEvent(TimingEvent event)166 void logEvent(TimingEvent event)
167 {
168 #ifdef ANDROID_LOG_PRESENT_EVENTS
169     timing.now = getTimeInNanoseconds();
170     uint64_t delta = timing.now - timing.lastEvent;
171     uint64_t deltaANI = timing.now - timing.lastANI;
172     if (timing.frame > 30) return;
173     switch (event) {
174         case EVENT_CALLING_ANI:
175             DbgMsg("About to call vkAcquireNextImageKHR at %llu nsec"
176                    " (delta %llu)", timing.now, delta);
177             break;
178         case EVENT_CALLED_ANI:
179             DbgMsg("After calling vkAcquireNextImageKHR at %llu nsec"
180                    " (delta %llu) (delta from last ANI: %llu)",
181                    timing.now, delta, deltaANI);
182             timing.lastANI = timing.now;
183             timing.frame++;
184             break;
185         case EVENT_CALLING_QS:
186             DbgMsg("About to call vkQueueSubmit at         %llu nsec"
187                    " (delta %llu)", timing.now, delta);
188             break;
189         case EVENT_CALLED_QS:
190             DbgMsg("After calling vkQueueSubmit at         %llu nsec"
191                    " (delta %llu)", timing.now, delta);
192             break;
193         case EVENT_CALLING_QP:
194             DbgMsg("About to call vkQueuePresentKHR at     %llu nsec"
195                    " (delta %llu)", timing.now, delta);
196             break;
197         case EVENT_CALLED_QP:
198             DbgMsg("After calling vkQueuePresentKHR at     %llu nsec"
199                    " (delta %llu)", timing.now, delta);
200             break;
201         case EVENT_CALLING_WFF:
202             DbgMsg("About to call vkWaitForFences at       %llu nsec"
203                    " (delta %llu)", timing.now, delta);
204             break;
205         case EVENT_CALLED_WFF:
206             DbgMsg("After calling vkWaitForFences at       %llu nsec"
207                    " (delta %llu)", timing.now, delta);
208             break;
209     }
210     timing.lastEvent = timing.now;
211 #endif // ANDROID_LOG_PRESENT_EVENTS
212 }
213 
214 #define GET_INSTANCE_PROC_ADDR(inst, entrypoint)                                                              \
215     {                                                                                                         \
216         demo->fp##entrypoint = (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint);             \
217         if (demo->fp##entrypoint == NULL) {                                                                   \
218             ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, "vkGetInstanceProcAddr Failure"); \
219         }                                                                                                     \
220     }
221 
222 static PFN_vkGetDeviceProcAddr g_gdpa = NULL;
223 
224 #define GET_DEVICE_PROC_ADDR(dev, entrypoint)                                                                    \
225     {                                                                                                            \
226         if (!g_gdpa) g_gdpa = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(demo->inst, "vkGetDeviceProcAddr"); \
227         demo->fp##entrypoint = (PFN_vk##entrypoint)g_gdpa(dev, "vk" #entrypoint);                                \
228         if (demo->fp##entrypoint == NULL) {                                                                      \
229             ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, "vkGetDeviceProcAddr Failure");        \
230         }                                                                                                        \
231     }
232 
233 /*
234  * structure to track all objects related to a texture.
235  */
236 struct texture_object {
237     VkSampler sampler;
238 
239     VkImage image;
240     VkBuffer buffer;
241     VkImageLayout imageLayout;
242 
243     VkMemoryAllocateInfo mem_alloc;
244     VkDeviceMemory mem;
245     VkImageView view;
246     int32_t tex_width, tex_height;
247 };
248 
249 static char *tex_files[] = {"lunarg.ppm"};
250 
251 static int validation_error = 0;
252 
253 struct vktexcube_vs_uniform {
254     // Must start with MVP
255     float mvp[4][4];
256     float position[12 * 3][4];
257     float attr[12 * 3][4];
258 };
259 
260 //--------------------------------------------------------------------------------------
261 // Mesh and VertexFormat Data
262 //--------------------------------------------------------------------------------------
263 // clang-format off
264 static const float g_vertex_buffer_data[] = {
265     -1.0f,-1.0f,-1.0f,  // -X side
266     -1.0f,-1.0f, 1.0f,
267     -1.0f, 1.0f, 1.0f,
268     -1.0f, 1.0f, 1.0f,
269     -1.0f, 1.0f,-1.0f,
270     -1.0f,-1.0f,-1.0f,
271 
272     -1.0f,-1.0f,-1.0f,  // -Z side
273      1.0f, 1.0f,-1.0f,
274      1.0f,-1.0f,-1.0f,
275     -1.0f,-1.0f,-1.0f,
276     -1.0f, 1.0f,-1.0f,
277      1.0f, 1.0f,-1.0f,
278 
279     -1.0f,-1.0f,-1.0f,  // -Y side
280      1.0f,-1.0f,-1.0f,
281      1.0f,-1.0f, 1.0f,
282     -1.0f,-1.0f,-1.0f,
283      1.0f,-1.0f, 1.0f,
284     -1.0f,-1.0f, 1.0f,
285 
286     -1.0f, 1.0f,-1.0f,  // +Y side
287     -1.0f, 1.0f, 1.0f,
288      1.0f, 1.0f, 1.0f,
289     -1.0f, 1.0f,-1.0f,
290      1.0f, 1.0f, 1.0f,
291      1.0f, 1.0f,-1.0f,
292 
293      1.0f, 1.0f,-1.0f,  // +X side
294      1.0f, 1.0f, 1.0f,
295      1.0f,-1.0f, 1.0f,
296      1.0f,-1.0f, 1.0f,
297      1.0f,-1.0f,-1.0f,
298      1.0f, 1.0f,-1.0f,
299 
300     -1.0f, 1.0f, 1.0f,  // +Z side
301     -1.0f,-1.0f, 1.0f,
302      1.0f, 1.0f, 1.0f,
303     -1.0f,-1.0f, 1.0f,
304      1.0f,-1.0f, 1.0f,
305      1.0f, 1.0f, 1.0f,
306 };
307 
308 static const float g_uv_buffer_data[] = {
309     0.0f, 1.0f,  // -X side
310     1.0f, 1.0f,
311     1.0f, 0.0f,
312     1.0f, 0.0f,
313     0.0f, 0.0f,
314     0.0f, 1.0f,
315 
316     1.0f, 1.0f,  // -Z side
317     0.0f, 0.0f,
318     0.0f, 1.0f,
319     1.0f, 1.0f,
320     1.0f, 0.0f,
321     0.0f, 0.0f,
322 
323     1.0f, 0.0f,  // -Y side
324     1.0f, 1.0f,
325     0.0f, 1.0f,
326     1.0f, 0.0f,
327     0.0f, 1.0f,
328     0.0f, 0.0f,
329 
330     1.0f, 0.0f,  // +Y side
331     0.0f, 0.0f,
332     0.0f, 1.0f,
333     1.0f, 0.0f,
334     0.0f, 1.0f,
335     1.0f, 1.0f,
336 
337     1.0f, 0.0f,  // +X side
338     0.0f, 0.0f,
339     0.0f, 1.0f,
340     0.0f, 1.0f,
341     1.0f, 1.0f,
342     1.0f, 0.0f,
343 
344     0.0f, 0.0f,  // +Z side
345     0.0f, 1.0f,
346     1.0f, 0.0f,
347     0.0f, 1.0f,
348     1.0f, 1.0f,
349     1.0f, 0.0f,
350 };
351 // clang-format on
352 
dumpMatrix(const char * note,mat4x4 MVP)353 void dumpMatrix(const char *note, mat4x4 MVP) {
354     int i;
355 
356     printf("%s: \n", note);
357     for (i = 0; i < 4; i++) {
358         printf("%f, %f, %f, %f\n", MVP[i][0], MVP[i][1], MVP[i][2], MVP[i][3]);
359     }
360     printf("\n");
361     fflush(stdout);
362 }
363 
dumpVec4(const char * note,vec4 vector)364 void dumpVec4(const char *note, vec4 vector) {
365     printf("%s: \n", note);
366     printf("%f, %f, %f, %f\n", vector[0], vector[1], vector[2], vector[3]);
367     printf("\n");
368     fflush(stdout);
369 }
370 
371 typedef struct {
372     VkImage image;
373     VkCommandBuffer cmd;
374     VkCommandBuffer graphics_to_present_cmd;
375     VkImageView view;
376     VkBuffer uniform_buffer;
377     VkDeviceMemory uniform_memory;
378     VkFramebuffer framebuffer;
379     VkDescriptorSet descriptor_set;
380 } SwapchainImageResources;
381 
382 struct demo {
383 #if defined(VK_USE_PLATFORM_WIN32_KHR)
384 #define APP_NAME_STR_LEN 80
385     HINSTANCE connection;         // hInstance - Windows Instance
386     char name[APP_NAME_STR_LEN];  // Name to put on the window/icon
387     HWND window;                  // hWnd - window handle
388     POINT minsize;                // minimum window size
389 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
390     Display *display;
391     Window xlib_window;
392     Atom xlib_wm_delete_window;
393 #elif defined(VK_USE_PLATFORM_XCB_KHR)
394     Display *display;
395     xcb_connection_t *connection;
396     xcb_screen_t *screen;
397     xcb_window_t xcb_window;
398     xcb_intern_atom_reply_t *atom_wm_delete_window;
399 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
400     struct wl_display *display;
401     struct wl_registry *registry;
402     struct wl_compositor *compositor;
403     struct wl_surface *window;
404     struct wl_shell *shell;
405     struct wl_shell_surface *shell_surface;
406     struct wl_seat *seat;
407     struct wl_pointer *pointer;
408     struct wl_keyboard *keyboard;
409 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
410     struct ANativeWindow *window;
411 #elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
412     void *window;
413 #endif
414     VkSurfaceKHR surface;
415     bool prepared;
416     bool use_staging_buffer;
417     bool separate_present_queue;
418     bool is_minimized;
419 
420     bool VK_KHR_incremental_present_enabled;
421 
422     bool VK_GOOGLE_display_timing_enabled;
423     bool syncd_with_actual_presents;
424     uint64_t refresh_duration;
425     uint64_t refresh_duration_multiplier;
426     uint64_t target_IPD;  // image present duration (inverse of frame rate)
427     uint64_t prev_desired_present_time;
428     uint32_t next_present_id;
429     uint32_t last_early_id;  // 0 if no early images
430     uint32_t last_late_id;   // 0 if no late images
431 
432     VkInstance inst;
433     VkPhysicalDevice gpu;
434     VkDevice device;
435     VkQueue graphics_queue;
436     VkQueue present_queue;
437     uint32_t graphics_queue_family_index;
438     uint32_t present_queue_family_index;
439     VkSemaphore image_acquired_semaphores[FRAME_LAG];
440     VkSemaphore draw_complete_semaphores[FRAME_LAG];
441     VkSemaphore image_ownership_semaphores[FRAME_LAG];
442     VkPhysicalDeviceProperties gpu_props;
443     VkQueueFamilyProperties *queue_props;
444     VkPhysicalDeviceMemoryProperties memory_properties;
445 
446     uint32_t enabled_extension_count;
447     uint32_t enabled_layer_count;
448     char *extension_names[64];
449     char *enabled_layers[64];
450 
451     int width, height;
452     VkFormat format;
453     VkColorSpaceKHR color_space;
454 
455     PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
456     PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
457     PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR;
458     PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR;
459     PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
460     PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
461     PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
462     PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
463     PFN_vkQueuePresentKHR fpQueuePresentKHR;
464     PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE;
465     PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE;
466     uint32_t swapchainImageCount;
467     VkSwapchainKHR swapchain;
468     SwapchainImageResources *swapchain_image_resources;
469     VkPresentModeKHR presentMode;
470     VkFence fences[FRAME_LAG];
471     int frame_index;
472 
473     VkCommandPool cmd_pool;
474     VkCommandPool present_cmd_pool;
475 
476     struct {
477         VkFormat format;
478 
479         VkImage image;
480         VkMemoryAllocateInfo mem_alloc;
481         VkDeviceMemory mem;
482         VkImageView view;
483     } depth;
484 
485     struct texture_object textures[DEMO_TEXTURE_COUNT];
486     struct texture_object staging_texture;
487 
488     VkCommandBuffer cmd;  // Buffer for initialization commands
489     VkPipelineLayout pipeline_layout;
490     VkDescriptorSetLayout desc_layout;
491     VkPipelineCache pipelineCache;
492     VkRenderPass render_pass;
493     VkPipeline pipeline;
494 
495     mat4x4 projection_matrix;
496     mat4x4 view_matrix;
497     mat4x4 model_matrix;
498 
499     float spin_angle;
500     float spin_increment;
501     bool pause;
502 
503     VkShaderModule vert_shader_module;
504     VkShaderModule frag_shader_module;
505 
506     VkDescriptorPool desc_pool;
507 
508     bool quit;
509     int32_t curFrame;
510     int32_t frameCount;
511     bool validate;
512     bool validate_checks_disabled;
513     bool use_break;
514     bool suppress_popups;
515 
516     PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT;
517     PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT;
518     PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT;
519     PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT;
520     PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT;
521     PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT;
522     PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT;
523     VkDebugUtilsMessengerEXT dbg_messenger;
524 
525     uint32_t current_buffer;
526     uint32_t queue_family_count;
527 };
528 
debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,VkDebugUtilsMessageTypeFlagsEXT messageType,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void * pUserData)529 VKAPI_ATTR VkBool32 VKAPI_CALL debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
530                                                         VkDebugUtilsMessageTypeFlagsEXT messageType,
531                                                         const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
532                                                         void *pUserData) {
533     char prefix[64] = "";
534     char *message = (char *)malloc(strlen(pCallbackData->pMessage) + 5000);
535     assert(message);
536     struct demo *demo = (struct demo *)pUserData;
537 
538     if (demo->use_break) {
539 #ifndef WIN32
540         raise(SIGTRAP);
541 #else
542         DebugBreak();
543 #endif
544     }
545 
546     if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
547         strcat(prefix, "VERBOSE : ");
548     } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
549         strcat(prefix, "INFO : ");
550     } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
551         strcat(prefix, "WARNING : ");
552     } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
553         strcat(prefix, "ERROR : ");
554     }
555 
556     if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
557         strcat(prefix, "GENERAL");
558     } else {
559         if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
560             strcat(prefix, "VALIDATION");
561             validation_error = 1;
562         }
563         if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
564             if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
565                 strcat(prefix, "|");
566             }
567             strcat(prefix, "PERFORMANCE");
568         }
569     }
570 
571     sprintf(message, "%s - Message Id Number: %d | Message Id Name: %s\n\t%s\n", prefix, pCallbackData->messageIdNumber,
572             pCallbackData->pMessageIdName, pCallbackData->pMessage);
573     if (pCallbackData->objectCount > 0) {
574         char tmp_message[500];
575         sprintf(tmp_message, "\n\tObjects - %d\n", pCallbackData->objectCount);
576         strcat(message, tmp_message);
577         for (uint32_t object = 0; object < pCallbackData->objectCount; ++object) {
578             if (NULL != pCallbackData->pObjects[object].pObjectName && strlen(pCallbackData->pObjects[object].pObjectName) > 0) {
579                 sprintf(tmp_message, "\t\tObject[%d] - %s, Handle %p, Name \"%s\"\n", object,
580                         string_VkObjectType(pCallbackData->pObjects[object].objectType),
581                         (void *)(pCallbackData->pObjects[object].objectHandle), pCallbackData->pObjects[object].pObjectName);
582             } else {
583                 sprintf(tmp_message, "\t\tObject[%d] - %s, Handle %p\n", object,
584                         string_VkObjectType(pCallbackData->pObjects[object].objectType),
585                         (void *)(pCallbackData->pObjects[object].objectHandle));
586             }
587             strcat(message, tmp_message);
588         }
589     }
590     if (pCallbackData->cmdBufLabelCount > 0) {
591         char tmp_message[500];
592         sprintf(tmp_message, "\n\tCommand Buffer Labels - %d\n", pCallbackData->cmdBufLabelCount);
593         strcat(message, tmp_message);
594         for (uint32_t cmd_buf_label = 0; cmd_buf_label < pCallbackData->cmdBufLabelCount; ++cmd_buf_label) {
595             sprintf(tmp_message, "\t\tLabel[%d] - %s { %f, %f, %f, %f}\n", cmd_buf_label,
596                     pCallbackData->pCmdBufLabels[cmd_buf_label].pLabelName, pCallbackData->pCmdBufLabels[cmd_buf_label].color[0],
597                     pCallbackData->pCmdBufLabels[cmd_buf_label].color[1], pCallbackData->pCmdBufLabels[cmd_buf_label].color[2],
598                     pCallbackData->pCmdBufLabels[cmd_buf_label].color[3]);
599             strcat(message, tmp_message);
600         }
601     }
602 
603 #ifdef _WIN32
604 
605     in_callback = true;
606     if (!demo->suppress_popups)
607         MessageBox(NULL, message, "Alert", MB_OK);
608     in_callback = false;
609 
610 #elif defined(ANDROID)
611 
612     if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
613         __android_log_print(ANDROID_LOG_INFO,  APP_SHORT_NAME, "%s", message);
614     } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
615         __android_log_print(ANDROID_LOG_WARN,  APP_SHORT_NAME, "%s", message);
616     } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
617         __android_log_print(ANDROID_LOG_ERROR, APP_SHORT_NAME, "%s", message);
618     } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
619         __android_log_print(ANDROID_LOG_VERBOSE, APP_SHORT_NAME, "%s", message);
620     } else {
621         __android_log_print(ANDROID_LOG_INFO,  APP_SHORT_NAME, "%s", message);
622     }
623 
624 #else
625 
626     printf("%s\n", message);
627     fflush(stdout);
628 
629 #endif
630 
631     free(message);
632 
633     // Don't bail out, but keep going.
634     return false;
635 }
636 
ActualTimeLate(uint64_t desired,uint64_t actual,uint64_t rdur)637 bool ActualTimeLate(uint64_t desired, uint64_t actual, uint64_t rdur) {
638     // The desired time was the earliest time that the present should have
639     // occured.  In almost every case, the actual time should be later than the
640     // desired time.  We should only consider the actual time "late" if it is
641     // after "desired + rdur".
642     if (actual <= desired) {
643         // The actual time was before or equal to the desired time.  This will
644         // probably never happen, but in case it does, return false since the
645         // present was obviously NOT late.
646         return false;
647     }
648     uint64_t deadline = desired + rdur;
649     if (actual > deadline) {
650         return true;
651     } else {
652         return false;
653     }
654 }
CanPresentEarlier(uint64_t earliest,uint64_t actual,uint64_t margin,uint64_t rdur)655 bool CanPresentEarlier(uint64_t earliest, uint64_t actual, uint64_t margin, uint64_t rdur) {
656     if (earliest < actual) {
657         // Consider whether this present could have occured earlier.  Make sure
658         // that earliest time was at least 2msec earlier than actual time, and
659         // that the margin was at least 2msec:
660         uint64_t diff = actual - earliest;
661         if ((diff >= (2 * MILLION)) && (margin >= (2 * MILLION))) {
662             // This present could have occured earlier because both: 1) the
663             // earliest time was at least 2 msec before actual time, and 2) the
664             // margin was at least 2msec.
665             return true;
666         }
667     }
668     return false;
669 }
670 
671 // Forward declaration:
672 static void demo_resize(struct demo *demo);
673 
memory_type_from_properties(struct demo * demo,uint32_t typeBits,VkFlags requirements_mask,uint32_t * typeIndex)674 static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex) {
675     // Search memtypes to find first index with those properties
676     for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
677         if ((typeBits & 1) == 1) {
678             // Type is available, does it match user properties?
679             if ((demo->memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {
680                 *typeIndex = i;
681                 return true;
682             }
683         }
684         typeBits >>= 1;
685     }
686     // No memory types matched, return failure
687     return false;
688 }
689 
demo_flush_init_cmd(struct demo * demo)690 static void demo_flush_init_cmd(struct demo *demo) {
691     VkResult U_ASSERT_ONLY err;
692 
693     // This function could get called twice if the texture uses a staging buffer
694     // In that case the second call should be ignored
695     if (demo->cmd == VK_NULL_HANDLE) return;
696 
697     err = vkEndCommandBuffer(demo->cmd);
698     assert(!err);
699 
700     VkFence fence;
701     VkFenceCreateInfo fence_ci = {.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = 0};
702     err = vkCreateFence(demo->device, &fence_ci, NULL, &fence);
703     assert(!err);
704 
705     const VkCommandBuffer cmd_bufs[] = {demo->cmd};
706     VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
707                                 .pNext = NULL,
708                                 .waitSemaphoreCount = 0,
709                                 .pWaitSemaphores = NULL,
710                                 .pWaitDstStageMask = NULL,
711                                 .commandBufferCount = 1,
712                                 .pCommandBuffers = cmd_bufs,
713                                 .signalSemaphoreCount = 0,
714                                 .pSignalSemaphores = NULL};
715 
716     err = vkQueueSubmit(demo->graphics_queue, 1, &submit_info, fence);
717     assert(!err);
718 
719     err = vkWaitForFences(demo->device, 1, &fence, VK_TRUE, UINT64_MAX);
720     assert(!err);
721 
722     vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs);
723     vkDestroyFence(demo->device, fence, NULL);
724     demo->cmd = VK_NULL_HANDLE;
725 }
726 
demo_set_image_layout(struct demo * demo,VkImage image,VkImageAspectFlags aspectMask,VkImageLayout old_image_layout,VkImageLayout new_image_layout,VkAccessFlagBits srcAccessMask,VkPipelineStageFlags src_stages,VkPipelineStageFlags dest_stages)727 static void demo_set_image_layout(struct demo *demo, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout,
728                                   VkImageLayout new_image_layout, VkAccessFlagBits srcAccessMask, VkPipelineStageFlags src_stages,
729                                   VkPipelineStageFlags dest_stages) {
730     assert(demo->cmd);
731 
732     VkImageMemoryBarrier image_memory_barrier = {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
733                                                  .pNext = NULL,
734                                                  .srcAccessMask = srcAccessMask,
735                                                  .dstAccessMask = 0,
736                                                  .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
737                                                  .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
738                                                  .oldLayout = old_image_layout,
739                                                  .newLayout = new_image_layout,
740                                                  .image = image,
741                                                  .subresourceRange = {aspectMask, 0, 1, 0, 1}};
742 
743     switch (new_image_layout) {
744         case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
745             /* Make sure anything that was copying from this image has completed */
746             image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
747             break;
748 
749         case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
750             image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
751             break;
752 
753         case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
754             image_memory_barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
755             break;
756 
757         case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
758             image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
759             break;
760 
761         case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
762             image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
763             break;
764 
765         case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
766             image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
767             break;
768 
769         default:
770             image_memory_barrier.dstAccessMask = 0;
771             break;
772     }
773 
774     VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
775 
776     vkCmdPipelineBarrier(demo->cmd, src_stages, dest_stages, 0, 0, NULL, 0, NULL, 1, pmemory_barrier);
777 }
778 
demo_draw_build_cmd(struct demo * demo,VkCommandBuffer cmd_buf)779 static void demo_draw_build_cmd(struct demo *demo, VkCommandBuffer cmd_buf) {
780     VkDebugUtilsLabelEXT label;
781     memset(&label, 0, sizeof(label));
782     const VkCommandBufferBeginInfo cmd_buf_info = {
783         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
784         .pNext = NULL,
785         .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
786         .pInheritanceInfo = NULL,
787     };
788     const VkClearValue clear_values[2] = {
789         [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}},
790         [1] = {.depthStencil = {1.0f, 0}},
791     };
792     const VkRenderPassBeginInfo rp_begin = {
793         .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
794         .pNext = NULL,
795         .renderPass = demo->render_pass,
796         .framebuffer = demo->swapchain_image_resources[demo->current_buffer].framebuffer,
797         .renderArea.offset.x = 0,
798         .renderArea.offset.y = 0,
799         .renderArea.extent.width = demo->width,
800         .renderArea.extent.height = demo->height,
801         .clearValueCount = 2,
802         .pClearValues = clear_values,
803     };
804     VkResult U_ASSERT_ONLY err;
805 
806     err = vkBeginCommandBuffer(cmd_buf, &cmd_buf_info);
807 
808     if (demo->validate) {
809         // Set a name for the command buffer
810         VkDebugUtilsObjectNameInfoEXT cmd_buf_name = {
811             .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
812             .pNext = NULL,
813             .objectType = VK_OBJECT_TYPE_COMMAND_BUFFER,
814             .objectHandle = (uint64_t)cmd_buf,
815             .pObjectName = "CubeDrawCommandBuf",
816         };
817         demo->SetDebugUtilsObjectNameEXT(demo->device, &cmd_buf_name);
818 
819         label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
820         label.pNext = NULL;
821         label.pLabelName = "DrawBegin";
822         label.color[0] = 0.4f;
823         label.color[1] = 0.3f;
824         label.color[2] = 0.2f;
825         label.color[3] = 0.1f;
826         demo->CmdBeginDebugUtilsLabelEXT(cmd_buf, &label);
827     }
828 
829     assert(!err);
830     vkCmdBeginRenderPass(cmd_buf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
831 
832     if (demo->validate) {
833         label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
834         label.pNext = NULL;
835         label.pLabelName = "InsideRenderPass";
836         label.color[0] = 8.4f;
837         label.color[1] = 7.3f;
838         label.color[2] = 6.2f;
839         label.color[3] = 7.1f;
840         demo->CmdBeginDebugUtilsLabelEXT(cmd_buf, &label);
841     }
842 
843     vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, demo->pipeline);
844     vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, demo->pipeline_layout, 0, 1,
845                             &demo->swapchain_image_resources[demo->current_buffer].descriptor_set, 0, NULL);
846     VkViewport viewport;
847     memset(&viewport, 0, sizeof(viewport));
848     viewport.height = (float)demo->height;
849     viewport.width = (float)demo->width;
850     viewport.minDepth = (float)0.0f;
851     viewport.maxDepth = (float)1.0f;
852     vkCmdSetViewport(cmd_buf, 0, 1, &viewport);
853 
854     VkRect2D scissor;
855     memset(&scissor, 0, sizeof(scissor));
856     scissor.extent.width = demo->width;
857     scissor.extent.height = demo->height;
858     scissor.offset.x = 0;
859     scissor.offset.y = 0;
860     vkCmdSetScissor(cmd_buf, 0, 1, &scissor);
861 
862     if (demo->validate) {
863         label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
864         label.pNext = NULL;
865         label.pLabelName = "ActualDraw";
866         label.color[0] = -0.4f;
867         label.color[1] = -0.3f;
868         label.color[2] = -0.2f;
869         label.color[3] = -0.1f;
870         demo->CmdBeginDebugUtilsLabelEXT(cmd_buf, &label);
871     }
872 
873     vkCmdDraw(cmd_buf, 12 * 3, 1, 0, 0);
874     if (demo->validate) {
875         demo->CmdEndDebugUtilsLabelEXT(cmd_buf);
876     }
877 
878     // Note that ending the renderpass changes the image's layout from
879     // COLOR_ATTACHMENT_OPTIMAL to PRESENT_SRC_KHR
880     vkCmdEndRenderPass(cmd_buf);
881     if (demo->validate) {
882         demo->CmdEndDebugUtilsLabelEXT(cmd_buf);
883     }
884 
885     if (demo->separate_present_queue) {
886         // We have to transfer ownership from the graphics queue family to the
887         // present queue family to be able to present.  Note that we don't have
888         // to transfer from present queue family back to graphics queue family at
889         // the start of the next frame because we don't care about the image's
890         // contents at that point.
891         VkImageMemoryBarrier image_ownership_barrier = {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
892                                                         .pNext = NULL,
893                                                         .srcAccessMask = 0,
894                                                         .dstAccessMask = 0,
895                                                         .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
896                                                         .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
897                                                         .srcQueueFamilyIndex = demo->graphics_queue_family_index,
898                                                         .dstQueueFamilyIndex = demo->present_queue_family_index,
899                                                         .image = demo->swapchain_image_resources[demo->current_buffer].image,
900                                                         .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
901 
902         vkCmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0,
903                              NULL, 1, &image_ownership_barrier);
904     }
905     if (demo->validate) {
906         demo->CmdEndDebugUtilsLabelEXT(cmd_buf);
907     }
908     err = vkEndCommandBuffer(cmd_buf);
909     assert(!err);
910 }
911 
demo_build_image_ownership_cmd(struct demo * demo,int i)912 void demo_build_image_ownership_cmd(struct demo *demo, int i) {
913     VkResult U_ASSERT_ONLY err;
914 
915     const VkCommandBufferBeginInfo cmd_buf_info = {
916         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
917         .pNext = NULL,
918         .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
919         .pInheritanceInfo = NULL,
920     };
921     err = vkBeginCommandBuffer(demo->swapchain_image_resources[i].graphics_to_present_cmd, &cmd_buf_info);
922     assert(!err);
923 
924     VkImageMemoryBarrier image_ownership_barrier = {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
925                                                     .pNext = NULL,
926                                                     .srcAccessMask = 0,
927                                                     .dstAccessMask = 0,
928                                                     .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
929                                                     .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
930                                                     .srcQueueFamilyIndex = demo->graphics_queue_family_index,
931                                                     .dstQueueFamilyIndex = demo->present_queue_family_index,
932                                                     .image = demo->swapchain_image_resources[i].image,
933                                                     .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
934 
935     vkCmdPipelineBarrier(demo->swapchain_image_resources[i].graphics_to_present_cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
936                          VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &image_ownership_barrier);
937     err = vkEndCommandBuffer(demo->swapchain_image_resources[i].graphics_to_present_cmd);
938     assert(!err);
939 }
940 
demo_update_data_buffer(struct demo * demo)941 void demo_update_data_buffer(struct demo *demo) {
942     mat4x4 MVP, Model, VP;
943     int matrixSize = sizeof(MVP);
944     uint8_t *pData;
945     VkResult U_ASSERT_ONLY err;
946 
947     mat4x4_mul(VP, demo->projection_matrix, demo->view_matrix);
948 
949     // Rotate around the Y axis
950     mat4x4_dup(Model, demo->model_matrix);
951     mat4x4_rotate(demo->model_matrix, Model, 0.0f, 1.0f, 0.0f, (float)degreesToRadians(demo->spin_angle));
952     mat4x4_mul(MVP, VP, demo->model_matrix);
953 
954     err = vkMapMemory(demo->device, demo->swapchain_image_resources[demo->current_buffer].uniform_memory, 0, VK_WHOLE_SIZE, 0,
955                       (void **)&pData);
956     assert(!err);
957 
958     memcpy(pData, (const void *)&MVP[0][0], matrixSize);
959 
960     vkUnmapMemory(demo->device, demo->swapchain_image_resources[demo->current_buffer].uniform_memory);
961 }
962 
DemoUpdateTargetIPD(struct demo * demo)963 void DemoUpdateTargetIPD(struct demo *demo) {
964     // Look at what happened to previous presents, and make appropriate
965     // adjustments in timing:
966     VkResult U_ASSERT_ONLY err;
967     VkPastPresentationTimingGOOGLE *past = NULL;
968     uint32_t count = 0;
969 
970     err = demo->fpGetPastPresentationTimingGOOGLE(demo->device, demo->swapchain, &count, NULL);
971     assert(!err);
972     if (count) {
973         past = (VkPastPresentationTimingGOOGLE *)malloc(sizeof(VkPastPresentationTimingGOOGLE) * count);
974         assert(past);
975         err = demo->fpGetPastPresentationTimingGOOGLE(demo->device, demo->swapchain, &count, past);
976         assert(!err);
977 
978         bool early = false;
979         bool late = false;
980         bool calibrate_next = false;
981         for (uint32_t i = 0; i < count; i++) {
982             if (!demo->syncd_with_actual_presents) {
983                 // This is the first time that we've received an
984                 // actualPresentTime for this swapchain.  In order to not
985                 // perceive these early frames as "late", we need to sync-up
986                 // our future desiredPresentTime's with the
987                 // actualPresentTime(s) that we're receiving now.
988                 calibrate_next = true;
989 
990                 // So that we don't suspect any pending presents as late,
991                 // record them all as suspected-late presents:
992                 demo->last_late_id = demo->next_present_id - 1;
993                 demo->last_early_id = 0;
994                 demo->syncd_with_actual_presents = true;
995                 break;
996             } else if (CanPresentEarlier(past[i].earliestPresentTime, past[i].actualPresentTime, past[i].presentMargin,
997                                          demo->refresh_duration)) {
998                 // This image could have been presented earlier.  We don't want
999                 // to decrease the target_IPD until we've seen early presents
1000                 // for at least two seconds.
1001                 if (demo->last_early_id == past[i].presentID) {
1002                     // We've now seen two seconds worth of early presents.
1003                     // Flag it as such, and reset the counter:
1004                     early = true;
1005                     demo->last_early_id = 0;
1006                 } else if (demo->last_early_id == 0) {
1007                     // This is the first early present we've seen.
1008                     // Calculate the presentID for two seconds from now.
1009                     uint64_t lastEarlyTime = past[i].actualPresentTime + (2 * BILLION);
1010                     uint32_t howManyPresents = (uint32_t)((lastEarlyTime - past[i].actualPresentTime) / demo->target_IPD);
1011                     demo->last_early_id = past[i].presentID + howManyPresents;
1012                 } else {
1013                     // We are in the midst of a set of early images,
1014                     // and so we won't do anything.
1015                 }
1016                 late = false;
1017                 demo->last_late_id = 0;
1018             } else if (ActualTimeLate(past[i].desiredPresentTime, past[i].actualPresentTime, demo->refresh_duration)) {
1019                 // This image was presented after its desired time.  Since
1020                 // there's a delay between calling vkQueuePresentKHR and when
1021                 // we get the timing data, several presents may have been late.
1022                 // Thus, we need to threat all of the outstanding presents as
1023                 // being likely late, so that we only increase the target_IPD
1024                 // once for all of those presents.
1025                 if ((demo->last_late_id == 0) || (demo->last_late_id < past[i].presentID)) {
1026                     late = true;
1027                     // Record the last suspected-late present:
1028                     demo->last_late_id = demo->next_present_id - 1;
1029                 } else {
1030                     // We are in the midst of a set of likely-late images,
1031                     // and so we won't do anything.
1032                 }
1033                 early = false;
1034                 demo->last_early_id = 0;
1035             } else {
1036                 // Since this image was not presented early or late, reset
1037                 // any sets of early or late presentIDs:
1038                 early = false;
1039                 late = false;
1040                 calibrate_next = true;
1041                 demo->last_early_id = 0;
1042                 demo->last_late_id = 0;
1043             }
1044         }
1045 
1046         if (early) {
1047             // Since we've seen at least two-seconds worth of presnts that
1048             // could have occured earlier than desired, let's decrease the
1049             // target_IPD (i.e. increase the frame rate):
1050             //
1051             // TODO(ianelliott): Try to calculate a better target_IPD based
1052             // on the most recently-seen present (this is overly-simplistic).
1053             demo->refresh_duration_multiplier--;
1054             if (demo->refresh_duration_multiplier == 0) {
1055                 // This should never happen, but in case it does, don't
1056                 // try to go faster.
1057                 demo->refresh_duration_multiplier = 1;
1058             }
1059             demo->target_IPD = demo->refresh_duration * demo->refresh_duration_multiplier;
1060         }
1061         if (late) {
1062             // Since we found a new instance of a late present, we want to
1063             // increase the target_IPD (i.e. decrease the frame rate):
1064             //
1065             // TODO(ianelliott): Try to calculate a better target_IPD based
1066             // on the most recently-seen present (this is overly-simplistic).
1067             demo->refresh_duration_multiplier++;
1068             demo->target_IPD = demo->refresh_duration * demo->refresh_duration_multiplier;
1069         }
1070 
1071         if (calibrate_next) {
1072             int64_t multiple = demo->next_present_id - past[count - 1].presentID;
1073             demo->prev_desired_present_time = (past[count - 1].actualPresentTime + (multiple * demo->target_IPD));
1074         }
1075         free(past);
1076     }
1077 }
1078 
demo_draw(struct demo * demo)1079 static void demo_draw(struct demo *demo) {
1080     VkResult U_ASSERT_ONLY err;
1081 
1082     // Ensure no more than FRAME_LAG renderings are outstanding
1083     logEvent(EVENT_CALLING_WFF);
1084     ATrace_beginSection("cube_WaitForFences");
1085     vkWaitForFences(demo->device, 1, &demo->fences[demo->frame_index], VK_TRUE, UINT64_MAX);
1086     ATrace_endSection();
1087     logEvent(EVENT_CALLED_WFF);
1088 
1089     vkResetFences(demo->device, 1, &demo->fences[demo->frame_index]);
1090 
1091     do {
1092         // Get the index of the next available swapchain image:
1093         logEvent(EVENT_CALLING_ANI);
1094         ATrace_beginSection("cube_AcquireNextImage");
1095         err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX,
1096                                       demo->image_acquired_semaphores[demo->frame_index], VK_NULL_HANDLE, &demo->current_buffer);
1097         ATrace_endSection();
1098         logEvent(EVENT_CALLED_ANI);
1099 
1100         if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1101             // demo->swapchain is out of date (e.g. the window was resized) and
1102             // must be recreated:
1103             demo_resize(demo);
1104         } else if (err == VK_SUBOPTIMAL_KHR) {
1105             // demo->swapchain is not as optimal as it could be, but the platform's
1106             // presentation engine will still present the image correctly.
1107             break;
1108         } else {
1109             assert(!err);
1110         }
1111     } while (err != VK_SUCCESS);
1112 
1113     demo_update_data_buffer(demo);
1114 
1115     if (demo->VK_GOOGLE_display_timing_enabled) {
1116         // Look at what happened to previous presents, and make appropriate
1117         // adjustments in timing:
1118         DemoUpdateTargetIPD(demo);
1119 
1120         // Note: a real application would position its geometry to that it's in
1121         // the correct locatoin for when the next image is presented.  It might
1122         // also wait, so that there's less latency between any input and when
1123         // the next image is rendered/presented.  This demo program is so
1124         // simple that it doesn't do either of those.
1125     }
1126 
1127     // Wait for the image acquired semaphore to be signaled to ensure
1128     // that the image won't be rendered to until the presentation
1129     // engine has fully released ownership to the application, and it is
1130     // okay to render to the image.
1131     VkPipelineStageFlags pipe_stage_flags;
1132     VkSubmitInfo submit_info;
1133     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1134     submit_info.pNext = NULL;
1135     submit_info.pWaitDstStageMask = &pipe_stage_flags;
1136     pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1137     submit_info.waitSemaphoreCount = 1;
1138     submit_info.pWaitSemaphores = &demo->image_acquired_semaphores[demo->frame_index];
1139     submit_info.commandBufferCount = 1;
1140     submit_info.pCommandBuffers = &demo->swapchain_image_resources[demo->current_buffer].cmd;
1141     submit_info.signalSemaphoreCount = 1;
1142     submit_info.pSignalSemaphores = &demo->draw_complete_semaphores[demo->frame_index];
1143     ATrace_beginSection("cube_QueueSubmit");
1144     err = vkQueueSubmit(demo->graphics_queue, 1, &submit_info, demo->fences[demo->frame_index]);
1145     assert(!err);
1146     ATrace_endSection();
1147 
1148     if (demo->separate_present_queue) {
1149         // If we are using separate queues, change image ownership to the
1150         // present queue before presenting, waiting for the draw complete
1151         // semaphore and signalling the ownership released semaphore when finished
1152         VkFence nullFence = VK_NULL_HANDLE;
1153         pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1154         submit_info.waitSemaphoreCount = 1;
1155         submit_info.pWaitSemaphores = &demo->draw_complete_semaphores[demo->frame_index];
1156         submit_info.commandBufferCount = 1;
1157         submit_info.pCommandBuffers = &demo->swapchain_image_resources[demo->current_buffer].graphics_to_present_cmd;
1158         submit_info.signalSemaphoreCount = 1;
1159         submit_info.pSignalSemaphores = &demo->image_ownership_semaphores[demo->frame_index];
1160         err = vkQueueSubmit(demo->present_queue, 1, &submit_info, nullFence);
1161         assert(!err);
1162     }
1163 
1164     // If we are using separate queues we have to wait for image ownership,
1165     // otherwise wait for draw complete
1166     VkPresentInfoKHR present = {
1167         .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1168         .pNext = NULL,
1169         .waitSemaphoreCount = 1,
1170         .pWaitSemaphores = (demo->separate_present_queue) ? &demo->image_ownership_semaphores[demo->frame_index]
1171                                                           : &demo->draw_complete_semaphores[demo->frame_index],
1172         .swapchainCount = 1,
1173         .pSwapchains = &demo->swapchain,
1174         .pImageIndices = &demo->current_buffer,
1175     };
1176 
1177     if (demo->VK_KHR_incremental_present_enabled) {
1178         // If using VK_KHR_incremental_present, we provide a hint of the region
1179         // that contains changed content relative to the previously-presented
1180         // image.  The implementation can use this hint in order to save
1181         // work/power (by only copying the region in the hint).  The
1182         // implementation is free to ignore the hint though, and so we must
1183         // ensure that the entire image has the correctly-drawn content.
1184         uint32_t eighthOfWidth = demo->width / 8;
1185         uint32_t eighthOfHeight = demo->height / 8;
1186         VkRectLayerKHR rect = {
1187             .offset.x = eighthOfWidth,
1188             .offset.y = eighthOfHeight,
1189             .extent.width = eighthOfWidth * 6,
1190             .extent.height = eighthOfHeight * 6,
1191             .layer = 0,
1192         };
1193         VkPresentRegionKHR region = {
1194             .rectangleCount = 1,
1195             .pRectangles = &rect,
1196         };
1197         VkPresentRegionsKHR regions = {
1198             .sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR,
1199             .pNext = present.pNext,
1200             .swapchainCount = present.swapchainCount,
1201             .pRegions = &region,
1202         };
1203         present.pNext = &regions;
1204     }
1205 
1206     if (demo->VK_GOOGLE_display_timing_enabled) {
1207         VkPresentTimeGOOGLE ptime;
1208         if (demo->prev_desired_present_time == 0) {
1209             // This must be the first present for this swapchain.
1210             //
1211             // We don't know where we are relative to the presentation engine's
1212             // display's refresh cycle.  We also don't know how long rendering
1213             // takes.  Let's make a grossly-simplified assumption that the
1214             // desiredPresentTime should be half way between now and
1215             // now+target_IPD.  We will adjust over time.
1216             uint64_t curtime = getTimeInNanoseconds();
1217             if (curtime == 0) {
1218                 // Since we didn't find out the current time, don't give a
1219                 // desiredPresentTime:
1220                 ptime.desiredPresentTime = 0;
1221             } else {
1222                 ptime.desiredPresentTime = curtime + (demo->target_IPD >> 1);
1223             }
1224         } else {
1225             ptime.desiredPresentTime = (demo->prev_desired_present_time + demo->target_IPD);
1226         }
1227         ptime.presentID = demo->next_present_id++;
1228         demo->prev_desired_present_time = ptime.desiredPresentTime;
1229 
1230         VkPresentTimesInfoGOOGLE present_time = {
1231             .sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
1232             .pNext = present.pNext,
1233             .swapchainCount = present.swapchainCount,
1234             .pTimes = &ptime,
1235         };
1236         if (demo->VK_GOOGLE_display_timing_enabled) {
1237             present.pNext = &present_time;
1238         }
1239     }
1240 
1241     logEvent(EVENT_CALLING_QP);
1242     ATrace_beginSection("cube_QueuePresent");
1243     //err = demo->fpQueuePresentKHR(demo->present_queue, &present);
1244     err = SwappyVk_queuePresent(demo->present_queue, &present);
1245     ATrace_endSection();
1246     logEvent(EVENT_CALLED_QP);
1247 
1248     demo->frame_index += 1;
1249     demo->frame_index %= FRAME_LAG;
1250 
1251     if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1252         // demo->swapchain is out of date (e.g. the window was resized) and
1253         // must be recreated:
1254         demo_resize(demo);
1255     } else if (err == VK_SUBOPTIMAL_KHR) {
1256         // demo->swapchain is not as optimal as it could be, but the platform's
1257         // presentation engine will still present the image correctly.
1258     } else {
1259         assert(!err);
1260     }
1261 }
1262 
demo_prepare_buffers(struct demo * demo)1263 static void demo_prepare_buffers(struct demo *demo) {
1264     VkResult U_ASSERT_ONLY err;
1265     VkSwapchainKHR oldSwapchain = demo->swapchain;
1266 
1267     // Check the surface capabilities and formats
1268     VkSurfaceCapabilitiesKHR surfCapabilities;
1269     err = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR(demo->gpu, demo->surface, &surfCapabilities);
1270     assert(!err);
1271 
1272     uint32_t presentModeCount;
1273     err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(demo->gpu, demo->surface, &presentModeCount, NULL);
1274     assert(!err);
1275     VkPresentModeKHR *presentModes = (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
1276     assert(presentModes);
1277     err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(demo->gpu, demo->surface, &presentModeCount, presentModes);
1278     assert(!err);
1279 
1280     VkExtent2D swapchainExtent;
1281     // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF.
1282     if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) {
1283         // If the surface size is undefined, the size is set to the size
1284         // of the images requested, which must fit within the minimum and
1285         // maximum values.
1286         swapchainExtent.width = demo->width;
1287         swapchainExtent.height = demo->height;
1288 
1289         if (swapchainExtent.width < surfCapabilities.minImageExtent.width) {
1290             swapchainExtent.width = surfCapabilities.minImageExtent.width;
1291         } else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width) {
1292             swapchainExtent.width = surfCapabilities.maxImageExtent.width;
1293         }
1294 
1295         if (swapchainExtent.height < surfCapabilities.minImageExtent.height) {
1296             swapchainExtent.height = surfCapabilities.minImageExtent.height;
1297         } else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height) {
1298             swapchainExtent.height = surfCapabilities.maxImageExtent.height;
1299         }
1300     } else {
1301         // If the surface size is defined, the swap chain size must match
1302         swapchainExtent = surfCapabilities.currentExtent;
1303         demo->width = surfCapabilities.currentExtent.width;
1304         demo->height = surfCapabilities.currentExtent.height;
1305     }
1306 
1307     if (demo->width == 0 || demo->height == 0) {
1308         demo->is_minimized = true;
1309         return;
1310     } else {
1311         demo->is_minimized = false;
1312     }
1313 
1314     // The FIFO present mode is guaranteed by the spec to be supported
1315     // and to have no tearing.  It's a great default present mode to use.
1316     VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
1317 
1318     //  There are times when you may wish to use another present mode.  The
1319     //  following code shows how to select them, and the comments provide some
1320     //  reasons you may wish to use them.
1321     //
1322     // It should be noted that Vulkan 1.0 doesn't provide a method for
1323     // synchronizing rendering with the presentation engine's display.  There
1324     // is a method provided for throttling rendering with the display, but
1325     // there are some presentation engines for which this method will not work.
1326     // If an application doesn't throttle its rendering, and if it renders much
1327     // faster than the refresh rate of the display, this can waste power on
1328     // mobile devices.  That is because power is being spent rendering images
1329     // that may never be seen.
1330 
1331     // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care about
1332     // tearing, or have some way of synchronizing their rendering with the
1333     // display.
1334     // VK_PRESENT_MODE_MAILBOX_KHR may be useful for applications that
1335     // generally render a new presentable image every refresh cycle, but are
1336     // occasionally early.  In this case, the application wants the new image
1337     // to be displayed instead of the previously-queued-for-presentation image
1338     // that has not yet been displayed.
1339     // VK_PRESENT_MODE_FIFO_RELAXED_KHR is for applications that generally
1340     // render a new presentable image every refresh cycle, but are occasionally
1341     // late.  In this case (perhaps because of stuttering/latency concerns),
1342     // the application wants the late image to be immediately displayed, even
1343     // though that may mean some tearing.
1344 
1345     if (demo->presentMode != swapchainPresentMode) {
1346         for (size_t i = 0; i < presentModeCount; ++i) {
1347             if (presentModes[i] == demo->presentMode) {
1348                 swapchainPresentMode = demo->presentMode;
1349                 break;
1350             }
1351         }
1352     }
1353     if (swapchainPresentMode != demo->presentMode) {
1354         ERR_EXIT("Present mode specified is not supported\n", "Present mode unsupported");
1355     }
1356 
1357     // Determine the number of VkImages to use in the swap chain.
1358     // Application desires to acquire 3 images at a time for triple
1359     // buffering
1360     uint32_t desiredNumOfSwapchainImages = 3;
1361     if (desiredNumOfSwapchainImages < surfCapabilities.minImageCount) {
1362         desiredNumOfSwapchainImages = surfCapabilities.minImageCount;
1363     }
1364     // If maxImageCount is 0, we can ask for as many images as we want;
1365     // otherwise we're limited to maxImageCount
1366     if ((surfCapabilities.maxImageCount > 0) && (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) {
1367         // Application must settle for fewer images than desired:
1368         desiredNumOfSwapchainImages = surfCapabilities.maxImageCount;
1369     }
1370 
1371     VkSurfaceTransformFlagsKHR preTransform;
1372     if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
1373         preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
1374     } else {
1375         preTransform = surfCapabilities.currentTransform;
1376     }
1377 
1378     // Find a supported composite alpha mode - one of these is guaranteed to be set
1379     VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1380     VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = {
1381         VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
1382         VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
1383         VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
1384         VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
1385     };
1386     for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) {
1387         if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) {
1388             compositeAlpha = compositeAlphaFlags[i];
1389             break;
1390         }
1391     }
1392 
1393     VkSwapchainCreateInfoKHR swapchain_ci = {
1394         .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
1395         .pNext = NULL,
1396         .surface = demo->surface,
1397         .minImageCount = desiredNumOfSwapchainImages,
1398         .imageFormat = demo->format,
1399         .imageColorSpace = demo->color_space,
1400         .imageExtent =
1401             {
1402                 .width = swapchainExtent.width,
1403                 .height = swapchainExtent.height,
1404             },
1405         .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1406         .preTransform = preTransform,
1407         .compositeAlpha = compositeAlpha,
1408         .imageArrayLayers = 1,
1409         .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
1410         .queueFamilyIndexCount = 0,
1411         .pQueueFamilyIndices = NULL,
1412         .presentMode = swapchainPresentMode,
1413         .oldSwapchain = oldSwapchain,
1414         .clipped = true,
1415     };
1416     uint32_t i;
1417     err = demo->fpCreateSwapchainKHR(demo->device, &swapchain_ci, NULL, &demo->swapchain);
1418     assert(!err);
1419 
1420     // If we just re-created an existing swapchain, we should destroy the old
1421     // swapchain at this point.
1422     // Note: destroying the swapchain also cleans up all its associated
1423     // presentable images once the platform is done with them.
1424     if (oldSwapchain != VK_NULL_HANDLE) {
1425         SwappyVk_destroySwapchain(demo->device, oldSwapchain);
1426         demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL);
1427     }
1428 
1429     err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, &demo->swapchainImageCount, NULL);
1430     assert(!err);
1431 
1432     VkImage *swapchainImages = (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage));
1433     assert(swapchainImages);
1434     err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, &demo->swapchainImageCount, swapchainImages);
1435     assert(!err);
1436 
1437     demo->swapchain_image_resources =
1438         (SwapchainImageResources *)malloc(sizeof(SwapchainImageResources) * demo->swapchainImageCount);
1439     assert(demo->swapchain_image_resources);
1440 
1441     for (i = 0; i < demo->swapchainImageCount; i++) {
1442         VkImageViewCreateInfo color_image_view = {
1443             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1444             .pNext = NULL,
1445             .format = demo->format,
1446             .components =
1447                 {
1448                     .r = VK_COMPONENT_SWIZZLE_R,
1449                     .g = VK_COMPONENT_SWIZZLE_G,
1450                     .b = VK_COMPONENT_SWIZZLE_B,
1451                     .a = VK_COMPONENT_SWIZZLE_A,
1452                 },
1453             .subresourceRange =
1454                 {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1},
1455             .viewType = VK_IMAGE_VIEW_TYPE_2D,
1456             .flags = 0,
1457         };
1458 
1459         demo->swapchain_image_resources[i].image = swapchainImages[i];
1460 
1461         color_image_view.image = demo->swapchain_image_resources[i].image;
1462 
1463         err = vkCreateImageView(demo->device, &color_image_view, NULL, &demo->swapchain_image_resources[i].view);
1464         assert(!err);
1465     }
1466 
1467     if (demo->VK_GOOGLE_display_timing_enabled) {
1468         VkRefreshCycleDurationGOOGLE rc_dur;
1469         err = demo->fpGetRefreshCycleDurationGOOGLE(demo->device, demo->swapchain, &rc_dur);
1470         assert(!err);
1471         demo->refresh_duration = rc_dur.refreshDuration;
1472 
1473         demo->syncd_with_actual_presents = false;
1474         // Initially target 1X the refresh duration:
1475         demo->target_IPD = demo->refresh_duration;
1476         demo->refresh_duration_multiplier = 1;
1477         demo->prev_desired_present_time = 0;
1478         demo->next_present_id = 1;
1479     }
1480 
1481     assert(SwappyVk_initAndGetRefreshCycleDuration(demo->gpu, demo->device, demo->swapchain,
1482                                            &demo->refresh_duration));
1483     if (demo->refresh_duration > (16 * MILLION)) {
1484         // Likely a 60Hz display--need a swap interval of 2 for 30FPS:
1485         SwappyVk_setSwapInterval(demo->device, demo->swapchain, 2);
1486     } else if (demo->refresh_duration > (10 * MILLION)) {
1487         // Likely a 90Hz display--need a swap interval of 3 for 30FPS:
1488         SwappyVk_setSwapInterval(demo->device, demo->swapchain, 3);
1489     } else {
1490         // Likely a 120Hz display--need a swap interval of 4 for 30FPS:
1491         SwappyVk_setSwapInterval(demo->device, demo->swapchain, 4);
1492     }
1493 
1494     if (NULL != presentModes) {
1495         free(presentModes);
1496     }
1497 }
1498 
demo_prepare_depth(struct demo * demo)1499 static void demo_prepare_depth(struct demo *demo) {
1500     const VkFormat depth_format = VK_FORMAT_D16_UNORM;
1501     const VkImageCreateInfo image = {
1502         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1503         .pNext = NULL,
1504         .imageType = VK_IMAGE_TYPE_2D,
1505         .format = depth_format,
1506         .extent = {demo->width, demo->height, 1},
1507         .mipLevels = 1,
1508         .arrayLayers = 1,
1509         .samples = VK_SAMPLE_COUNT_1_BIT,
1510         .tiling = VK_IMAGE_TILING_OPTIMAL,
1511         .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
1512         .flags = 0,
1513     };
1514 
1515     VkImageViewCreateInfo view = {
1516         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1517         .pNext = NULL,
1518         .image = VK_NULL_HANDLE,
1519         .format = depth_format,
1520         .subresourceRange =
1521             {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1},
1522         .flags = 0,
1523         .viewType = VK_IMAGE_VIEW_TYPE_2D,
1524     };
1525 
1526     VkMemoryRequirements mem_reqs;
1527     VkResult U_ASSERT_ONLY err;
1528     bool U_ASSERT_ONLY pass;
1529 
1530     demo->depth.format = depth_format;
1531 
1532     /* create image */
1533     err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image);
1534     assert(!err);
1535 
1536     vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs);
1537     assert(!err);
1538 
1539     demo->depth.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1540     demo->depth.mem_alloc.pNext = NULL;
1541     demo->depth.mem_alloc.allocationSize = mem_reqs.size;
1542     demo->depth.mem_alloc.memoryTypeIndex = 0;
1543 
1544     pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1545                                        &demo->depth.mem_alloc.memoryTypeIndex);
1546     assert(pass);
1547 
1548     /* allocate memory */
1549     err = vkAllocateMemory(demo->device, &demo->depth.mem_alloc, NULL, &demo->depth.mem);
1550     assert(!err);
1551 
1552     /* bind memory */
1553     err = vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0);
1554     assert(!err);
1555 
1556     /* create image view */
1557     view.image = demo->depth.image;
1558     err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view);
1559     assert(!err);
1560 }
1561 
1562 /* Convert ppm image data from header file into RGBA texture image */
1563 #include "lunarg.ppm.h"
loadTexture(const char * filename,uint8_t * rgba_data,VkSubresourceLayout * layout,int32_t * width,int32_t * height)1564 bool loadTexture(const char *filename, uint8_t *rgba_data, VkSubresourceLayout *layout, int32_t *width, int32_t *height) {
1565     (void)filename;
1566     char *cPtr;
1567     cPtr = (char *)lunarg_ppm;
1568     if ((unsigned char *)cPtr >= (lunarg_ppm + lunarg_ppm_len) || strncmp(cPtr, "P6\n", 3)) {
1569         return false;
1570     }
1571     while (strncmp(cPtr++, "\n", 1))
1572         ;
1573     sscanf(cPtr, "%u %u", width, height);
1574     if (rgba_data == NULL) {
1575         return true;
1576     }
1577     while (strncmp(cPtr++, "\n", 1))
1578         ;
1579     if ((unsigned char *)cPtr >= (lunarg_ppm + lunarg_ppm_len) || strncmp(cPtr, "255\n", 4)) {
1580         return false;
1581     }
1582     while (strncmp(cPtr++, "\n", 1))
1583         ;
1584     for (int y = 0; y < *height; y++) {
1585         uint8_t *rowPtr = rgba_data;
1586         for (int x = 0; x < *width; x++) {
1587             memcpy(rowPtr, cPtr, 3);
1588             rowPtr[3] = 255; /* Alpha of 1 */
1589             rowPtr += 4;
1590             cPtr += 3;
1591         }
1592         rgba_data += layout->rowPitch;
1593     }
1594     return true;
1595 }
1596 
demo_prepare_texture_buffer(struct demo * demo,const char * filename,struct texture_object * tex_obj)1597 static void demo_prepare_texture_buffer(struct demo *demo, const char *filename, struct texture_object *tex_obj) {
1598     int32_t tex_width;
1599     int32_t tex_height;
1600     VkResult U_ASSERT_ONLY err;
1601     bool U_ASSERT_ONLY pass;
1602 
1603     if (!loadTexture(filename, NULL, NULL, &tex_width, &tex_height)) {
1604         ERR_EXIT("Failed to load textures", "Load Texture Failure");
1605     }
1606 
1607     tex_obj->tex_width = tex_width;
1608     tex_obj->tex_height = tex_height;
1609 
1610     const VkBufferCreateInfo buffer_create_info = {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
1611                                                    .pNext = NULL,
1612                                                    .flags = 0,
1613                                                    .size = tex_width * tex_height * 4,
1614                                                    .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1615                                                    .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1616                                                    .queueFamilyIndexCount = 0,
1617                                                    .pQueueFamilyIndices = NULL};
1618 
1619     err = vkCreateBuffer(demo->device, &buffer_create_info, NULL, &tex_obj->buffer);
1620     assert(!err);
1621 
1622     VkMemoryRequirements mem_reqs;
1623     vkGetBufferMemoryRequirements(demo->device, tex_obj->buffer, &mem_reqs);
1624 
1625     tex_obj->mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1626     tex_obj->mem_alloc.pNext = NULL;
1627     tex_obj->mem_alloc.allocationSize = mem_reqs.size;
1628     tex_obj->mem_alloc.memoryTypeIndex = 0;
1629 
1630     VkFlags requirements = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1631     pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, requirements, &tex_obj->mem_alloc.memoryTypeIndex);
1632     assert(pass);
1633 
1634     err = vkAllocateMemory(demo->device, &tex_obj->mem_alloc, NULL, &(tex_obj->mem));
1635     assert(!err);
1636 
1637     /* bind memory */
1638     err = vkBindBufferMemory(demo->device, tex_obj->buffer, tex_obj->mem, 0);
1639     assert(!err);
1640 
1641     VkSubresourceLayout layout;
1642     memset(&layout, 0, sizeof(layout));
1643     layout.rowPitch = tex_width * 4;
1644 
1645     void *data;
1646     err = vkMapMemory(demo->device, tex_obj->mem, 0, tex_obj->mem_alloc.allocationSize, 0, &data);
1647     assert(!err);
1648 
1649     if (!loadTexture(filename, data, &layout, &tex_width, &tex_height)) {
1650         fprintf(stderr, "Error loading texture: %s\n", filename);
1651     }
1652 
1653     vkUnmapMemory(demo->device, tex_obj->mem);
1654 }
1655 
demo_prepare_texture_image(struct demo * demo,const char * filename,struct texture_object * tex_obj,VkImageTiling tiling,VkImageUsageFlags usage,VkFlags required_props)1656 static void demo_prepare_texture_image(struct demo *demo, const char *filename, struct texture_object *tex_obj,
1657                                        VkImageTiling tiling, VkImageUsageFlags usage, VkFlags required_props) {
1658     const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM;
1659     int32_t tex_width;
1660     int32_t tex_height;
1661     VkResult U_ASSERT_ONLY err;
1662     bool U_ASSERT_ONLY pass;
1663 
1664     if (!loadTexture(filename, NULL, NULL, &tex_width, &tex_height)) {
1665         ERR_EXIT("Failed to load textures", "Load Texture Failure");
1666     }
1667 
1668     tex_obj->tex_width = tex_width;
1669     tex_obj->tex_height = tex_height;
1670 
1671     const VkImageCreateInfo image_create_info = {
1672         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1673         .pNext = NULL,
1674         .imageType = VK_IMAGE_TYPE_2D,
1675         .format = tex_format,
1676         .extent = {tex_width, tex_height, 1},
1677         .mipLevels = 1,
1678         .arrayLayers = 1,
1679         .samples = VK_SAMPLE_COUNT_1_BIT,
1680         .tiling = tiling,
1681         .usage = usage,
1682         .flags = 0,
1683         .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
1684     };
1685 
1686     VkMemoryRequirements mem_reqs;
1687 
1688     err = vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image);
1689     assert(!err);
1690 
1691     vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs);
1692 
1693     tex_obj->mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1694     tex_obj->mem_alloc.pNext = NULL;
1695     tex_obj->mem_alloc.allocationSize = mem_reqs.size;
1696     tex_obj->mem_alloc.memoryTypeIndex = 0;
1697 
1698     pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, required_props, &tex_obj->mem_alloc.memoryTypeIndex);
1699     assert(pass);
1700 
1701     /* allocate memory */
1702     err = vkAllocateMemory(demo->device, &tex_obj->mem_alloc, NULL, &(tex_obj->mem));
1703     assert(!err);
1704 
1705     /* bind memory */
1706     err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0);
1707     assert(!err);
1708 
1709     if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
1710         const VkImageSubresource subres = {
1711             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1712             .mipLevel = 0,
1713             .arrayLayer = 0,
1714         };
1715         VkSubresourceLayout layout;
1716         void *data;
1717 
1718         vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres, &layout);
1719 
1720         err = vkMapMemory(demo->device, tex_obj->mem, 0, tex_obj->mem_alloc.allocationSize, 0, &data);
1721         assert(!err);
1722 
1723         if (!loadTexture(filename, data, &layout, &tex_width, &tex_height)) {
1724             fprintf(stderr, "Error loading texture: %s\n", filename);
1725         }
1726 
1727         vkUnmapMemory(demo->device, tex_obj->mem);
1728     }
1729 
1730     tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1731 }
1732 
demo_destroy_texture(struct demo * demo,struct texture_object * tex_objs)1733 static void demo_destroy_texture(struct demo *demo, struct texture_object *tex_objs) {
1734     /* clean up staging resources */
1735     vkFreeMemory(demo->device, tex_objs->mem, NULL);
1736     if (tex_objs->image) vkDestroyImage(demo->device, tex_objs->image, NULL);
1737     if (tex_objs->buffer) vkDestroyBuffer(demo->device, tex_objs->buffer, NULL);
1738 }
1739 
demo_prepare_textures(struct demo * demo)1740 static void demo_prepare_textures(struct demo *demo) {
1741     const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM;
1742     VkFormatProperties props;
1743     uint32_t i;
1744 
1745     vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props);
1746 
1747     for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1748         VkResult U_ASSERT_ONLY err;
1749 
1750         if ((props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !demo->use_staging_buffer) {
1751             /* Device can texture using linear textures */
1752             demo_prepare_texture_image(demo, tex_files[i], &demo->textures[i], VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT,
1753                                        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1754             // Nothing in the pipeline needs to be complete to start, and don't allow fragment
1755             // shader to run until layout transition completes
1756             demo_set_image_layout(demo, demo->textures[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED,
1757                                   demo->textures[i].imageLayout, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1758                                   VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1759             demo->staging_texture.image = 0;
1760         } else if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) {
1761             /* Must use staging buffer to copy linear texture to optimized */
1762 
1763             memset(&demo->staging_texture, 0, sizeof(demo->staging_texture));
1764             demo_prepare_texture_buffer(demo, tex_files[i], &demo->staging_texture);
1765 
1766             demo_prepare_texture_image(demo, tex_files[i], &demo->textures[i], VK_IMAGE_TILING_OPTIMAL,
1767                                        (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
1768                                        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1769 
1770             demo_set_image_layout(demo, demo->textures[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED,
1771                                   VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1772                                   VK_PIPELINE_STAGE_TRANSFER_BIT);
1773 
1774             VkBufferImageCopy copy_region = {
1775                 .bufferOffset = 0,
1776                 .bufferRowLength = demo->staging_texture.tex_width,
1777                 .bufferImageHeight = demo->staging_texture.tex_height,
1778                 .imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
1779                 .imageOffset = {0, 0, 0},
1780                 .imageExtent = {demo->staging_texture.tex_width, demo->staging_texture.tex_height, 1},
1781             };
1782 
1783             vkCmdCopyBufferToImage(demo->cmd, demo->staging_texture.buffer, demo->textures[i].image,
1784                                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
1785 
1786             demo_set_image_layout(demo, demo->textures[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1787                                   demo->textures[i].imageLayout, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
1788                                   VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1789 
1790         } else {
1791             /* Can't support VK_FORMAT_R8G8B8A8_UNORM !? */
1792             assert(!"No support for R8G8B8A8_UNORM as texture image format");
1793         }
1794 
1795         const VkSamplerCreateInfo sampler = {
1796             .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1797             .pNext = NULL,
1798             .magFilter = VK_FILTER_NEAREST,
1799             .minFilter = VK_FILTER_NEAREST,
1800             .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1801             .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1802             .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1803             .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1804             .mipLodBias = 0.0f,
1805             .anisotropyEnable = VK_FALSE,
1806             .maxAnisotropy = 1,
1807             .compareOp = VK_COMPARE_OP_NEVER,
1808             .minLod = 0.0f,
1809             .maxLod = 0.0f,
1810             .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
1811             .unnormalizedCoordinates = VK_FALSE,
1812         };
1813 
1814         VkImageViewCreateInfo view = {
1815             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1816             .pNext = NULL,
1817             .image = VK_NULL_HANDLE,
1818             .viewType = VK_IMAGE_VIEW_TYPE_2D,
1819             .format = tex_format,
1820             .components =
1821                 {
1822                     VK_COMPONENT_SWIZZLE_R,
1823                     VK_COMPONENT_SWIZZLE_G,
1824                     VK_COMPONENT_SWIZZLE_B,
1825                     VK_COMPONENT_SWIZZLE_A,
1826                 },
1827             .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
1828             .flags = 0,
1829         };
1830 
1831         /* create sampler */
1832         err = vkCreateSampler(demo->device, &sampler, NULL, &demo->textures[i].sampler);
1833         assert(!err);
1834 
1835         /* create image view */
1836         view.image = demo->textures[i].image;
1837         err = vkCreateImageView(demo->device, &view, NULL, &demo->textures[i].view);
1838         assert(!err);
1839     }
1840 }
1841 
demo_prepare_cube_data_buffers(struct demo * demo)1842 void demo_prepare_cube_data_buffers(struct demo *demo) {
1843     VkBufferCreateInfo buf_info;
1844     VkMemoryRequirements mem_reqs;
1845     VkMemoryAllocateInfo mem_alloc;
1846     uint8_t *pData;
1847     mat4x4 MVP, VP;
1848     VkResult U_ASSERT_ONLY err;
1849     bool U_ASSERT_ONLY pass;
1850     struct vktexcube_vs_uniform data;
1851 
1852     mat4x4_mul(VP, demo->projection_matrix, demo->view_matrix);
1853     mat4x4_mul(MVP, VP, demo->model_matrix);
1854     memcpy(data.mvp, MVP, sizeof(MVP));
1855     //    dumpMatrix("MVP", MVP);
1856 
1857     for (unsigned int i = 0; i < 12 * 3; i++) {
1858         data.position[i][0] = g_vertex_buffer_data[i * 3];
1859         data.position[i][1] = g_vertex_buffer_data[i * 3 + 1];
1860         data.position[i][2] = g_vertex_buffer_data[i * 3 + 2];
1861         data.position[i][3] = 1.0f;
1862         data.attr[i][0] = g_uv_buffer_data[2 * i];
1863         data.attr[i][1] = g_uv_buffer_data[2 * i + 1];
1864         data.attr[i][2] = 0;
1865         data.attr[i][3] = 0;
1866     }
1867 
1868     memset(&buf_info, 0, sizeof(buf_info));
1869     buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1870     buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
1871     buf_info.size = sizeof(data);
1872 
1873     for (unsigned int i = 0; i < demo->swapchainImageCount; i++) {
1874         err = vkCreateBuffer(demo->device, &buf_info, NULL, &demo->swapchain_image_resources[i].uniform_buffer);
1875         assert(!err);
1876 
1877         vkGetBufferMemoryRequirements(demo->device, demo->swapchain_image_resources[i].uniform_buffer, &mem_reqs);
1878 
1879         mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1880         mem_alloc.pNext = NULL;
1881         mem_alloc.allocationSize = mem_reqs.size;
1882         mem_alloc.memoryTypeIndex = 0;
1883 
1884         pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
1885                                            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1886                                            &mem_alloc.memoryTypeIndex);
1887         assert(pass);
1888 
1889         err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->swapchain_image_resources[i].uniform_memory);
1890         assert(!err);
1891 
1892         err = vkMapMemory(demo->device, demo->swapchain_image_resources[i].uniform_memory, 0, VK_WHOLE_SIZE, 0, (void **)&pData);
1893         assert(!err);
1894 
1895         memcpy(pData, &data, sizeof data);
1896 
1897         vkUnmapMemory(demo->device, demo->swapchain_image_resources[i].uniform_memory);
1898 
1899         err = vkBindBufferMemory(demo->device, demo->swapchain_image_resources[i].uniform_buffer,
1900                                  demo->swapchain_image_resources[i].uniform_memory, 0);
1901         assert(!err);
1902     }
1903 }
1904 
demo_prepare_descriptor_layout(struct demo * demo)1905 static void demo_prepare_descriptor_layout(struct demo *demo) {
1906     const VkDescriptorSetLayoutBinding layout_bindings[2] = {
1907         [0] =
1908             {
1909                 .binding = 0,
1910                 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1911                 .descriptorCount = 1,
1912                 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
1913                 .pImmutableSamplers = NULL,
1914             },
1915         [1] =
1916             {
1917                 .binding = 1,
1918                 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1919                 .descriptorCount = DEMO_TEXTURE_COUNT,
1920                 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
1921                 .pImmutableSamplers = NULL,
1922             },
1923     };
1924     const VkDescriptorSetLayoutCreateInfo descriptor_layout = {
1925         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1926         .pNext = NULL,
1927         .bindingCount = 2,
1928         .pBindings = layout_bindings,
1929     };
1930     VkResult U_ASSERT_ONLY err;
1931 
1932     err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL, &demo->desc_layout);
1933     assert(!err);
1934 
1935     const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
1936         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
1937         .pNext = NULL,
1938         .setLayoutCount = 1,
1939         .pSetLayouts = &demo->desc_layout,
1940     };
1941 
1942     err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL, &demo->pipeline_layout);
1943     assert(!err);
1944 }
1945 
demo_prepare_render_pass(struct demo * demo)1946 static void demo_prepare_render_pass(struct demo *demo) {
1947     // The initial layout for the color and depth attachments will be LAYOUT_UNDEFINED
1948     // because at the start of the renderpass, we don't care about their contents.
1949     // At the start of the subpass, the color attachment's layout will be transitioned
1950     // to LAYOUT_COLOR_ATTACHMENT_OPTIMAL and the depth stencil attachment's layout
1951     // will be transitioned to LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL.  At the end of
1952     // the renderpass, the color attachment's layout will be transitioned to
1953     // LAYOUT_PRESENT_SRC_KHR to be ready to present.  This is all done as part of
1954     // the renderpass, no barriers are necessary.
1955     const VkAttachmentDescription attachments[2] = {
1956         [0] =
1957             {
1958                 .format = demo->format,
1959                 .flags = 0,
1960                 .samples = VK_SAMPLE_COUNT_1_BIT,
1961                 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1962                 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
1963                 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1964                 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1965                 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1966                 .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
1967             },
1968         [1] =
1969             {
1970                 .format = demo->depth.format,
1971                 .flags = 0,
1972                 .samples = VK_SAMPLE_COUNT_1_BIT,
1973                 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1974                 .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1975                 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1976                 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1977                 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1978                 .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1979             },
1980     };
1981     const VkAttachmentReference color_reference = {
1982         .attachment = 0,
1983         .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1984     };
1985     const VkAttachmentReference depth_reference = {
1986         .attachment = 1,
1987         .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1988     };
1989     const VkSubpassDescription subpass = {
1990         .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
1991         .flags = 0,
1992         .inputAttachmentCount = 0,
1993         .pInputAttachments = NULL,
1994         .colorAttachmentCount = 1,
1995         .pColorAttachments = &color_reference,
1996         .pResolveAttachments = NULL,
1997         .pDepthStencilAttachment = &depth_reference,
1998         .preserveAttachmentCount = 0,
1999         .pPreserveAttachments = NULL,
2000     };
2001     const VkRenderPassCreateInfo rp_info = {
2002         .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
2003         .pNext = NULL,
2004         .flags = 0,
2005         .attachmentCount = 2,
2006         .pAttachments = attachments,
2007         .subpassCount = 1,
2008         .pSubpasses = &subpass,
2009         .dependencyCount = 0,
2010         .pDependencies = NULL,
2011     };
2012     VkResult U_ASSERT_ONLY err;
2013 
2014     err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass);
2015     assert(!err);
2016 }
2017 
demo_prepare_shader_module(struct demo * demo,const uint32_t * code,size_t size)2018 static VkShaderModule demo_prepare_shader_module(struct demo *demo, const uint32_t *code, size_t size) {
2019     VkShaderModule module;
2020     VkShaderModuleCreateInfo moduleCreateInfo;
2021     VkResult U_ASSERT_ONLY err;
2022 
2023     moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
2024     moduleCreateInfo.pNext = NULL;
2025     moduleCreateInfo.flags = 0;
2026     moduleCreateInfo.codeSize = size;
2027     moduleCreateInfo.pCode = code;
2028 
2029     err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module);
2030     assert(!err);
2031 
2032     return module;
2033 }
2034 
demo_prepare_vs(struct demo * demo)2035 static void demo_prepare_vs(struct demo *demo) {
2036     const uint32_t vs_code[] = {
2037 #include "cube.vert.inc"
2038     };
2039     demo->vert_shader_module = demo_prepare_shader_module(demo, vs_code, sizeof(vs_code));
2040 }
2041 
demo_prepare_fs(struct demo * demo)2042 static void demo_prepare_fs(struct demo *demo) {
2043     const uint32_t fs_code[] = {
2044 #include "cube.frag.inc"
2045     };
2046     demo->frag_shader_module = demo_prepare_shader_module(demo, fs_code, sizeof(fs_code));
2047 }
2048 
demo_prepare_pipeline(struct demo * demo)2049 static void demo_prepare_pipeline(struct demo *demo) {
2050     VkGraphicsPipelineCreateInfo pipeline;
2051     VkPipelineCacheCreateInfo pipelineCache;
2052     VkPipelineVertexInputStateCreateInfo vi;
2053     VkPipelineInputAssemblyStateCreateInfo ia;
2054     VkPipelineRasterizationStateCreateInfo rs;
2055     VkPipelineColorBlendStateCreateInfo cb;
2056     VkPipelineDepthStencilStateCreateInfo ds;
2057     VkPipelineViewportStateCreateInfo vp;
2058     VkPipelineMultisampleStateCreateInfo ms;
2059     VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE];
2060     VkPipelineDynamicStateCreateInfo dynamicState;
2061     VkResult U_ASSERT_ONLY err;
2062 
2063     memset(dynamicStateEnables, 0, sizeof dynamicStateEnables);
2064     memset(&dynamicState, 0, sizeof dynamicState);
2065     dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
2066     dynamicState.pDynamicStates = dynamicStateEnables;
2067 
2068     memset(&pipeline, 0, sizeof(pipeline));
2069     pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
2070     pipeline.layout = demo->pipeline_layout;
2071 
2072     memset(&vi, 0, sizeof(vi));
2073     vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
2074 
2075     memset(&ia, 0, sizeof(ia));
2076     ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
2077     ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
2078 
2079     memset(&rs, 0, sizeof(rs));
2080     rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
2081     rs.polygonMode = VK_POLYGON_MODE_FILL;
2082     rs.cullMode = VK_CULL_MODE_BACK_BIT;
2083     rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
2084     rs.depthClampEnable = VK_FALSE;
2085     rs.rasterizerDiscardEnable = VK_FALSE;
2086     rs.depthBiasEnable = VK_FALSE;
2087     rs.lineWidth = 1.0f;
2088 
2089     memset(&cb, 0, sizeof(cb));
2090     cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
2091     VkPipelineColorBlendAttachmentState att_state[1];
2092     memset(att_state, 0, sizeof(att_state));
2093     att_state[0].colorWriteMask = 0xf;
2094     att_state[0].blendEnable = VK_FALSE;
2095     cb.attachmentCount = 1;
2096     cb.pAttachments = att_state;
2097 
2098     memset(&vp, 0, sizeof(vp));
2099     vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
2100     vp.viewportCount = 1;
2101     dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
2102     vp.scissorCount = 1;
2103     dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
2104 
2105     memset(&ds, 0, sizeof(ds));
2106     ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
2107     ds.depthTestEnable = VK_TRUE;
2108     ds.depthWriteEnable = VK_TRUE;
2109     ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
2110     ds.depthBoundsTestEnable = VK_FALSE;
2111     ds.back.failOp = VK_STENCIL_OP_KEEP;
2112     ds.back.passOp = VK_STENCIL_OP_KEEP;
2113     ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
2114     ds.stencilTestEnable = VK_FALSE;
2115     ds.front = ds.back;
2116 
2117     memset(&ms, 0, sizeof(ms));
2118     ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
2119     ms.pSampleMask = NULL;
2120     ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
2121 
2122     demo_prepare_vs(demo);
2123     demo_prepare_fs(demo);
2124 
2125     // Two stages: vs and fs
2126     VkPipelineShaderStageCreateInfo shaderStages[2];
2127     memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo));
2128 
2129     shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2130     shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
2131     shaderStages[0].module = demo->vert_shader_module;
2132     shaderStages[0].pName = "main";
2133 
2134     shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2135     shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
2136     shaderStages[1].module = demo->frag_shader_module;
2137     shaderStages[1].pName = "main";
2138 
2139     memset(&pipelineCache, 0, sizeof(pipelineCache));
2140     pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
2141 
2142     err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL, &demo->pipelineCache);
2143     assert(!err);
2144 
2145     pipeline.pVertexInputState = &vi;
2146     pipeline.pInputAssemblyState = &ia;
2147     pipeline.pRasterizationState = &rs;
2148     pipeline.pColorBlendState = &cb;
2149     pipeline.pMultisampleState = &ms;
2150     pipeline.pViewportState = &vp;
2151     pipeline.pDepthStencilState = &ds;
2152     pipeline.stageCount = ARRAY_SIZE(shaderStages);
2153     pipeline.pStages = shaderStages;
2154     pipeline.renderPass = demo->render_pass;
2155     pipeline.pDynamicState = &dynamicState;
2156 
2157     pipeline.renderPass = demo->render_pass;
2158 
2159     err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1, &pipeline, NULL, &demo->pipeline);
2160     assert(!err);
2161 
2162     vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL);
2163     vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL);
2164 }
2165 
demo_prepare_descriptor_pool(struct demo * demo)2166 static void demo_prepare_descriptor_pool(struct demo *demo) {
2167     const VkDescriptorPoolSize type_counts[2] = {
2168         [0] =
2169             {
2170                 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
2171                 .descriptorCount = demo->swapchainImageCount,
2172             },
2173         [1] =
2174             {
2175                 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
2176                 .descriptorCount = demo->swapchainImageCount * DEMO_TEXTURE_COUNT,
2177             },
2178     };
2179     const VkDescriptorPoolCreateInfo descriptor_pool = {
2180         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
2181         .pNext = NULL,
2182         .maxSets = demo->swapchainImageCount,
2183         .poolSizeCount = 2,
2184         .pPoolSizes = type_counts,
2185     };
2186     VkResult U_ASSERT_ONLY err;
2187 
2188     err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL, &demo->desc_pool);
2189     assert(!err);
2190 }
2191 
demo_prepare_descriptor_set(struct demo * demo)2192 static void demo_prepare_descriptor_set(struct demo *demo) {
2193     VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT];
2194     VkWriteDescriptorSet writes[2];
2195     VkResult U_ASSERT_ONLY err;
2196 
2197     VkDescriptorSetAllocateInfo alloc_info = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
2198                                               .pNext = NULL,
2199                                               .descriptorPool = demo->desc_pool,
2200                                               .descriptorSetCount = 1,
2201                                               .pSetLayouts = &demo->desc_layout};
2202 
2203     VkDescriptorBufferInfo buffer_info;
2204     buffer_info.offset = 0;
2205     buffer_info.range = sizeof(struct vktexcube_vs_uniform);
2206 
2207     memset(&tex_descs, 0, sizeof(tex_descs));
2208     for (unsigned int i = 0; i < DEMO_TEXTURE_COUNT; i++) {
2209         tex_descs[i].sampler = demo->textures[i].sampler;
2210         tex_descs[i].imageView = demo->textures[i].view;
2211         tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2212     }
2213 
2214     memset(&writes, 0, sizeof(writes));
2215 
2216     writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
2217     writes[0].descriptorCount = 1;
2218     writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
2219     writes[0].pBufferInfo = &buffer_info;
2220 
2221     writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
2222     writes[1].dstBinding = 1;
2223     writes[1].descriptorCount = DEMO_TEXTURE_COUNT;
2224     writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2225     writes[1].pImageInfo = tex_descs;
2226 
2227     for (unsigned int i = 0; i < demo->swapchainImageCount; i++) {
2228         err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->swapchain_image_resources[i].descriptor_set);
2229         assert(!err);
2230         buffer_info.buffer = demo->swapchain_image_resources[i].uniform_buffer;
2231         writes[0].dstSet = demo->swapchain_image_resources[i].descriptor_set;
2232         writes[1].dstSet = demo->swapchain_image_resources[i].descriptor_set;
2233         vkUpdateDescriptorSets(demo->device, 2, writes, 0, NULL);
2234     }
2235 }
2236 
demo_prepare_framebuffers(struct demo * demo)2237 static void demo_prepare_framebuffers(struct demo *demo) {
2238     VkImageView attachments[2];
2239     attachments[1] = demo->depth.view;
2240 
2241     const VkFramebufferCreateInfo fb_info = {
2242         .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
2243         .pNext = NULL,
2244         .renderPass = demo->render_pass,
2245         .attachmentCount = 2,
2246         .pAttachments = attachments,
2247         .width = demo->width,
2248         .height = demo->height,
2249         .layers = 1,
2250     };
2251     VkResult U_ASSERT_ONLY err;
2252     uint32_t i;
2253 
2254     for (i = 0; i < demo->swapchainImageCount; i++) {
2255         attachments[0] = demo->swapchain_image_resources[i].view;
2256         err = vkCreateFramebuffer(demo->device, &fb_info, NULL, &demo->swapchain_image_resources[i].framebuffer);
2257         assert(!err);
2258     }
2259 }
2260 
demo_prepare(struct demo * demo)2261 static void demo_prepare(struct demo *demo) {
2262     VkResult U_ASSERT_ONLY err;
2263     if (demo->cmd_pool == VK_NULL_HANDLE) {
2264         const VkCommandPoolCreateInfo cmd_pool_info = {
2265             .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
2266             .pNext = NULL,
2267             .queueFamilyIndex = demo->graphics_queue_family_index,
2268             .flags = 0,
2269         };
2270         err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL, &demo->cmd_pool);
2271         assert(!err);
2272     }
2273 
2274     DbgMsg("graphics_queue_family_index=%u", demo->graphics_queue_family_index);
2275 
2276     const VkCommandBufferAllocateInfo cmd = {
2277         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
2278         .pNext = NULL,
2279         .commandPool = demo->cmd_pool,
2280         .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
2281         .commandBufferCount = 1,
2282     };
2283     err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->cmd);
2284     assert(!err);
2285     VkCommandBufferBeginInfo cmd_buf_info = {
2286         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
2287         .pNext = NULL,
2288         .flags = 0,
2289         .pInheritanceInfo = NULL,
2290     };
2291     err = vkBeginCommandBuffer(demo->cmd, &cmd_buf_info);
2292     assert(!err);
2293 
2294     demo_prepare_buffers(demo);
2295 
2296     if (demo->is_minimized) {
2297         demo->prepared = false;
2298         return;
2299     }
2300 
2301     demo_prepare_depth(demo);
2302     demo_prepare_textures(demo);
2303     demo_prepare_cube_data_buffers(demo);
2304 
2305     demo_prepare_descriptor_layout(demo);
2306     demo_prepare_render_pass(demo);
2307     demo_prepare_pipeline(demo);
2308 
2309     for (uint32_t i = 0; i < demo->swapchainImageCount; i++) {
2310         err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->swapchain_image_resources[i].cmd);
2311         assert(!err);
2312     }
2313 
2314     if (demo->separate_present_queue) {
2315         const VkCommandPoolCreateInfo present_cmd_pool_info = {
2316             .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
2317             .pNext = NULL,
2318             .queueFamilyIndex = demo->present_queue_family_index,
2319             .flags = 0,
2320         };
2321         err = vkCreateCommandPool(demo->device, &present_cmd_pool_info, NULL, &demo->present_cmd_pool);
2322         assert(!err);
2323         const VkCommandBufferAllocateInfo present_cmd_info = {
2324             .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
2325             .pNext = NULL,
2326             .commandPool = demo->present_cmd_pool,
2327             .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
2328             .commandBufferCount = 1,
2329         };
2330         for (uint32_t i = 0; i < demo->swapchainImageCount; i++) {
2331             err = vkAllocateCommandBuffers(demo->device, &present_cmd_info,
2332                                            &demo->swapchain_image_resources[i].graphics_to_present_cmd);
2333             assert(!err);
2334             demo_build_image_ownership_cmd(demo, i);
2335         }
2336     }
2337 
2338     demo_prepare_descriptor_pool(demo);
2339     demo_prepare_descriptor_set(demo);
2340 
2341     demo_prepare_framebuffers(demo);
2342 
2343     for (uint32_t i = 0; i < demo->swapchainImageCount; i++) {
2344         demo->current_buffer = i;
2345         demo_draw_build_cmd(demo, demo->swapchain_image_resources[i].cmd);
2346     }
2347 
2348     /*
2349      * Prepare functions above may generate pipeline commands
2350      * that need to be flushed before beginning the render loop.
2351      */
2352     demo_flush_init_cmd(demo);
2353     if (demo->staging_texture.buffer) {
2354         demo_destroy_texture(demo, &demo->staging_texture);
2355     }
2356 
2357     demo->current_buffer = 0;
2358     demo->prepared = true;
2359 }
2360 
demo_cleanup(struct demo * demo)2361 static void demo_cleanup(struct demo *demo) {
2362     uint32_t i;
2363 
2364     demo->prepared = false;
2365     vkDeviceWaitIdle(demo->device);
2366 
2367     // Wait for fences from present operations
2368     for (i = 0; i < FRAME_LAG; i++) {
2369         vkWaitForFences(demo->device, 1, &demo->fences[i], VK_TRUE, UINT64_MAX);
2370         vkDestroyFence(demo->device, demo->fences[i], NULL);
2371         vkDestroySemaphore(demo->device, demo->image_acquired_semaphores[i], NULL);
2372         vkDestroySemaphore(demo->device, demo->draw_complete_semaphores[i], NULL);
2373         if (demo->separate_present_queue) {
2374             vkDestroySemaphore(demo->device, demo->image_ownership_semaphores[i], NULL);
2375         }
2376     }
2377 
2378     // If the window is currently minimized, demo_resize has already done some cleanup for us.
2379     if (!demo->is_minimized) {
2380         for (i = 0; i < demo->swapchainImageCount; i++) {
2381             vkDestroyFramebuffer(demo->device, demo->swapchain_image_resources[i].framebuffer, NULL);
2382         }
2383         vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
2384 
2385         vkDestroyPipeline(demo->device, demo->pipeline, NULL);
2386         vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
2387         vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
2388         vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
2389         vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, 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         SwappyVk_destroySwapchain(demo->device, demo->swapchain);
2398         demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL);
2399 
2400         vkDestroyImageView(demo->device, demo->depth.view, NULL);
2401         vkDestroyImage(demo->device, demo->depth.image, NULL);
2402         vkFreeMemory(demo->device, demo->depth.mem, NULL);
2403 
2404         for (i = 0; i < demo->swapchainImageCount; i++) {
2405             vkDestroyImageView(demo->device, demo->swapchain_image_resources[i].view, NULL);
2406             vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->swapchain_image_resources[i].cmd);
2407             vkDestroyBuffer(demo->device, demo->swapchain_image_resources[i].uniform_buffer, NULL);
2408             vkFreeMemory(demo->device, demo->swapchain_image_resources[i].uniform_memory, NULL);
2409         }
2410         free(demo->swapchain_image_resources);
2411         free(demo->queue_props);
2412         vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
2413 
2414         if (demo->separate_present_queue) {
2415             vkDestroyCommandPool(demo->device, demo->present_cmd_pool, NULL);
2416         }
2417     }
2418     vkDeviceWaitIdle(demo->device);
2419     vkDestroyDevice(demo->device, NULL);
2420     if (demo->validate) {
2421         demo->DestroyDebugUtilsMessengerEXT(demo->inst, demo->dbg_messenger, NULL);
2422     }
2423     vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
2424 
2425 #if defined(VK_USE_PLATFORM_XLIB_KHR)
2426     XDestroyWindow(demo->display, demo->xlib_window);
2427     XCloseDisplay(demo->display);
2428 #elif defined(VK_USE_PLATFORM_XCB_KHR)
2429     xcb_destroy_window(demo->connection, demo->xcb_window);
2430     xcb_disconnect(demo->connection);
2431     free(demo->atom_wm_delete_window);
2432 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
2433     wl_keyboard_destroy(demo->keyboard);
2434     wl_pointer_destroy(demo->pointer);
2435     wl_seat_destroy(demo->seat);
2436     wl_shell_surface_destroy(demo->shell_surface);
2437     wl_surface_destroy(demo->window);
2438     wl_shell_destroy(demo->shell);
2439     wl_compositor_destroy(demo->compositor);
2440     wl_registry_destroy(demo->registry);
2441     wl_display_disconnect(demo->display);
2442 #endif
2443 
2444     vkDestroyInstance(demo->inst, NULL);
2445 }
2446 
demo_resize(struct demo * demo)2447 static void demo_resize(struct demo *demo) {
2448     uint32_t i;
2449 
2450     // Don't react to resize until after first initialization.
2451     if (!demo->prepared) {
2452         if (demo->is_minimized) {
2453             demo_prepare(demo);
2454         }
2455         return;
2456     }
2457     // In order to properly resize the window, we must re-create the swapchain
2458     // AND redo the command buffers, etc.
2459     //
2460     // First, perform part of the demo_cleanup() function:
2461     demo->prepared = false;
2462     vkDeviceWaitIdle(demo->device);
2463 
2464     for (i = 0; i < demo->swapchainImageCount; i++) {
2465         vkDestroyFramebuffer(demo->device, demo->swapchain_image_resources[i].framebuffer, NULL);
2466     }
2467     vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
2468 
2469     vkDestroyPipeline(demo->device, demo->pipeline, NULL);
2470     vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
2471     vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
2472     vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
2473     vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
2474 
2475     for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
2476         vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
2477         vkDestroyImage(demo->device, demo->textures[i].image, NULL);
2478         vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
2479         vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
2480     }
2481 
2482     vkDestroyImageView(demo->device, demo->depth.view, NULL);
2483     vkDestroyImage(demo->device, demo->depth.image, NULL);
2484     vkFreeMemory(demo->device, demo->depth.mem, NULL);
2485 
2486     for (i = 0; i < demo->swapchainImageCount; i++) {
2487         vkDestroyImageView(demo->device, demo->swapchain_image_resources[i].view, NULL);
2488         vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->swapchain_image_resources[i].cmd);
2489         vkDestroyBuffer(demo->device, demo->swapchain_image_resources[i].uniform_buffer, NULL);
2490         vkFreeMemory(demo->device, demo->swapchain_image_resources[i].uniform_memory, NULL);
2491     }
2492     vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
2493     demo->cmd_pool = VK_NULL_HANDLE;
2494     if (demo->separate_present_queue) {
2495         vkDestroyCommandPool(demo->device, demo->present_cmd_pool, NULL);
2496     }
2497     free(demo->swapchain_image_resources);
2498 
2499     // Second, re-perform the demo_prepare() function, which will re-create the
2500     // swapchain:
2501     demo_prepare(demo);
2502 }
2503 
2504 // On MS-Windows, make this a global, so it's available to WndProc()
2505 struct demo demo;
2506 
2507 #if defined(VK_USE_PLATFORM_WIN32_KHR)
demo_run(struct demo * demo)2508 static void demo_run(struct demo *demo) {
2509     if (!demo->prepared) return;
2510 
2511     demo_draw(demo);
2512     demo->curFrame++;
2513     if (demo->frameCount != INT_MAX && demo->curFrame == demo->frameCount) {
2514         PostQuitMessage(validation_error);
2515     }
2516 }
2517 
2518 // MS-Windows event handling function:
WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)2519 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
2520     switch (uMsg) {
2521         case WM_CLOSE:
2522             PostQuitMessage(validation_error);
2523             break;
2524         case WM_PAINT:
2525             // The validation callback calls MessageBox which can generate paint
2526             // events - don't make more Vulkan calls if we got here from the
2527             // callback
2528             if (!in_callback) {
2529                 demo_run(&demo);
2530             }
2531             break;
2532         case WM_GETMINMAXINFO:  // set window's minimum size
2533             ((MINMAXINFO *)lParam)->ptMinTrackSize = demo.minsize;
2534             return 0;
2535         case WM_ERASEBKGND:
2536             return 1;
2537         case WM_SIZE:
2538             // Resize the application to the new window size, except when
2539             // it was minimized. Vulkan doesn't support images or swapchains
2540             // with width=0 and height=0.
2541             if (wParam != SIZE_MINIMIZED) {
2542                 demo.width = lParam & 0xffff;
2543                 demo.height = (lParam & 0xffff0000) >> 16;
2544                 demo_resize(&demo);
2545             }
2546             break;
2547         default:
2548             break;
2549     }
2550     return (DefWindowProc(hWnd, uMsg, wParam, lParam));
2551 }
2552 
demo_create_window(struct demo * demo)2553 static void demo_create_window(struct demo *demo) {
2554     WNDCLASSEX win_class;
2555 
2556     // Initialize the window class structure:
2557     win_class.cbSize = sizeof(WNDCLASSEX);
2558     win_class.style = CS_HREDRAW | CS_VREDRAW;
2559     win_class.lpfnWndProc = WndProc;
2560     win_class.cbClsExtra = 0;
2561     win_class.cbWndExtra = 0;
2562     win_class.hInstance = demo->connection;  // hInstance
2563     win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
2564     win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
2565     win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
2566     win_class.lpszMenuName = NULL;
2567     win_class.lpszClassName = demo->name;
2568     win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
2569     // Register window class:
2570     if (!RegisterClassEx(&win_class)) {
2571         // It didn't work, so try to give a useful error:
2572         printf("Unexpected error trying to start the application!\n");
2573         fflush(stdout);
2574         exit(1);
2575     }
2576     // Create window with the registered class:
2577     RECT wr = {0, 0, demo->width, demo->height};
2578     AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
2579     demo->window = CreateWindowEx(0,
2580                                   demo->name,            // class name
2581                                   demo->name,            // app name
2582                                   WS_OVERLAPPEDWINDOW |  // window style
2583                                       WS_VISIBLE | WS_SYSMENU,
2584                                   100, 100,            // x/y coords
2585                                   wr.right - wr.left,  // width
2586                                   wr.bottom - wr.top,  // height
2587                                   NULL,                // handle to parent
2588                                   NULL,                // handle to menu
2589                                   demo->connection,    // hInstance
2590                                   NULL);               // no extra parameters
2591     if (!demo->window) {
2592         // It didn't work, so try to give a useful error:
2593         printf("Cannot create a window in which to draw!\n");
2594         fflush(stdout);
2595         exit(1);
2596     }
2597     // Window client area size must be at least 1 pixel high, to prevent crash.
2598     demo->minsize.x = GetSystemMetrics(SM_CXMINTRACK);
2599     demo->minsize.y = GetSystemMetrics(SM_CYMINTRACK) + 1;
2600 }
2601 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
demo_create_xlib_window(struct demo * demo)2602 static void demo_create_xlib_window(struct demo *demo) {
2603     const char *display_envar = getenv("DISPLAY");
2604     if (display_envar == NULL || display_envar[0] == '\0') {
2605         printf("Environment variable DISPLAY requires a valid value.\nExiting ...\n");
2606         fflush(stdout);
2607         exit(1);
2608     }
2609 
2610     XInitThreads();
2611     demo->display = XOpenDisplay(NULL);
2612     long visualMask = VisualScreenMask;
2613     int numberOfVisuals;
2614     XVisualInfo vInfoTemplate = {};
2615     vInfoTemplate.screen = DefaultScreen(demo->display);
2616     XVisualInfo *visualInfo = XGetVisualInfo(demo->display, visualMask, &vInfoTemplate, &numberOfVisuals);
2617 
2618     Colormap colormap =
2619         XCreateColormap(demo->display, RootWindow(demo->display, vInfoTemplate.screen), visualInfo->visual, AllocNone);
2620 
2621     XSetWindowAttributes windowAttributes = {};
2622     windowAttributes.colormap = colormap;
2623     windowAttributes.background_pixel = 0xFFFFFFFF;
2624     windowAttributes.border_pixel = 0;
2625     windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
2626 
2627     demo->xlib_window = XCreateWindow(demo->display, RootWindow(demo->display, vInfoTemplate.screen), 0, 0, demo->width,
2628                                       demo->height, 0, visualInfo->depth, InputOutput, visualInfo->visual,
2629                                       CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, &windowAttributes);
2630 
2631     XSelectInput(demo->display, demo->xlib_window, ExposureMask | KeyPressMask);
2632     XMapWindow(demo->display, demo->xlib_window);
2633     XFlush(demo->display);
2634     demo->xlib_wm_delete_window = XInternAtom(demo->display, "WM_DELETE_WINDOW", False);
2635 }
demo_handle_xlib_event(struct demo * demo,const XEvent * event)2636 static void demo_handle_xlib_event(struct demo *demo, const XEvent *event) {
2637     switch (event->type) {
2638         case ClientMessage:
2639             if ((Atom)event->xclient.data.l[0] == demo->xlib_wm_delete_window) demo->quit = true;
2640             break;
2641         case KeyPress:
2642             switch (event->xkey.keycode) {
2643                 case 0x9:  // Escape
2644                     demo->quit = true;
2645                     break;
2646                 case 0x71:  // left arrow key
2647                     demo->spin_angle -= demo->spin_increment;
2648                     break;
2649                 case 0x72:  // right arrow key
2650                     demo->spin_angle += demo->spin_increment;
2651                     break;
2652                 case 0x41:  // space bar
2653                     demo->pause = !demo->pause;
2654                     break;
2655             }
2656             break;
2657         case ConfigureNotify:
2658             if ((demo->width != event->xconfigure.width) || (demo->height != event->xconfigure.height)) {
2659                 demo->width = event->xconfigure.width;
2660                 demo->height = event->xconfigure.height;
2661                 demo_resize(demo);
2662             }
2663             break;
2664         default:
2665             break;
2666     }
2667 }
2668 
demo_run_xlib(struct demo * demo)2669 static void demo_run_xlib(struct demo *demo) {
2670     while (!demo->quit) {
2671         XEvent event;
2672 
2673         if (demo->pause) {
2674             XNextEvent(demo->display, &event);
2675             demo_handle_xlib_event(demo, &event);
2676         }
2677         while (XPending(demo->display) > 0) {
2678             XNextEvent(demo->display, &event);
2679             demo_handle_xlib_event(demo, &event);
2680         }
2681 
2682         demo_draw(demo);
2683         demo->curFrame++;
2684         if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) demo->quit = true;
2685     }
2686 }
2687 #elif defined(VK_USE_PLATFORM_XCB_KHR)
demo_handle_xcb_event(struct demo * demo,const xcb_generic_event_t * event)2688 static void demo_handle_xcb_event(struct demo *demo, const xcb_generic_event_t *event) {
2689     uint8_t event_code = event->response_type & 0x7f;
2690     switch (event_code) {
2691         case XCB_EXPOSE:
2692             // TODO: Resize window
2693             break;
2694         case XCB_CLIENT_MESSAGE:
2695             if ((*(xcb_client_message_event_t *)event).data.data32[0] == (*demo->atom_wm_delete_window).atom) {
2696                 demo->quit = true;
2697             }
2698             break;
2699         case XCB_KEY_RELEASE: {
2700             const xcb_key_release_event_t *key = (const xcb_key_release_event_t *)event;
2701 
2702             switch (key->detail) {
2703                 case 0x9:  // Escape
2704                     demo->quit = true;
2705                     break;
2706                 case 0x71:  // left arrow key
2707                     demo->spin_angle -= demo->spin_increment;
2708                     break;
2709                 case 0x72:  // right arrow key
2710                     demo->spin_angle += demo->spin_increment;
2711                     break;
2712                 case 0x41:  // space bar
2713                     demo->pause = !demo->pause;
2714                     break;
2715             }
2716         } break;
2717         case XCB_CONFIGURE_NOTIFY: {
2718             const xcb_configure_notify_event_t *cfg = (const xcb_configure_notify_event_t *)event;
2719             if ((demo->width != cfg->width) || (demo->height != cfg->height)) {
2720                 demo->width = cfg->width;
2721                 demo->height = cfg->height;
2722                 demo_resize(demo);
2723             }
2724         } break;
2725         default:
2726             break;
2727     }
2728 }
2729 
demo_run_xcb(struct demo * demo)2730 static void demo_run_xcb(struct demo *demo) {
2731     xcb_flush(demo->connection);
2732 
2733     while (!demo->quit) {
2734         xcb_generic_event_t *event;
2735 
2736         if (demo->pause) {
2737             event = xcb_wait_for_event(demo->connection);
2738         } else {
2739             event = xcb_poll_for_event(demo->connection);
2740         }
2741         while (event) {
2742             demo_handle_xcb_event(demo, event);
2743             free(event);
2744             event = xcb_poll_for_event(demo->connection);
2745         }
2746 
2747         demo_draw(demo);
2748         demo->curFrame++;
2749         if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) demo->quit = true;
2750     }
2751 }
2752 
demo_create_xcb_window(struct demo * demo)2753 static void demo_create_xcb_window(struct demo *demo) {
2754     uint32_t value_mask, value_list[32];
2755 
2756     demo->xcb_window = xcb_generate_id(demo->connection);
2757 
2758     value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
2759     value_list[0] = demo->screen->black_pixel;
2760     value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
2761 
2762     xcb_create_window(demo->connection, XCB_COPY_FROM_PARENT, demo->xcb_window, demo->screen->root, 0, 0, demo->width, demo->height,
2763                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, demo->screen->root_visual, value_mask, value_list);
2764 
2765     /* Magic code that will send notification when window is destroyed */
2766     xcb_intern_atom_cookie_t cookie = xcb_intern_atom(demo->connection, 1, 12, "WM_PROTOCOLS");
2767     xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(demo->connection, cookie, 0);
2768 
2769     xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(demo->connection, 0, 16, "WM_DELETE_WINDOW");
2770     demo->atom_wm_delete_window = xcb_intern_atom_reply(demo->connection, cookie2, 0);
2771 
2772     xcb_change_property(demo->connection, XCB_PROP_MODE_REPLACE, demo->xcb_window, (*reply).atom, 4, 32, 1,
2773                         &(*demo->atom_wm_delete_window).atom);
2774     free(reply);
2775 
2776     xcb_map_window(demo->connection, demo->xcb_window);
2777 
2778     // Force the x/y coordinates to 100,100 results are identical in consecutive
2779     // runs
2780     const uint32_t coords[] = {100, 100};
2781     xcb_configure_window(demo->connection, demo->xcb_window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords);
2782 }
2783 // VK_USE_PLATFORM_XCB_KHR
2784 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
demo_run(struct demo * demo)2785 static void demo_run(struct demo *demo) {
2786     while (!demo->quit) {
2787         if (demo->pause) {
2788             wl_display_dispatch(demo->display);  // block and wait for input
2789         } else {
2790             wl_display_dispatch_pending(demo->display);  // don't block
2791             demo_draw(demo);
2792             demo->curFrame++;
2793             if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) demo->quit = true;
2794         }
2795     }
2796 }
2797 
handle_ping(void * data UNUSED,struct wl_shell_surface * shell_surface,uint32_t serial)2798 static void handle_ping(void *data UNUSED, struct wl_shell_surface *shell_surface, uint32_t serial) {
2799     wl_shell_surface_pong(shell_surface, serial);
2800 }
2801 
handle_configure(void * data UNUSED,struct wl_shell_surface * shell_surface UNUSED,uint32_t edges UNUSED,int32_t width UNUSED,int32_t height UNUSED)2802 static void handle_configure(void *data UNUSED, struct wl_shell_surface *shell_surface UNUSED, uint32_t edges UNUSED,
2803                              int32_t width UNUSED, int32_t height UNUSED) {}
2804 
handle_popup_done(void * data UNUSED,struct wl_shell_surface * shell_surface UNUSED)2805 static void handle_popup_done(void *data UNUSED, struct wl_shell_surface *shell_surface UNUSED) {}
2806 
2807 static const struct wl_shell_surface_listener shell_surface_listener = {handle_ping, handle_configure, handle_popup_done};
2808 
demo_create_window(struct demo * demo)2809 static void demo_create_window(struct demo *demo) {
2810     demo->window = wl_compositor_create_surface(demo->compositor);
2811     if (!demo->window) {
2812         printf("Can not create wayland_surface from compositor!\n");
2813         fflush(stdout);
2814         exit(1);
2815     }
2816 
2817     demo->shell_surface = wl_shell_get_shell_surface(demo->shell, demo->window);
2818     if (!demo->shell_surface) {
2819         printf("Can not get shell_surface from wayland_surface!\n");
2820         fflush(stdout);
2821         exit(1);
2822     }
2823     wl_shell_surface_add_listener(demo->shell_surface, &shell_surface_listener, demo);
2824     wl_shell_surface_set_toplevel(demo->shell_surface);
2825     wl_shell_surface_set_title(demo->shell_surface, APP_SHORT_NAME);
2826 }
2827 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
demo_run(struct demo * demo)2828 static void demo_run(struct demo *demo) {
2829     if (!demo->prepared) return;
2830 
2831     demo_draw(demo);
2832     demo->curFrame++;
2833 }
2834 #elif defined(VK_USE_PLATFORM_MACOS_MVK)
demo_run(struct demo * demo)2835 static void demo_run(struct demo *demo) {
2836     demo_draw(demo);
2837     demo->curFrame++;
2838     if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) {
2839         demo->quit = TRUE;
2840     }
2841 }
2842 #elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
demo_create_display_surface(struct demo * demo)2843 static VkResult demo_create_display_surface(struct demo *demo) {
2844     VkResult U_ASSERT_ONLY err;
2845     uint32_t display_count;
2846     uint32_t mode_count;
2847     uint32_t plane_count;
2848     VkDisplayPropertiesKHR display_props;
2849     VkDisplayKHR display;
2850     VkDisplayModePropertiesKHR mode_props;
2851     VkDisplayPlanePropertiesKHR *plane_props;
2852     VkBool32 found_plane = VK_FALSE;
2853     uint32_t plane_index;
2854     VkExtent2D image_extent;
2855     VkDisplaySurfaceCreateInfoKHR create_info;
2856 
2857     // Get the first display
2858     err = vkGetPhysicalDeviceDisplayPropertiesKHR(demo->gpu, &display_count, NULL);
2859     assert(!err);
2860 
2861     if (display_count == 0) {
2862         printf("Cannot find any display!\n");
2863         fflush(stdout);
2864         exit(1);
2865     }
2866 
2867     display_count = 1;
2868     err = vkGetPhysicalDeviceDisplayPropertiesKHR(demo->gpu, &display_count, &display_props);
2869     assert(!err || (err == VK_INCOMPLETE));
2870 
2871     display = display_props.display;
2872 
2873     // Get the first mode of the display
2874     err = vkGetDisplayModePropertiesKHR(demo->gpu, display, &mode_count, NULL);
2875     assert(!err);
2876 
2877     if (mode_count == 0) {
2878         printf("Cannot find any mode for the display!\n");
2879         fflush(stdout);
2880         exit(1);
2881     }
2882 
2883     mode_count = 1;
2884     err = vkGetDisplayModePropertiesKHR(demo->gpu, display, &mode_count, &mode_props);
2885     assert(!err || (err == VK_INCOMPLETE));
2886 
2887     // Get the list of planes
2888     err = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(demo->gpu, &plane_count, NULL);
2889     assert(!err);
2890 
2891     if (plane_count == 0) {
2892         printf("Cannot find any plane!\n");
2893         fflush(stdout);
2894         exit(1);
2895     }
2896 
2897     plane_props = malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
2898     assert(plane_props);
2899 
2900     err = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(demo->gpu, &plane_count, plane_props);
2901     assert(!err);
2902 
2903     // Find a plane compatible with the display
2904     for (plane_index = 0; plane_index < plane_count; plane_index++) {
2905         uint32_t supported_count;
2906         VkDisplayKHR *supported_displays;
2907 
2908         // Disqualify planes that are bound to a different display
2909         if ((plane_props[plane_index].currentDisplay != VK_NULL_HANDLE) && (plane_props[plane_index].currentDisplay != display)) {
2910             continue;
2911         }
2912 
2913         err = vkGetDisplayPlaneSupportedDisplaysKHR(demo->gpu, plane_index, &supported_count, NULL);
2914         assert(!err);
2915 
2916         if (supported_count == 0) {
2917             continue;
2918         }
2919 
2920         supported_displays = malloc(sizeof(VkDisplayKHR) * supported_count);
2921         assert(supported_displays);
2922 
2923         err = vkGetDisplayPlaneSupportedDisplaysKHR(demo->gpu, plane_index, &supported_count, supported_displays);
2924         assert(!err);
2925 
2926         for (uint32_t i = 0; i < supported_count; i++) {
2927             if (supported_displays[i] == display) {
2928                 found_plane = VK_TRUE;
2929                 break;
2930             }
2931         }
2932 
2933         free(supported_displays);
2934 
2935         if (found_plane) {
2936             break;
2937         }
2938     }
2939 
2940     if (!found_plane) {
2941         printf("Cannot find a plane compatible with the display!\n");
2942         fflush(stdout);
2943         exit(1);
2944     }
2945 
2946     free(plane_props);
2947 
2948     VkDisplayPlaneCapabilitiesKHR planeCaps;
2949     vkGetDisplayPlaneCapabilitiesKHR(demo->gpu, mode_props.displayMode, plane_index, &planeCaps);
2950     // Find a supported alpha mode
2951     VkCompositeAlphaFlagBitsKHR alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
2952     VkCompositeAlphaFlagBitsKHR alphaModes[4] = {
2953         VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR,
2954         VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR,
2955         VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR,
2956         VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR,
2957     };
2958     for (uint32_t i = 0; i < sizeof(alphaModes); i++) {
2959         if (planeCaps.supportedAlpha & alphaModes[i]) {
2960             alphaMode = alphaModes[i];
2961             break;
2962         }
2963     }
2964     image_extent.width = mode_props.parameters.visibleRegion.width;
2965     image_extent.height = mode_props.parameters.visibleRegion.height;
2966 
2967     create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
2968     create_info.pNext = NULL;
2969     create_info.flags = 0;
2970     create_info.displayMode = mode_props.displayMode;
2971     create_info.planeIndex = plane_index;
2972     create_info.planeStackIndex = plane_props[plane_index].currentStackIndex;
2973     create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
2974     create_info.alphaMode = alphaMode;
2975     create_info.globalAlpha = 1.0f;
2976     create_info.imageExtent = image_extent;
2977 
2978     return vkCreateDisplayPlaneSurfaceKHR(demo->inst, &create_info, NULL, &demo->surface);
2979 }
2980 
demo_run_display(struct demo * demo)2981 static void demo_run_display(struct demo *demo) {
2982     while (!demo->quit) {
2983         demo_draw(demo);
2984         demo->curFrame++;
2985 
2986         if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) {
2987             demo->quit = true;
2988         }
2989     }
2990 }
2991 #endif
2992 
2993 /*
2994  * Return 1 (true) if all layer names specified in check_names
2995  * can be found in given layer properties.
2996  */
demo_check_layers(uint32_t check_count,char ** check_names,uint32_t layer_count,VkLayerProperties * layers)2997 static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, uint32_t layer_count, VkLayerProperties *layers) {
2998     for (uint32_t i = 0; i < check_count; i++) {
2999         VkBool32 found = 0;
3000         for (uint32_t j = 0; j < layer_count; j++) {
3001             if (!strcmp(check_names[i], layers[j].layerName)) {
3002                 found = 1;
3003                 break;
3004             }
3005         }
3006         if (!found) {
3007             fprintf(stderr, "Cannot find layer: %s\n", check_names[i]);
3008             return 0;
3009         }
3010     }
3011     return 1;
3012 }
3013 
demo_init_vk(struct demo * demo)3014 static void demo_init_vk(struct demo *demo) {
3015     VkResult err;
3016     uint32_t instance_extension_count = 0;
3017     uint32_t instance_layer_count = 0;
3018     uint32_t validation_layer_count = 0;
3019     char **instance_validation_layers = NULL;
3020     demo->enabled_extension_count = 0;
3021     demo->enabled_layer_count = 0;
3022     demo->is_minimized = false;
3023     demo->cmd_pool = VK_NULL_HANDLE;
3024 
3025     char *instance_validation_layers_alt1[] = {"VK_LAYER_LUNARG_standard_validation"};
3026 
3027     char *instance_validation_layers_alt2[] = {"VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation",
3028                                                "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation",
3029                                                "VK_LAYER_GOOGLE_unique_objects"};
3030 
3031     /* Look for validation layers */
3032     VkBool32 validation_found = 0;
3033     if (demo->validate) {
3034         err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
3035         assert(!err);
3036 
3037         instance_validation_layers = instance_validation_layers_alt1;
3038         if (instance_layer_count > 0) {
3039             VkLayerProperties *instance_layers = malloc(sizeof(VkLayerProperties) * instance_layer_count);
3040             err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers);
3041             assert(!err);
3042 
3043             validation_found = demo_check_layers(ARRAY_SIZE(instance_validation_layers_alt1), instance_validation_layers,
3044                                                  instance_layer_count, instance_layers);
3045             if (validation_found) {
3046                 demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1);
3047                 demo->enabled_layers[0] = "VK_LAYER_LUNARG_standard_validation";
3048                 validation_layer_count = 1;
3049             } else {
3050                 // use alternative set of validation layers
3051                 instance_validation_layers = instance_validation_layers_alt2;
3052                 demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2);
3053                 validation_found = demo_check_layers(ARRAY_SIZE(instance_validation_layers_alt2), instance_validation_layers,
3054                                                      instance_layer_count, instance_layers);
3055                 validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt2);
3056                 for (uint32_t i = 0; i < validation_layer_count; i++) {
3057                     demo->enabled_layers[i] = instance_validation_layers[i];
3058                 }
3059             }
3060             free(instance_layers);
3061         }
3062 
3063         if (!validation_found) {
3064             ERR_EXIT(
3065                 "vkEnumerateInstanceLayerProperties failed to find required validation layer.\n\n"
3066                 "Please look at the Getting Started guide for additional information.\n",
3067                 "vkCreateInstance Failure");
3068         }
3069     }
3070 
3071     /* Look for instance extensions */
3072     VkBool32 surfaceExtFound = 0;
3073     VkBool32 platformSurfaceExtFound = 0;
3074     memset(demo->extension_names, 0, sizeof(demo->extension_names));
3075 
3076     err = vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, NULL);
3077     assert(!err);
3078 
3079     if (instance_extension_count > 0) {
3080         VkExtensionProperties *instance_extensions = malloc(sizeof(VkExtensionProperties) * instance_extension_count);
3081         err = vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, instance_extensions);
3082         assert(!err);
3083         for (uint32_t i = 0; i < instance_extension_count; i++) {
3084             if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3085                 surfaceExtFound = 1;
3086                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_SURFACE_EXTENSION_NAME;
3087             }
3088 #if defined(VK_USE_PLATFORM_WIN32_KHR)
3089             if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3090                 platformSurfaceExtFound = 1;
3091                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
3092             }
3093 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
3094             if (!strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3095                 platformSurfaceExtFound = 1;
3096                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
3097             }
3098 #elif defined(VK_USE_PLATFORM_XCB_KHR)
3099             if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3100                 platformSurfaceExtFound = 1;
3101                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_XCB_SURFACE_EXTENSION_NAME;
3102             }
3103 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
3104             if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3105                 platformSurfaceExtFound = 1;
3106                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
3107             }
3108 #elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
3109             if (!strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3110                 platformSurfaceExtFound = 1;
3111                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_DISPLAY_EXTENSION_NAME;
3112             }
3113 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
3114             if (!strcmp(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3115                 platformSurfaceExtFound = 1;
3116                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
3117             }
3118 #elif defined(VK_USE_PLATFORM_IOS_MVK)
3119             if (!strcmp(VK_MVK_IOS_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3120                 platformSurfaceExtFound = 1;
3121                 demo->extension_names[demo->enabled_extension_count++] = VK_MVK_IOS_SURFACE_EXTENSION_NAME;
3122             }
3123 #elif defined(VK_USE_PLATFORM_MACOS_MVK)
3124             if (!strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3125                 platformSurfaceExtFound = 1;
3126                 demo->extension_names[demo->enabled_extension_count++] = VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
3127             }
3128 #endif
3129             if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) {
3130                 if (demo->validate) {
3131                     demo->extension_names[demo->enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
3132                 }
3133             }
3134             assert(demo->enabled_extension_count < 64);
3135         }
3136 
3137         free(instance_extensions);
3138     }
3139 
3140     if (!surfaceExtFound) {
3141         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_SURFACE_EXTENSION_NAME
3142                  " extension.\n\n"
3143                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3144                  "Please look at the Getting Started guide for additional information.\n",
3145                  "vkCreateInstance Failure");
3146     }
3147     if (!platformSurfaceExtFound) {
3148 #if defined(VK_USE_PLATFORM_WIN32_KHR)
3149         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME
3150                  " extension.\n\n"
3151                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3152                  "Please look at the Getting Started guide for additional information.\n",
3153                  "vkCreateInstance Failure");
3154 #elif defined(VK_USE_PLATFORM_IOS_MVK)
3155         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_MVK_IOS_SURFACE_EXTENSION_NAME
3156                  " extension.\n\n"
3157                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3158                  "Please look at the Getting Started guide for additional information.\n",
3159                  "vkCreateInstance Failure");
3160 #elif defined(VK_USE_PLATFORM_MACOS_MVK)
3161         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_MVK_MACOS_SURFACE_EXTENSION_NAME
3162                  " extension.\n\n"
3163                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3164                  "Please look at the Getting Started guide for additional information.\n",
3165                  "vkCreateInstance Failure");
3166 #elif defined(VK_USE_PLATFORM_XCB_KHR)
3167         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_XCB_SURFACE_EXTENSION_NAME
3168                  " extension.\n\n"
3169                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3170                  "Please look at the Getting Started guide for additional information.\n",
3171                  "vkCreateInstance Failure");
3172 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
3173         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
3174                  " extension.\n\n"
3175                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3176                  "Please look at the Getting Started guide for additional information.\n",
3177                  "vkCreateInstance Failure");
3178 #elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
3179         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_DISPLAY_EXTENSION_NAME
3180                  " extension.\n\n"
3181                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3182                  "Please look at the Getting Started guide for additional information.\n",
3183                  "vkCreateInstance Failure");
3184 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
3185         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
3186                  " extension.\n\n"
3187                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3188                  "Please look at the Getting Started guide for additional information.\n",
3189                  "vkCreateInstance Failure");
3190 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
3191         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find the " VK_KHR_XLIB_SURFACE_EXTENSION_NAME
3192                  " extension.\n\n"
3193                  "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3194                  "Please look at the Getting Started guide for additional information.\n",
3195                  "vkCreateInstance Failure");
3196 #endif
3197     }
3198     const VkApplicationInfo app = {
3199         .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
3200         .pNext = NULL,
3201         .pApplicationName = APP_SHORT_NAME,
3202         .applicationVersion = 0,
3203         .pEngineName = APP_SHORT_NAME,
3204         .engineVersion = 0,
3205         .apiVersion = VK_API_VERSION_1_0,
3206     };
3207     VkInstanceCreateInfo inst_info = {
3208         .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
3209         .pNext = NULL,
3210         .pApplicationInfo = &app,
3211         .enabledLayerCount = demo->enabled_layer_count,
3212         .ppEnabledLayerNames = (const char *const *)instance_validation_layers,
3213         .enabledExtensionCount = demo->enabled_extension_count,
3214         .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
3215     };
3216 
3217     /*
3218      * This is info for a temp callback to use during CreateInstance.
3219      * After the instance is created, we use the instance-based
3220      * function to register the final callback.
3221      */
3222     VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info;
3223     if (demo->validate) {
3224         // VK_EXT_debug_utils style
3225         dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
3226         dbg_messenger_create_info.pNext = NULL;
3227         dbg_messenger_create_info.flags = 0;
3228         dbg_messenger_create_info.messageSeverity =
3229             VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
3230         dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
3231                                                 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
3232                                                 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
3233         dbg_messenger_create_info.pfnUserCallback = debug_messenger_callback;
3234         dbg_messenger_create_info.pUserData = demo;
3235         inst_info.pNext = &dbg_messenger_create_info;
3236     }
3237 
3238     uint32_t gpu_count;
3239 
3240     err = vkCreateInstance(&inst_info, NULL, &demo->inst);
3241     if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
3242         ERR_EXIT(
3243             "Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
3244             "Please look at the Getting Started guide for additional information.\n",
3245             "vkCreateInstance Failure");
3246     } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) {
3247         ERR_EXIT(
3248             "Cannot find a specified extension library.\n"
3249             "Make sure your layers path is set appropriately.\n",
3250             "vkCreateInstance Failure");
3251     } else if (err) {
3252         ERR_EXIT(
3253             "vkCreateInstance failed.\n\n"
3254             "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3255             "Please look at the Getting Started guide for additional information.\n",
3256             "vkCreateInstance Failure");
3257     }
3258 
3259     /* Make initial call to query gpu_count, then second call for gpu info*/
3260     err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
3261     assert(!err);
3262 
3263     if (gpu_count > 0) {
3264         VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
3265         err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, physical_devices);
3266         assert(!err);
3267         /* For cube demo we just grab the first physical device */
3268         demo->gpu = physical_devices[0];
3269         free(physical_devices);
3270     } else {
3271         ERR_EXIT(
3272             "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
3273             "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
3274             "Please look at the Getting Started guide for additional information.\n",
3275             "vkEnumeratePhysicalDevices Failure");
3276     }
3277 
3278     /* Look for device extensions */
3279     uint32_t device_extension_count = 0;
3280     VkBool32 swapchainExtFound = 0;
3281     demo->enabled_extension_count = 0;
3282     memset(demo->extension_names, 0, sizeof(demo->extension_names));
3283 
3284     err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, &device_extension_count, NULL);
3285     assert(!err);
3286 
3287     if (device_extension_count > 0) {
3288         VkExtensionProperties *device_extensions = malloc(sizeof(VkExtensionProperties) * device_extension_count);
3289         err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, &device_extension_count, device_extensions);
3290         assert(!err);
3291 
3292         for (uint32_t i = 0; i < device_extension_count; i++) {
3293             if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, device_extensions[i].extensionName)) {
3294                 swapchainExtFound = 1;
3295                 demo->extension_names[demo->enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
3296             }
3297             assert(demo->enabled_extension_count < 64);
3298         }
3299 
3300         if (demo->VK_KHR_incremental_present_enabled) {
3301             // Even though the user "enabled" the extension via the command
3302             // line, we must make sure that it's enumerated for use with the
3303             // device.  Therefore, disable it here, and re-enable it again if
3304             // enumerated.
3305             demo->VK_KHR_incremental_present_enabled = false;
3306             for (uint32_t i = 0; i < device_extension_count; i++) {
3307                 if (!strcmp(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, device_extensions[i].extensionName)) {
3308                     demo->extension_names[demo->enabled_extension_count++] = VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME;
3309                     demo->VK_KHR_incremental_present_enabled = true;
3310                     DbgMsg("VK_KHR_incremental_present extension enabled\n");
3311                 }
3312                 assert(demo->enabled_extension_count < 64);
3313             }
3314             if (!demo->VK_KHR_incremental_present_enabled) {
3315                 DbgMsg("VK_KHR_incremental_present extension NOT AVAILABLE\n");
3316             }
3317         }
3318 
3319         if (demo->VK_GOOGLE_display_timing_enabled) {
3320             // Even though the user "enabled" the extension via the command
3321             // line, we must make sure that it's enumerated for use with the
3322             // device.  Therefore, disable it here, and re-enable it again if
3323             // enumerated.
3324             demo->VK_GOOGLE_display_timing_enabled = false;
3325             for (uint32_t i = 0; i < device_extension_count; i++) {
3326                 if (!strcmp(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, device_extensions[i].extensionName)) {
3327                     demo->extension_names[demo->enabled_extension_count++] = VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME;
3328                     demo->VK_GOOGLE_display_timing_enabled = true;
3329                     DbgMsg("VK_GOOGLE_display_timing extension enabled\n");
3330                 }
3331                 assert(demo->enabled_extension_count < 64);
3332             }
3333             if (!demo->VK_GOOGLE_display_timing_enabled) {
3334                 DbgMsg("VK_GOOGLE_display_timing extension NOT AVAILABLE\n");
3335             }
3336         }
3337 
3338         // Add any extensions that SwappyVk requires:
3339         uint32_t swappy_required_extension_count = 0;
3340         char **swappy_required_extension_names;
3341         SwappyVk_determineDeviceExtensions(demo->gpu, device_extension_count, device_extensions,
3342                                           &swappy_required_extension_count, NULL);
3343         swappy_required_extension_names = malloc(swappy_required_extension_count);
3344         for (uint32_t i = 0; i < swappy_required_extension_count; i++) {
3345             swappy_required_extension_names[i] = malloc(VK_MAX_EXTENSION_NAME_SIZE + 1);
3346         }
3347         SwappyVk_determineDeviceExtensions(demo->gpu, device_extension_count, device_extensions,
3348                                           &swappy_required_extension_count,
3349                                           swappy_required_extension_names);
3350         for (uint32_t i = 0; i < swappy_required_extension_count; i++) {
3351             demo->extension_names[demo->enabled_extension_count++] = swappy_required_extension_names[i];
3352             DbgMsg("SwappyVk requires the following extension: %s\n", swappy_required_extension_names[i]);
3353         }
3354 
3355         free(device_extensions);
3356     }
3357 
3358     if (!swapchainExtFound) {
3359         ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find the " VK_KHR_SWAPCHAIN_EXTENSION_NAME
3360                  " extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n"
3361                  "Please look at the Getting Started guide for additional information.\n",
3362                  "vkCreateInstance Failure");
3363     }
3364 
3365     if (demo->validate) {
3366         // Setup VK_EXT_debug_utils function pointers always (we use them for
3367         // debug labels and names).
3368         demo->CreateDebugUtilsMessengerEXT =
3369             (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(demo->inst, "vkCreateDebugUtilsMessengerEXT");
3370         demo->DestroyDebugUtilsMessengerEXT =
3371             (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(demo->inst, "vkDestroyDebugUtilsMessengerEXT");
3372         demo->SubmitDebugUtilsMessageEXT =
3373             (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(demo->inst, "vkSubmitDebugUtilsMessageEXT");
3374         demo->CmdBeginDebugUtilsLabelEXT =
3375             (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(demo->inst, "vkCmdBeginDebugUtilsLabelEXT");
3376         demo->CmdEndDebugUtilsLabelEXT =
3377             (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(demo->inst, "vkCmdEndDebugUtilsLabelEXT");
3378         demo->CmdInsertDebugUtilsLabelEXT =
3379             (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(demo->inst, "vkCmdInsertDebugUtilsLabelEXT");
3380         demo->SetDebugUtilsObjectNameEXT =
3381             (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(demo->inst, "vkSetDebugUtilsObjectNameEXT");
3382         if (NULL == demo->CreateDebugUtilsMessengerEXT || NULL == demo->DestroyDebugUtilsMessengerEXT ||
3383             NULL == demo->SubmitDebugUtilsMessageEXT || NULL == demo->CmdBeginDebugUtilsLabelEXT ||
3384             NULL == demo->CmdEndDebugUtilsLabelEXT || NULL == demo->CmdInsertDebugUtilsLabelEXT ||
3385             NULL == demo->SetDebugUtilsObjectNameEXT) {
3386             ERR_EXIT("GetProcAddr: Failed to init VK_EXT_debug_utils\n", "GetProcAddr: Failure");
3387         }
3388 
3389         err = demo->CreateDebugUtilsMessengerEXT(demo->inst, &dbg_messenger_create_info, NULL, &demo->dbg_messenger);
3390         switch (err) {
3391             case VK_SUCCESS:
3392                 break;
3393             case VK_ERROR_OUT_OF_HOST_MEMORY:
3394                 ERR_EXIT("CreateDebugUtilsMessengerEXT: out of host memory\n", "CreateDebugUtilsMessengerEXT Failure");
3395                 break;
3396             default:
3397                 ERR_EXIT("CreateDebugUtilsMessengerEXT: unknown failure\n", "CreateDebugUtilsMessengerEXT Failure");
3398                 break;
3399         }
3400     }
3401     vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props);
3402 
3403     /* Call with NULL data to get count */
3404     vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_family_count, NULL);
3405     assert(demo->queue_family_count >= 1);
3406 
3407     demo->queue_props = (VkQueueFamilyProperties *)malloc(demo->queue_family_count * sizeof(VkQueueFamilyProperties));
3408     vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_family_count, demo->queue_props);
3409 
3410     // Query fine-grained feature support for this device.
3411     //  If app has specific feature requirements it should check supported
3412     //  features based on this query
3413     VkPhysicalDeviceFeatures physDevFeatures;
3414     vkGetPhysicalDeviceFeatures(demo->gpu, &physDevFeatures);
3415 
3416     GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR);
3417     GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR);
3418     GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR);
3419     GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR);
3420     GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR);
3421 }
3422 
demo_create_device(struct demo * demo)3423 static void demo_create_device(struct demo *demo) {
3424     VkResult U_ASSERT_ONLY err;
3425     float queue_priorities[1] = {0.0};
3426     VkDeviceQueueCreateInfo queues[2];
3427     queues[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
3428     queues[0].pNext = NULL;
3429     queues[0].queueFamilyIndex = demo->graphics_queue_family_index;
3430     queues[0].queueCount = 1;
3431     queues[0].pQueuePriorities = queue_priorities;
3432     queues[0].flags = 0;
3433 
3434     VkDeviceCreateInfo device = {
3435         .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
3436         .pNext = NULL,
3437         .queueCreateInfoCount = 1,
3438         .pQueueCreateInfos = queues,
3439         .enabledLayerCount = 0,
3440         .ppEnabledLayerNames = NULL,
3441         .enabledExtensionCount = demo->enabled_extension_count,
3442         .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
3443         .pEnabledFeatures = NULL,  // If specific features are required, pass them in here
3444     };
3445     if (demo->separate_present_queue) {
3446         queues[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
3447         queues[1].pNext = NULL;
3448         queues[1].queueFamilyIndex = demo->present_queue_family_index;
3449         queues[1].queueCount = 1;
3450         queues[1].pQueuePriorities = queue_priorities;
3451         queues[1].flags = 0;
3452         device.queueCreateInfoCount = 2;
3453     }
3454     err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device);
3455     assert(!err);
3456 }
3457 
demo_init_vk_swapchain(struct demo * demo)3458 static void demo_init_vk_swapchain(struct demo *demo) {
3459     VkResult U_ASSERT_ONLY err;
3460 
3461 // Create a WSI surface for the window:
3462 #if defined(VK_USE_PLATFORM_WIN32_KHR)
3463     VkWin32SurfaceCreateInfoKHR createInfo;
3464     createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
3465     createInfo.pNext = NULL;
3466     createInfo.flags = 0;
3467     createInfo.hinstance = demo->connection;
3468     createInfo.hwnd = demo->window;
3469 
3470     err = vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
3471 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
3472     VkWaylandSurfaceCreateInfoKHR createInfo;
3473     createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
3474     createInfo.pNext = NULL;
3475     createInfo.flags = 0;
3476     createInfo.display = demo->display;
3477     createInfo.surface = demo->window;
3478 
3479     err = vkCreateWaylandSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
3480 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
3481     VkAndroidSurfaceCreateInfoKHR createInfo;
3482     createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
3483     createInfo.pNext = NULL;
3484     createInfo.flags = 0;
3485     createInfo.window = (struct ANativeWindow *)(demo->window);
3486 
3487     err = vkCreateAndroidSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
3488 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
3489     VkXlibSurfaceCreateInfoKHR createInfo;
3490     createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
3491     createInfo.pNext = NULL;
3492     createInfo.flags = 0;
3493     createInfo.dpy = demo->display;
3494     createInfo.window = demo->xlib_window;
3495 
3496     err = vkCreateXlibSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
3497 #elif defined(VK_USE_PLATFORM_XCB_KHR)
3498     VkXcbSurfaceCreateInfoKHR createInfo;
3499     createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
3500     createInfo.pNext = NULL;
3501     createInfo.flags = 0;
3502     createInfo.connection = demo->connection;
3503     createInfo.window = demo->xcb_window;
3504 
3505     err = vkCreateXcbSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
3506 #elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
3507     err = demo_create_display_surface(demo);
3508 #elif defined(VK_USE_PLATFORM_IOS_MVK)
3509     VkIOSSurfaceCreateInfoMVK surface;
3510     surface.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
3511     surface.pNext = NULL;
3512     surface.flags = 0;
3513     surface.pView = demo->window;
3514 
3515     err = vkCreateIOSSurfaceMVK(demo->inst, &surface, NULL, &demo->surface);
3516 #elif defined(VK_USE_PLATFORM_MACOS_MVK)
3517     VkMacOSSurfaceCreateInfoMVK surface;
3518     surface.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
3519     surface.pNext = NULL;
3520     surface.flags = 0;
3521     surface.pView = demo->window;
3522 
3523     err = vkCreateMacOSSurfaceMVK(demo->inst, &surface, NULL, &demo->surface);
3524 #endif
3525     assert(!err);
3526 
3527     // Iterate over each queue to learn whether it supports presenting:
3528     VkBool32 *supportsPresent = (VkBool32 *)malloc(demo->queue_family_count * sizeof(VkBool32));
3529     for (uint32_t i = 0; i < demo->queue_family_count; i++) {
3530         demo->fpGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface, &supportsPresent[i]);
3531     }
3532 
3533     // Search for a graphics and a present queue in the array of queue
3534     // families, try to find one that supports both
3535     uint32_t graphicsQueueFamilyIndex = UINT32_MAX;
3536     uint32_t presentQueueFamilyIndex = UINT32_MAX;
3537     for (uint32_t i = 0; i < demo->queue_family_count; i++) {
3538         if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
3539             if (graphicsQueueFamilyIndex == UINT32_MAX) {
3540                 graphicsQueueFamilyIndex = i;
3541             }
3542 
3543             if (supportsPresent[i] == VK_TRUE) {
3544                 graphicsQueueFamilyIndex = i;
3545                 presentQueueFamilyIndex = i;
3546                 break;
3547             }
3548         }
3549     }
3550 
3551     if (presentQueueFamilyIndex == UINT32_MAX) {
3552         // If didn't find a queue that supports both graphics and present, then
3553         // find a separate present queue.
3554         for (uint32_t i = 0; i < demo->queue_family_count; ++i) {
3555             if (supportsPresent[i] == VK_TRUE) {
3556                 presentQueueFamilyIndex = i;
3557                 break;
3558             }
3559         }
3560     }
3561 
3562     // Generate error if could not find both a graphics and a present queue
3563     if (graphicsQueueFamilyIndex == UINT32_MAX || presentQueueFamilyIndex == UINT32_MAX) {
3564         ERR_EXIT("Could not find both graphics and present queues\n", "Swapchain Initialization Failure");
3565     }
3566 
3567     demo->graphics_queue_family_index = graphicsQueueFamilyIndex;
3568     demo->present_queue_family_index = presentQueueFamilyIndex;
3569     demo->separate_present_queue = (demo->graphics_queue_family_index != demo->present_queue_family_index);
3570     free(supportsPresent);
3571 
3572     demo_create_device(demo);
3573 
3574     GET_DEVICE_PROC_ADDR(demo->device, CreateSwapchainKHR);
3575     GET_DEVICE_PROC_ADDR(demo->device, DestroySwapchainKHR);
3576     GET_DEVICE_PROC_ADDR(demo->device, GetSwapchainImagesKHR);
3577     GET_DEVICE_PROC_ADDR(demo->device, AcquireNextImageKHR);
3578     GET_DEVICE_PROC_ADDR(demo->device, QueuePresentKHR);
3579     if (demo->VK_GOOGLE_display_timing_enabled) {
3580         GET_DEVICE_PROC_ADDR(demo->device, GetRefreshCycleDurationGOOGLE);
3581         GET_DEVICE_PROC_ADDR(demo->device, GetPastPresentationTimingGOOGLE);
3582     }
3583 
3584     vkGetDeviceQueue(demo->device, demo->graphics_queue_family_index, 0, &demo->graphics_queue);
3585     SwappyVk_setQueueFamilyIndex(demo->device, demo->graphics_queue, demo->graphics_queue_family_index);
3586 
3587     if (!demo->separate_present_queue) {
3588         demo->present_queue = demo->graphics_queue;
3589     } else {
3590         vkGetDeviceQueue(demo->device, demo->present_queue_family_index, 0, &demo->present_queue);
3591         SwappyVk_setQueueFamilyIndex(demo->device, demo->present_queue, demo->present_queue_family_index);
3592     }
3593 
3594     // Get the list of VkFormat's that are supported:
3595     uint32_t formatCount;
3596     err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, &formatCount, NULL);
3597     assert(!err);
3598     VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
3599     err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, &formatCount, surfFormats);
3600     assert(!err);
3601     // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
3602     // the surface has no preferred format.  Otherwise, at least one
3603     // supported format will be returned.
3604     if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
3605         demo->format = VK_FORMAT_B8G8R8A8_UNORM;
3606     } else {
3607         assert(formatCount >= 1);
3608         demo->format = surfFormats[0].format;
3609     }
3610     demo->color_space = surfFormats[0].colorSpace;
3611 
3612     demo->quit = false;
3613     demo->curFrame = 0;
3614 
3615     // Create semaphores to synchronize acquiring presentable buffers before
3616     // rendering and waiting for drawing to be complete before presenting
3617     VkSemaphoreCreateInfo semaphoreCreateInfo = {
3618         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
3619         .pNext = NULL,
3620         .flags = 0,
3621     };
3622 
3623     // Create fences that we can use to throttle if we get too far
3624     // ahead of the image presents
3625     VkFenceCreateInfo fence_ci = {
3626         .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = VK_FENCE_CREATE_SIGNALED_BIT};
3627     for (uint32_t i = 0; i < FRAME_LAG; i++) {
3628         err = vkCreateFence(demo->device, &fence_ci, NULL, &demo->fences[i]);
3629         assert(!err);
3630 
3631         err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, &demo->image_acquired_semaphores[i]);
3632         assert(!err);
3633 
3634         err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, &demo->draw_complete_semaphores[i]);
3635         assert(!err);
3636 
3637         if (demo->separate_present_queue) {
3638             err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, &demo->image_ownership_semaphores[i]);
3639             assert(!err);
3640         }
3641     }
3642     demo->frame_index = 0;
3643 
3644     // Get Memory information and properties
3645     vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
3646 }
3647 
3648 #if defined(VK_USE_PLATFORM_WAYLAND_KHR)
pointer_handle_enter(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)3649 static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
3650                                  wl_fixed_t sy) {}
3651 
pointer_handle_leave(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)3652 static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) {}
3653 
pointer_handle_motion(void * data,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)3654 static void pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {}
3655 
pointer_handle_button(void * data,struct wl_pointer * wl_pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)3656 static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button,
3657                                   uint32_t state) {
3658     struct demo *demo = data;
3659     if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
3660         wl_shell_surface_move(demo->shell_surface, demo->seat, serial);
3661     }
3662 }
3663 
pointer_handle_axis(void * data,struct wl_pointer * wl_pointer,uint32_t time,uint32_t axis,wl_fixed_t value)3664 static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {}
3665 
3666 static const struct wl_pointer_listener pointer_listener = {
3667     pointer_handle_enter, pointer_handle_leave, pointer_handle_motion, pointer_handle_button, pointer_handle_axis,
3668 };
3669 
keyboard_handle_keymap(void * data,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)3670 static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) {}
3671 
keyboard_handle_enter(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)3672 static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface,
3673                                   struct wl_array *keys) {}
3674 
keyboard_handle_leave(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)3675 static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) {}
3676 
keyboard_handle_key(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t key,uint32_t state)3677 static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key,
3678                                 uint32_t state) {
3679     if (state != WL_KEYBOARD_KEY_STATE_RELEASED) return;
3680     struct demo *demo = data;
3681     switch (key) {
3682         case KEY_ESC:  // Escape
3683             demo->quit = true;
3684             break;
3685         case KEY_LEFT:  // left arrow key
3686             demo->spin_angle -= demo->spin_increment;
3687             break;
3688         case KEY_RIGHT:  // right arrow key
3689             demo->spin_angle += demo->spin_increment;
3690             break;
3691         case KEY_SPACE:  // space bar
3692             demo->pause = !demo->pause;
3693             break;
3694     }
3695 }
3696 
keyboard_handle_modifiers(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)3697 static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed,
3698                                       uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {}
3699 
3700 static const struct wl_keyboard_listener keyboard_listener = {
3701     keyboard_handle_keymap, keyboard_handle_enter, keyboard_handle_leave, keyboard_handle_key, keyboard_handle_modifiers,
3702 };
3703 
seat_handle_capabilities(void * data,struct wl_seat * seat,enum wl_seat_capability caps)3704 static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps) {
3705     // Subscribe to pointer events
3706     struct demo *demo = data;
3707     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !demo->pointer) {
3708         demo->pointer = wl_seat_get_pointer(seat);
3709         wl_pointer_add_listener(demo->pointer, &pointer_listener, demo);
3710     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && demo->pointer) {
3711         wl_pointer_destroy(demo->pointer);
3712         demo->pointer = NULL;
3713     }
3714     // Subscribe to keyboard events
3715     if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
3716         demo->keyboard = wl_seat_get_keyboard(seat);
3717         wl_keyboard_add_listener(demo->keyboard, &keyboard_listener, demo);
3718     } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
3719         wl_keyboard_destroy(demo->keyboard);
3720         demo->keyboard = NULL;
3721     }
3722 }
3723 
3724 static const struct wl_seat_listener seat_listener = {
3725     seat_handle_capabilities,
3726 };
3727 
registry_handle_global(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version UNUSED)3728 static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface,
3729                                    uint32_t version UNUSED) {
3730     struct demo *demo = data;
3731     // pickup wayland objects when they appear
3732     if (strcmp(interface, "wl_compositor") == 0) {
3733         demo->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1);
3734     } else if (strcmp(interface, "wl_shell") == 0) {
3735         demo->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
3736     } else if (strcmp(interface, "wl_seat") == 0) {
3737         demo->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
3738         wl_seat_add_listener(demo->seat, &seat_listener, demo);
3739     }
3740 }
3741 
registry_handle_global_remove(void * data UNUSED,struct wl_registry * registry UNUSED,uint32_t name UNUSED)3742 static void registry_handle_global_remove(void *data UNUSED, struct wl_registry *registry UNUSED, uint32_t name UNUSED) {}
3743 
3744 static const struct wl_registry_listener registry_listener = {registry_handle_global, registry_handle_global_remove};
3745 #endif
3746 
demo_init_connection(struct demo * demo)3747 static void demo_init_connection(struct demo *demo) {
3748 #if defined(VK_USE_PLATFORM_XCB_KHR)
3749     const xcb_setup_t *setup;
3750     xcb_screen_iterator_t iter;
3751     int scr;
3752 
3753     const char *display_envar = getenv("DISPLAY");
3754     if (display_envar == NULL || display_envar[0] == '\0') {
3755         printf("Environment variable DISPLAY requires a valid value.\nExiting ...\n");
3756         fflush(stdout);
3757         exit(1);
3758     }
3759 
3760     demo->connection = xcb_connect(NULL, &scr);
3761     if (xcb_connection_has_error(demo->connection) > 0) {
3762         printf("Cannot find a compatible Vulkan installable client driver (ICD).\nExiting ...\n");
3763         fflush(stdout);
3764         exit(1);
3765     }
3766 
3767     setup = xcb_get_setup(demo->connection);
3768     iter = xcb_setup_roots_iterator(setup);
3769     while (scr-- > 0) xcb_screen_next(&iter);
3770 
3771     demo->screen = iter.data;
3772 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
3773     demo->display = wl_display_connect(NULL);
3774 
3775     if (demo->display == NULL) {
3776         printf("Cannot find a compatible Vulkan installable client driver (ICD).\nExiting ...\n");
3777         fflush(stdout);
3778         exit(1);
3779     }
3780 
3781     demo->registry = wl_display_get_registry(demo->display);
3782     wl_registry_add_listener(demo->registry, &registry_listener, demo);
3783     wl_display_dispatch(demo->display);
3784 #endif
3785 }
3786 
demo_init(struct demo * demo,int argc,char ** argv)3787 static void demo_init(struct demo *demo, int argc, char **argv) {
3788     vec3 eye = {0.0f, 3.0f, 5.0f};
3789     vec3 origin = {0, 0, 0};
3790     vec3 up = {0.0f, 1.0f, 0.0};
3791 
3792     memset(demo, 0, sizeof(*demo));
3793     demo->presentMode = VK_PRESENT_MODE_FIFO_KHR;
3794     demo->frameCount = INT32_MAX;
3795 
3796     for (int i = 1; i < argc; i++) {
3797         if (strcmp(argv[i], "--use_staging") == 0) {
3798             demo->use_staging_buffer = true;
3799             continue;
3800         }
3801         if ((strcmp(argv[i], "--present_mode") == 0) && (i < argc - 1)) {
3802             demo->presentMode = atoi(argv[i + 1]);
3803             i++;
3804             continue;
3805         }
3806         if (strcmp(argv[i], "--break") == 0) {
3807             demo->use_break = true;
3808             continue;
3809         }
3810         if (strcmp(argv[i], "--validate") == 0) {
3811             demo->validate = true;
3812             continue;
3813         }
3814         if (strcmp(argv[i], "--validate-checks-disabled") == 0) {
3815             demo->validate = true;
3816             demo->validate_checks_disabled = true;
3817             continue;
3818         }
3819         if (strcmp(argv[i], "--xlib") == 0) {
3820             fprintf(stderr, "--xlib is deprecated and no longer does anything");
3821             continue;
3822         }
3823         if (strcmp(argv[i], "--c") == 0 && demo->frameCount == INT32_MAX && i < argc - 1 &&
3824             sscanf(argv[i + 1], "%d", &demo->frameCount) == 1 && demo->frameCount >= 0) {
3825             i++;
3826             continue;
3827         }
3828         if (strcmp(argv[i], "--suppress_popups") == 0) {
3829             demo->suppress_popups = true;
3830             continue;
3831         }
3832         if (strcmp(argv[i], "--display_timing") == 0) {
3833             demo->VK_GOOGLE_display_timing_enabled = true;
3834             continue;
3835         }
3836         if (strcmp(argv[i], "--incremental_present") == 0) {
3837             demo->VK_KHR_incremental_present_enabled = true;
3838             continue;
3839         }
3840 
3841 #if defined(ANDROID)
3842         ERR_EXIT("Usage: cube [--validate]\n", "Usage");
3843 #else
3844         fprintf(stderr,
3845                 "Usage:\n  %s\t[--use_staging] [--validate] [--validate-checks-disabled] [--break]\n"
3846                 "\t[--c <framecount>] [--suppress_popups] [--incremental_present] [--display_timing]\n"
3847                 "\t[--present_mode <present mode enum>]\n"
3848                 "\t <present_mode_enum>\tVK_PRESENT_MODE_IMMEDIATE_KHR = %d\n"
3849                 "\t\t\t\tVK_PRESENT_MODE_MAILBOX_KHR = %d\n"
3850                 "\t\t\t\tVK_PRESENT_MODE_FIFO_KHR = %d\n"
3851                 "\t\t\t\tVK_PRESENT_MODE_FIFO_RELAXED_KHR = %d\n",
3852                 APP_SHORT_NAME, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
3853                 VK_PRESENT_MODE_FIFO_RELAXED_KHR);
3854         fflush(stderr);
3855         exit(1);
3856 #endif
3857     }
3858 
3859     demo_init_connection(demo);
3860 
3861     demo_init_vk(demo);
3862 
3863     demo->width = 500;
3864     demo->height = 500;
3865 
3866     demo->spin_angle = 4.0f;
3867     demo->spin_increment = 0.2f;
3868     demo->pause = false;
3869 
3870     mat4x4_perspective(demo->projection_matrix, (float)degreesToRadians(45.0f), 1.0f, 0.1f, 100.0f);
3871     mat4x4_look_at(demo->view_matrix, eye, origin, up);
3872     mat4x4_identity(demo->model_matrix);
3873 
3874     demo->projection_matrix[1][1] *= -1;  // Flip projection matrix from GL to Vulkan orientation.
3875 }
3876 
3877 #if defined(VK_USE_PLATFORM_WIN32_KHR)
3878 // Include header required for parsing the command line options.
3879 #include <shellapi.h>
3880 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR pCmdLine,int nCmdShow)3881 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) {
3882     MSG msg;    // message
3883     bool done;  // flag saying when app is complete
3884     int argc;
3885     char **argv;
3886 
3887     // Ensure wParam is initialized.
3888     msg.wParam = 0;
3889 
3890     // Use the CommandLine functions to get the command line arguments.
3891     // Unfortunately, Microsoft outputs
3892     // this information as wide characters for Unicode, and we simply want the
3893     // Ascii version to be compatible
3894     // with the non-Windows side.  So, we have to convert the information to
3895     // Ascii character strings.
3896     LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc);
3897     if (NULL == commandLineArgs) {
3898         argc = 0;
3899     }
3900 
3901     if (argc > 0) {
3902         argv = (char **)malloc(sizeof(char *) * argc);
3903         if (argv == NULL) {
3904             argc = 0;
3905         } else {
3906             for (int iii = 0; iii < argc; iii++) {
3907                 size_t wideCharLen = wcslen(commandLineArgs[iii]);
3908                 size_t numConverted = 0;
3909 
3910                 argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1));
3911                 if (argv[iii] != NULL) {
3912                     wcstombs_s(&numConverted, argv[iii], wideCharLen + 1, commandLineArgs[iii], wideCharLen + 1);
3913                 }
3914             }
3915         }
3916     } else {
3917         argv = NULL;
3918     }
3919 
3920     demo_init(&demo, argc, argv);
3921 
3922     // Free up the items we had to allocate for the command line arguments.
3923     if (argc > 0 && argv != NULL) {
3924         for (int iii = 0; iii < argc; iii++) {
3925             if (argv[iii] != NULL) {
3926                 free(argv[iii]);
3927             }
3928         }
3929         free(argv);
3930     }
3931 
3932     demo.connection = hInstance;
3933     strncpy(demo.name, "cube", APP_NAME_STR_LEN);
3934     demo_create_window(&demo);
3935     demo_init_vk_swapchain(&demo);
3936 
3937     demo_prepare(&demo);
3938 
3939     done = false;  // initialize loop condition variable
3940 
3941     // main message loop
3942     while (!done) {
3943         PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
3944         if (msg.message == WM_QUIT)  // check for a quit message
3945         {
3946             done = true;  // if found, quit app
3947         } else {
3948             /* Translate and dispatch to event queue*/
3949             TranslateMessage(&msg);
3950             DispatchMessage(&msg);
3951         }
3952         RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT);
3953     }
3954 
3955     demo_cleanup(&demo);
3956 
3957     return (int)msg.wParam;
3958 }
3959 
3960 #elif defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)
demo_main(struct demo * demo,void * view,int argc,const char * argv[])3961 static void demo_main(struct demo *demo, void *view, int argc, const char *argv[]) {
3962 
3963     demo_init(demo, argc, (char **)argv);
3964     demo->window = view;
3965     demo_init_vk_swapchain(demo);
3966     demo_prepare(demo);
3967     demo->spin_angle = 0.4f;
3968 }
3969 
3970 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
3971 #include <android/log.h>
3972 #include <android_native_app_glue.h>
3973 #include "android_util.h"
3974 
3975 static bool initialized = false;
3976 static bool active = false;
3977 struct demo demo;
3978 
processInput(struct android_app * app,AInputEvent * event)3979 static int32_t processInput(struct android_app *app, AInputEvent *event) { return 0; }
3980 
processCommand(struct android_app * app,int32_t cmd)3981 static void processCommand(struct android_app *app, int32_t cmd) {
3982     switch (cmd) {
3983         case APP_CMD_INIT_WINDOW: {
3984             if (app->window) {
3985                 // We're getting a new window.  If the app is starting up, we
3986                 // need to initialize.  If the app has already been
3987                 // initialized, that means that we lost our previous window,
3988                 // which means that we have a lot of work to do.  At a minimum,
3989                 // we need to destroy the swapchain and surface associated with
3990                 // the old window, and create a new surface and swapchain.
3991                 // However, since there are a lot of other objects/state that
3992                 // is tied to the swapchain, it's easiest to simply cleanup and
3993                 // start over (i.e. use a brute-force approach of re-starting
3994                 // the app)
3995                 if (demo.prepared) {
3996                     demo_cleanup(&demo);
3997                 }
3998 
3999                 // Parse Intents into argc, argv
4000                 // Use the following key to send arguments, i.e.
4001                 // --es args "--validate"
4002                 const char key[] = "args";
4003                 char *appTag = (char *)APP_SHORT_NAME;
4004                 int argc = 0;
4005                 char **argv = get_args(app, key, appTag, &argc);
4006 
4007                 __android_log_print(ANDROID_LOG_INFO, appTag, "argc = %i", argc);
4008                 for (int i = 0; i < argc; i++) __android_log_print(ANDROID_LOG_INFO, appTag, "argv[%i] = %s", i, argv[i]);
4009 
4010                 demo_init(&demo, argc, argv);
4011 
4012                 // Free the argv malloc'd by get_args
4013                 for (int i = 0; i < argc; i++) free(argv[i]);
4014 
4015                 demo.window = (void *)app->window;
4016                 demo_init_vk_swapchain(&demo);
4017                 demo_prepare(&demo);
4018                 initialized = true;
4019             }
4020             break;
4021         }
4022         case APP_CMD_GAINED_FOCUS: {
4023             active = true;
4024             break;
4025         }
4026         case APP_CMD_LOST_FOCUS: {
4027             active = false;
4028             break;
4029         }
4030     }
4031 }
4032 
android_main(struct android_app * app)4033 void android_main(struct android_app *app) {
4034 #ifdef ANDROID
4035     int vulkanSupport = InitVulkan();
4036     if (vulkanSupport == 0) return;
4037 #endif
4038 
4039     demo.prepared = false;
4040 
4041     app->onAppCmd = processCommand;
4042     app->onInputEvent = processInput;
4043 
4044     while (1) {
4045         int events;
4046         struct android_poll_source *source;
4047         while (ALooper_pollAll(active ? 0 : -1, NULL, &events, (void **)&source) >= 0) {
4048             if (source) {
4049                 source->process(app, source);
4050             }
4051 
4052             if (app->destroyRequested != 0) {
4053                 demo_cleanup(&demo);
4054                 return;
4055             }
4056         }
4057         if (initialized && active) {
4058             demo_run(&demo);
4059         }
4060     }
4061 }
4062 #else
main(int argc,char ** argv)4063 int main(int argc, char **argv) {
4064     struct demo demo;
4065 
4066     demo_init(&demo, argc, argv);
4067 #if defined(VK_USE_PLATFORM_XCB_KHR)
4068     demo_create_xcb_window(&demo);
4069 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
4070     demo_create_xlib_window(&demo);
4071 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
4072     demo_create_window(&demo);
4073 #endif
4074 
4075     demo_init_vk_swapchain(&demo);
4076 
4077     demo_prepare(&demo);
4078 
4079 #if defined(VK_USE_PLATFORM_XCB_KHR)
4080     demo_run_xcb(&demo);
4081 #elif defined(VK_USE_PLATFORM_XLIB_KHR)
4082     demo_run_xlib(&demo);
4083 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
4084     demo_run(&demo);
4085 #elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
4086     demo_run_display(&demo);
4087 #endif
4088 
4089     demo_cleanup(&demo);
4090 
4091     return validation_error;
4092 }
4093 #endif
4094