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