Creation of a container with buffers in it

I have GameState class, that is contains buffers and uses them for rendering through calls to VulkanRenderer class.
In GameState there is std::unordered_map<uint32_t, Unit> _unit_IDs field
Class Unit have GPUMeshBuffers _renderedBody in it, where GPUMeshBuffers is struct that basically have fields:
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo info;
VkDeviceAddress vertexBufferAddress;

GPUMeshBuffers getting initialized in Unit constructor through such means:

GPUMeshBuffers newBuffer;
      
VkBufferCreateInfo bufferInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufferInfo.pNext = nullptr;
bufferInfo.size = allocSize;
bufferInfo.usage = usage;
VmaAllocationCreateInfo vmaallocInfo = {};
vmaallocInfo.usage = memoryUsage;
vmaallocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
AllocatedBuffer newBuffer;

VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo, &vmaallocInfo, &newBuffer.buffer, &newBuffer.allocation,
       &newBuffer.info));
    
VkBufferDeviceAddressInfo deviceAdressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,.buffer = newBuffer.buffer };
newBuffer.vertexBufferAddress = vkGetBufferDeviceAddress(_device, &deviceAdressInfo);

GPUMeshBuffers staging = create_buffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);

void* data = staging.allocation->GetMappedData();

memcpy(data, vertices.data(), vertexBufferSize);

immediate_submit([&](VkCommandBuffer cmd) {
    VkBufferCopy vertexCopy{ 0 };
    vertexCopy.dstOffset = 0;
    vertexCopy.srcOffset = 0;
    vertexCopy.size = vertexBufferSize;

    vkCmdCopyBuffer(cmd, staging.buffer, newSurface.vertexBuffer.buffer, 1, &vertexCopy);
    });

    destroy_buffer(staging);

    return newSurface;

So, when initializing Unit in GameState with such code:

++_unit_IDs_count;
_unit_IDs.insert(std::pair{ _unit_IDs_count, Unit(vertices, texture_path, engine) });
return _unit_IDs_count;

There was memory access violation error at .insert line of a code

Then i tried to use

++_unit_IDs_count;
_unit_IDs.emplace(_unit_IDs_count, Unit(vertices, texture_path, engine));
return _unit_IDs_count;
And this way this exact error were gone, but when i was closing game there is now:
Assertion failed: m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!", file D:\Vuararar-v0.2\third_party\VulkanMemoryAllocator-17.11.2024\include\vk_mem_alloc.h, line 10650

Unit have destructor, where it frees buffer, so there is shouldn’t be such problem with freeing memory.
So then i tried to delete all copy constructors and mess explicitly with move constructors, but result was the same.
Then i tried explicitly delete buffers of a first instance of a object and create new ones in a move constructor with the same data, which lead to absence of any rendered objects in a launched “game”.
Which probably means that game for somewhat reason still takes old buffers of an object to render game and new buffers of a now placed in a unordered_map object aren’t used for somewhat reason?

How to solve this problem?

I don’t know how solve the problem and I’m not sure I can say much more than that it looks like you have (probably multiple) object lifetime/ownership issues in there.
There’s a couple of things that jump out as suspect to me:

  • Where you call immediate_submit your lambda calls destroy_buffer without ensuring the copy command has completed.
  • When using emplace to avoid constructing temporary objects and you have multiple arguments to pass to the key or value constructor use:
    emplace(std::piecewise_construct,
            std::forward_as_tuple(_unit_IDs_count),
            std::forward_as_tuple(vertices, texture_path, engine))
    
    if you know the key is not already in the map; otherwise use try_emplace.
  • If Unit owns the GPU memory buffers you most likely want it to be (at most) a move only type where the move constructor and move assignment operator transfer ownership of the GPU memory to the destination object. You’ll also have to ensure that the destructor of Unit synchronizes destroying the GPU buffers with any rendering that might be using those buffers (one approach is to transfer the GPU buffer ownership to some other object that destroys it after a few more frames have elapsed).