Program Listing for File device.hpp

Return to documentation for file (inexor/vulkan-renderer/wrapper/device.hpp)

#pragma once

#include "inexor/vulkan-renderer/tools/exception.hpp"
#include "inexor/vulkan-renderer/tools/representation.hpp"
#include "inexor/vulkan-renderer/wrapper/commands/command_pool.hpp"
#include "inexor/vulkan-renderer/wrapper/make_info.hpp"

#include <array>
#include <functional>
#include <optional>
#include <shared_mutex>
#include <span>
#include <string>

namespace inexor::vulkan_renderer::wrapper {

// Forward declaration
class Instance;

// Using declarations
using tools::InexorException;
using tools::VulkanException;
using wrapper::commands::CommandBuffer;
using wrapper::commands::CommandPool;

enum class VulkanQueueType {
    QUEUE_TYPE_GRAPHICS,
    QUEUE_TYPE_COMPUTE,
    QUEUE_TYPE_TRANSFER,
    QUEUE_TYPE_SPARSE_BINDING,
};

class Device {
private:
    VkDevice m_device{VK_NULL_HANDLE};
    VkPhysicalDevice m_physical_device{VK_NULL_HANDLE};
    VmaAllocator m_allocator{VK_NULL_HANDLE};
    std::string m_gpu_name;
    VkPhysicalDeviceFeatures m_enabled_features{};
    std::array<std::uint8_t, VK_UUID_SIZE> m_pipeline_cache_uuid{};

    VkQueue m_graphics_queue{VK_NULL_HANDLE};
    VkQueue m_transfer_queue{VK_NULL_HANDLE};
    VkQueue m_compute_queue{VK_NULL_HANDLE};
    VkQueue m_sparse_binding_queue{VK_NULL_HANDLE};
    VkQueue m_present_queue{VK_NULL_HANDLE};

    std::optional<std::uint32_t> m_graphics_queue_family_index{0};
    std::optional<std::uint32_t> m_compute_queue_family_index{0};
    std::optional<std::uint32_t> m_transfer_queue_family_index{0};
    std::optional<std::uint32_t> m_sparse_binding_queue_family_index{0};
    std::optional<std::uint32_t> m_present_queue_family_index{0};

    mutable std::vector<std::unique_ptr<CommandPool>> m_cmd_pools;
    mutable std::shared_mutex m_mutex;

    CommandPool &get_thread_command_pool(VulkanQueueType queue_type) const;

    // @TODO Implement get_thread_command_pool with "transfer if available, graphics otherwise" for copy operations.

public:
    Device(const Instance &inst, VkSurfaceKHR surface, VkPhysicalDevice physical_device,
           const VkPhysicalDeviceFeatures &required_features, std::span<const char *> required_extensions);

    Device(const Device &) = delete;
    Device(Device &&) noexcept;

    ~Device();

    Device &operator=(const Device &) = delete;
    Device &operator=(Device &&) = delete;

    [[nodiscard]] auto device() const {
        return m_device;
    }

    [[nodiscard]] VkSurfaceCapabilitiesKHR get_surface_capabilities(VkSurfaceKHR surface) const;

    [[nodiscard]] bool is_presentation_supported(VkSurfaceKHR surface, std::uint32_t queue_family_index) const;

    void execute(const std::string &name, const VulkanQueueType queue_type,
                 const std::function<void(const CommandBuffer &cmd_buf)> &cmd_lambda) const;

    [[nodiscard]] VkPhysicalDevice physical_device() const {
        return m_physical_device;
    }

    [[nodiscard]] VmaAllocator allocator() const {
        return m_allocator;
    }

    [[nodiscard]] const VkPhysicalDeviceFeatures &enabled_features() const {
        return m_enabled_features;
    }

    [[nodiscard]] const std::string &gpu_name() const {
        return m_gpu_name;
    }

    [[nodiscard]] std::span<const std::uint8_t, VK_UUID_SIZE> pipeline_cache_uuid() const {
        return m_pipeline_cache_uuid;
    }

    [[nodiscard]] bool has_any_compute_queue() const {
        return m_compute_queue != VK_NULL_HANDLE;
    }

    [[nodiscard]] bool has_any_transfer_queue() const {
        return m_transfer_queue != VK_NULL_HANDLE;
    }

    [[nodiscard]] bool has_any_sparse_binding_queue() const {
        return m_transfer_queue != VK_NULL_HANDLE;
    }

    // TODO: Move to command buffer wrapper!
    [[nodiscard]] VkQueue compute_queue() const {
        return m_graphics_queue;
    }

    // TODO: Move to command buffer wrapper!
    [[nodiscard]] VkQueue graphics_queue() const {
        return m_graphics_queue;
    }

    // TODO: Move to command buffer wrapper!
    [[nodiscard]] VkQueue present_queue() const {
        return m_present_queue;
    }

    // TODO: Move to command buffer wrapper!
    [[nodiscard]] VkQueue transfer_queue() const {
        return m_transfer_queue;
    }

    [[nodiscard]] const CommandBuffer &request_command_buffer(VulkanQueueType queue_type, const std::string &name);

    [[nodiscard]] bool surface_supports_usage(VkSurfaceKHR surface, VkImageUsageFlagBits usage) const;

    template <typename VulkanObjectType>
    void set_debug_name(const VulkanObjectType &vk_object, const std::string &name) const {
        if (!vk_object) {
            throw InexorException("Error: Parameter 'vk_object' is invalid!");
        }

        const auto dbg_obj_name = wrapper::make_info<VkDebugUtilsObjectNameInfoEXT>({
            .objectType = tools::get_vk_object_type(vk_object),
            .objectHandle = reinterpret_cast<std::uint64_t>(vk_object),
            .pObjectName = name.c_str(),
        });

        if (const auto result = vkSetDebugUtilsObjectNameEXT(m_device, &dbg_obj_name); result != VK_SUCCESS) {
            throw VulkanException("Error: vkSetDebugUtilsObjectNameEXT failed!", result);
        }
    }

    void wait_idle(VkQueue queue = VK_NULL_HANDLE) const;
};

} // namespace inexor::vulkan_renderer::wrapper