0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在Vulkan-hpp中有助于将错误转移到编译时的特性

星星科技指导员 来源:NVIDIA 作者:Andreas Süßenbach 2022-04-27 15:19 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

在专业软件开发中,最重要的一个方面就是尽早发现错误。当然,最好的情况是我们甚至不能编写错误的代码。其次最好的是编译器可以检测到的错误。

最坏的情况是运行时错误。最难的部分隐藏在只在特定情况下运行的代码中。墨菲定律说,这种情况首次发生在顾客的环境中。

如果您使用的是 Vulkan ,有几种方法可以创建运行时错误。即使 Vulkan 提供了很好的验证层,您也必须运行这部分代码来检测此类错误。顺便说一句,我建议你不要在没有使用验证层的情况下用 Vulkan 编程

当使用 Vulkan – hpp 时,一些运行时错误变成编译时错误 。 Vulkan -HPP 是针对 Vulkan API 的头报头 C ++绑定。它由 Khronos 维护,作为 Vulkan 生态系统的一部分,可以在 GitHub 上找到 Khronos Group / Vulkan – hpp 。它也是 LunargVulkan SDK 的一部分。

有助于将错误转移到编译时的特性

Vulkan -hpp 通过以下功能帮助消除运行时错误:

枚举类与普通枚举比较

帮助程序类 vk::Flags

结构 常量成员 sType

vk::StructureChain

处理 32 位版本中的 类型安全

枚举类

使用 Vulkan ,可以得到很多枚举类型。除了 VkResult ,它们都是使用以下命名方案构造的:

typedef enum VkEnumName {
 VK_ENUM_NAME_VALUE_A = 0,
 VK_ENUM_NAME_VALUE_B = 1,
 …
} VkEnumName;

使用 Vulkan -hpp ,可以为这些枚举类型中的每一种获得一个枚举类:

namespace vk
{
 …
 enum class EnumName
 {
 eValueA = VK_ENUM_NAME_VALUE_A,
 eValueB = VK_ENUM_NAME_VALUE_B,
 …
 };
 …
}

首先,它们都位于名称空间 vk 。您可以通过定义 VULKAN_HPP_NAMESPACE 来调整该命名空间。 enum 类本身没有前缀 Vk ,因为这对于命名空间来说是多余的。最后,一个 enum 类的每个值都跳过前缀 VK_ENUM_NAME ,因为这个前缀又与命名空间和枚举类名冗余。它们以小写字母“ e ”作为前缀,并包含实际枚举值名称的 camelCase 版本。枚举类值不允许以数字开头,因此“ e ”前缀阻止了这一点。例如,无论你在 C 代码中使用 VK_ENUM_NAME_VALUE_A ,都使用 vk::EnumName::eValueA 代替 C ++代码。

那么,你从 Vulkan -hpp 中的 enum 类得到了什么呢?毕竟,由于 Vulkan 中的枚举值命名方案,不可能有两个同名的枚举值。这根本不是你的问题,而是 Khronos 的 Vulkan 人的问题。此外,您不太可能希望将变量或函数作为枚举值之一命名,即使这些名称已导出到全局范围。谁知道呢?有人喜欢函数名,比如 MIG 。

这里重要的一点是,在 Vulkan -hpp 中,没有隐式转换到 int 。不能将枚举类值赋给 int 类型,至少不会意外。也不能比较来自不同枚举类的两个枚举类值。当您比较两个枚举器中的两个枚举值时,会产生警告。 MIG 是依赖于编译器的,当然,一个警告比错误更容易被忽略。

助手类 vk :: Flags

对于 Vulkan ,有两个数据类型对,使用以下命名方案:

typedef enum VkEnumNameFlagBits = {
 VK_ENUM_NAME_VALUE_A_BIT = 0x00000001,
 VK_ENUM_NAME_VALUE_B_BIT = 0x00000002,
 …
} VkEnumNameFlagBits;
typedef VkFlags VkEnumNameFlags;

这里, VkFlags 只是一个 uint32_t ,并且 VkEnumNameFlags 应该通过从 VkEnumNameFlagBits 中对适当的枚举值进行排序来保存相应枚举 VkEnumNameFlagBits 的零个或多个值。由于* FlagBits 和* Flags 之间除了它们的公共名称部分之外,没有真正的联系,编译器对此无能为力。允许对任意枚举值或整数应用位运算符。如果错误组合的* Flags 值恰好是位的有效组合,即使它们可能不是您所希望的那样,即使验证层 MIG ht 也无法捕捉到这一点。它 MIG 感觉你像是在未定义的行为领域,即使程序完全按照你告诉它做的去做。这不是你想让它做的。

使用 Vulkan -hpp ,可以得到相应的对:

namespace vk
{
 …
 enum class EnumNameFlagBits : VkEnumNameFlagBits
 {
 eValueA = VK_ENUM_NAME_VALUE_A_BIT,
 eValueB = VK_ENUM_NAME_VALUE_B_BIT,
 …
 };
 using EnumNameFlags = Flags;
 …
}

有了这个结构,这样的尴尬局面就不会发生了。不能对枚举类值应用位运算符。 vk::EnumNameFlags 枚举知道相应的 vk::EnumNameFlagBits 。 helper 类 vk::Flags 提供的功能允许您对来自同一个枚举类的枚举类值应用位运算符,但仅对这些值应用这些值。不能将它们与来自不同枚举类的值组合。在编译时,只使用允许的标志位构造标志。

结构的 sType 成员

稍微远离枚举, Vulkan 中有许多结构将枚举类型 VkStructureType 的成员 sType 作为第一个元素。对于这些结构中的每一个,都必须将该成员设置为为为该结构指定的值。在下面的代码示例中,成员 sType 必须设置为 VK_STRUCTURE_TYPE_STRUCT_NAME 。

typedef struct VkStructName {
 VkStructureType sType;
 …
} VkStructName;

没那么难,但你必须做对。不要忘记设置它,也不要通过从代码中的另一个位置复制来将其设置为错误的值。例如,以下值在视觉上接近:

  • VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
  • VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO

对于 Vulkan -hpp ,在结构上有以下限制:

namespace vk
{
 …
 struct StructName
 {
 …
 const vk::StructureType sType = vk::StructureType::eStructName;
 …
 };
 …
}

就这么简单。当您实例化类型为 StructName 的结构时,您不必担心为成员 sType 设置正确的值,因为它已经设置好了。因为它是常量成员,所以不能意外地覆盖它。

对于感兴趣的模板元程序员来说: struct StructName 还提供了一个静态成员 structureType ,即 vk::StructureType 值。有一个名为 CppType 的类型特征,它从 vk::StructureType 值中获取结构的类型。

帮助程序类 vk :: StructureChain

Vulkan 中的许多结构都有 pNext 作为第二个成员:

typedef struct VkStructName {
 VkStructureType sType;
 const void* pNext;
 …
} VkStructName;

某些结构被指定为延伸其他结构。pNext指针用于创建结构链。有些结构可以多次成为该链的一部分,具有不同的实例。您的代码 MIG ht 如下所示:

ChainedStruct chained = {};
chained.sType = VK_STRUCTURE_TYPE_CHAINED_STRUCT;
// set other values of chained

AnchorStruct anchor = {};
anchor.sType = VK_STRUCTURE_TYPE_ANCHOR_STRUCT;
anchor.pNext = &chained;
// set other values of anchor

使用这种方法,有几个潜在的错误。例如, MIG ht 将一个结构链到 AnchorStruct 实例,该实例没有指定在链中。或者您意外地链接了一个结构的多个实例,其中只允许一个实例。甚至内存管理也会导致意外行为。例如,当您有一个使用局部变量创建链的函数时,就像前面的代码示例中那样,当该函数最终按值返回锚点时,您已经注定要失败了。 MIG 指出的链式结构已经消失。

对于 Vulkan -hpp ,该代码看起来几乎相同:

namespace vk
{
 …
 struct StructName
 {
 …
 const vk::StructureType sType = vk::StructureType::eStructName;
 const void * pNext = {};
 …
 }
 …
}

您可以使用与 Vulkan 相同的方法来使用它,潜在错误的来源相同。 helper 类vk::StructureChain在这里帮助编译器,如下面的代码示例所示:

vk::StructureChain chain
(
 { /* set other values of anchor */ }
 { /* set other values of chained */ }
);

当然,这条链会变得任意长。编译器可以检查是否所有链式结构都指定为扩展 MIG 。如果同一个链式结构多次出现,编译器会检查是否允许。访问这样一个链的元素将与您习惯于使用纯 C 型结构链略有不同:

vk::AnchorStruct const & anchorStruct = chain.get();
vk::ChainedStruct const & chainedStruct = chain.get();

如果必须在运行时从结构链中删除元素,可以使用成员函数 vk::StructureChain::unlink 来执行此操作。这样,结构链的内存占用不会改变,但是现在未使用的部分将被跳过,因为该链中的任何 pNext 指针都不会指向这些部分。要重新链接,请使用 vk::StructureChain::relink 。

型式安全

最后,我们来看一个稍微不同的主题,类型安全。这是 Vulkan 的一个问题,尤其是对于 32 位构建。在 32 位内部版本中,所有不可分派的句柄(如 VkBuffer 、 VkImage 和 VkSemaphore 都只是 uint64_t 上的 typedef :

#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
…
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer)
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage)

您可以调用 vkCreateBuffer 并传入指向 VkImage 的指针作为最后一个参数。您还可以将 VkImage 分配给 VkBuffer 或对它们进行比较,没有任何错误!

由于 Vulkan -hpp 中的相应类型是独立的类,没有任何继承关系,编译器不允许这些操作中的任何一个。你不会弄错的。

与运行时错误相比,更倾向于编辑时错误预防

正如我前面所说,最好的情况是甚至不能编写错误的代码。 Vulkan -hpp ,可能与所有现代 IDE 一起帮助您设置结构的成员。因为每个 vk :: struct 都有一个构造函数,该构造函数的每个成员都有一个参数列表,除了前面提到的 sType 和 pNext , IDE 可能会在编辑时引导您遍历所有这些参数,从而更难忽略任何错误。

把您的 Vulkan 的项目切换到 C ++

这些是 Vulkan -hpp 的一些特性,它们极大地简化了用 Vulkan 进行编码的工作。这些特性在运行时开销为零的情况下可用。只是编译器需要多工作一点。您仍然必须(几乎)像使用 plainVulkan 一样显式地编程,但是编译器可以更好地检查代码。这样可以节省你很多时间!

关于作者

Andreas Süßenbach 是 NVIDIA 的高级软件开发人员。他是 Vulkan.hpp 的发明者之一,并不断努力使之更进一步。

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    22

    文章

    2122

    浏览量

    76714
  • 编译器
    +关注

    关注

    1

    文章

    1669

    浏览量

    51083
  • SDK
    SDK
    +关注

    关注

    3

    文章

    1093

    浏览量

    51201
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何创建一个浮点立即数

    常量的高20位 addi x15,x15,0x678 # 再将低12位加到高20位 而对于F寄存器,则需要另一条F指令S寄存器转移到F寄存器,即浮点搬运指令 FMV.W.X 该指令结构为
    发表于 10-21 13:51

    rtsmart开启C++特性支持后,工具链编译内核不通过怎么解决?

    各位大佬好,本人在rtsmart项目中需要使用C++11特性menuconfig那里配置了支持C++特性后,使用7.3.0版本的arm-linux-musleabi编译
    发表于 09-29 07:49

    基于Vulkan的端侧AI运算

    本期内容由AI Model SIG提供,介绍了开源鸿蒙中,利用图形接口Vulkan的计算着色器能力,端侧部署大模型的的整体思路和实践分享。
    的头像 发表于 09-10 17:19 952次阅读
    基于<b class='flag-5'>Vulkan</b>的端侧AI运算

    如何在Keil中将NuMicro BSP从Arm编译器5迁移到编译器6?

    Keil中将NuMicro BSP从Arm编译器5迁移到编译器6!
    发表于 08-20 06:29

    大模型真的有助于自动驾驶落地吗?

    其实大模型带来的并不是单一的“万能解”,而是一个能够显著提升认知、生成和推理能力的新工具箱。它能加速数据闭环、提升对复杂场景的理解、改善人机交互、并在工程流程中提高效率。
    的头像 发表于 08-16 09:43 889次阅读
    大模型真的<b class='flag-5'>有助于</b>自动驾驶落地吗?

    飞凌嵌入式ElfBoard ELF 1板卡-uboot编译中间文件

    中间文件有助于我们判断我们的uboot工程用到了哪些源文件,哪些源文件参与了编译,方便我们调试代码或调查异常的时候定位问题。
    发表于 05-22 11:19

    使用灵活的 AFE、运动控制和认证 IC 设计即时诊断系统

    即时 (PoC) 医学测试的趋势正在从实验室转移到医生办公室、诊所甚至家庭。这种迁移有可能加快诊断速度,从而加快患者护理速度、改善结果并降低成本。 实现 PoC 从多功能、应用优化的 IC 开始
    的头像 发表于 04-30 09:54 2014次阅读
    使用灵活的 AFE、运动控制和认证 IC 设计即时诊断系统

    使用AD7616时,转换完成发送一次写指令0x00就能将所有通道的数据转移到SPIFIFO吗?

    使用AD7616时,转换完成发送一次写指令0x00就能将所有通道的数据转移到SPIFIFO吗?DMA又是怎么配置,我是读取的SPIFIFO吗?转换两次读进fifo的时,第二次是接着第一个还是直接覆盖了第一次的?
    发表于 04-24 06:02

    rtsmart开启C++特性支持后,工具链编译内核不通过怎么解决?

    各位大佬好,本人在rtsmart项目中需要使用C++11特性menuconfig那里配置了支持C++特性后,使用7.3.0版本的arm-linux-musleabi编译
    发表于 03-10 08:06

    是否可以使用OpenVINO™部署管理器部署机器上运行Python应用程序?

    使用 OpenVINO™部署管理器创建运行时软件包。 运行时包转移到部署机器中。 无法确定是否可以部署机器上运行 Python 应用程序,而无需安装OpenVINO™ Toolkit 和 Python。
    发表于 03-05 08:16

    如何项目从IAR迁移到Embedded Studio

    本文描述如何IAR EWARM项目迁移到SEGGER Embedded Studio(简称SES)中。
    的头像 发表于 02-25 17:11 1062次阅读
    如何<b class='flag-5'>将</b>项目从IAR迁<b class='flag-5'>移到</b>Embedded Studio

    英伟达RTX 5070显卡获Vulkan1.4认证

    近日,IT 之家 2 月 3 日消息称,尽管英伟达 RTX 5070 Ti 和 5070 显卡尚未正式上市,但已成功获得 Vulkan 1.4 兼容性认证。 Vulkan 一致性列表对各类图形设备
    的头像 发表于 02-05 14:56 2478次阅读

    电源负载主要特性和参数是什么?

    : 电源负载的预期使用寿命和可靠性指标,如MTBF(平均无故障时间)。 环境适应性: 电源负载不同环境条件下的适应能力,如温度、湿度、海拔等。 了解这些特性和参数有助于用户选择适合自己需求的电源负载,并确保其
    发表于 12-25 15:09

    光刻胶成为半导体产业的关键材料

    对光的敏感度。半导体制造过程中,光刻胶通过光化学反应,掩膜版上的图案精确地转移到硅片表面。 光刻工艺是半导体制造的核心步骤之一。硅片表面涂上光刻胶(负胶)后,使用特定波长的光线通
    的头像 发表于 12-19 13:57 1797次阅读

    红外感应单片机智能卫浴的抗干扰性分析

    一、自身优势带来的抗干扰潜力 自带恒流驱动电路 :XD08M3232单片机自带恒流驱动电路,这一特性可能有助于增强其智能卫浴环境中的抗干扰性。因为恒流驱动电路可以使相关感应部件的工作电流保持稳定
    发表于 12-14 15:41