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::exchange
与NULL
、0
或(最后)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 应该如何使用,所以我分享了有问题的故障代码:一个移动构造函数,它传输句柄并用 NULL
或 VK_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_HANDLE
和NULL
可以分别用于代替有效的不可分派句柄和可分派句柄
也就是说,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?的主要内容,如果未能解决你的问题,请参考以下文章
为啥尽管损失衰减且评估结果合理,但张量流的“准确度”值始终为 0
为啥我应该始终为我的 Authorize 属性定义 JwtBearerDefaults.AuthenticationScheme?