Skip to content

File vulkan_display.hpp

File List > src > vulkan_display.hpp

Go to the documentation of this file

#pragma once

#ifndef __ANDROID__

// #define VMA_IMPLEMENTATION
#    include "display/glfw_extended.hpp"
#    include "display/headless.hpp"
#    include "display/x11_direct.hpp"
#    include "illixr/phonebook.hpp"
#    include "illixr/switchboard.hpp"
#    include "illixr/threadloop.hpp"
#    include "illixr/vk/display_provider.hpp"

#    include 
#    include 

#    ifdef __linux__
#        include "illixr/vk/third_party/vk_mem_alloc.h"
#    else
#        include 
#    endif
#    include 

using namespace ILLIXR;
#    ifdef __linux__
using namespace std;
#    endif

class display_vk : public vulkan::display_provider {
public:
    explicit display_vk(const phonebook* const pb)
        : switchboard_{pb->lookup_impl<switchboard>()}
        , clock_{pb->lookup_impl<relative_clock>()} { }

    ~display_vk() override {
        running_ = false;
        if (main_thread_.joinable()) {
            main_thread_.join();
        }
        cleanup();
    }

    void start(std::set<const char*> instance_extensions, std::set<const char*> device_extensions) {
        auto manual_device_selection = switchboard_->get_env_char("ILLIXR_VULKAN_SELECT_GPU");
        selected_gpu_                = manual_device_selection ? std::stoi(manual_device_selection) : -1;

        // ILLIXR_DISPLAY_MODE defaults to GLFW if not specified.
        const char* env_var = switchboard_->get_env_char("ILLIXR_DISPLAY_MODE");
        if (!strcmp(env_var, "glfw")) {
            spdlog::get("illixr")->info("[vulkan_display] Selected GLFW for display backend");
            backend_type_ = display::display_backend::GLFW;
        } else if (!strcmp(env_var, "headless")) {
            spdlog::get("illixr")->info("[vulkan_display] Selected headless for display backend");
            backend_type_ = display::display_backend::HEADLESS;
        } else if (!strcmp(env_var, "x11_direct")) {
            spdlog::get("illixr")->info("[vulkan_display] Selected X11 direct mode for display backend");
            backend_type_ = display::display_backend::X11_DIRECT;
            direct_mode_  = true;
        } else {
            throw std::runtime_error("Invalid display mode: " + std::string(env_var));
        }

        setup(std::move(instance_extensions), std::move(device_extensions));

        if (backend_type_ == display::display_backend::GLFW /* || backend_type_ == display::display_backend::X11_DIRECT*/) {
            main_thread_ = std::thread(&display_vk::main_loop, this);
            while (!ready_) {
                // yield
                std::this_thread::yield();
            }
        }
    }

    void setup(std::set<const char*> instance_extensions, std::set<const char*> device_extensions) {
        if (backend_type_ == display::display_backend::GLFW) {
            backend_ = std::make_shared<display::glfw_extended>();
        } else if (backend_type_ == display::display_backend::X11_DIRECT) {
            backend_ = std::make_shared<display::x11_direct>(
                clock_, switchboard_->get_writer<switchboard::event_wrapper<time_point>>("vsync_estimate"));
        } else {
            backend_ = std::make_shared<display::headless>();
        }

        if (backend_type_ != display::display_backend::HEADLESS) {
            required_device_extensions_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
        }

        create_vk_instance(std::move(instance_extensions));
        if (backend_type_ == display::display_backend::GLFW) {
            backend_->setup_display(switchboard_, vk_instance_, nullptr);
            vk_surface_ = backend_->create_surface();
            select_physical_device();
        } else {
            select_physical_device();
            backend_->setup_display(switchboard_, vk_instance_, vk_physical_device_);
            vk_surface_ = backend_->create_surface();
        }

        auto backend_device_extensions = backend_->get_required_device_extensions();
        device_extensions.insert(backend_device_extensions.begin(), backend_device_extensions.end());
        create_logical_device(std::move(device_extensions));

        if (backend_type_ != display::display_backend::HEADLESS) {
            create_swapchain();
        }
    }

    void recreate_swapchain() override {
        if (backend_type_ == display::display_backend::HEADLESS) {
            throw std::runtime_error("Cannot recreate swapchain in headless mode!");
        }

        vkDeviceWaitIdle(vk_device_);

        destroy_swapchain();

        create_swapchain();
    }

    void poll_window_events() override {
        should_poll_ = true;
    }

private:
    void create_vk_instance(const std::set<const char*>& instance_extensions) {
        // Enable validation layers if ILLIXR_VULKAN_VALIDATION_LAYERS is set to true.
        bool enable_validation_layers = switchboard_->get_env_bool("ILLIXR_VULKAN_VALIDATION_LAYERS");
        if (enable_validation_layers)
            spdlog::get("illixr")->info("[vulkan_display] Vulkan validation layers enabled");

        VkApplicationInfo app_info{};
        app_info.sType              = VK_STRUCTURE_TYPE_APPLICATION_INFO;
        app_info.pApplicationName   = "ILLIXR Vulkan Display";
        app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
        app_info.pEngineName        = "ILLIXR";
        app_info.engineVersion      = VK_MAKE_VERSION(1, 0, 0);
        app_info.apiVersion         = VK_API_VERSION_1_2;

        auto                     backend_required_instance_extensions = backend_->get_required_instance_extensions();
        std::vector<const char*> backend_required_instance_extensions_vec(backend_required_instance_extensions.begin(),
                                                                          backend_required_instance_extensions.end());
        if (enable_validation_layers) {
            backend_required_instance_extensions_vec.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
        }
        backend_required_instance_extensions_vec.insert(backend_required_instance_extensions_vec.end(),
                                                        instance_extensions.begin(), instance_extensions.end());

        this->enabled_instance_extensions_ = backend_required_instance_extensions_vec;

        VkInstanceCreateInfo create_info{};
        create_info.sType                   = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
        create_info.pApplicationInfo        = &app_info;
        create_info.enabledExtensionCount   = static_cast<uint32_t>(backend_required_instance_extensions_vec.size());
        create_info.ppEnabledExtensionNames = backend_required_instance_extensions_vec.data();

        // print enabled instance extensions
        spdlog::get("illixr")->info("[vulkan_display] Enabled instance extensions:");
        for (const auto& extension : backend_required_instance_extensions_vec) {
            spdlog::get("illixr")->info("\t {}", extension);
        }

        // enable validation layers
        std::vector<const char*> validation_layers = {"VK_LAYER_KHRONOS_validation"};

        if (enable_validation_layers) {
            create_info.enabledLayerCount   = static_cast<uint32_t>(validation_layers.size());
            create_info.ppEnabledLayerNames = validation_layers.data();

            // debug messenger
            VkDebugUtilsMessengerCreateInfoEXT debug_messenger_create_info{};
            debug_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
            debug_messenger_create_info.messageSeverity =
                VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
            debug_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
            debug_messenger_create_info.pfnUserCallback =
                [](VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
                   const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) -> VkBool32 {
                (void) pUserData;
                // convert severity flag to string
                const char* severity = "???";
                if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
                    severity = "VERBOSE";
                } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
                    severity = "INFO";
                } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
                    severity = "WARNING";
                } else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
                    severity = "ERROR";
                }

                // convert message type flag to string
                const char* type = "???";
                if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
                    type = "GENERAL";
                } else if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
                    type = "VALIDATION";
                } else if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
                    type = "PERFORMANCE";
                }
                spdlog::get("illixr")->warn("[display_vk] [{}: {}] {}", severity, type, pCallbackData->pMessage);
                return VK_FALSE;
            };

            create_info.pNext = &debug_messenger_create_info;
        } else {
            create_info.enabledLayerCount = 0;
        }

        if (vkCreateInstance(&create_info, nullptr, &vk_instance_) != VK_SUCCESS) {
            ILLIXR::abort("Failed to create Vulkan instance!");
        }
    }

    bool is_physical_device_suitable(VkPhysicalDevice const& physical_device) {
        VkPhysicalDeviceProperties device_properties;
        vkGetPhysicalDeviceProperties(physical_device, &device_properties);

        VkPhysicalDeviceFeatures device_features;
        vkGetPhysicalDeviceFeatures(physical_device, &device_features);

        // check if the device supports the extensions we need
        std::set<std::string> unmet_extensions(required_device_extensions_.begin(), required_device_extensions_.end());

        uint32_t extension_count;
        vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, nullptr);
        std::vector<VkExtensionProperties> available_extensions(extension_count);
        vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, available_extensions.data());

        for (const auto& extension : available_extensions) {
            unmet_extensions.erase(extension.extensionName);
        }

        if (!unmet_extensions.empty()) {
            return false;
        }

        if (backend_type_ == display::display_backend::GLFW) {
            // check if the device supports the swapchain we need
            auto swapchain_details = vulkan::query_swapchain_details(physical_device, vk_surface_);
            if (swapchain_details.formats.empty() || swapchain_details.present_modes.empty()) {
                return false;
            }
        }

        return true;
    }

    void select_physical_device() {
        uint32_t device_count = 0;
        vkEnumeratePhysicalDevices(vk_instance_, &device_count, nullptr);

        if (device_count == 0) {
            ILLIXR::abort("No Vulkan devices found!");
        }

        std::vector<VkPhysicalDevice> devices(device_count);
        vkEnumeratePhysicalDevices(vk_instance_, &device_count, devices.data());

        std::vector<VkPhysicalDevice> suitable_devices;
        std::vector<VkPhysicalDevice> unsuitable_devices;
        for (const auto& device : devices) {
            if (is_physical_device_suitable(device)) {
                suitable_devices.push_back(device);
            } else {
                unsuitable_devices.push_back(device);
            }
        }

        spdlog::get("illixr")->info("[vulkan_display] Found {} Vulkan devices", device_count);
        int index = 0;
        for (const auto& device : suitable_devices) {
            VkPhysicalDeviceProperties device_properties;
            vkGetPhysicalDeviceProperties(device, &device_properties);
            spdlog::get("illixr")->info("\t[{}] {}", index, device_properties.deviceName);
            index++;
        }
        if (!unsuitable_devices.empty()) {
            spdlog::get("illixr")->info("[vulkan_display] Found {} unsuitable Vulkan devices", unsuitable_devices.size());
            for (const auto& device : unsuitable_devices) {
                VkPhysicalDeviceProperties device_properties;
                vkGetPhysicalDeviceProperties(device, &device_properties);
                spdlog::get("illixr")->info("\t{}", device_properties.deviceName);
            }
        }

        if (selected_gpu_ == -1) {
            // select the first suitable device
            vk_physical_device_ = suitable_devices[0];
        } else {
            // select the device specified by the user
            vk_physical_device_ = suitable_devices[selected_gpu_];
        }

        VkPhysicalDeviceProperties device_properties;
        vkGetPhysicalDeviceProperties(vk_physical_device_, &device_properties);
        spdlog::get("illixr")->info("[vulkan_display] Selected device: {}", device_properties.deviceName);
    }

    void create_logical_device(const std::set<const char*>& device_extensions) {
        vulkan::queue_families indices =
            vulkan::find_queue_families(vk_physical_device_, vk_surface_, backend_type_ == display::display_backend::HEADLESS);

        std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
        std::set<uint32_t>                   unique_queue_families = {indices.graphics_family.value()};

        if (indices.present_family.has_value()) {
            unique_queue_families.insert(indices.present_family.value());
        }

        if (indices.has_compression()) {
            unique_queue_families.insert(indices.encode_family.value());
            unique_queue_families.insert(indices.decode_family.value());
        }

        if (indices.dedicated_transfer.has_value()) {
            unique_queue_families.insert(indices.dedicated_transfer.value());
        }

        if (indices.compute_family.has_value()) {
            unique_queue_families.insert(indices.compute_family.value());
        }

        float queue_priority = 1.0f;
        for (uint32_t queue_family : unique_queue_families) {
            VkDeviceQueueCreateInfo queue_create_info{};
            queue_create_info.sType            = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
            queue_create_info.queueFamilyIndex = queue_family;
            queue_create_info.queueCount       = 1;
            queue_create_info.pQueuePriorities = &queue_priority;
            queue_create_infos.push_back(queue_create_info);
        }

        VkPhysicalDeviceFeatures device_features{};
        device_features.samplerAnisotropy = VK_TRUE;

        VkPhysicalDeviceSynchronization2FeaturesKHR synchronization_2_features = {
            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, nullptr, true};

        VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features{
            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
            &synchronization_2_features, // pNext
            VK_TRUE                      // timelineSemaphore
        };

        features_ = VkPhysicalDeviceFeatures2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &timeline_semaphore_features,
                                              device_features};

        required_device_extensions_.insert(required_device_extensions_.end(), device_extensions.begin(),
                                           device_extensions.end());

        this->enabled_device_extensions_ = required_device_extensions_;

        VkDeviceCreateInfo create_info{};
        create_info.sType                   = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
        create_info.pQueueCreateInfos       = queue_create_infos.data();
        create_info.queueCreateInfoCount    = static_cast<uint32_t>(queue_create_infos.size());
        create_info.enabledExtensionCount   = static_cast<uint32_t>(required_device_extensions_.size());
        create_info.ppEnabledExtensionNames = required_device_extensions_.data();
        create_info.pNext                   = &features_;

        // print enabled device extensions
        spdlog::get("illixr")->info("[vulkan_display] Enabled instance extensions:");
        for (const auto& extension : required_device_extensions_) {
            spdlog::get("illixr")->info("\t {}", extension);
        }

        VK_ASSERT_SUCCESS(vkCreateDevice(vk_physical_device_, &create_info, nullptr, &vk_device_));

        vulkan::queue graphics_queue{};
        vkGetDeviceQueue(vk_device_, indices.graphics_family.value(), 0, &graphics_queue.vk_queue);
        graphics_queue.family        = indices.graphics_family.value();
        graphics_queue.type          = vulkan::queue::GRAPHICS;
        graphics_queue.mutex         = std::make_shared<std::mutex>();
        queues_[graphics_queue.type] = graphics_queue;

        if (indices.present_family.has_value()) {
            vulkan::queue present_queue{};
            vkGetDeviceQueue(vk_device_, indices.present_family.value(), 0, &present_queue.vk_queue);
            present_queue.family        = indices.present_family.value();
            present_queue.type          = vulkan::queue::PRESENT;
            present_queue.mutex         = std::make_shared<std::mutex>();
            queues_[present_queue.type] = present_queue;
        }

        if (indices.has_compression()) {
            vulkan::queue encode_queue{};
            vkGetDeviceQueue(vk_device_, indices.encode_family.value(), 0, &encode_queue.vk_queue);
            encode_queue.family        = indices.encode_family.value();
            encode_queue.type          = vulkan::queue::ENCODE;
            encode_queue.mutex         = std::make_shared<std::mutex>();
            queues_[encode_queue.type] = encode_queue;

            vulkan::queue decode_queue{};
            vkGetDeviceQueue(vk_device_, indices.decode_family.value(), 0, &decode_queue.vk_queue);
            decode_queue.family        = indices.decode_family.value();
            decode_queue.type          = vulkan::queue::DECODE;
            decode_queue.mutex         = std::make_shared<std::mutex>();
            queues_[decode_queue.type] = decode_queue;
        }

        if (indices.compute_family.has_value()) {
            vulkan::queue compute_queue{};
            vkGetDeviceQueue(vk_device_, indices.compute_family.value(), 0, &compute_queue.vk_queue);
            compute_queue.family        = indices.compute_family.value();
            compute_queue.type          = vulkan::queue::COMPUTE;
            compute_queue.mutex         = std::make_shared<std::mutex>();
            queues_[compute_queue.type] = compute_queue;
        }

        if (indices.dedicated_transfer.has_value()) {
            vulkan::queue transfer_queue{};
            vkGetDeviceQueue(vk_device_, indices.dedicated_transfer.value(), 0, &transfer_queue.vk_queue);
            transfer_queue.family        = indices.dedicated_transfer.value();
            transfer_queue.type          = vulkan::queue::DEDICATED_TRANSFER;
            transfer_queue.mutex         = std::make_shared<std::mutex>();
            queues_[transfer_queue.type] = transfer_queue;
        }

        vma_allocator_ = vulkan::create_vma_allocator(vk_instance_, vk_physical_device_, vk_device_);
    }

    void create_swapchain() {
        // create surface
        vulkan::swapchain_details swapchain_details = vulkan::query_swapchain_details(vk_physical_device_, vk_surface_);

        // choose surface format
        for (const auto& available_format : swapchain_details.formats) {
            if (available_format.format == VK_FORMAT_B8G8R8A8_SRGB &&
                available_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
                spdlog::get("illixr")->info("[vulkan_display] Using VK_FORMAT_B8G8R8A8_SRGB");
                swapchain_image_format_ = available_format;
                break;
            } else if (available_format.format == VK_FORMAT_B8G8R8A8_UNORM &&
                       available_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
                spdlog::get("illixr")->info("[vulkan_display] Using VK_FORMAT_B8G8R8A8_UNORM (direct mode)");
                swapchain_image_format_ = available_format;
                break;
            }
        }

        // choose present mode
        VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
        for (const auto& available_present_mode : swapchain_details.present_modes) {
            if (available_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) {
                swapchain_present_mode = available_present_mode;
                break;
            }
        }

        // choose swapchain extent
        if (swapchain_details.capabilities.currentExtent.width != UINT32_MAX) {
            swapchain_extent_ = swapchain_details.capabilities.currentExtent;
        } else if (std::dynamic_pointer_cast<display::glfw_extended>(backend_) != nullptr) {
            auto fb_size             = std::dynamic_pointer_cast<display::glfw_extended>(backend_)->get_framebuffer_size();
            swapchain_extent_.width  = std::clamp(fb_size.first, swapchain_details.capabilities.minImageExtent.width,
                                                  swapchain_details.capabilities.maxImageExtent.width);
            swapchain_extent_.height = std::clamp(fb_size.second, swapchain_details.capabilities.minImageExtent.height,
                                                  swapchain_details.capabilities.maxImageExtent.height);
        }

        uint32_t image_count = max(swapchain_details.capabilities.minImageCount, 2u); // double buffering

        if (swapchain_details.capabilities.maxImageCount > 0 && image_count > swapchain_details.capabilities.maxImageCount) {
            image_count = swapchain_details.capabilities.maxImageCount;
        }

        VkSwapchainCreateInfoKHR create_info{}; //{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
        create_info.sType            = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
        create_info.surface          = vk_surface_;
        create_info.minImageCount    = image_count;
        create_info.imageFormat      = swapchain_image_format_.format;
        create_info.imageColorSpace  = swapchain_image_format_.colorSpace;
        create_info.imageExtent      = swapchain_extent_;
        create_info.imageArrayLayers = 1;
        create_info.imageUsage       = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

        vulkan::queue_families indices                = vulkan::find_queue_families(vk_physical_device_, vk_surface_);
        uint32_t               queue_family_indices[] = {indices.graphics_family.value(), indices.present_family.value()};

        if (indices.graphics_family != indices.present_family) {
            create_info.imageSharingMode      = VK_SHARING_MODE_CONCURRENT;
            create_info.queueFamilyIndexCount = 2;
            create_info.pQueueFamilyIndices   = queue_family_indices;
        } else {
            create_info.imageSharingMode      = VK_SHARING_MODE_EXCLUSIVE;
            create_info.queueFamilyIndexCount = 0;
            create_info.pQueueFamilyIndices   = nullptr;
        }

        create_info.preTransform   = swapchain_details.capabilities.currentTransform;
        create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
        create_info.presentMode    = swapchain_present_mode;
        create_info.clipped        = VK_TRUE;
        create_info.oldSwapchain   = VK_NULL_HANDLE;

        auto create_shared_swapchains =
            (PFN_vkCreateSharedSwapchainsKHR) vkGetInstanceProcAddr(vk_instance_, "vkCreateSharedSwapchainsKHR");

        if (vkCreateSwapchainKHR(vk_device_, &create_info, nullptr, &vk_swapchain_) != VK_SUCCESS) {
            ILLIXR::abort("Failed to create Vulkan swapchain!");
        }

        // get swapchain images
        vkGetSwapchainImagesKHR(vk_device_, vk_swapchain_, &image_count, nullptr);
        swapchain_images_.resize(image_count);
        vkGetSwapchainImagesKHR(vk_device_, vk_swapchain_, &image_count, swapchain_images_.data());

        swapchain_image_views_.resize(swapchain_images_.size());
        for (size_t i = 0; i < swapchain_images_.size(); i++) {
            swapchain_image_views_[i] = vulkan::create_image_view(vk_device_, swapchain_images_[i],
                                                                  swapchain_image_format_.format, VK_IMAGE_ASPECT_COLOR_BIT);
        }
    }

    void destroy_swapchain() {
        for (auto& image_view : swapchain_image_views_) {
            vkDestroyImageView(vk_device_, image_view, nullptr);
        }
        vkDestroySwapchainKHR(vk_device_, vk_swapchain_, nullptr);
    }

    void cleanup() {
        vkDeviceWaitIdle(vk_device_);
        destroy_swapchain();

        vmaDestroyAllocator(vma_allocator_);

        vkDestroyDevice(vk_device_, nullptr);

        if (!direct_mode_) {
            vkDestroySurfaceKHR(vk_instance_, vk_surface_, nullptr);
        }

        vkDestroyInstance(vk_instance_, nullptr);

        backend_->cleanup();
    }

    void main_loop() {
        ready_ = true;

        while (running_) {
            if (!direct_mode_) {
                if (should_poll_.exchange(false)) {
                    auto glfw_backend = std::dynamic_pointer_cast<display::glfw_extended>(backend_);
                    glfw_backend->poll_window_events();
                }
            } else {
                auto x11_backend = std::dynamic_pointer_cast<display::x11_direct>(backend_);
                if (!x11_backend->display_timings_event_registered_) {
                    x11_backend->register_display_timings_event(vk_device_);
                } else {
                    x11_backend->tick();
                }
            }
        }
    }

private:
    std::vector<const char*> required_device_extensions_ = {VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME};

    std::thread       main_thread_;
    std::atomic<bool> ready_{false};
    std::atomic<bool> running_{true};

    display::display_backend::display_backend_type backend_type_;
    bool                                           direct_mode_{false};
    int                                            selected_gpu_{-1};

    std::shared_ptr<display::display_backend> backend_;

    const std::shared_ptr<switchboard> switchboard_;

    std::atomic<bool> should_poll_{true};

    std::shared_ptr<relative_clock> clock_;
};

#endif