NicoS
NicoS

Reputation: 55

How to use std::vector<char> instead of void** when mapping memory with Vulkan?

I've been trying to remove as much as possible any raw and unique_ptr from a custom game engine. The API used is Vulkan. And for now one on my easiest solution for all vulkan's data is to use a simple std::vector<char>.

But their is one point that I can't get my head around, it's when I'm mapping my memory to the LogicalDevice.

To map my memory I use this function:

void Buffer::MapMemory(char** data) const {
    CheckVk(vkMapMemory(kLogicalDevice_, GetBufferMemory(), 0, size_, 0, reinterpret_cast<void**>(data)));
}

Now I'm calling this function from a few point from my graphics engine, one of them is from my UniformBuffer:

void UniformBuffer::Update(const std::vector<char>& newData) const {
    char* data;
    MapMemory(&data);
    std::memcpy(data, newData.data(), static_cast<std::size_t>(size_));
    UnmapMemory();
}

A simple function when I map the data to the GPU, write on it, then unmap it.

I wish to remove the char* and replace it with a std::vector<char>.


What I've tested so far:

void UniformBuffer::Update(const std::vector<char>& newData) const {
    std::vector<char> data;
    MapMemory(reinterpret_cast<char**>(data.data()));
    std::memcpy(data.data(), newData.data(), static_cast<std::size_t>(size_));
    UnmapMemory();
}

The program compile but the datas doesn't seemed to be mapped because nothing is written on the GPU. (I've even looked with RenderDoc and nothing seems to be sent to the GPU).

Upvotes: 1

Views: 459

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473447

I wish to remove the char* and replace it with a std::vector<char>.

That's nice. But it's not gonna happen.

First, stop treating memory mapping like a temporary operation. If you're going to map a block of memory, do it once when you allocate it and keep the pointer around. vkUnmapMemory should only ever be called when you're about to deallocate the memory that has been mapped.

Also map the entire block of memory, since any single allocation of memory can only be mapped once. And you no doubt already know not to call vkAllocateMemory for every single individual buffer you want to create.

Second, mapping memory means that Vulkan device makes the memory available to the CPU for reading/writing (based on the mapping operation). You do not own that memory; the Vulkan device own it. It is just sharing that pointer with you.

vector owns the memory it allocates. It allocates it and it deletes it, through a user-specified allocator type.

Now, you might think that you can just create a vector<char, MappedAllocator<char>>, where MappedAllocator is some allocator object which "allocates" its memory from a mapped pointer. But that won't work. See, the allocator allocates the memory, but the vector is the one to decide how much memory to allocate and when to allocate it.

Without being able to control how much memory the vector can attempt to allocate, there's not really an effective way to write an allocator over a static buffer of memory.

If you just want to keep track of the size of the mapped storage, it'd be better to use something like gsl::span<char>. But otherwise, just have a pointer and do pointer-y stuff. It's really not worth it to try to write your own version of vector just for this.

Upvotes: 2

Related Questions