我想添加功能,该功能将从场景中删除网格,因此我尝试在场景运行时删除 vk::Buffer 和 vk::Memory 。每当顶点数据为 nullptr 时,缓冲区的重新创建会导致错误:“无法在命令缓冲区当前正在使用的 VkDeviceMemory 上调用 vkFreeMemory。”。但是,当顶点数据包含某些内容时,就不会出现错误。只有当我在重新创建之前调用transferQueue.waitIdle()或device.waitIdle()时,错误才会消失。
娱乐伪代码:
auto temp = m_Buffer;
m_Buffer = Buffer(...);
temp.Cleanup(...);
缓冲区代码:
void CopyBuffer(
const vk::Buffer& srcBuffer,
const vk::Buffer& dstBuffer,
const vk::DeviceSize& size,
const vk::Device& device,
const vk::CommandPool& transferPool,
const vk::Queue& transferQueue
)
{
const vk::CommandBufferAllocateInfo allocInfo(
transferPool,
vk::CommandBufferLevel::ePrimary,
1
);
const auto cBuffer = device.allocateCommandBuffers(allocInfo)[0];
const vk::CommandBufferBeginInfo beginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
GE_ASSERT_FN(cBuffer.begin(&beginInfo) == vk::Result::eSuccess, "cannot begin command buffer when copying buffer");
const vk::BufferCopy copy{0, 0, size};
cBuffer.copyBuffer(srcBuffer, dstBuffer, 1, ©);
cBuffer.end();
vk::SubmitInfo submitInfo{};
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cBuffer;
GE_ASSERT_FN(transferQueue.submit(1, &submitInfo, nullptr) == vk::Result::eSuccess, "cannot submit on transfer queue");
transferQueue.waitIdle();
device.freeCommandBuffers(transferPool, 1, &cBuffer);
}
VertexBuffer::VertexBuffer(
const vk::Device& device,
const vk::PhysicalDevice& physicalDevice,
const QueueFamilyIndices& queueFamilyIndices,
const vk::CommandPool& transferPool,
const vk::Queue& transferQueue,
const void* data,
const uint& sizeOfAllData
)
{
m_Size = sizeOfAllData;
if(m_Size)
{
vk::BufferCreateInfo info{ vk::BufferCreateFlagBits(),
sizeOfAllData,
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst
};
if(queueFamilyIndices.GetGraphicsFamily() != queueFamilyIndices.GetTransferFamily())
{
const uint uniqueIndices[] = { queueFamilyIndices.GetGraphicsFamily(), queueFamilyIndices.GetTransferFamily() };
info.queueFamilyIndexCount = 2;
info.pQueueFamilyIndices = uniqueIndices;
info.sharingMode = vk::SharingMode::eConcurrent;
}
else
info.sharingMode = vk::SharingMode::eExclusive;
m_Buffer = device.createBuffer(info);
const auto memRequirements = device.getBufferMemoryRequirements(m_Buffer);
vk::MemoryAllocateInfo allocInfo{
memRequirements.size,
IBuffer::FindMemoryType(
physicalDevice,
memRequirements.memoryTypeBits,
vk::MemoryPropertyFlagBits::eDeviceLocal
)
};
m_BufferMemory = device.allocateMemory(allocInfo);
device.bindBufferMemory(m_Buffer, m_BufferMemory, 0);
const auto stagingBuffer = StagingBuffer(device, physicalDevice, queueFamilyIndices, data, sizeOfAllData);
CopyBuffer(stagingBuffer.GetBuffer(), m_Buffer, m_Size, device, transferPool, transferQueue);
stagingBuffer.Cleanup(device);
}
}
暂存缓冲区代码:
class StagingBuffer : public IBuffer
{
public:
StagingBuffer(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, const QueueFamilyIndices& indices, const void* data, const uint& sizeOfAllData)
{
m_Size = sizeOfAllData;
if(m_Size)
{
vk::BufferCreateInfo info{vk::BufferCreateFlagBits(),
m_Size,
vk::BufferUsageFlagBits::eTransferSrc
};
if(indices.GetGraphicsFamily() != indices.GetTransferFamily())
{
const uint uniqueIndices[] = { indices.GetGraphicsFamily(), indices.GetTransferFamily() };
info.queueFamilyIndexCount = 2;
info.pQueueFamilyIndices = uniqueIndices;
info.sharingMode = vk::SharingMode::eConcurrent;
}
else
info.sharingMode = vk::SharingMode::eExclusive;
m_Buffer = device.createBuffer(info);
const auto memRequirements = device.getBufferMemoryRequirements(m_Buffer);
const vk::MemoryAllocateInfo allocInfo{
memRequirements.size,
IBuffer::FindMemoryType(
physicalDevice,
memRequirements.memoryTypeBits,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
)
};
m_BufferMemory = device.allocateMemory(allocInfo);
device.bindBufferMemory(m_Buffer, m_BufferMemory, 0);
void* memdata = device.mapMemory(m_BufferMemory, 0, info.size);
memcpy(memdata, data, info.size);
device.unmapMemory(m_BufferMemory);
}
}
StagingBuffer(const StagingBuffer& other)
{
m_Buffer = other.m_Buffer;
m_BufferMemory = other.m_BufferMemory;
m_Size = other.m_Size;
}
StagingBuffer& operator=(const StagingBuffer& other)
{
if(this == &other)
return *this;
m_Buffer = other.m_Buffer;
m_BufferMemory = other.m_BufferMemory;
m_Size = other.m_Size;
return *this;
}
StagingBuffer(StagingBuffer&& other) noexcept
{
m_Buffer = other.m_Buffer;
m_BufferMemory = other.m_BufferMemory;
m_Size = other.m_Size;
other.Reset();
}
StagingBuffer& operator=(StagingBuffer&& other) noexcept
{
m_Buffer = other.m_Buffer;
m_BufferMemory = other.m_BufferMemory;
m_Size = other.m_Size;
other.Reset();
return *this;
}
};
那么,为什么只有当顶点数据为 nullptr 时才会抛出错误?
验证错误表明您正在删除正在使用的 vkBuffer。
所以我认为正确的方法是在记录命令缓冲区之后或之前删除 vkBuffer,无论您的缓冲区数据是什么。
只要把事情说清楚即可。