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