Device

说明

在创建了Vulkan Instance 对象后,下一步就是枚举physical devices,选择其中一个,并根据它创建又给logical device,这些操作通过instance-level级函数进行执行.

Vulkan里几乎所有工作都是在logical device上执行的:在其上创建资源,管理他们的内存,记录由它们创建的command buffers,向他们的queues提交处理commands.

physical device

枚举所有可以使用的physical devices

1
2
3
4
5
6
uint32_t devices_count = 0;
vkEnumeratePhysicalDevices( instance, &devices_count,
nullptr ).
std::vector<VkPhysicalDevice> available_devices(devices_count);
vkEnumeratePhysicalDevices( instance, &devices_count,
&available_devices[0]).

extensions

1
2
3
4
uint32_t extensions_count = 0;
vkEnumerateDeviceExtensionProperties(physical_device,nullptr,&extensions_count,nullptr);
std::vector<VkExtensionProperties> available_extensions(extensions_count);
vkEnumerateDeviceExtensionProperties(physical_device,nullptr,&extensions_count,&available_extensions[0]);

获取pyhsical device的features和properties

1
2
3
4
5
vkEnumeratePhysicalDevices()
VkPhysicalDeviceFeatures device_features;//支持的特性,支持的shader类型(是否支持geometry,tessellation),depth clamp 和 bias,multiple viewports,或者wide lines.
VkPhysicalDeviceProperties device_properties;//物理设备的属性,名字,驱动版本,api支持版本,设备类型(可能是CPU),limits(texture大小,buffer数量)
vkGetPhysicalDeviceFeatures(physical_device, &device_features);
vkGetPhysicalDeviceProperties(physical_device,&device_properties);

logic device

检查可用的queue families和他们的properties

在vulkan里,对硬件的所有操作都是通过提交到queues里进行的.提交到同一个queue里的命令是一个接着一个执行的,提交到不同queue里的命令是独立的(需要同步他们)

不同的queues可能表示硬件的不同部分,因此会支持不同的操作,并非所有操作都可以在所有queue上执行.

有相同策略的queue被聚合在同样的family里,一个device可能暴露任意数量的queue families,每一个family可能包括一个或多个queues.要想检查硬件能执行什么操作,需要便利所有queue families的属性.

步骤

1
2
3
4
5
6
uint32_t queue_families_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties( physical_device,
&queue_families_count, nullptr ).
std::vector<VkQueueFamilyProperties> queue_families(queue_families_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device,
&queue_families_count, &queue_families[0]);

从properties中能获取到的最重要的信息是能在已给family的queues里进行的操作类型,操作类型被分为:

  • Graphics:创建graphics pipelines和drawing
  • Compute:创建compute pipelines和dispatching compute shaders
  • Transfer:用于非常快速的memory-copying操作
  • Sparse:允许附加的内存管理功能

family的queues可能支持不知一种操作,可能不同的queue families支持相同类型的操作.

family属性也告知了queues的数量,是否支持时间戳,图像传输操作的粒度(在copy/blit操作时能控制的图像的最小部分).

创建logic device需要的queue families数量,属性,每个family可用的queues数量准备好后,只在创建logical device时请求他们,需要制定需要多少queue以及来自哪些families,当创建好logic device后,对应的queue也自动被创建,我们只需要获得请求的queue的handles.

重要属性 TODO

选择期望的queue family

选择一个physical device

1
2
3
4
5
6
7
8
9
10
11
uint32_t queue_family_index = 0;
VkQueueFlags desired_capabilities;//将期望的操作类型存储到该flag
//VK_QUEUE_GRAPHICS_BIT,VK_QUEUE_COMPUTE_BIT,
//VK_QUEUE_TRANSFER_BIT or VK_QUEUE_SPARSE_BINDING_BIT
std::vector<VkQueueFamilyProperties> queue_families;
//将上文获取到的结果存储到queue_families
对queue_families所有元素进行判断
检查其queues数量是否>0
检查desired_capabilities变量和queueFlags逻辑与操作不为0
如果二者都通过,则存储索引到queue_family_index并跳出循环
重复上述操作知道所有元素被检查

创建logical devcie

logical device是app里创建的最重要的object,它代表了真实硬件,包括所有的扩展和激活的特性,以及向它请求的所有队列.

logical device允许我们做渲染软件里的所有工作,比如创建images和buffers,设置pipeline state或者加载shaders.最终要的是记录commands(比如draw calls或者分发computational works)和提交它们到queues.

准备

1
2
3
4
struct QueueInfo {
uint32_t FamilyIndex;
std::vector<float> Priorities;
};
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
std::vector<VkExtensionProperties> available_extensions;
if( !CheckAvailableDeviceExtensions( physical_device, available_extensions
) ) {
return false;
}
for( auto & extension : desired_extensions ) {
if( !IsExtensionSupported( available_extensions, extension ) ) {
std::cout << "Extension named '" << extension << "' is not supported by
a physical device." << std::endl;
return false;
}
}
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
for( auto & info : queue_infos ) {
queue_create_infos.push_back( {
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
nullptr,
0,
info.FamilyIndex,
static_cast<uint32_t>(info.Priorities.size()),
info.Priorities.size() > 0 ? &info.Priorities[0] : nullptr
} );
};
//Layers和extensions为非必需的
//但如果想使用高级特性(geometry,tessellation shaders)需要激活他们,
//不需要的特性可以关闭它们(有一些特性可能会影响性能)
VkDeviceCreateInfo device_create_info = {
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
nullptr,
0,
static_cast<uint32_t>(queue_create_infos.size()),
queue_create_infos.size() > 0 ? &queue_create_infos[0] : nullptr,
0,
nullptr,
static_cast<uint32_t>(desired_extensions.size()),
desired_extensions.size() > 0 ? &desired_extensions[0] : nullptr,
desired_features
};

vkCreateDevice( physical_device, &device_create_info,
nullptr, &logical_device ).

获取device queue

device queue是硬件资源,只能获取已有数量的

前提

1
2
3
4
获取到VkDvice logical_dvice
选取了queueFamilyIndx
选择一个queue_indexx
准备变量VkQueue queue
1
2
3
调用
vkGetDeviceQueue(logical_device, queue_family_index,
queue_index, &queue);

重复上述操作可以获取到所有queue families里的所有queues

创建有geometry shaders,graphics和compute queues的logical device

现在现需要美剧所有physical device找到其中支持geometry shaders,graphics和compute queues的来创建logical device

前期准备

1
2
3
4
声明变量
VkDevice logical_device;
VkQueue graphics_queue;
VkQueue compute_queue;
1
2
std::vector<VkPhysicalDevice> physical_devices;
EnumerateAvailablePhysicalDevices( instance, physical_devices );

对每一个physical device进行如下操作

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
for( auto & physical_device : physical_devices ) {
VkPhysicalDeviceFeatures device_features;
VkPhysicalDeviceProperties device_properties;
GetTheFeaturesAndPropertiesOfAPhysicalDevice( physical_device,
device_features, device_properties );
//geometry shader
if( !device_features.geometryShader ) {
continue;
} else {
device_features = {};
device_features.geometryShader = VK_TRUE;
}
//graphic queue
uint32_t graphics_queue_family_index;
if( !SelectIndexOfQueueFamilyWithDesiredCapabilities( physical_device,
VK_QUEUE_GRAPHICS_BIT, graphics_queue_family_index ) ) {
continue;
}
//compute queue
uint32_t compute_queue_family_index;
if( !SelectIndexOfQueueFamilyWithDesiredCapabilities( physical_device,
VK_QUEUE_COMPUTE_BIT, compute_queue_family_index ) ) {
continue;
}
std::vector<QueueInfo> requested_queues = { {
graphics_queue_family_index, { 1.0f } } };
if( graphics_queue_family_index != compute_queue_family_index ) {
requested_queues.push_back( { compute_queue_family_index, { 1.0f } } );
}

如果graphics和computee queue families有相同的index,我们能使用同一个queue,如果不同就要用两个

创建logical device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if( !CreateLogicalDevice( physical_device, requested_queues, {},
&device_features, logical_device ) ) {
continue;
} else {
if( !LoadDeviceLevelFunctions( logical_device, {} ) ) {
return false;
}
GetDeviceQueue( logical_device, graphics_queue_family_index, 0,
graphics_queue );
GetDeviceQueue( logical_device, compute_queue_family_index, 0,
compute_queue );
return true;
}
}
return false;

Destroying a logical device

按照创建的相反顺序销毁资源

1
2
3
4
if( logical_device ) {
vkDestroyDevice( logical_device, nullptr );
logical_device = VK_NULL_HANDLE;
}