Home | History | Annotate | Download | only in smoke
      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