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