首页 > 解决方案 > 升级到 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 到一般)、构建加速结构、复制缓冲区但并非每次都复制(例如,从暂存缓冲区到设备缓冲区,之后释放暂存缓冲区也会引发错误,由于它仍在使用中,因此副本尚未完成)...但是对于其他用途,它可以正常工作。我目前无法确定一个共同点...

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

自从我上次使用它以来,这有什么变化吗?这是有问题的代码(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 大楼,如果我发现任何东西,我会回复你。

标签: c++vulkan

解决方案


事实证明,实际错误可能发生在其 vkQueueWaitIdle 返回 DEVICE_LOST 错误的命令缓冲区之前。在我的加速结构构建代码中,我已经并将继续存在各种错误。我不能轻易调试它,因为显然验证层没有显示提供给 vkCmdBuildAccelerationStructures 的结构中是否存在细微的错误,而是进行了大量的试验和错误。

我确信升级前的验证层会发现一个值得注意的例子是忘记设置VkAccelerationStructureBuildGeometryInfoKHR::scratchData字段,这是我必须修复的最后一个错误才能最终让一切运行。

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

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


推荐阅读