Данный цикл статей будет посвящен выводу цветного треугольника в Vulkan c использованием библиотеки ash. Я надеюсь, что читатель имел дело с какими либо графическими api, потому что Vulkan - это сложно и очень много кода, но так же и возможность использования крутых фич от GPU

В данной статье будет раскрыто создание vulkan device

  1. Проверка версий Vulkan

  2. Создание AppInfo<'_>

  3. Проверка поддерживаемых слоев

  4. Проверка поддерживаемых расширений

  5. Создание Instance

  6. Просмотр доступных устройств и выбор устройства

  7. Получение свойств выбранного устройства

  8. Получение свойств семейств очередей

  9. Получение поддерживаемых слоев и расширений

  10. Создание логического устройства

Для начала создадим пустой проект на rust

cargo new --bin CryEngine

Добавим зависимость

cargo add ash
use ash::Entry;
use ash::vk::*;

fn main() {

    /*
       Загружаем функции Vulkan, мы можем подгрузить их динамически с помощью load или
       статически с помощью linked функции, но для этого потребуется добавить features 
       для ash
    */
    let entry = unsafe { Entry::load().unwrap() };

    // Получаем последнюю доступную версию VK_API_VERSION
    let version = unsafe { 
        entry.try_enumerate_instance_version()
       .expect("Error enumerate instance version") 
    };

    let api_version = match version {
        // Тут уже будет поновее версия 1.1+
        Some(version) => {
            version
        },
        // Если доступна только первая версия
        None => {
            API_VERSION_1_0
        }
    };
}

Дальше нам нужно заполнить информацию о нашем приложении, заполнив структуру ApplicationInfo<'_>, она понадобится для создания Vulkan Instance


use ash::Entry;
use ash::vk::*;

fn main() {

    let entry = unsafe { Entry::load().unwrap() };

    // Получаем максимально доступную версию
    let version = unsafe { 
        entry.try_enumerate_instance_version()
       .expect("Error enumerate instance version") 
    };

    // Выбор версии
    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    /* 
        Самая важная строчка здесь - это версия vulkan, которую мы будем использовать, 
        остальное можете на свое усмотрение заполнить. Буква "с" перед строкой автоматически конвертирует 
        её в CStr строку
    */  
    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

}

Layers and Extensions

В Vulkan для отлова ошибок используются слои(Layers). Layers - это опциональные компоненты Vulkan API, которые перехватывают вызовы от приложения до драйверов, они могут проверять правильно ли используется Vulkan. Подробнее можно почитать в спецификации: https://docs.vulkan.org/guide/latest/layers.html


use ash::Entry;
use ash::vk::*;

fn main() {


    let entry = unsafe { Entry::load().unwrap() };
    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version")};

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

    println!("-----------Layers--------------");
    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }
}

У меня после вызова данного кода будут доступны такие слои:

"VK_LAYER_AMD_switchable_graphics"
"VK_LAYER_VALVE_steam_overlay"
"VK_LAYER_VALVE_steam_fossilize"
"VK_LAYER_OBS_HOOK"
"VK_LAYER_RENDERDOC_Capture"
"VK_LAYER_LUNARG_api_dump"
"VK_LAYER_LUNARG_gfxreconstruct"
"VK_LAYER_KHRONOS_synchronization2"
"VK_LAYER_KHRONOS_validation"
"VK_LAYER_LUNARG_monitor"
"VK_LAYER_LUNARG_screenshot"
"VK_LAYER_KHRONOS_profiles"
"VK_LAYER_KHRONOS_shader_object"
"VK_LAYER_LUNARG_crash_diagnostic"

Нам потребуется поддержка лишь одного слоя - VK_LAYER_KHRONOS_validation, именно она будет отслеживать неправильное использование Vulkan

Так же нам потребуются extensions(Расширения), они добавляют новые возможности в Vulkan

use ash::Entry;
use ash::vk::*;

fn main() {


    let entry = unsafe { Entry::load().unwrap() };

    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version")};

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         /// Мы передаем None, чтобы узнать о глобальных расширениях, так же можно узнать
         /// об расширений конкретного слоя, но нам это не нужно
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

    println!("-----------Layers--------------");
    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("------------Extensions------------");
    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("---------------------------------");
}

Я использую Window OS, поэтому буду видеть расширения связанные с этой операционной системой

"VK_KHR_device_group_creation"
"VK_KHR_external_fence_capabilities"
"VK_KHR_external_memory_capabilities"
"VK_KHR_external_semaphore_capabilities"
"VK_KHR_get_physical_device_properties2"
"VK_KHR_get_surface_capabilities2"
"VK_KHR_surface"
"VK_KHR_win32_surface"
"VK_EXT_debug_report"
"VK_EXT_debug_utils"
"VK_EXT_swapchain_colorspace"
"VK_KHR_portability_enumeration"
"VK_LUNARG_direct_driver_loading"

Из всех этих расширений я выделю только 4 нужных расширения:

  1. "VK_KHR_surface" - Позволит нам выводить изображение на экран. Vulkan может рендерить в свой FrameBuffre не задействую само окно ос

  2. "VK_KHR_win32_surface" - Расширение для поддержки поверхности Windows

  3. "VK_EXT_debug_report" - Расширение для отлова ошибок

  4. "VK_EXT_debug_utils" - Еще одно расширение для отлова ошибок

Теперь заполним InstanceCreateInfo<'_> нужными нам расширениями и слоями


use ash::Entry;
use ash::vk::*;

fn main() {


    let entry = unsafe { Entry::load().unwrap() };

    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version") };

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

     println!("------------Layers---------------");

    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("------------Extensions------------");

    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("---------------------------------");

    /// Берем только нужные расширения!
    let required_extensions = [
        CStr::from_bytes_with_nul(b"VK_KHR_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_KHR_win32_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_report\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_utils\0").unwrap().as_ptr(),
    ];

    /// Только необходимые слои!
    let required_layers = [
        CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap().as_ptr(),
    ];

    let instance_info = InstanceCreateInfo::default()
        .enabled_extension_names(&required_extensions)
        .enabled_layer_names(&required_layers)
        .application_info(&app_info);

    let instance = unsafe {
        // Вместо None можем передать свою callback функцию
        entry.create_instance(&instance_info, None)
        .map_err(|e| format!("Error create insatnce with errror: {}", e))
        .unwrap()
    };
}

Device(Логическое устройство)

Device - это абстракция над физическим устройством. С помощью device уже можно будет создавать буферы, изображения, текстуры, а так же отправлять команды на GPU и ждать синхронизации. Логическое устройство создается похожим образом на Instance, так же нужно запросить слои и расширения для него, но будет чуть больше того, что мы должны передать для его создания.

Сначала просмотрим, какие вообще устройства у нас есть и выберем дискретную(встроенную) GPU

use ash::Entry;
use ash::vk::*;

fn main() {

    let entry = unsafe { Entry::load().unwrap() };
    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version") };

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

    println!("-----------Layers--------------");

    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("------------Extensions------------");

    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("---------------------------------");

    let extension_names = extensions
        .iter()
        .map(|x| x.extension_name.as_ptr())
        .collect::<Vec<*const i8>>();

    let layer_names = layers
        .iter()
        .map(|x| x.layer_name.as_ptr())
        .collect::<Vec<*const i8>>();

    let instance_info = InstanceCreateInfo::default()
        .enabled_extension_names(&extension_names)
        .enabled_layer_names(&layer_names)
        .application_info(&app_info);

    let instance = unsafe {
        entry.create_instance(&instance_info, None)
        .map_err(|e| format!("Error create insatnce with errror: {}", e))
        .unwrap()
    };

    //  Получаем все доступные устройства
    let phys_devs = unsafe { instance.enumerate_physical_devices().expect("Error enumerate Physical Devices")};

    let mut phsy_dev_index = 0;

    println!("-------------Available GPU-------------------");
    for (index, i) in phys_devs.iter().enumerate() {

        // Запрашиваем свойства физического устройства
        let prop = unsafe { instance.get_physical_device_properties(*i) };

        // Выводим общую информацию
        println!("DEVICE_NAME:        {:?}", prop.device_name_as_c_str().unwrap_or(&c"Undefined"));
        println!("VULKAN_API_VERSION: {:?}", prop.api_version);
        println!("DEVICE_TYPE:        {:?}", prop.device_type);
        println!("DRIVER VERSION:     {:?}", prop.driver_version);

        if prop.device_type == PhysicalDeviceType::INTEGRATED_GPU {
            phsy_dev_index = index;
            break;
        }
    }
}

QueueFamily

Перед тем, как собрать полную информацию о нашем GPU, я бы хотел рассказать о такой вещи, как Queue Familes(Семейства очередей). У GPU есть так называемые Queue(Очереди) у которых есть свойства. Одни могут вычислять, другие передавать данные с CPU на GPU, другие могут непосредственно работать с графикой. Семейства очередей - это очереди с одними и теми же свойствами. Перед созданием логического устройства нужно будет указать какие семейства очередей будет использовать логическое устройство в будущем.


use std::ffi::CStr;
use ash::Entry;
use ash::vk::*;

fn main() {


    let entry = unsafe { Entry::load().unwrap() };

    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version") };

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

    println!("-----------Layers--------------");

    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("------------Extensions------------");

    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("---------------------------------");

    let required_extensions = [
        CStr::from_bytes_with_nul(b"VK_KHR_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_KHR_win32_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_report\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_utils\0").unwrap().as_ptr(),
    ];

    let required_layers = [
        CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap().as_ptr(),
    ];

    let instance_info = InstanceCreateInfo::default()
        .enabled_extension_names(&required_extensions)
        .enabled_layer_names(&required_layers)
        .application_info(&app_info);

    let instance = unsafe {
        entry.create_instance(&instance_info, None)
        .map_err(|e| format!("Error create insatnce with errror: {}", e))
        .unwrap()
    };

    let phys_devs = unsafe { instance.enumerate_physical_devices().expect("Error enumerate Physical Devices") };
    let mut phys_dev_index = 0;

    println!("-------------Avaliable GPU-------------------");
    for (index, i) in phys_devs.iter().enumerate() {

        let prop = unsafe { instance.get_physical_device_properties(*i) };
        println!("DEVICE_NAME:        {:?}", prop.device_name_as_c_str().unwrap_or(&c"Undefined"));
        println!("VULKAN_API_VERSION: {:?}", prop.api_version);
        println!("DEVICE_TYPE:        {:?}", prop.device_type);
        println!("DRIVER VERSION:     {:?}", prop.driver_version);

        if prop.device_type == PhysicalDeviceType::INTEGRATED_GPU {
            phys_dev_index = index;
            break;
        }
    }

    let phys_dev = phys_devs[phys_dev_index];

    /// Информацию о памяти
    let memory_prop = unsafe { instance.get_physical_device_memory_properties(phys_dev) };
    /// Информация о семействах очередей
    let queue_family_prop = unsafe { instance.get_physical_device_queue_family_properties(phys_dev) };
    /// Общая информация о GPU
    let phys_prop = unsafe { instance.get_physical_device_properties(phys_dev) };

    // Сохраняем информацию в свою структуру
    struct QueueFamilyInfo {
        queue_family_index: usize,
        queue_prop: QueueFamilyProperties
    }

    let mut queue_infos = vec![];

    for (index, i) in queue_family_prop.iter().enumerate() {
        println!("Queue Family: {}: Queue Count: {:?}, Flags: {:?}", index, i.queue_count, i.queue_flags);
        queue_infos.push(QueueFamilyInfo {
            queue_family_index: index,
            queue_prop: *i
        });
    }
}

У меня вывод будет такой:

Queue Family: 0: Queue Count: 1, Flags: GRAPHICS | COMPUTE | TRANSFER | SPARSE_BINDING
Queue Family: 1: Queue Count: 2, Flags: COMPUTE | TRANSFER | SPARSE_BINDING
Queue Family: 2: Queue Count: 1, Flags: TRANSFER | SPARSE_BINDING

Может быть как одно семейство поддерживающее вообще все операции, так и семейства заточенные под 1-2 операции, они считаются более быстрыми, чем общие.

Priority

Так же помимо информации о семействах нужно передать приоритет каждой очереди в семействе, но можно просто упростить и сделать все очереди равными. Приоритеты нужны, чтобы драйвер мог понять, какие операции предпочтительнее выполнить первыми, а какие можно позже.


use std::ffi::CStr;
use ash::Entry;
use ash::vk::*;

fn main() {


    let entry = unsafe { Entry::load().unwrap() };

    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version") };

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

    println!("-----------Layers--------------");

    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("------------Extensions------------");

    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("---------------------------------");

    let required_extensions = [
        CStr::from_bytes_with_nul(b"VK_KHR_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_KHR_win32_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_report\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_utils\0").unwrap().as_ptr(),
    ];

    let required_layers = [
        CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap().as_ptr(),
    ];

    let instance_info = InstanceCreateInfo::default()
        .enabled_extension_names(&required_extensions)
        .enabled_layer_names(&required_layers)
        .application_info(&app_info);

    let instance = unsafe {
        entry.create_instance(&instance_info, None)
        .map_err(|e| format!("Error create insatnce with errror: {}", e))
        .unwrap()
    };

    let phys_devs = unsafe { instance.enumerate_physical_devices().expect("Error enumerate Physical Devices") };
    let mut phys_dev_index = 0;

    println!("-------------Avaliable GPU-------------------");
    for (index, i) in phys_devs.iter().enumerate() {

        let prop = unsafe { instance.get_physical_device_properties(*i) };
        println!("DEVICE_NAME:        {:?}", prop.device_name_as_c_str().unwrap_or(&c"Undefined"));
        println!("VULKAN_API_VERSION: {:?}", prop.api_version);
        println!("DEVICE_TYPE:        {:?}", prop.device_type);
        println!("DRIVER VERSION:     {:?}", prop.driver_version);

        if prop.device_type == PhysicalDeviceType::INTEGRATED_GPU {
            phys_dev_index = index;
            break;
        }
    }

    let phys_dev = phys_devs[phys_dev_index];

    let memory_prop = unsafe { instance.get_physical_device_memory_properties(phys_dev) };
    let queue_family_prop = unsafe { instance.get_physical_device_queue_family_properties(phys_dev) };
    let phys_prop = unsafe { instance.get_physical_device_properties(phys_dev) };

    struct QueueFamilyInfo {
        queue_family_index: usize,
        queue_prop: QueueFamilyProperties
    }

    let mut queue_infos = vec![];

    for (index, i) in queue_family_prop.iter().enumerate() {
        println!("Queue Family: {}: Queue Count: {:?}, Flags: {:?}", index, i.queue_count, i.queue_flags);
        queue_infos.push(QueueFamilyInfo {
            queue_family_index: index,
            queue_prop: *i
        });
    }

    // Устанавливаем приоритет одинаковым для всех очередей в семействе
    let priority = [1.0f32];
    let mut queue_family_infos = vec![];

    for i in queue_infos {

        let device_queue_info = DeviceQueueCreateInfo::default()
            .queue_family_index(i.queue_family_index as u32)
            .queue_priorities(&priority);

        queue_family_infos.push(device_queue_info)
    }

}

Device Extensions and Layers

У устройства нам понадобится лишь одно расширение - это поддержка Swapchain(Цепочка обмена)


use std::ffi::CStr;
use ash::Entry;
use ash::vk::*;

fn main() {


    let entry = unsafe { Entry::load().unwrap() };
    let version = unsafe { entry.try_enumerate_instance_version().expect("Error enumerate instance version") };

    let api_version = match version {
        Some(version) => {
            version
        },
        None => {
            API_VERSION_1_0
        }
    };

    let app_info = ApplicationInfo::default()
        .application_name(c"Far Cry 9")
        .engine_name(c"Cry Engine")
        .engine_version(12)
        .application_version(0)
        .api_version(api_version);

    let layers = unsafe { entry.enumerate_instance_layer_properties().expect("Error enumerate layers") };

    let extensions = unsafe {
         entry.enumerate_instance_extension_properties(None).expect("Error enumerate instance properties")
    };

    println!("-----------Layers--------------");
    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("------------Extensions------------");
    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }
    println!("---------------------------------");

    let required_extensions = [
        CStr::from_bytes_with_nul(b"VK_KHR_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_KHR_win32_surface\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_report\0").unwrap().as_ptr(),
        CStr::from_bytes_with_nul(b"VK_EXT_debug_utils\0").unwrap().as_ptr(),
    ];

    let required_layers = [
        CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap().as_ptr(),
    ];

    let instance_info = InstanceCreateInfo::default()
        .enabled_extension_names(&required_extensions)
        .enabled_layer_names(&required_layers)
        .application_info(&app_info);

    let instance = unsafe {
        entry.create_instance(&instance_info, None)
        .map_err(|e| format!("Error create insatnce with errror: {}", e))
        .unwrap()
    };

    let phys_devs = unsafe { instance.enumerate_physical_devices().expect("Error enumerate Physical Devices") };
    let mut phys_dev_index = 0;

    println!("-------------Avaliable GPU-------------------");
    for (index, i) in phys_devs.iter().enumerate() {

        let prop = unsafe { instance.get_physical_device_properties(*i) };
        println!("DEVICE_NAME:        {:?}", prop.device_name_as_c_str().unwrap_or(&c"Undefined"));
        println!("VULKAN_API_VERSION: {:?}", prop.api_version);
        println!("DEVICE_TYPE:        {:?}", prop.device_type);
        println!("DRIVER VERSION:     {:?}", prop.driver_version);

        if prop.device_type == PhysicalDeviceType::INTEGRATED_GPU {
            phys_dev_index = index;
            break;
        }
    }

    let phys_dev = phys_devs[phys_dev_index];

    let memory_prop = unsafe { instance.get_physical_device_memory_properties(phys_dev) };
    let queue_family_prop = unsafe { instance.get_physical_device_queue_family_properties(phys_dev) };
    let phys_prop = unsafe { instance.get_physical_device_properties(phys_dev) };

    struct QueueFamilyInfo {
        queue_family_index: usize,
        queue_prop: QueueFamilyProperties
    }

    let mut queue_infos = vec![];

    for (index, i) in queue_family_prop.iter().enumerate() {
        println!("Queue Family: {}: Queue Count: {:?}, Flags: {:?}", index, i.queue_count, i.queue_flags);
        queue_infos.push(QueueFamilyInfo {
            queue_family_index: index,
            queue_prop: *i
        });
    }

    let priority = [1.0f32];
    let mut queue_family_infos = vec![];

    for i in queue_infos {

        let device_queue_info = DeviceQueueCreateInfo::default()
            .queue_family_index(i.queue_family_index as u32)
            .queue_priorities(&priority);

        queue_family_infos.push(device_queue_info)
    }

    let extensions = unsafe { instance.enumerate_device_extension_properties(phys_dev).expect("Error enumerate device extensions") };

    println!("------------Device Extensions---------------------");
    for i in &extensions {
        println!("{:?}", i.extension_name_as_c_str().unwrap_or(&c"None"));
    }

    // Единственное расширение которое нам необходимо!
    let required_extesions = [
        CStr::from_bytes_with_nul(b"VK_KHR_swapchain\0").unwrap().as_ptr()
    ];

    let layers = unsafe { 
        instance.enumerate_device_layer_properties(phys_dev)
        .expect("Error enumerate device layers")
    };

    println!("------------Device Layers---------------------");
    for i in &layers {
        println!("{:?}", i.layer_name_as_c_str().unwrap_or(&c"None"));
    }

    println!("-------------------------------------------------");

    // Пустой features
    let features = PhysicalDeviceFeatures::default();

    // Заполняем старыми значениями
    let device_info = DeviceCreateInfo::default()
        .enabled_features(&features)
        .queue_create_infos(&queue_family_infos)
        .enabled_extension_names(&required_extesions);

    // Создаем устройство
    let device = unsafe {
        instance.create_device(phys_dev, &device_info, None).expect("Error create device")
    };


}

На этом всё! Постепенно будут выходить и новые статьи. Надеюсь, тех, кто интересуется графикой, не отпугнул этот объём - да, здесь куда больше действий, чем в старых добрых glBegin/glEnd, но зато можно прочувствовать полный контроль над GPU. Взять её за жабры и сделать ровно то, что ты хочешь.

Полезные ссылки:

Комментарии (0)