1
General / Using std::futures for creating chunks
« on: July 21, 2024, 06:50:54 pm »
I am currently working on creating an isometric terrain generation program and was wondering if it would be possible to optimize my code by using futures when creating chunks. The chunks get created and destroyed pretty often, so allowing this to run in parallel would optimize my code by a ton. I also modify the blocks in the individual chunks, so being able to split this process up into multiple threads would render be big performance gains. I just cannot figure out how to actually put this into code. What I have been trying to do is using these data structures, where chunks to load are the chunks created asynchronously to be merged with chunks, chunks is the map of all the chunks currently visible, and futures are just a collection of futures I used to create the chunks:
std::map<std::pair<int,int>, Chunk> chunks;
std::map<std::pair<int,int>, Chunk> chunksToLoad; //used threads
std::vector<std::future<void>> futures;
Then I attempted to create the chunks and update them in these two functions:std::map<std::pair<int,int>, Chunk> chunksToLoad; //used threads
std::vector<std::future<void>> futures;
static std::mutex mtx;
static void generateChunk(int chunkX, int chunkY, std::map<std::pair<int,int>, Chunk> *chunkToLoad,
const siv::PerlinNoise &perlin, int octaves, float persistence, float frequency) {
sf::Vector2i blockCoords(chunkX * Chunks::size, chunkY * Chunks::size);
Chunk chunk;
chunk.setVisibleBlocks(blockCoords, perlin, octaves, persistence, frequency);
std::lock_guard<std::mutex> lock(mtx);
chunkToLoad->insert({{blockCoords.x, blockCoords.y}, chunk});
}
void chunkManager::updateChunks() {
for(int i = -Manage::renderDistance; i <= Manage::renderDistance; ++i){
for(int j = -Manage::renderDistance; j <= Manage::renderDistance; ++j){
futures.push_back(std::async(std::launch::async, generateChunk, chunkPosition.x + i, chunkPosition.y + j,
&chunkToLoad, perlin, octaves, persistence, frequency));
}
}
for(auto &pair : chunkToLoad){
if(chunks.find(pair.first) == chunks.end()){
chunks[pair.first] = pair.second;
}
}
chunkToLoad.clear();
futures.clear();
}
This whole process proves to be super slow for creating the world, much slower than when I run just on one thread. Does anyone have any advice for how I could take a better approach at creating these chunks in parallel? Is it even possible in this case? Thank you very much to anyone who can help.
static void generateChunk(int chunkX, int chunkY, std::map<std::pair<int,int>, Chunk> *chunkToLoad,
const siv::PerlinNoise &perlin, int octaves, float persistence, float frequency) {
sf::Vector2i blockCoords(chunkX * Chunks::size, chunkY * Chunks::size);
Chunk chunk;
chunk.setVisibleBlocks(blockCoords, perlin, octaves, persistence, frequency);
std::lock_guard<std::mutex> lock(mtx);
chunkToLoad->insert({{blockCoords.x, blockCoords.y}, chunk});
}
void chunkManager::updateChunks() {
for(int i = -Manage::renderDistance; i <= Manage::renderDistance; ++i){
for(int j = -Manage::renderDistance; j <= Manage::renderDistance; ++j){
futures.push_back(std::async(std::launch::async, generateChunk, chunkPosition.x + i, chunkPosition.y + j,
&chunkToLoad, perlin, octaves, persistence, frequency));
}
}
for(auto &pair : chunkToLoad){
if(chunks.find(pair.first) == chunks.end()){
chunks[pair.first] = pair.second;
}
}
chunkToLoad.clear();
futures.clear();
}