Creating a Logical Device#

A logical device (VkDevice) is an application’s view of the GPU. It is used for creating and managing most API objects, including:

  • Queues.

  • Memory.

  • Command buffers.

  • Graphics and compute pipelines.

  • Buffers.

  • Images.

  • Descriptor sets.

When a logical device is created, a number of command queues are also created alongside it. The supported operations of these queues are specified by selecting the queue family they will originate from. The logical device can then control the GPU by submitting command buffers via the associated queues.

The code example uses initLogicalDevice, which makes use of the device-level extensions that were identified previously. View in context.

void VulkanHelloAPI::initLogicalDevice(std::vector<std::string>& deviceExtensions)
{
    // There are priorities for queues (range: 0 - 1). Each queue in the same device is assigned a priority with higher priority queues
    // potentially being given more processing time than lower priority ones.
    // In this case there is only one, so it does not matter.
    float queuePriorities[1] = { 0.0f };

    // Populate the device queue creation info struct with the previously found compatible queue family
    // and number of queues to be created. Again, only one queue is needed.
    VkDeviceQueueCreateInfo deviceQueueInfo = {};
    deviceQueueInfo.pNext = nullptr;
    deviceQueueInfo.flags = 0;
    deviceQueueInfo.queueFamilyIndex = appManager.graphicsQueueFamilyIndex;
    deviceQueueInfo.pQueuePriorities = queuePriorities;
    deviceQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    deviceQueueInfo.queueCount = 1;

    // Declare and populate the logical device creation info struct. This will be used to create the logical device and its associated queues.
    // The device extensions that were looked up earlier are specified here. They will be initialised when the logical device
    // is created. Additionally, the physical device is queried for its supported features so the logical device can enable them.
    VkDeviceCreateInfo deviceInfo;
    deviceInfo.flags = 0;
    deviceInfo.pNext = nullptr;
    deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    deviceInfo.enabledLayerCount = 0;
    deviceInfo.ppEnabledLayerNames = nullptr;

    appManager.deviceExtensionNames.resize(deviceExtensions.size());
    for (uint32_t i = 0; i < deviceExtensions.size(); ++i) { appManager.deviceExtensionNames[i] = deviceExtensions[i].c_str(); }

    deviceInfo.enabledExtensionCount = static_cast<uint32_t>(appManager.deviceExtensionNames.size());
    deviceInfo.ppEnabledExtensionNames = appManager.deviceExtensionNames.data();
    deviceInfo.queueCreateInfoCount = 1;
    deviceInfo.pQueueCreateInfos = &deviceQueueInfo;
    VkPhysicalDeviceFeatures features;
    vk::GetPhysicalDeviceFeatures(appManager.physicalDevice, &features);
    features.robustBufferAccess = false;
    deviceInfo.pEnabledFeatures = &features;

    // Create the logical device using the deviceInfo struct defined above.
    debugAssertFunctionResult(vk::CreateDevice(appManager.physicalDevice, &deviceInfo, nullptr, &appManager.device), "Logic Device Creation");

    // Initialise the function pointers that require the device address. This is the same process as for the instance function pointers.
    if (!vk::initVulkanDevice(appManager.device)) { Log(true, "Could not initialise the device function pointers."); }
}