user14558378
user14558378

Reputation:

How to ensure correct destruction of vk::UniqueBuffer and vk::UniqueDeviceMemory

I have run into the following conundrum trying to use Vulkan Hpp unique handles to store a buffer and its allocated memory. I declare the handles

vk::UniqueBuffer vertex_buffer;
vk::UniqueDeviceMemory vertex_buffer_memory;

and populate them using vk::Device::createBufferUnique and vk::Device::allocateMemoryUnique. These need to be called in this order since the latter depends on the memory requirements of the former (obtained via vk::Device::getBufferMemoryRequirements). However, this results in vertex_memory_buffer being destroyed before vertex_buffer and triggers the following warning from validation layers:

Validation Warning: [ CHASSIS ] Object 0: handle = 0x558599c91fc0, type = VK_OBJECT_TYPE_DEVICE; Object 1: handle = 0x1b000000001b, type = VK_OBJECT_TYPE_BUFFER; Object 2: handle = 0x1c000000001c, type = VK_OBJECT_TYPE_DEVICE_MEMORY; | MessageID = 0x7366b7dd | 
VK Object VkBuffer 0x1b000000001b[] still has a reference to mem obj VkDeviceMemory 0x1c000000001c[].

Indeed, all C API examples that I looked up destroy the buffer first and then free the memory. I can of course use some sort of workaround to ensure that the same happens here (e.g., default initializing vertex_buffer_memory and assigning to it after creating vertex_buffer), but that really defeats the purpose of unique handles (which is exactly to make the resource management automatic). Unless I'm misunderstanding something, there seems to be an incompatibility between the design of Vulkan API and RAII. What is the correct way of solving this issue?

Upvotes: 4

Views: 937

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473302

These need to be called in this order since the latter depends on the memory requirements of the former

No, they do not. You do need to get the memory requirements appropriate to that buffer, but you do not need to use that specific VkBuffer object to do that.

It's best to compute the requirements once at the start of the application by creating VkBuffers that represent your specific use cases. That is, create some number of VkBuffer instances that represent all of the kinds of buffers you will ever use.

The memory requirements for a buffer are not specific to that VkBuffer object; there are a bunch of rules in the Vulkan specification defining the circumstances under which the requirements of two VkBuffers will be the same. So if you create a VkBuffer that's like the one you'll actually use, you can get the requirements for that and know that the memory allocated for those requirements will work for the one you plan to use.

Just stick to those rules and you won't need to reverse the initialization order of these objects.

Also, it's pretty much always a bad idea to allocate memory for just one buffer or image. Vulkan is not OpenGL, and you should not treat vkAllocateMemory and vkCreateBuffer like glBufferStorage. Allocate large blocks of memory, then subdivide it into buffers (and images) as you see fit. You can also use a memory manager like VulkanMemoryAllocator to handle memory management for you.

Upvotes: 3

Related Questions