fallimentary attempt at fixing memory management with onetbb

fix-multithread
EmaMaker 2023-09-30 15:09:26 +02:00
parent 00cdd22e10
commit 1ec529fae5
9 changed files with 141 additions and 129 deletions

View File

@ -116,7 +116,7 @@ public:
private:
glm::vec3 cameraPos = glm::vec3(512.0, 80.0f, 512.0f);
glm::vec3 cameraPos = glm::vec3(512.0, 256.0f, 512.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 direction = glm::vec3(0.0f);

View File

@ -11,7 +11,7 @@
#include "globals.hpp"
// Seconds to be passed outside of render distance for a chunk to be destroyed
#define UNLOAD_TIMEOUT 10
#define UNLOAD_TIMEOUT 0
#define MESHING_PRIORITY_NORMAL 0
#define MESHING_PRIORITY_PLAYER_EDIT 10

View File

@ -9,7 +9,8 @@
#include "shader.hpp"
namespace renderer{
typedef oneapi::tbb::concurrent_unordered_set<Chunk::Chunk*> RenderSet;
//typedef oneapi::tbb::concurrent_unordered_set<Chunk::Chunk*> RenderSet;
typedef oneapi::tbb::concurrent_queue<Chunk::Chunk*> RenderQueue;
void init(GLFWwindow* window);
void render();
@ -20,7 +21,7 @@ namespace renderer{
void saveScreenshot(bool forceFullHD=false);
Shader* getRenderShader();
RenderSet& getChunksToRender();
RenderQueue& getChunksToRender();
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*>& getMeshDataQueue();
};

View File

@ -7,4 +7,4 @@ set(SOURCE_FILES main.cpp chunk.cpp chunkmanager.cpp chunkmesher.cpp chunkgenera
add_executable(OpenGLTest ${SOURCE_FILES})
target_link_libraries(OpenGLTest glfw tbb glad glm imgui)
install(TARGETS OpenGLTest DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR})
install(TARGETS OpenGLTest DESTINATION)

View File

@ -1,12 +1,12 @@
#include "chunkmanager.hpp"
#include <oneapi/tbb.h>
#include <atomic>
#include <math.h>
#include <vector>
#include <thread>
#include <oneapi/tbb.h>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/gtx/string_cast.hpp>
@ -63,7 +63,7 @@ namespace chunkmanager
chunkmesher::getMeshDataQueue().push(new chunkmesher::MeshData());
should_run = true;
update_thread = std::thread(update);
//update_thread = std::thread(update);
gen_thread = std::thread(generate);
mesh_thread = std::thread(mesh);
@ -92,11 +92,14 @@ namespace chunkmanager
}
oneapi::tbb::concurrent_queue<Chunk::Chunk*> chunks_todelete;
oneapi::tbb::concurrent_queue<Chunk::Chunk*> chunks_primary_delete;
int nUnloaded{0};
int already{0};
bool first{true};
void update(){
while(should_run) {
}
void primary_thread_update(){
int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE);
int chunkY=static_cast<int>(theCamera.getAtomicPosY() / CHUNK_SIZE);
int chunkZ=static_cast<int>(theCamera.getAtomicPosZ() / CHUNK_SIZE);
@ -139,43 +142,66 @@ namespace chunkmanager
debug::window::set_parameter("update_chunks_total", (int) (chunks.size()));
debug::window::set_parameter("update_chunks_bucket", (int) (chunks.max_size()));
// Perform needed operations on all the chunks
oneapi::tbb::parallel_for(chunks.range(), [](ChunkTable::range_type &r){
oneapi::tbb::parallel_for(chunks.range(), [=](ChunkTable::range_type &r){
for(ChunkTable::const_iterator a = r.begin(); a != r.end(); a++){
if(a->second->getState(Chunk::CHUNK_STATE_UNLOADED)){
chunks_todelete.push(a->second);
if(a->second->getState(Chunk::CHUNK_STATE_MESH_LOADED)) chunks_todelete.push(a->second);
//nUnloaded++;
continue;
}
if(! (a->second->getState(Chunk::CHUNK_STATE_GENERATED))) {
chunks_to_generate_queue.push(std::make_pair(a->second, GENERATION_PRIORITY_NORMAL));
}else if(a->second->getState(Chunk::CHUNK_STATE_EMPTY)){
continue;
}else if(! (a->second->getState(Chunk::CHUNK_STATE_MESHED))){
int x = a->second->getPosition().x;
int y = a->second->getPosition().y;
int z = a->second->getPosition().z;
int distx = a->second->getPosition().x - chunkX;
int disty = a->second->getPosition().y - chunkY;
int distz = a->second->getPosition().z - chunkZ;
if(distx >= -RENDER_DISTANCE && distx < RENDER_DISTANCE &&
disty >= -RENDER_DISTANCE && disty < RENDER_DISTANCE &&
distz >= -RENDER_DISTANCE && distz < RENDER_DISTANCE){
ChunkTable::const_accessor a1;
if(
(x + 1 > 1023 || (chunks.find(a1, calculateIndex(x+1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(x - 1 < 0|| (chunks.find(a1, calculateIndex(x-1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y+1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y - 1 < 0|| (chunks.find(a1, calculateIndex(x, y-1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y, z+1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z - 1 < 0|| (chunks.find(a1, calculateIndex(x, y, z-1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED)))
)
chunks_to_mesh_queue.push(std::make_pair(a->second, MESHING_PRIORITY_NORMAL));
// reset out-of-vision and unload flags
a->second->setState(Chunk::CHUNK_STATE_OUTOFVISION, false);
a->second->setState(Chunk::CHUNK_STATE_UNLOADED, false);
if(! (a->second->getState(Chunk::CHUNK_STATE_GENERATED))) {
chunks_to_generate_queue.push(std::make_pair(a->second, GENERATION_PRIORITY_NORMAL));
}else if(! (a->second->getState(Chunk::CHUNK_STATE_MESHED))){
int x = a->second->getPosition().x;
int y = a->second->getPosition().y;
int z = a->second->getPosition().z;
ChunkTable::const_accessor a1;
if(
(x + 1 > 1023 || (chunks.find(a1, calculateIndex(x+1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(x - 1 < 0|| (chunks.find(a1, calculateIndex(x-1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y+1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y - 1 < 0|| (chunks.find(a1, calculateIndex(x, y-1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y, z+1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z - 1 < 0|| (chunks.find(a1, calculateIndex(x, y, z-1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED)))
)
chunks_to_mesh_queue.push(std::make_pair(a->second, MESHING_PRIORITY_NORMAL));
}else{
renderer::getChunksToRender().push(a->second);
}
}else{
renderer::getChunksToRender().insert(a->second);
if(a->second->getState(Chunk::CHUNK_STATE_OUTOFVISION)){
// If chunk was already out and enough time has passed
if(glfwGetTime() - a->second->unload_timer > UNLOAD_TIMEOUT){
// Mark the chunk to be unloaded
a->second->setState(Chunk::CHUNK_STATE_UNLOADED, true);
}
} else{
// Mark has out of vision and annotate when it started
a->second->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
a->second->setState(Chunk::CHUNK_STATE_UNLOADED, false);
a->second->unload_timer = glfwGetTime();
}
}
}
});
@ -191,15 +217,13 @@ namespace chunkmanager
//std::cout << n->getState(Chunk::CHUNK_STATE_GENERATED) << "\n";
if(chunks.erase(index)){
//n->deleteBuffers();
delete n;
nUnloaded++;
}else
std::cout << "failed to free chunk at" << glm::to_string(n->getPosition()) <<
std::endl;
}
debug::window::set_parameter("update_chunks_freed", nUnloaded);
}
}
// uint32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1023). There's actually two spare bits
@ -212,14 +236,11 @@ namespace chunkmanager
void stop() {
should_run=false;
update_thread.join();
//update_thread.join();
gen_thread.join();
mesh_thread.join();
}
void destroy(){
for(const auto& n : chunks){
delete n.second;
}
}
void blockpick(bool place){

View File

@ -117,8 +117,6 @@ namespace debug{
std::any_cast<int>(parameters.at("update_chunks_total")));
ImGui::Text("Chunks freed from memory: %d",
std::any_cast<int>(parameters.at("update_chunks_freed")));
ImGui::Text("Bucket size: %d",
std::any_cast<int>(parameters.at("update_chunks_bucket")));
ImGui::Text("Chunks explored: %d",
std::any_cast<int>(parameters.at("update_chunks_explored")));
}

View File

@ -106,6 +106,8 @@ int main()
// Render pass
renderer::render();
chunkmanager::primary_thread_update();
// Swap buffers to avoid tearing
glfwSwapBuffers(window);
glfwPollEvents();

View File

@ -15,15 +15,14 @@
#include "stb_image_write.h"
namespace renderer{
RenderSet chunks_torender;
oneapi::tbb::concurrent_vector<Chunk::Chunk*> render_todelete;
RenderQueue chunks_torender;
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*> MeshDataQueue;
Shader* theShader, *quadShader;
GLuint chunkTexture;
Shader* getRenderShader() { return theShader; }
RenderSet& getChunksToRender(){ return chunks_torender; }
RenderQueue& getChunksToRender(){ return chunks_torender; }
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*>& getMeshDataQueue(){ return MeshDataQueue; }
GLuint renderTexFrameBuffer, renderTex, renderTexDepthBuffer, quadVAO, quadVBO;
@ -147,102 +146,59 @@ namespace renderer{
chunkmesher::getMeshDataQueue().push(m);
}
for(auto& c : chunks_torender){
//float dist = glm::distance(c->getPosition(), cameraChunkPos);
//if(static_cast<int>(dist) <= RENDER_DISTANCE + 1){
int distx = c->getPosition().x - cameraChunkPos.x;
int disty = c->getPosition().y - cameraChunkPos.y;
int distz = c->getPosition().z - cameraChunkPos.z;
if(distx >= -RENDER_DISTANCE && distx < RENDER_DISTANCE &&
disty >= -RENDER_DISTANCE && disty < RENDER_DISTANCE &&
distz >= -RENDER_DISTANCE && distz < RENDER_DISTANCE){
if(!c->getState(Chunk::CHUNK_STATE_MESH_LOADED)) continue;
Chunk::Chunk* c;
while(chunks_torender.try_pop(c)){
if(!c->getState(Chunk::CHUNK_STATE_MESH_LOADED)) continue;
total++;
// reset out-of-vision and unload flags
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, false);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
if(c->numVertices > 0)
{
if(c->numVertices > 0)
{
// Increase total vertex count
vertices += c->numVertices;
// Increase total vertex count
vertices += c->numVertices;
// Perform frustum culling and eventually render
glm::vec3 chunk = c->getPosition();
glm::vec4 chunkW = glm::vec4(chunk.x*static_cast<float>(CHUNK_SIZE), chunk.y*static_cast<float>(CHUNK_SIZE), chunk.z*static_cast<float>(CHUNK_SIZE),1.0);
glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk);
// Perform frustum culling and eventually render
glm::vec3 chunk = c->getPosition();
glm::vec4 chunkW = glm::vec4(chunk.x*static_cast<float>(CHUNK_SIZE), chunk.y*static_cast<float>(CHUNK_SIZE), chunk.z*static_cast<float>(CHUNK_SIZE),1.0);
glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk);
// Check if all the corners of the chunk are outside any of the planes
// TODO (?) implement frustum culling as per (Inigo Quilez)[https://iquilezles.org/articles/frustumcorrect/], and check each
// plane against each corner of the chunk
bool out=false;
int a{0};
for(int p = 0; p < 6; p++){
a = 0;
for(int i = 0; i < 8; i++) a += glm::dot(frustumPlanes[p], glm::vec4(chunkW.x + ((float)(i & 1))*CHUNK_SIZE, chunkW.y
+ ((float)((i & 2) >> 1))*CHUNK_SIZE, chunkW.z + ((float)((i & 4) >> 2))*CHUNK_SIZE, 1.0)) < 0.0;
// Check if all the corners of the chunk are outside any of the planes
// TODO (?) implement frustum culling as per (Inigo Quilez)[https://iquilezles.org/articles/frustumcorrect/], and check each
// plane against each corner of the chunk
bool out=false;
int a{0};
for(int p = 0; p < 6; p++){
a = 0;
for(int i = 0; i < 8; i++) a += glm::dot(frustumPlanes[p], glm::vec4(chunkW.x + ((float)(i & 1))*CHUNK_SIZE, chunkW.y
+ ((float)((i & 2) >> 1))*CHUNK_SIZE, chunkW.z + ((float)((i & 4) >> 2))*CHUNK_SIZE, 1.0)) < 0.0;
if(a==8){
out=true;
break;
}
}
if (!out)
{
theShader->setMat4("model", model);
theShader->setMat4("view", theCamera.getView());
theShader->setMat4("projection", theCamera.getProjection());
glBindVertexArray(c->VAO);
glDrawArrays(GL_POINTS, 0, c->numVertices);
glBindVertexArray(0);
toGpu++;
if(a==8){
out=true;
break;
}
}
}else{
// When the chunk is outside render distance
/*if(c->getState(Chunk::CHUNK_STATE_OUTOFVISION)){
oof++;
if(glfwGetTime() - c->unload_timer > UNLOAD_TIMEOUT){
// If chunk was already out and enough time has passed
// Mark the chunk to be unloaded
// And mark is to be removed from the render set
render_todelete.push_back(c);
}
} else{
// Mark has out of vision and annotate when it started
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
c->unload_timer = glfwGetTime();
}*/
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
c->setState(Chunk::CHUNK_STATE_UNLOADED, true);
//render_todelete.push_back(c);
oof++;
if (!out)
{
theShader->setMat4("model", model);
theShader->setMat4("view", theCamera.getView());
theShader->setMat4("projection", theCamera.getProjection());
glBindVertexArray(c->VAO);
glDrawArrays(GL_POINTS, 0, c->numVertices);
glBindVertexArray(0);
toGpu++;
}
}
}
total = chunks_torender.size();
debug::window::set_parameter("render_chunks_total", total);
debug::window::set_parameter("render_chunks_rendered", toGpu);
debug::window::set_parameter("render_chunks_culled", total-toGpu);
debug::window::set_parameter("render_chunks_oof", oof);
debug::window::set_parameter("render_chunks_deleted", (int) (render_todelete.size()));
debug::window::set_parameter("render_chunks_vertices", vertices);
/*for(auto& c : render_todelete){
// we can get away with unsafe erase as access to the container is only done by this
// thread
chunks_torender.unsafe_erase(c);
c->setState(Chunk::CHUNK_STATE_UNLOADED, true);
//c->deleteBuffers();
}
render_todelete.clear();*/
/* DISPLAY TEXTURE ON A QUAD THAT FILLS THE SCREEN */
// Now to render the quad, with the texture on top
// Switch to the default frame buffer

34
test2.cpp Normal file
View File

@ -0,0 +1,34 @@
#include <iostream>
#include <string>
#include <thread>
#include "oneapi/tbb/concurrent_hash_map.h"
#include "oneapi/tbb/tbb_allocator.h"
typedef oneapi::tbb::concurrent_hash_map<int, int, oneapi::tbb::tbb_allocator<int>> CTable;
CTable table;
void f(){
/*while(table.size() > 0){
std::cout << "---------------\n";
oneapi::tbb::parallel_for(table.range(), [=](Table::range_type &r){
for(Table::const_iterator a = r.begin(); a != r.end(); a++){
std::cout << a->first << ": " << a->second << std::endl;
}
});
}*/
}
int main(){
std::thread t = std::thread(f);
//Table::accessor a;
/*table.emplace(a, std::make_pair(0, "zero"));
table.emplace(a, std::make_pair(1, "one"));
table.emplace(a, std::make_pair(2, "two"));
table.emplace(a, std::make_pair(3, "three"));
table.emplace(a, std::make_pair(4, "four"));
table.emplace(a, std::make_pair(5, "five"));*/
t.join();
}