Vulkan с использованием Rust. Часть 1

от автора

Данный цикл статей будет посвящен выводу цветного треугольника в 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. Взять её за жабры и сделать ровно то, что ты хочешь.

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


ссылка на оригинал статьи https://habr.com/ru/articles/942880/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *