
Данный цикл статей будет посвящен выводу цветного треугольника в Vulkan c использованием библиотеки ash. Я надеюсь, что читатель имел дело с какими либо графическими api, потому что Vulkan - это сложно и очень много кода, но так же и возможность использования крутых фич от GPU
В данной статье будет раскрыто создание vulkan device
Проверка версий Vulkan
Создание AppInfo<'_>
Проверка поддерживаемых слоев
Проверка поддерживаемых расширений
Создание Instance
Просмотр доступных устройств и выбор устройства
Получение свойств выбранного устройства
Получение свойств семейств очередей
Получение поддерживаемых слоев и расширений
Создание логического устройства
Для начала создадим пустой проект на 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 нужных расширения:
"VK_KHR_surface" - Позволит нам выводить изображение на экран. Vulkan может рендерить в свой FrameBuffre не задействую само окно ос
"VK_KHR_win32_surface" - Расширение для поддержки поверхности Windows
"VK_EXT_debug_report" - Расширение для отлова ошибок
"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. Взять её за жабры и сделать ровно то, что ты хочешь.
Полезные ссылки:
https://github.com/adrien-ben/vulkan-tutorial-rs - Хороший туториал на Rust
https://vulkan-tutorial.com/ - Самый проверенный туториал, но используется язык C++
https://github.com/ash-rs/ash/tree/master/ash-examples - официальные примеры от создателей
ash
https://github.com/EmbarkStudios/kajiya - Графический движок, написан на Rust бывшим программистом Frostbite
https://github.com/olejaaaaaaaa/VulkanExamples - Ссылка на полный код туториал