1/* 2 * Copyright (c) 2015-2016 The Khronos Group Inc. 3 * Copyright (c) 2015-2016 Valve Corporation 4 * Copyright (c) 2015-2016 LunarG, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and/or associated documentation files (the "Materials"), to 8 * deal in the Materials without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Materials, and to permit persons to whom the Materials are 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice(s) and this permission notice shall be included in 14 * all copies or substantial portions of the Materials. 15 * 16 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 * 20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 23 * USE OR OTHER DEALINGS IN THE MATERIALS. 24 * 25 * Author: Chia-I Wu <olv@lunarg.com> 26 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> 27 * Author: Ian Elliott <ian@LunarG.com> 28 * Author: Jon Ashburn <jon@lunarg.com> 29 */ 30 31#define _GNU_SOURCE 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <stdbool.h> 36#include <assert.h> 37#include <signal.h> 38 39#ifdef _WIN32 40#pragma comment(linker, "/subsystem:windows") 41#define APP_NAME_STR_LEN 80 42#endif // _WIN32 43 44#include <vulkan/vulkan.h> 45 46#include <vulkan/vk_sdk_platform.h> 47#include "linmath.h" 48 49#define DEMO_TEXTURE_COUNT 1 50#define APP_SHORT_NAME "cube" 51#define APP_LONG_NAME "The Vulkan Cube Demo Program" 52 53#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 54 55#if defined(NDEBUG) && defined(__GNUC__) 56#define U_ASSERT_ONLY __attribute__((unused)) 57#else 58#define U_ASSERT_ONLY 59#endif 60 61#ifdef _WIN32 62#define ERR_EXIT(err_msg, err_class) \ 63 do { \ 64 MessageBox(NULL, err_msg, err_class, MB_OK); \ 65 exit(1); \ 66 } while (0) 67 68#else // _WIN32 69 70#define ERR_EXIT(err_msg, err_class) \ 71 do { \ 72 printf(err_msg); \ 73 fflush(stdout); \ 74 exit(1); \ 75 } while (0) 76#endif // _WIN32 77 78#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ 79 { \ 80 demo->fp##entrypoint = \ 81 (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \ 82 if (demo->fp##entrypoint == NULL) { \ 83 ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \ 84 "vkGetInstanceProcAddr Failure"); \ 85 } \ 86 } 87 88static PFN_vkGetDeviceProcAddr g_gdpa = NULL; 89 90#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ 91 { \ 92 if (!g_gdpa) \ 93 g_gdpa = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr( \ 94 demo->inst, "vkGetDeviceProcAddr"); \ 95 demo->fp##entrypoint = \ 96 (PFN_vk##entrypoint)g_gdpa(dev, "vk" #entrypoint); \ 97 if (demo->fp##entrypoint == NULL) { \ 98 ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \ 99 "vkGetDeviceProcAddr Failure"); \ 100 } \ 101 } 102 103/* 104 * structure to track all objects related to a texture. 105 */ 106struct texture_object { 107 VkSampler sampler; 108 109 VkImage image; 110 VkImageLayout imageLayout; 111 112 VkMemoryAllocateInfo mem_alloc; 113 VkDeviceMemory mem; 114 VkImageView view; 115 int32_t tex_width, tex_height; 116}; 117 118static char *tex_files[] = {"lunarg.ppm"}; 119 120static int validation_error = 0; 121 122struct vkcube_vs_uniform { 123 // Must start with MVP 124 float mvp[4][4]; 125 float position[12 * 3][4]; 126 float color[12 * 3][4]; 127}; 128 129struct vktexcube_vs_uniform { 130 // Must start with MVP 131 float mvp[4][4]; 132 float position[12 * 3][4]; 133 float attr[12 * 3][4]; 134}; 135 136//-------------------------------------------------------------------------------------- 137// Mesh and VertexFormat Data 138//-------------------------------------------------------------------------------------- 139// clang-format off 140struct Vertex 141{ 142 float posX, posY, posZ, posW; // Position data 143 float r, g, b, a; // Color 144}; 145 146struct VertexPosTex 147{ 148 float posX, posY, posZ, posW; // Position data 149 float u, v, s, t; // Texcoord 150}; 151 152#define XYZ1(_x_, _y_, _z_) (_x_), (_y_), (_z_), 1.f 153#define UV(_u_, _v_) (_u_), (_v_), 0.f, 1.f 154 155static const float g_vertex_buffer_data[] = { 156 -1.0f,-1.0f,-1.0f, // -X side 157 -1.0f,-1.0f, 1.0f, 158 -1.0f, 1.0f, 1.0f, 159 -1.0f, 1.0f, 1.0f, 160 -1.0f, 1.0f,-1.0f, 161 -1.0f,-1.0f,-1.0f, 162 163 -1.0f,-1.0f,-1.0f, // -Z side 164 1.0f, 1.0f,-1.0f, 165 1.0f,-1.0f,-1.0f, 166 -1.0f,-1.0f,-1.0f, 167 -1.0f, 1.0f,-1.0f, 168 1.0f, 1.0f,-1.0f, 169 170 -1.0f,-1.0f,-1.0f, // -Y side 171 1.0f,-1.0f,-1.0f, 172 1.0f,-1.0f, 1.0f, 173 -1.0f,-1.0f,-1.0f, 174 1.0f,-1.0f, 1.0f, 175 -1.0f,-1.0f, 1.0f, 176 177 -1.0f, 1.0f,-1.0f, // +Y side 178 -1.0f, 1.0f, 1.0f, 179 1.0f, 1.0f, 1.0f, 180 -1.0f, 1.0f,-1.0f, 181 1.0f, 1.0f, 1.0f, 182 1.0f, 1.0f,-1.0f, 183 184 1.0f, 1.0f,-1.0f, // +X side 185 1.0f, 1.0f, 1.0f, 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 191 -1.0f, 1.0f, 1.0f, // +Z side 192 -1.0f,-1.0f, 1.0f, 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}; 198 199static const float g_uv_buffer_data[] = { 200 0.0f, 0.0f, // -X side 201 1.0f, 0.0f, 202 1.0f, 1.0f, 203 1.0f, 1.0f, 204 0.0f, 1.0f, 205 0.0f, 0.0f, 206 207 1.0f, 0.0f, // -Z side 208 0.0f, 1.0f, 209 0.0f, 0.0f, 210 1.0f, 0.0f, 211 1.0f, 1.0f, 212 0.0f, 1.0f, 213 214 1.0f, 1.0f, // -Y side 215 1.0f, 0.0f, 216 0.0f, 0.0f, 217 1.0f, 1.0f, 218 0.0f, 0.0f, 219 0.0f, 1.0f, 220 221 1.0f, 1.0f, // +Y side 222 0.0f, 1.0f, 223 0.0f, 0.0f, 224 1.0f, 1.0f, 225 0.0f, 0.0f, 226 1.0f, 0.0f, 227 228 1.0f, 1.0f, // +X side 229 0.0f, 1.0f, 230 0.0f, 0.0f, 231 0.0f, 0.0f, 232 1.0f, 0.0f, 233 1.0f, 1.0f, 234 235 0.0f, 1.0f, // +Z side 236 0.0f, 0.0f, 237 1.0f, 1.0f, 238 0.0f, 0.0f, 239 1.0f, 0.0f, 240 1.0f, 1.0f, 241}; 242// clang-format on 243 244void dumpMatrix(const char *note, mat4x4 MVP) { 245 int i; 246 247 printf("%s: \n", note); 248 for (i = 0; i < 4; i++) { 249 printf("%f, %f, %f, %f\n", MVP[i][0], MVP[i][1], MVP[i][2], MVP[i][3]); 250 } 251 printf("\n"); 252 fflush(stdout); 253} 254 255void dumpVec4(const char *note, vec4 vector) { 256 printf("%s: \n", note); 257 printf("%f, %f, %f, %f\n", vector[0], vector[1], vector[2], vector[3]); 258 printf("\n"); 259 fflush(stdout); 260} 261 262VKAPI_ATTR VkBool32 VKAPI_CALL 263dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, 264 uint64_t srcObject, size_t location, int32_t msgCode, 265 const char *pLayerPrefix, const char *pMsg, void *pUserData) { 266 char *message = (char *)malloc(strlen(pMsg) + 100); 267 268 assert(message); 269 270 if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { 271 sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, 272 pMsg); 273 validation_error = 1; 274 } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { 275 // We know that we're submitting queues without fences, ignore this 276 // warning 277 if (strstr(pMsg, 278 "vkQueueSubmit parameter, VkFence fence, is null pointer")) { 279 return false; 280 } 281 sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, 282 pMsg); 283 validation_error = 1; 284 } else { 285 validation_error = 1; 286 return false; 287 } 288 289#ifdef _WIN32 290 MessageBox(NULL, message, "Alert", MB_OK); 291#else 292 printf("%s\n", message); 293 fflush(stdout); 294#endif 295 free(message); 296 297 /* 298 * false indicates that layer should not bail-out of an 299 * API call that had validation failures. This may mean that the 300 * app dies inside the driver due to invalid parameter(s). 301 * That's what would happen without validation layers, so we'll 302 * keep that behavior here. 303 */ 304 return false; 305} 306 307VkBool32 BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, 308 uint64_t srcObject, size_t location, int32_t msgCode, 309 const char *pLayerPrefix, const char *pMsg, 310 void *pUserData) { 311#ifndef WIN32 312 raise(SIGTRAP); 313#else 314 DebugBreak(); 315#endif 316 317 return false; 318} 319 320typedef struct _SwapchainBuffers { 321 VkImage image; 322 VkCommandBuffer cmd; 323 VkImageView view; 324} SwapchainBuffers; 325 326struct demo { 327#ifdef _WIN32 328#define APP_NAME_STR_LEN 80 329 HINSTANCE connection; // hInstance - Windows Instance 330 char name[APP_NAME_STR_LEN]; // Name to put on the window/icon 331 HWND window; // hWnd - window handle 332#else // _WIN32 333 xcb_connection_t *connection; 334 xcb_screen_t *screen; 335 xcb_window_t window; 336 xcb_intern_atom_reply_t *atom_wm_delete_window; 337#endif // _WIN32 338 VkSurfaceKHR surface; 339 bool prepared; 340 bool use_staging_buffer; 341 342 VkInstance inst; 343 VkPhysicalDevice gpu; 344 VkDevice device; 345 VkQueue queue; 346 uint32_t graphics_queue_node_index; 347 VkPhysicalDeviceProperties gpu_props; 348 VkQueueFamilyProperties *queue_props; 349 VkPhysicalDeviceMemoryProperties memory_properties; 350 351 uint32_t enabled_extension_count; 352 uint32_t enabled_layer_count; 353 char *extension_names[64]; 354 char *device_validation_layers[64]; 355 356 int width, height; 357 VkFormat format; 358 VkColorSpaceKHR color_space; 359 360 PFN_vkGetPhysicalDeviceSurfaceSupportKHR 361 fpGetPhysicalDeviceSurfaceSupportKHR; 362 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR 363 fpGetPhysicalDeviceSurfaceCapabilitiesKHR; 364 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR 365 fpGetPhysicalDeviceSurfaceFormatsKHR; 366 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR 367 fpGetPhysicalDeviceSurfacePresentModesKHR; 368 PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; 369 PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; 370 PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; 371 PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; 372 PFN_vkQueuePresentKHR fpQueuePresentKHR; 373 uint32_t swapchainImageCount; 374 VkSwapchainKHR swapchain; 375 SwapchainBuffers *buffers; 376 377 VkCommandPool cmd_pool; 378 379 struct { 380 VkFormat format; 381 382 VkImage image; 383 VkMemoryAllocateInfo mem_alloc; 384 VkDeviceMemory mem; 385 VkImageView view; 386 } depth; 387 388 struct texture_object textures[DEMO_TEXTURE_COUNT]; 389 390 struct { 391 VkBuffer buf; 392 VkMemoryAllocateInfo mem_alloc; 393 VkDeviceMemory mem; 394 VkDescriptorBufferInfo buffer_info; 395 } uniform_data; 396 397 VkCommandBuffer cmd; // Buffer for initialization commands 398 VkPipelineLayout pipeline_layout; 399 VkDescriptorSetLayout desc_layout; 400 VkPipelineCache pipelineCache; 401 VkRenderPass render_pass; 402 VkPipeline pipeline; 403 404 mat4x4 projection_matrix; 405 mat4x4 view_matrix; 406 mat4x4 model_matrix; 407 408 float spin_angle; 409 float spin_increment; 410 bool pause; 411 412 VkShaderModule vert_shader_module; 413 VkShaderModule frag_shader_module; 414 415 VkDescriptorPool desc_pool; 416 VkDescriptorSet desc_set; 417 418 VkFramebuffer *framebuffers; 419 420 bool quit; 421 int32_t curFrame; 422 int32_t frameCount; 423 bool validate; 424 bool use_break; 425 PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; 426 PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; 427 VkDebugReportCallbackEXT msg_callback; 428 PFN_vkDebugReportMessageEXT DebugReportMessage; 429 430 uint32_t current_buffer; 431 uint32_t queue_count; 432}; 433 434// Forward declaration: 435static void demo_resize(struct demo *demo); 436 437static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits, 438 VkFlags requirements_mask, 439 uint32_t *typeIndex) { 440 // Search memtypes to find first index with those properties 441 for (uint32_t i = 0; i < 32; i++) { 442 if ((typeBits & 1) == 1) { 443 // Type is available, does it match user properties? 444 if ((demo->memory_properties.memoryTypes[i].propertyFlags & 445 requirements_mask) == requirements_mask) { 446 *typeIndex = i; 447 return true; 448 } 449 } 450 typeBits >>= 1; 451 } 452 // No memory types matched, return failure 453 return false; 454} 455 456static void demo_flush_init_cmd(struct demo *demo) { 457 VkResult U_ASSERT_ONLY err; 458 459 if (demo->cmd == VK_NULL_HANDLE) 460 return; 461 462 err = vkEndCommandBuffer(demo->cmd); 463 assert(!err); 464 465 const VkCommandBuffer cmd_bufs[] = {demo->cmd}; 466 VkFence nullFence = VK_NULL_HANDLE; 467 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 468 .pNext = NULL, 469 .waitSemaphoreCount = 0, 470 .pWaitSemaphores = NULL, 471 .pWaitDstStageMask = NULL, 472 .commandBufferCount = 1, 473 .pCommandBuffers = cmd_bufs, 474 .signalSemaphoreCount = 0, 475 .pSignalSemaphores = NULL}; 476 477 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); 478 assert(!err); 479 480 err = vkQueueWaitIdle(demo->queue); 481 assert(!err); 482 483 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs); 484 demo->cmd = VK_NULL_HANDLE; 485} 486 487static void demo_set_image_layout(struct demo *demo, VkImage image, 488 VkImageAspectFlags aspectMask, 489 VkImageLayout old_image_layout, 490 VkImageLayout new_image_layout, 491 VkAccessFlagBits srcAccessMask) { 492 VkResult U_ASSERT_ONLY err; 493 494 if (demo->cmd == VK_NULL_HANDLE) { 495 const VkCommandBufferAllocateInfo cmd = { 496 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 497 .pNext = NULL, 498 .commandPool = demo->cmd_pool, 499 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, 500 .commandBufferCount = 1, 501 }; 502 503 err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->cmd); 504 assert(!err); 505 506 VkCommandBufferInheritanceInfo cmd_buf_hinfo = { 507 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, 508 .pNext = NULL, 509 .renderPass = VK_NULL_HANDLE, 510 .subpass = 0, 511 .framebuffer = VK_NULL_HANDLE, 512 .occlusionQueryEnable = VK_FALSE, 513 .queryFlags = 0, 514 .pipelineStatistics = 0, 515 }; 516 VkCommandBufferBeginInfo cmd_buf_info = { 517 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 518 .pNext = NULL, 519 .flags = 0, 520 .pInheritanceInfo = &cmd_buf_hinfo, 521 }; 522 err = vkBeginCommandBuffer(demo->cmd, &cmd_buf_info); 523 assert(!err); 524 } 525 526 VkImageMemoryBarrier image_memory_barrier = { 527 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 528 .pNext = NULL, 529 .srcAccessMask = srcAccessMask, 530 .dstAccessMask = 0, 531 .oldLayout = old_image_layout, 532 .newLayout = new_image_layout, 533 .image = image, 534 .subresourceRange = {aspectMask, 0, 1, 0, 1}}; 535 536 if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { 537 /* Make sure anything that was copying from this image has completed */ 538 image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; 539 } 540 541 if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { 542 image_memory_barrier.dstAccessMask = 543 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; 544 } 545 546 if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { 547 image_memory_barrier.dstAccessMask = 548 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; 549 } 550 551 if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { 552 /* Make sure any Copy or CPU writes to image are flushed */ 553 image_memory_barrier.dstAccessMask = 554 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; 555 } 556 557 VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier; 558 559 VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; 560 VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; 561 562 vkCmdPipelineBarrier(demo->cmd, src_stages, dest_stages, 0, 0, NULL, 0, 563 NULL, 1, pmemory_barrier); 564} 565 566static void demo_draw_build_cmd(struct demo *demo, VkCommandBuffer cmd_buf) { 567 VkCommandBufferInheritanceInfo cmd_buf_hinfo = { 568 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, 569 .pNext = NULL, 570 .renderPass = VK_NULL_HANDLE, 571 .subpass = 0, 572 .framebuffer = VK_NULL_HANDLE, 573 .occlusionQueryEnable = VK_FALSE, 574 .queryFlags = 0, 575 .pipelineStatistics = 0, 576 }; 577 const VkCommandBufferBeginInfo cmd_buf_info = { 578 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 579 .pNext = NULL, 580 .flags = 0, 581 .pInheritanceInfo = &cmd_buf_hinfo, 582 }; 583 const VkClearValue clear_values[2] = { 584 [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}}, 585 [1] = {.depthStencil = {1.0f, 0}}, 586 }; 587 const VkRenderPassBeginInfo rp_begin = { 588 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 589 .pNext = NULL, 590 .renderPass = demo->render_pass, 591 .framebuffer = demo->framebuffers[demo->current_buffer], 592 .renderArea.offset.x = 0, 593 .renderArea.offset.y = 0, 594 .renderArea.extent.width = demo->width, 595 .renderArea.extent.height = demo->height, 596 .clearValueCount = 2, 597 .pClearValues = clear_values, 598 }; 599 VkResult U_ASSERT_ONLY err; 600 601 err = vkBeginCommandBuffer(cmd_buf, &cmd_buf_info); 602 assert(!err); 603 604 vkCmdBeginRenderPass(cmd_buf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); 605 606 vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, demo->pipeline); 607 vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, 608 demo->pipeline_layout, 0, 1, &demo->desc_set, 0, 609 NULL); 610 611 VkViewport viewport; 612 memset(&viewport, 0, sizeof(viewport)); 613 viewport.height = (float)demo->height; 614 viewport.width = (float)demo->width; 615 viewport.minDepth = (float)0.0f; 616 viewport.maxDepth = (float)1.0f; 617 vkCmdSetViewport(cmd_buf, 0, 1, &viewport); 618 619 VkRect2D scissor; 620 memset(&scissor, 0, sizeof(scissor)); 621 scissor.extent.width = demo->width; 622 scissor.extent.height = demo->height; 623 scissor.offset.x = 0; 624 scissor.offset.y = 0; 625 vkCmdSetScissor(cmd_buf, 0, 1, &scissor); 626 627 vkCmdDraw(cmd_buf, 12 * 3, 1, 0, 0); 628 vkCmdEndRenderPass(cmd_buf); 629 630 VkImageMemoryBarrier prePresentBarrier = { 631 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 632 .pNext = NULL, 633 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 634 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, 635 .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 636 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 637 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 638 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 639 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; 640 641 prePresentBarrier.image = demo->buffers[demo->current_buffer].image; 642 VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier; 643 vkCmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 644 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, 645 NULL, 1, pmemory_barrier); 646 647 err = vkEndCommandBuffer(cmd_buf); 648 assert(!err); 649} 650 651void demo_update_data_buffer(struct demo *demo) { 652 mat4x4 MVP, Model, VP; 653 int matrixSize = sizeof(MVP); 654 uint8_t *pData; 655 VkResult U_ASSERT_ONLY err; 656 657 mat4x4_mul(VP, demo->projection_matrix, demo->view_matrix); 658 659 // Rotate 22.5 degrees around the Y axis 660 mat4x4_dup(Model, demo->model_matrix); 661 mat4x4_rotate(demo->model_matrix, Model, 0.0f, 1.0f, 0.0f, 662 (float)degreesToRadians(demo->spin_angle)); 663 mat4x4_mul(MVP, VP, demo->model_matrix); 664 665 err = vkMapMemory(demo->device, demo->uniform_data.mem, 0, 666 demo->uniform_data.mem_alloc.allocationSize, 0, 667 (void **)&pData); 668 assert(!err); 669 670 memcpy(pData, (const void *)&MVP[0][0], matrixSize); 671 672 vkUnmapMemory(demo->device, demo->uniform_data.mem); 673} 674 675static void demo_draw(struct demo *demo) { 676 VkResult U_ASSERT_ONLY err; 677 VkSemaphore presentCompleteSemaphore; 678 VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = { 679 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, 680 .pNext = NULL, 681 .flags = 0, 682 }; 683 VkFence nullFence = VK_NULL_HANDLE; 684 685 err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo, 686 NULL, &presentCompleteSemaphore); 687 assert(!err); 688 689 // Get the index of the next available swapchain image: 690 err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX, 691 presentCompleteSemaphore, 692 (VkFence)0, // TODO: Show use of fence 693 &demo->current_buffer); 694 if (err == VK_ERROR_OUT_OF_DATE_KHR) { 695 // demo->swapchain is out of date (e.g. the window was resized) and 696 // must be recreated: 697 demo_resize(demo); 698 demo_draw(demo); 699 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); 700 return; 701 } else if (err == VK_SUBOPTIMAL_KHR) { 702 // demo->swapchain is not as optimal as it could be, but the platform's 703 // presentation engine will still present the image correctly. 704 } else { 705 assert(!err); 706 } 707 708 // Assume the command buffer has been run on current_buffer before so 709 // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL 710 demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image, 711 VK_IMAGE_ASPECT_COLOR_BIT, 712 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 713 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 714 0); 715 demo_flush_init_cmd(demo); 716 717 // Wait for the present complete semaphore to be signaled to ensure 718 // that the image won't be rendered to until the presentation 719 // engine has fully released ownership to the application, and it is 720 // okay to render to the image. 721 722 // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 723 VkPipelineStageFlags pipe_stage_flags = 724 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; 725 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 726 .pNext = NULL, 727 .waitSemaphoreCount = 1, 728 .pWaitSemaphores = &presentCompleteSemaphore, 729 .pWaitDstStageMask = &pipe_stage_flags, 730 .commandBufferCount = 1, 731 .pCommandBuffers = 732 &demo->buffers[demo->current_buffer].cmd, 733 .signalSemaphoreCount = 0, 734 .pSignalSemaphores = NULL}; 735 736 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); 737 assert(!err); 738 739 VkPresentInfoKHR present = { 740 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 741 .pNext = NULL, 742 .swapchainCount = 1, 743 .pSwapchains = &demo->swapchain, 744 .pImageIndices = &demo->current_buffer, 745 }; 746 747 // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER? 748 err = demo->fpQueuePresentKHR(demo->queue, &present); 749 if (err == VK_ERROR_OUT_OF_DATE_KHR) { 750 // demo->swapchain is out of date (e.g. the window was resized) and 751 // must be recreated: 752 demo_resize(demo); 753 } else if (err == VK_SUBOPTIMAL_KHR) { 754 // demo->swapchain is not as optimal as it could be, but the platform's 755 // presentation engine will still present the image correctly. 756 } else { 757 assert(!err); 758 } 759 760 err = vkQueueWaitIdle(demo->queue); 761 assert(err == VK_SUCCESS); 762 763 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); 764} 765 766static void demo_prepare_buffers(struct demo *demo) { 767 VkResult U_ASSERT_ONLY err; 768 VkSwapchainKHR oldSwapchain = demo->swapchain; 769 770 // Check the surface capabilities and formats 771 VkSurfaceCapabilitiesKHR surfCapabilities; 772 err = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR( 773 demo->gpu, demo->surface, &surfCapabilities); 774 assert(!err); 775 776 uint32_t presentModeCount; 777 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR( 778 demo->gpu, demo->surface, &presentModeCount, NULL); 779 assert(!err); 780 VkPresentModeKHR *presentModes = 781 (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); 782 assert(presentModes); 783 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR( 784 demo->gpu, demo->surface, &presentModeCount, presentModes); 785 assert(!err); 786 787 VkExtent2D swapchainExtent; 788 // width and height are either both -1, or both not -1. 789 if (surfCapabilities.currentExtent.width == (uint32_t)-1) { 790 // If the surface size is undefined, the size is set to 791 // the size of the images requested. 792 swapchainExtent.width = demo->width; 793 swapchainExtent.height = demo->height; 794 } else { 795 // If the surface size is defined, the swap chain size must match 796 swapchainExtent = surfCapabilities.currentExtent; 797 demo->width = surfCapabilities.currentExtent.width; 798 demo->height = surfCapabilities.currentExtent.height; 799 } 800 801 // If mailbox mode is available, use it, as is the lowest-latency non- 802 // tearing mode. If not, try IMMEDIATE which will usually be available, 803 // and is fastest (though it tears). If not, fall back to FIFO which is 804 // always available. 805 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; 806 for (size_t i = 0; i < presentModeCount; i++) { 807 if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { 808 swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; 809 break; 810 } 811 if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && 812 (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { 813 swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; 814 } 815 } 816 817 // Determine the number of VkImage's to use in the swap chain (we desire to 818 // own only 1 image at a time, besides the images being displayed and 819 // queued for display): 820 uint32_t desiredNumberOfSwapchainImages = 821 surfCapabilities.minImageCount + 1; 822 if ((surfCapabilities.maxImageCount > 0) && 823 (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { 824 // Application must settle for fewer images than desired: 825 desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; 826 } 827 828 VkSurfaceTransformFlagsKHR preTransform; 829 if (surfCapabilities.supportedTransforms & 830 VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { 831 preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; 832 } else { 833 preTransform = surfCapabilities.currentTransform; 834 } 835 836 const VkSwapchainCreateInfoKHR swapchain = { 837 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 838 .pNext = NULL, 839 .surface = demo->surface, 840 .minImageCount = desiredNumberOfSwapchainImages, 841 .imageFormat = demo->format, 842 .imageColorSpace = demo->color_space, 843 .imageExtent = 844 { 845 .width = swapchainExtent.width, .height = swapchainExtent.height, 846 }, 847 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 848 .preTransform = preTransform, 849 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 850 .imageArrayLayers = 1, 851 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, 852 .queueFamilyIndexCount = 0, 853 .pQueueFamilyIndices = NULL, 854 .presentMode = swapchainPresentMode, 855 .oldSwapchain = oldSwapchain, 856 .clipped = true, 857 }; 858 uint32_t i; 859 860 err = demo->fpCreateSwapchainKHR(demo->device, &swapchain, NULL, 861 &demo->swapchain); 862 assert(!err); 863 864 // If we just re-created an existing swapchain, we should destroy the old 865 // swapchain at this point. 866 // Note: destroying the swapchain also cleans up all its associated 867 // presentable images once the platform is done with them. 868 if (oldSwapchain != VK_NULL_HANDLE) { 869 demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL); 870 } 871 872 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, 873 &demo->swapchainImageCount, NULL); 874 assert(!err); 875 876 VkImage *swapchainImages = 877 (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage)); 878 assert(swapchainImages); 879 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, 880 &demo->swapchainImageCount, 881 swapchainImages); 882 assert(!err); 883 884 demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) * 885 demo->swapchainImageCount); 886 assert(demo->buffers); 887 888 for (i = 0; i < demo->swapchainImageCount; i++) { 889 VkImageViewCreateInfo color_image_view = { 890 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 891 .pNext = NULL, 892 .format = demo->format, 893 .components = 894 { 895 .r = VK_COMPONENT_SWIZZLE_R, 896 .g = VK_COMPONENT_SWIZZLE_G, 897 .b = VK_COMPONENT_SWIZZLE_B, 898 .a = VK_COMPONENT_SWIZZLE_A, 899 }, 900 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 901 .baseMipLevel = 0, 902 .levelCount = 1, 903 .baseArrayLayer = 0, 904 .layerCount = 1}, 905 .viewType = VK_IMAGE_VIEW_TYPE_2D, 906 .flags = 0, 907 }; 908 909 demo->buffers[i].image = swapchainImages[i]; 910 911 // Render loop will expect image to have been used before and in 912 // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 913 // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image 914 // to that state 915 demo_set_image_layout( 916 demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, 917 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 918 0); 919 920 color_image_view.image = demo->buffers[i].image; 921 922 err = vkCreateImageView(demo->device, &color_image_view, NULL, 923 &demo->buffers[i].view); 924 assert(!err); 925 } 926 927 if (NULL != presentModes) { 928 free(presentModes); 929 } 930} 931 932static void demo_prepare_depth(struct demo *demo) { 933 const VkFormat depth_format = VK_FORMAT_D16_UNORM; 934 const VkImageCreateInfo image = { 935 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 936 .pNext = NULL, 937 .imageType = VK_IMAGE_TYPE_2D, 938 .format = depth_format, 939 .extent = {demo->width, demo->height, 1}, 940 .mipLevels = 1, 941 .arrayLayers = 1, 942 .samples = VK_SAMPLE_COUNT_1_BIT, 943 .tiling = VK_IMAGE_TILING_OPTIMAL, 944 .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 945 .flags = 0, 946 }; 947 948 VkImageViewCreateInfo view = { 949 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 950 .pNext = NULL, 951 .image = VK_NULL_HANDLE, 952 .format = depth_format, 953 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, 954 .baseMipLevel = 0, 955 .levelCount = 1, 956 .baseArrayLayer = 0, 957 .layerCount = 1}, 958 .flags = 0, 959 .viewType = VK_IMAGE_VIEW_TYPE_2D, 960 }; 961 962 VkMemoryRequirements mem_reqs; 963 VkResult U_ASSERT_ONLY err; 964 bool U_ASSERT_ONLY pass; 965 966 demo->depth.format = depth_format; 967 968 /* create image */ 969 err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image); 970 assert(!err); 971 972 vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs); 973 assert(!err); 974 975 demo->depth.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 976 demo->depth.mem_alloc.pNext = NULL; 977 demo->depth.mem_alloc.allocationSize = mem_reqs.size; 978 demo->depth.mem_alloc.memoryTypeIndex = 0; 979 980 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, 981 0, /* No requirements */ 982 &demo->depth.mem_alloc.memoryTypeIndex); 983 assert(pass); 984 985 /* allocate memory */ 986 err = vkAllocateMemory(demo->device, &demo->depth.mem_alloc, NULL, 987 &demo->depth.mem); 988 assert(!err); 989 990 /* bind memory */ 991 err = 992 vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0); 993 assert(!err); 994 995 demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, 996 VK_IMAGE_LAYOUT_UNDEFINED, 997 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 998 0); 999 1000 /* create image view */ 1001 view.image = demo->depth.image; 1002 err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view); 1003 assert(!err); 1004} 1005 1006/* Load a ppm file into memory */ 1007bool loadTexture(const char *filename, uint8_t *rgba_data, 1008 VkSubresourceLayout *layout, int32_t *width, int32_t *height) { 1009 FILE *fPtr = fopen(filename, "rb"); 1010 char header[256], *cPtr, *tmp; 1011 1012 if (!fPtr) 1013 return false; 1014 1015 cPtr = fgets(header, 256, fPtr); // P6 1016 if (cPtr == NULL || strncmp(header, "P6\n", 3)) { 1017 fclose(fPtr); 1018 return false; 1019 } 1020 1021 do { 1022 cPtr = fgets(header, 256, fPtr); 1023 if (cPtr == NULL) { 1024 fclose(fPtr); 1025 return false; 1026 } 1027 } while (!strncmp(header, "#", 1)); 1028 1029 sscanf(header, "%u %u", height, width); 1030 if (rgba_data == NULL) { 1031 fclose(fPtr); 1032 return true; 1033 } 1034 tmp = fgets(header, 256, fPtr); // Format 1035 (void)tmp; 1036 if (cPtr == NULL || strncmp(header, "255\n", 3)) { 1037 fclose(fPtr); 1038 return false; 1039 } 1040 1041 for (int y = 0; y < *height; y++) { 1042 uint8_t *rowPtr = rgba_data; 1043 for (int x = 0; x < *width; x++) { 1044 size_t s = fread(rowPtr, 3, 1, fPtr); 1045 (void)s; 1046 rowPtr[3] = 255; /* Alpha of 1 */ 1047 rowPtr += 4; 1048 } 1049 rgba_data += layout->rowPitch; 1050 } 1051 fclose(fPtr); 1052 return true; 1053} 1054 1055static void demo_prepare_texture_image(struct demo *demo, const char *filename, 1056 struct texture_object *tex_obj, 1057 VkImageTiling tiling, 1058 VkImageUsageFlags usage, 1059 VkFlags required_props) { 1060 const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM; 1061 int32_t tex_width; 1062 int32_t tex_height; 1063 VkResult U_ASSERT_ONLY err; 1064 bool U_ASSERT_ONLY pass; 1065 1066 if (!loadTexture(filename, NULL, NULL, &tex_width, &tex_height)) { 1067 printf("Failed to load textures\n"); 1068 fflush(stdout); 1069 exit(1); 1070 } 1071 1072 tex_obj->tex_width = tex_width; 1073 tex_obj->tex_height = tex_height; 1074 1075 const VkImageCreateInfo image_create_info = { 1076 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 1077 .pNext = NULL, 1078 .imageType = VK_IMAGE_TYPE_2D, 1079 .format = tex_format, 1080 .extent = {tex_width, tex_height, 1}, 1081 .mipLevels = 1, 1082 .arrayLayers = 1, 1083 .samples = VK_SAMPLE_COUNT_1_BIT, 1084 .tiling = tiling, 1085 .usage = usage, 1086 .flags = 0, 1087 .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED, 1088 }; 1089 1090 VkMemoryRequirements mem_reqs; 1091 1092 err = 1093 vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image); 1094 assert(!err); 1095 1096 vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs); 1097 1098 tex_obj->mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 1099 tex_obj->mem_alloc.pNext = NULL; 1100 tex_obj->mem_alloc.allocationSize = mem_reqs.size; 1101 tex_obj->mem_alloc.memoryTypeIndex = 0; 1102 1103 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, 1104 required_props, 1105 &tex_obj->mem_alloc.memoryTypeIndex); 1106 assert(pass); 1107 1108 /* allocate memory */ 1109 err = vkAllocateMemory(demo->device, &tex_obj->mem_alloc, NULL, 1110 &(tex_obj->mem)); 1111 assert(!err); 1112 1113 /* bind memory */ 1114 err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0); 1115 assert(!err); 1116 1117 if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { 1118 const VkImageSubresource subres = { 1119 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 1120 .mipLevel = 0, 1121 .arrayLayer = 0, 1122 }; 1123 VkSubresourceLayout layout; 1124 void *data; 1125 1126 vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres, 1127 &layout); 1128 1129 err = vkMapMemory(demo->device, tex_obj->mem, 0, 1130 tex_obj->mem_alloc.allocationSize, 0, &data); 1131 assert(!err); 1132 1133 if (!loadTexture(filename, data, &layout, &tex_width, &tex_height)) { 1134 fprintf(stderr, "Error loading texture: %s\n", filename); 1135 } 1136 1137 vkUnmapMemory(demo->device, tex_obj->mem); 1138 } 1139 1140 tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 1141 demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT, 1142 VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout, 1143 VK_ACCESS_HOST_WRITE_BIT); 1144 /* setting the image layout does not reference the actual memory so no need 1145 * to add a mem ref */ 1146} 1147 1148static void demo_destroy_texture_image(struct demo *demo, 1149 struct texture_object *tex_objs) { 1150 /* clean up staging resources */ 1151 vkFreeMemory(demo->device, tex_objs->mem, NULL); 1152 vkDestroyImage(demo->device, tex_objs->image, NULL); 1153} 1154 1155static void demo_prepare_textures(struct demo *demo) { 1156 const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM; 1157 VkFormatProperties props; 1158 uint32_t i; 1159 1160 vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props); 1161 1162 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { 1163 VkResult U_ASSERT_ONLY err; 1164 1165 if ((props.linearTilingFeatures & 1166 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && 1167 !demo->use_staging_buffer) { 1168 /* Device can texture using linear textures */ 1169 demo_prepare_texture_image(demo, tex_files[i], &demo->textures[i], 1170 VK_IMAGE_TILING_LINEAR, 1171 VK_IMAGE_USAGE_SAMPLED_BIT, 1172 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); 1173 } else if (props.optimalTilingFeatures & 1174 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { 1175 /* Must use staging buffer to copy linear texture to optimized */ 1176 struct texture_object staging_texture; 1177 1178 memset(&staging_texture, 0, sizeof(staging_texture)); 1179 demo_prepare_texture_image(demo, tex_files[i], &staging_texture, 1180 VK_IMAGE_TILING_LINEAR, 1181 VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1182 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); 1183 1184 demo_prepare_texture_image( 1185 demo, tex_files[i], &demo->textures[i], VK_IMAGE_TILING_OPTIMAL, 1186 (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), 1187 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); 1188 1189 demo_set_image_layout(demo, staging_texture.image, 1190 VK_IMAGE_ASPECT_COLOR_BIT, 1191 staging_texture.imageLayout, 1192 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 1193 0); 1194 1195 demo_set_image_layout(demo, demo->textures[i].image, 1196 VK_IMAGE_ASPECT_COLOR_BIT, 1197 demo->textures[i].imageLayout, 1198 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1199 0); 1200 1201 VkImageCopy copy_region = { 1202 .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, 1203 .srcOffset = {0, 0, 0}, 1204 .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, 1205 .dstOffset = {0, 0, 0}, 1206 .extent = {staging_texture.tex_width, 1207 staging_texture.tex_height, 1}, 1208 }; 1209 vkCmdCopyImage( 1210 demo->cmd, staging_texture.image, 1211 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image, 1212 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); 1213 1214 demo_set_image_layout(demo, demo->textures[i].image, 1215 VK_IMAGE_ASPECT_COLOR_BIT, 1216 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1217 demo->textures[i].imageLayout, 1218 0); 1219 1220 demo_flush_init_cmd(demo); 1221 1222 demo_destroy_texture_image(demo, &staging_texture); 1223 } else { 1224 /* Can't support VK_FORMAT_R8G8B8A8_UNORM !? */ 1225 assert(!"No support for R8G8B8A8_UNORM as texture image format"); 1226 } 1227 1228 const VkSamplerCreateInfo sampler = { 1229 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 1230 .pNext = NULL, 1231 .magFilter = VK_FILTER_NEAREST, 1232 .minFilter = VK_FILTER_NEAREST, 1233 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, 1234 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 1235 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 1236 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 1237 .mipLodBias = 0.0f, 1238 .anisotropyEnable = VK_FALSE, 1239 .maxAnisotropy = 1, 1240 .compareOp = VK_COMPARE_OP_NEVER, 1241 .minLod = 0.0f, 1242 .maxLod = 0.0f, 1243 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, 1244 .unnormalizedCoordinates = VK_FALSE, 1245 }; 1246 1247 VkImageViewCreateInfo view = { 1248 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 1249 .pNext = NULL, 1250 .image = VK_NULL_HANDLE, 1251 .viewType = VK_IMAGE_VIEW_TYPE_2D, 1252 .format = tex_format, 1253 .components = 1254 { 1255 VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, 1256 VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A, 1257 }, 1258 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, 1259 .flags = 0, 1260 }; 1261 1262 /* create sampler */ 1263 err = vkCreateSampler(demo->device, &sampler, NULL, 1264 &demo->textures[i].sampler); 1265 assert(!err); 1266 1267 /* create image view */ 1268 view.image = demo->textures[i].image; 1269 err = vkCreateImageView(demo->device, &view, NULL, 1270 &demo->textures[i].view); 1271 assert(!err); 1272 } 1273} 1274 1275void demo_prepare_cube_data_buffer(struct demo *demo) { 1276 VkBufferCreateInfo buf_info; 1277 VkMemoryRequirements mem_reqs; 1278 uint8_t *pData; 1279 int i; 1280 mat4x4 MVP, VP; 1281 VkResult U_ASSERT_ONLY err; 1282 bool U_ASSERT_ONLY pass; 1283 struct vktexcube_vs_uniform data; 1284 1285 mat4x4_mul(VP, demo->projection_matrix, demo->view_matrix); 1286 mat4x4_mul(MVP, VP, demo->model_matrix); 1287 memcpy(data.mvp, MVP, sizeof(MVP)); 1288 // dumpMatrix("MVP", MVP); 1289 1290 for (i = 0; i < 12 * 3; i++) { 1291 data.position[i][0] = g_vertex_buffer_data[i * 3]; 1292 data.position[i][1] = g_vertex_buffer_data[i * 3 + 1]; 1293 data.position[i][2] = g_vertex_buffer_data[i * 3 + 2]; 1294 data.position[i][3] = 1.0f; 1295 data.attr[i][0] = g_uv_buffer_data[2 * i]; 1296 data.attr[i][1] = g_uv_buffer_data[2 * i + 1]; 1297 data.attr[i][2] = 0; 1298 data.attr[i][3] = 0; 1299 } 1300 1301 memset(&buf_info, 0, sizeof(buf_info)); 1302 buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 1303 buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; 1304 buf_info.size = sizeof(data); 1305 err = 1306 vkCreateBuffer(demo->device, &buf_info, NULL, &demo->uniform_data.buf); 1307 assert(!err); 1308 1309 vkGetBufferMemoryRequirements(demo->device, demo->uniform_data.buf, 1310 &mem_reqs); 1311 1312 demo->uniform_data.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 1313 demo->uniform_data.mem_alloc.pNext = NULL; 1314 demo->uniform_data.mem_alloc.allocationSize = mem_reqs.size; 1315 demo->uniform_data.mem_alloc.memoryTypeIndex = 0; 1316 1317 pass = memory_type_from_properties( 1318 demo, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 1319 &demo->uniform_data.mem_alloc.memoryTypeIndex); 1320 assert(pass); 1321 1322 err = vkAllocateMemory(demo->device, &demo->uniform_data.mem_alloc, NULL, 1323 &(demo->uniform_data.mem)); 1324 assert(!err); 1325 1326 err = vkMapMemory(demo->device, demo->uniform_data.mem, 0, 1327 demo->uniform_data.mem_alloc.allocationSize, 0, 1328 (void **)&pData); 1329 assert(!err); 1330 1331 memcpy(pData, &data, sizeof data); 1332 1333 vkUnmapMemory(demo->device, demo->uniform_data.mem); 1334 1335 err = vkBindBufferMemory(demo->device, demo->uniform_data.buf, 1336 demo->uniform_data.mem, 0); 1337 assert(!err); 1338 1339 demo->uniform_data.buffer_info.buffer = demo->uniform_data.buf; 1340 demo->uniform_data.buffer_info.offset = 0; 1341 demo->uniform_data.buffer_info.range = sizeof(data); 1342} 1343 1344static void demo_prepare_descriptor_layout(struct demo *demo) { 1345 const VkDescriptorSetLayoutBinding layout_bindings[2] = { 1346 [0] = 1347 { 1348 .binding = 0, 1349 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1350 .descriptorCount = 1, 1351 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, 1352 .pImmutableSamplers = NULL, 1353 }, 1354 [1] = 1355 { 1356 .binding = 1, 1357 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1358 .descriptorCount = DEMO_TEXTURE_COUNT, 1359 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, 1360 .pImmutableSamplers = NULL, 1361 }, 1362 }; 1363 const VkDescriptorSetLayoutCreateInfo descriptor_layout = { 1364 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 1365 .pNext = NULL, 1366 .bindingCount = 2, 1367 .pBindings = layout_bindings, 1368 }; 1369 VkResult U_ASSERT_ONLY err; 1370 1371 err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL, 1372 &demo->desc_layout); 1373 assert(!err); 1374 1375 const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = { 1376 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 1377 .pNext = NULL, 1378 .setLayoutCount = 1, 1379 .pSetLayouts = &demo->desc_layout, 1380 }; 1381 1382 err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL, 1383 &demo->pipeline_layout); 1384 assert(!err); 1385} 1386 1387static void demo_prepare_render_pass(struct demo *demo) { 1388 const VkAttachmentDescription attachments[2] = { 1389 [0] = 1390 { 1391 .format = demo->format, 1392 .samples = VK_SAMPLE_COUNT_1_BIT, 1393 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, 1394 .storeOp = VK_ATTACHMENT_STORE_OP_STORE, 1395 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, 1396 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 1397 .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1398 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1399 }, 1400 [1] = 1401 { 1402 .format = demo->depth.format, 1403 .samples = VK_SAMPLE_COUNT_1_BIT, 1404 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, 1405 .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 1406 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, 1407 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 1408 .initialLayout = 1409 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1410 .finalLayout = 1411 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1412 }, 1413 }; 1414 const VkAttachmentReference color_reference = { 1415 .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1416 }; 1417 const VkAttachmentReference depth_reference = { 1418 .attachment = 1, 1419 .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1420 }; 1421 const VkSubpassDescription subpass = { 1422 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, 1423 .flags = 0, 1424 .inputAttachmentCount = 0, 1425 .pInputAttachments = NULL, 1426 .colorAttachmentCount = 1, 1427 .pColorAttachments = &color_reference, 1428 .pResolveAttachments = NULL, 1429 .pDepthStencilAttachment = &depth_reference, 1430 .preserveAttachmentCount = 0, 1431 .pPreserveAttachments = NULL, 1432 }; 1433 const VkRenderPassCreateInfo rp_info = { 1434 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, 1435 .pNext = NULL, 1436 .attachmentCount = 2, 1437 .pAttachments = attachments, 1438 .subpassCount = 1, 1439 .pSubpasses = &subpass, 1440 .dependencyCount = 0, 1441 .pDependencies = NULL, 1442 }; 1443 VkResult U_ASSERT_ONLY err; 1444 1445 err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass); 1446 assert(!err); 1447} 1448 1449static VkShaderModule 1450demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) { 1451 VkShaderModule module; 1452 VkShaderModuleCreateInfo moduleCreateInfo; 1453 VkResult U_ASSERT_ONLY err; 1454 1455 moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 1456 moduleCreateInfo.pNext = NULL; 1457 1458 moduleCreateInfo.codeSize = size; 1459 moduleCreateInfo.pCode = code; 1460 moduleCreateInfo.flags = 0; 1461 err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module); 1462 assert(!err); 1463 1464 return module; 1465} 1466 1467char *demo_read_spv(const char *filename, size_t *psize) { 1468 long int size; 1469 size_t U_ASSERT_ONLY retval; 1470 void *shader_code; 1471 1472 FILE *fp = fopen(filename, "rb"); 1473 if (!fp) 1474 return NULL; 1475 1476 fseek(fp, 0L, SEEK_END); 1477 size = ftell(fp); 1478 1479 fseek(fp, 0L, SEEK_SET); 1480 1481 shader_code = malloc(size); 1482 retval = fread(shader_code, size, 1, fp); 1483 assert(retval == 1); 1484 1485 *psize = size; 1486 1487 fclose(fp); 1488 return shader_code; 1489} 1490 1491static VkShaderModule demo_prepare_vs(struct demo *demo) { 1492 void *vertShaderCode; 1493 size_t size; 1494 1495 vertShaderCode = demo_read_spv("cube-vert.spv", &size); 1496 1497 demo->vert_shader_module = 1498 demo_prepare_shader_module(demo, vertShaderCode, size); 1499 1500 free(vertShaderCode); 1501 1502 return demo->vert_shader_module; 1503} 1504 1505static VkShaderModule demo_prepare_fs(struct demo *demo) { 1506 void *fragShaderCode; 1507 size_t size; 1508 1509 fragShaderCode = demo_read_spv("cube-frag.spv", &size); 1510 1511 demo->frag_shader_module = 1512 demo_prepare_shader_module(demo, fragShaderCode, size); 1513 1514 free(fragShaderCode); 1515 1516 return demo->frag_shader_module; 1517} 1518 1519static void demo_prepare_pipeline(struct demo *demo) { 1520 VkGraphicsPipelineCreateInfo pipeline; 1521 VkPipelineCacheCreateInfo pipelineCache; 1522 VkPipelineVertexInputStateCreateInfo vi; 1523 VkPipelineInputAssemblyStateCreateInfo ia; 1524 VkPipelineRasterizationStateCreateInfo rs; 1525 VkPipelineColorBlendStateCreateInfo cb; 1526 VkPipelineDepthStencilStateCreateInfo ds; 1527 VkPipelineViewportStateCreateInfo vp; 1528 VkPipelineMultisampleStateCreateInfo ms; 1529 VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE]; 1530 VkPipelineDynamicStateCreateInfo dynamicState; 1531 VkResult U_ASSERT_ONLY err; 1532 1533 memset(dynamicStateEnables, 0, sizeof dynamicStateEnables); 1534 memset(&dynamicState, 0, sizeof dynamicState); 1535 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 1536 dynamicState.pDynamicStates = dynamicStateEnables; 1537 1538 memset(&pipeline, 0, sizeof(pipeline)); 1539 pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; 1540 pipeline.layout = demo->pipeline_layout; 1541 1542 memset(&vi, 0, sizeof(vi)); 1543 vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; 1544 1545 memset(&ia, 0, sizeof(ia)); 1546 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 1547 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 1548 1549 memset(&rs, 0, sizeof(rs)); 1550 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 1551 rs.polygonMode = VK_POLYGON_MODE_FILL; 1552 rs.cullMode = VK_CULL_MODE_BACK_BIT; 1553 rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 1554 rs.depthClampEnable = VK_FALSE; 1555 rs.rasterizerDiscardEnable = VK_FALSE; 1556 rs.depthBiasEnable = VK_FALSE; 1557 1558 memset(&cb, 0, sizeof(cb)); 1559 cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; 1560 VkPipelineColorBlendAttachmentState att_state[1]; 1561 memset(att_state, 0, sizeof(att_state)); 1562 att_state[0].colorWriteMask = 0xf; 1563 att_state[0].blendEnable = VK_FALSE; 1564 cb.attachmentCount = 1; 1565 cb.pAttachments = att_state; 1566 1567 memset(&vp, 0, sizeof(vp)); 1568 vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 1569 vp.viewportCount = 1; 1570 dynamicStateEnables[dynamicState.dynamicStateCount++] = 1571 VK_DYNAMIC_STATE_VIEWPORT; 1572 vp.scissorCount = 1; 1573 dynamicStateEnables[dynamicState.dynamicStateCount++] = 1574 VK_DYNAMIC_STATE_SCISSOR; 1575 1576 memset(&ds, 0, sizeof(ds)); 1577 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; 1578 ds.depthTestEnable = VK_TRUE; 1579 ds.depthWriteEnable = VK_TRUE; 1580 ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; 1581 ds.depthBoundsTestEnable = VK_FALSE; 1582 ds.back.failOp = VK_STENCIL_OP_KEEP; 1583 ds.back.passOp = VK_STENCIL_OP_KEEP; 1584 ds.back.compareOp = VK_COMPARE_OP_ALWAYS; 1585 ds.stencilTestEnable = VK_FALSE; 1586 ds.front = ds.back; 1587 1588 memset(&ms, 0, sizeof(ms)); 1589 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; 1590 ms.pSampleMask = NULL; 1591 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; 1592 1593 // Two stages: vs and fs 1594 pipeline.stageCount = 2; 1595 VkPipelineShaderStageCreateInfo shaderStages[2]; 1596 memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo)); 1597 1598 shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 1599 shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; 1600 shaderStages[0].module = demo_prepare_vs(demo); 1601 shaderStages[0].pName = "main"; 1602 1603 shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 1604 shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; 1605 shaderStages[1].module = demo_prepare_fs(demo); 1606 shaderStages[1].pName = "main"; 1607 1608 memset(&pipelineCache, 0, sizeof(pipelineCache)); 1609 pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; 1610 1611 err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL, 1612 &demo->pipelineCache); 1613 assert(!err); 1614 1615 pipeline.pVertexInputState = &vi; 1616 pipeline.pInputAssemblyState = &ia; 1617 pipeline.pRasterizationState = &rs; 1618 pipeline.pColorBlendState = &cb; 1619 pipeline.pMultisampleState = &ms; 1620 pipeline.pViewportState = &vp; 1621 pipeline.pDepthStencilState = &ds; 1622 pipeline.pStages = shaderStages; 1623 pipeline.renderPass = demo->render_pass; 1624 pipeline.pDynamicState = &dynamicState; 1625 1626 pipeline.renderPass = demo->render_pass; 1627 1628 err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1, 1629 &pipeline, NULL, &demo->pipeline); 1630 assert(!err); 1631 1632 vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL); 1633 vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL); 1634} 1635 1636static void demo_prepare_descriptor_pool(struct demo *demo) { 1637 const VkDescriptorPoolSize type_counts[2] = { 1638 [0] = 1639 { 1640 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1641 .descriptorCount = 1, 1642 }, 1643 [1] = 1644 { 1645 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1646 .descriptorCount = DEMO_TEXTURE_COUNT, 1647 }, 1648 }; 1649 const VkDescriptorPoolCreateInfo descriptor_pool = { 1650 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 1651 .pNext = NULL, 1652 .maxSets = 1, 1653 .poolSizeCount = 2, 1654 .pPoolSizes = type_counts, 1655 }; 1656 VkResult U_ASSERT_ONLY err; 1657 1658 err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL, 1659 &demo->desc_pool); 1660 assert(!err); 1661} 1662 1663static void demo_prepare_descriptor_set(struct demo *demo) { 1664 VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT]; 1665 VkWriteDescriptorSet writes[2]; 1666 VkResult U_ASSERT_ONLY err; 1667 uint32_t i; 1668 1669 VkDescriptorSetAllocateInfo alloc_info = { 1670 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 1671 .pNext = NULL, 1672 .descriptorPool = demo->desc_pool, 1673 .descriptorSetCount = 1, 1674 .pSetLayouts = &demo->desc_layout}; 1675 err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set); 1676 assert(!err); 1677 1678 memset(&tex_descs, 0, sizeof(tex_descs)); 1679 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { 1680 tex_descs[i].sampler = demo->textures[i].sampler; 1681 tex_descs[i].imageView = demo->textures[i].view; 1682 tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; 1683 } 1684 1685 memset(&writes, 0, sizeof(writes)); 1686 1687 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 1688 writes[0].dstSet = demo->desc_set; 1689 writes[0].descriptorCount = 1; 1690 writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 1691 writes[0].pBufferInfo = &demo->uniform_data.buffer_info; 1692 1693 writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 1694 writes[1].dstSet = demo->desc_set; 1695 writes[1].dstBinding = 1; 1696 writes[1].descriptorCount = DEMO_TEXTURE_COUNT; 1697 writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; 1698 writes[1].pImageInfo = tex_descs; 1699 1700 vkUpdateDescriptorSets(demo->device, 2, writes, 0, NULL); 1701} 1702 1703static void demo_prepare_framebuffers(struct demo *demo) { 1704 VkImageView attachments[2]; 1705 attachments[1] = demo->depth.view; 1706 1707 const VkFramebufferCreateInfo fb_info = { 1708 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 1709 .pNext = NULL, 1710 .renderPass = demo->render_pass, 1711 .attachmentCount = 2, 1712 .pAttachments = attachments, 1713 .width = demo->width, 1714 .height = demo->height, 1715 .layers = 1, 1716 }; 1717 VkResult U_ASSERT_ONLY err; 1718 uint32_t i; 1719 1720 demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount * 1721 sizeof(VkFramebuffer)); 1722 assert(demo->framebuffers); 1723 1724 for (i = 0; i < demo->swapchainImageCount; i++) { 1725 attachments[0] = demo->buffers[i].view; 1726 err = vkCreateFramebuffer(demo->device, &fb_info, NULL, 1727 &demo->framebuffers[i]); 1728 assert(!err); 1729 } 1730} 1731 1732static void demo_prepare(struct demo *demo) { 1733 VkResult U_ASSERT_ONLY err; 1734 1735 const VkCommandPoolCreateInfo cmd_pool_info = { 1736 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 1737 .pNext = NULL, 1738 .queueFamilyIndex = demo->graphics_queue_node_index, 1739 .flags = 0, 1740 }; 1741 err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL, 1742 &demo->cmd_pool); 1743 assert(!err); 1744 1745 const VkCommandBufferAllocateInfo cmd = { 1746 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 1747 .pNext = NULL, 1748 .commandPool = demo->cmd_pool, 1749 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1750 .commandBufferCount = 1, 1751 }; 1752 1753 demo_prepare_buffers(demo); 1754 demo_prepare_depth(demo); 1755 demo_prepare_textures(demo); 1756 demo_prepare_cube_data_buffer(demo); 1757 1758 demo_prepare_descriptor_layout(demo); 1759 demo_prepare_render_pass(demo); 1760 demo_prepare_pipeline(demo); 1761 1762 for (uint32_t i = 0; i < demo->swapchainImageCount; i++) { 1763 err = 1764 vkAllocateCommandBuffers(demo->device, &cmd, &demo->buffers[i].cmd); 1765 assert(!err); 1766 } 1767 1768 demo_prepare_descriptor_pool(demo); 1769 demo_prepare_descriptor_set(demo); 1770 1771 demo_prepare_framebuffers(demo); 1772 1773 for (uint32_t i = 0; i < demo->swapchainImageCount; i++) { 1774 demo->current_buffer = i; 1775 demo_draw_build_cmd(demo, demo->buffers[i].cmd); 1776 } 1777 1778 /* 1779 * Prepare functions above may generate pipeline commands 1780 * that need to be flushed before beginning the render loop. 1781 */ 1782 demo_flush_init_cmd(demo); 1783 1784 demo->current_buffer = 0; 1785 demo->prepared = true; 1786} 1787 1788static void demo_cleanup(struct demo *demo) { 1789 uint32_t i; 1790 1791 demo->prepared = false; 1792 1793 for (i = 0; i < demo->swapchainImageCount; i++) { 1794 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); 1795 } 1796 free(demo->framebuffers); 1797 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL); 1798 1799 vkDestroyPipeline(demo->device, demo->pipeline, NULL); 1800 vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL); 1801 vkDestroyRenderPass(demo->device, demo->render_pass, NULL); 1802 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); 1803 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL); 1804 1805 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { 1806 vkDestroyImageView(demo->device, demo->textures[i].view, NULL); 1807 vkDestroyImage(demo->device, demo->textures[i].image, NULL); 1808 vkFreeMemory(demo->device, demo->textures[i].mem, NULL); 1809 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL); 1810 } 1811 demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL); 1812 1813 vkDestroyImageView(demo->device, demo->depth.view, NULL); 1814 vkDestroyImage(demo->device, demo->depth.image, NULL); 1815 vkFreeMemory(demo->device, demo->depth.mem, NULL); 1816 1817 vkDestroyBuffer(demo->device, demo->uniform_data.buf, NULL); 1818 vkFreeMemory(demo->device, demo->uniform_data.mem, NULL); 1819 1820 for (i = 0; i < demo->swapchainImageCount; i++) { 1821 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL); 1822 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, 1823 &demo->buffers[i].cmd); 1824 } 1825 free(demo->buffers); 1826 1827 free(demo->queue_props); 1828 1829 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); 1830 vkDestroyDevice(demo->device, NULL); 1831 if (demo->validate) { 1832 demo->DestroyDebugReportCallback(demo->inst, demo->msg_callback, NULL); 1833 } 1834 vkDestroySurfaceKHR(demo->inst, demo->surface, NULL); 1835 vkDestroyInstance(demo->inst, NULL); 1836 1837#ifndef _WIN32 1838 xcb_destroy_window(demo->connection, demo->window); 1839 xcb_disconnect(demo->connection); 1840 free(demo->atom_wm_delete_window); 1841#endif // _WIN32 1842} 1843 1844static void demo_resize(struct demo *demo) { 1845 uint32_t i; 1846 1847 // Don't react to resize until after first initialization. 1848 if (!demo->prepared) { 1849 return; 1850 } 1851 // In order to properly resize the window, we must re-create the swapchain 1852 // AND redo the command buffers, etc. 1853 // 1854 // First, perform part of the demo_cleanup() function: 1855 demo->prepared = false; 1856 1857 for (i = 0; i < demo->swapchainImageCount; i++) { 1858 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); 1859 } 1860 free(demo->framebuffers); 1861 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL); 1862 1863 vkDestroyPipeline(demo->device, demo->pipeline, NULL); 1864 vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL); 1865 vkDestroyRenderPass(demo->device, demo->render_pass, NULL); 1866 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); 1867 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL); 1868 1869 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { 1870 vkDestroyImageView(demo->device, demo->textures[i].view, NULL); 1871 vkDestroyImage(demo->device, demo->textures[i].image, NULL); 1872 vkFreeMemory(demo->device, demo->textures[i].mem, NULL); 1873 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL); 1874 } 1875 1876 vkDestroyImageView(demo->device, demo->depth.view, NULL); 1877 vkDestroyImage(demo->device, demo->depth.image, NULL); 1878 vkFreeMemory(demo->device, demo->depth.mem, NULL); 1879 1880 vkDestroyBuffer(demo->device, demo->uniform_data.buf, NULL); 1881 vkFreeMemory(demo->device, demo->uniform_data.mem, NULL); 1882 1883 for (i = 0; i < demo->swapchainImageCount; i++) { 1884 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL); 1885 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, 1886 &demo->buffers[i].cmd); 1887 } 1888 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); 1889 free(demo->buffers); 1890 1891 // Second, re-perform the demo_prepare() function, which will re-create the 1892 // swapchain: 1893 demo_prepare(demo); 1894} 1895 1896// On MS-Windows, make this a global, so it's available to WndProc() 1897struct demo demo; 1898 1899#ifdef _WIN32 1900static void demo_run(struct demo *demo) { 1901 if (!demo->prepared) 1902 return; 1903 // Wait for work to finish before updating MVP. 1904 vkDeviceWaitIdle(demo->device); 1905 demo_update_data_buffer(demo); 1906 1907 demo_draw(demo); 1908 1909 // Wait for work to finish before updating MVP. 1910 vkDeviceWaitIdle(demo->device); 1911 1912 demo->curFrame++; 1913 1914 if (demo->frameCount != INT_MAX && demo->curFrame == demo->frameCount) { 1915 demo->quit = true; 1916 demo_cleanup(demo); 1917 ExitProcess(0); 1918 } 1919} 1920 1921// MS-Windows event handling function: 1922LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 1923 switch (uMsg) { 1924 case WM_CLOSE: 1925 PostQuitMessage(validation_error); 1926 break; 1927 case WM_PAINT: 1928 demo_run(&demo); 1929 break; 1930 case WM_SIZE: 1931 // Resize the application to the new window size, except when 1932 // it was minimized. Vulkan doesn't support images or swapchains 1933 // with width=0 and height=0. 1934 if (wParam != SIZE_MINIMIZED) { 1935 demo.width = lParam & 0xffff; 1936 demo.height = lParam & 0xffff0000 >> 16; 1937 demo_resize(&demo); 1938 } 1939 break; 1940 default: 1941 break; 1942 } 1943 return (DefWindowProc(hWnd, uMsg, wParam, lParam)); 1944} 1945 1946static void demo_create_window(struct demo *demo) { 1947 WNDCLASSEX win_class; 1948 1949 // Initialize the window class structure: 1950 win_class.cbSize = sizeof(WNDCLASSEX); 1951 win_class.style = CS_HREDRAW | CS_VREDRAW; 1952 win_class.lpfnWndProc = WndProc; 1953 win_class.cbClsExtra = 0; 1954 win_class.cbWndExtra = 0; 1955 win_class.hInstance = demo->connection; // hInstance 1956 win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); 1957 win_class.hCursor = LoadCursor(NULL, IDC_ARROW); 1958 win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 1959 win_class.lpszMenuName = NULL; 1960 win_class.lpszClassName = demo->name; 1961 win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO); 1962 // Register window class: 1963 if (!RegisterClassEx(&win_class)) { 1964 // It didn't work, so try to give a useful error: 1965 printf("Unexpected error trying to start the application!\n"); 1966 fflush(stdout); 1967 exit(1); 1968 } 1969 // Create window with the registered class: 1970 RECT wr = {0, 0, demo->width, demo->height}; 1971 AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); 1972 demo->window = CreateWindowEx(0, 1973 demo->name, // class name 1974 demo->name, // app name 1975 WS_OVERLAPPEDWINDOW | // window style 1976 WS_VISIBLE | WS_SYSMENU, 1977 100, 100, // x/y coords 1978 wr.right - wr.left, // width 1979 wr.bottom - wr.top, // height 1980 NULL, // handle to parent 1981 NULL, // handle to menu 1982 demo->connection, // hInstance 1983 NULL); // no extra parameters 1984 if (!demo->window) { 1985 // It didn't work, so try to give a useful error: 1986 printf("Cannot create a window in which to draw!\n"); 1987 fflush(stdout); 1988 exit(1); 1989 } 1990} 1991#else // _WIN32 1992static void demo_handle_event(struct demo *demo, 1993 const xcb_generic_event_t *event) { 1994 uint8_t event_code = event->response_type & 0x7f; 1995 switch (event_code) { 1996 case XCB_EXPOSE: 1997 // TODO: Resize window 1998 break; 1999 case XCB_CLIENT_MESSAGE: 2000 if ((*(xcb_client_message_event_t *)event).data.data32[0] == 2001 (*demo->atom_wm_delete_window).atom) { 2002 demo->quit = true; 2003 } 2004 break; 2005 case XCB_KEY_RELEASE: { 2006 const xcb_key_release_event_t *key = 2007 (const xcb_key_release_event_t *)event; 2008 2009 switch (key->detail) { 2010 case 0x9: // Escape 2011 demo->quit = true; 2012 break; 2013 case 0x71: // left arrow key 2014 demo->spin_angle += demo->spin_increment; 2015 break; 2016 case 0x72: // right arrow key 2017 demo->spin_angle -= demo->spin_increment; 2018 break; 2019 case 0x41: 2020 demo->pause = !demo->pause; 2021 break; 2022 } 2023 } break; 2024 case XCB_CONFIGURE_NOTIFY: { 2025 const xcb_configure_notify_event_t *cfg = 2026 (const xcb_configure_notify_event_t *)event; 2027 if ((demo->width != cfg->width) || (demo->height != cfg->height)) { 2028 demo->width = cfg->width; 2029 demo->height = cfg->height; 2030 demo_resize(demo); 2031 } 2032 } break; 2033 default: 2034 break; 2035 } 2036} 2037 2038static void demo_run(struct demo *demo) { 2039 xcb_flush(demo->connection); 2040 2041 while (!demo->quit) { 2042 xcb_generic_event_t *event; 2043 2044 if (demo->pause) { 2045 event = xcb_wait_for_event(demo->connection); 2046 } else { 2047 event = xcb_poll_for_event(demo->connection); 2048 } 2049 if (event) { 2050 demo_handle_event(demo, event); 2051 free(event); 2052 } 2053 2054 // Wait for work to finish before updating MVP. 2055 vkDeviceWaitIdle(demo->device); 2056 demo_update_data_buffer(demo); 2057 2058 demo_draw(demo); 2059 2060 // Wait for work to finish before updating MVP. 2061 vkDeviceWaitIdle(demo->device); 2062 demo->curFrame++; 2063 if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) 2064 demo->quit = true; 2065 } 2066} 2067 2068static void demo_create_window(struct demo *demo) { 2069 uint32_t value_mask, value_list[32]; 2070 2071 demo->window = xcb_generate_id(demo->connection); 2072 2073 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; 2074 value_list[0] = demo->screen->black_pixel; 2075 value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | 2076 XCB_EVENT_MASK_STRUCTURE_NOTIFY; 2077 2078 xcb_create_window(demo->connection, XCB_COPY_FROM_PARENT, demo->window, 2079 demo->screen->root, 0, 0, demo->width, demo->height, 0, 2080 XCB_WINDOW_CLASS_INPUT_OUTPUT, demo->screen->root_visual, 2081 value_mask, value_list); 2082 2083 /* Magic code that will send notification when window is destroyed */ 2084 xcb_intern_atom_cookie_t cookie = 2085 xcb_intern_atom(demo->connection, 1, 12, "WM_PROTOCOLS"); 2086 xcb_intern_atom_reply_t *reply = 2087 xcb_intern_atom_reply(demo->connection, cookie, 0); 2088 2089 xcb_intern_atom_cookie_t cookie2 = 2090 xcb_intern_atom(demo->connection, 0, 16, "WM_DELETE_WINDOW"); 2091 demo->atom_wm_delete_window = 2092 xcb_intern_atom_reply(demo->connection, cookie2, 0); 2093 2094 xcb_change_property(demo->connection, XCB_PROP_MODE_REPLACE, demo->window, 2095 (*reply).atom, 4, 32, 1, 2096 &(*demo->atom_wm_delete_window).atom); 2097 free(reply); 2098 2099 xcb_map_window(demo->connection, demo->window); 2100 2101 // Force the x/y coordinates to 100,100 results are identical in consecutive 2102 // runs 2103 const uint32_t coords[] = {100, 100}; 2104 xcb_configure_window(demo->connection, demo->window, 2105 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords); 2106} 2107#endif // _WIN32 2108 2109/* 2110 * Return 1 (true) if all layer names specified in check_names 2111 * can be found in given layer properties. 2112 */ 2113static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, 2114 uint32_t layer_count, 2115 VkLayerProperties *layers) { 2116 for (uint32_t i = 0; i < check_count; i++) { 2117 VkBool32 found = 0; 2118 for (uint32_t j = 0; j < layer_count; j++) { 2119 if (!strcmp(check_names[i], layers[j].layerName)) { 2120 found = 1; 2121 break; 2122 } 2123 } 2124 if (!found) { 2125 fprintf(stderr, "Cannot find layer: %s\n", check_names[i]); 2126 return 0; 2127 } 2128 } 2129 return 1; 2130} 2131 2132static void demo_init_vk(struct demo *demo) { 2133 VkResult err; 2134 char *extension_names[64]; 2135 uint32_t instance_extension_count = 0; 2136 uint32_t instance_layer_count = 0; 2137 uint32_t device_validation_layer_count = 0; 2138 uint32_t enabled_extension_count = 0; 2139 uint32_t enabled_layer_count = 0; 2140 2141 char *instance_validation_layers[] = { 2142 "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", 2143 "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_object_tracker", 2144 "VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation", 2145 "VK_LAYER_LUNARG_swapchain", 2146 "VK_LAYER_GOOGLE_unique_objects" 2147 }; 2148 2149 demo->device_validation_layers[0] = "VK_LAYER_GOOGLE_threading"; 2150 demo->device_validation_layers[1] = "VK_LAYER_LUNARG_parameter_validation"; 2151 demo->device_validation_layers[2] = "VK_LAYER_LUNARG_device_limits"; 2152 demo->device_validation_layers[3] = "VK_LAYER_LUNARG_object_tracker"; 2153 demo->device_validation_layers[4] = "VK_LAYER_LUNARG_image"; 2154 demo->device_validation_layers[5] = "VK_LAYER_LUNARG_core_validation"; 2155 demo->device_validation_layers[6] = "VK_LAYER_LUNARG_swapchain"; 2156 demo->device_validation_layers[7] = "VK_LAYER_GOOGLE_unique_objects"; 2157 device_validation_layer_count = 8; 2158 2159 /* Look for validation layers */ 2160 VkBool32 validation_found = 0; 2161 err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); 2162 assert(!err); 2163 2164 if (instance_layer_count > 0) { 2165 VkLayerProperties *instance_layers = 2166 malloc(sizeof(VkLayerProperties) * instance_layer_count); 2167 err = vkEnumerateInstanceLayerProperties(&instance_layer_count, 2168 instance_layers); 2169 assert(!err); 2170 2171 if (demo->validate) { 2172 validation_found = demo_check_layers( 2173 ARRAY_SIZE(instance_validation_layers), 2174 instance_validation_layers, instance_layer_count, 2175 instance_layers); 2176 2177 enabled_layer_count = ARRAY_SIZE(instance_validation_layers); 2178 } 2179 2180 free(instance_layers); 2181 } 2182 2183 if (demo->validate && !validation_found) { 2184 ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find" 2185 "required validation layer.\n\n" 2186 "Please look at the Getting Started guide for additional " 2187 "information.\n", 2188 "vkCreateInstance Failure"); 2189 } 2190 2191 /* Look for instance extensions */ 2192 VkBool32 surfaceExtFound = 0; 2193 VkBool32 platformSurfaceExtFound = 0; 2194 memset(extension_names, 0, sizeof(extension_names)); 2195 2196 err = vkEnumerateInstanceExtensionProperties( 2197 NULL, &instance_extension_count, NULL); 2198 assert(!err); 2199 2200 if (instance_extension_count > 0) { 2201 VkExtensionProperties *instance_extensions = 2202 malloc(sizeof(VkExtensionProperties) * instance_extension_count); 2203 err = vkEnumerateInstanceExtensionProperties( 2204 NULL, &instance_extension_count, instance_extensions); 2205 assert(!err); 2206 for (uint32_t i = 0; i < instance_extension_count; i++) { 2207 if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, 2208 instance_extensions[i].extensionName)) { 2209 surfaceExtFound = 1; 2210 extension_names[enabled_extension_count++] = 2211 VK_KHR_SURFACE_EXTENSION_NAME; 2212 } 2213#ifdef _WIN32 2214 if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, 2215 instance_extensions[i].extensionName)) { 2216 platformSurfaceExtFound = 1; 2217 extension_names[enabled_extension_count++] = 2218 VK_KHR_WIN32_SURFACE_EXTENSION_NAME; 2219 } 2220#else // _WIN32 2221 if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, 2222 instance_extensions[i].extensionName)) { 2223 platformSurfaceExtFound = 1; 2224 extension_names[enabled_extension_count++] = 2225 VK_KHR_XCB_SURFACE_EXTENSION_NAME; 2226 } 2227#endif // _WIN32 2228 if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, 2229 instance_extensions[i].extensionName)) { 2230 if (demo->validate) { 2231 extension_names[enabled_extension_count++] = 2232 VK_EXT_DEBUG_REPORT_EXTENSION_NAME; 2233 } 2234 } 2235 assert(enabled_extension_count < 64); 2236 } 2237 2238 free(instance_extensions); 2239 } 2240 2241 if (!surfaceExtFound) { 2242 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 2243 "the " VK_KHR_SURFACE_EXTENSION_NAME 2244 " extension.\n\nDo you have a compatible " 2245 "Vulkan installable client driver (ICD) installed?\nPlease " 2246 "look at the Getting Started guide for additional " 2247 "information.\n", 2248 "vkCreateInstance Failure"); 2249 } 2250 if (!platformSurfaceExtFound) { 2251#ifdef _WIN32 2252 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 2253 "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME 2254 " extension.\n\nDo you have a compatible " 2255 "Vulkan installable client driver (ICD) installed?\nPlease " 2256 "look at the Getting Started guide for additional " 2257 "information.\n", 2258 "vkCreateInstance Failure"); 2259#else // _WIN32 2260 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 2261 "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME 2262 " extension.\n\nDo you have a compatible " 2263 "Vulkan installable client driver (ICD) installed?\nPlease " 2264 "look at the Getting Started guide for additional " 2265 "information.\n", 2266 "vkCreateInstance Failure"); 2267#endif // _WIN32 2268 } 2269 const VkApplicationInfo app = { 2270 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 2271 .pNext = NULL, 2272 .pApplicationName = APP_SHORT_NAME, 2273 .applicationVersion = 0, 2274 .pEngineName = APP_SHORT_NAME, 2275 .engineVersion = 0, 2276 .apiVersion = VK_API_VERSION_1_0, 2277 }; 2278 VkInstanceCreateInfo inst_info = { 2279 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 2280 .pNext = NULL, 2281 .pApplicationInfo = &app, 2282 .enabledLayerCount = enabled_layer_count, 2283 .ppEnabledLayerNames = 2284 (const char *const *)((demo->validate) ? instance_validation_layers 2285 : NULL), 2286 .enabledExtensionCount = enabled_extension_count, 2287 .ppEnabledExtensionNames = (const char *const *)extension_names, 2288 }; 2289 2290 uint32_t gpu_count; 2291 2292 err = vkCreateInstance(&inst_info, NULL, &demo->inst); 2293 if (err == VK_ERROR_INCOMPATIBLE_DRIVER) { 2294 ERR_EXIT("Cannot find a compatible Vulkan installable client driver " 2295 "(ICD).\n\nPlease look at the Getting Started guide for " 2296 "additional information.\n", 2297 "vkCreateInstance Failure"); 2298 } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) { 2299 ERR_EXIT("Cannot find a specified extension library" 2300 ".\nMake sure your layers path is set appropriately.\n", 2301 "vkCreateInstance Failure"); 2302 } else if (err) { 2303 ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan " 2304 "installable client driver (ICD) installed?\nPlease look at " 2305 "the Getting Started guide for additional information.\n", 2306 "vkCreateInstance Failure"); 2307 } 2308 2309 /* Make initial call to query gpu_count, then second call for gpu info*/ 2310 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL); 2311 assert(!err && gpu_count > 0); 2312 2313 if (gpu_count > 0) { 2314 VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count); 2315 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, physical_devices); 2316 assert(!err); 2317 /* For cube demo we just grab the first physical device */ 2318 demo->gpu = physical_devices[0]; 2319 free(physical_devices); 2320 } else { 2321 ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices.\n\n" 2322 "Do you have a compatible Vulkan installable client driver (ICD) " 2323 "installed?\nPlease look at the Getting Started guide for " 2324 "additional information.\n", 2325 "vkEnumeratePhysicalDevices Failure"); 2326 } 2327 2328 /* Look for validation layers */ 2329 validation_found = 0; 2330 demo->enabled_layer_count = 0; 2331 uint32_t device_layer_count = 0; 2332 err = 2333 vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL); 2334 assert(!err); 2335 2336 if (device_layer_count > 0) { 2337 VkLayerProperties *device_layers = 2338 malloc(sizeof(VkLayerProperties) * device_layer_count); 2339 err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, 2340 device_layers); 2341 assert(!err); 2342 2343 if (demo->validate) { 2344 validation_found = demo_check_layers(device_validation_layer_count, 2345 demo->device_validation_layers, 2346 device_layer_count, 2347 device_layers); 2348 demo->enabled_layer_count = device_validation_layer_count; 2349 } 2350 2351 free(device_layers); 2352 } 2353 2354 if (demo->validate && !validation_found) { 2355 ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find" 2356 "a required validation layer.\n\n" 2357 "Please look at the Getting Started guide for additional " 2358 "information.\n", 2359 "vkCreateDevice Failure"); 2360 } 2361 2362 /* Look for device extensions */ 2363 uint32_t device_extension_count = 0; 2364 VkBool32 swapchainExtFound = 0; 2365 demo->enabled_extension_count = 0; 2366 memset(extension_names, 0, sizeof(extension_names)); 2367 2368 err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, 2369 &device_extension_count, NULL); 2370 assert(!err); 2371 2372 if (device_extension_count > 0) { 2373 VkExtensionProperties *device_extensions = 2374 malloc(sizeof(VkExtensionProperties) * device_extension_count); 2375 err = vkEnumerateDeviceExtensionProperties( 2376 demo->gpu, NULL, &device_extension_count, device_extensions); 2377 assert(!err); 2378 2379 for (uint32_t i = 0; i < device_extension_count; i++) { 2380 if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 2381 device_extensions[i].extensionName)) { 2382 swapchainExtFound = 1; 2383 demo->extension_names[demo->enabled_extension_count++] = 2384 VK_KHR_SWAPCHAIN_EXTENSION_NAME; 2385 } 2386 assert(demo->enabled_extension_count < 64); 2387 } 2388 2389 free(device_extensions); 2390 } 2391 2392 if (!swapchainExtFound) { 2393 ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find " 2394 "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME 2395 " extension.\n\nDo you have a compatible " 2396 "Vulkan installable client driver (ICD) installed?\nPlease " 2397 "look at the Getting Started guide for additional " 2398 "information.\n", 2399 "vkCreateInstance Failure"); 2400 } 2401 2402 if (demo->validate) { 2403 demo->CreateDebugReportCallback = 2404 (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( 2405 demo->inst, "vkCreateDebugReportCallbackEXT"); 2406 demo->DestroyDebugReportCallback = 2407 (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr( 2408 demo->inst, "vkDestroyDebugReportCallbackEXT"); 2409 if (!demo->CreateDebugReportCallback) { 2410 ERR_EXIT( 2411 "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n", 2412 "vkGetProcAddr Failure"); 2413 } 2414 if (!demo->DestroyDebugReportCallback) { 2415 ERR_EXIT( 2416 "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n", 2417 "vkGetProcAddr Failure"); 2418 } 2419 demo->DebugReportMessage = 2420 (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr( 2421 demo->inst, "vkDebugReportMessageEXT"); 2422 if (!demo->DebugReportMessage) { 2423 ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n", 2424 "vkGetProcAddr Failure"); 2425 } 2426 2427 PFN_vkDebugReportCallbackEXT callback; 2428 2429 if (!demo->use_break) { 2430 callback = dbgFunc; 2431 } else { 2432 callback = dbgFunc; 2433 // TODO add a break callback defined locally since there is no 2434 // longer 2435 // one included in the loader 2436 } 2437 VkDebugReportCallbackCreateInfoEXT dbgCreateInfo; 2438 dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; 2439 dbgCreateInfo.pNext = NULL; 2440 dbgCreateInfo.pfnCallback = callback; 2441 dbgCreateInfo.pUserData = NULL; 2442 dbgCreateInfo.flags = 2443 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 2444 err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL, 2445 &demo->msg_callback); 2446 switch (err) { 2447 case VK_SUCCESS: 2448 break; 2449 case VK_ERROR_OUT_OF_HOST_MEMORY: 2450 ERR_EXIT("CreateDebugReportCallback: out of host memory\n", 2451 "CreateDebugReportCallback Failure"); 2452 break; 2453 default: 2454 ERR_EXIT("CreateDebugReportCallback: unknown failure\n", 2455 "CreateDebugReportCallback Failure"); 2456 break; 2457 } 2458 } 2459 vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props); 2460 2461 /* Call with NULL data to get count */ 2462 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count, 2463 NULL); 2464 assert(demo->queue_count >= 1); 2465 2466 demo->queue_props = (VkQueueFamilyProperties *)malloc( 2467 demo->queue_count * sizeof(VkQueueFamilyProperties)); 2468 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count, 2469 demo->queue_props); 2470 // Find a queue that supports gfx 2471 uint32_t gfx_queue_idx = 0; 2472 for (gfx_queue_idx = 0; gfx_queue_idx < demo->queue_count; 2473 gfx_queue_idx++) { 2474 if (demo->queue_props[gfx_queue_idx].queueFlags & VK_QUEUE_GRAPHICS_BIT) 2475 break; 2476 } 2477 assert(gfx_queue_idx < demo->queue_count); 2478 // Query fine-grained feature support for this device. 2479 // If app has specific feature requirements it should check supported 2480 // features based on this query 2481 VkPhysicalDeviceFeatures physDevFeatures; 2482 vkGetPhysicalDeviceFeatures(demo->gpu, &physDevFeatures); 2483 2484 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR); 2485 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR); 2486 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR); 2487 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR); 2488 GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR); 2489} 2490 2491static void demo_create_device(struct demo *demo) { 2492 VkResult U_ASSERT_ONLY err; 2493 float queue_priorities[1] = {0.0}; 2494 const VkDeviceQueueCreateInfo queue = { 2495 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 2496 .pNext = NULL, 2497 .queueFamilyIndex = demo->graphics_queue_node_index, 2498 .queueCount = 1, 2499 .pQueuePriorities = queue_priorities}; 2500 2501 VkDeviceCreateInfo device = { 2502 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 2503 .pNext = NULL, 2504 .queueCreateInfoCount = 1, 2505 .pQueueCreateInfos = &queue, 2506 .enabledLayerCount = demo->enabled_layer_count, 2507 .ppEnabledLayerNames = 2508 (const char *const *)((demo->validate) 2509 ? demo->device_validation_layers 2510 : NULL), 2511 .enabledExtensionCount = demo->enabled_extension_count, 2512 .ppEnabledExtensionNames = (const char *const *)demo->extension_names, 2513 .pEnabledFeatures = 2514 NULL, // If specific features are required, pass them in here 2515 }; 2516 2517 err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device); 2518 assert(!err); 2519} 2520 2521static void demo_init_vk_swapchain(struct demo *demo) { 2522 VkResult U_ASSERT_ONLY err; 2523 uint32_t i; 2524 2525// Create a WSI surface for the window: 2526#ifdef _WIN32 2527 VkWin32SurfaceCreateInfoKHR createInfo; 2528 createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 2529 createInfo.pNext = NULL; 2530 createInfo.flags = 0; 2531 createInfo.hinstance = demo->connection; 2532 createInfo.hwnd = demo->window; 2533 2534 err = 2535 vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface); 2536 2537#else // _WIN32 2538 VkXcbSurfaceCreateInfoKHR createInfo; 2539 createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; 2540 createInfo.pNext = NULL; 2541 createInfo.flags = 0; 2542 createInfo.connection = demo->connection; 2543 createInfo.window = demo->window; 2544 2545 err = vkCreateXcbSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface); 2546#endif // _WIN32 2547 2548 // Iterate over each queue to learn whether it supports presenting: 2549 VkBool32 *supportsPresent = 2550 (VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32)); 2551 for (i = 0; i < demo->queue_count; i++) { 2552 demo->fpGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface, 2553 &supportsPresent[i]); 2554 } 2555 2556 // Search for a graphics and a present queue in the array of queue 2557 // families, try to find one that supports both 2558 uint32_t graphicsQueueNodeIndex = UINT32_MAX; 2559 uint32_t presentQueueNodeIndex = UINT32_MAX; 2560 for (i = 0; i < demo->queue_count; i++) { 2561 if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { 2562 if (graphicsQueueNodeIndex == UINT32_MAX) { 2563 graphicsQueueNodeIndex = i; 2564 } 2565 2566 if (supportsPresent[i] == VK_TRUE) { 2567 graphicsQueueNodeIndex = i; 2568 presentQueueNodeIndex = i; 2569 break; 2570 } 2571 } 2572 } 2573 if (presentQueueNodeIndex == UINT32_MAX) { 2574 // If didn't find a queue that supports both graphics and present, then 2575 // find a separate present queue. 2576 for (uint32_t i = 0; i < demo->queue_count; ++i) { 2577 if (supportsPresent[i] == VK_TRUE) { 2578 presentQueueNodeIndex = i; 2579 break; 2580 } 2581 } 2582 } 2583 free(supportsPresent); 2584 2585 // Generate error if could not find both a graphics and a present queue 2586 if (graphicsQueueNodeIndex == UINT32_MAX || 2587 presentQueueNodeIndex == UINT32_MAX) { 2588 ERR_EXIT("Could not find a graphics and a present queue\n", 2589 "Swapchain Initialization Failure"); 2590 } 2591 2592 // TODO: Add support for separate queues, including presentation, 2593 // synchronization, and appropriate tracking for QueueSubmit. 2594 // NOTE: While it is possible for an application to use a separate graphics 2595 // and a present queues, this demo program assumes it is only using 2596 // one: 2597 if (graphicsQueueNodeIndex != presentQueueNodeIndex) { 2598 ERR_EXIT("Could not find a common graphics and a present queue\n", 2599 "Swapchain Initialization Failure"); 2600 } 2601 2602 demo->graphics_queue_node_index = graphicsQueueNodeIndex; 2603 2604 demo_create_device(demo); 2605 2606 GET_DEVICE_PROC_ADDR(demo->device, CreateSwapchainKHR); 2607 GET_DEVICE_PROC_ADDR(demo->device, DestroySwapchainKHR); 2608 GET_DEVICE_PROC_ADDR(demo->device, GetSwapchainImagesKHR); 2609 GET_DEVICE_PROC_ADDR(demo->device, AcquireNextImageKHR); 2610 GET_DEVICE_PROC_ADDR(demo->device, QueuePresentKHR); 2611 2612 vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0, 2613 &demo->queue); 2614 2615 // Get the list of VkFormat's that are supported: 2616 uint32_t formatCount; 2617 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, 2618 &formatCount, NULL); 2619 assert(!err); 2620 VkSurfaceFormatKHR *surfFormats = 2621 (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); 2622 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, 2623 &formatCount, surfFormats); 2624 assert(!err); 2625 // If the format list includes just one entry of VK_FORMAT_UNDEFINED, 2626 // the surface has no preferred format. Otherwise, at least one 2627 // supported format will be returned. 2628 if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { 2629 demo->format = VK_FORMAT_B8G8R8A8_UNORM; 2630 } else { 2631 assert(formatCount >= 1); 2632 demo->format = surfFormats[0].format; 2633 } 2634 demo->color_space = surfFormats[0].colorSpace; 2635 2636 demo->quit = false; 2637 demo->curFrame = 0; 2638 2639 // Get Memory information and properties 2640 vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties); 2641} 2642 2643static void demo_init_connection(struct demo *demo) { 2644#ifndef _WIN32 2645 const xcb_setup_t *setup; 2646 xcb_screen_iterator_t iter; 2647 int scr; 2648 2649 demo->connection = xcb_connect(NULL, &scr); 2650 if (demo->connection == NULL) { 2651 printf("Cannot find a compatible Vulkan installable client driver " 2652 "(ICD).\nExiting ...\n"); 2653 fflush(stdout); 2654 exit(1); 2655 } 2656 2657 setup = xcb_get_setup(demo->connection); 2658 iter = xcb_setup_roots_iterator(setup); 2659 while (scr-- > 0) 2660 xcb_screen_next(&iter); 2661 2662 demo->screen = iter.data; 2663#endif // _WIN32 2664} 2665 2666static void demo_init(struct demo *demo, int argc, char **argv) { 2667 vec3 eye = {0.0f, 3.0f, 5.0f}; 2668 vec3 origin = {0, 0, 0}; 2669 vec3 up = {0.0f, 1.0f, 0.0}; 2670 2671 memset(demo, 0, sizeof(*demo)); 2672 demo->frameCount = INT32_MAX; 2673 2674 for (int i = 1; i < argc; i++) { 2675 if (strcmp(argv[i], "--use_staging") == 0) { 2676 demo->use_staging_buffer = true; 2677 continue; 2678 } 2679 if (strcmp(argv[i], "--break") == 0) { 2680 demo->use_break = true; 2681 continue; 2682 } 2683 if (strcmp(argv[i], "--validate") == 0) { 2684 demo->validate = true; 2685 continue; 2686 } 2687 if (strcmp(argv[i], "--c") == 0 && demo->frameCount == INT32_MAX && 2688 i < argc - 1 && sscanf(argv[i + 1], "%d", &demo->frameCount) == 1 && 2689 demo->frameCount >= 0) { 2690 i++; 2691 continue; 2692 } 2693 2694 fprintf(stderr, "Usage:\n %s [--use_staging] [--validate] [--break] " 2695 "[--c <framecount>]\n", 2696 APP_SHORT_NAME); 2697 fflush(stderr); 2698 exit(1); 2699 } 2700 2701 demo_init_connection(demo); 2702 demo_init_vk(demo); 2703 2704 demo->width = 500; 2705 demo->height = 500; 2706 2707 demo->spin_angle = 0.01f; 2708 demo->spin_increment = 0.01f; 2709 demo->pause = false; 2710 2711 mat4x4_perspective(demo->projection_matrix, (float)degreesToRadians(45.0f), 2712 1.0f, 0.1f, 100.0f); 2713 mat4x4_look_at(demo->view_matrix, eye, origin, up); 2714 mat4x4_identity(demo->model_matrix); 2715} 2716 2717#ifdef _WIN32 2718// Include header required for parsing the command line options. 2719#include <shellapi.h> 2720 2721int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, 2722 int nCmdShow) { 2723 MSG msg; // message 2724 bool done; // flag saying when app is complete 2725 int argc; 2726 char **argv; 2727 2728 // Use the CommandLine functions to get the command line arguments. 2729 // Unfortunately, Microsoft outputs 2730 // this information as wide characters for Unicode, and we simply want the 2731 // Ascii version to be compatible 2732 // with the non-Windows side. So, we have to convert the information to 2733 // Ascii character strings. 2734 LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc); 2735 if (NULL == commandLineArgs) { 2736 argc = 0; 2737 } 2738 2739 if (argc > 0) { 2740 argv = (char **)malloc(sizeof(char *) * argc); 2741 if (argv == NULL) { 2742 argc = 0; 2743 } else { 2744 for (int iii = 0; iii < argc; iii++) { 2745 size_t wideCharLen = wcslen(commandLineArgs[iii]); 2746 size_t numConverted = 0; 2747 2748 argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1)); 2749 if (argv[iii] != NULL) { 2750 wcstombs_s(&numConverted, argv[iii], wideCharLen + 1, 2751 commandLineArgs[iii], wideCharLen + 1); 2752 } 2753 } 2754 } 2755 } else { 2756 argv = NULL; 2757 } 2758 2759 demo_init(&demo, argc, argv); 2760 2761 // Free up the items we had to allocate for the command line arguments. 2762 if (argc > 0 && argv != NULL) { 2763 for (int iii = 0; iii < argc; iii++) { 2764 if (argv[iii] != NULL) { 2765 free(argv[iii]); 2766 } 2767 } 2768 free(argv); 2769 } 2770 2771 demo.connection = hInstance; 2772 strncpy(demo.name, "cube", APP_NAME_STR_LEN); 2773 demo_create_window(&demo); 2774 demo_init_vk_swapchain(&demo); 2775 2776 demo_prepare(&demo); 2777 2778 done = false; // initialize loop condition variable 2779 2780 // main message loop 2781 while (!done) { 2782 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); 2783 if (msg.message == WM_QUIT) // check for a quit message 2784 { 2785 done = true; // if found, quit app 2786 } else { 2787 /* Translate and dispatch to event queue*/ 2788 TranslateMessage(&msg); 2789 DispatchMessage(&msg); 2790 } 2791 RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT); 2792 } 2793 2794 demo_cleanup(&demo); 2795 2796 return (int)msg.wParam; 2797} 2798#else // _WIN32 2799int main(int argc, char **argv) { 2800 struct demo demo; 2801 2802 demo_init(&demo, argc, argv); 2803 demo_create_window(&demo); 2804 demo_init_vk_swapchain(&demo); 2805 2806 demo_prepare(&demo); 2807 demo_run(&demo); 2808 2809 demo_cleanup(&demo); 2810 2811 return validation_error; 2812} 2813#endif // _WIN32 2814