VK_NULL_HANDLE 始终为 0;为什么不是 nullptr?

VK_NULL_HANDLE is always 0; Why not nullptr?

在大多数情况下,您可以将 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 应该如何使用,所以我分享了有问题的代码:一个转移句柄并用 NULL 填充旧对象的移动构造函数或 VK_NULL_HANDLE。通过仔细管理所有权,可以简单地设计 class 并处理 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++ 中的模板需要精确的类型,而不仅仅是兼容的类型,才能正确实例化。因此 C 的 NULL(定义为 0)可能是个问题。 0 也可能是一个问题,因为它是 int,而不是 uint64_t,它是不可分派句柄的 ABI(可以存储在 64 位指针中以获得某种类型安全性C).这在处理来自 C++(或一般)的 C 库时并不新鲜。

其余是作者提出的设计问题和建议,因此不适合 Whosebug。问题跟踪系统可在 https://github.com/KhronosGroup/Vulkan-Docs/issues 获得。从表面上看,你的建议对我来说很有意义。他们可能不想让它普及,但另外,这样做可能是 #ifdef __cplusplus #define VK_NULL_HANDLE nullptr.

或者,还有 Vulkan-Hpp,它应该向 Vulkan 公开更多 C++ 式绑定。

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

In most cases, VK_NULL_HANDLE can be assigned to any handle defined by VK_DEFINE_HANDLE(object) or VK_DEFINE_NON_DISPATCHABLE_HANDLE(object)

错了。您可以 摆脱它 因为 C 和 C++ 让您如此。但是Vulkan的规范很明确:

The reserved values VK_NULL_HANDLE and NULL can be used in place of valid non-dispatchable handles and dispatchable handles, respectively

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

您永远不应该尝试在可分派句柄和不可分派句柄之间 std::exchange,就像您永远不应该在两种不同类型之间尝试 std::exchange 一样。