Reputation: 195
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?
Upvotes: 2
Views: 622
Reputation: 9100
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
.
Upvotes: 1
Reputation: 474456
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.
Upvotes: 4