1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! This module provides a structure for finding the queue families used by
//! this application.

use crate::graphics::vulkan::WindowSurface;

use super::Queue;

use anyhow::{Context, Result};
use ash::{
    version::{DeviceV1_0, InstanceV1_0},
    vk,
};

/// This struct holds all of the queue indices required by this application.
pub struct QueueFamilyIndices {
    /// the index for the graphics queue
    graphics_family_index: u32,

    /// the index for the presentation queue
    present_family_index: u32,
}

impl QueueFamilyIndices {
    /// Find all of the queue families required by this application.
    ///
    /// Yields an Err if any of the queues cannot be found.
    ///
    /// The implementation is greedy, e.g. the same queue will be used for
    /// multiple operations where possible.
    pub fn find(
        physical_device: &vk::PhysicalDevice,
        ash: &ash::Instance,
        window_surface: &dyn WindowSurface,
    ) -> Result<Self> {
        let queue_families = unsafe {
            ash.get_physical_device_queue_family_properties(*physical_device)
        };

        let mut graphics_family = None;
        let mut present_family = None;

        queue_families.iter().enumerate().for_each(|(i, family)| {
            if family.queue_flags.contains(vk::QueueFlags::GRAPHICS) {
                graphics_family = Some(i as u32);
            }

            let present_support = unsafe {
                window_surface.get_physical_device_surface_support(
                    physical_device,
                    i as u32,
                )
            };
            match present_support {
                Ok(true) => {
                    present_family = Some(i as u32);
                }
                _ => {}
            }
        });

        let graphics_family_index = graphics_family
            .context("unable to find queue family which supports graphics")?;

        let present_family_index = present_family
            .context("unable to find a queue which supports presentation")?;

        Ok(Self {
            graphics_family_index,
            present_family_index,
        })
    }

    const SINGLE_QUEUE_PRIORITY: [f32; 1] = [1.0];

    /// Create a vector of queue create info structs based on the indices.
    ///
    /// Automatically handles duplicate indices
    pub fn as_queue_create_infos(&self) -> Vec<vk::DeviceQueueCreateInfo> {
        let mut create_infos = vec![vk::DeviceQueueCreateInfo {
            queue_family_index: self.graphics_family_index,
            p_queue_priorities: Self::SINGLE_QUEUE_PRIORITY.as_ptr(),
            queue_count: 1,
            ..Default::default()
        }];

        if self.graphics_family_index != self.present_family_index {
            create_infos.push(vk::DeviceQueueCreateInfo {
                queue_family_index: self.present_family_index,
                p_queue_priorities: Self::SINGLE_QUEUE_PRIORITY.as_ptr(),
                queue_count: 1,
                ..Default::default()
            });
        }

        create_infos
    }

    /// Return a tuple of the actual vulkan queues.
    ///
    /// Handles duplicate queue family indices automatically.
    pub fn get_queues(
        &self,
        logical_device: &ash::Device,
    ) -> Result<(Queue, Queue)> {
        let raw_graphics_queue = unsafe {
            logical_device.get_device_queue(self.graphics_family_index, 0)
        };
        let graphics_queue =
            Queue::from_raw(raw_graphics_queue, self.graphics_family_index, 0);

        let is_same = self.graphics_family_index == self.present_family_index;
        let present_queue = if is_same {
            graphics_queue
        } else {
            let raw_present_queue = unsafe {
                logical_device.get_device_queue(self.present_family_index, 0)
            };
            Queue::from_raw(raw_present_queue, self.present_family_index, 0)
        };

        Ok((graphics_queue, present_queue))
    }
}