Vulkan:渲染帧间正确销毁VkBuffer的方法及内存屏障使用疑问
问题
调用vkDestroyBuffer时出现错误:
vkDestroyBuffer(): can't be called on VkBuffer that is currently in use by VkCommandBuffer
我在提交渲染通道的间隙销毁无用缓冲区,明白问题是缓冲区还在被命令缓冲使用。用vkQueueWaitIdle可以解决,但想通过内存屏障实现,尝试了以下代码:
auto& commandPool = commandPoolMap.at(transferQueueFamily); vk::CommandBufferAllocateInfo commandBufferInfo(commandPool, vk::CommandBufferLevel::ePrimary, 1); auto commandBuffer = vulkanContext.deviceController->device.allocateCommandBuffers(commandBufferInfo).front(); vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); commandBuffer.begin(beginInfo); { auto srcAccess = vk::AccessFlagBits::eTransferWrite; auto dstAccess = vk::AccessFlagBits::eTransferWrite; vk::BufferMemoryBarrier barrier(srcAccess, dstAccess, {}, {}, buffer, 0, count); auto srcStage = vk::PipelineStageFlagBits::eTransfer; auto dstStage = vk::PipelineStageFlagBits::eTransfer; commandBuffer.pipelineBarrier(srcStage, dstStage, {}, {}, barrier, {}); vulkanContext.deviceController->device.destroyBuffer(buffer); //vkDestroyBuffer vulkanContext.deviceController->device.freeMemory(deviceMemory.memory); //vkFreeMemory } commandBuffer.end(); vk::SubmitInfo submitInfo({}, {}, commandBuffer, {}); auto& queue = vulkanContext.queueFamilies->queueMap.at(transferQueueFamily); queue.submit(submitInfo); queue.waitIdle();
执行后又出现错误:
vkEndCommandBuffer(): was called in VkCommandBuffer which is invalid because bound VkBuffer was destroyed.
想知道在渲染帧之间销毁VkBuffer的正确方式。
正确解决方案
错误原因分析
- 你在命令缓冲的记录阶段就销毁了缓冲区,直接违反Vulkan对象生命周期规则:只要命令缓冲还没完成提交执行,它引用的资源就不能被销毁。你写的内存屏障完全无效,因为屏障是命令缓冲的一部分,提前销毁buffer本身就是非法操作。
- 内存屏障的作用是同步GPU对资源的访问顺序,它不能替代“等待GPU完全用完资源”的逻辑,更不能让你在GPU还可能访问资源时提前销毁它。
正确的帧间隙销毁流程
核心是确保所有可能访问该缓冲区的GPU命令都已执行完毕,再进行销毁操作。不需要内存屏障,而是用Vulkan的同步机制(Fence)追踪GPU工作状态:
- 提交命令时绑定Fence:记录并提交所有使用该缓冲区的命令缓冲,同时绑定一个Fence来追踪提交的完成状态。
- 等待Fence触发:确认GPU已经完全执行完所有涉及该缓冲区的命令。
- 安全销毁资源:调用
vkDestroyBuffer和vkFreeMemory。
示例代码:
// 创建并初始化Fence vk::FenceCreateInfo fenceInfo(vk::FenceCreateFlagBits::eSignaled); auto fence = vulkanContext.deviceController->device.createFence(fenceInfo); // 重置Fence并提交使用buffer的命令缓冲 vk::SubmitInfo submitInfo(...); // 填入包含使用buffer的命令缓冲 vulkanContext.deviceController->device.resetFences({fence}); queue.submit(submitInfo, fence); // 等待Fence,确认GPU已用完buffer vk::Result result = vulkanContext.deviceController->device.waitForFences( {fence}, VK_TRUE, UINT64_MAX // 可设置合理超时,避免无限等待 ); // 现在可以安全销毁资源 vulkanContext.deviceController->device.destroyBuffer(buffer); vulkanContext.deviceController->device.freeMemory(deviceMemory.memory); vulkanContext.deviceController->device.destroyFence(fence);
进阶优化:帧延迟销毁
如果不想每次销毁都阻塞CPU,可以采用延迟销毁:
- 把要销毁的资源(buffer、memory等)和对应的Fence加入待销毁队列。
- 每帧结束时,检查队列中最早加入的资源对应的Fence是否触发。
- 若Fence触发,销毁该资源并从队列中移除。
这种方式能让CPU和GPU并行工作,避免不必要的阻塞,适合高性能场景。
内容的提问来源于stack exchange,提问作者R r
相关产品推荐
相关产品推荐

