2

In the Vulkan header vulkan.h there is a struct defined as

typedef struct VkSwapchainCreateInfoKHR {
    VkStructureType                  sType;
    const void*                      pNext;
    VkSwapchainCreateFlagsKHR        flags;
    VkSurfaceKHR                     surface;
    uint32_t                         minImageCount;
    VkFormat                         imageFormat;
    VkColorSpaceKHR                  imageColorSpace;
    VkExtent2D                       imageExtent;
    uint32_t                         imageArrayLayers;
    VkImageUsageFlags                imageUsage;
    VkSharingMode                    imageSharingMode;
    uint32_t                         queueFamilyIndexCount;
    const uint32_t*                  pQueueFamilyIndices;
    VkSurfaceTransformFlagBitsKHR    preTransform;
    VkCompositeAlphaFlagBitsKHR      compositeAlpha;
    VkPresentModeKHR                 presentMode;
    VkBool32                         clipped;
    VkSwapchainKHR                   oldSwapchain;
} VkSwapchainCreateInfoKHR;

I've used the following code to see each field's alignment (Visual Studio 2015)

    std::cout <<
        "sType: " << offsetof(VkSwapchainCreateInfoKHR, sType) << std::endl <<
        "pNext: " << offsetof(VkSwapchainCreateInfoKHR, pNext) << std::endl <<
        "flags: " << offsetof(VkSwapchainCreateInfoKHR, flags) << std::endl <<
        "surface: " << offsetof(VkSwapchainCreateInfoKHR, surface) << std::endl <<
        "minImageCount: " << offsetof(VkSwapchainCreateInfoKHR, minImageCount) << std::endl <<
        "imageFormat: " << offsetof(VkSwapchainCreateInfoKHR, imageFormat) << std::endl <<
        "imageColorSpace: " << offsetof(VkSwapchainCreateInfoKHR, imageColorSpace) << std::endl <<
        "imageExtent: " << offsetof(VkSwapchainCreateInfoKHR, imageExtent) << std::endl <<
        "imageArrayLayers: " << offsetof(VkSwapchainCreateInfoKHR, imageArrayLayers) << std::endl <<
        "imageUsage: " << offsetof(VkSwapchainCreateInfoKHR, imageUsage) << std::endl <<
        "imageSharingMode: " << offsetof(VkSwapchainCreateInfoKHR, imageSharingMode) << std::endl <<
        "queueFamilyIndexCount: " << offsetof(VkSwapchainCreateInfoKHR, queueFamilyIndexCount) << std::endl <<
        "pQueueFamilyIndices: " << offsetof(VkSwapchainCreateInfoKHR, pQueueFamilyIndices) << std::endl <<
        "preTransform: " << offsetof(VkSwapchainCreateInfoKHR, preTransform) << std::endl <<
        "compositeAlpha: " << offsetof(VkSwapchainCreateInfoKHR, compositeAlpha) << std::endl <<
        "presentMode: " << offsetof(VkSwapchainCreateInfoKHR, presentMode) << std::endl <<
        "clipped: " << offsetof(VkSwapchainCreateInfoKHR, clipped) << std::endl <<
        "oldSwapchain: " << offsetof(VkSwapchainCreateInfoKHR, oldSwapchain) << std::endl <<
        std::endl;

And got these results

sType: 0
pNext: 8
flags: 16
surface: 24
minImageCount: 32
imageFormat: 36
imageColorSpace: 40
imageExtent: 44
imageArrayLayers: 52
imageUsageFlags: 56
imageSharingMode: 60
queueFamilyIndexCount: 64
pQueueFamilyIndices: 72
preTransform: 80
compositeAlpha: 84
presentMode: 88
clipped: 92
oldSwapchain: 96

Between the fields flags and surface there is an 8 byte gap, even though flags has an underlying type of uint32_t. The same is true with the fields queueFamilyIndexCount and pQueueFamilyIndices. Why do flags and queueFamilyIndexCount take up 8 bytes when they are only 4 bytes wide and every other field of type uint32_t takes up only 4 bytes? Is there something special about the memory alignment requirements at those offsets?

rhynodegreat
  • 185
  • 1
  • 2
  • 5

2 Answers2

4

VkSurfaceKHR is a non-dispatchable handle. Which, by Vulkan's definitions, is a 64-bit integer. And therefore it must have 8-byte alignment.

When structures are being laid out, the compiler will ensure that each member gets the alignment that the type requires. If surface had come immediately after flags, it would not have 8-byte alignment. Thus, the compiler inserts 4 bytes of padding between the two.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • The issue is that `flags` is taking up 8 bytes. – rhynodegreat Aug 09 '16 at 22:12
  • 6
    @rhynodegreat: No, it isn't. It takes up 4 bytes. Following it are four *unusued bytes*, so that the next field can be 8-byte aligned. – Nicol Bolas Aug 09 '16 at 22:13
  • 1
    Ah, so the padding is added because the next field is 8 bytes and needs to be aligned. Same with `queueFamilyIndexCount`, which is followed by a pointer. – rhynodegreat Aug 09 '16 at 22:15
  • Technically there is no "padding", just alignment. `surface` is aligned at a multiple of 8 bytes because it's a 64 bit value. If you inserted another uint32_t between `flags` and `surface`, the offset to `surface` would remain the same. – Tibrogargan Aug 09 '16 at 22:18
1

All type have a size sizeof(T), and an alignment requirement alignof(T). Instances of it always need to be laid out in memory at addresses that are multiple of alignof(T).

In a struct, the compiler automatically inserts padding between the elements such that this requirement is fulfilled for all its elements, relative to the start address of the struct (i.e. for the offsetof values). This is empty space between subsequent struct elements.

It also sets the alignof of the whole struct, such that all its elements will be correctly aligned in memory. For example on a typical 64 bit platform:

struct A {
    char c;    // sizeof(char) == 1; alignof(char) == 1
    int* ptr;  // sizeof(char) == 8; alignof(char) == 8
}

Pointers have a size of 8 byte (64 bit), and also an alignment requirement of 8 byte. In this struct there will be 7 bytes of padding between c and ptr, such that offsetof(A, c) == 0 and offsetof(A, ptr) == 8. And alignof(A) == 8. Whenever an instance A a is created (on stack or on heap), &a % 8 == 0, and so &(a.ptr) % 8 == 0.


This is done because some CPU instructions (for example SIMD vector instructions) require their operands to be aligned at word boundaries, i.e. multiples of 8 (or 4 for 32 bit) in memory. Without the proper alignment these instructions could not be directly used and the program would become slower.


In this example VkSurfaceKHR and pQueueFamilyIndices are pointers with alignof == 8.

tmlen
  • 8,533
  • 5
  • 31
  • 84