You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的正确方式。

正确解决方案

错误原因分析

  1. 你在命令缓冲的记录阶段就销毁了缓冲区,直接违反Vulkan对象生命周期规则:只要命令缓冲还没完成提交执行,它引用的资源就不能被销毁。你写的内存屏障完全无效,因为屏障是命令缓冲的一部分,提前销毁buffer本身就是非法操作。
  2. 内存屏障的作用是同步GPU对资源的访问顺序,它不能替代“等待GPU完全用完资源”的逻辑,更不能让你在GPU还可能访问资源时提前销毁它。

正确的帧间隙销毁流程

核心是确保所有可能访问该缓冲区的GPU命令都已执行完毕,再进行销毁操作。不需要内存屏障,而是用Vulkan的同步机制(Fence)追踪GPU工作状态:

  1. 提交命令时绑定Fence:记录并提交所有使用该缓冲区的命令缓冲,同时绑定一个Fence来追踪提交的完成状态。
  2. 等待Fence触发:确认GPU已经完全执行完所有涉及该缓冲区的命令。
  3. 安全销毁资源:调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:33:22