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