It’s normal if a vulkan report is very big because it’s the hardest backend, and I spend many hours on this post.
// Step 1: Delete textures.
{
SAFE_DELETE(_internalFonts._fontAtlasTexture); // <-- The texture I want to delete.
SAFE_DELETE(_iconsTexture);
}
// Step 2: Delete the command buffer.
SAFE_DELETE(_copyCmd); // <-- This just deletes the class CopyCommandBuffer but the real vulkan command buffer inside it is already ended before.
Goal: I will always repeat the following words, when I say “Texture that cannot be deleted”, I’m talking about Vulkan memory leak, which means that the texture (class Texture) is not completely deleted.
For example, if the call below fails, then it means that the texture cannot be deleted, because there are some vulkan objects that cannot be freed / deleted / destroyed.
// In the function Texture::Vulkan::destroy()
Texture *texture = this;
vkFreeMemory(device, texture->_memory, nullptr);
The error on that line above:
[vulkan] Debug report from ObjectType: VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT
Validation Error: [ VUID-vkFreeMemory-memory-00677 ] Object 0: handle = 0x27e7d8679a0, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x485c8ea2 | Cannot call vkFreeMemory on VkDeviceMemory 0x908683000000001d that is currently in use by a command buffer. The Vulkan spec states: All submitted commands that refer to memory (via images or buffers) must have completed execution (Link: [Vulkan® 1.2.182 - A Specification (with all registered extensions)] I cannot include link because I’m a beginner).
The thing I don’t understand:
- Cannot call vkFreeMemory on VkDeviceMemory 0x908683000000001d that is currently in use by a command buffer.
- The Vulkan spec states: All submitted commands that refer to memory (via images or buffers) must have completed execution.
My own interpretation, cannot free a “Device Memory” because it’s currently in use by a command buffer. But according to the codes below, I try to END / FLUSHING it like this _engine->_copyCmd->end(); and does it means that the command buffer doesn’t use the “Device Memory” anymore, I’m not sure?
The command buffer:
//****************************************************************************************************
// CopyCommandBuffer
// Referenced from "examples/imgui/main.cpp" > initResources in "Vulkan C++ examples and demos" https://github.com/SaschaWillems/Vulkan
//****************************************************************************************************
VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandBufferLevel level, VkCommandPool pool, bool begin)
{
VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(pool, level, 1);
VkCommandBuffer cmdBuffer;
VK_CHECK_RESULT(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer));
// If requested, also start recording for the new command buffer
if (begin)
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));
}
return cmdBuffer;
}
void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, VkCommandPool pool, bool free)
{
if (commandBuffer == VK_NULL_HANDLE)
{
return;
}
VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));
VkSubmitInfo submitInfo = vks::initializers::submitInfo();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
// Create fence to ensure that the command buffer has finished executing
VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(VK_FLAGS_NONE);
VkFence fence;
VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence));
// Submit to the queue
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence));
// Wait for the fence to signal that command buffer has finished executing
VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, DEFAULT_FENCE_TIMEOUT));
vkDestroyFence(logicalDevice, fence, nullptr);
if (free)
{
vkFreeCommandBuffers(logicalDevice, pool, 1, &commandBuffer);
}
}
void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free)
{
return flushCommandBuffer(commandBuffer, queue, commandPool, free);
}
emc::CopyCommandBuffer::CopyCommandBuffer(Engine *engine) {
_engine = engine;
_isBegan = false;
_vk_copyCmd = nullptr;
}
emc::CopyCommandBuffer::~CopyCommandBuffer() {
EMC_VERIFY(!_isBegan);
EMC_VERIFY(_vk_copyCmd == nullptr);
}
void emc::CopyCommandBuffer::begin() {
EMC_VERIFY(!_isBegan);
_isBegan = true;
_vk_copyCmd = _engine->_vk._vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
}
void emc::CopyCommandBuffer::end() {
EMC_VERIFY(_isBegan);
_isBegan = false;
// Free
if (_vk_copyCmd) { _engine->_vk._vulkanDevice->flushCommandBuffer(_vk_copyCmd, _engine->_vk._copyQueue, true); _vk_copyCmd = nullptr; }
}
How I create the texture (I cannot delete the texture _fontAtlasTexture):
_engine->_copyCmd->begin(); // <-- Begin the Command Buffer.
if (!_fontAtlasTexture) {
emc::TextureCreateInfo CI = {};
CI.type = emc::TextureCreateInfoType_Blank;
CI.extent = { extent.x, extent.y, 1 };
_fontAtlasTexture = new Texture(_engine, _engine->_copyCmd, CI);
}
else {
_fontAtlasTexture->resize(extent);
}
// Map & Write & Unmap
emc::TextureMappedResource mappedResource = _fontAtlasTexture->map();
memcpy(mappedResource._rgba_data, fontData, uploadSize);
_fontAtlasTexture->unmap();
_engine->_copyCmd->end(); // <-- End the Command Buffer.
Note: There are many textures but only that texture (_fontAtlasTexture) which cannot be deleted. And it’s hard for me to detect where is the problem because the codes are almost with the same method, for example, read the code below:
How I create the icons texture (I can delete the texture _iconsTexture without memory leak):
TextureCreateInfo CI = {};
CI.type = TextureCreateInfoType_FromFile;
CI.fileName = binPath(_projectName) + "../data/icons/icons.png";
_iconsTexture_size = { 12, 12 };
_copyCmd->begin(); // <-- Begin the Command Buffer.
_iconsTexture = new Texture(this, _copyCmd, CI);
_copyCmd->end(); // <-- End the Command Buffer.