12f18b292ff155af7df35930474857b507dbf18feTony Barbour/*
22f18b292ff155af7df35930474857b507dbf18feTony Barbour * Copyright (C) 2016 Google, Inc.
32f18b292ff155af7df35930474857b507dbf18feTony Barbour *
42f18b292ff155af7df35930474857b507dbf18feTony Barbour * Permission is hereby granted, free of charge, to any person obtaining a
52f18b292ff155af7df35930474857b507dbf18feTony Barbour * copy of this software and associated documentation files (the "Software"),
62f18b292ff155af7df35930474857b507dbf18feTony Barbour * to deal in the Software without restriction, including without limitation
72f18b292ff155af7df35930474857b507dbf18feTony Barbour * the rights to use, copy, modify, merge, publish, distribute, sublicense,
82f18b292ff155af7df35930474857b507dbf18feTony Barbour * and/or sell copies of the Software, and to permit persons to whom the
92f18b292ff155af7df35930474857b507dbf18feTony Barbour * Software is furnished to do so, subject to the following conditions:
102f18b292ff155af7df35930474857b507dbf18feTony Barbour *
112f18b292ff155af7df35930474857b507dbf18feTony Barbour * The above copyright notice and this permission notice shall be included
122f18b292ff155af7df35930474857b507dbf18feTony Barbour * in all copies or substantial portions of the Software.
132f18b292ff155af7df35930474857b507dbf18feTony Barbour *
142f18b292ff155af7df35930474857b507dbf18feTony Barbour * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
152f18b292ff155af7df35930474857b507dbf18feTony Barbour * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
162f18b292ff155af7df35930474857b507dbf18feTony Barbour * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
172f18b292ff155af7df35930474857b507dbf18feTony Barbour * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
182f18b292ff155af7df35930474857b507dbf18feTony Barbour * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
192f18b292ff155af7df35930474857b507dbf18feTony Barbour * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
202f18b292ff155af7df35930474857b507dbf18feTony Barbour * DEALINGS IN THE SOFTWARE.
212f18b292ff155af7df35930474857b507dbf18feTony Barbour */
222f18b292ff155af7df35930474857b507dbf18feTony Barbour
232f18b292ff155af7df35930474857b507dbf18feTony Barbour#include <cassert>
242f18b292ff155af7df35930474857b507dbf18feTony Barbour#include <array>
252f18b292ff155af7df35930474857b507dbf18feTony Barbour#include <iostream>
262f18b292ff155af7df35930474857b507dbf18feTony Barbour#include <string>
272f18b292ff155af7df35930474857b507dbf18feTony Barbour#include <sstream>
282f18b292ff155af7df35930474857b507dbf18feTony Barbour#include <set>
292f18b292ff155af7df35930474857b507dbf18feTony Barbour#include "Helpers.h"
302f18b292ff155af7df35930474857b507dbf18feTony Barbour#include "Shell.h"
312f18b292ff155af7df35930474857b507dbf18feTony Barbour#include "Game.h"
322f18b292ff155af7df35930474857b507dbf18feTony Barbour
332f18b292ff155af7df35930474857b507dbf18feTony BarbourShell::Shell(Game &game)
342f18b292ff155af7df35930474857b507dbf18feTony Barbour    : game_(game), settings_(game.settings()), ctx_(),
352f18b292ff155af7df35930474857b507dbf18feTony Barbour      game_tick_(1.0f / settings_.ticks_per_second), game_time_(game_tick_)
362f18b292ff155af7df35930474857b507dbf18feTony Barbour{
372f18b292ff155af7df35930474857b507dbf18feTony Barbour    // require generic WSI extensions
382f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_extensions_.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
392f18b292ff155af7df35930474857b507dbf18feTony Barbour    device_extensions_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
402f18b292ff155af7df35930474857b507dbf18feTony Barbour
412f18b292ff155af7df35930474857b507dbf18feTony Barbour    // require "standard" validation layers
422f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (settings_.validate) {
432f18b292ff155af7df35930474857b507dbf18feTony Barbour        device_layers_.push_back("VK_LAYER_LUNARG_standard_validation");
442f18b292ff155af7df35930474857b507dbf18feTony Barbour        instance_layers_.push_back("VK_LAYER_LUNARG_standard_validation");
452f18b292ff155af7df35930474857b507dbf18feTony Barbour
462f18b292ff155af7df35930474857b507dbf18feTony Barbour        instance_extensions_.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
472f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
482f18b292ff155af7df35930474857b507dbf18feTony Barbour}
492f18b292ff155af7df35930474857b507dbf18feTony Barbour
502f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::log(LogPriority priority, const char *msg)
512f18b292ff155af7df35930474857b507dbf18feTony Barbour{
522f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::ostream &st = (priority >= LOG_ERR) ? std::cerr : std::cout;
532f18b292ff155af7df35930474857b507dbf18feTony Barbour    st << msg << "\n";
542f18b292ff155af7df35930474857b507dbf18feTony Barbour}
552f18b292ff155af7df35930474857b507dbf18feTony Barbour
562f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::init_vk()
572f18b292ff155af7df35930474857b507dbf18feTony Barbour{
582f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::init_dispatch_table_top(load_vk());
592f18b292ff155af7df35930474857b507dbf18feTony Barbour
602f18b292ff155af7df35930474857b507dbf18feTony Barbour    init_instance();
612f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::init_dispatch_table_middle(ctx_.instance, false);
622f18b292ff155af7df35930474857b507dbf18feTony Barbour
632f18b292ff155af7df35930474857b507dbf18feTony Barbour    init_debug_report();
642f18b292ff155af7df35930474857b507dbf18feTony Barbour    init_physical_dev();
652f18b292ff155af7df35930474857b507dbf18feTony Barbour}
662f18b292ff155af7df35930474857b507dbf18feTony Barbour
672f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::cleanup_vk()
682f18b292ff155af7df35930474857b507dbf18feTony Barbour{
692f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (settings_.validate)
702f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DestroyDebugReportCallbackEXT(ctx_.instance, ctx_.debug_report, nullptr);
712f18b292ff155af7df35930474857b507dbf18feTony Barbour
722f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::DestroyInstance(ctx_.instance, nullptr);
732f18b292ff155af7df35930474857b507dbf18feTony Barbour}
742f18b292ff155af7df35930474857b507dbf18feTony Barbour
752f18b292ff155af7df35930474857b507dbf18feTony Barbourbool Shell::debug_report_callback(VkDebugReportFlagsEXT flags,
762f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  VkDebugReportObjectTypeEXT obj_type,
772f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  uint64_t object,
782f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  size_t location,
792f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  int32_t msg_code,
802f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  const char *layer_prefix,
812f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  const char *msg)
822f18b292ff155af7df35930474857b507dbf18feTony Barbour{
832f18b292ff155af7df35930474857b507dbf18feTony Barbour    LogPriority prio = LOG_WARN;
842f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
852f18b292ff155af7df35930474857b507dbf18feTony Barbour        prio = LOG_ERR;
862f18b292ff155af7df35930474857b507dbf18feTony Barbour    else if (flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT))
872f18b292ff155af7df35930474857b507dbf18feTony Barbour        prio = LOG_WARN;
882f18b292ff155af7df35930474857b507dbf18feTony Barbour    else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
892f18b292ff155af7df35930474857b507dbf18feTony Barbour        prio = LOG_INFO;
902f18b292ff155af7df35930474857b507dbf18feTony Barbour    else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
912f18b292ff155af7df35930474857b507dbf18feTony Barbour        prio = LOG_DEBUG;
922f18b292ff155af7df35930474857b507dbf18feTony Barbour
932f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::stringstream ss;
942f18b292ff155af7df35930474857b507dbf18feTony Barbour    ss << layer_prefix << ": " << msg;
952f18b292ff155af7df35930474857b507dbf18feTony Barbour
962f18b292ff155af7df35930474857b507dbf18feTony Barbour    log(prio, ss.str().c_str());
972f18b292ff155af7df35930474857b507dbf18feTony Barbour
982f18b292ff155af7df35930474857b507dbf18feTony Barbour    return false;
992f18b292ff155af7df35930474857b507dbf18feTony Barbour}
1002f18b292ff155af7df35930474857b507dbf18feTony Barbour
1012f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::assert_all_instance_layers() const
1022f18b292ff155af7df35930474857b507dbf18feTony Barbour{
1032f18b292ff155af7df35930474857b507dbf18feTony Barbour    // enumerate instance layer
1042f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkLayerProperties> layers;
1052f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::enumerate(layers);
1062f18b292ff155af7df35930474857b507dbf18feTony Barbour
1072f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::set<std::string> layer_names;
1082f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &layer : layers)
1092f18b292ff155af7df35930474857b507dbf18feTony Barbour        layer_names.insert(layer.layerName);
1102f18b292ff155af7df35930474857b507dbf18feTony Barbour
1112f18b292ff155af7df35930474857b507dbf18feTony Barbour    // all listed instance layers are required
1122f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &name : instance_layers_) {
1132f18b292ff155af7df35930474857b507dbf18feTony Barbour        if (layer_names.find(name) == layer_names.end()) {
1142f18b292ff155af7df35930474857b507dbf18feTony Barbour            std::stringstream ss;
1152f18b292ff155af7df35930474857b507dbf18feTony Barbour            ss << "instance layer " << name << " is missing";
1162f18b292ff155af7df35930474857b507dbf18feTony Barbour            throw std::runtime_error(ss.str());
1172f18b292ff155af7df35930474857b507dbf18feTony Barbour        }
1182f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
1192f18b292ff155af7df35930474857b507dbf18feTony Barbour}
1202f18b292ff155af7df35930474857b507dbf18feTony Barbour
1212f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::assert_all_instance_extensions() const
1222f18b292ff155af7df35930474857b507dbf18feTony Barbour{
1232f18b292ff155af7df35930474857b507dbf18feTony Barbour    // enumerate instance extensions
1242f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkExtensionProperties> exts;
1252f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::enumerate(nullptr, exts);
1262f18b292ff155af7df35930474857b507dbf18feTony Barbour
1272f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::set<std::string> ext_names;
1282f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &ext : exts)
1292f18b292ff155af7df35930474857b507dbf18feTony Barbour        ext_names.insert(ext.extensionName);
1302f18b292ff155af7df35930474857b507dbf18feTony Barbour
1312f18b292ff155af7df35930474857b507dbf18feTony Barbour    // all listed instance extensions are required
1322f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &name : instance_extensions_) {
1332f18b292ff155af7df35930474857b507dbf18feTony Barbour        if (ext_names.find(name) == ext_names.end()) {
1342f18b292ff155af7df35930474857b507dbf18feTony Barbour            std::stringstream ss;
1352f18b292ff155af7df35930474857b507dbf18feTony Barbour            ss << "instance extension " << name << " is missing";
1362f18b292ff155af7df35930474857b507dbf18feTony Barbour            throw std::runtime_error(ss.str());
1372f18b292ff155af7df35930474857b507dbf18feTony Barbour        }
1382f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
1392f18b292ff155af7df35930474857b507dbf18feTony Barbour}
1402f18b292ff155af7df35930474857b507dbf18feTony Barbour
1412f18b292ff155af7df35930474857b507dbf18feTony Barbourbool Shell::has_all_device_layers(VkPhysicalDevice phy) const
1422f18b292ff155af7df35930474857b507dbf18feTony Barbour{
1432f18b292ff155af7df35930474857b507dbf18feTony Barbour    // enumerate device layers
1442f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkLayerProperties> layers;
1452f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::enumerate(phy, layers);
1462f18b292ff155af7df35930474857b507dbf18feTony Barbour
1472f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::set<std::string> layer_names;
1482f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &layer : layers)
1492f18b292ff155af7df35930474857b507dbf18feTony Barbour        layer_names.insert(layer.layerName);
1502f18b292ff155af7df35930474857b507dbf18feTony Barbour
1512f18b292ff155af7df35930474857b507dbf18feTony Barbour    // all listed device layers are required
1522f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &name : device_layers_) {
1532f18b292ff155af7df35930474857b507dbf18feTony Barbour        if (layer_names.find(name) == layer_names.end())
1542f18b292ff155af7df35930474857b507dbf18feTony Barbour            return false;
1552f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
1562f18b292ff155af7df35930474857b507dbf18feTony Barbour
1572f18b292ff155af7df35930474857b507dbf18feTony Barbour    return true;
1582f18b292ff155af7df35930474857b507dbf18feTony Barbour}
1592f18b292ff155af7df35930474857b507dbf18feTony Barbour
1602f18b292ff155af7df35930474857b507dbf18feTony Barbourbool Shell::has_all_device_extensions(VkPhysicalDevice phy) const
1612f18b292ff155af7df35930474857b507dbf18feTony Barbour{
1622f18b292ff155af7df35930474857b507dbf18feTony Barbour    // enumerate device extensions
1632f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkExtensionProperties> exts;
1642f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::enumerate(phy, nullptr, exts);
1652f18b292ff155af7df35930474857b507dbf18feTony Barbour
1662f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::set<std::string> ext_names;
1672f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &ext : exts)
1682f18b292ff155af7df35930474857b507dbf18feTony Barbour        ext_names.insert(ext.extensionName);
1692f18b292ff155af7df35930474857b507dbf18feTony Barbour
1702f18b292ff155af7df35930474857b507dbf18feTony Barbour    // all listed device extensions are required
1712f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (const auto &name : device_extensions_) {
1722f18b292ff155af7df35930474857b507dbf18feTony Barbour        if (ext_names.find(name) == ext_names.end())
1732f18b292ff155af7df35930474857b507dbf18feTony Barbour            return false;
1742f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
1752f18b292ff155af7df35930474857b507dbf18feTony Barbour
1762f18b292ff155af7df35930474857b507dbf18feTony Barbour    return true;
1772f18b292ff155af7df35930474857b507dbf18feTony Barbour}
1782f18b292ff155af7df35930474857b507dbf18feTony Barbour
1792f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::init_instance()
1802f18b292ff155af7df35930474857b507dbf18feTony Barbour{
1812f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert_all_instance_layers();
1822f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert_all_instance_extensions();
1832f18b292ff155af7df35930474857b507dbf18feTony Barbour
1842f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkApplicationInfo app_info = {};
1852f18b292ff155af7df35930474857b507dbf18feTony Barbour    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1862f18b292ff155af7df35930474857b507dbf18feTony Barbour    app_info.pApplicationName = settings_.name.c_str();
1872f18b292ff155af7df35930474857b507dbf18feTony Barbour    app_info.applicationVersion = 0;
188d3995c987ba7e47700ceb54535880e782c6b04c1Jon Ashburn    app_info.apiVersion = VK_API_VERSION_1_0;
1892f18b292ff155af7df35930474857b507dbf18feTony Barbour
1902f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkInstanceCreateInfo instance_info = {};
1912f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1922f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_info.pApplicationInfo = &app_info;
1932f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_info.enabledLayerCount = static_cast<uint32_t>(instance_layers_.size());
1942f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_info.ppEnabledLayerNames = instance_layers_.data();
1952f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_info.enabledExtensionCount = static_cast<uint32_t>(instance_extensions_.size());
1962f18b292ff155af7df35930474857b507dbf18feTony Barbour    instance_info.ppEnabledExtensionNames = instance_extensions_.data();
1972f18b292ff155af7df35930474857b507dbf18feTony Barbour
1982f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::CreateInstance(&instance_info, nullptr, &ctx_.instance));
1992f18b292ff155af7df35930474857b507dbf18feTony Barbour}
2002f18b292ff155af7df35930474857b507dbf18feTony Barbour
2012f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::init_debug_report()
2022f18b292ff155af7df35930474857b507dbf18feTony Barbour{
2032f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (!settings_.validate)
2042f18b292ff155af7df35930474857b507dbf18feTony Barbour        return;
2052f18b292ff155af7df35930474857b507dbf18feTony Barbour
2062f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkDebugReportCallbackCreateInfoEXT debug_report_info = {};
2072f18b292ff155af7df35930474857b507dbf18feTony Barbour    debug_report_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
2082f18b292ff155af7df35930474857b507dbf18feTony Barbour
2092f18b292ff155af7df35930474857b507dbf18feTony Barbour    debug_report_info.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT |
2102f18b292ff155af7df35930474857b507dbf18feTony Barbour                              VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |
2112f18b292ff155af7df35930474857b507dbf18feTony Barbour                              VK_DEBUG_REPORT_ERROR_BIT_EXT;
2122f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (settings_.validate_verbose) {
2132f18b292ff155af7df35930474857b507dbf18feTony Barbour        debug_report_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
2142f18b292ff155af7df35930474857b507dbf18feTony Barbour                                  VK_DEBUG_REPORT_DEBUG_BIT_EXT;
2152f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
2162f18b292ff155af7df35930474857b507dbf18feTony Barbour
2172f18b292ff155af7df35930474857b507dbf18feTony Barbour    debug_report_info.pfnCallback = debug_report_callback;
2182f18b292ff155af7df35930474857b507dbf18feTony Barbour    debug_report_info.pUserData = reinterpret_cast<void *>(this);
2192f18b292ff155af7df35930474857b507dbf18feTony Barbour
2202f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::CreateDebugReportCallbackEXT(ctx_.instance,
2212f18b292ff155af7df35930474857b507dbf18feTony Barbour                &debug_report_info, nullptr, &ctx_.debug_report));
2222f18b292ff155af7df35930474857b507dbf18feTony Barbour}
2232f18b292ff155af7df35930474857b507dbf18feTony Barbour
2242f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::init_physical_dev()
2252f18b292ff155af7df35930474857b507dbf18feTony Barbour{
2262f18b292ff155af7df35930474857b507dbf18feTony Barbour    // enumerate physical devices
2272f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkPhysicalDevice> phys;
2282f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::enumerate(ctx_.instance, phys));
2292f18b292ff155af7df35930474857b507dbf18feTony Barbour
2302f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.physical_dev = VK_NULL_HANDLE;
2312f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (auto phy : phys) {
2322f18b292ff155af7df35930474857b507dbf18feTony Barbour        if (!has_all_device_layers(phy) || !has_all_device_extensions(phy))
2332f18b292ff155af7df35930474857b507dbf18feTony Barbour            continue;
2342f18b292ff155af7df35930474857b507dbf18feTony Barbour
2352f18b292ff155af7df35930474857b507dbf18feTony Barbour        // get queue properties
2362f18b292ff155af7df35930474857b507dbf18feTony Barbour        std::vector<VkQueueFamilyProperties> queues;
2372f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::get(phy, queues);
2382f18b292ff155af7df35930474857b507dbf18feTony Barbour
2392f18b292ff155af7df35930474857b507dbf18feTony Barbour        int game_queue_family = -1, present_queue_family = -1;
2402f18b292ff155af7df35930474857b507dbf18feTony Barbour        for (uint32_t i = 0; i < queues.size(); i++) {
2412f18b292ff155af7df35930474857b507dbf18feTony Barbour            const VkQueueFamilyProperties &q = queues[i];
2422f18b292ff155af7df35930474857b507dbf18feTony Barbour
2432f18b292ff155af7df35930474857b507dbf18feTony Barbour            // requires only GRAPHICS for game queues
2442f18b292ff155af7df35930474857b507dbf18feTony Barbour            const VkFlags game_queue_flags = VK_QUEUE_GRAPHICS_BIT;
2452f18b292ff155af7df35930474857b507dbf18feTony Barbour            if (game_queue_family < 0 &&
2462f18b292ff155af7df35930474857b507dbf18feTony Barbour                (q.queueFlags & game_queue_flags) == game_queue_flags)
2472f18b292ff155af7df35930474857b507dbf18feTony Barbour                game_queue_family = i;
2482f18b292ff155af7df35930474857b507dbf18feTony Barbour
2492f18b292ff155af7df35930474857b507dbf18feTony Barbour            // present queue must support the surface
2502f18b292ff155af7df35930474857b507dbf18feTony Barbour            if (present_queue_family < 0 && can_present(phy, i))
2512f18b292ff155af7df35930474857b507dbf18feTony Barbour                present_queue_family = i;
2522f18b292ff155af7df35930474857b507dbf18feTony Barbour
2532f18b292ff155af7df35930474857b507dbf18feTony Barbour            if (game_queue_family >= 0 && present_queue_family >= 0)
2542f18b292ff155af7df35930474857b507dbf18feTony Barbour                break;
2552f18b292ff155af7df35930474857b507dbf18feTony Barbour        }
2562f18b292ff155af7df35930474857b507dbf18feTony Barbour
2572f18b292ff155af7df35930474857b507dbf18feTony Barbour        if (game_queue_family >= 0 && present_queue_family >= 0) {
2582f18b292ff155af7df35930474857b507dbf18feTony Barbour            ctx_.physical_dev = phy;
2592f18b292ff155af7df35930474857b507dbf18feTony Barbour            ctx_.game_queue_family = game_queue_family;
2602f18b292ff155af7df35930474857b507dbf18feTony Barbour            ctx_.present_queue_family = present_queue_family;
2612f18b292ff155af7df35930474857b507dbf18feTony Barbour            break;
2622f18b292ff155af7df35930474857b507dbf18feTony Barbour        }
2632f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
2642f18b292ff155af7df35930474857b507dbf18feTony Barbour
2652f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (ctx_.physical_dev == VK_NULL_HANDLE)
2662f18b292ff155af7df35930474857b507dbf18feTony Barbour        throw std::runtime_error("failed to find any capable Vulkan physical device");
2672f18b292ff155af7df35930474857b507dbf18feTony Barbour}
2682f18b292ff155af7df35930474857b507dbf18feTony Barbour
2692f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::create_context()
2702f18b292ff155af7df35930474857b507dbf18feTony Barbour{
2712f18b292ff155af7df35930474857b507dbf18feTony Barbour    create_dev();
2722f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::init_dispatch_table_bottom(ctx_.instance, ctx_.dev);
2732f18b292ff155af7df35930474857b507dbf18feTony Barbour
2742f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::GetDeviceQueue(ctx_.dev, ctx_.game_queue_family, 0, &ctx_.game_queue);
2752f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::GetDeviceQueue(ctx_.dev, ctx_.present_queue_family, 0, &ctx_.present_queue);
2762f18b292ff155af7df35930474857b507dbf18feTony Barbour
2772f18b292ff155af7df35930474857b507dbf18feTony Barbour    create_back_buffers();
2782f18b292ff155af7df35930474857b507dbf18feTony Barbour
2792f18b292ff155af7df35930474857b507dbf18feTony Barbour    // initialize ctx_.{surface,format} before attach_shell
2802f18b292ff155af7df35930474857b507dbf18feTony Barbour    create_swapchain();
2812f18b292ff155af7df35930474857b507dbf18feTony Barbour
2822f18b292ff155af7df35930474857b507dbf18feTony Barbour    game_.attach_shell(*this);
2832f18b292ff155af7df35930474857b507dbf18feTony Barbour}
2842f18b292ff155af7df35930474857b507dbf18feTony Barbour
2852f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::destroy_context()
2862f18b292ff155af7df35930474857b507dbf18feTony Barbour{
2872f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (ctx_.dev == VK_NULL_HANDLE)
2882f18b292ff155af7df35930474857b507dbf18feTony Barbour        return;
2892f18b292ff155af7df35930474857b507dbf18feTony Barbour
2902f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::DeviceWaitIdle(ctx_.dev);
2912f18b292ff155af7df35930474857b507dbf18feTony Barbour
2922f18b292ff155af7df35930474857b507dbf18feTony Barbour    destroy_swapchain();
2932f18b292ff155af7df35930474857b507dbf18feTony Barbour
2942f18b292ff155af7df35930474857b507dbf18feTony Barbour    game_.detach_shell();
2952f18b292ff155af7df35930474857b507dbf18feTony Barbour
2962f18b292ff155af7df35930474857b507dbf18feTony Barbour    destroy_back_buffers();
2972f18b292ff155af7df35930474857b507dbf18feTony Barbour
2982f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.game_queue = VK_NULL_HANDLE;
2992f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.present_queue = VK_NULL_HANDLE;
3002f18b292ff155af7df35930474857b507dbf18feTony Barbour
3012f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::DestroyDevice(ctx_.dev, nullptr);
3022f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.dev = VK_NULL_HANDLE;
3032f18b292ff155af7df35930474857b507dbf18feTony Barbour}
3042f18b292ff155af7df35930474857b507dbf18feTony Barbour
3052f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::create_dev()
3062f18b292ff155af7df35930474857b507dbf18feTony Barbour{
3072f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkDeviceCreateInfo dev_info = {};
3082f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
3092f18b292ff155af7df35930474857b507dbf18feTony Barbour
3102f18b292ff155af7df35930474857b507dbf18feTony Barbour    const std::vector<float> queue_priorities(settings_.queue_count, 0.0f);
3112f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::array<VkDeviceQueueCreateInfo, 2> queue_info = {};
3122f18b292ff155af7df35930474857b507dbf18feTony Barbour    queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
3132f18b292ff155af7df35930474857b507dbf18feTony Barbour    queue_info[0].queueFamilyIndex = ctx_.game_queue_family;
3142f18b292ff155af7df35930474857b507dbf18feTony Barbour    queue_info[0].queueCount = settings_.queue_count;
3152f18b292ff155af7df35930474857b507dbf18feTony Barbour    queue_info[0].pQueuePriorities = queue_priorities.data();
3162f18b292ff155af7df35930474857b507dbf18feTony Barbour
3172f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (ctx_.game_queue_family != ctx_.present_queue_family) {
3182f18b292ff155af7df35930474857b507dbf18feTony Barbour        queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
3192f18b292ff155af7df35930474857b507dbf18feTony Barbour        queue_info[1].queueFamilyIndex = ctx_.present_queue_family;
3202f18b292ff155af7df35930474857b507dbf18feTony Barbour        queue_info[1].queueCount = 1;
3212f18b292ff155af7df35930474857b507dbf18feTony Barbour        queue_info[1].pQueuePriorities = queue_priorities.data();
3222f18b292ff155af7df35930474857b507dbf18feTony Barbour
3232f18b292ff155af7df35930474857b507dbf18feTony Barbour        dev_info.queueCreateInfoCount = 2;
3242f18b292ff155af7df35930474857b507dbf18feTony Barbour    } else {
3252f18b292ff155af7df35930474857b507dbf18feTony Barbour        dev_info.queueCreateInfoCount = 1;
3262f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
3272f18b292ff155af7df35930474857b507dbf18feTony Barbour
3282f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.pQueueCreateInfos = queue_info.data();
3292f18b292ff155af7df35930474857b507dbf18feTony Barbour
3302f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.enabledLayerCount = static_cast<uint32_t>(device_layers_.size());
3312f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.ppEnabledLayerNames = device_layers_.data();
3322f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions_.size());
3332f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.ppEnabledExtensionNames = device_extensions_.data();
3342f18b292ff155af7df35930474857b507dbf18feTony Barbour
3352f18b292ff155af7df35930474857b507dbf18feTony Barbour    // disable all features
3362f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkPhysicalDeviceFeatures features = {};
3372f18b292ff155af7df35930474857b507dbf18feTony Barbour    dev_info.pEnabledFeatures = &features;
3382f18b292ff155af7df35930474857b507dbf18feTony Barbour
3392f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::CreateDevice(ctx_.physical_dev, &dev_info, nullptr, &ctx_.dev));
3402f18b292ff155af7df35930474857b507dbf18feTony Barbour}
3412f18b292ff155af7df35930474857b507dbf18feTony Barbour
3422f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::create_back_buffers()
3432f18b292ff155af7df35930474857b507dbf18feTony Barbour{
3442f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkSemaphoreCreateInfo sem_info = {};
3452f18b292ff155af7df35930474857b507dbf18feTony Barbour    sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
3462f18b292ff155af7df35930474857b507dbf18feTony Barbour
3472f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkFenceCreateInfo fence_info = {};
3482f18b292ff155af7df35930474857b507dbf18feTony Barbour    fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
3492f18b292ff155af7df35930474857b507dbf18feTony Barbour    fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
3502f18b292ff155af7df35930474857b507dbf18feTony Barbour
3512f18b292ff155af7df35930474857b507dbf18feTony Barbour    // BackBuffer is used to track which swapchain image and its associated
3522f18b292ff155af7df35930474857b507dbf18feTony Barbour    // sync primitives are busy.  Having more BackBuffer's than swapchain
3532f18b292ff155af7df35930474857b507dbf18feTony Barbour    // images may allows us to replace CPU wait on present_fence by GPU wait
3542f18b292ff155af7df35930474857b507dbf18feTony Barbour    // on acquire_semaphore.
3552f18b292ff155af7df35930474857b507dbf18feTony Barbour    const int count = settings_.back_buffer_count + 1;
3562f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (int i = 0; i < count; i++) {
3572f18b292ff155af7df35930474857b507dbf18feTony Barbour        BackBuffer buf = {};
3582f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::assert_success(vk::CreateSemaphore(ctx_.dev, &sem_info, nullptr, &buf.acquire_semaphore));
3592f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::assert_success(vk::CreateSemaphore(ctx_.dev, &sem_info, nullptr, &buf.render_semaphore));
3602f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::assert_success(vk::CreateFence(ctx_.dev, &fence_info, nullptr, &buf.present_fence));
3612f18b292ff155af7df35930474857b507dbf18feTony Barbour
3622f18b292ff155af7df35930474857b507dbf18feTony Barbour        ctx_.back_buffers.push(buf);
3632f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
3642f18b292ff155af7df35930474857b507dbf18feTony Barbour}
3652f18b292ff155af7df35930474857b507dbf18feTony Barbour
3662f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::destroy_back_buffers()
3672f18b292ff155af7df35930474857b507dbf18feTony Barbour{
3682f18b292ff155af7df35930474857b507dbf18feTony Barbour    while (!ctx_.back_buffers.empty()) {
3692f18b292ff155af7df35930474857b507dbf18feTony Barbour        const auto &buf = ctx_.back_buffers.front();
3702f18b292ff155af7df35930474857b507dbf18feTony Barbour
3712f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DestroySemaphore(ctx_.dev, buf.acquire_semaphore, nullptr);
3722f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DestroySemaphore(ctx_.dev, buf.render_semaphore, nullptr);
3732f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DestroyFence(ctx_.dev, buf.present_fence, nullptr);
3742f18b292ff155af7df35930474857b507dbf18feTony Barbour
3752f18b292ff155af7df35930474857b507dbf18feTony Barbour        ctx_.back_buffers.pop();
3762f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
3772f18b292ff155af7df35930474857b507dbf18feTony Barbour}
3782f18b292ff155af7df35930474857b507dbf18feTony Barbour
3792f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::create_swapchain()
3802f18b292ff155af7df35930474857b507dbf18feTony Barbour{
3812f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.surface = create_surface(ctx_.instance);
3822f18b292ff155af7df35930474857b507dbf18feTony Barbour
3832f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkBool32 supported;
3842f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::GetPhysicalDeviceSurfaceSupportKHR(ctx_.physical_dev,
3852f18b292ff155af7df35930474857b507dbf18feTony Barbour                ctx_.present_queue_family, ctx_.surface, &supported));
3862f18b292ff155af7df35930474857b507dbf18feTony Barbour    // this should be guaranteed by the platform-specific can_present call
3872f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert(supported);
3882f18b292ff155af7df35930474857b507dbf18feTony Barbour
3892f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkSurfaceFormatKHR> formats;
3902f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::get(ctx_.physical_dev, ctx_.surface, formats);
3912f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.format = formats[0];
3922f18b292ff155af7df35930474857b507dbf18feTony Barbour
3932f18b292ff155af7df35930474857b507dbf18feTony Barbour    // defer to resize_swapchain()
3942f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.swapchain = VK_NULL_HANDLE;
3952f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.extent.width = (uint32_t) -1;
3962f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.extent.height = (uint32_t) -1;
3972f18b292ff155af7df35930474857b507dbf18feTony Barbour}
3982f18b292ff155af7df35930474857b507dbf18feTony Barbour
3992f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::destroy_swapchain()
4002f18b292ff155af7df35930474857b507dbf18feTony Barbour{
4012f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (ctx_.swapchain != VK_NULL_HANDLE) {
4022f18b292ff155af7df35930474857b507dbf18feTony Barbour        game_.detach_swapchain();
4032f18b292ff155af7df35930474857b507dbf18feTony Barbour
4042f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DestroySwapchainKHR(ctx_.dev, ctx_.swapchain, nullptr);
4052f18b292ff155af7df35930474857b507dbf18feTony Barbour        ctx_.swapchain = VK_NULL_HANDLE;
4062f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
4072f18b292ff155af7df35930474857b507dbf18feTony Barbour
4082f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::DestroySurfaceKHR(ctx_.instance, ctx_.surface, nullptr);
4092f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.surface = VK_NULL_HANDLE;
4102f18b292ff155af7df35930474857b507dbf18feTony Barbour}
4112f18b292ff155af7df35930474857b507dbf18feTony Barbour
4122f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::resize_swapchain(uint32_t width_hint, uint32_t height_hint)
4132f18b292ff155af7df35930474857b507dbf18feTony Barbour{
4142f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkSurfaceCapabilitiesKHR caps;
4152f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::GetPhysicalDeviceSurfaceCapabilitiesKHR(ctx_.physical_dev,
4162f18b292ff155af7df35930474857b507dbf18feTony Barbour                ctx_.surface, &caps));
4172f18b292ff155af7df35930474857b507dbf18feTony Barbour
4182f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkExtent2D extent = caps.currentExtent;
4192f18b292ff155af7df35930474857b507dbf18feTony Barbour    // use the hints
4202f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (extent.width == (uint32_t) -1) {
4212f18b292ff155af7df35930474857b507dbf18feTony Barbour        extent.width = width_hint;
4222f18b292ff155af7df35930474857b507dbf18feTony Barbour        extent.height = height_hint;
4232f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
4242f18b292ff155af7df35930474857b507dbf18feTony Barbour    // clamp width; to protect us from broken hints?
4252f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (extent.width < caps.minImageExtent.width)
4262f18b292ff155af7df35930474857b507dbf18feTony Barbour        extent.width = caps.minImageExtent.width;
4272f18b292ff155af7df35930474857b507dbf18feTony Barbour    else if (extent.width > caps.maxImageExtent.width)
4282f18b292ff155af7df35930474857b507dbf18feTony Barbour        extent.width = caps.maxImageExtent.width;
4292f18b292ff155af7df35930474857b507dbf18feTony Barbour    // clamp height
4302f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (extent.height < caps.minImageExtent.height)
4312f18b292ff155af7df35930474857b507dbf18feTony Barbour        extent.height = caps.minImageExtent.height;
4322f18b292ff155af7df35930474857b507dbf18feTony Barbour    else if (extent.height > caps.maxImageExtent.height)
4332f18b292ff155af7df35930474857b507dbf18feTony Barbour        extent.height = caps.maxImageExtent.height;
4342f18b292ff155af7df35930474857b507dbf18feTony Barbour
4352f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (ctx_.extent.width == extent.width && ctx_.extent.height == extent.height)
4362f18b292ff155af7df35930474857b507dbf18feTony Barbour        return;
4372f18b292ff155af7df35930474857b507dbf18feTony Barbour
4382f18b292ff155af7df35930474857b507dbf18feTony Barbour    uint32_t image_count = settings_.back_buffer_count;
4392f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (image_count < caps.minImageCount)
4402f18b292ff155af7df35930474857b507dbf18feTony Barbour        image_count = caps.minImageCount;
4412f18b292ff155af7df35930474857b507dbf18feTony Barbour    else if (image_count > caps.maxImageCount)
4422f18b292ff155af7df35930474857b507dbf18feTony Barbour        image_count = caps.maxImageCount;
4432f18b292ff155af7df35930474857b507dbf18feTony Barbour
4442f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert(caps.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
4452f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert(caps.supportedTransforms & caps.currentTransform);
4462f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
4472f18b292ff155af7df35930474857b507dbf18feTony Barbour                                           VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
4482f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkCompositeAlphaFlagBitsKHR composite_alpha =
4492f18b292ff155af7df35930474857b507dbf18feTony Barbour        (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
4502f18b292ff155af7df35930474857b507dbf18feTony Barbour        VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
4512f18b292ff155af7df35930474857b507dbf18feTony Barbour
4522f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<VkPresentModeKHR> modes;
4532f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::get(ctx_.physical_dev, ctx_.surface, modes);
4542f18b292ff155af7df35930474857b507dbf18feTony Barbour
4552f18b292ff155af7df35930474857b507dbf18feTony Barbour    // FIFO is the only mode universally supported
4562f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
4572f18b292ff155af7df35930474857b507dbf18feTony Barbour    for (auto m : modes) {
4582f18b292ff155af7df35930474857b507dbf18feTony Barbour        if ((settings_.vsync && m == VK_PRESENT_MODE_MAILBOX_KHR) ||
4592f18b292ff155af7df35930474857b507dbf18feTony Barbour            (!settings_.vsync && m == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
4602f18b292ff155af7df35930474857b507dbf18feTony Barbour            mode = m;
4612f18b292ff155af7df35930474857b507dbf18feTony Barbour            break;
4622f18b292ff155af7df35930474857b507dbf18feTony Barbour        }
4632f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
4642f18b292ff155af7df35930474857b507dbf18feTony Barbour
4652f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkSwapchainCreateInfoKHR swapchain_info = {};
4662f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
4672f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.surface = ctx_.surface;
4682f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.minImageCount = image_count;
4692f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.imageFormat = ctx_.format.format;
4702f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.imageColorSpace = ctx_.format.colorSpace;
4712f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.imageExtent = extent;
4722f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.imageArrayLayers = 1;
4732f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
4742f18b292ff155af7df35930474857b507dbf18feTony Barbour
4752f18b292ff155af7df35930474857b507dbf18feTony Barbour    std::vector<uint32_t> queue_families(1, ctx_.game_queue_family);
4762f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (ctx_.game_queue_family != ctx_.present_queue_family) {
4772f18b292ff155af7df35930474857b507dbf18feTony Barbour        queue_families.push_back(ctx_.present_queue_family);
4782f18b292ff155af7df35930474857b507dbf18feTony Barbour
4792f18b292ff155af7df35930474857b507dbf18feTony Barbour        swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
4802f18b292ff155af7df35930474857b507dbf18feTony Barbour        swapchain_info.queueFamilyIndexCount = (uint32_t)queue_families.size();
4812f18b292ff155af7df35930474857b507dbf18feTony Barbour        swapchain_info.pQueueFamilyIndices = queue_families.data();
4822f18b292ff155af7df35930474857b507dbf18feTony Barbour    } else {
4832f18b292ff155af7df35930474857b507dbf18feTony Barbour        swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
4842f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
4852f18b292ff155af7df35930474857b507dbf18feTony Barbour
4862f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.preTransform = caps.currentTransform;;
4872f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.compositeAlpha = composite_alpha;
4882f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.presentMode = mode;
4892f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.clipped = true;
4902f18b292ff155af7df35930474857b507dbf18feTony Barbour    swapchain_info.oldSwapchain = ctx_.swapchain;
4912f18b292ff155af7df35930474857b507dbf18feTony Barbour
4922f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::CreateSwapchainKHR(ctx_.dev, &swapchain_info, nullptr, &ctx_.swapchain));
4932f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.extent = extent;
4942f18b292ff155af7df35930474857b507dbf18feTony Barbour
4952f18b292ff155af7df35930474857b507dbf18feTony Barbour    // destroy the old swapchain
4962f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (swapchain_info.oldSwapchain != VK_NULL_HANDLE) {
4972f18b292ff155af7df35930474857b507dbf18feTony Barbour        game_.detach_swapchain();
4982f18b292ff155af7df35930474857b507dbf18feTony Barbour
4992f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DeviceWaitIdle(ctx_.dev);
5002f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::DestroySwapchainKHR(ctx_.dev, swapchain_info.oldSwapchain, nullptr);
5012f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
5022f18b292ff155af7df35930474857b507dbf18feTony Barbour
5032f18b292ff155af7df35930474857b507dbf18feTony Barbour    game_.attach_swapchain();
5042f18b292ff155af7df35930474857b507dbf18feTony Barbour}
5052f18b292ff155af7df35930474857b507dbf18feTony Barbour
5062f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::add_game_time(float time)
5072f18b292ff155af7df35930474857b507dbf18feTony Barbour{
5082f18b292ff155af7df35930474857b507dbf18feTony Barbour    int max_ticks = 3;
5092f18b292ff155af7df35930474857b507dbf18feTony Barbour
5102f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (!settings_.no_tick)
5112f18b292ff155af7df35930474857b507dbf18feTony Barbour        game_time_ += time;
5122f18b292ff155af7df35930474857b507dbf18feTony Barbour
5132f18b292ff155af7df35930474857b507dbf18feTony Barbour    while (game_time_ >= game_tick_ && max_ticks--) {
5142f18b292ff155af7df35930474857b507dbf18feTony Barbour        game_.on_tick();
5152f18b292ff155af7df35930474857b507dbf18feTony Barbour        game_time_ -= game_tick_;
5162f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
5172f18b292ff155af7df35930474857b507dbf18feTony Barbour}
5182f18b292ff155af7df35930474857b507dbf18feTony Barbour
5192f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::acquire_back_buffer()
5202f18b292ff155af7df35930474857b507dbf18feTony Barbour{
5212f18b292ff155af7df35930474857b507dbf18feTony Barbour    // acquire just once when not presenting
5222f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (settings_.no_present &&
5232f18b292ff155af7df35930474857b507dbf18feTony Barbour        ctx_.acquired_back_buffer.acquire_semaphore != VK_NULL_HANDLE)
5242f18b292ff155af7df35930474857b507dbf18feTony Barbour        return;
5252f18b292ff155af7df35930474857b507dbf18feTony Barbour
5262f18b292ff155af7df35930474857b507dbf18feTony Barbour    auto &buf = ctx_.back_buffers.front();
5272f18b292ff155af7df35930474857b507dbf18feTony Barbour
5282f18b292ff155af7df35930474857b507dbf18feTony Barbour    // wait until acquire and render semaphores are waited/unsignaled
5292f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::WaitForFences(ctx_.dev, 1, &buf.present_fence,
5302f18b292ff155af7df35930474857b507dbf18feTony Barbour                true, UINT64_MAX));
5312f18b292ff155af7df35930474857b507dbf18feTony Barbour    // reset the fence
5322f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::ResetFences(ctx_.dev, 1, &buf.present_fence));
5332f18b292ff155af7df35930474857b507dbf18feTony Barbour
5342f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::AcquireNextImageKHR(ctx_.dev, ctx_.swapchain,
5352f18b292ff155af7df35930474857b507dbf18feTony Barbour                UINT64_MAX, buf.acquire_semaphore, VK_NULL_HANDLE,
5362f18b292ff155af7df35930474857b507dbf18feTony Barbour                &buf.image_index));
5372f18b292ff155af7df35930474857b507dbf18feTony Barbour
5382f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.acquired_back_buffer = buf;
5392f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.back_buffers.pop();
5402f18b292ff155af7df35930474857b507dbf18feTony Barbour}
5412f18b292ff155af7df35930474857b507dbf18feTony Barbour
5422f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::present_back_buffer()
5432f18b292ff155af7df35930474857b507dbf18feTony Barbour{
5442f18b292ff155af7df35930474857b507dbf18feTony Barbour    const auto &buf = ctx_.acquired_back_buffer;
5452f18b292ff155af7df35930474857b507dbf18feTony Barbour
5462f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (!settings_.no_render)
5472f18b292ff155af7df35930474857b507dbf18feTony Barbour        game_.on_frame(game_time_ / game_tick_);
5482f18b292ff155af7df35930474857b507dbf18feTony Barbour
5492f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (settings_.no_present) {
5502f18b292ff155af7df35930474857b507dbf18feTony Barbour        fake_present();
5512f18b292ff155af7df35930474857b507dbf18feTony Barbour        return;
5522f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
5532f18b292ff155af7df35930474857b507dbf18feTony Barbour
5542f18b292ff155af7df35930474857b507dbf18feTony Barbour    VkPresentInfoKHR present_info = {};
5552f18b292ff155af7df35930474857b507dbf18feTony Barbour    present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
5562f18b292ff155af7df35930474857b507dbf18feTony Barbour    present_info.waitSemaphoreCount = 1;
5572f18b292ff155af7df35930474857b507dbf18feTony Barbour    present_info.pWaitSemaphores = (settings_.no_render) ?
5582f18b292ff155af7df35930474857b507dbf18feTony Barbour        &buf.acquire_semaphore : &buf.render_semaphore;
5592f18b292ff155af7df35930474857b507dbf18feTony Barbour    present_info.swapchainCount = 1;
5602f18b292ff155af7df35930474857b507dbf18feTony Barbour    present_info.pSwapchains = &ctx_.swapchain;
5612f18b292ff155af7df35930474857b507dbf18feTony Barbour    present_info.pImageIndices = &buf.image_index;
5622f18b292ff155af7df35930474857b507dbf18feTony Barbour
5632f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::QueuePresentKHR(ctx_.present_queue, &present_info));
5642f18b292ff155af7df35930474857b507dbf18feTony Barbour
5652f18b292ff155af7df35930474857b507dbf18feTony Barbour    vk::assert_success(vk::QueueSubmit(ctx_.present_queue, 0, nullptr, buf.present_fence));
5662f18b292ff155af7df35930474857b507dbf18feTony Barbour    ctx_.back_buffers.push(buf);
5672f18b292ff155af7df35930474857b507dbf18feTony Barbour}
5682f18b292ff155af7df35930474857b507dbf18feTony Barbour
5692f18b292ff155af7df35930474857b507dbf18feTony Barbourvoid Shell::fake_present()
5702f18b292ff155af7df35930474857b507dbf18feTony Barbour{
5712f18b292ff155af7df35930474857b507dbf18feTony Barbour    const auto &buf = ctx_.acquired_back_buffer;
5722f18b292ff155af7df35930474857b507dbf18feTony Barbour
5732f18b292ff155af7df35930474857b507dbf18feTony Barbour    assert(settings_.no_present);
5742f18b292ff155af7df35930474857b507dbf18feTony Barbour
5752f18b292ff155af7df35930474857b507dbf18feTony Barbour    // wait render semaphore and signal acquire semaphore
5762f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (!settings_.no_render) {
5772f18b292ff155af7df35930474857b507dbf18feTony Barbour        VkPipelineStageFlags stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
5782f18b292ff155af7df35930474857b507dbf18feTony Barbour        VkSubmitInfo submit_info = {};
5792f18b292ff155af7df35930474857b507dbf18feTony Barbour        submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
5802f18b292ff155af7df35930474857b507dbf18feTony Barbour        submit_info.waitSemaphoreCount = 1;
5812f18b292ff155af7df35930474857b507dbf18feTony Barbour        submit_info.pWaitSemaphores = &buf.render_semaphore;
5822f18b292ff155af7df35930474857b507dbf18feTony Barbour        submit_info.pWaitDstStageMask = &stage;
5832f18b292ff155af7df35930474857b507dbf18feTony Barbour        submit_info.signalSemaphoreCount = 1;
5842f18b292ff155af7df35930474857b507dbf18feTony Barbour        submit_info.pSignalSemaphores = &buf.acquire_semaphore;
5852f18b292ff155af7df35930474857b507dbf18feTony Barbour        vk::assert_success(vk::QueueSubmit(ctx_.game_queue, 1, &submit_info, VK_NULL_HANDLE));
5862f18b292ff155af7df35930474857b507dbf18feTony Barbour    }
5872f18b292ff155af7df35930474857b507dbf18feTony Barbour
5882f18b292ff155af7df35930474857b507dbf18feTony Barbour    // push the buffer back just once for Shell::cleanup_vk
5892f18b292ff155af7df35930474857b507dbf18feTony Barbour    if (buf.acquire_semaphore != ctx_.back_buffers.back().acquire_semaphore)
5902f18b292ff155af7df35930474857b507dbf18feTony Barbour        ctx_.back_buffers.push(buf);
5912f18b292ff155af7df35930474857b507dbf18feTony Barbour}
592