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 = ®ion,
1202 };
1203 present.pNext = ®ions;
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, ©_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, ®istry_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