pNext and sType

People new to Vulkan will start to notice the pNext and sType variables all around the Vulkan Spec. The void* pNext is used to allow for expanding the Vulkan Spec by creating a Linked List between structures. The VkStructureType sType is used by the loader, layers, and implementations to know what type of struct was passed in by pNext. pNext is mostly used when dealing with extensions that expose new structures.

Two Base Structures

The Vulkan API provides two base structures, VkBaseInStructure and VkBaseOutStructure, to be used as a convenient way to iterate through a structure pointer chain.

The In of VkBaseInStructure refers to the fact pNext is a const * and are read-only to loader, layers, and driver receiving them. The Out of VkBaseOutStructure refers the pNext being used to return data back to the application.

Setting pNext Structure Example

// An example with two simple structures, "a" and "b"
typedef struct VkA {
    VkStructureType sType;
    void* pNext;
    uint32_t value;
} VkA;

typedef struct VkB {
    VkStructureType sType;
    void* pNext;
    uint32_t value;
} VkB;

// A Vulkan Function that takes struct "a" as an argument
// This function is in charge of populating the values
void vkGetValue(VkA* pA);

// Define "a" and "b" and set their sType
struct VkB b = {};
b.sType = VK_STRUCTURE_TYPE_B;

struct VkA a = {};
a.sType = VK_STRUCTURE_TYPE_A;

// Set the pNext pointer from "a" to "b"
a.pNext = (void*)&b;

// Pass "a" to the function
vkGetValue(&a);

// Use the values which were both set from vkGetValue()
printf("VkA value = %u \n", a.value);
printf("VkB value = %u \n", b.value);

Reading pNext Values Example

Underneath, the loader, layers, and driver are now able to find the chained pNext structures. Here is an example to help illustrate how one could implement pNext from the loader, layer, or driver point of view.

void vkGetValue(VkA* pA) {

    VkBaseOutStructure* next = reinterpret_cast<VkBaseOutStructure*>(pA->pNext);
    while (next != nullptr) {
        switch (next->sType) {

            case VK_STRUCTURE_TYPE_B:
                VkB* pB = reinterpret_cast<VkB*>(next);
                // This is where the "b.value" above got set
                pB->value = 42;
                break;

            case VK_STRUCTURE_TYPE_C:
                // Can chain as many structures as supported
                VkC* pC = reinterpret_cast<VkC*>(next);
                SomeFunction(pC);
                break;

            default:
                LOG("Unsupported sType %d", next->sType);
        }

        // This works because the first two values of all chainable Vulkan structs
        // are "sType" and "pNext" making the offsets the same for pNext
        next = reinterpret_cast<VkBaseOutStructure*>(next->pNext);
    }

    // ...
}