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