VK_NULL_HANDLE 始终为 0;为啥不是 nullptr?

Posted

技术标签:

【中文标题】VK_NULL_HANDLE 始终为 0;为啥不是 nullptr?【英文标题】:VK_NULL_HANDLE is always 0; Why not nullptr?VK_NULL_HANDLE 始终为 0;为什么不是 nullptr? 【发布时间】:2020-07-27 08:13:10 【问题描述】:

在大多数情况下,您可以将VK_NULL_HANDLE 分配给VK_DEFINE_HANDLE(object)VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) 定义的任何句柄,但std::exchange() 几乎在所有情况下都无法设置此值。

在大多数情况下,我必须致电 std::exchange(m_dispatchableHandle, nullptr)std::exchange(m_nonDispatchableHandle, nullptr) 但是,在 x86 上,由于重新定义了 VK_DEFINE_NON_DISPATCHABLE_HANDLE,nullptr 无效。我必须将std::exchangeNULL0 或(最后)VK_NULL_HANDLE 一起使用。

我的问题:在遵循VK_DEFINE_HANDLE 宏模式时,不应该有两个如下定义的空句柄宏吗?

#define VK_NULL_HANDLE nullptr
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
#define VK_NON_DISPATCHABLE_NULL_HANDLE nullptr
#else
#define VK_NON_DISPATCHABLE_NULL_HANDLE 0
#endif

如果不出意外,似乎#define VK_NULL_HANDLE nullptr 总体上会更实用。

我可能误解了 API 应该如何使用,所以我分享了有问题的故障代码:一个移动构造函数,它传输句柄并用 NULLVK_NULL_HANDLE 填充旧对象。通过仔细管理所有权,可以简单地设计类并处理vkDestroy* 等。人。在它的析构函数中。

class RenderContext

public:
    RenderContext(VkPhysicalDevice physicalDevice, uint32_t gfxQueueFamilyIdx, uint32_t presentQueueFamilyIdx);
    RenderContext(const RenderContext& other) = delete;
    RenderContext& operator=(const RenderContext& other) = delete;
    RenderContext(RenderContext&& other) noexcept;
    RenderContext&& operator=(RenderContext&& other) = delete;
    ~RenderContext();
private:
    VkDevice _device;
    VkCommandPool _gCmdPool;
    VkCommandPool _pCmdPool;
    VkQueue _gQueue;
    VkQueue _pQueue;
;

RenderContext::RenderContext(VkPhysicalDevice physicalDevice, uint32_t gfxQueueFamilyIdx, uint32_t presentQueueFamilyIdx) :
    _device(VK_NULL_HANDLE),
    _gCmdPool(VK_NULL_HANDLE),
    _pCmdPool(VK_NULL_HANDLE),
    _gQueue(VK_NULL_HANDLE),
    _pQueue(VK_NULL_HANDLE)

    // ...


RenderContext::RenderContext(RenderContext&& other) noexcept :
    _device(std::exchange(other._device, nullptr)),
    _gCmdPool(std::exchange(other._gCmdPool, VK_NULL_HANDLE)),
    _pCmdPool(std::exchange(other._pCmdPool, VK_NULL_HANDLE)),
    _gQueue(std::exchange(other._gQueue, nullptr)),
    _pQueue(std::exchange(other._pQueue, nullptr))



RenderContext::~RenderContext()

    vkDestroyCommandPool(_device, _pCmdPool, nullptr);
    vkDestroyCommandPool(_device, _gCmdPool, nullptr);
    vkDestroyDevice(_device, nullptr);

【问题讨论】:

那不是 C 库吗? nullptr 在 C 中不存在。 这难道不是说明 C 语言无法强制执行某些 Vulkan 规范吗?有一些特定的行为可以确定可调度句柄可以是 VK_NULL_HANDLE 而不可调度句柄可以是 NULL。如果 VK_NULL_HANDLE 扩展为 int,则两者实际上都是 int。 Vulkan 绝对不是我的领域,但 (void*)0 是空指针常量通常会与 int 混淆的方式。当然0 仍然适合两者,但在尝试将两者分开时并不理想。 【参考方案1】:

C++ 中的模板需要精确的类型,而不仅仅是兼容的类型,才能正确实例化。因此,C 的NULL(定义为0)可能是个问题。 0 也可能是一个问题,因为它是 int,而不是 uint64_t,这是不可调度句柄的 ABI(可以存储在 64 位指针中以从 C 中获得某种类型安全)。这在处理来自 C++(或通常)的 C 库时并不新鲜。

Rest 是提交给作者的设计问题和建议,因此不适合 ***。问题跟踪系统位于https://github.com/KhronosGroup/Vulkan-Docs/issues。从表面上看,你的建议对我来说很有意义。他们可能不想让它无处不在,但另外,这可能是#ifdef __cplusplus #define VK_NULL_HANDLE nullptr 的一个案例。

另外,还有 Vulkan-Hpp,它应该向 Vulkan 公开更多 C++-istic 绑定。

当前调用模板函数的正确方法是std::exchange(m_nonDispatchableHandle, decltype(m_nonDispatchableHandle)(VK_NULL_HANDLE));

【讨论】:

谢谢。我看到我的问题比 Vulkan 更像是一个 C/C++ 问题,但我会保留它,因为您的回答对于寻找此技术信息的新手非常有用。其他人可能会像我一样在几乎没有 C 知识的情况下来到 Vulkan。【参考方案2】:

在大多数情况下,VK_NULL_HANDLE 可以分配给由VK_DEFINE_HANDLE(object)VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) 定义的任何句柄

错了。您可以摆脱它,因为 C 和 C++ 允许。但是 Vulkan 规范非常明确:

保留值VK_NULL_HANDLENULL 可以分别用于代替有效的不可分派句柄和可分派句柄

也就是说,VK_NULL_HANDLE用于不可调度的句柄。 C/C++ 语言允许您将 VK_NULL_HANDLE 的值分配给可调度句柄只是语言的一个功能,Vulkan 本身的行为没有定义。

您永远不应该在可调度句柄和不可调度句柄之间尝试std::exchange,就像您永远不应该在两个不同类型之间尝试std::exchange

【讨论】:

我的问题仍然有效,因为基于 VK_DEFINE_NON_DISPATCHABLE_HANDLE.在 x64 架构等上,VK_NULL_HANDLE 可能应该定义为(void*)0 而不仅仅是0 @AaronHull:但不可分派的句柄不是指针;它们是整数。如果你做(void*)0,那么它是一个指针,C++ 不会 隐式地将指针转换为整数。然而,它会将整数文字零隐式转换为(空)指针,这允许您将VK_NULL_HANDLE 传递给可调度句柄。 @NicolBolas 这应该是实现细节,但它们是 x64 上的指针,用于从 C 中获得一些类型安全性。

以上是关于VK_NULL_HANDLE 始终为 0;为啥不是 nullptr?的主要内容,如果未能解决你的问题,请参考以下文章

为啥标准输入上的 Gnu grep 退出状态始终为 0?

为啥除法后我的 double 或 int 值始终为 0?

为啥尽管损失衰减且评估结果合理,但张量流的“准确度”值始终为 0

为啥带有附加字段“指定”的字段始终为空?

为啥我应该始终为我的 Authorize 属性定义 JwtBearerDefaults.AuthenticationScheme?

为啥我的 KendoGrid “更新”参数在控制器中始终为空?