Graphics glossary 30 terms mentioned in this lesson
Select any term for a clear definition. The return button brings you back to the exact term link you used.
Vulkan is often introduced by the number of objects required before a pixel appears. That makes the API look like a ceremony whose purpose is to be completed as quickly as possible. Copy an initialisation function, accept the first device returned by enumeration and celebrate when a window changes colour. The colour is evidence that some work reached some device. It is not yet evidence that we understand which object owns that work or why the selected device can execute the renderer we intend to build.
Part II therefore begins with ownership and selection. The manual renderer let JavaScript and the browser supply an execution environment. Vulkan asks the application to state that environment: which API version it expects, which physical device satisfies its requirements, which queue family can execute and present the work, and which logical device enables the features used by later lessons. This explicitness is valuable only if each choice has a criterion.
What you should be able to account for
- Distinguish a Vulkan instance, physical device, logical device, queue family and queue.
- Request a Vulkan 1.3 teaching baseline without confusing that request with the loader's supported version.
- Select a device using reported capabilities and retain a written reason for every rejection.
Choose a baseline before choosing a device
The current Vulkan specification is newer than the minimum required by this course. Our compute renderer uses Vulkan 1.3 as its core baseline because synchronisation2 is core there and the resulting responsibilities are available without teaching an older and newer barrier vocabulary at the same time. A Vulkan 1.4 implementation can still create an application that requests 1.3. Hardware ray tracing remains feature and extension gated in Lessons 27 and 28; a high core version does not silently promise those facilities.
First ask what the loader supports. Rejecting a loader below the course baseline is better than creating an instance and discovering later that a required core feature cannot be enabled.
uint32_t loaderVersion = VK_API_VERSION_1_0;
VkResult versionResult = vkEnumerateInstanceVersion(&loaderVersion);
if (versionResult != VK_SUCCESS ||
loaderVersion < VK_API_VERSION_1_3) {
throw std::runtime_error("Vulkan 1.3 loader required");
}
VkApplicationInfo applicationInfo{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Inspectable Vulkan Ray Tracer",
.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0),
.pEngineName = "Course renderer",
.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0),
.apiVersion = VK_API_VERSION_1_3
};
The application version describes our program. The API version describes the Vulkan interface it requests. Neither value selects a GPU. That happens only after an instance has connected the application to the loader and the enabled instance extensions.
Create the instance deliberately
A window-system library may supply the platform-specific instance extensions needed to create a presentation surface. That library is allowed to create a window and report extension names. It is not allowed to choose the renderer's physical device, construct its rays or decide its synchronisation. Keep the boundary visible.
std::vector<const char*> instanceExtensions =
window.requiredVulkanInstanceExtensions();
#ifndef NDEBUG
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
const char* validationLayer = "VK_LAYER_KHRONOS_validation";
#endif
VkInstanceCreateInfo instanceInfo{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &applicationInfo,
.enabledExtensionCount =
static_cast<uint32_t>(instanceExtensions.size()),
.ppEnabledExtensionNames = instanceExtensions.data()
};
#ifndef NDEBUG
instanceInfo.enabledLayerCount = 1;
instanceInfo.ppEnabledLayerNames = &validationLayer;
#endif
VkInstance instance = VK_NULL_HANDLE;
check(vkCreateInstance(&instanceInfo, nullptr, &instance));
Before enabling the validation layer, enumerate layers and establish that it is present. Validation is a diagnostic witness. It can report many invalid API relationships, but a quiet validation log does not prove that the camera ray, intersection interval or final colour is correct. Part I remains our behavioural reference.
Enumerate physical devices as candidates
A physical device is a reported implementation, not an object we create. For each candidate, record its properties, Vulkan 1.3 features, memory properties, device extensions and queue families. The course baseline requires a queue family that can execute compute work and support presentation to the selected surface. We also require the swapchain device extension. We do not enable the ray-tracing extensions merely because one candidate reports them.
struct Candidate {
VkPhysicalDevice physical = VK_NULL_HANDLE;
uint32_t queueFamily = UINT32_MAX;
VkPhysicalDeviceProperties properties{};
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES
};
};
bool suitable(const Candidate& candidate) {
return candidate.queueFamily != UINT32_MAX
&& candidate.features13.synchronization2 == VK_TRUE
&& supportsDeviceExtension(
candidate.physical,
VK_KHR_SWAPCHAIN_EXTENSION_NAME);
}
The complete selector also checks that the surface has at least one usable format and presentation mode. A discrete device is not automatically superior. An integrated implementation satisfying every requirement may be the appropriate candidate, especially when unified memory avoids transfers that another device would require. We can rank suitable devices later. Suitability is the gate.
Worked Vulkan account
How Vulkan Decides Which Device May Be Used
Device selection is an acceptance decision. The application must establish the complete capability contract before it creates the logical device and retrieves a queue.
What the code must establish
The application enumerates physical devices and questions each one about API version, queue-family capabilities, presentation support and required features. Only a candidate satisfying that complete baseline may be used to create the logical device. The queue is then retrieved from the family and index requested during logical-device creation.
What to inspect
Keep the queried properties and feature chain beside the selection decision. Record the selected physical-device handle, queue-family index, enabled extensions and enabled features. The created queue must come from that stated family. A device name, vendor or broad classification such as discrete GPU is descriptive evidence, but it is not the acceptance test.
What this establishes, and what it does not: Creating the logical device and retrieving its queue establishes that the requested ownership chain was accepted. It does not establish that later resources are compatible, that commands are correct or that this device will render the scene faster.
Create one logical device and retrieve its queue
The logical device enables a chosen subset of the physical device's reported features and creates queues from named families. It owns most Vulkan objects constructed in subsequent lessons. This ownership is why a buffer created from one logical device cannot be submitted through a queue belonging to another.
float priority = 1.0f;
VkDeviceQueueCreateInfo queueInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = selected.queueFamily,
.queueCount = 1,
.pQueuePriorities = &priority
};
VkPhysicalDeviceVulkan13Features enabled13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.synchronization2 = VK_TRUE
};
const char* deviceExtensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
VkDeviceCreateInfo deviceInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &enabled13,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueInfo,
.enabledExtensionCount = 1,
.ppEnabledExtensionNames = deviceExtensions
};
VkDevice device = VK_NULL_HANDLE;
check(vkCreateDevice(selected.physical, &deviceInfo, nullptr, &device));
VkQueue queue = VK_NULL_HANDLE;
vkGetDeviceQueue(device, selected.queueFamily, 0, &queue);
VkQueue is retrieved, not independently created. The logical device creation request asked for one queue from the selected family; index zero retrieves that queue. Later commands will be recorded elsewhere and submitted here. Recording is not execution, and a queue handle is not a background thread promised to our application.
Reject a tempting device
Candidate A reports Vulkan 1.4 and hardware ray tracing but no presentation support for the chosen surface on its compute family. Candidate B reports Vulkan 1.3, synchronisation2, the swapchain extension and one family supporting compute and presentation. For the renderer built in Lessons 15 to 26, B satisfies the complete baseline and A does not. The ray-tracing features of A do not repair the missing presentation route. Lesson 27 may revisit device requirements, but it must not rewrite this decision after resources have already been created.
Name the owner
Which object owns an image created in Lesson 16: the instance, physical device, logical device, queue family or queue?
The answer
The image is created from and owned by the logical device. The physical device supplied the properties and memory capabilities used to choose that logical device. A command buffer may later use the image and a queue may execute those commands, but neither becomes the image's owner.
Official reference: consult the Khronos Vulkan initialisation chapter and extension-enabling guide when checking the current object and feature rules.
Make the selection explain itself
Extend the selector so every physical device produces a short report containing API version, device type, queue-family flags, presentation support, swapchain support and synchronisation2. Do not print only the winning device. A rejection without a recorded failed criterion is difficult to distinguish from an enumeration bug. Run the program with validation enabled, then deliberately request a device extension that no candidate supports and confirm that selection fails before vkCreateDevice.
Carry the ownership chain forward
Retain the instance, selected physical device, logical device, queue-family index and queue as separate fields in the renderer. The physical device will answer capability and memory questions. The logical device will create resources. The family index will create a compatible command pool. The queue will execute submissions. Collapsing those fields into an unnamed initialisation block may shorten a listing, but it removes the account needed when a later object is incompatible.
We have not rendered anything. That is the correct result for this lesson. We have established who may create the storage image, who reports its memory requirements and where recorded work will eventually be submitted. Lesson 16 now constructs the first device resource without pretending that an image handle is also its memory.