1 /* 2 * Copyright (c) 2015-2016 The Khronos Group Inc. 3 * Copyright (c) 2015-2016 Valve Corporation 4 * Copyright (c) 2015-2016 LunarG, Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * Author: Jeremy Hayes <jeremy (at) lunarg.com> 19 */ 20 21 #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) 22 #include <X11/Xutil.h> 23 #endif 24 25 #include <cassert> 26 #include <cstdio> 27 #include <cstdlib> 28 #include <cstring> 29 #include <csignal> 30 #include <memory> 31 32 #define VULKAN_HPP_NO_EXCEPTIONS 33 #include <vulkan/vulkan.hpp> 34 #include <vulkan/vk_sdk_platform.h> 35 36 #include "linmath.h" 37 38 #ifndef NDEBUG 39 #define VERIFY(x) assert(x) 40 #else 41 #define VERIFY(x) ((void)(x)) 42 #endif 43 44 #define APP_SHORT_NAME "cube" 45 #ifdef _WIN32 46 #define APP_NAME_STR_LEN 80 47 #endif 48 49 // Allow a maximum of two outstanding presentation operations. 50 #define FRAME_LAG 2 51 52 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 53 54 #ifdef _WIN32 55 #define ERR_EXIT(err_msg, err_class) \ 56 do { \ 57 if (!suppress_popups) \ 58 MessageBox(nullptr, err_msg, err_class, MB_OK); \ 59 exit(1); \ 60 } while (0) 61 #else 62 #define ERR_EXIT(err_msg, err_class) \ 63 do { \ 64 printf(err_msg); \ 65 fflush(stdout); \ 66 exit(1); \ 67 } while (0) 68 #endif 69 70 struct texture_object { 71 vk::Sampler sampler; 72 73 vk::Image image; 74 vk::ImageLayout imageLayout; 75 76 vk::MemoryAllocateInfo mem_alloc; 77 vk::DeviceMemory mem; 78 vk::ImageView view; 79 80 int32_t tex_width; 81 int32_t tex_height; 82 }; 83 84 static char const *const tex_files[] = {"lunarg.ppm"}; 85 86 static int validation_error = 0; 87 88 struct vkcube_vs_uniform { 89 // Must start with MVP 90 float mvp[4][4]; 91 float position[12 * 3][4]; 92 float color[12 * 3][4]; 93 }; 94 95 struct vktexcube_vs_uniform { 96 // Must start with MVP 97 float mvp[4][4]; 98 float position[12 * 3][4]; 99 float attr[12 * 3][4]; 100 }; 101 102 //-------------------------------------------------------------------------------------- 103 // Mesh and VertexFormat Data 104 //-------------------------------------------------------------------------------------- 105 // clang-format off 106 static const float g_vertex_buffer_data[] = { 107 -1.0f,-1.0f,-1.0f, // -X side 108 -1.0f,-1.0f, 1.0f, 109 -1.0f, 1.0f, 1.0f, 110 -1.0f, 1.0f, 1.0f, 111 -1.0f, 1.0f,-1.0f, 112 -1.0f,-1.0f,-1.0f, 113 114 -1.0f,-1.0f,-1.0f, // -Z side 115 1.0f, 1.0f,-1.0f, 116 1.0f,-1.0f,-1.0f, 117 -1.0f,-1.0f,-1.0f, 118 -1.0f, 1.0f,-1.0f, 119 1.0f, 1.0f,-1.0f, 120 121 -1.0f,-1.0f,-1.0f, // -Y side 122 1.0f,-1.0f,-1.0f, 123 1.0f,-1.0f, 1.0f, 124 -1.0f,-1.0f,-1.0f, 125 1.0f,-1.0f, 1.0f, 126 -1.0f,-1.0f, 1.0f, 127 128 -1.0f, 1.0f,-1.0f, // +Y side 129 -1.0f, 1.0f, 1.0f, 130 1.0f, 1.0f, 1.0f, 131 -1.0f, 1.0f,-1.0f, 132 1.0f, 1.0f, 1.0f, 133 1.0f, 1.0f,-1.0f, 134 135 1.0f, 1.0f,-1.0f, // +X side 136 1.0f, 1.0f, 1.0f, 137 1.0f,-1.0f, 1.0f, 138 1.0f,-1.0f, 1.0f, 139 1.0f,-1.0f,-1.0f, 140 1.0f, 1.0f,-1.0f, 141 142 -1.0f, 1.0f, 1.0f, // +Z side 143 -1.0f,-1.0f, 1.0f, 144 1.0f, 1.0f, 1.0f, 145 -1.0f,-1.0f, 1.0f, 146 1.0f,-1.0f, 1.0f, 147 1.0f, 1.0f, 1.0f, 148 }; 149 150 static const float g_uv_buffer_data[] = { 151 0.0f, 1.0f, // -X side 152 1.0f, 1.0f, 153 1.0f, 0.0f, 154 1.0f, 0.0f, 155 0.0f, 0.0f, 156 0.0f, 1.0f, 157 158 1.0f, 1.0f, // -Z side 159 0.0f, 0.0f, 160 0.0f, 1.0f, 161 1.0f, 1.0f, 162 1.0f, 0.0f, 163 0.0f, 0.0f, 164 165 1.0f, 0.0f, // -Y side 166 1.0f, 1.0f, 167 0.0f, 1.0f, 168 1.0f, 0.0f, 169 0.0f, 1.0f, 170 0.0f, 0.0f, 171 172 1.0f, 0.0f, // +Y side 173 0.0f, 0.0f, 174 0.0f, 1.0f, 175 1.0f, 0.0f, 176 0.0f, 1.0f, 177 1.0f, 1.0f, 178 179 1.0f, 0.0f, // +X side 180 0.0f, 0.0f, 181 0.0f, 1.0f, 182 0.0f, 1.0f, 183 1.0f, 1.0f, 184 1.0f, 0.0f, 185 186 0.0f, 0.0f, // +Z side 187 0.0f, 1.0f, 188 1.0f, 0.0f, 189 0.0f, 1.0f, 190 1.0f, 1.0f, 191 1.0f, 0.0f, 192 }; 193 // clang-format on 194 195 typedef struct { 196 vk::Image image; 197 vk::CommandBuffer cmd; 198 vk::CommandBuffer graphics_to_present_cmd; 199 vk::ImageView view; 200 } SwapchainBuffers; 201 202 #ifdef _WIN32 203 // MS-Windows event handling function: 204 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 205 #endif 206 207 struct Demo { 208 Demo() 209 : 210 #if defined(VK_USE_PLATFORM_WIN32_KHR) 211 connection{nullptr}, 212 window{nullptr}, 213 minsize(POINT{ 214 0, 0}), // Use explicit construction to avoid MSVC error C2797. 215 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) 216 display{nullptr}, 217 xlib_window{0}, xlib_wm_delete_window{0}, connection{nullptr}, 218 screen{nullptr}, xcb_window{0}, atom_wm_delete_window{nullptr}, 219 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 220 display{nullptr}, 221 registry{nullptr}, compositor{nullptr}, window{nullptr}, 222 shell{nullptr}, shell_surface{nullptr}, 223 #endif 224 prepared{false}, 225 use_staging_buffer{false}, use_xlib{false}, 226 graphics_queue_family_index{0}, present_queue_family_index{0}, 227 enabled_extension_count{0}, enabled_layer_count{0}, width{0}, 228 height{0}, swapchainImageCount{0}, frame_index{0}, spin_angle{0.0f}, 229 spin_increment{0.0f}, pause{false}, quit{false}, curFrame{0}, 230 frameCount{0}, validate{false}, use_break{false}, 231 suppress_popups{false}, current_buffer{0}, queue_family_count{0} { 232 #if defined(VK_USE_PLATFORM_WIN32_KHR) 233 memset(name, '\0', APP_NAME_STR_LEN); 234 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) 235 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 236 #endif 237 memset(fencesInited, 0, sizeof(bool) * FRAME_LAG); 238 memset(projection_matrix, 0, sizeof(projection_matrix)); 239 memset(view_matrix, 0, sizeof(view_matrix)); 240 memset(model_matrix, 0, sizeof(model_matrix)); 241 } 242 243 void build_image_ownership_cmd(uint32_t const &i) { 244 auto const cmd_buf_info = vk::CommandBufferBeginInfo().setFlags( 245 vk::CommandBufferUsageFlagBits::eSimultaneousUse); 246 auto result = buffers[i].graphics_to_present_cmd.begin(&cmd_buf_info); 247 VERIFY(result == vk::Result::eSuccess); 248 249 auto const image_ownership_barrier = 250 vk::ImageMemoryBarrier() 251 .setSrcAccessMask(vk::AccessFlags()) 252 .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite) 253 .setOldLayout(vk::ImageLayout::ePresentSrcKHR) 254 .setNewLayout(vk::ImageLayout::ePresentSrcKHR) 255 .setSrcQueueFamilyIndex(graphics_queue_family_index) 256 .setDstQueueFamilyIndex(present_queue_family_index) 257 .setImage(buffers[i].image) 258 .setSubresourceRange(vk::ImageSubresourceRange( 259 vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); 260 261 buffers[i].graphics_to_present_cmd.pipelineBarrier( 262 vk::PipelineStageFlagBits::eColorAttachmentOutput, 263 vk::PipelineStageFlagBits::eColorAttachmentOutput, 264 vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1, 265 &image_ownership_barrier); 266 267 result = buffers[i].graphics_to_present_cmd.end(); 268 VERIFY(result == vk::Result::eSuccess); 269 } 270 271 vk::Bool32 check_layers(uint32_t check_count, 272 char const *const *const check_names, 273 uint32_t layer_count, vk::LayerProperties *layers) { 274 for (uint32_t i = 0; i < check_count; i++) { 275 vk::Bool32 found = VK_FALSE; 276 for (uint32_t j = 0; j < layer_count; j++) { 277 if (!strcmp(check_names[i], layers[j].layerName)) { 278 found = VK_TRUE; 279 break; 280 } 281 } 282 if (!found) { 283 fprintf(stderr, "Cannot find layer: %s\n", check_names[i]); 284 return 0; 285 } 286 } 287 return VK_TRUE; 288 } 289 290 void cleanup() { 291 prepared = false; 292 device.waitIdle(); 293 294 // Wait for fences from present operations 295 for (uint32_t i = 0; i < FRAME_LAG; i++) { 296 if (fencesInited[i]) { 297 device.waitForFences(1, &fences[i], VK_TRUE, UINT64_MAX); 298 } 299 device.destroyFence(fences[i], nullptr); 300 device.destroySemaphore(image_acquired_semaphores[i], nullptr); 301 device.destroySemaphore(draw_complete_semaphores[i], nullptr); 302 if (separate_present_queue) { 303 device.destroySemaphore(image_ownership_semaphores[i], nullptr); 304 } 305 } 306 307 for (uint32_t i = 0; i < swapchainImageCount; i++) { 308 device.destroyFramebuffer(framebuffers[i], nullptr); 309 } 310 device.destroyDescriptorPool(desc_pool, nullptr); 311 312 device.destroyPipeline(pipeline, nullptr); 313 device.destroyPipelineCache(pipelineCache, nullptr); 314 device.destroyRenderPass(render_pass, nullptr); 315 device.destroyPipelineLayout(pipeline_layout, nullptr); 316 device.destroyDescriptorSetLayout(desc_layout, nullptr); 317 318 for (uint32_t i = 0; i < texture_count; i++) { 319 device.destroyImageView(textures[i].view, nullptr); 320 device.destroyImage(textures[i].image, nullptr); 321 device.freeMemory(textures[i].mem, nullptr); 322 device.destroySampler(textures[i].sampler, nullptr); 323 } 324 device.destroySwapchainKHR(swapchain, nullptr); 325 326 device.destroyImageView(depth.view, nullptr); 327 device.destroyImage(depth.image, nullptr); 328 device.freeMemory(depth.mem, nullptr); 329 330 device.destroyBuffer(uniform_data.buf, nullptr); 331 device.freeMemory(uniform_data.mem, nullptr); 332 333 for (uint32_t i = 0; i < swapchainImageCount; i++) { 334 device.destroyImageView(buffers[i].view, nullptr); 335 device.freeCommandBuffers(cmd_pool, 1, &buffers[i].cmd); 336 } 337 338 device.destroyCommandPool(cmd_pool, nullptr); 339 340 if (separate_present_queue) { 341 device.destroyCommandPool(present_cmd_pool, nullptr); 342 } 343 344 device.destroy(nullptr); 345 inst.destroySurfaceKHR(surface, nullptr); 346 inst.destroy(nullptr); 347 348 #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) 349 if (use_xlib) { 350 XDestroyWindow(display, xlib_window); 351 XCloseDisplay(display); 352 } else { 353 xcb_destroy_window(connection, xcb_window); 354 xcb_disconnect(connection); 355 } 356 free(atom_wm_delete_window); 357 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 358 wl_shell_surface_destroy(shell_surface); 359 wl_surface_destroy(window); 360 wl_shell_destroy(shell); 361 wl_compositor_destroy(compositor); 362 wl_registry_destroy(registry); 363 wl_display_disconnect(display); 364 #endif 365 } 366 367 void create_device() { 368 float const priorities[1] = {0.0}; 369 370 vk::DeviceQueueCreateInfo queues[2]; 371 queues[0].setQueueFamilyIndex(graphics_queue_family_index); 372 queues[0].setQueueCount(1); 373 queues[0].setPQueuePriorities(priorities); 374 375 auto deviceInfo = vk::DeviceCreateInfo() 376 .setQueueCreateInfoCount(1) 377 .setPQueueCreateInfos(queues) 378 .setEnabledLayerCount(0) 379 .setPpEnabledLayerNames(nullptr) 380 .setEnabledExtensionCount(enabled_extension_count) 381 .setPpEnabledExtensionNames( 382 (const char *const *)extension_names) 383 .setPEnabledFeatures(nullptr); 384 385 if (separate_present_queue) { 386 queues[1].setQueueFamilyIndex(present_queue_family_index); 387 queues[1].setQueueCount(1); 388 queues[1].setPQueuePriorities(priorities); 389 deviceInfo.setQueueCreateInfoCount(2); 390 } 391 392 auto result = gpu.createDevice(&deviceInfo, nullptr, &device); 393 VERIFY(result == vk::Result::eSuccess); 394 } 395 396 void destroy_texture_image(texture_object *tex_objs) { 397 // clean up staging resources 398 device.freeMemory(tex_objs->mem, nullptr); 399 device.destroyImage(tex_objs->image, nullptr); 400 } 401 402 void draw() { 403 if (fencesInited[frame_index]) { 404 // Ensure no more than FRAME_LAG presentations are outstanding 405 device.waitForFences(1, &fences[frame_index], VK_TRUE, UINT64_MAX); 406 device.resetFences(1, &fences[frame_index]); 407 } 408 409 // Get the index of the next available swapchain image: 410 auto result = device.acquireNextImageKHR( 411 swapchain, UINT64_MAX, image_acquired_semaphores[frame_index], 412 fences[frame_index], ¤t_buffer); 413 fencesInited[frame_index] = true; 414 if (result == vk::Result::eErrorOutOfDateKHR) { 415 // swapchain is out of date (e.g. the window was resized) and 416 // must be recreated: 417 frame_index += 1; 418 frame_index %= FRAME_LAG; 419 420 resize(); 421 draw(); 422 return; 423 } else if (result == vk::Result::eSuboptimalKHR) { 424 // swapchain is not as optimal as it could be, but the platform's 425 // presentation engine will still present the image correctly. 426 } else { 427 VERIFY(result == vk::Result::eSuccess); 428 } 429 430 // Wait for the image acquired semaphore to be signaled to ensure 431 // that the image won't be rendered to until the presentation 432 // engine has fully released ownership to the application, and it is 433 // okay to render to the image. 434 vk::PipelineStageFlags const pipe_stage_flags = 435 vk::PipelineStageFlagBits::eColorAttachmentOutput; 436 auto const submit_info = 437 vk::SubmitInfo() 438 .setPWaitDstStageMask(&pipe_stage_flags) 439 .setWaitSemaphoreCount(1) 440 .setPWaitSemaphores(&image_acquired_semaphores[frame_index]) 441 .setCommandBufferCount(1) 442 .setPCommandBuffers(&buffers[current_buffer].cmd) 443 .setSignalSemaphoreCount(1) 444 .setPSignalSemaphores(&draw_complete_semaphores[frame_index]); 445 446 result = graphics_queue.submit(1, &submit_info, vk::Fence()); 447 VERIFY(result == vk::Result::eSuccess); 448 449 if (separate_present_queue) { 450 // If we are using separate queues, change image ownership to the 451 // present queue before presenting, waiting for the draw complete 452 // semaphore and signalling the ownership released semaphore when 453 // finished 454 auto const submit_info = 455 vk::SubmitInfo() 456 .setPWaitDstStageMask(&pipe_stage_flags) 457 .setWaitSemaphoreCount(1) 458 .setPWaitSemaphores(&draw_complete_semaphores[frame_index]) 459 .setCommandBufferCount(1) 460 .setPCommandBuffers( 461 &buffers[current_buffer].graphics_to_present_cmd) 462 .setSignalSemaphoreCount(1) 463 .setPSignalSemaphores( 464 &image_ownership_semaphores[frame_index]); 465 466 result = present_queue.submit(1, &submit_info, vk::Fence()); 467 VERIFY(result == vk::Result::eSuccess); 468 } 469 470 // If we are using separate queues we have to wait for image ownership, 471 // otherwise wait for draw complete 472 auto const presentInfo = 473 vk::PresentInfoKHR() 474 .setWaitSemaphoreCount(1) 475 .setPWaitSemaphores( 476 separate_present_queue 477 ? &image_ownership_semaphores[frame_index] 478 : &draw_complete_semaphores[frame_index]) 479 .setSwapchainCount(1) 480 .setPSwapchains(&swapchain) 481 .setPImageIndices(¤t_buffer); 482 483 result = present_queue.presentKHR(&presentInfo); 484 frame_index += 1; 485 frame_index %= FRAME_LAG; 486 if (result == vk::Result::eErrorOutOfDateKHR) { 487 // swapchain is out of date (e.g. the window was resized) and 488 // must be recreated: 489 resize(); 490 } else if (result == vk::Result::eSuboptimalKHR) { 491 // swapchain is not as optimal as it could be, but the platform's 492 // presentation engine will still present the image correctly. 493 } else { 494 VERIFY(result == vk::Result::eSuccess); 495 } 496 } 497 498 void draw_build_cmd(vk::CommandBuffer commandBuffer) { 499 auto const commandInfo = vk::CommandBufferBeginInfo().setFlags( 500 vk::CommandBufferUsageFlagBits::eSimultaneousUse); 501 502 vk::ClearValue const clearValues[2] = { 503 vk::ClearColorValue(std::array<float, 4>({0.2f, 0.2f, 0.2f, 0.2f})), 504 vk::ClearDepthStencilValue(1.0f, 0u)}; 505 506 auto const passInfo = 507 vk::RenderPassBeginInfo() 508 .setRenderPass(render_pass) 509 .setFramebuffer(framebuffers[current_buffer]) 510 .setRenderArea( 511 vk::Rect2D(vk::Offset2D(0, 0), 512 vk::Extent2D((uint32_t)width, (uint32_t)height))) 513 .setClearValueCount(2) 514 .setPClearValues(clearValues); 515 516 auto result = commandBuffer.begin(&commandInfo); 517 VERIFY(result == vk::Result::eSuccess); 518 519 auto const image_memory_barrier = 520 vk::ImageMemoryBarrier() 521 .setSrcAccessMask(vk::AccessFlagBits::eMemoryRead) 522 .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite) 523 .setOldLayout(vk::ImageLayout::ePresentSrcKHR) 524 .setNewLayout(vk::ImageLayout::eColorAttachmentOptimal) 525 .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) 526 .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) 527 .setImage(buffers[current_buffer].image) 528 .setSubresourceRange(vk::ImageSubresourceRange( 529 vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); 530 531 commandBuffer.pipelineBarrier( 532 vk::PipelineStageFlagBits::eColorAttachmentOutput, 533 vk::PipelineStageFlagBits::eColorAttachmentOutput, 534 vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1, 535 &image_memory_barrier); 536 537 commandBuffer.beginRenderPass(&passInfo, vk::SubpassContents::eInline); 538 539 commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); 540 commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, 541 pipeline_layout, 0, 1, &desc_set, 0, 542 nullptr); 543 544 auto const viewport = vk::Viewport() 545 .setWidth((float)width) 546 .setHeight((float)height) 547 .setMinDepth((float)0.0f) 548 .setMaxDepth((float)1.0f); 549 commandBuffer.setViewport(0, 1, &viewport); 550 551 vk::Rect2D const scissor(vk::Offset2D(0, 0), 552 vk::Extent2D(width, height)); 553 commandBuffer.setScissor(0, 1, &scissor); 554 commandBuffer.draw(12 * 3, 1, 0, 0); 555 // Note that ending the renderpass changes the image's layout from 556 // COLOR_ATTACHMENT_OPTIMAL to PRESENT_SRC_KHR 557 commandBuffer.endRenderPass(); 558 559 if (separate_present_queue) { 560 // We have to transfer ownership from the graphics queue family to 561 // the 562 // present queue family to be able to present. Note that we don't 563 // have 564 // to transfer from present queue family back to graphics queue 565 // family at 566 // the start of the next frame because we don't care about the 567 // image's 568 // contents at that point. 569 auto const image_ownership_barrier = 570 vk::ImageMemoryBarrier() 571 .setSrcAccessMask(vk::AccessFlags()) 572 .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite) 573 .setOldLayout(vk::ImageLayout::ePresentSrcKHR) 574 .setNewLayout(vk::ImageLayout::ePresentSrcKHR) 575 .setSrcQueueFamilyIndex(graphics_queue_family_index) 576 .setDstQueueFamilyIndex(present_queue_family_index) 577 .setImage(buffers[current_buffer].image) 578 .setSubresourceRange(vk::ImageSubresourceRange( 579 vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); 580 581 commandBuffer.pipelineBarrier( 582 vk::PipelineStageFlagBits::eColorAttachmentOutput, 583 vk::PipelineStageFlagBits::eColorAttachmentOutput, 584 vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1, 585 &image_ownership_barrier); 586 } 587 588 result = commandBuffer.end(); 589 VERIFY(result == vk::Result::eSuccess); 590 } 591 592 void flush_init_cmd() { 593 // TODO: hmm. 594 // This function could get called twice if the texture uses a staging 595 // buffer 596 // In that case the second call should be ignored 597 if (!cmd) { 598 return; 599 } 600 601 auto result = cmd.end(); 602 VERIFY(result == vk::Result::eSuccess); 603 604 auto const fenceInfo = 605 vk::FenceCreateInfo().setFlags(vk::FenceCreateFlagBits(0)); 606 vk::Fence fence; 607 device.createFence(&fenceInfo, nullptr, &fence); 608 609 vk::CommandBuffer const commandBuffers[] = {cmd}; 610 auto const submitInfo = 611 vk::SubmitInfo().setCommandBufferCount(1).setPCommandBuffers( 612 commandBuffers); 613 614 result = graphics_queue.submit(1, &submitInfo, fence); 615 VERIFY(result == vk::Result::eSuccess); 616 617 result = device.waitForFences(1, &fence, VK_TRUE, UINT64_MAX); 618 VERIFY(result == vk::Result::eSuccess); 619 620 device.freeCommandBuffers(cmd_pool, 1, commandBuffers); 621 device.destroyFence(fence, nullptr); 622 623 cmd = vk::CommandBuffer(); 624 } 625 626 void init(int argc, char **argv) { 627 vec3 eye = {0.0f, 3.0f, 5.0f}; 628 vec3 origin = {0, 0, 0}; 629 vec3 up = {0.0f, 1.0f, 0.0}; 630 631 frameCount = UINT32_MAX; 632 use_xlib = false; 633 634 for (int i = 1; i < argc; i++) { 635 if (strcmp(argv[i], "--use_staging") == 0) { 636 use_staging_buffer = true; 637 continue; 638 } 639 if (strcmp(argv[i], "--break") == 0) { 640 use_break = true; 641 continue; 642 } 643 if (strcmp(argv[i], "--validate") == 0) { 644 validate = true; 645 continue; 646 } 647 #if defined(VK_USE_PLATFORM_XLIB_KHR) 648 if (strcmp(argv[i], "--xlib") == 0) { 649 use_xlib = true; 650 continue; 651 } 652 #endif 653 if (strcmp(argv[i], "--c") == 0 && frameCount == UINT32_MAX && 654 i < argc - 1 && sscanf(argv[i + 1], "%d", &frameCount) == 1) { 655 i++; 656 continue; 657 } 658 if (strcmp(argv[i], "--suppress_popups") == 0) { 659 suppress_popups = true; 660 continue; 661 } 662 663 fprintf(stderr, 664 "Usage:\n %s [--use_staging] [--validate] [--break] " 665 #if defined(VK_USE_PLATFORM_XLIB_KHR) 666 "[--xlib] " 667 #endif 668 "[--c <framecount>] [--suppress_popups]\n", 669 APP_SHORT_NAME); 670 fflush(stderr); 671 exit(1); 672 } 673 674 if (!use_xlib) { 675 init_connection(); 676 } 677 678 init_vk(); 679 680 width = 500; 681 height = 500; 682 683 spin_angle = 4.0f; 684 spin_increment = 0.2f; 685 pause = false; 686 687 mat4x4_perspective(projection_matrix, (float)degreesToRadians(45.0f), 688 1.0f, 0.1f, 100.0f); 689 mat4x4_look_at(view_matrix, eye, origin, up); 690 mat4x4_identity(model_matrix); 691 692 projection_matrix[1][1] *= 693 -1; // Flip projection matrix from GL to Vulkan orientation. 694 } 695 696 void init_connection() { 697 #if defined(VK_USE_PLATFORM_XCB_KHR) 698 const xcb_setup_t *setup; 699 xcb_screen_iterator_t iter; 700 int scr; 701 702 connection = xcb_connect(nullptr, &scr); 703 if (xcb_connection_has_error(connection) > 0) { 704 printf("Cannot find a compatible Vulkan installable client driver " 705 "(ICD).\nExiting ...\n"); 706 fflush(stdout); 707 exit(1); 708 } 709 710 setup = xcb_get_setup(connection); 711 iter = xcb_setup_roots_iterator(setup); 712 while (scr-- > 0) 713 xcb_screen_next(&iter); 714 715 screen = iter.data; 716 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 717 display = wl_display_connect(nullptr); 718 719 if (display == nullptr) { 720 printf("Cannot find a compatible Vulkan installable client driver " 721 "(ICD).\nExiting ...\n"); 722 fflush(stdout); 723 exit(1); 724 } 725 726 registry = wl_display_get_registry(display); 727 wl_registry_add_listener(registry, ®istry_listener, this); 728 wl_display_dispatch(display); 729 #endif 730 } 731 732 void init_vk() { 733 uint32_t instance_extension_count = 0; 734 uint32_t instance_layer_count = 0; 735 uint32_t validation_layer_count = 0; 736 char const *const *instance_validation_layers = nullptr; 737 enabled_extension_count = 0; 738 enabled_layer_count = 0; 739 740 char const *const instance_validation_layers_alt1[] = { 741 "VK_LAYER_LUNARG_standard_validation"}; 742 743 char const *const instance_validation_layers_alt2[] = { 744 "VK_LAYER_GOOGLE_threading", 745 "VK_LAYER_LUNARG_parameter_validation", 746 "VK_LAYER_LUNARG_object_tracker", 747 "VK_LAYER_LUNARG_image", 748 "VK_LAYER_LUNARG_core_validation", 749 "VK_LAYER_LUNARG_swapchain", 750 "VK_LAYER_GOOGLE_unique_objects"}; 751 752 // Look for validation layers 753 vk::Bool32 validation_found = VK_FALSE; 754 if (validate) { 755 auto result = vk::enumerateInstanceLayerProperties( 756 &instance_layer_count, nullptr); 757 VERIFY(result == vk::Result::eSuccess); 758 759 instance_validation_layers = instance_validation_layers_alt1; 760 if (instance_layer_count > 0) { 761 std::unique_ptr<vk::LayerProperties[]> instance_layers( 762 new vk::LayerProperties[instance_layer_count]); 763 result = vk::enumerateInstanceLayerProperties( 764 &instance_layer_count, instance_layers.get()); 765 VERIFY(result == vk::Result::eSuccess); 766 767 validation_found = 768 check_layers(ARRAY_SIZE(instance_validation_layers_alt1), 769 instance_validation_layers, 770 instance_layer_count, instance_layers.get()); 771 if (validation_found) { 772 enabled_layer_count = 773 ARRAY_SIZE(instance_validation_layers_alt1); 774 enabled_layers[0] = "VK_LAYER_LUNARG_standard_validation"; 775 validation_layer_count = 1; 776 } else { 777 // use alternative set of validation layers 778 instance_validation_layers = 779 instance_validation_layers_alt2; 780 enabled_layer_count = 781 ARRAY_SIZE(instance_validation_layers_alt2); 782 validation_found = check_layers( 783 ARRAY_SIZE(instance_validation_layers_alt2), 784 instance_validation_layers, instance_layer_count, 785 instance_layers.get()); 786 validation_layer_count = 787 ARRAY_SIZE(instance_validation_layers_alt2); 788 for (uint32_t i = 0; i < validation_layer_count; i++) { 789 enabled_layers[i] = instance_validation_layers[i]; 790 } 791 } 792 } 793 794 if (!validation_found) { 795 ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find " 796 "required validation layer.\n\n" 797 "Please look at the Getting Started guide for " 798 "additional information.\n", 799 "vkCreateInstance Failure"); 800 } 801 } 802 803 /* Look for instance extensions */ 804 vk::Bool32 surfaceExtFound = VK_FALSE; 805 vk::Bool32 platformSurfaceExtFound = VK_FALSE; 806 #if defined(VK_USE_PLATFORM_XLIB_KHR) 807 vk::Bool32 xlibSurfaceExtFound = VK_FALSE; 808 #endif 809 memset(extension_names, 0, sizeof(extension_names)); 810 811 auto result = vk::enumerateInstanceExtensionProperties( 812 nullptr, &instance_extension_count, nullptr); 813 VERIFY(result == vk::Result::eSuccess); 814 815 if (instance_extension_count > 0) { 816 std::unique_ptr<vk::ExtensionProperties[]> instance_extensions( 817 new vk::ExtensionProperties[instance_extension_count]); 818 result = vk::enumerateInstanceExtensionProperties( 819 nullptr, &instance_extension_count, instance_extensions.get()); 820 VERIFY(result == vk::Result::eSuccess); 821 822 for (uint32_t i = 0; i < instance_extension_count; i++) { 823 if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, 824 instance_extensions[i].extensionName)) { 825 surfaceExtFound = 1; 826 extension_names[enabled_extension_count++] = 827 VK_KHR_SURFACE_EXTENSION_NAME; 828 } 829 #if defined(VK_USE_PLATFORM_WIN32_KHR) 830 if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, 831 instance_extensions[i].extensionName)) { 832 platformSurfaceExtFound = 1; 833 extension_names[enabled_extension_count++] = 834 VK_KHR_WIN32_SURFACE_EXTENSION_NAME; 835 } 836 #endif 837 #if defined(VK_USE_PLATFORM_XLIB_KHR) 838 if (!strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, 839 instance_extensions[i].extensionName)) { 840 platformSurfaceExtFound = 1; 841 xlibSurfaceExtFound = 1; 842 extension_names[enabled_extension_count++] = 843 VK_KHR_XLIB_SURFACE_EXTENSION_NAME; 844 } 845 #endif 846 #if defined(VK_USE_PLATFORM_XCB_KHR) 847 if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, 848 instance_extensions[i].extensionName)) { 849 platformSurfaceExtFound = 1; 850 extension_names[enabled_extension_count++] = 851 VK_KHR_XCB_SURFACE_EXTENSION_NAME; 852 } 853 #endif 854 #if defined(VK_USE_PLATFORM_WAYLAND_KHR) 855 if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, 856 instance_extensions[i].extensionName)) { 857 platformSurfaceExtFound = 1; 858 extension_names[enabled_extension_count++] = 859 VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; 860 } 861 #endif 862 assert(enabled_extension_count < 64); 863 } 864 } 865 866 if (!surfaceExtFound) { 867 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 868 "the " VK_KHR_SURFACE_EXTENSION_NAME " extension.\n\n" 869 "Do you have a compatible Vulkan installable client " 870 "driver (ICD) installed?\n" 871 "Please look at the Getting Started guide for additional " 872 "information.\n", 873 "vkCreateInstance Failure"); 874 } 875 876 if (!platformSurfaceExtFound) { 877 #if defined(VK_USE_PLATFORM_WIN32_KHR) 878 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 879 "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME 880 " extension.\n\n" 881 "Do you have a compatible Vulkan installable client " 882 "driver (ICD) installed?\n" 883 "Please look at the Getting Started guide for additional " 884 "information.\n", 885 "vkCreateInstance Failure"); 886 #elif defined(VK_USE_PLATFORM_XCB_KHR) 887 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 888 "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension.\n\n" 889 "Do you have a compatible Vulkan installable client " 890 "driver (ICD) installed?\n" 891 "Please look at the Getting Started guide for additional " 892 "information.\n", 893 "vkCreateInstance Failure"); 894 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 895 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 896 "the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME 897 " extension.\n\n" 898 "Do you have a compatible Vulkan installable client " 899 "driver (ICD) installed?\n" 900 "Please look at the Getting Started guide for additional " 901 "information.\n", 902 "vkCreateInstance Failure"); 903 #endif 904 } 905 906 #if defined(VK_USE_PLATFORM_XLIB_KHR) 907 if (use_xlib && !xlibSurfaceExtFound) { 908 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " 909 "the " VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension.\n\n" 910 "Do you have a compatible Vulkan installable client " 911 "driver (ICD) installed?\n" 912 "Please look at the Getting Started guide for additional " 913 "information.\n", 914 "vkCreateInstance Failure"); 915 } 916 #endif 917 918 auto const app = vk::ApplicationInfo() 919 .setPApplicationName(APP_SHORT_NAME) 920 .setApplicationVersion(0) 921 .setPEngineName(APP_SHORT_NAME) 922 .setEngineVersion(0) 923 .setApiVersion(VK_API_VERSION_1_0); 924 auto const inst_info = 925 vk::InstanceCreateInfo() 926 .setPApplicationInfo(&app) 927 .setEnabledLayerCount(enabled_layer_count) 928 .setPpEnabledLayerNames(instance_validation_layers) 929 .setEnabledExtensionCount(enabled_extension_count) 930 .setPpEnabledExtensionNames(extension_names); 931 932 result = vk::createInstance(&inst_info, nullptr, &inst); 933 if (result == vk::Result::eErrorIncompatibleDriver) { 934 ERR_EXIT("Cannot find a compatible Vulkan installable client " 935 "driver (ICD).\n\n" 936 "Please look at the Getting Started guide for additional " 937 "information.\n", 938 "vkCreateInstance Failure"); 939 } else if (result == vk::Result::eErrorExtensionNotPresent) { 940 ERR_EXIT("Cannot find a specified extension library.\n" 941 "Make sure your layers path is set appropriately.\n", 942 "vkCreateInstance Failure"); 943 } else if (result != vk::Result::eSuccess) { 944 ERR_EXIT("vkCreateInstance failed.\n\n" 945 "Do you have a compatible Vulkan installable client " 946 "driver (ICD) installed?\n" 947 "Please look at the Getting Started guide for additional " 948 "information.\n", 949 "vkCreateInstance Failure"); 950 } 951 952 /* Make initial call to query gpu_count, then second call for gpu info*/ 953 uint32_t gpu_count; 954 result = inst.enumeratePhysicalDevices(&gpu_count, nullptr); 955 VERIFY(result == vk::Result::eSuccess); 956 assert(gpu_count > 0); 957 958 if (gpu_count > 0) { 959 std::unique_ptr<vk::PhysicalDevice[]> physical_devices( 960 new vk::PhysicalDevice[gpu_count]); 961 result = inst.enumeratePhysicalDevices(&gpu_count, 962 physical_devices.get()); 963 VERIFY(result == vk::Result::eSuccess); 964 /* For cube demo we just grab the first physical device */ 965 gpu = physical_devices[0]; 966 } else { 967 ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible " 968 "devices.\n\n" 969 "Do you have a compatible Vulkan installable client " 970 "driver (ICD) installed?\n" 971 "Please look at the Getting Started guide for additional " 972 "information.\n", 973 "vkEnumeratePhysicalDevices Failure"); 974 } 975 976 /* Look for device extensions */ 977 uint32_t device_extension_count = 0; 978 vk::Bool32 swapchainExtFound = VK_FALSE; 979 enabled_extension_count = 0; 980 memset(extension_names, 0, sizeof(extension_names)); 981 982 result = gpu.enumerateDeviceExtensionProperties( 983 nullptr, &device_extension_count, nullptr); 984 VERIFY(result == vk::Result::eSuccess); 985 986 if (device_extension_count > 0) { 987 std::unique_ptr<vk::ExtensionProperties[]> device_extensions( 988 new vk::ExtensionProperties[device_extension_count]); 989 result = gpu.enumerateDeviceExtensionProperties( 990 nullptr, &device_extension_count, device_extensions.get()); 991 VERIFY(result == vk::Result::eSuccess); 992 993 for (uint32_t i = 0; i < device_extension_count; i++) { 994 if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 995 device_extensions[i].extensionName)) { 996 swapchainExtFound = 1; 997 extension_names[enabled_extension_count++] = 998 VK_KHR_SWAPCHAIN_EXTENSION_NAME; 999 } 1000 assert(enabled_extension_count < 64); 1001 } 1002 } 1003 1004 if (!swapchainExtFound) { 1005 ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find " 1006 "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME " extension.\n\n" 1007 "Do you have a compatible Vulkan installable client " 1008 "driver (ICD) installed?\n" 1009 "Please look at the Getting Started guide for additional " 1010 "information.\n", 1011 "vkCreateInstance Failure"); 1012 } 1013 1014 gpu.getProperties(&gpu_props); 1015 1016 /* Call with nullptr data to get count */ 1017 gpu.getQueueFamilyProperties(&queue_family_count, nullptr); 1018 assert(queue_family_count >= 1); 1019 1020 queue_props.reset(new vk::QueueFamilyProperties[queue_family_count]); 1021 gpu.getQueueFamilyProperties(&queue_family_count, queue_props.get()); 1022 1023 // Query fine-grained feature support for this device. 1024 // If app has specific feature requirements it should check supported 1025 // features based on this query 1026 vk::PhysicalDeviceFeatures physDevFeatures; 1027 gpu.getFeatures(&physDevFeatures); 1028 } 1029 1030 void init_vk_swapchain() { 1031 // Create a WSI surface for the window: 1032 #if defined(VK_USE_PLATFORM_WIN32_KHR) 1033 { 1034 auto const createInfo = vk::Win32SurfaceCreateInfoKHR() 1035 .setHinstance(connection) 1036 .setHwnd(window); 1037 1038 auto result = 1039 inst.createWin32SurfaceKHR(&createInfo, nullptr, &surface); 1040 VERIFY(result == vk::Result::eSuccess); 1041 } 1042 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) && !defined(VK_USE_PLATFORM_XCB_KHR) 1043 { 1044 auto const createInfo = vk::WaylandSurfaceCreateInfoKHR() 1045 .setDisplay(display) 1046 .setSurface(window); 1047 1048 auto result = 1049 inst.createWaylandSurfaceKHR(&createInfo, nullptr, &surface); 1050 VERIFY(result == vk::Result::eSuccess); 1051 } 1052 #endif 1053 if (use_xlib) { 1054 #if defined(VK_USE_PLATFORM_XLIB_KHR) 1055 auto const createInfo = 1056 vk::XlibSurfaceCreateInfoKHR().setDpy(display).setWindow( 1057 xlib_window); 1058 1059 auto result = 1060 inst.createXlibSurfaceKHR(&createInfo, nullptr, &surface); 1061 VERIFY(result == vk::Result::eSuccess); 1062 #endif 1063 } else { 1064 #if defined(VK_USE_PLATFORM_XCB_KHR) 1065 auto const createInfo = vk::XcbSurfaceCreateInfoKHR() 1066 .setConnection(connection) 1067 .setWindow(xcb_window); 1068 1069 auto result = 1070 inst.createXcbSurfaceKHR(&createInfo, nullptr, &surface); 1071 VERIFY(result == vk::Result::eSuccess); 1072 #endif 1073 } 1074 1075 // Iterate over each queue to learn whether it supports presenting: 1076 std::unique_ptr<vk::Bool32[]> supportsPresent( 1077 new vk::Bool32[queue_family_count]); 1078 for (uint32_t i = 0; i < queue_family_count; i++) { 1079 gpu.getSurfaceSupportKHR(i, surface, &supportsPresent[i]); 1080 } 1081 1082 uint32_t graphicsQueueFamilyIndex = UINT32_MAX; 1083 uint32_t presentQueueFamilyIndex = UINT32_MAX; 1084 for (uint32_t i = 0; i < queue_family_count; i++) { 1085 if (queue_props[i].queueFlags & vk::QueueFlagBits::eGraphics) { 1086 if (graphicsQueueFamilyIndex == UINT32_MAX) { 1087 graphicsQueueFamilyIndex = i; 1088 } 1089 1090 if (supportsPresent[i] == VK_TRUE) { 1091 graphicsQueueFamilyIndex = i; 1092 presentQueueFamilyIndex = i; 1093 break; 1094 } 1095 } 1096 } 1097 1098 if (presentQueueFamilyIndex == UINT32_MAX) { 1099 // If didn't find a queue that supports both graphics and present, 1100 // then 1101 // find a separate present queue. 1102 for (uint32_t i = 0; i < queue_family_count; ++i) { 1103 if (supportsPresent[i] == VK_TRUE) { 1104 presentQueueFamilyIndex = i; 1105 break; 1106 } 1107 } 1108 } 1109 1110 // Generate error if could not find both a graphics and a present queue 1111 if (graphicsQueueFamilyIndex == UINT32_MAX || 1112 presentQueueFamilyIndex == UINT32_MAX) { 1113 ERR_EXIT("Could not find both graphics and present queues\n", 1114 "Swapchain Initialization Failure"); 1115 } 1116 1117 graphics_queue_family_index = graphicsQueueFamilyIndex; 1118 present_queue_family_index = presentQueueFamilyIndex; 1119 separate_present_queue = 1120 (graphics_queue_family_index != present_queue_family_index); 1121 1122 create_device(); 1123 1124 device.getQueue(graphics_queue_family_index, 0, &graphics_queue); 1125 if (!separate_present_queue) { 1126 present_queue = graphics_queue; 1127 } else { 1128 device.getQueue(present_queue_family_index, 0, &present_queue); 1129 } 1130 1131 // Get the list of VkFormat's that are supported: 1132 uint32_t formatCount; 1133 auto result = gpu.getSurfaceFormatsKHR(surface, &formatCount, nullptr); 1134 VERIFY(result == vk::Result::eSuccess); 1135 1136 std::unique_ptr<vk::SurfaceFormatKHR[]> surfFormats( 1137 new vk::SurfaceFormatKHR[formatCount]); 1138 result = 1139 gpu.getSurfaceFormatsKHR(surface, &formatCount, surfFormats.get()); 1140 VERIFY(result == vk::Result::eSuccess); 1141 1142 // If the format list includes just one entry of VK_FORMAT_UNDEFINED, 1143 // the surface has no preferred format. Otherwise, at least one 1144 // supported format will be returned. 1145 if (formatCount == 1 && 1146 surfFormats[0].format == vk::Format::eUndefined) { 1147 format = vk::Format::eB8G8R8A8Unorm; 1148 } else { 1149 assert(formatCount >= 1); 1150 format = surfFormats[0].format; 1151 } 1152 color_space = surfFormats[0].colorSpace; 1153 1154 quit = false; 1155 curFrame = 0; 1156 1157 // Create semaphores to synchronize acquiring presentable buffers before 1158 // rendering and waiting for drawing to be complete before presenting 1159 auto const semaphoreCreateInfo = vk::SemaphoreCreateInfo(); 1160 1161 // Create fences that we can use to throttle if we get too far 1162 // ahead of the image presents 1163 vk::FenceCreateInfo const fence_ci; 1164 for (uint32_t i = 0; i < FRAME_LAG; i++) { 1165 device.createFence(&fence_ci, nullptr, &fences[i]); 1166 fencesInited[i] = false; 1167 result = device.createSemaphore(&semaphoreCreateInfo, nullptr, 1168 &image_acquired_semaphores[i]); 1169 VERIFY(result == vk::Result::eSuccess); 1170 1171 result = device.createSemaphore(&semaphoreCreateInfo, nullptr, 1172 &draw_complete_semaphores[i]); 1173 VERIFY(result == vk::Result::eSuccess); 1174 1175 if (separate_present_queue) { 1176 result = device.createSemaphore(&semaphoreCreateInfo, nullptr, 1177 &image_ownership_semaphores[i]); 1178 VERIFY(result == vk::Result::eSuccess); 1179 } 1180 } 1181 frame_index = 0; 1182 1183 // Get Memory information and properties 1184 gpu.getMemoryProperties(&memory_properties); 1185 } 1186 1187 void prepare() { 1188 auto const cmd_pool_info = 1189 vk::CommandPoolCreateInfo().setQueueFamilyIndex( 1190 graphics_queue_family_index); 1191 auto result = 1192 device.createCommandPool(&cmd_pool_info, nullptr, &cmd_pool); 1193 VERIFY(result == vk::Result::eSuccess); 1194 1195 auto const cmd = vk::CommandBufferAllocateInfo() 1196 .setCommandPool(cmd_pool) 1197 .setLevel(vk::CommandBufferLevel::ePrimary) 1198 .setCommandBufferCount(1); 1199 1200 prepare_buffers(); 1201 prepare_depth(); 1202 prepare_textures(); 1203 prepare_cube_data_buffer(); 1204 1205 prepare_descriptor_layout(); 1206 prepare_render_pass(); 1207 prepare_pipeline(); 1208 1209 for (uint32_t i = 0; i < swapchainImageCount; ++i) { 1210 result = device.allocateCommandBuffers(&cmd, &buffers[i].cmd); 1211 VERIFY(result == vk::Result::eSuccess); 1212 } 1213 1214 if (separate_present_queue) { 1215 auto const cmd_pool_info = 1216 vk::CommandPoolCreateInfo().setQueueFamilyIndex( 1217 present_queue_family_index); 1218 1219 result = device.createCommandPool(&cmd_pool_info, nullptr, 1220 &present_cmd_pool); 1221 VERIFY(result == vk::Result::eSuccess); 1222 1223 auto const cmd = vk::CommandBufferAllocateInfo() 1224 .setCommandPool(present_cmd_pool) 1225 .setLevel(vk::CommandBufferLevel::ePrimary) 1226 .setCommandBufferCount(1); 1227 1228 for (uint32_t i = 0; i < swapchainImageCount; i++) { 1229 result = device.allocateCommandBuffers( 1230 &cmd, &buffers[i].graphics_to_present_cmd); 1231 VERIFY(result == vk::Result::eSuccess); 1232 1233 build_image_ownership_cmd(i); 1234 } 1235 } 1236 1237 prepare_descriptor_pool(); 1238 prepare_descriptor_set(); 1239 1240 prepare_framebuffers(); 1241 1242 for (uint32_t i = 0; i < swapchainImageCount; ++i) { 1243 current_buffer = i; 1244 draw_build_cmd(buffers[i].cmd); 1245 } 1246 1247 /* 1248 * Prepare functions above may generate pipeline commands 1249 * that need to be flushed before beginning the render loop. 1250 */ 1251 flush_init_cmd(); 1252 1253 current_buffer = 0; 1254 prepared = true; 1255 } 1256 1257 void prepare_buffers() { 1258 vk::SwapchainKHR oldSwapchain = swapchain; 1259 1260 // Check the surface capabilities and formats 1261 vk::SurfaceCapabilitiesKHR surfCapabilities; 1262 auto result = gpu.getSurfaceCapabilitiesKHR(surface, &surfCapabilities); 1263 VERIFY(result == vk::Result::eSuccess); 1264 1265 uint32_t presentModeCount; 1266 result = 1267 gpu.getSurfacePresentModesKHR(surface, &presentModeCount, nullptr); 1268 VERIFY(result == vk::Result::eSuccess); 1269 1270 std::unique_ptr<vk::PresentModeKHR[]> presentModes( 1271 new vk::PresentModeKHR[presentModeCount]); 1272 result = gpu.getSurfacePresentModesKHR(surface, &presentModeCount, 1273 presentModes.get()); 1274 VERIFY(result == vk::Result::eSuccess); 1275 1276 vk::Extent2D swapchainExtent; 1277 // width and height are either both -1, or both not -1. 1278 if (surfCapabilities.currentExtent.width == (uint32_t)-1) { 1279 // If the surface size is undefined, the size is set to 1280 // the size of the images requested. 1281 swapchainExtent.width = width; 1282 swapchainExtent.height = height; 1283 } else { 1284 // If the surface size is defined, the swap chain size must match 1285 swapchainExtent = surfCapabilities.currentExtent; 1286 width = surfCapabilities.currentExtent.width; 1287 height = surfCapabilities.currentExtent.height; 1288 } 1289 1290 // The FIFO present mode is guaranteed by the spec to be supported 1291 // and to have no tearing. It's a great default present mode to use. 1292 vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo; 1293 1294 // There are times when you may wish to use another present mode. The 1295 // following code shows how to select them, and the comments provide some 1296 // reasons you may wish to use them. 1297 // 1298 // It should be noted that Vulkan 1.0 doesn't provide a method for 1299 // synchronizing rendering with the presentation engine's display. There 1300 // is a method provided for throttling rendering with the display, but 1301 // there are some presentation engines for which this method will not work. 1302 // If an application doesn't throttle its rendering, and if it renders much 1303 // faster than the refresh rate of the display, this can waste power on 1304 // mobile devices. That is because power is being spent rendering images 1305 // that may never be seen. 1306 //#define DESIRE_VK_PRESENT_MODE_IMMEDIATE_KHR 1307 //#define DESIRE_VK_PRESENT_MODE_MAILBOX_KHR 1308 //#define DESIRE_VK_PRESENT_MODE_FIFO_RELAXED_KHR 1309 #if defined(DESIRE_VK_PRESENT_MODE_IMMEDIATE_KHR) 1310 // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care 1311 // about 1312 // tearing, or have some way of synchronizing their rendering with the 1313 // display. 1314 for (size_t i = 0; i < presentModeCount; ++i) { 1315 if (presentModes[i] == vk::PresentModeKHR::eImmediate) { 1316 swapchainPresentMode = vk::PresentModeKHR::eImmediate; 1317 break; 1318 } 1319 } 1320 #elif defined(DESIRE_VK_PRESENT_MODE_MAILBOX_KHR) 1321 // VK_PRESENT_MODE_MAILBOX_KHR may be useful for applications that 1322 // generally render a new presentable image every refresh cycle, but are 1323 // occasionally early. In this case, the application wants the new 1324 // image 1325 // to be displayed instead of the previously-queued-for-presentation 1326 // image 1327 // that has not yet been displayed. 1328 for (size_t i = 0; i < presentModeCount; ++i) { 1329 if (presentModes[i] == vk::PresentModeKHR::eMailbox) { 1330 swapchainPresentMode = vk::PresentModeKHR::eMailbox; 1331 break; 1332 } 1333 } 1334 #elif defined(DESIRE_VK_PRESENT_MODE_FIFO_RELAXED_KHR) 1335 // VK_PRESENT_MODE_FIFO_RELAXED_KHR is for applications that generally 1336 // render a new presentable image every refresh cycle, but are 1337 // occasionally 1338 // late. In this case (perhaps because of stuttering/latency concerns), 1339 // the application wants the late image to be immediately displayed, 1340 // even 1341 // though that may mean some tearing. 1342 for (size_t i = 0; i < presentModeCount; ++i) { 1343 if (presentModes[i] == vk::PresentModeKHR::eFifoRelaxed) { 1344 swapchainPresentMode = vk::PresentModeKHR::eFifoRelaxed; 1345 break; 1346 } 1347 } 1348 #endif 1349 1350 // Determine the number of VkImage's to use in the swap chain (we desire 1351 // to 1352 // own only 1 image at a time, besides the images being displayed and 1353 // queued for display): 1354 uint32_t desiredNumberOfSwapchainImages = 1355 surfCapabilities.minImageCount + 1; 1356 // If maxImageCount is 0, we can ask for as many images as we want, 1357 // otherwise 1358 // we're limited to maxImageCount 1359 if ((surfCapabilities.maxImageCount > 0) && 1360 (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { 1361 // Application must settle for fewer images than desired: 1362 desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; 1363 } 1364 1365 vk::SurfaceTransformFlagBitsKHR preTransform; 1366 if (surfCapabilities.supportedTransforms & 1367 vk::SurfaceTransformFlagBitsKHR::eIdentity) { 1368 preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity; 1369 } else { 1370 preTransform = surfCapabilities.currentTransform; 1371 } 1372 1373 auto const swapchain_ci = 1374 vk::SwapchainCreateInfoKHR() 1375 .setSurface(surface) 1376 .setMinImageCount(desiredNumberOfSwapchainImages) 1377 .setImageFormat(format) 1378 .setImageColorSpace(color_space) 1379 .setImageExtent({swapchainExtent.width, swapchainExtent.height}) 1380 .setImageArrayLayers(1) 1381 .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment) 1382 .setImageSharingMode(vk::SharingMode::eExclusive) 1383 .setQueueFamilyIndexCount(0) 1384 .setPQueueFamilyIndices(nullptr) 1385 .setPreTransform(preTransform) 1386 .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque) 1387 .setPresentMode(swapchainPresentMode) 1388 .setClipped(true) 1389 .setOldSwapchain(oldSwapchain); 1390 1391 result = device.createSwapchainKHR(&swapchain_ci, nullptr, &swapchain); 1392 VERIFY(result == vk::Result::eSuccess); 1393 1394 // If we just re-created an existing swapchain, we should destroy the 1395 // old 1396 // swapchain at this point. 1397 // Note: destroying the swapchain also cleans up all its associated 1398 // presentable images once the platform is done with them. 1399 if (oldSwapchain) { 1400 device.destroySwapchainKHR(oldSwapchain, nullptr); 1401 } 1402 1403 result = device.getSwapchainImagesKHR(swapchain, &swapchainImageCount, 1404 nullptr); 1405 VERIFY(result == vk::Result::eSuccess); 1406 1407 std::unique_ptr<vk::Image[]> swapchainImages( 1408 new vk::Image[swapchainImageCount]); 1409 result = device.getSwapchainImagesKHR(swapchain, &swapchainImageCount, 1410 swapchainImages.get()); 1411 VERIFY(result == vk::Result::eSuccess); 1412 1413 buffers.reset(new SwapchainBuffers[swapchainImageCount]); 1414 1415 for (uint32_t i = 0; i < swapchainImageCount; ++i) { 1416 auto const color_image_view = 1417 vk::ImageViewCreateInfo() 1418 .setImage(swapchainImages[i]) 1419 .setViewType(vk::ImageViewType::e2D) 1420 .setFormat(format) 1421 .setSubresourceRange(vk::ImageSubresourceRange( 1422 vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); 1423 1424 buffers[i].image = swapchainImages[i]; 1425 1426 result = device.createImageView(&color_image_view, nullptr, 1427 &buffers[i].view); 1428 VERIFY(result == vk::Result::eSuccess); 1429 1430 // The draw loop will be expecting the presentable images to be in 1431 // LAYOUT_PRESENT_SRC_KHR since that's how they're left at the end 1432 // of every frame. 1433 set_image_layout(buffers[i].image, vk::ImageAspectFlagBits::eColor, 1434 vk::ImageLayout::eUndefined, 1435 vk::ImageLayout::ePresentSrcKHR, 1436 vk::AccessFlagBits()); 1437 } 1438 } 1439 1440 void prepare_cube_data_buffer() { 1441 mat4x4 VP; 1442 mat4x4_mul(VP, projection_matrix, view_matrix); 1443 1444 mat4x4 MVP; 1445 mat4x4_mul(MVP, VP, model_matrix); 1446 1447 vktexcube_vs_uniform data; 1448 memcpy(data.mvp, MVP, sizeof(MVP)); 1449 // dumpMatrix("MVP", MVP) 1450 for (int32_t i = 0; i < 12 * 3; i++) { 1451 data.position[i][0] = g_vertex_buffer_data[i * 3]; 1452 data.position[i][1] = g_vertex_buffer_data[i * 3 + 1]; 1453 data.position[i][2] = g_vertex_buffer_data[i * 3 + 2]; 1454 data.position[i][3] = 1.0f; 1455 data.attr[i][0] = g_uv_buffer_data[2 * i]; 1456 data.attr[i][1] = g_uv_buffer_data[2 * i + 1]; 1457 data.attr[i][2] = 0; 1458 data.attr[i][3] = 0; 1459 } 1460 1461 auto const buf_info = 1462 vk::BufferCreateInfo() 1463 .setSize(sizeof(data)) 1464 .setUsage(vk::BufferUsageFlagBits::eUniformBuffer); 1465 auto result = 1466 device.createBuffer(&buf_info, nullptr, &uniform_data.buf); 1467 VERIFY(result == vk::Result::eSuccess); 1468 1469 vk::MemoryRequirements mem_reqs; 1470 device.getBufferMemoryRequirements(uniform_data.buf, &mem_reqs); 1471 1472 uniform_data.mem_alloc.setAllocationSize(mem_reqs.size); 1473 uniform_data.mem_alloc.setMemoryTypeIndex(0); 1474 1475 bool const pass = memory_type_from_properties( 1476 mem_reqs.memoryTypeBits, 1477 vk::MemoryPropertyFlagBits::eHostVisible | 1478 vk::MemoryPropertyFlagBits::eHostCoherent, 1479 &uniform_data.mem_alloc.memoryTypeIndex); 1480 VERIFY(pass); 1481 1482 result = device.allocateMemory(&uniform_data.mem_alloc, nullptr, 1483 &(uniform_data.mem)); 1484 VERIFY(result == vk::Result::eSuccess); 1485 1486 auto pData = device.mapMemory(uniform_data.mem, 0, 1487 uniform_data.mem_alloc.allocationSize, 1488 vk::MemoryMapFlags()); 1489 VERIFY(pData.result == vk::Result::eSuccess); 1490 1491 memcpy(pData.value, &data, sizeof data); 1492 1493 device.unmapMemory(uniform_data.mem); 1494 1495 result = device.bindBufferMemory(uniform_data.buf, uniform_data.mem, 0); 1496 VERIFY(result == vk::Result::eSuccess); 1497 1498 uniform_data.buffer_info.buffer = uniform_data.buf; 1499 uniform_data.buffer_info.offset = 0; 1500 uniform_data.buffer_info.range = sizeof(data); 1501 } 1502 1503 void prepare_depth() { 1504 depth.format = vk::Format::eD16Unorm; 1505 1506 auto const image = 1507 vk::ImageCreateInfo() 1508 .setImageType(vk::ImageType::e2D) 1509 .setFormat(depth.format) 1510 .setExtent({(uint32_t)width, (uint32_t)height, 1}) 1511 .setMipLevels(1) 1512 .setArrayLayers(1) 1513 .setSamples(vk::SampleCountFlagBits::e1) 1514 .setTiling(vk::ImageTiling::eOptimal) 1515 .setUsage(vk::ImageUsageFlagBits::eDepthStencilAttachment) 1516 .setSharingMode(vk::SharingMode::eExclusive) 1517 .setQueueFamilyIndexCount(0) 1518 .setPQueueFamilyIndices(nullptr) 1519 .setInitialLayout(vk::ImageLayout::eUndefined); 1520 1521 auto result = device.createImage(&image, nullptr, &depth.image); 1522 VERIFY(result == vk::Result::eSuccess); 1523 1524 vk::MemoryRequirements mem_reqs; 1525 device.getImageMemoryRequirements(depth.image, &mem_reqs); 1526 1527 depth.mem_alloc.setAllocationSize(mem_reqs.size); 1528 depth.mem_alloc.setMemoryTypeIndex(0); 1529 1530 auto const pass = memory_type_from_properties( 1531 mem_reqs.memoryTypeBits, vk::MemoryPropertyFlagBits(0), 1532 &depth.mem_alloc.memoryTypeIndex); 1533 VERIFY(pass); 1534 1535 result = device.allocateMemory(&depth.mem_alloc, nullptr, &depth.mem); 1536 VERIFY(result == vk::Result::eSuccess); 1537 1538 result = device.bindImageMemory(depth.image, depth.mem, 0); 1539 VERIFY(result == vk::Result::eSuccess); 1540 1541 set_image_layout(depth.image, vk::ImageAspectFlagBits::eDepth, 1542 vk::ImageLayout::eUndefined, 1543 vk::ImageLayout::eDepthStencilAttachmentOptimal, 1544 vk::AccessFlagBits()); 1545 1546 auto const view = vk::ImageViewCreateInfo() 1547 .setImage(depth.image) 1548 .setViewType(vk::ImageViewType::e2D) 1549 .setFormat(depth.format) 1550 .setSubresourceRange(vk::ImageSubresourceRange( 1551 vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1)); 1552 result = device.createImageView(&view, nullptr, &depth.view); 1553 VERIFY(result == vk::Result::eSuccess); 1554 } 1555 1556 void prepare_descriptor_layout() { 1557 vk::DescriptorSetLayoutBinding const layout_bindings[2] = { 1558 vk::DescriptorSetLayoutBinding() 1559 .setBinding(0) 1560 .setDescriptorType(vk::DescriptorType::eUniformBuffer) 1561 .setDescriptorCount(1) 1562 .setStageFlags(vk::ShaderStageFlagBits::eVertex) 1563 .setPImmutableSamplers(nullptr), 1564 vk::DescriptorSetLayoutBinding() 1565 .setBinding(1) 1566 .setDescriptorType(vk::DescriptorType::eCombinedImageSampler) 1567 .setDescriptorCount(texture_count) 1568 .setStageFlags(vk::ShaderStageFlagBits::eFragment) 1569 .setPImmutableSamplers(nullptr)}; 1570 1571 auto const descriptor_layout = 1572 vk::DescriptorSetLayoutCreateInfo().setBindingCount(2).setPBindings( 1573 layout_bindings); 1574 1575 auto result = device.createDescriptorSetLayout(&descriptor_layout, 1576 nullptr, &desc_layout); 1577 VERIFY(result == vk::Result::eSuccess); 1578 1579 auto const pPipelineLayoutCreateInfo = 1580 vk::PipelineLayoutCreateInfo().setSetLayoutCount(1).setPSetLayouts( 1581 &desc_layout); 1582 1583 result = device.createPipelineLayout(&pPipelineLayoutCreateInfo, 1584 nullptr, &pipeline_layout); 1585 VERIFY(result == vk::Result::eSuccess); 1586 } 1587 1588 void prepare_descriptor_pool() { 1589 vk::DescriptorPoolSize const poolSizes[2] = { 1590 vk::DescriptorPoolSize() 1591 .setType(vk::DescriptorType::eUniformBuffer) 1592 .setDescriptorCount(1), 1593 vk::DescriptorPoolSize() 1594 .setType(vk::DescriptorType::eCombinedImageSampler) 1595 .setDescriptorCount(texture_count)}; 1596 1597 auto const descriptor_pool = vk::DescriptorPoolCreateInfo() 1598 .setMaxSets(1) 1599 .setPoolSizeCount(2) 1600 .setPPoolSizes(poolSizes); 1601 1602 auto result = 1603 device.createDescriptorPool(&descriptor_pool, nullptr, &desc_pool); 1604 VERIFY(result == vk::Result::eSuccess); 1605 } 1606 1607 void prepare_descriptor_set() { 1608 auto const alloc_info = vk::DescriptorSetAllocateInfo() 1609 .setDescriptorPool(desc_pool) 1610 .setDescriptorSetCount(1) 1611 .setPSetLayouts(&desc_layout); 1612 auto result = device.allocateDescriptorSets(&alloc_info, &desc_set); 1613 VERIFY(result == vk::Result::eSuccess); 1614 1615 vk::DescriptorImageInfo tex_descs[texture_count]; 1616 for (uint32_t i = 0; i < texture_count; i++) { 1617 tex_descs[i].setSampler(textures[i].sampler); 1618 tex_descs[i].setImageView(textures[i].view); 1619 tex_descs[i].setImageLayout(vk::ImageLayout::eGeneral); 1620 } 1621 1622 vk::WriteDescriptorSet writes[2]; 1623 1624 writes[0].setDstSet(desc_set); 1625 writes[0].setDescriptorCount(1); 1626 writes[0].setDescriptorType(vk::DescriptorType::eUniformBuffer); 1627 writes[0].setPBufferInfo(&uniform_data.buffer_info); 1628 1629 writes[1].setDstSet(desc_set); 1630 writes[1].setDstBinding(1); 1631 writes[1].setDescriptorCount(texture_count); 1632 writes[1].setDescriptorType(vk::DescriptorType::eCombinedImageSampler); 1633 writes[1].setPImageInfo(tex_descs); 1634 1635 device.updateDescriptorSets(2, writes, 0, nullptr); 1636 } 1637 1638 void prepare_framebuffers() { 1639 vk::ImageView attachments[2]; 1640 attachments[1] = depth.view; 1641 1642 auto const fb_info = vk::FramebufferCreateInfo() 1643 .setRenderPass(render_pass) 1644 .setAttachmentCount(2) 1645 .setPAttachments(attachments) 1646 .setWidth((uint32_t)width) 1647 .setHeight((uint32_t)height) 1648 .setLayers(1); 1649 1650 framebuffers.reset(new vk::Framebuffer[swapchainImageCount]); 1651 1652 for (uint32_t i = 0; i < swapchainImageCount; i++) { 1653 attachments[0] = buffers[i].view; 1654 auto const result = 1655 device.createFramebuffer(&fb_info, nullptr, &framebuffers[i]); 1656 VERIFY(result == vk::Result::eSuccess); 1657 } 1658 } 1659 1660 vk::ShaderModule prepare_fs() { 1661 size_t size = 0; 1662 void *fragShaderCode = read_spv("cube-frag.spv", &size); 1663 1664 frag_shader_module = prepare_shader_module(fragShaderCode, size); 1665 1666 free(fragShaderCode); 1667 1668 return frag_shader_module; 1669 } 1670 1671 void prepare_pipeline() { 1672 vk::PipelineCacheCreateInfo const pipelineCacheInfo; 1673 auto result = device.createPipelineCache(&pipelineCacheInfo, nullptr, 1674 &pipelineCache); 1675 VERIFY(result == vk::Result::eSuccess); 1676 1677 vk::PipelineShaderStageCreateInfo const shaderStageInfo[2] = { 1678 vk::PipelineShaderStageCreateInfo() 1679 .setStage(vk::ShaderStageFlagBits::eVertex) 1680 .setModule(prepare_vs()) 1681 .setPName("main"), 1682 vk::PipelineShaderStageCreateInfo() 1683 .setStage(vk::ShaderStageFlagBits::eFragment) 1684 .setModule(prepare_fs()) 1685 .setPName("main")}; 1686 1687 vk::PipelineVertexInputStateCreateInfo const vertexInputInfo; 1688 1689 auto const inputAssemblyInfo = 1690 vk::PipelineInputAssemblyStateCreateInfo().setTopology( 1691 vk::PrimitiveTopology::eTriangleList); 1692 1693 // TODO: Where are pViewports and pScissors set? 1694 auto const viewportInfo = vk::PipelineViewportStateCreateInfo() 1695 .setViewportCount(1) 1696 .setScissorCount(1); 1697 1698 auto const rasterizationInfo = 1699 vk::PipelineRasterizationStateCreateInfo() 1700 .setDepthClampEnable(VK_FALSE) 1701 .setRasterizerDiscardEnable(VK_FALSE) 1702 .setPolygonMode(vk::PolygonMode::eFill) 1703 .setCullMode(vk::CullModeFlagBits::eBack) 1704 .setFrontFace(vk::FrontFace::eCounterClockwise) 1705 .setDepthBiasEnable(VK_FALSE) 1706 .setLineWidth(1.0f); 1707 1708 auto const multisampleInfo = vk::PipelineMultisampleStateCreateInfo(); 1709 1710 auto const stencilOp = vk::StencilOpState() 1711 .setFailOp(vk::StencilOp::eKeep) 1712 .setPassOp(vk::StencilOp::eKeep) 1713 .setCompareOp(vk::CompareOp::eAlways); 1714 1715 auto const depthStencilInfo = 1716 vk::PipelineDepthStencilStateCreateInfo() 1717 .setDepthTestEnable(VK_TRUE) 1718 .setDepthWriteEnable(VK_TRUE) 1719 .setDepthCompareOp(vk::CompareOp::eLessOrEqual) 1720 .setDepthBoundsTestEnable(VK_FALSE) 1721 .setStencilTestEnable(VK_FALSE) 1722 .setFront(stencilOp) 1723 .setBack(stencilOp); 1724 1725 vk::PipelineColorBlendAttachmentState const colorBlendAttachments[1] = { 1726 vk::PipelineColorBlendAttachmentState().setColorWriteMask( 1727 vk::ColorComponentFlagBits::eR | 1728 vk::ColorComponentFlagBits::eG | 1729 vk::ColorComponentFlagBits::eB | 1730 vk::ColorComponentFlagBits::eA)}; 1731 1732 auto const colorBlendInfo = vk::PipelineColorBlendStateCreateInfo() 1733 .setAttachmentCount(1) 1734 .setPAttachments(colorBlendAttachments); 1735 1736 vk::DynamicState const dynamicStates[2] = {vk::DynamicState::eViewport, 1737 vk::DynamicState::eScissor}; 1738 1739 auto const dynamicStateInfo = vk::PipelineDynamicStateCreateInfo() 1740 .setPDynamicStates(dynamicStates) 1741 .setDynamicStateCount(2); 1742 1743 auto const pipeline = vk::GraphicsPipelineCreateInfo() 1744 .setStageCount(2) 1745 .setPStages(shaderStageInfo) 1746 .setPVertexInputState(&vertexInputInfo) 1747 .setPInputAssemblyState(&inputAssemblyInfo) 1748 .setPViewportState(&viewportInfo) 1749 .setPRasterizationState(&rasterizationInfo) 1750 .setPMultisampleState(&multisampleInfo) 1751 .setPDepthStencilState(&depthStencilInfo) 1752 .setPColorBlendState(&colorBlendInfo) 1753 .setPDynamicState(&dynamicStateInfo) 1754 .setLayout(pipeline_layout) 1755 .setRenderPass(render_pass); 1756 1757 result = device.createGraphicsPipelines(pipelineCache, 1, &pipeline, 1758 nullptr, &this->pipeline); 1759 VERIFY(result == vk::Result::eSuccess); 1760 1761 device.destroyShaderModule(frag_shader_module, nullptr); 1762 device.destroyShaderModule(vert_shader_module, nullptr); 1763 } 1764 1765 void prepare_render_pass() { 1766 const vk::AttachmentDescription attachments[2] = { 1767 vk::AttachmentDescription() 1768 .setFlags(vk::AttachmentDescriptionFlagBits::eMayAlias) 1769 .setFormat(format) 1770 .setSamples(vk::SampleCountFlagBits::e1) 1771 .setLoadOp(vk::AttachmentLoadOp::eClear) 1772 .setStoreOp(vk::AttachmentStoreOp::eStore) 1773 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare) 1774 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) 1775 .setInitialLayout(vk::ImageLayout::eColorAttachmentOptimal) 1776 .setFinalLayout(vk::ImageLayout::ePresentSrcKHR), 1777 vk::AttachmentDescription() 1778 .setFlags(vk::AttachmentDescriptionFlagBits::eMayAlias) 1779 .setFormat(depth.format) 1780 .setSamples(vk::SampleCountFlagBits::e1) 1781 .setLoadOp(vk::AttachmentLoadOp::eClear) 1782 .setStoreOp(vk::AttachmentStoreOp::eDontCare) 1783 .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare) 1784 .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) 1785 .setInitialLayout( 1786 vk::ImageLayout::eDepthStencilAttachmentOptimal) 1787 .setFinalLayout( 1788 vk::ImageLayout::eDepthStencilAttachmentOptimal)}; 1789 1790 auto const color_reference = 1791 vk::AttachmentReference().setAttachment(0).setLayout( 1792 vk::ImageLayout::eColorAttachmentOptimal); 1793 1794 auto const depth_reference = 1795 vk::AttachmentReference().setAttachment(1).setLayout( 1796 vk::ImageLayout::eDepthStencilAttachmentOptimal); 1797 1798 auto const subpass = 1799 vk::SubpassDescription() 1800 .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics) 1801 .setInputAttachmentCount(0) 1802 .setPInputAttachments(nullptr) 1803 .setColorAttachmentCount(1) 1804 .setPColorAttachments(&color_reference) 1805 .setPResolveAttachments(nullptr) 1806 .setPDepthStencilAttachment(&depth_reference) 1807 .setPreserveAttachmentCount(0) 1808 .setPPreserveAttachments(nullptr); 1809 1810 auto const rp_info = vk::RenderPassCreateInfo() 1811 .setAttachmentCount(2) 1812 .setPAttachments(attachments) 1813 .setSubpassCount(1) 1814 .setPSubpasses(&subpass) 1815 .setDependencyCount(0) 1816 .setPDependencies(nullptr); 1817 1818 auto result = device.createRenderPass(&rp_info, nullptr, &render_pass); 1819 VERIFY(result == vk::Result::eSuccess); 1820 } 1821 1822 vk::ShaderModule prepare_shader_module(const void *code, size_t size) { 1823 auto const moduleCreateInfo = 1824 vk::ShaderModuleCreateInfo().setCodeSize(size).setPCode( 1825 (uint32_t const *)code); 1826 1827 vk::ShaderModule module; 1828 auto result = 1829 device.createShaderModule(&moduleCreateInfo, nullptr, &module); 1830 VERIFY(result == vk::Result::eSuccess); 1831 1832 return module; 1833 } 1834 1835 void prepare_texture_image(const char *filename, texture_object *tex_obj, 1836 vk::ImageTiling tiling, 1837 vk::ImageUsageFlags usage, 1838 vk::MemoryPropertyFlags required_props) { 1839 int32_t tex_width; 1840 int32_t tex_height; 1841 if (!loadTexture(filename, nullptr, nullptr, &tex_width, &tex_height)) { 1842 ERR_EXIT("Failed to load textures", "Load Texture Failure"); 1843 } 1844 1845 tex_obj->tex_width = tex_width; 1846 tex_obj->tex_height = tex_height; 1847 1848 auto const image_create_info = 1849 vk::ImageCreateInfo() 1850 .setImageType(vk::ImageType::e2D) 1851 .setFormat(vk::Format::eR8G8B8A8Unorm) 1852 .setExtent({(uint32_t)tex_width, (uint32_t)tex_height, 1}) 1853 .setMipLevels(1) 1854 .setArrayLayers(1) 1855 .setSamples(vk::SampleCountFlagBits::e1) 1856 .setTiling(tiling) 1857 .setUsage(usage) 1858 .setSharingMode(vk::SharingMode::eExclusive) 1859 .setQueueFamilyIndexCount(0) 1860 .setPQueueFamilyIndices(nullptr) 1861 .setInitialLayout(vk::ImageLayout::ePreinitialized); 1862 1863 auto result = 1864 device.createImage(&image_create_info, nullptr, &tex_obj->image); 1865 VERIFY(result == vk::Result::eSuccess); 1866 1867 vk::MemoryRequirements mem_reqs; 1868 device.getImageMemoryRequirements(tex_obj->image, &mem_reqs); 1869 1870 tex_obj->mem_alloc.setAllocationSize(mem_reqs.size); 1871 tex_obj->mem_alloc.setMemoryTypeIndex(0); 1872 1873 auto pass = 1874 memory_type_from_properties(mem_reqs.memoryTypeBits, required_props, 1875 &tex_obj->mem_alloc.memoryTypeIndex); 1876 VERIFY(pass == true); 1877 1878 result = device.allocateMemory(&tex_obj->mem_alloc, nullptr, 1879 &(tex_obj->mem)); 1880 VERIFY(result == vk::Result::eSuccess); 1881 1882 result = device.bindImageMemory(tex_obj->image, tex_obj->mem, 0); 1883 VERIFY(result == vk::Result::eSuccess); 1884 1885 if (required_props & vk::MemoryPropertyFlagBits::eHostVisible) { 1886 auto const subres = 1887 vk::ImageSubresource() 1888 .setAspectMask(vk::ImageAspectFlagBits::eColor) 1889 .setMipLevel(0) 1890 .setArrayLayer(0); 1891 vk::SubresourceLayout layout; 1892 device.getImageSubresourceLayout(tex_obj->image, &subres, &layout); 1893 1894 auto data = device.mapMemory(tex_obj->mem, 0, 1895 tex_obj->mem_alloc.allocationSize); 1896 VERIFY(data.result == vk::Result::eSuccess); 1897 1898 if (!loadTexture(filename, (uint8_t *)data.value, &layout, 1899 &tex_width, &tex_height)) { 1900 fprintf(stderr, "Error loading texture: %s\n", filename); 1901 } 1902 1903 device.unmapMemory(tex_obj->mem); 1904 } 1905 1906 tex_obj->imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal; 1907 set_image_layout(tex_obj->image, vk::ImageAspectFlagBits::eColor, 1908 vk::ImageLayout::ePreinitialized, tex_obj->imageLayout, 1909 vk::AccessFlagBits::eHostWrite); 1910 } 1911 1912 void prepare_textures() { 1913 vk::Format const tex_format = vk::Format::eR8G8B8A8Unorm; 1914 vk::FormatProperties props; 1915 gpu.getFormatProperties(tex_format, &props); 1916 1917 for (uint32_t i = 0; i < texture_count; i++) { 1918 if ((props.linearTilingFeatures & 1919 vk::FormatFeatureFlagBits::eSampledImage) && 1920 !use_staging_buffer) { 1921 /* Device can texture using linear textures */ 1922 prepare_texture_image( 1923 tex_files[i], &textures[i], vk::ImageTiling::eLinear, 1924 vk::ImageUsageFlagBits::eSampled, 1925 vk::MemoryPropertyFlagBits::eHostVisible | 1926 vk::MemoryPropertyFlagBits::eHostCoherent); 1927 } else if (props.optimalTilingFeatures & 1928 vk::FormatFeatureFlagBits::eSampledImage) { 1929 /* Must use staging buffer to copy linear texture to optimized 1930 */ 1931 texture_object staging_texture; 1932 1933 prepare_texture_image( 1934 tex_files[i], &staging_texture, vk::ImageTiling::eLinear, 1935 vk::ImageUsageFlagBits::eTransferSrc, 1936 vk::MemoryPropertyFlagBits::eHostVisible | 1937 vk::MemoryPropertyFlagBits::eHostCoherent); 1938 1939 prepare_texture_image(tex_files[i], &textures[i], 1940 vk::ImageTiling::eOptimal, 1941 vk::ImageUsageFlagBits::eTransferDst | 1942 vk::ImageUsageFlagBits::eSampled, 1943 vk::MemoryPropertyFlagBits::eDeviceLocal); 1944 1945 set_image_layout( 1946 staging_texture.image, vk::ImageAspectFlagBits::eColor, 1947 staging_texture.imageLayout, 1948 vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlags()); 1949 1950 set_image_layout( 1951 textures[i].image, vk::ImageAspectFlagBits::eColor, 1952 textures[i].imageLayout, 1953 vk::ImageLayout::eTransferDstOptimal, vk::AccessFlags()); 1954 1955 auto const subresource = 1956 vk::ImageSubresourceLayers() 1957 .setAspectMask(vk::ImageAspectFlagBits::eColor) 1958 .setMipLevel(0) 1959 .setBaseArrayLayer(0) 1960 .setLayerCount(1); 1961 1962 auto const copy_region = 1963 vk::ImageCopy() 1964 .setSrcSubresource(subresource) 1965 .setSrcOffset({0, 0, 0}) 1966 .setDstSubresource(subresource) 1967 .setDstOffset({0, 0, 0}) 1968 .setExtent({(uint32_t)staging_texture.tex_width, 1969 (uint32_t)staging_texture.tex_height, 1}); 1970 1971 cmd.copyImage( 1972 staging_texture.image, vk::ImageLayout::eTransferSrcOptimal, 1973 textures[i].image, vk::ImageLayout::eTransferDstOptimal, 1, 1974 ©_region); 1975 1976 set_image_layout(textures[i].image, 1977 vk::ImageAspectFlagBits::eColor, 1978 vk::ImageLayout::eTransferDstOptimal, 1979 textures[i].imageLayout, vk::AccessFlags()); 1980 1981 flush_init_cmd(); 1982 1983 destroy_texture_image(&staging_texture); 1984 } else { 1985 assert( 1986 !"No support for R8G8B8A8_UNORM as texture image format"); 1987 } 1988 1989 auto const samplerInfo = 1990 vk::SamplerCreateInfo() 1991 .setMagFilter(vk::Filter::eNearest) 1992 .setMinFilter(vk::Filter::eNearest) 1993 .setMipmapMode(vk::SamplerMipmapMode::eNearest) 1994 .setAddressModeU(vk::SamplerAddressMode::eClampToEdge) 1995 .setAddressModeV(vk::SamplerAddressMode::eClampToEdge) 1996 .setAddressModeW(vk::SamplerAddressMode::eClampToEdge) 1997 .setMipLodBias(0.0f) 1998 .setAnisotropyEnable(VK_FALSE) 1999 .setMaxAnisotropy(1) 2000 .setCompareEnable(VK_FALSE) 2001 .setCompareOp(vk::CompareOp::eNever) 2002 .setMinLod(0.0f) 2003 .setMaxLod(0.0f) 2004 .setBorderColor(vk::BorderColor::eFloatOpaqueWhite) 2005 .setUnnormalizedCoordinates(VK_FALSE); 2006 2007 auto result = device.createSampler(&samplerInfo, nullptr, 2008 &textures[i].sampler); 2009 VERIFY(result == vk::Result::eSuccess); 2010 2011 auto const viewInfo = 2012 vk::ImageViewCreateInfo() 2013 .setImage(textures[i].image) 2014 .setViewType(vk::ImageViewType::e2D) 2015 .setFormat(tex_format) 2016 .setSubresourceRange(vk::ImageSubresourceRange( 2017 vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); 2018 2019 result = 2020 device.createImageView(&viewInfo, nullptr, &textures[i].view); 2021 VERIFY(result == vk::Result::eSuccess); 2022 } 2023 } 2024 2025 vk::ShaderModule prepare_vs() { 2026 size_t size = 0; 2027 void *vertShaderCode = read_spv("cube-vert.spv", &size); 2028 2029 vert_shader_module = prepare_shader_module(vertShaderCode, size); 2030 2031 free(vertShaderCode); 2032 2033 return vert_shader_module; 2034 } 2035 2036 char *read_spv(const char *filename, size_t *psize) { 2037 FILE *fp = fopen(filename, "rb"); 2038 if (!fp) { 2039 return nullptr; 2040 } 2041 2042 fseek(fp, 0L, SEEK_END); 2043 long int size = ftell(fp); 2044 2045 fseek(fp, 0L, SEEK_SET); 2046 2047 void *shader_code = malloc(size); 2048 size_t retval = fread(shader_code, size, 1, fp); 2049 VERIFY(retval == 1); 2050 2051 *psize = size; 2052 2053 fclose(fp); 2054 2055 return (char *)shader_code; 2056 } 2057 2058 void resize() { 2059 uint32_t i; 2060 2061 // Don't react to resize until after first initialization. 2062 if (!prepared) { 2063 return; 2064 } 2065 2066 // In order to properly resize the window, we must re-create the 2067 // swapchain 2068 // AND redo the command buffers, etc. 2069 // 2070 // First, perform part of the cleanup() function: 2071 prepared = false; 2072 auto result = device.waitIdle(); 2073 VERIFY(result == vk::Result::eSuccess); 2074 2075 for (i = 0; i < swapchainImageCount; i++) { 2076 device.destroyFramebuffer(framebuffers[i], nullptr); 2077 } 2078 2079 device.destroyDescriptorPool(desc_pool, nullptr); 2080 2081 device.destroyPipeline(pipeline, nullptr); 2082 device.destroyPipelineCache(pipelineCache, nullptr); 2083 device.destroyRenderPass(render_pass, nullptr); 2084 device.destroyPipelineLayout(pipeline_layout, nullptr); 2085 device.destroyDescriptorSetLayout(desc_layout, nullptr); 2086 2087 for (i = 0; i < texture_count; i++) { 2088 device.destroyImageView(textures[i].view, nullptr); 2089 device.destroyImage(textures[i].image, nullptr); 2090 device.freeMemory(textures[i].mem, nullptr); 2091 device.destroySampler(textures[i].sampler, nullptr); 2092 } 2093 2094 device.destroyImageView(depth.view, nullptr); 2095 device.destroyImage(depth.image, nullptr); 2096 device.freeMemory(depth.mem, nullptr); 2097 2098 device.destroyBuffer(uniform_data.buf, nullptr); 2099 device.freeMemory(uniform_data.mem, nullptr); 2100 2101 for (i = 0; i < swapchainImageCount; i++) { 2102 device.destroyImageView(buffers[i].view, nullptr); 2103 device.freeCommandBuffers(cmd_pool, 1, &buffers[i].cmd); 2104 } 2105 2106 device.destroyCommandPool(cmd_pool, nullptr); 2107 if (separate_present_queue) { 2108 device.destroyCommandPool(present_cmd_pool, nullptr); 2109 } 2110 2111 // Second, re-perform the prepare() function, which will re-create the 2112 // swapchain. 2113 prepare(); 2114 } 2115 2116 void set_image_layout(vk::Image image, vk::ImageAspectFlags aspectMask, 2117 vk::ImageLayout oldLayout, vk::ImageLayout newLayout, 2118 vk::AccessFlags srcAccessMask) { 2119 if (!cmd) { 2120 auto const cmd = vk::CommandBufferAllocateInfo() 2121 .setCommandPool(cmd_pool) 2122 .setLevel(vk::CommandBufferLevel::ePrimary) 2123 .setCommandBufferCount(1); 2124 2125 auto result = device.allocateCommandBuffers(&cmd, &this->cmd); 2126 VERIFY(result == vk::Result::eSuccess); 2127 2128 auto const cmd_buf_info = 2129 vk::CommandBufferBeginInfo().setPInheritanceInfo(nullptr); 2130 2131 result = this->cmd.begin(&cmd_buf_info); 2132 VERIFY(result == vk::Result::eSuccess); 2133 } 2134 2135 auto DstAccessMask = [](vk::ImageLayout const &layout) { 2136 vk::AccessFlags flags; 2137 2138 switch (layout) { 2139 case vk::ImageLayout::eTransferDstOptimal: 2140 // Make sure anything that was copying from this image has 2141 // completed 2142 flags = vk::AccessFlagBits::eTransferRead; 2143 break; 2144 case vk::ImageLayout::eColorAttachmentOptimal: 2145 flags = vk::AccessFlagBits::eColorAttachmentWrite; 2146 break; 2147 case vk::ImageLayout::eDepthStencilAttachmentOptimal: 2148 flags = vk::AccessFlagBits::eDepthStencilAttachmentWrite; 2149 break; 2150 case vk::ImageLayout::eShaderReadOnlyOptimal: 2151 // Make sure any Copy or CPU writes to image are flushed 2152 flags = vk::AccessFlagBits::eShaderRead | 2153 vk::AccessFlagBits::eInputAttachmentRead; 2154 break; 2155 case vk::ImageLayout::ePresentSrcKHR: 2156 flags = vk::AccessFlagBits::eMemoryRead; 2157 break; 2158 default: 2159 break; 2160 } 2161 2162 return flags; 2163 }; 2164 2165 auto const barrier = vk::ImageMemoryBarrier() 2166 .setSrcAccessMask(srcAccessMask) 2167 .setDstAccessMask(DstAccessMask(newLayout)) 2168 .setOldLayout(oldLayout) 2169 .setNewLayout(newLayout) 2170 .setSrcQueueFamilyIndex(0) 2171 .setDstQueueFamilyIndex(0) 2172 .setImage(image) 2173 .setSubresourceRange(vk::ImageSubresourceRange( 2174 aspectMask, 0, 1, 0, 1)); 2175 2176 cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, 2177 vk::PipelineStageFlagBits::eTopOfPipe, 2178 vk::DependencyFlagBits(), 0, nullptr, 0, nullptr, 1, 2179 &barrier); 2180 } 2181 2182 void update_data_buffer() { 2183 mat4x4 VP; 2184 mat4x4_mul(VP, projection_matrix, view_matrix); 2185 2186 // Rotate 22.5 degrees around the Y axis 2187 mat4x4 Model; 2188 mat4x4_dup(Model, model_matrix); 2189 mat4x4_rotate(model_matrix, Model, 0.0f, 1.0f, 0.0f, 2190 (float)degreesToRadians(spin_angle)); 2191 2192 mat4x4 MVP; 2193 mat4x4_mul(MVP, VP, model_matrix); 2194 2195 auto data = device.mapMemory(uniform_data.mem, 0, 2196 uniform_data.mem_alloc.allocationSize, 2197 vk::MemoryMapFlags()); 2198 VERIFY(data.result == vk::Result::eSuccess); 2199 2200 memcpy(data.value, (const void *)&MVP[0][0], sizeof(MVP)); 2201 2202 device.unmapMemory(uniform_data.mem); 2203 } 2204 2205 bool loadTexture(const char *filename, uint8_t *rgba_data, 2206 vk::SubresourceLayout *layout, int32_t *width, 2207 int32_t *height) { 2208 FILE *fPtr = fopen(filename, "rb"); 2209 if (!fPtr) { 2210 return false; 2211 } 2212 2213 char header[256]; 2214 char *cPtr = fgets(header, 256, fPtr); // P6 2215 if (cPtr == nullptr || strncmp(header, "P6\n", 3)) { 2216 fclose(fPtr); 2217 return false; 2218 } 2219 2220 do { 2221 cPtr = fgets(header, 256, fPtr); 2222 if (cPtr == nullptr) { 2223 fclose(fPtr); 2224 return false; 2225 } 2226 } while (!strncmp(header, "#", 1)); 2227 2228 sscanf(header, "%u %u", width, height); 2229 if (rgba_data == nullptr) { 2230 fclose(fPtr); 2231 return true; 2232 } 2233 2234 char *result = fgets(header, 256, fPtr); // Format 2235 VERIFY(result != nullptr); 2236 if (cPtr == nullptr || strncmp(header, "255\n", 3)) { 2237 fclose(fPtr); 2238 return false; 2239 } 2240 2241 for (int y = 0; y < *height; y++) { 2242 uint8_t *rowPtr = rgba_data; 2243 2244 for (int x = 0; x < *width; x++) { 2245 size_t s = fread(rowPtr, 3, 1, fPtr); 2246 (void)s; 2247 rowPtr[3] = 255; /* Alpha of 1 */ 2248 rowPtr += 4; 2249 } 2250 2251 rgba_data += layout->rowPitch; 2252 } 2253 2254 fclose(fPtr); 2255 return true; 2256 } 2257 2258 bool memory_type_from_properties(uint32_t typeBits, 2259 vk::MemoryPropertyFlags requirements_mask, 2260 uint32_t *typeIndex) { 2261 // Search memtypes to find first index with those properties 2262 for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) { 2263 if ((typeBits & 1) == 1) { 2264 // Type is available, does it match user properties? 2265 if ((memory_properties.memoryTypes[i].propertyFlags & 2266 requirements_mask) == requirements_mask) { 2267 *typeIndex = i; 2268 return true; 2269 } 2270 } 2271 typeBits >>= 1; 2272 } 2273 2274 // No memory types matched, return failure 2275 return false; 2276 } 2277 2278 #if defined(VK_USE_PLATFORM_WIN32_KHR) 2279 void run() { 2280 if (!prepared) { 2281 return; 2282 } 2283 2284 update_data_buffer(); 2285 draw(); 2286 curFrame++; 2287 2288 if (frameCount != INT_MAX && curFrame == frameCount) { 2289 PostQuitMessage(validation_error); 2290 } 2291 } 2292 2293 void create_window() { 2294 WNDCLASSEX win_class; 2295 2296 // Initialize the window class structure: 2297 win_class.cbSize = sizeof(WNDCLASSEX); 2298 win_class.style = CS_HREDRAW | CS_VREDRAW; 2299 win_class.lpfnWndProc = WndProc; 2300 win_class.cbClsExtra = 0; 2301 win_class.cbWndExtra = 0; 2302 win_class.hInstance = connection; // hInstance 2303 win_class.hIcon = LoadIcon(nullptr, IDI_APPLICATION); 2304 win_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 2305 win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 2306 win_class.lpszMenuName = nullptr; 2307 win_class.lpszClassName = name; 2308 win_class.hIconSm = LoadIcon(nullptr, IDI_WINLOGO); 2309 2310 // Register window class: 2311 if (!RegisterClassEx(&win_class)) { 2312 // It didn't work, so try to give a useful error: 2313 printf("Unexpected error trying to start the application!\n"); 2314 fflush(stdout); 2315 exit(1); 2316 } 2317 2318 // Create window with the registered class: 2319 RECT wr = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)}; 2320 AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); 2321 window = CreateWindowEx(0, 2322 name, // class name 2323 name, // app name 2324 WS_OVERLAPPEDWINDOW | // window style 2325 WS_VISIBLE | WS_SYSMENU, 2326 100, 100, // x/y coords 2327 wr.right - wr.left, // width 2328 wr.bottom - wr.top, // height 2329 nullptr, // handle to parent 2330 nullptr, // handle to menu 2331 connection, // hInstance 2332 nullptr); // no extra parameters 2333 2334 if (!window) { 2335 // It didn't work, so try to give a useful error: 2336 printf("Cannot create a window in which to draw!\n"); 2337 fflush(stdout); 2338 exit(1); 2339 } 2340 2341 // Window client area size must be at least 1 pixel high, to prevent 2342 // crash. 2343 minsize.x = GetSystemMetrics(SM_CXMINTRACK); 2344 minsize.y = GetSystemMetrics(SM_CYMINTRACK) + 1; 2345 } 2346 2347 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) 2348 #if defined(VK_USE_PLATFORM_XLIB_KHR) 2349 2350 void create_xlib_window() { 2351 display = XOpenDisplay(nullptr); 2352 long visualMask = VisualScreenMask; 2353 int numberOfVisuals; 2354 XVisualInfo vInfoTemplate = {}; 2355 vInfoTemplate.screen = DefaultScreen(display); 2356 XVisualInfo *visualInfo = XGetVisualInfo( 2357 display, visualMask, &vInfoTemplate, &numberOfVisuals); 2358 2359 Colormap colormap = 2360 XCreateColormap(display, RootWindow(display, vInfoTemplate.screen), 2361 visualInfo->visual, AllocNone); 2362 2363 XSetWindowAttributes windowAttributes = {}; 2364 windowAttributes.colormap = colormap; 2365 windowAttributes.background_pixel = 0xFFFFFFFF; 2366 windowAttributes.border_pixel = 0; 2367 windowAttributes.event_mask = 2368 KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask; 2369 2370 xlib_window = XCreateWindow( 2371 display, RootWindow(display, vInfoTemplate.screen), 0, 0, width, 2372 height, 0, visualInfo->depth, InputOutput, visualInfo->visual, 2373 CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, 2374 &windowAttributes); 2375 2376 XSelectInput(display, xlib_window, ExposureMask | KeyPressMask); 2377 XMapWindow(display, xlib_window); 2378 XFlush(display); 2379 xlib_wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); 2380 } 2381 2382 void handle_xlib_event(const XEvent *event) { 2383 switch (event->type) { 2384 case ClientMessage: 2385 if ((Atom)event->xclient.data.l[0] == xlib_wm_delete_window) { 2386 quit = true; 2387 } 2388 break; 2389 case KeyPress: 2390 switch (event->xkey.keycode) { 2391 case 0x9: // Escape 2392 quit = true; 2393 break; 2394 case 0x71: // left arrow key 2395 spin_angle += spin_increment; 2396 break; 2397 case 0x72: // right arrow key 2398 spin_angle -= spin_increment; 2399 break; 2400 case 0x41: 2401 pause = !pause; 2402 break; 2403 } 2404 break; 2405 case ConfigureNotify: 2406 if (((int32_t)width != event->xconfigure.width) || 2407 ((int32_t)height != event->xconfigure.height)) { 2408 width = event->xconfigure.width; 2409 height = event->xconfigure.height; 2410 resize(); 2411 } 2412 break; 2413 default: 2414 break; 2415 } 2416 } 2417 2418 void run_xlib() { 2419 while (!quit) { 2420 XEvent event; 2421 2422 if (pause) { 2423 XNextEvent(display, &event); 2424 handle_xlib_event(&event); 2425 } else { 2426 while (XPending(display) > 0) { 2427 XNextEvent(display, &event); 2428 handle_xlib_event(&event); 2429 } 2430 } 2431 2432 update_data_buffer(); 2433 draw(); 2434 curFrame++; 2435 2436 if (frameCount != UINT32_MAX && curFrame == frameCount) { 2437 quit = true; 2438 } 2439 } 2440 } 2441 2442 #endif 2443 #if defined(VK_USE_PLATFORM_XCB_KHR) 2444 2445 void handle_xcb_event(const xcb_generic_event_t *event) { 2446 uint8_t event_code = event->response_type & 0x7f; 2447 switch (event_code) { 2448 case XCB_EXPOSE: 2449 // TODO: Resize window 2450 break; 2451 case XCB_CLIENT_MESSAGE: 2452 if ((*(xcb_client_message_event_t *)event).data.data32[0] == 2453 (*atom_wm_delete_window).atom) { 2454 quit = true; 2455 } 2456 break; 2457 case XCB_KEY_RELEASE: { 2458 const xcb_key_release_event_t *key = 2459 (const xcb_key_release_event_t *)event; 2460 2461 switch (key->detail) { 2462 case 0x9: // Escape 2463 quit = true; 2464 break; 2465 case 0x71: // left arrow key 2466 spin_angle += spin_increment; 2467 break; 2468 case 0x72: // right arrow key 2469 spin_angle -= spin_increment; 2470 break; 2471 case 0x41: 2472 pause = !pause; 2473 break; 2474 } 2475 } break; 2476 case XCB_CONFIGURE_NOTIFY: { 2477 const xcb_configure_notify_event_t *cfg = 2478 (const xcb_configure_notify_event_t *)event; 2479 if ((width != cfg->width) || (height != cfg->height)) { 2480 width = cfg->width; 2481 height = cfg->height; 2482 resize(); 2483 } 2484 } break; 2485 default: 2486 break; 2487 } 2488 } 2489 2490 void run_xcb() { 2491 xcb_flush(connection); 2492 2493 while (!quit) { 2494 xcb_generic_event_t *event; 2495 2496 if (pause) { 2497 event = xcb_wait_for_event(connection); 2498 } else { 2499 event = xcb_poll_for_event(connection); 2500 while (event) { 2501 handle_xcb_event(event); 2502 free(event); 2503 event = xcb_poll_for_event(connection); 2504 } 2505 } 2506 2507 update_data_buffer(); 2508 draw(); 2509 curFrame++; 2510 if (frameCount != UINT32_MAX && curFrame == frameCount) { 2511 quit = true; 2512 } 2513 } 2514 } 2515 2516 void create_xcb_window() { 2517 uint32_t value_mask, value_list[32]; 2518 2519 xcb_window = xcb_generate_id(connection); 2520 2521 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; 2522 value_list[0] = screen->black_pixel; 2523 value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | 2524 XCB_EVENT_MASK_STRUCTURE_NOTIFY; 2525 2526 xcb_create_window(connection, XCB_COPY_FROM_PARENT, xcb_window, 2527 screen->root, 0, 0, width, height, 0, 2528 XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, 2529 value_mask, value_list); 2530 2531 /* Magic code that will send notification when window is destroyed */ 2532 xcb_intern_atom_cookie_t cookie = 2533 xcb_intern_atom(connection, 1, 12, "WM_PROTOCOLS"); 2534 xcb_intern_atom_reply_t *reply = 2535 xcb_intern_atom_reply(connection, cookie, 0); 2536 2537 xcb_intern_atom_cookie_t cookie2 = 2538 xcb_intern_atom(connection, 0, 16, "WM_DELETE_WINDOW"); 2539 atom_wm_delete_window = xcb_intern_atom_reply(connection, cookie2, 0); 2540 2541 xcb_change_property(connection, XCB_PROP_MODE_REPLACE, xcb_window, 2542 (*reply).atom, 4, 32, 1, 2543 &(*atom_wm_delete_window).atom); 2544 2545 free(reply); 2546 2547 xcb_map_window(connection, xcb_window); 2548 2549 // Force the x/y coordinates to 100,100 results are identical in 2550 // consecutive 2551 // runs 2552 const uint32_t coords[] = {100, 100}; 2553 xcb_configure_window(connection, xcb_window, 2554 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords); 2555 } 2556 2557 #endif 2558 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 2559 2560 void run() { 2561 while (!quit) { 2562 update_data_buffer(); 2563 draw(); 2564 curFrame++; 2565 if (frameCount != UINT32_MAX && curFrame == frameCount) { 2566 quit = true; 2567 } 2568 } 2569 } 2570 2571 void create_window() { 2572 window = wl_compositor_create_surface(compositor); 2573 if (!window) { 2574 printf("Can not create wayland_surface from compositor!\n"); 2575 fflush(stdout); 2576 exit(1); 2577 } 2578 2579 shell_surface = wl_shell_get_shell_surface(shell, window); 2580 if (!shell_surface) { 2581 printf("Can not get shell_surface from wayland_surface!\n"); 2582 fflush(stdout); 2583 exit(1); 2584 } 2585 2586 wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, 2587 this); 2588 wl_shell_surface_set_toplevel(shell_surface); 2589 wl_shell_surface_set_title(shell_surface, APP_SHORT_NAME); 2590 } 2591 2592 #endif 2593 2594 #if defined(VK_USE_PLATFORM_WIN32_KHR) 2595 HINSTANCE connection; // hInstance - Windows Instance 2596 HWND window; // hWnd - window handle 2597 POINT minsize; // minimum window size 2598 char name[APP_NAME_STR_LEN]; // Name to put on the window/icon 2599 #elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) 2600 Display *display; 2601 Window xlib_window; 2602 Atom xlib_wm_delete_window; 2603 2604 xcb_connection_t *connection; 2605 xcb_screen_t *screen; 2606 xcb_window_t xcb_window; 2607 xcb_intern_atom_reply_t *atom_wm_delete_window; 2608 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 2609 wl_display *display; 2610 wl_registry *registry; 2611 wl_compositor *compositor; 2612 wl_surface *window; 2613 wl_shell *shell; 2614 wl_shell_surface *shell_surface; 2615 #endif 2616 2617 vk::SurfaceKHR surface; 2618 bool prepared; 2619 bool use_staging_buffer; 2620 bool use_xlib; 2621 bool separate_present_queue; 2622 2623 vk::Instance inst; 2624 vk::PhysicalDevice gpu; 2625 vk::Device device; 2626 vk::Queue graphics_queue; 2627 vk::Queue present_queue; 2628 uint32_t graphics_queue_family_index; 2629 uint32_t present_queue_family_index; 2630 vk::Semaphore image_acquired_semaphores[FRAME_LAG]; 2631 vk::Semaphore draw_complete_semaphores[FRAME_LAG]; 2632 vk::Semaphore image_ownership_semaphores[FRAME_LAG]; 2633 vk::PhysicalDeviceProperties gpu_props; 2634 std::unique_ptr<vk::QueueFamilyProperties[]> queue_props; 2635 vk::PhysicalDeviceMemoryProperties memory_properties; 2636 2637 uint32_t enabled_extension_count; 2638 uint32_t enabled_layer_count; 2639 char const *extension_names[64]; 2640 char const *enabled_layers[64]; 2641 2642 uint32_t width; 2643 uint32_t height; 2644 vk::Format format; 2645 vk::ColorSpaceKHR color_space; 2646 2647 uint32_t swapchainImageCount; 2648 vk::SwapchainKHR swapchain; 2649 std::unique_ptr<SwapchainBuffers[]> buffers; 2650 vk::Fence fences[FRAME_LAG]; 2651 bool fencesInited[FRAME_LAG]; 2652 uint32_t frame_index; 2653 2654 vk::CommandPool cmd_pool; 2655 vk::CommandPool present_cmd_pool; 2656 2657 struct { 2658 vk::Format format; 2659 vk::Image image; 2660 vk::MemoryAllocateInfo mem_alloc; 2661 vk::DeviceMemory mem; 2662 vk::ImageView view; 2663 } depth; 2664 2665 static int32_t const texture_count = 1; 2666 texture_object textures[texture_count]; 2667 2668 struct { 2669 vk::Buffer buf; 2670 vk::MemoryAllocateInfo mem_alloc; 2671 vk::DeviceMemory mem; 2672 vk::DescriptorBufferInfo buffer_info; 2673 } uniform_data; 2674 2675 vk::CommandBuffer cmd; // Buffer for initialization commands 2676 vk::PipelineLayout pipeline_layout; 2677 vk::DescriptorSetLayout desc_layout; 2678 vk::PipelineCache pipelineCache; 2679 vk::RenderPass render_pass; 2680 vk::Pipeline pipeline; 2681 2682 mat4x4 projection_matrix; 2683 mat4x4 view_matrix; 2684 mat4x4 model_matrix; 2685 2686 float spin_angle; 2687 float spin_increment; 2688 bool pause; 2689 2690 vk::ShaderModule vert_shader_module; 2691 vk::ShaderModule frag_shader_module; 2692 2693 vk::DescriptorPool desc_pool; 2694 vk::DescriptorSet desc_set; 2695 2696 std::unique_ptr<vk::Framebuffer[]> framebuffers; 2697 2698 bool quit; 2699 uint32_t curFrame; 2700 uint32_t frameCount; 2701 bool validate; 2702 bool use_break; 2703 bool suppress_popups; 2704 2705 uint32_t current_buffer; 2706 uint32_t queue_family_count; 2707 }; 2708 2709 #if _WIN32 2710 // Include header required for parsing the command line options. 2711 #include <shellapi.h> 2712 2713 Demo demo; 2714 2715 // MS-Windows event handling function: 2716 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 2717 switch (uMsg) { 2718 case WM_CLOSE: 2719 PostQuitMessage(validation_error); 2720 break; 2721 case WM_PAINT: 2722 demo.run(); 2723 break; 2724 case WM_GETMINMAXINFO: // set window's minimum size 2725 ((MINMAXINFO *)lParam)->ptMinTrackSize = demo.minsize; 2726 return 0; 2727 case WM_SIZE: 2728 // Resize the application to the new window size, except when 2729 // it was minimized. Vulkan doesn't support images or swapchains 2730 // with width=0 and height=0. 2731 if (wParam != SIZE_MINIMIZED) { 2732 demo.width = lParam & 0xffff; 2733 demo.height = (lParam & 0xffff0000) >> 16; 2734 demo.resize(); 2735 } 2736 break; 2737 default: 2738 break; 2739 } 2740 2741 return (DefWindowProc(hWnd, uMsg, wParam, lParam)); 2742 } 2743 2744 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, 2745 int nCmdShow) { 2746 // TODO: Gah.. refactor. This isn't 1989. 2747 MSG msg; // message 2748 bool done; // flag saying when app is complete 2749 int argc; 2750 char **argv; 2751 2752 // Use the CommandLine functions to get the command line arguments. 2753 // Unfortunately, Microsoft outputs 2754 // this information as wide characters for Unicode, and we simply want the 2755 // Ascii version to be compatible 2756 // with the non-Windows side. So, we have to convert the information to 2757 // Ascii character strings. 2758 LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc); 2759 if (nullptr == commandLineArgs) { 2760 argc = 0; 2761 } 2762 2763 if (argc > 0) { 2764 argv = (char **)malloc(sizeof(char *) * argc); 2765 if (argv == nullptr) { 2766 argc = 0; 2767 } else { 2768 for (int iii = 0; iii < argc; iii++) { 2769 size_t wideCharLen = wcslen(commandLineArgs[iii]); 2770 size_t numConverted = 0; 2771 2772 argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1)); 2773 if (argv[iii] != nullptr) { 2774 wcstombs_s(&numConverted, argv[iii], wideCharLen + 1, 2775 commandLineArgs[iii], wideCharLen + 1); 2776 } 2777 } 2778 } 2779 } else { 2780 argv = nullptr; 2781 } 2782 2783 demo.init(argc, argv); 2784 2785 // Free up the items we had to allocate for the command line arguments. 2786 if (argc > 0 && argv != nullptr) { 2787 for (int iii = 0; iii < argc; iii++) { 2788 if (argv[iii] != nullptr) { 2789 free(argv[iii]); 2790 } 2791 } 2792 free(argv); 2793 } 2794 2795 demo.connection = hInstance; 2796 strncpy(demo.name, "cube", APP_NAME_STR_LEN); 2797 demo.create_window(); 2798 demo.init_vk_swapchain(); 2799 2800 demo.prepare(); 2801 2802 done = false; // initialize loop condition variable 2803 2804 // main message loop 2805 while (!done) { 2806 PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE); 2807 if (msg.message == WM_QUIT) // check for a quit message 2808 { 2809 done = true; // if found, quit app 2810 } else { 2811 /* Translate and dispatch to event queue*/ 2812 TranslateMessage(&msg); 2813 DispatchMessage(&msg); 2814 } 2815 RedrawWindow(demo.window, nullptr, nullptr, RDW_INTERNALPAINT); 2816 } 2817 2818 demo.cleanup(); 2819 2820 return (int)msg.wParam; 2821 } 2822 2823 #elif __linux__ 2824 2825 #if defined(VK_USE_PLATFORM_WAYLAND_KHR) 2826 static void handle_ping(void *data, wl_shell_surface *shell_surface, 2827 uint32_t serial) { 2828 wl_shell_surface_pong(shell_surface, serial); 2829 } 2830 2831 static void handle_configure(void *data, 2832 wl_shell_surface *shell_surface, 2833 uint32_t edges, int32_t width, 2834 int32_t height) {} 2835 2836 static void handle_popup_done(void *data, 2837 wl_shell_surface *shell_surface) {} 2838 2839 static const wl_shell_surface_listener shell_surface_listener = { 2840 handle_ping, handle_configure, handle_popup_done}; 2841 #endif 2842 2843 int main(int argc, char **argv) { 2844 Demo demo; 2845 2846 demo.init(argc, argv); 2847 2848 #if defined(VK_USE_PLATFORM_XLIB_KHR) && defined(VK_USE_PLATFORM_XCB_KHR) 2849 if (demo.use_xlib) { 2850 demo.create_xlib_window(); 2851 } else { 2852 demo.create_xcb_window(); 2853 #elif defined(VK_USE_PLATFORM_XCB_KHR) 2854 demo.create_xcb_window(); 2855 #elif defined(VK_USE_PLATFORM_XLIB_KHR) 2856 demo.create_xlib_window(); 2857 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 2858 demo.create_window(); 2859 #endif 2860 } 2861 2862 demo.init_vk_swapchain(); 2863 2864 demo.prepare(); 2865 2866 #if defined(VK_USE_PLATFORM_XLIB_KHR) && defined(VK_USE_PLATFORM_XCB_KHR) 2867 if (demo.use_xlib) { 2868 demo.run_xlib(); 2869 } else { 2870 demo.run_xcb(); 2871 #elif defined(VK_USE_PLATFORM_XCB_KHR) 2872 demo.run_xcb(); 2873 #elif defined(VK_USE_PLATFORM_XLIB_KHR) 2874 demo.run_xlib(); 2875 #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) 2876 demo.run(); 2877 #endif 2878 } 2879 2880 demo.cleanup(); 2881 2882 return validation_error; 2883 } 2884 2885 #else 2886 #error "Platform not supported" 2887 #endif 2888