当我尝试在 vkQueueWaitIddle 之后重置 commandPool 时出现 Vulkan 验证错误

Vulkan validation error when I try to reset a commandPool after vkQueueWaitIddle

我有一个循环运行计算着色器的小 Vulkan 程序。

只有一个commandBuffer是从我唯一的commandPool中分配的。

commandBuffer构建完成后,提交到队列,等待完成vkQueueWaitIddle。我确实在那行代码中等待了一段时间。之后,我调用 vkResetCommandPool,它应该重置分配给该池的所有 commandBuffer(反正只有一个)。

...

vkEndCommandBuffer(commandBuffer);
        
{
    VkSubmitInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    info.commandBufferCount = 1;
    info.pCommandBuffers = &commandBuffer;
    vkQueueSubmit(queue, 1, &info, VK_NULL_HANDLE);
}
vkQueueWaitIdle(queue);

vkResetCommandPool(device, commandPool, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);

当它尝试重置 commandPool 时,验证给出了以下错误。

VUID-vkResetCommandPool-commandPool-00040(ERROR / SPEC): msgNum: -1254218959
- Validation Error: [ VUID-vkResetCommandPool-commandPool-00040 ]
Object 0: handle = 0x20d2ce0b718, type = VK_OBJECT_TYPE_COMMAND_BUFFER; |
MessageID = 0xb53e2331 |
Attempt to reset command pool with VkCommandBuffer 0x20d2ce0b718[] which is in use.
The Vulkan spec states: All VkCommandBuffer objects allocated from commandPool must not be in the pending state
(https://vulkan.lunarg.com/doc/view/1.2.176.1/windows/1.2-extensions/vkspec.html#VUID-vkResetCommandPool-commandPool-00040)
    Objects: 1
        [0] 0x20d2ce0b718, type: 6, name: NULL

但我不明白为什么,因为我已经在等待 vkQueueWaitIdle。根据 documentation,一旦 commandBuffer 完成执行,它应该进入 invalid 状态,我应该能够重置它。

这里是相关的周边代码:

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

for (i64 i = 0; i < numIterations; i++)
{
    vkBeginCommandBuffer(commandBuffer, &beginInfo);

    vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
    vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout,
        0, 2, descriptorSets, 0, nullptr);

    uniforms.start = i * numThreads;
    vkCmdUpdateBuffer(commandBuffer, unifsBuffer, 0, sizeof(uniforms), &uniforms);
    vkCmdPipelineBarrier(commandBuffer,
        VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0,
        0, nullptr,
        1, &memBarriers[0],
        0, nullptr);

    vkCmdDispatch(commandBuffer, numThreads, 1, 1);

    vkCmdPipelineBarrier(commandBuffer,
        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
        0, nullptr,
        1, &memBarriers[1],
        0, nullptr);

    VkBufferCopy copyInfo = {};
    copyInfo.srcOffset = 0;
    copyInfo.dstOffset = 0;
    copyInfo.size = sizeof(i64) * numThreads;
    vkCmdCopyBuffer(commandBuffer,
        buffer, stagingBuffer, 1, &copyInfo);

    vkEndCommandBuffer(commandBuffer);
        
    {
        VkSubmitInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
        info.commandBufferCount = 1;
        info.pCommandBuffers = &commandBuffer;
        vkQueueSubmit(queue, 1, &info, VK_NULL_HANDLE);
    }
    vkQueueWaitIdle(queue);
    vkResetCommandPool(device, commandPool, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);

    i64* result;
    vkMapMemory(device, stagingBufferMem, 0, sizeof(i64) * numThreads, 0, (void**)&result);

    for (int i = 0; i < numThreads; i++)
    {
        if (result[i]) {
            auto res = result[i];
            vkUnmapMemory(device, stagingBufferMem);
            return res;
        }
    }
    vkUnmapMemory(device, stagingBufferMem);
}

我找到了我的问题。在 vkCmdDispatch 中,我认为参数指定了全局大小(计算着色器调用的数量),但它实际上是工作组的数量。因此,我分派了比预期更多的线程,而且我的缓冲区不够大,所以线程越界写入。 我相信验证层没有给我正确的提示。