diff --git a/include/camera.hpp b/include/camera.hpp index 9460182..3905f2f 100644 --- a/include/camera.hpp +++ b/include/camera.hpp @@ -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); diff --git a/include/chunkmanager.hpp b/include/chunkmanager.hpp index 3597b0d..688e376 100644 --- a/include/chunkmanager.hpp +++ b/include/chunkmanager.hpp @@ -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 diff --git a/include/renderer.hpp b/include/renderer.hpp index d772c77..a7575e6 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -9,7 +9,8 @@ #include "shader.hpp" namespace renderer{ - typedef oneapi::tbb::concurrent_unordered_set RenderSet; + //typedef oneapi::tbb::concurrent_unordered_set RenderSet; + typedef oneapi::tbb::concurrent_queue 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& getMeshDataQueue(); }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6775018..ea523ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/chunkmanager.cpp b/src/chunkmanager.cpp index 2176b1e..e8cb1d6 100644 --- a/src/chunkmanager.cpp +++ b/src/chunkmanager.cpp @@ -1,12 +1,12 @@ #include "chunkmanager.hpp" +#include + #include #include #include #include -#include - #include #include #include @@ -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 chunks_todelete; + oneapi::tbb::concurrent_queue 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(theCamera.getAtomicPosX() / CHUNK_SIZE); int chunkY=static_cast(theCamera.getAtomicPosY() / CHUNK_SIZE); int chunkZ=static_cast(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){ diff --git a/src/debugwindow.cpp b/src/debugwindow.cpp index bd16c64..d9a7228 100644 --- a/src/debugwindow.cpp +++ b/src/debugwindow.cpp @@ -117,8 +117,6 @@ namespace debug{ std::any_cast(parameters.at("update_chunks_total"))); ImGui::Text("Chunks freed from memory: %d", std::any_cast(parameters.at("update_chunks_freed"))); - ImGui::Text("Bucket size: %d", - std::any_cast(parameters.at("update_chunks_bucket"))); ImGui::Text("Chunks explored: %d", std::any_cast(parameters.at("update_chunks_explored"))); } diff --git a/src/main.cpp b/src/main.cpp index 1f185f4..cfdff9b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -106,6 +106,8 @@ int main() // Render pass renderer::render(); + chunkmanager::primary_thread_update(); + // Swap buffers to avoid tearing glfwSwapBuffers(window); glfwPollEvents(); diff --git a/src/renderer.cpp b/src/renderer.cpp index e4f67b3..c9dd381 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -15,15 +15,14 @@ #include "stb_image_write.h" namespace renderer{ - RenderSet chunks_torender; - oneapi::tbb::concurrent_vector render_todelete; + RenderQueue chunks_torender; oneapi::tbb::concurrent_queue MeshDataQueue; Shader* theShader, *quadShader; GLuint chunkTexture; Shader* getRenderShader() { return theShader; } - RenderSet& getChunksToRender(){ return chunks_torender; } + RenderQueue& getChunksToRender(){ return chunks_torender; } oneapi::tbb::concurrent_queue& 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(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(CHUNK_SIZE), chunk.y*static_cast(CHUNK_SIZE), chunk.z*static_cast(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(CHUNK_SIZE), chunk.y*static_cast(CHUNK_SIZE), chunk.z*static_cast(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 diff --git a/test2.cpp b/test2.cpp new file mode 100644 index 0000000..737cc0a --- /dev/null +++ b/test2.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "oneapi/tbb/concurrent_hash_map.h" +#include "oneapi/tbb/tbb_allocator.h" + +typedef oneapi::tbb::concurrent_hash_map> 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(); +}