Physical Devices
После того как Вулкан был проинициализирован, мы можем начать работу с основными основными объектами API - устройствами (devices) и очередями (queues). В этой статье представлена информация о физических устрйоствах и приведен небольшой пример инициализации Вулкана и перечисления некотрых свойств инстанса и устройства.
Вулкан разделяет концепцию физических и логических устройств. Физические устройства это обычно конкретное устройство в системе (возможно - составленное из нескольких отдельных устройств, но работающих вместе), их количество конечно. Логическое устройство это отображение этого устройства для приложения.
Чтоб получить список физических устройств в системе необходимо вызвать:
VkResult vkEnumeratePhysicalDevices(
VkInstance instance,
uint32_t * pPhysicalDeviceCount,
VkPhysicalDevice * pPhysicalDevices);
• instance - хэндл инстанса Вулкана (VkInstance), созданного через vkCreateInstance.
• pPhysicalDevices - если NULL, тогда количество доступных физических устройств возвращается через pPhysicalDeviceCount, иначе,
• pPhysicalDeviceCount должен указывать на переменную, содержащую размер выделеннйо памяти под pPhysicalDevices, после выполнения там будет актуальное количество физических устройств, записанных в pPhysicalDevices.
Возвращаемые коды ошибок:
Success
• VK_SUCCESS
Failure
• VK_ERROR_OUT_OF_HOST_MEMORY
• VK_ERROR_OUT_OF_DEVICE_MEMORY
• VK_ERROR_INITIALIZATION_FAILED
После того как был получен список физических устройств можно получить свойства этого физического устройства вызвав:
void vkGetPhysicalDeviceProperties(
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties * pProperties);
• physicalDevice хэндл (VkPhysicalDevice) физического устройства, чьи свойства мы хотим получить.
• pProperties - указатель на структуру VkPhysicalDeviceProperties, в которую будут возвращены свойства устройства.
Структура VkPhysicalDeviceProperties определена как:
typedef struct VkPhysicalDeviceProperties {
uint32_t apiVersion;
uint32_t driverVersion;
uint32_t vendorID;
uint32_t deviceID;
VkPhysicalDeviceType deviceType;
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
uint8_t pipelineCacheUUID[VK_UUID_SIZE];
VkPhysicalDeviceLimits limits;
VkPhysicalDeviceSparseProperties sparseProperties;
} VkPhysicalDeviceProperties;
• apiVersion - версия Вулкана, поддерживаемая устройством.
• driverVersion - указанная вендором версия драйвера.
• vendorID - уникальный идентификатор вендора физического устройства.
• deviceID - уникальный идентификатор физического устройства (в рамках вендора).
Поля vendorID и deviceID позволяют приложению адаптироваться под характеристики устройства.
• deviceType - VkPhysicalDeviceType - тип устройства.
• deviceName - UTF-8 строка, содержащая имя устройства.
• pipelineCacheUUID - массив размером VK_UUID_SIZE, содержащий 8-ми битное представление универсального уникального идентификатора устройства (UUID).
• limits - структура VkPhysicalDeviceLimits, которая определяет специфические для устройства ограничения.
• sparseProperties - структура VkPhysicalDeviceSparseProperties, которая определяет некоторые специфичные свойства физического устройства.
Тип физического устройства определяется как:
typedef enum VkPhysicalDeviceType {
VK_PHYSICAL_DEVICE_TYPE_OTHER = 0,
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1,
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2,
VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3,
VK_PHYSICAL_DEVICE_TYPE_CPU = 4,
} VkPhysicalDeviceType;
• VK_PHYSICAL_DEVICE_TYPE_OTHER - устройство не совпадает ни с одним из доступных типов.
• VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU - устройство обычно встроенное или тесно связано с хостом.
• VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU - обычно это отдельный процессор подключенный к хосту.
• VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU - устройство обычно является виртуальной нодой в виртуальном окружении.
• VK_PHYSICAL_DEVICE_TYPE_CPU - устройство работает на том же процессоре что и хост.
Тип физического устройства имеет только информативное значение и не оказывает влияния на работу системы. Однако, тип устройства может коррелировать с другими предоставляемыми системой ресурсами, такими как количество доступной памяти.
====================================================================
Прежде чем перейти к рассмотрению/переводу остальной части спецификации, хотелось бы подвести черту под инициализацией, в виде небольшой демки с инициализацией Вулкана. Из предыдущей главы мы уже знаем что для этого необходимо создать инстанс через vkCreateInstance, заполнив соответствующие структуры. Чтоб проверить что Вулкан проинициализировался и что инстанс создался успешно попробуем получить кое-какую информацию о свойствах инстанса и имеющихся в системе устройствах.
Так как импортировать все функции вручную это долго, то воспользуюсь рекомендацией Хроноса и использую для этого их загрузчик:
Создание проекта и компиляция библиотеки в целом задача тривиальная, следуйте инструкциям. На выходе у вас будет библиотека “vulkan-1.lib”, которую нужно будет прилинковать к вашему проекту.
Для проекта вам так же потребуются заголовочные файлы Вулкана, их так же можно найти в проекте Vulkan-LoaderAndValidationLayers.
Включаем в программу заголовочные файлы:
#include <vulkan/vulkan.h>
#include <vulkan/vk_sdk_platform.h>
И переходим к инициализации Вулкана. Для того, чтоб создать инстанс через vkCreateInstance нам необходимо заполнить структуру VkInstanceCreateInfo. Поля этой структуры были детально описаны в предыдущей статье:
VkInstanceCreateInfo inst_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.pApplicationInfo = NULL,
.enabledLayerCount = 0,
.ppEnabledLayerNames = NULL,
.enabledExtensionCount = 0,
.ppEnabledExtensionNames = NULL,
};
Как видите - все просто :) Главное правильно указать тип структуры.
При желании можно заполнить структуру VkApplicationInfo, в которой вы можете указать различную информацию о вашем приложении, но это действие не обязательно.
Отдельно стоило бы упомянуть о полях ppEnabledLayerNames и ppEnabledExtensionNames, однако в спецификации пока было лишь одно упоминание о них, молв если вы хотите использовать дополнительные лееры или расширения, то вы должны их активировать через соответствующие структуры. Мы пока понятия не имеем что это такое и нужно ли оно нам, и уж точно сейчас мы ничего из этого не будем использовать, поэтому смело указываем там NULL.
Все, этого достаточно чтоб проинициализировать вулкан и создать наш инстанс.
Останется только вызвать соответствующую команду и проверить что у нас нет ошибок:
VkInstance inst;
VkResult err = vkCreateInstance(&inst_info, NULL, &inst);
Если функция отработает успешно, то она вернет код ошибки VK_SUCCESS, в противном случае может быть одна из перечисленных ошибок:
• VK_ERROR_OUT_OF_HOST_MEMORY
• VK_ERROR_OUT_OF_DEVICE_MEMORY
• VK_ERROR_INITIALIZATION_FAILED
• VK_ERROR_LAYER_NOT_PRESENT
• VK_ERROR_EXTENSION_NOT_PRESENT
• VK_ERROR_INCOMPATIBLE_DRIVER
Так как работать с памятью мы еще не умеем, то в обоих случаях у нас в качестве аллокатора в функциях указан NULL, на этом этапе этого достаточно.
Первым желанием, после того как я создал инстанс, было узнать, какие устройства будут обнаружены в моей системе (AMD Kaveri + NVidia) и какие свойства у них оно мне покажет. Для этого воспользуемся описанной ранее функцией: vkEnumeratePhysicalDevices. Для начала нам нужно запроситьколичество физических устрйоств в системе:
err = vkEnumeratePhysicalDevices(inst, &dev_count, NULL);
Первым параметром мы передаем созданный нами инстанс, вторым параметром - переменную, куда мы будем сохранять количество устройств, в качестве третьего параметра указываем NULL, что означает что нас интересует только количество устройств.
Узнав количество устройств выделяем под их описание память, к примеру так:
VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * dev_count);
И повторно вызываем эту же функцию, чтоб получить список этих устройств:
err = vkEnumeratePhysicalDevices(inst, &dev_count, physical_devices);
Полученные физические устройства пригодятся нам позже, а пока они не несут нам никакой информации (а мы любопытны). Чтоб получить информацию о типе устройства необходимо вызвать описанную ранее функцию: vkGetPhysicalDeviceProperties.
VkPhysicalDeviceProperties dev_props;
vkGetPhysicalDeviceProperties(physical_devices[i], &dev_props);
Эту функцию нужно вызывать для каждого найденного физического устройства (если конечно вам нужно получить о них информацию, что не обязательно для работы приложения).
Проверили? работает? Тогда этот инстанс можно удалить с чистой совестью:
vkDestroyInstance(inst, NULL);
Естественно на этом мы завершаем нашу работу с Вулканом.
Не знаю как вас, а меня смутило то, что при создании инстанса туда нужно передать список используемых лееров и расширений, мне сразу стало интересно - что это и нельзя ли получить список доступных нам лееров? Оказалось что можно, и для этого не нужно даже создавать инстанс Вулкана!
Для этих целей предназначена функция:
VkResult vkEnumerateInstanceLayerProperties(
uint32_t *pPropertyCount, VkLayerProperties* pProperties);
Предполагаю что эта функция будет описана позже, пока, забегая на перед, могу сказать что используется она так же как и описанные в этой главе vkEnumeratePhysicalDevices, где первый вызов функции с указателем на pPropertyCount и NULL вместо pProperties запрашивает количество доступных слоев, а второй вызов, с переданным pProperties нужного размера - возвращает массив строк. Если вам не жалко памяти, то сюда можно передать массив большего размера, актуальное количество элементов будет так же возвращено в pPropertyCount. Результат возвращается в виде структуры:
typedef struct VkLayerProperties {
char layerName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
uint32_t implementationVersion;
char description[VK_MAX_DESCRIPTION_SIZE];
} VkLayerProperties;
Аналогичным образом можно получить и список поддерживаемых расширений, для этого существует функция:
VkResult vkEnumerateInstanceExtensionProperties(
const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties);
const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties);
Отличие от предыдущей функции только в том, что первым параметром мы можем указать имя леера, для которого нам нужно получить список поддерживаемых расширений, если указать там NULL - получим полный(глобальный) список поддерживаемых расширений в виде VkExtensionProperties:
typedef struct VkExtensionProperties {
char extensionName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
} VkExtensionProperties;
Как видно из объявления структур, каждая из них имеет то самое имя слоя/расширения, которое нам нужно передать при создании инстанса + некоторую информацию о версии спецификации/имплементации. Коды ошибок в обоих случаях будут следующими:
Success
- VK_SUCCESS
- VK_INCOMPLETE
Failure
- VK_ERROR_OUT_OF_HOST_MEMORY
- VK_ERROR_OUT_OF_DEVICE_MEMORY
VulkanTest1.zip - здесь вы можете найти исходный код описанного примера и бинарник, он основан на примере cube.c из набора LoaderAndValidationLayers, предоставляемый Хроносом, который был разработан ими совместно с Valve Corporation и LunarG, Inc.
Как бы там ни было - демка подверглась некоторым переработкам :)
Комментариев нет:
Отправить комментарий