1/* 2 * Copyright (C) 2016 Google, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included 12 * in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23#include <cassert> 24#include <array> 25#include <iostream> 26#include <string> 27#include <sstream> 28#include <set> 29#include "Helpers.h" 30#include "Shell.h" 31#include "Game.h" 32 33Shell::Shell(Game &game) 34 : game_(game), settings_(game.settings()), ctx_(), 35 game_tick_(1.0f / settings_.ticks_per_second), game_time_(game_tick_) 36{ 37 // require generic WSI extensions 38 instance_extensions_.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 39 device_extensions_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 40 41 // require "standard" validation layers 42 if (settings_.validate) { 43 device_layers_.push_back("VK_LAYER_LUNARG_standard_validation"); 44 instance_layers_.push_back("VK_LAYER_LUNARG_standard_validation"); 45 46 instance_extensions_.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 47 } 48} 49 50void Shell::log(LogPriority priority, const char *msg) 51{ 52 std::ostream &st = (priority >= LOG_ERR) ? std::cerr : std::cout; 53 st << msg << "\n"; 54} 55 56void Shell::init_vk() 57{ 58 vk::init_dispatch_table_top(load_vk()); 59 60 init_instance(); 61 vk::init_dispatch_table_middle(ctx_.instance, false); 62 63 init_debug_report(); 64 init_physical_dev(); 65} 66 67void Shell::cleanup_vk() 68{ 69 if (settings_.validate) 70 vk::DestroyDebugReportCallbackEXT(ctx_.instance, ctx_.debug_report, nullptr); 71 72 vk::DestroyInstance(ctx_.instance, nullptr); 73} 74 75bool Shell::debug_report_callback(VkDebugReportFlagsEXT flags, 76 VkDebugReportObjectTypeEXT obj_type, 77 uint64_t object, 78 size_t location, 79 int32_t msg_code, 80 const char *layer_prefix, 81 const char *msg) 82{ 83 LogPriority prio = LOG_WARN; 84 if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) 85 prio = LOG_ERR; 86 else if (flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)) 87 prio = LOG_WARN; 88 else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) 89 prio = LOG_INFO; 90 else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) 91 prio = LOG_DEBUG; 92 93 std::stringstream ss; 94 ss << layer_prefix << ": " << msg; 95 96 log(prio, ss.str().c_str()); 97 98 return false; 99} 100 101void Shell::assert_all_instance_layers() const 102{ 103 // enumerate instance layer 104 std::vector<VkLayerProperties> layers; 105 vk::enumerate(layers); 106 107 std::set<std::string> layer_names; 108 for (const auto &layer : layers) 109 layer_names.insert(layer.layerName); 110 111 // all listed instance layers are required 112 for (const auto &name : instance_layers_) { 113 if (layer_names.find(name) == layer_names.end()) { 114 std::stringstream ss; 115 ss << "instance layer " << name << " is missing"; 116 throw std::runtime_error(ss.str()); 117 } 118 } 119} 120 121void Shell::assert_all_instance_extensions() const 122{ 123 // enumerate instance extensions 124 std::vector<VkExtensionProperties> exts; 125 vk::enumerate(nullptr, exts); 126 127 std::set<std::string> ext_names; 128 for (const auto &ext : exts) 129 ext_names.insert(ext.extensionName); 130 131 // all listed instance extensions are required 132 for (const auto &name : instance_extensions_) { 133 if (ext_names.find(name) == ext_names.end()) { 134 std::stringstream ss; 135 ss << "instance extension " << name << " is missing"; 136 throw std::runtime_error(ss.str()); 137 } 138 } 139} 140 141bool Shell::has_all_device_layers(VkPhysicalDevice phy) const 142{ 143 // enumerate device layers 144 std::vector<VkLayerProperties> layers; 145 vk::enumerate(phy, layers); 146 147 std::set<std::string> layer_names; 148 for (const auto &layer : layers) 149 layer_names.insert(layer.layerName); 150 151 // all listed device layers are required 152 for (const auto &name : device_layers_) { 153 if (layer_names.find(name) == layer_names.end()) 154 return false; 155 } 156 157 return true; 158} 159 160bool Shell::has_all_device_extensions(VkPhysicalDevice phy) const 161{ 162 // enumerate device extensions 163 std::vector<VkExtensionProperties> exts; 164 vk::enumerate(phy, nullptr, exts); 165 166 std::set<std::string> ext_names; 167 for (const auto &ext : exts) 168 ext_names.insert(ext.extensionName); 169 170 // all listed device extensions are required 171 for (const auto &name : device_extensions_) { 172 if (ext_names.find(name) == ext_names.end()) 173 return false; 174 } 175 176 return true; 177} 178 179void Shell::init_instance() 180{ 181 assert_all_instance_layers(); 182 assert_all_instance_extensions(); 183 184 VkApplicationInfo app_info = {}; 185 app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 186 app_info.pApplicationName = settings_.name.c_str(); 187 app_info.applicationVersion = 0; 188 app_info.apiVersion = VK_API_VERSION_1_0; 189 190 VkInstanceCreateInfo instance_info = {}; 191 instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 192 instance_info.pApplicationInfo = &app_info; 193 instance_info.enabledLayerCount = static_cast<uint32_t>(instance_layers_.size()); 194 instance_info.ppEnabledLayerNames = instance_layers_.data(); 195 instance_info.enabledExtensionCount = static_cast<uint32_t>(instance_extensions_.size()); 196 instance_info.ppEnabledExtensionNames = instance_extensions_.data(); 197 198 vk::assert_success(vk::CreateInstance(&instance_info, nullptr, &ctx_.instance)); 199} 200 201void Shell::init_debug_report() 202{ 203 if (!settings_.validate) 204 return; 205 206 VkDebugReportCallbackCreateInfoEXT debug_report_info = {}; 207 debug_report_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; 208 209 debug_report_info.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | 210 VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | 211 VK_DEBUG_REPORT_ERROR_BIT_EXT; 212 if (settings_.validate_verbose) { 213 debug_report_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | 214 VK_DEBUG_REPORT_DEBUG_BIT_EXT; 215 } 216 217 debug_report_info.pfnCallback = debug_report_callback; 218 debug_report_info.pUserData = reinterpret_cast<void *>(this); 219 220 vk::assert_success(vk::CreateDebugReportCallbackEXT(ctx_.instance, 221 &debug_report_info, nullptr, &ctx_.debug_report)); 222} 223 224void Shell::init_physical_dev() 225{ 226 // enumerate physical devices 227 std::vector<VkPhysicalDevice> phys; 228 vk::assert_success(vk::enumerate(ctx_.instance, phys)); 229 230 ctx_.physical_dev = VK_NULL_HANDLE; 231 for (auto phy : phys) { 232 if (!has_all_device_layers(phy) || !has_all_device_extensions(phy)) 233 continue; 234 235 // get queue properties 236 std::vector<VkQueueFamilyProperties> queues; 237 vk::get(phy, queues); 238 239 int game_queue_family = -1, present_queue_family = -1; 240 for (uint32_t i = 0; i < queues.size(); i++) { 241 const VkQueueFamilyProperties &q = queues[i]; 242 243 // requires only GRAPHICS for game queues 244 const VkFlags game_queue_flags = VK_QUEUE_GRAPHICS_BIT; 245 if (game_queue_family < 0 && 246 (q.queueFlags & game_queue_flags) == game_queue_flags) 247 game_queue_family = i; 248 249 // present queue must support the surface 250 if (present_queue_family < 0 && can_present(phy, i)) 251 present_queue_family = i; 252 253 if (game_queue_family >= 0 && present_queue_family >= 0) 254 break; 255 } 256 257 if (game_queue_family >= 0 && present_queue_family >= 0) { 258 ctx_.physical_dev = phy; 259 ctx_.game_queue_family = game_queue_family; 260 ctx_.present_queue_family = present_queue_family; 261 break; 262 } 263 } 264 265 if (ctx_.physical_dev == VK_NULL_HANDLE) 266 throw std::runtime_error("failed to find any capable Vulkan physical device"); 267} 268 269void Shell::create_context() 270{ 271 create_dev(); 272 vk::init_dispatch_table_bottom(ctx_.instance, ctx_.dev); 273 274 vk::GetDeviceQueue(ctx_.dev, ctx_.game_queue_family, 0, &ctx_.game_queue); 275 vk::GetDeviceQueue(ctx_.dev, ctx_.present_queue_family, 0, &ctx_.present_queue); 276 277 create_back_buffers(); 278 279 // initialize ctx_.{surface,format} before attach_shell 280 create_swapchain(); 281 282 game_.attach_shell(*this); 283} 284 285void Shell::destroy_context() 286{ 287 if (ctx_.dev == VK_NULL_HANDLE) 288 return; 289 290 vk::DeviceWaitIdle(ctx_.dev); 291 292 destroy_swapchain(); 293 294 game_.detach_shell(); 295 296 destroy_back_buffers(); 297 298 ctx_.game_queue = VK_NULL_HANDLE; 299 ctx_.present_queue = VK_NULL_HANDLE; 300 301 vk::DestroyDevice(ctx_.dev, nullptr); 302 ctx_.dev = VK_NULL_HANDLE; 303} 304 305void Shell::create_dev() 306{ 307 VkDeviceCreateInfo dev_info = {}; 308 dev_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 309 310 const std::vector<float> queue_priorities(settings_.queue_count, 0.0f); 311 std::array<VkDeviceQueueCreateInfo, 2> queue_info = {}; 312 queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 313 queue_info[0].queueFamilyIndex = ctx_.game_queue_family; 314 queue_info[0].queueCount = settings_.queue_count; 315 queue_info[0].pQueuePriorities = queue_priorities.data(); 316 317 if (ctx_.game_queue_family != ctx_.present_queue_family) { 318 queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 319 queue_info[1].queueFamilyIndex = ctx_.present_queue_family; 320 queue_info[1].queueCount = 1; 321 queue_info[1].pQueuePriorities = queue_priorities.data(); 322 323 dev_info.queueCreateInfoCount = 2; 324 } else { 325 dev_info.queueCreateInfoCount = 1; 326 } 327 328 dev_info.pQueueCreateInfos = queue_info.data(); 329 330 dev_info.enabledLayerCount = static_cast<uint32_t>(device_layers_.size()); 331 dev_info.ppEnabledLayerNames = device_layers_.data(); 332 dev_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions_.size()); 333 dev_info.ppEnabledExtensionNames = device_extensions_.data(); 334 335 // disable all features 336 VkPhysicalDeviceFeatures features = {}; 337 dev_info.pEnabledFeatures = &features; 338 339 vk::assert_success(vk::CreateDevice(ctx_.physical_dev, &dev_info, nullptr, &ctx_.dev)); 340} 341 342void Shell::create_back_buffers() 343{ 344 VkSemaphoreCreateInfo sem_info = {}; 345 sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 346 347 VkFenceCreateInfo fence_info = {}; 348 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 349 fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; 350 351 // BackBuffer is used to track which swapchain image and its associated 352 // sync primitives are busy. Having more BackBuffer's than swapchain 353 // images may allows us to replace CPU wait on present_fence by GPU wait 354 // on acquire_semaphore. 355 const int count = settings_.back_buffer_count + 1; 356 for (int i = 0; i < count; i++) { 357 BackBuffer buf = {}; 358 vk::assert_success(vk::CreateSemaphore(ctx_.dev, &sem_info, nullptr, &buf.acquire_semaphore)); 359 vk::assert_success(vk::CreateSemaphore(ctx_.dev, &sem_info, nullptr, &buf.render_semaphore)); 360 vk::assert_success(vk::CreateFence(ctx_.dev, &fence_info, nullptr, &buf.present_fence)); 361 362 ctx_.back_buffers.push(buf); 363 } 364} 365 366void Shell::destroy_back_buffers() 367{ 368 while (!ctx_.back_buffers.empty()) { 369 const auto &buf = ctx_.back_buffers.front(); 370 371 vk::DestroySemaphore(ctx_.dev, buf.acquire_semaphore, nullptr); 372 vk::DestroySemaphore(ctx_.dev, buf.render_semaphore, nullptr); 373 vk::DestroyFence(ctx_.dev, buf.present_fence, nullptr); 374 375 ctx_.back_buffers.pop(); 376 } 377} 378 379void Shell::create_swapchain() 380{ 381 ctx_.surface = create_surface(ctx_.instance); 382 383 VkBool32 supported; 384 vk::assert_success(vk::GetPhysicalDeviceSurfaceSupportKHR(ctx_.physical_dev, 385 ctx_.present_queue_family, ctx_.surface, &supported)); 386 // this should be guaranteed by the platform-specific can_present call 387 assert(supported); 388 389 std::vector<VkSurfaceFormatKHR> formats; 390 vk::get(ctx_.physical_dev, ctx_.surface, formats); 391 ctx_.format = formats[0]; 392 393 // defer to resize_swapchain() 394 ctx_.swapchain = VK_NULL_HANDLE; 395 ctx_.extent.width = (uint32_t) -1; 396 ctx_.extent.height = (uint32_t) -1; 397} 398 399void Shell::destroy_swapchain() 400{ 401 if (ctx_.swapchain != VK_NULL_HANDLE) { 402 game_.detach_swapchain(); 403 404 vk::DestroySwapchainKHR(ctx_.dev, ctx_.swapchain, nullptr); 405 ctx_.swapchain = VK_NULL_HANDLE; 406 } 407 408 vk::DestroySurfaceKHR(ctx_.instance, ctx_.surface, nullptr); 409 ctx_.surface = VK_NULL_HANDLE; 410} 411 412void Shell::resize_swapchain(uint32_t width_hint, uint32_t height_hint) 413{ 414 VkSurfaceCapabilitiesKHR caps; 415 vk::assert_success(vk::GetPhysicalDeviceSurfaceCapabilitiesKHR(ctx_.physical_dev, 416 ctx_.surface, &caps)); 417 418 VkExtent2D extent = caps.currentExtent; 419 // use the hints 420 if (extent.width == (uint32_t) -1) { 421 extent.width = width_hint; 422 extent.height = height_hint; 423 } 424 // clamp width; to protect us from broken hints? 425 if (extent.width < caps.minImageExtent.width) 426 extent.width = caps.minImageExtent.width; 427 else if (extent.width > caps.maxImageExtent.width) 428 extent.width = caps.maxImageExtent.width; 429 // clamp height 430 if (extent.height < caps.minImageExtent.height) 431 extent.height = caps.minImageExtent.height; 432 else if (extent.height > caps.maxImageExtent.height) 433 extent.height = caps.maxImageExtent.height; 434 435 if (ctx_.extent.width == extent.width && ctx_.extent.height == extent.height) 436 return; 437 438 uint32_t image_count = settings_.back_buffer_count; 439 if (image_count < caps.minImageCount) 440 image_count = caps.minImageCount; 441 else if (image_count > caps.maxImageCount) 442 image_count = caps.maxImageCount; 443 444 assert(caps.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); 445 assert(caps.supportedTransforms & caps.currentTransform); 446 assert(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | 447 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)); 448 VkCompositeAlphaFlagBitsKHR composite_alpha = 449 (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ? 450 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 451 452 std::vector<VkPresentModeKHR> modes; 453 vk::get(ctx_.physical_dev, ctx_.surface, modes); 454 455 // FIFO is the only mode universally supported 456 VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; 457 for (auto m : modes) { 458 if ((settings_.vsync && m == VK_PRESENT_MODE_MAILBOX_KHR) || 459 (!settings_.vsync && m == VK_PRESENT_MODE_IMMEDIATE_KHR)) { 460 mode = m; 461 break; 462 } 463 } 464 465 VkSwapchainCreateInfoKHR swapchain_info = {}; 466 swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 467 swapchain_info.surface = ctx_.surface; 468 swapchain_info.minImageCount = image_count; 469 swapchain_info.imageFormat = ctx_.format.format; 470 swapchain_info.imageColorSpace = ctx_.format.colorSpace; 471 swapchain_info.imageExtent = extent; 472 swapchain_info.imageArrayLayers = 1; 473 swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 474 475 std::vector<uint32_t> queue_families(1, ctx_.game_queue_family); 476 if (ctx_.game_queue_family != ctx_.present_queue_family) { 477 queue_families.push_back(ctx_.present_queue_family); 478 479 swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 480 swapchain_info.queueFamilyIndexCount = (uint32_t)queue_families.size(); 481 swapchain_info.pQueueFamilyIndices = queue_families.data(); 482 } else { 483 swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 484 } 485 486 swapchain_info.preTransform = caps.currentTransform;; 487 swapchain_info.compositeAlpha = composite_alpha; 488 swapchain_info.presentMode = mode; 489 swapchain_info.clipped = true; 490 swapchain_info.oldSwapchain = ctx_.swapchain; 491 492 vk::assert_success(vk::CreateSwapchainKHR(ctx_.dev, &swapchain_info, nullptr, &ctx_.swapchain)); 493 ctx_.extent = extent; 494 495 // destroy the old swapchain 496 if (swapchain_info.oldSwapchain != VK_NULL_HANDLE) { 497 game_.detach_swapchain(); 498 499 vk::DeviceWaitIdle(ctx_.dev); 500 vk::DestroySwapchainKHR(ctx_.dev, swapchain_info.oldSwapchain, nullptr); 501 } 502 503 game_.attach_swapchain(); 504} 505 506void Shell::add_game_time(float time) 507{ 508 int max_ticks = 3; 509 510 if (!settings_.no_tick) 511 game_time_ += time; 512 513 while (game_time_ >= game_tick_ && max_ticks--) { 514 game_.on_tick(); 515 game_time_ -= game_tick_; 516 } 517} 518 519void Shell::acquire_back_buffer() 520{ 521 // acquire just once when not presenting 522 if (settings_.no_present && 523 ctx_.acquired_back_buffer.acquire_semaphore != VK_NULL_HANDLE) 524 return; 525 526 auto &buf = ctx_.back_buffers.front(); 527 528 // wait until acquire and render semaphores are waited/unsignaled 529 vk::assert_success(vk::WaitForFences(ctx_.dev, 1, &buf.present_fence, 530 true, UINT64_MAX)); 531 // reset the fence 532 vk::assert_success(vk::ResetFences(ctx_.dev, 1, &buf.present_fence)); 533 534 vk::assert_success(vk::AcquireNextImageKHR(ctx_.dev, ctx_.swapchain, 535 UINT64_MAX, buf.acquire_semaphore, VK_NULL_HANDLE, 536 &buf.image_index)); 537 538 ctx_.acquired_back_buffer = buf; 539 ctx_.back_buffers.pop(); 540} 541 542void Shell::present_back_buffer() 543{ 544 const auto &buf = ctx_.acquired_back_buffer; 545 546 if (!settings_.no_render) 547 game_.on_frame(game_time_ / game_tick_); 548 549 if (settings_.no_present) { 550 fake_present(); 551 return; 552 } 553 554 VkPresentInfoKHR present_info = {}; 555 present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; 556 present_info.waitSemaphoreCount = 1; 557 present_info.pWaitSemaphores = (settings_.no_render) ? 558 &buf.acquire_semaphore : &buf.render_semaphore; 559 present_info.swapchainCount = 1; 560 present_info.pSwapchains = &ctx_.swapchain; 561 present_info.pImageIndices = &buf.image_index; 562 563 vk::assert_success(vk::QueuePresentKHR(ctx_.present_queue, &present_info)); 564 565 vk::assert_success(vk::QueueSubmit(ctx_.present_queue, 0, nullptr, buf.present_fence)); 566 ctx_.back_buffers.push(buf); 567} 568 569void Shell::fake_present() 570{ 571 const auto &buf = ctx_.acquired_back_buffer; 572 573 assert(settings_.no_present); 574 575 // wait render semaphore and signal acquire semaphore 576 if (!settings_.no_render) { 577 VkPipelineStageFlags stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; 578 VkSubmitInfo submit_info = {}; 579 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 580 submit_info.waitSemaphoreCount = 1; 581 submit_info.pWaitSemaphores = &buf.render_semaphore; 582 submit_info.pWaitDstStageMask = &stage; 583 submit_info.signalSemaphoreCount = 1; 584 submit_info.pSignalSemaphores = &buf.acquire_semaphore; 585 vk::assert_success(vk::QueueSubmit(ctx_.game_queue, 1, &submit_info, VK_NULL_HANDLE)); 586 } 587 588 // push the buffer back just once for Shell::cleanup_vk 589 if (buf.acquire_semaphore != ctx_.back_buffers.back().acquire_semaphore) 590 ctx_.back_buffers.push(buf); 591} 592