Vulkan logical device
The concept
This is the final step of initialization. If the Vulkan physical device is the hardware graphics card, the logical device is the software interface we use to communicate with it. This is where we enable the specific features we need.
Gamegine activates modern Vulkan 1.3 features, including dynamic rendering (which eliminates the need for heavy RenderPass objects).
Feature configuration
Vulkan uses a chain of pointers (pNext) to extend configuration structures.
Here, we chain the structures to activate Vulkan 1.1, 1.3, and extension
features.
// Setup Features - explicitly enable features if supported
vk::PhysicalDeviceVulkan13Features vk13Features{};
if (supportedVk13Features.dynamicRendering) {
vk13Features.dynamicRendering = VK_TRUE;
}
if (supportedVk13Features.synchronization2) {
vk13Features.synchronization2 = VK_TRUE;
}
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT
extendedDynamicState{};
const auto& supportedExtendedDynamicState =
supportedFeatures
.get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>();
if (supportedExtendedDynamicState.extendedDynamicState) {
extendedDynamicState.extendedDynamicState = VK_TRUE;
}
// Setup base features (samplerAnisotropy)
vk::PhysicalDeviceFeatures2 baseFeatures{};
if (supportedFeatures.get<vk::PhysicalDeviceFeatures2>()
.features.samplerAnisotropy) {
baseFeatures.features.samplerAnisotropy = VK_TRUE;
}
// Manually set up the pNext chain to ensure it's correct
extendedDynamicState.pNext = nullptr;
vk13Features.pNext = &extendedDynamicState;
vk::PhysicalDeviceVulkan11Features vk11Features{};
vk11Features.pNext = &vk13Features;
baseFeatures.pNext = &vk11Features;Device creation
Finally, we create the device by providing it with the list of required device extensions (such as swapchain, see Vulkan physical device) and information about the queue found in Vulkan queue families.
// Setup Device Info
vk::DeviceCreateInfo deviceCreateInfo{};
deviceCreateInfo.pNext = &baseFeatures;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(
deviceExtensions.size());
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
d_device = vk::raii::Device(d_physicalDevice, deviceCreateInfo);
d_graphicsQueue = vk::raii::Queue(d_device,
d_graphicsQueueFamilyIndex,
0);What comes next
Now that our logical device is created and our graphics queue is ready to receive commands, the GPU is technically capable of rendering images. However, we still have a major problem: where do we put these rendered images so the user can see them?
The Vulkan surface we created earlier is just an abstract connection to the window; it does not hold any actual pixels. To display a continuous stream of frames without tearing or flickering, we need an infrastructure that manages a queue of images waiting to be displayed on the screen. That infrastructure is the Swapchain — the bridge that takes finished frames from our logical device and presents them to the surface.