首页 > 解决方案 > Vulkan 和结构成员对齐

问题描述

Vulkan 在其函数中广泛使用结构来传递大量参数,并通过使用结构类型和“下一个”指针作为每个结构的前两个成员将这些结构以菊花链方式连接在一起来启用扩展。例如,使用这个函数:

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

说明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;

对此我感兴趣的是,据我所知,结构成员的打包由编译器自行决定(除了排序,它不是)。据我所知,这与基于 COM 的 API 形成对比,后者不受编译器相关问题的影响。我一直在查看 Vulkan 标头,希望找到特定于编译器的对齐编译指示/语句,但没有什么特别突出的。

查看诸如数据结构对齐维基百科页面之类的页面会表明常见的知名编译器遵循 x86 上的某些规则:

结构的每个成员的类型通常都有一个默认对齐方式,这意味着除非程序员另有要求,否则它将在预先确定的边界上对齐。在为 32 位 x86 进行编译时,以下典型对齐对来自 Microsoft (Visual C++)、Borland/CodeGear (C++Builder)、Digital Mars (DMC) 和 GNU (GCC) 的编译器有效...

但是像“通常”和“典型”这样的狡猾词的使用让我不可靠。这个 SO 答案中的一段更引人注目:

重要提示:C 和 C++ 标准都声明结构对齐是实现定义的。因此,每个编译器可能会选择不同的数据对齐方式,从而导致不同且不兼容的数据布局。因此,在处理将由不同编译器使用的库时,了解编译器如何对齐数据非常重要。一些编译器具有命令行设置和/或特殊的#pragma 语句来更改结构对齐设置。

如果我使用由一个编译器和一组规则编译的 Vulkan SDK,并且我正在使用另一个编译器编写客户端应用程序,是否存在对齐问题的可能性?我在这里想念什么?

标签: cdata-structuresstructvulkan

解决方案


如果我使用由一个编译器和一组规则编译的 Vulkan SDK,并且我正在使用另一个编译器编写客户端应用程序,是否存在对齐问题的可能性?

我可以编写一个由 GCC 编译的程序,该程序链接(动态或以其他方式)到由 Clang 编译的程序。或者它支持的平台上的 Visual Studio。或任何其他编译器。从我的编译器中,我可以调用将各种结构传递给在目标编译器下编译的代码的函数。

它有效。为什么?

因为在这些库/SO/DLL/可执行文件的边界发生的行为不是由编译器定义的;它由平台定义。库间通信由该平台上所有编译器同意的通信协议管理。这是您可以对可能尚未编译的代码进行操作系统系统调用的方式。这是因为源和目标已就调用约定和应用程序二进制接口 (ABI) 达成一致。

系统的 ABI 定义了结构布局的外观。如果编译器正在编译尝试跨 ABI 边界传递结构的代码,则编译器必须确保其生成的代码的结构布局符合 ABI。对于编译器确定不会跨 ABI 边界转换的类型,它可以为所欲为,但一般来说,它仍会使用 ABI 的约定。

Linux 系统使用安腾 ABI。Windows 有自己的 ABI。每个平台都有一个 ABI,这是编译到该平台的编译器所遵循的。

因此,布局问题的唯一可能是您将这些结构发送到另一个操作系统。鉴于 Vulkan 是一个低级 API,这可能不是一个好主意。而且肯定不是 Vulkan 的预期用例。


推荐阅读