升级到 1.2.162.1 后:vkQueueWaitIdle == VK_ERROR_DEVICE_LOST

After upgrading to 1.2.162.1: vkQueueWaitIdle == VK_ERROR_DEVICE_LOST

我最近将我的光线追踪渲染器从 Vulkan SDK 版本 1.2.148.0 升级到了 1.2.162.1。 这是必要的,因为光线追踪扩展已经结束测试版,因此现在可以与非测试版一起使用 图形驱动程序(我的 RTX 2070 SUPER 版本为 461.40)。它要求我对渲染器的光线追踪端进行相当多的更改 感谢 nvidia 教程,我成功了。

不幸的是,以前可以正常工作的代码现在开始出错了。 在许多情况下,提交单个时间命令会导致 vkQueueWaitIdle 失败并显示 VK_ERROR_DEVICE_LOST,这会导致验证错误,表示我正在尝试释放仍在使用中的命令缓冲区。这种情况发生在多种用途中:转换图像布局(似乎是 undef 到 general),构建加速结构,复制缓冲区但不是每次都复制缓冲区(例如,从暂存到设备缓冲区,之后释放暂存缓冲区也会引发错误,因为它仍在使用中,副本尚未完成)...但是对于其他用途,它可以正常工作。我目前无法确定共同点...

最后,程序崩溃,因为呈现第一帧失败,因为它的布局未定义 - 我认为这是由一个或多个前面提到的错误引起的。

自从我上次使用它以来,这方面有什么变化吗?这是有问题的代码 (endSingleTimeCommands):

    vkEndCommandBuffer(commandBuffer);

    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffer;

    vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
    switch (vkQueueWaitIdle(graphicsQueue)) {
        //debug output removed for brevity
    };

    vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);

它失败的地方之一是:

    //[fill the structs with info...]

    //function pointer grabbed via vkGetDeviceProcAddr
    vk::vkCmdBuildAccelerationStructuresKHR(cmd, 1, &buildInfo, &buildOffset);

    //[call to the above code here]

但与扩展无关的代码也会失败(有时!),例如:

    VkCommandBuffer commandBuffer = beginSingleTimeCommands();

    VkBufferCopy copyRegion{};
    copyRegion.srcOffset = 0; // Optional
    copyRegion.dstOffset = 0; // Optional
    copyRegion.size = size;
    vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

    endSingleTimeCommands(commandBuffer);

也许 beginSingleTimeCommands 也相关:

    VkCommandBufferAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandPool = commandPool;
    allocInfo.commandBufferCount = 1;

    VkCommandBuffer commandBuffer;
    if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer) != VK_SUCCESS) {
        std::cout << "beginSingleTimeCommands: could not allocate command buffer!\n";
    }

    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

    if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
        std::cout << "beginSingleTimeCommands: could not begin command buffer!\n";
    }

    return commandBuffer;

我想我收集到了一些额外的信息: 我使用 nvidia 管道检查点系统在调用 vkCmdBuildAccelerationStructuresKHR 之前和之后添加了一个检查点,两个检查点都在 TOP_OF_PIPE。第一次调用此函数后,不再生成检查点输出,这让我相信第一次调用构建以某种方式破坏了一切。我想我会三次检查我的 AS 大楼,如果我发现任何东西,我会回复你。

事实证明,实际错误可能发生在 vkQueueWaitIdle returns DEVICE_LOST 错误的命令缓冲区之前。在我的加速结构构建代码中,我已经并且继续存在各种错误。我无法轻松调试它,因为显然验证层不会显示提供给 vkCmdBuildAccelerationStructures 的结构中是否存在细微错误,而是需要大量的反复试验。

一个值得注意的例子是忘记设置 VkAccelerationStructureBuildGeometryInfoKHR::scratchData 字段,我确定升级前的验证层会发现这个例子,这是我必须修复的最后一个错误,最终将所有内容都发送到 运行.

我的问题的答案是:不要看触发 DEVICE_LOST 的命令,看看你在 之前 该命令对队列做了什么,有可能出现错误。事实上,一旦第一个 DEVICE_LOST 错误发生,(几乎?)所有进一步的 vkQueueWaitIdle 都因相同的错误而失败(与 vkQueueSubmit 相同)。在我的复制缓冲区代码是第一个失败的情况下,错误总是在队列使用 before 那个。

我无法 post 我的问题的确切解决方案,因为 - 就像我说过的 - 原因不止一个,到目前为止我只修复了其中的一些,还有一些。我认为这些细节与未来遇到我的问题的人无关,但如果我可以添加任何内容来帮助其他人,请告诉我。

这是真的!我被这个问题困扰了几天,结果发现当我使用 vkGetAccelerationStructureBuildSizesKHR() 查询大小时与我使用它实际构建 BLAS 时 VkAccelerationStructureBuildGeometryInfoKHR 标志不匹配!就我而言,我使用的是 VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR 在查询大小时仅 FAST_TRACE 在实际创建 AS 时,这导致了同样的问题!