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