0关于vulkan

1)定义了一堆的概念,跟opengl太不一样了,以至于出来很多年了,实际普及率很低。
2)驱动极大简化,很多工作都丢给了开发者维护。

Vulkan不像opengl,一个Context太过于抽象和笼统。vulkan的概念更加切合实际,走进vulkan,首先要理解device。
在这里插入图片描述

0.1 vulkan pattern

获取属性接口的pattern,
XXXGet(int* counter, XXXProperty* arr); 参数可能还包含一个作用域 比较device,之类。
一般两次调用,

  1. 获取个数:后面array传nullptr,第一个参数传入counter的地址,
  2. 创建动态数组以后,
  3. 精准填充数组,传入参数和数组地址即可,

0.2 name pattern

结构体V大写,函数vk都小写,

1 ApplicationCreateInfo,

填充VkApplicationInfo结构体,
apiVersion

  • apiVersion = VK_MAKE_VERSION(1, 0, 0);
  • 或者 apiVersion = VK_API_VERSION_1_1

2 Instance

创建Instance之前,先一顿骚操作,查询各种关联信息。

1、获取instance个数,
android上测试获取instance个数失败,始终为0,

    uint32_t instance_layer_count = 0;
    // use nullptr for second parameters in order to get all the layer count
    vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr);
    if (instance_layer_count == 0) {
        // return;
    }
    VkLayerProperties instance_properties[50];  // 方便调试所以用栈
    vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_properties);

2、获取扩展信息ok的,

    uint32_t extension_count = 0;
    auto xxhr = vkEnumerateInstanceExtensionProperties(
            nullptr, &extension_count, nullptr);
    VkExtensionProperties extension_availables[extension_count];
    vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_availables);

p40pro上获取到信息如下:

(VkExtensionProperties[50]) {
[0] = (extensionName = “VK_KHR_surface”, specVersion = 25)
[1] = (extensionName = “VK_KHR_android_surface”, specVersion = 6)
[2] = (extensionName = “VK_EXT_swapchain_colorspace”, specVersion = 4)
[3] = (extensionName = “VK_KHR_get_surface_capabilities2”, specVersion = 1)
[4] = (extensionName = “VK_EXT_debug_report”, specVersion = 9)
[5] = (extensionName = “VK_KHR_device_group_creation”, specVersion = 1)
[6] = (extensionName = “VK_KHR_external_fence_capabilities”, specVersion = 1)
[7] = (extensionName = “VK_KHR_external_memory_capabilities”, specVersion = 1)
[8] = (extensionName = “VK_KHR_external_semaphore_capabilities”, specVersion = 1)
[9] = (extensionName = “VK_KHR_get_physical_device_properties2”, specVersion = 2)

3、创建Instance
先填充VkInstanceCreateInfo结构体,要引用上面ApplicationInfo,
Instance的扩展属性需要分平台填充,一些demo中都是如下代码直接给出的,不科学。

    instance_extensions.push_back("VK_KHR_surface");
    instance_extensions.push_back("VK_KHR_android_surface");

正确分平台获取方式

    instance_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);	// 
#ifdef __ANDROID
    instance_extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); // vulkan_android.h 中
#elif defined(__WIN32)
    instance_extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(__linux)
    instance_extensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#elif defined(__APPLE__)
    instance_extensions.push_back(VK_KHR_MACOS_SURFACE_EXTENSION_NAME);
#endif

3 创建surface

pattern=AndroidSurface
填充结构体:Vk{pattern}CreateInfoKHR
创建函数,vkCreate{pattern}KHR

4 GPU/physical Device

创建之前,还是先查询,

4.1 穷举物理设备的个数

还是相同套路,先获取个数,再获取数组内容,
一般android上就一个GPU设备,

    // Find one GPU to use:
    // On Android, every GPU device is equal -- supporting
    // graphics/compute/present
    // for this sample, we use the very first GPU device found on the system
    uint32_t gpuCount = 0;
    CALL_VK(vkEnumeratePhysicalDevices(device.instance_, &gpuCount, nullptr));

    VkPhysicalDevice tmpGpus[gpuCount];
    CALL_VK(vkEnumeratePhysicalDevices(device.instance_, &gpuCount, tmpGpus));
    device.gpuDevice_ = tmpGpus[0];  // Pick up the first GPU Device

4.2 获取属性

    // 获取属性 deviceName是厂家GPU型号
    VkPhysicalDeviceProperties vk_physical_device_properties;
    vkGetPhysicalDeviceProperties(device.gpuDevice_, &vk_physical_device_properties);

    // 获取特性
    VkPhysicalDeviceFeatures vk_physical_device_features;
    vkGetPhysicalDeviceFeatures(device.gpuDevice_, &vk_physical_device_features);

    vkGetPhysicalDeviceMemoryProperties(device.gpuDevice_,
                                        &device.gpuMemoryProperties_);

DeviceProperty是gpu厂商名称,vendorID,驱动版本。

(lldb) p vk_physical_device_properties
(VkPhysicalDeviceProperties) {
  apiVersion = 4198591
  driverVersion = 142606336
  vendorID = 5045
  deviceID = 1913716736
  deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
  deviceName = "Mali-G76\0"
  pipelineCacheUUID = "\x8b\r\x85ց\xecb#\xde\xf6>ݮb\xc7\U00000011"
  limits = // 其他扩展信息
}

DeviceFeature和MemoryProoerty都是一些专业参数,

5 QueueFamily

队列家族概念的理解

队列的类型如下:

typedef enum VkQueueFlagBits {
    VK_QUEUE_GRAPHICS_BIT = 0x00000001,
    VK_QUEUE_COMPUTE_BIT = 0x00000002,
    VK_QUEUE_TRANSFER_BIT = 0x00000004,
    VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
    VK_QUEUE_PROTECTED_BIT = 0x00000010,
    VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkQueueFlagBits;

至少得有一个graphics队列才能正常渲染,否则就得退出程序了。
p40上测试,最多支持16个队列,实际获取的只有一个队列,类型为graphics。

    // Find a GFX queue family
    uint32_t queueFamilyCount;
    vkGetPhysicalDeviceQueueFamilyProperties(device.gpuDevice_, &queueFamilyCount,
                                             nullptr);
    assert(queueFamilyCount);
    std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(device.gpuDevice_, &queueFamilyCount,
                                             queueFamilyProperties.data());

VkQueueFamilyProperties::queueFlags 代表队列类型,上文的VkQueueFlagBits

6 创建Device

vkCreateDevice,在制定物理设备(GPU)上创建一个逻辑设备,参数如下:

  • physicalDevice 指定在哪一个物理设备上创建逻辑设备。
  • pCreateInfo 创建逻辑设备的配置信息。
  • pAllocator 内存分配器。为 nullptr 表示使用内部默认分配器,否则为自定义分配器。
  • pDevice 创建逻辑设备的结果

VkDeviceCreateInfo 要关联VkDeviceQueueCreateInfo数组,
VkDeviceQueueCreateInfo::queueFamilyIndex 必须 是目标物理设备中有效的设备队列族索引,

6.2 获取queue

获取队列保存handle,

  vkGetDeviceQueue(device.device_, 0, 0, &device.queue_);

queueFamilyIndex=0
graphics_queueIndex=0

附录

CreateVulkanDevice代码

// Create vulkan device
void CreateVulkanDevice(ANativeWindow *platformWindow,
                        VkApplicationInfo *appInfo) {
    std::vector<const char *> instance_extensions;
    std::vector<const char *> device_extensions;

    //instance_extensions.push_back("VK_KHR_surface");
    //instance_extensions.push_back("VK_KHR_android_surface");
    instance_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);	//
    instance_extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);

    device_extensions.push_back("VK_KHR_swapchain");

    // #Section1 Instance

    uint32_t instance_layer_count = 0;
    // use nullptr for second parameters in order to get all the layer count
    vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr);
    if (instance_layer_count == 0) {
        // return;
    }

    VkLayerProperties instance_properties[5];  // 方便调试所以用栈
    vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_properties);

    uint32_t extension_count = 0;
    auto xxhr = vkEnumerateInstanceExtensionProperties(
            nullptr, &extension_count, nullptr);
    VkExtensionProperties extension_availables[extension_count];
    vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_availables);

    // **********************************************************
    // Create the Vulkan instance
    VkInstanceCreateInfo instanceCreateInfo{
            .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
            .pNext = nullptr,
            .pApplicationInfo = appInfo,
            .enabledLayerCount = 0,
            .ppEnabledLayerNames = nullptr,
            .enabledExtensionCount =
            static_cast<uint32_t>(instance_extensions.size()),
            .ppEnabledExtensionNames = instance_extensions.data(),
    };
    CALL_VK(vkCreateInstance(&instanceCreateInfo, nullptr, &device.instance_));

    // #Section2 surface
    VkAndroidSurfaceCreateInfoKHR createInfo{
            .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
            .pNext = nullptr,
            .flags = 0,
            .window = platformWindow};

    CALL_VK(vkCreateAndroidSurfaceKHR(device.instance_, &createInfo, nullptr,
                                      &device.surface_));

    // #Section3 physical devices
    // Find one GPU to use:
    // On Android, every GPU device is equal -- supporting
    // graphics/compute/present
    // for this sample, we use the very first GPU device found on the system
    uint32_t gpuCount = 0;
    CALL_VK(vkEnumeratePhysicalDevices(device.instance_, &gpuCount, nullptr));

    VkPhysicalDevice tmpGpus[gpuCount];
    CALL_VK(vkEnumeratePhysicalDevices(device.instance_, &gpuCount, tmpGpus));
    device.gpuDevice_ = tmpGpus[0];  // Pick up the first GPU Device

    // 获取属性 deviceName是厂家GPU型号
    VkPhysicalDeviceProperties vk_physical_device_properties;
    vkGetPhysicalDeviceProperties(device.gpuDevice_, &vk_physical_device_properties);

    // 获取特性
    VkPhysicalDeviceFeatures vk_physical_device_features;
    vkGetPhysicalDeviceFeatures(device.gpuDevice_, &vk_physical_device_features);

    vkGetPhysicalDeviceMemoryProperties(device.gpuDevice_,
                                        &device.gpuMemoryProperties_);

    // #Section4 queue family
    // Find a GFX queue family
    uint32_t queueFamilyCount;
    vkGetPhysicalDeviceQueueFamilyProperties(device.gpuDevice_, &queueFamilyCount,
                                             nullptr);
    assert(queueFamilyCount);
    std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(device.gpuDevice_, &queueFamilyCount,
                                             queueFamilyProperties.data());

    uint32_t queueFamilyIndex;
    for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++) {
        if (queueFamilyProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
            break;
        }
    }

    // {{ 遍历queueFamily
    auto fill_index = [&](VkQueueFlagBits filter, int& dstSlot) {
        for (int k = 0; k < queueFamilyCount; k++) {
            if (queueFamilyProperties[k].queueFlags & filter) {
                dstSlot = k;
                break;
            }
        }
    };

    int graphics_queue_index = -1;
    int compute_queue_index = -1;
    int sparse_queue_index = -1;
    fill_index(VK_QUEUE_GRAPHICS_BIT, graphics_queue_index);
    fill_index(VK_QUEUE_COMPUTE_BIT, compute_queue_index);
    fill_index(VK_QUEUE_SPARSE_BINDING_BIT, sparse_queue_index);

    VkQueue graphics_queue;
    VkQueue compute_queue;
    VkQueue sparse_queue;
    // 遍历queueFamily }}

    assert(queueFamilyIndex < queueFamilyCount);
    device.queueFamilyIndex_ = queueFamilyIndex;
    // Create a logical device (vulkan device)
    float priorities[] = {
            1.0f,
    };
    VkDeviceQueueCreateInfo queueCreateInfo{
            .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .queueFamilyIndex = device.queueFamilyIndex_,
            .queueCount = 1,
            .pQueuePriorities = priorities,
    };

    VkDeviceCreateInfo deviceCreateInfo{
            .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
            .pNext = nullptr,
            .queueCreateInfoCount = 1,
            .pQueueCreateInfos = &queueCreateInfo,
            .enabledLayerCount = 0,
            .ppEnabledLayerNames = nullptr,
            .enabledExtensionCount = static_cast<uint32_t>(device_extensions.size()),
            .ppEnabledExtensionNames = device_extensions.data(),
            .pEnabledFeatures = &vk_physical_device_features,
    };

    CALL_VK(vkCreateDevice(device.gpuDevice_, &deviceCreateInfo, nullptr,
                           &device.device_));
    vkGetDeviceQueue(device.device_, 0, 0, &device.queue_);
}

更多推荐