use concurrent_hash_map and parallel_for for chunk update

memory VIRT is higher, RES is the same. memory usage stabilizes after a while
fix-multithread
EmaMaker 2023-10-01 19:45:51 +02:00
parent 49edf2de85
commit a05d019c69
3 changed files with 109 additions and 93 deletions

View File

@ -47,9 +47,11 @@ namespace Chunk
void deleteBuffers(); void deleteBuffers();
glm::vec3 getPosition() { return this->position; } glm::vec3 getPosition() { return this->position; }
uint16_t getTotalState() { return this->state; }
bool getState(uint16_t n) { return (this->state & n) == n; }
void setState(uint16_t nstate, bool value); void setState(uint16_t nstate, bool value);
uint16_t getTotalState() { return this->state; }
bool getState(uint16_t n) { return (this->state & n) == n; }
bool isFree(){ return !(getState(CHUNK_STATE_IN_GENERATION_QUEUE) ||
getState(CHUNK_STATE_IN_MESHING_QUEUE)); }
void setBlock(Block b, int x, int y, int z); void setBlock(Block b, int x, int y, int z);
void setBlocks(int start, int end, Block b); void setBlocks(int start, int end, Block b);

View File

@ -21,8 +21,10 @@
namespace chunkmanager namespace chunkmanager
{ {
//typedef oneapi::tbb::concurrent_hash_map<uint32_t, Chunk::Chunk*> ChunkTable; typedef oneapi::tbb::concurrent_hash_map<uint32_t, Chunk::Chunk*> ChunkTable;
typedef std::unordered_map<int32_t, Chunk::Chunk*> ChunkTable; typedef oneapi::tbb::concurrent_queue<int> IntQueue;
//typedef std::unordered_map<int32_t, Chunk::Chunk*> ChunkTable;
typedef std::pair<Chunk::Chunk*, uint8_t> ChunkPQEntry; typedef std::pair<Chunk::Chunk*, uint8_t> ChunkPQEntry;
// The comparing function to use // The comparing function to use
struct compare_f { struct compare_f {
@ -38,7 +40,6 @@ namespace chunkmanager
void stop(); void stop();
void destroy(); void destroy();
oneapi::tbb::concurrent_queue<Chunk::Chunk*>& getDeleteVector();
std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices(); std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices();
Block getBlockAtPos(int x, int y, int z); Block getBlockAtPos(int x, int y, int z);
void update(); void update();

View File

@ -27,6 +27,8 @@ namespace chunkmanager
// Concurrent hash table of chunks // Concurrent hash table of chunks
ChunkTable chunks; ChunkTable chunks;
// Concurrent queue for chunks to be deleted
IntQueue chunks_todelete;
// Chunk indices. Centered at (0,0,0), going in concentric sphere outwards // Chunk indices. Centered at (0,0,0), going in concentric sphere outwards
std::array<std::array<int16_t, 3>, chunks_volume> chunks_indices; std::array<std::array<int16_t, 3>, chunks_volume> chunks_indices;
@ -40,13 +42,7 @@ namespace chunkmanager
int block_to_place{2}; int block_to_place{2};
// MEMORYTEST
bool populated{false};
bool populated2{false};
float start_time{0};
// Init chunkmanager. Chunk indices and start threads // Init chunkmanager. Chunk indices and start threads
int chunks_volume_real;
void init(){ void init(){
int index{0}; int index{0};
constexpr int rr{RENDER_DISTANCE * RENDER_DISTANCE}; constexpr int rr{RENDER_DISTANCE * RENDER_DISTANCE};
@ -73,9 +69,6 @@ namespace chunkmanager
mesh_thread = std::thread(mesh); mesh_thread = std::thread(mesh);
debug::window::set_parameter("block_type_return", &block_to_place); debug::window::set_parameter("block_type_return", &block_to_place);
// MEMORYTEST
start_time = glfwGetTime();
} }
// Method for world generation thread(s) // Method for world generation thread(s)
@ -96,39 +89,34 @@ namespace chunkmanager
ChunkPQEntry entry; ChunkPQEntry entry;
if(chunks_to_mesh_queue.try_pop(entry)){ if(chunks_to_mesh_queue.try_pop(entry)){
Chunk::Chunk* chunk = entry.first; Chunk::Chunk* chunk = entry.first;
if(chunk->getState(Chunk::CHUNK_STATE_GENERATED)){ chunkmesher::mesh(chunk);
chunkmesher::mesh(chunk); chunk->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, false);
entry.first->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, false);
}
} }
} }
chunks_to_mesh_queue.clear(); chunks_to_mesh_queue.clear();
} }
int nUnloaded{0};
std::queue<int32_t> chunks_todelete;
void update(){ void update(){
while(should_run) { while(should_run) {
std::atomic_int nUnloaded{0}, nMarkUnload{0}, nExplored{0}, nMeshed{0}, nGenerated{0};
std::atomic_int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE);
std::atomic_int chunkY=static_cast<int>(theCamera.getAtomicPosY() / CHUNK_SIZE);
std::atomic_int chunkZ=static_cast<int>(theCamera.getAtomicPosZ() / CHUNK_SIZE);
int nExplored{0}, nMeshed{0}, nGenerated{0}; // Eventually delete old chunks
int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE); int i;
int chunkY=static_cast<int>(theCamera.getAtomicPosY() / CHUNK_SIZE); ChunkTable::accessor a;
int chunkZ=static_cast<int>(theCamera.getAtomicPosZ() / CHUNK_SIZE); while(chunks_todelete.try_pop(i)){
const int index = i;
debug::window::set_parameter("update_chunks_tobedeleted", (int) chunks_todelete.size()); if(chunks.find(a, index)){
while(!chunks_todelete.empty()){ Chunk::Chunk* c = a->second;
int a = chunks_todelete.front(); if(chunks.erase(a)){
auto i = chunks.find(a); nUnloaded++;
if(chunks.erase(a)){ delete c;
delete i->second; } else std::cout << "failed to delete " << index << std::endl;
nUnloaded++; } else std::cout << "no such element found to delete\n";
}
else
std::cout << "no such element found to delete\n";
chunks_todelete.pop();
} }
// Eventually create new chunks near the player // Eventually create new chunks near the player
for(int i = 0; i < chunks_volume; i++) { for(int i = 0; i < chunks_volume; i++) {
const int16_t x = chunks_indices[i][0] + chunkX; const int16_t x = chunks_indices[i][0] + chunkX;
@ -138,74 +126,98 @@ namespace chunkmanager
if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue; if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue;
nExplored++; nExplored++;
const int32_t index = calculateIndex(x, y, z); const int32_t index = calculateIndex(x, y, z);
if(chunks.find(index) == chunks.end()) chunks.emplace(std::make_pair(index, new Chunk::Chunk(glm::vec3(x,y,z)))); ChunkTable::accessor a;
if(!chunks.find(a, index)) chunks.emplace(a, std::make_pair(index, new
Chunk::Chunk(glm::vec3(x,y,z))));
} }
for(auto a = chunks.begin(); a != chunks.end(); a++){ // Now update all the chunks
Chunk::Chunk* c = a->second; oneapi::tbb::parallel_for(chunks.range(), [&](ChunkTable::range_type &r){
for(ChunkTable::iterator a = r.begin(); a != r.end(); a++){
Chunk::Chunk* c = a->second;
int x = c->getPosition().x; int x = c->getPosition().x;
int y = c->getPosition().y; int y = c->getPosition().y;
int z = c->getPosition().z; int z = c->getPosition().z;
int distx = x - chunkX; int distx = x - chunkX;
int disty = y - chunkY; int disty = y - chunkY;
int distz = z - chunkZ; int distz = z - chunkZ;
if( int gen{0}, mesh{0}, unload{0};
distx >= -RENDER_DISTANCE && distx <= RENDER_DISTANCE &&
disty >= -RENDER_DISTANCE && disty <= RENDER_DISTANCE &&
distz >= -RENDER_DISTANCE && distz <= RENDER_DISTANCE
){
// If within distance if(
// Reset out-of-view flags distx >= -RENDER_DISTANCE && distx <= RENDER_DISTANCE &&
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, false); disty >= -RENDER_DISTANCE && disty <= RENDER_DISTANCE &&
c->setState(Chunk::CHUNK_STATE_UNLOADED, false); distz >= -RENDER_DISTANCE && distz <= RENDER_DISTANCE
){
// If not yet generated // If within distance
if(!c->getState(Chunk::CHUNK_STATE_GENERATED)){ // Reset out-of-view flags
if(!c->getState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE)){ c->setState(Chunk::CHUNK_STATE_OUTOFVISION, false);
// Generate c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
chunks_to_generate_queue.push(std::make_pair(c, GENERATION_PRIORITY_NORMAL));
c->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, true); // If not yet generated
} if(!c->getState(Chunk::CHUNK_STATE_GENERATED)){
}else{ if(!c->getState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE)){
nGenerated++; // Generate
// If generated but not yet meshed chunks_to_generate_queue.push(std::make_pair(c, GENERATION_PRIORITY_NORMAL));
// TODO: not getting meshed c->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, true);
if(!c->getState(Chunk::CHUNK_STATE_MESHED)){
if(!c->getState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE)){
// Mesh
chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_NORMAL));
c->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, true);
} }
}else{ }else{
nMeshed++; gen++;
// If generated & meshed, render // If generated but not yet meshed
/*if(!c->getState(Chunk::CHUNK_STATE_IN_RENDERING_QUEUE)){ // TODO: not getting meshed
renderer::getChunksToRender().push(c); if(!c->getState(Chunk::CHUNK_STATE_MESHED)){
c->setState(Chunk::CHUNK_STATE_IN_RENDERING_QUEUE, true); if(!c->getState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE)){
}*/ // Mesh
chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_NORMAL));
c->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, true);
}
}else{
mesh++;
// If generated & meshed, render
/*if(!c->getState(Chunk::CHUNK_STATE_IN_RENDERING_QUEUE)){
renderer::getChunksToRender().push(c);
c->setState(Chunk::CHUNK_STATE_IN_RENDERING_QUEUE, true);
}*/
}
}
}else{
// If not within distance
if(c->getState(Chunk::CHUNK_STATE_OUTOFVISION)){
// If enough time has passed, set to be deleted
if(c->isFree() && glfwGetTime() - c->unload_timer >= UNLOAD_TIMEOUT){
chunks_todelete.push(calculateIndex(x,y,z));
unload++;
}
}else{
// Mark as out of view, and start waiting time
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
c->unload_timer = glfwGetTime();
} }
} }
}else{ nGenerated += gen;
// If not within distance nMeshed += mesh;
if(c->getState(Chunk::CHUNK_STATE_OUTOFVISION)){ nMarkUnload += unload;
// If enough time has passed, set to be deleted
if(glfwGetTime() - c->unload_timer >= UNLOAD_TIMEOUT){
chunks_todelete.push(calculateIndex(x,y,z));
}
}else{
// Mark as out of view, and start waiting time
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
c->unload_timer = glfwGetTime();
}
} }
} });
std::cout << "time: " << glfwGetTime() << "\ntotal: " << chunks.size() << "\ngenerated: " << nGenerated << "\nmeshed: "
<< nMeshed << "\nunloaded from prev loop: " << nUnloaded << "\nnew marked for unload: " << nMarkUnload << std::endl;
/*debug::window::set_parameter("px", theCamera.getAtomicPosX());
debug::window::set_parameter("py", theCamera.getAtomicPosY());
debug::window::set_parameter("pz", theCamera.getAtomicPosZ());
debug::window::set_parameter("cx", chunkX);
debug::window::set_parameter("cy", chunkY);
debug::window::set_parameter("cz", chunkZ);
debug::window::set_parameter("lx", theCamera.getFront().x);
debug::window::set_parameter("ly", theCamera.getFront().y);
debug::window::set_parameter("lz", theCamera.getFront().z);
debug::window::set_parameter("update_chunks_total", (int) chunks.size()); debug::window::set_parameter("update_chunks_total", (int) chunks.size());
debug::window::set_parameter("update_chunks_buckets", (int) chunks.bucket_count()); debug::window::set_parameter("update_chunks_buckets", (int) chunks.bucket_count());
@ -213,6 +225,7 @@ namespace chunkmanager
debug::window::set_parameter("update_chunks_generated", nGenerated); debug::window::set_parameter("update_chunks_generated", nGenerated);
debug::window::set_parameter("update_chunks_meshed", nMeshed); debug::window::set_parameter("update_chunks_meshed", nMeshed);
debug::window::set_parameter("update_chunks_explored", nExplored); debug::window::set_parameter("update_chunks_explored", nExplored);
debug::window::set_parameter("update_chunks_tobedeleted", 0);*/
} }
} }