1
votes

Vulkan makes extensive use of structs in its functions for passing lots of arguments, and enabling extensions by daisy-chaining these structs together by using a structure type and "next" pointer as the first two members of each struct. For example, take this function:

VkResult vkCreateInstance(
    const VkInstanceCreateInfo*   pCreateInfo,
    const VkAllocationCallbacks*  pAllocator,
    VkInstance*                   pInstance);

Description of VkInstanceCreateInfo:

typedef struct VkInstanceCreateInfo {
    VkStructureType           sType;
    const void*               pNext;
    VkInstanceCreateFlags     flags;
    const VkApplicationInfo*  pApplicationInfo;
    uint32_t                  enabledLayerCount;
    const char* const*        ppEnabledLayerNames;
    uint32_t                  enabledExtensionCount;
    const char* const*        ppEnabledExtensionNames;

What intrigues me about this is that, from what I can tell, the packaging of struct members is at the discretion of the compiler (except for the ordering, which is not). This would be in contrast to a COM-based API which does not suffer from compiler-dependent issues, as far as I know. I've been looking through the Vulkan headers expecting to find compiler-specific alignment pragmas/statements, but nothing stands out.

Looking at pages like the data structure alignment Wikipedia page would suggest that the common, well-known compilers follow certain rules on x86:

The type of each member of the structure usually has a default alignment, meaning that it will, unless otherwise requested by the programmer, be aligned on a pre-determined boundary. The following typical alignments are valid for compilers from Microsoft (Visual C++), Borland/CodeGear (C++Builder), Digital Mars (DMC), and GNU (GCC) when compiling for 32-bit x86...

but the use of weasel words like "usually" and "typical" makes it unreliable to me. A paragraph from this SO answer is more compelling:

IMPORTANT NOTE: Both the C and C++ standards state that structure alignment is implementation-defined. Therefore each compiler may choose to align data differently, resulting in different and incompatible data layouts. For this reason, when dealing with libraries that will be used by different compilers, it is important to understand how the compilers align data. Some compilers have command-line settings and/or special #pragma statements to change the structure alignment settings.

If I'm using the Vulkan SDK compiled by one compiler with one set of rules, and I'm writing a client application using another compiler, isn't there the potential for alignment problems? What am I missing here?

1

1 Answers

4
votes

If I'm using the Vulkan SDK compiled by one compiler with one set of rules, and I'm writing a client application using another compiler, isn't there the potential for alignment problems?

I can write a program compiled by GCC that links (dynamically or otherwise) to a program compiled by Clang. Or Visual Studio on the platforms it supports. Or any other compiler. And from my compiler, I can call functions that pass structs of all kinds to the code compiled under the destination compiler.

And it works. Why?

Because the behavior of what happens at the boundaries of these libraries/SO/DLL/executables is not defined by the compiler; it's defined by the platform. Inter-library communication is governed by a communication protocol agreed to by all compilers on that platform. It's how you can make OS system calls to code that may not have even been compiled. It's because the source and the destination have agreed on a calling convention and an application binary interface (ABI).

It is the system's ABI which defines what a struct layout looks like. If a compiler is compiling code that attempts to pass a struct across ABI boundaries, the compiler must make sure that the layout of the struct for the code it generates conforms to the ABI. For a type that the compiler is sure won't transition across ABI boundaries, it could do whatever it wants, but generally speaking, it'll still use the ABI's conventions.

Linux systems use the Itanium ABI. Windows has its own ABI. Each platform has an ABI, and this is what compilers which compile to that platform conform to.

So the only potential for layout problems is if you're sending those structs to another operating system. Which, given that Vulkan is a low-level API, is probably not a good idea. And is certainly not an intended use case for Vulkan.