inexorgame / vulkan-renderer

A new 3D game engine for Linux and Windows using C++20 and Vulkan API 1.3, in very early but ongoing development
https://inexor.org
MIT License
777 stars 34 forks source link

[device] Implement find_queue_family_index_if #503

Closed IAmNotHanni closed 1 year ago

IAmNotHanni commented 1 year ago

The problem

We are in the process of getting rid of SettingsDecisionMakerentirely. When it comes to finding the right queue family indices, the old code contained several functions like this:

std::optional<std::uint32_t>
VulkanSettingsDecisionMaker::find_graphics_queue_family(const VkPhysicalDevice graphics_card) {
    assert(graphics_card);

    std::uint32_t number_of_available_queue_families = 0;

    // First check how many queue families are available.
    vkGetPhysicalDeviceQueueFamilyProperties(graphics_card, &number_of_available_queue_families, nullptr);

    spdlog::trace("There are {} queue families available", number_of_available_queue_families);

    // Preallocate memory for the available queue families.
    std::vector<VkQueueFamilyProperties> available_queue_families(number_of_available_queue_families);

    // Get information about the available queue families.
    vkGetPhysicalDeviceQueueFamilyProperties(graphics_card, &number_of_available_queue_families,
                                             available_queue_families.data());

    // Loop through all available queue families and look for a suitable one.
    for (std::size_t i = 0; i < available_queue_families.size(); i++) {
        if (available_queue_families[i].queueCount > 0) {
            // Check if this queue family supports graphics.
            if ((available_queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
                // Ok this queue family supports graphics!
                return static_cast<std::uint32_t>(i);
            }
        }
    }

    // In this case we could not find any suitable graphics queue family!
    return std::nullopt;
}

That's a lot of code considering how little it does. Even worse, there was a lot of duplicate code like this!

The solution

All we need is:

std::optional<std::uint32_t> Device::find_queue_family_index_if(
    const std::function<bool(const std::uint32_t index, const VkQueueFamilyProperties &)> criteria_lambda) {
    std::uint32_t index = 0;
    for (const auto queue_family : vk_tools::get_all_physical_device_queue_family_properties(m_physical_device)) {
        if (queue_family.queueCount > 0 && criteria_lambda(index, queue_family)) {
            return index;
        }
        index++;
    }
    return std::nullopt;
}

I also implemented get_all_physical_device_queue_family_properties and is_presentation_supported. Now we can easily search for queue family indices as we like. All we need is to give a lambda to filter out the unsuitable queue family indices.

Examples

Need one which supports graphics and presentation?

std::optional<std::uint32_t> queue_candidate =
    find_queue_family_index_if([&](const std::uint32_t index, const VkQueueFamilyProperties &queue_family) {
        return is_presentation_supported(surface, index) &&
                  (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0u;
    });

Need one which supports only transfer but no graphics?

std::optional<std::uint32_t> queue_candidate = find_queue_family_index_if([&](const std::uint32_t index, const VkQueueFamilyProperties &queue_family) {
            return is_presentation_supported(surface, index) &&
                   // No graphics bit, only transfer bit
                   (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0u &&
                   (queue_family.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0u;
        });

Closing remarks

Btw if we merge https://github.com/inexorgame/vulkan-renderer/pull/498 before this, we can get rid of SettingsDecisionMaker entirely! :) (we could also merge them in the other order and get remove it as well..)

Links

Vulkan spec chapter 5: Devices and Queues Stackoverflow question: What is actually a Queue family in Vulkan? Vulkan tutorial: Physical devices and queue families

IAmNotHanni commented 1 year ago
IceflowRE commented 1 year ago

Would like to get a short overview what the difference to this https://github.com/inexorgame/vulkan-renderer/pull/489 is.

IAmNotHanni commented 1 year ago

@IceflowRE This pull request refactors the code which finds the queues we need to create the device. Pull request https://github.com/inexorgame/vulkan-renderer/pull/489 deals with finding the most suitable graphics card (or more general: physical device).