From 7ffc349eb3ba86b1cd07d9700d737dab574b5439 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:34:45 +0800 Subject: [PATCH] refactor: chunk load (#21) * feat(renderer): add fog effect based on render distance * refactor(gameplay): remove dead code and simplify chunk neighbor context Remove the large commented-out `init_chunks()` function, and eliminate the `affected_neighbor` tracking in `gen_chunks_internal()`. This simplifies the neighbor context building and removes unused vertex data regeneration for affected neighbors. * refactor(gameplay): move chunk generation phases into gen_chunk method Consolidate multiple phase generation calls into a single gen_chunk() method on Chunk, which handles neighbor generation and ensures thread safety. Simplify World::gen_chunks_internal by using gen_chunk() instead of manual phase orchestration. * fix(gameplay): use gen_phase_one to get seed * feat(world): integrate thread pool and async chunk generation * fix(renderer): correct shader uniform name and remove unused uniform * feat(gameplay): add temporary chunk flag to prevent path clearing * fix: add thread safety for cave and river path mutexes * refactor: remove unused uniforms and set cameraPos uniform outside lambda * feat(world): add dynamic thread pool resizing * feat(world): add thread pool size management and UI controls * feat(world): add ChunkLoadStyle enum and rename chunk_pos to get_chunk_pos * feat(player): add configurable fly Y speed * refactor(world): add thread pool start/stop and auto-detect threads Extract thread pool starting and stopping into dedicated methods. Set default pool threads to 0 to enable automatic detection of available cores, and ensure thread pool is properly managed during world rebuild. * fix: include shared_mutex header * fix(gameplay): simplify set_chunk_load_style switch and fix fallthrough bug * fix(world): add missing include for --- assets/shaders/block_f_shader.glsl | 15 +- assets/shaders/water_f_shader.glsl | 1 - include/Cubed/constants.hpp | 2 +- include/Cubed/dev_panel.hpp | 2 + include/Cubed/gameplay/cave_carver.hpp | 4 + include/Cubed/gameplay/chunk.hpp | 9 +- include/Cubed/gameplay/player.hpp | 2 + include/Cubed/gameplay/river_worm.hpp | 5 + include/Cubed/gameplay/world.hpp | 51 +- include/Cubed/tools/thread_pool.hpp | 123 +++++ lsan.supp | 3 +- src/dev_panel.cpp | 25 + src/gameplay/cave_carver.cpp | 3 + src/gameplay/chunk.cpp | 42 +- src/gameplay/chunk_generator.cpp | 75 +-- src/gameplay/player.cpp | 7 +- src/gameplay/river_worm.cpp | 6 +- src/gameplay/world.cpp | 718 +++++++------------------ src/renderer.cpp | 9 +- 19 files changed, 501 insertions(+), 601 deletions(-) create mode 100644 include/Cubed/tools/thread_pool.hpp diff --git a/assets/shaders/block_f_shader.glsl b/assets/shaders/block_f_shader.glsl index eef3983..b3d28d8 100644 --- a/assets/shaders/block_f_shader.glsl +++ b/assets/shaders/block_f_shader.glsl @@ -25,6 +25,10 @@ uniform float minRadius; uniform float maxRadius; uniform bool enablePBR; uniform bool flipY; + +uniform int renderDistance; +uniform vec3 skyColor; + const vec2 poissonDisk32[32] = vec2[]( vec2(-0.975402, -0.071138), vec2(-0.920347, -0.411420), @@ -341,8 +345,17 @@ void main(void) { vec3 specular = spec * sunlightColor * specularStrength; float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); - + + // fog + float dist = length(cameraPos - vert_pos); + vec4 fogColor = vec4(skyColor, 1.0); + float fogStart = renderDistance * 16 * 0.9; + float fogEnd = renderDistance * 16; + + float fogFactor = smoothstep(fogEnd, fogStart, dist); color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular * objectColor.rgb, objectColor.a); + + color = mix(fogColor, color, fogFactor); //color = vec4(normal * 0.5 + 0.5, 1.0); //color = vec4(tangent * 0.5 + 0.5, 1.0);; //color = vec4(norm * 0.5 + 0.5, 1.0); diff --git a/assets/shaders/water_f_shader.glsl b/assets/shaders/water_f_shader.glsl index 31ff808..7b3f52d 100644 --- a/assets/shaders/water_f_shader.glsl +++ b/assets/shaders/water_f_shader.glsl @@ -22,7 +22,6 @@ uniform float ambientStrength; uniform vec3 sunlightColor; uniform vec3 ambientColor; uniform vec3 sunlightDir; -uniform vec3 cameraPos; uniform bool shader_on; uniform float specularStrength; diff --git a/include/Cubed/constants.hpp b/include/Cubed/constants.hpp index a8a3151..aad82aa 100644 --- a/include/Cubed/constants.hpp +++ b/include/Cubed/constants.hpp @@ -26,7 +26,7 @@ constexpr float DEFAULT_G = 22.5f; constexpr int SIZE_X = CHUNK_SIZE; constexpr int SIZE_Y = WORLD_SIZE_Y; constexpr int SIZE_Z = CHUNK_SIZE; - +constexpr int RESERVED_THREADS = 3; constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; diff --git a/include/Cubed/dev_panel.hpp b/include/Cubed/dev_panel.hpp index 7993746..75d2c96 100644 --- a/include/Cubed/dev_panel.hpp +++ b/include/Cubed/dev_panel.hpp @@ -48,6 +48,8 @@ private: int m_pre_set_tick_speed = 1; bool m_tick_frezze = false; int m_samples_idx = 1; + int m_threads = 1; + int m_chunk_style = 0; void show_about_table_bar(); void show_biome_table_bar(); void show_time_table_bar(); diff --git a/include/Cubed/gameplay/cave_carver.hpp b/include/Cubed/gameplay/cave_carver.hpp index cbfea66..e737f3f 100644 --- a/include/Cubed/gameplay/cave_carver.hpp +++ b/include/Cubed/gameplay/cave_carver.hpp @@ -1,7 +1,9 @@ #pragma once #include "Cubed/gameplay/cave_path.hpp" +#include #include + namespace Cubed { class CaveCarver { using CaveHashMap = tbb::concurrent_hash_map; @@ -17,11 +19,13 @@ public: int cave_sum() const; float& cave_probability(); + std::shared_mutex& path_mutex(); private: CaveHashMap m_paths; unsigned m_seed = 0; Random m_random; float m_cave_probability = 0.035f; + std::shared_mutex m_path_mutex; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index 95df9a1..166864b 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -23,6 +23,9 @@ private: std::atomic m_dirty{false}; std::atomic m_need_upload{true}; std::atomic m_is_on_gen_vertex_data{false}; + std::atomic m_gening{false}; + std::atomic m_temp_chunk{false}; + std::atomic m_biome = BiomeType::PLAIN; std::mutex m_vertexs_data_mutex; @@ -54,7 +57,7 @@ private: BlockType id); public: - Chunk(World& world, ChunkPos chunk_pos); + Chunk(World& world, ChunkPos chunk_pos, bool temp_chunk = false); ~Chunk(); Chunk(const Chunk&) = delete; Chunk& operator=(const Chunk&) = delete; @@ -124,7 +127,9 @@ public: void need_upload(); void set_chunk_block(int index, unsigned id); - + // ensure thread safe! + void gen_chunk(); + bool is_temp_chunk() const; ChunkPos chunk_pos() const; BiomeType biome() const; void biome(BiomeType b); diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/player.hpp index b3a9322..2dba631 100644 --- a/include/Cubed/gameplay/player.hpp +++ b/include/Cubed/gameplay/player.hpp @@ -34,6 +34,7 @@ private: float m_max_speed = m_max_walk_speed; float m_y_speed = 0.0f; + float m_fly_y_speed = 7.5f; bool can_up = true; float space_on_time = 0.0f; @@ -99,6 +100,7 @@ public: float& acceleration(); float& deceleration(); float& g(); + float& fly_y_speed(); unsigned place_block() const; diff --git a/include/Cubed/gameplay/river_worm.hpp b/include/Cubed/gameplay/river_worm.hpp index c2c4fa2..cdcdec8 100644 --- a/include/Cubed/gameplay/river_worm.hpp +++ b/include/Cubed/gameplay/river_worm.hpp @@ -4,7 +4,9 @@ #include "Cubed/tools/cubed_random.hpp" #include +#include #include + namespace Cubed { class RiverWorm { @@ -12,6 +14,7 @@ class RiverWorm { public: RiverWorm(); + ~RiverWorm(); RiverHashMap& paths(); void init(unsigned world_seed); void reload(unsigned world_seed); @@ -21,12 +24,14 @@ public: int river_sum() const; float& river_probability(); + std::shared_mutex& paths_mutex(); private: RiverHashMap m_paths; unsigned m_seed = 0; Random m_random; float m_probability = 0.01f; + std::shared_mutex m_paths_mutex; }; }; // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index aafe7c4..1d22473 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -4,6 +4,7 @@ #include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/river_worm.hpp" +#include "Cubed/tools/thread_pool.hpp" #include #include @@ -34,15 +35,24 @@ class Player; class TextureManager; class World { private: + enum class ChunkLoadStyle { RANDOM, CENTER }; + + struct PendingChunk { + Chunk chunk; + std::future future; + }; + using OptionalBlockVectorArray = std::array>, 4>; using ChunkPtrUpdateList = std::vector>; using ChunkPairVector = std::vector>; + using ChunkPairQueue = std::queue>; using ConstChunkMap = std::unordered_map; using ChunkPosSet = std::unordered_set; using ChunkHashMap = std::unordered_map; - + using PendingChunkHashMap = + std::unordered_map; glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f}; ChunkHashMap m_chunks; std::unordered_map m_players; @@ -50,7 +60,7 @@ private: std::thread m_gen_thread; std::thread m_server_thread; - + std::atomic> m_gen_thread_pool; std::stop_source m_server_stop_source; std::atomic m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms @@ -59,7 +69,7 @@ private: mutable std::mutex m_chunks_mutex; std::mutex m_gen_signal_mutex; - std::mutex m_new_chunk_queue_mutex; + std::mutex m_new_chunk_mutex; std::mutex m_delete_vbo_mutex; std::mutex m_delete_vao_mutex; std::mutex m_gen_player_pos_mutex; @@ -74,13 +84,15 @@ private: std::atomic m_tick_running{true}; std::atomic m_rendering_distance{24}; std::atomic m_chunk_gen_fraction{0.0f}; - + std::atomic m_pool_threads{0}; + std::atomic m_max_threads{1}; std::atomic m_game_ticks{0}; - + std::atomic m_chunk_load_style{ChunkLoadStyle::RANDOM}; std::vector m_dirty_queue; std::vector m_render_snapshots; - std::vector> m_new_chunk; - std::vector> m_new_chunk_queue; + std::vector> m_new_finished_chunk; + // Can only be used in the gen thread + PendingChunkHashMap new_chunks; CaveCarver m_cave_carcer; RiverWorm m_river_worm; @@ -88,18 +100,14 @@ private: void gen_chunks_internal(); void sync_player_pos(glm::vec3& player_pos); - void - compute_required_chunks(ChunkPosSet& required_chunks, - ChunkPairVector& temp_neighbor, - std::vector& need_gen_temp_chunks_pos); + void compute_required_chunks(ChunkPosSet& required_chunks, + ChunkPairVector& temp_neighbor); void sync_and_collect_missing_chunks(std::vector&, const ChunkPosSet&); - void - build_neighbor_context_for_new_chunks(ConstChunkMap& new_chunks_neighbor, - ChunkPtrUpdateList& affected_neighbor, - const ChunkPairVector& new_chunks); - void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&, - ConstChunkMap&); + + void submit_new_chunks(); + void poll_finished_chunks(); + void wait_all_chunk_tasks(); public: World(); @@ -118,7 +126,7 @@ public: bool is_solid(const glm::ivec3& block_pos) const; bool can_pass_block(const glm::ivec3& block_pos) const; BlockType get_block_tpye(const glm::ivec3& block_pos) const; - static ChunkPos chunk_pos(int world_x, int world_z); + static ChunkPos get_chunk_pos(int world_x, int world_z); void need_gen(); @@ -138,6 +146,8 @@ public: void start_server_thread(); void stop_gen_thread(); void stop_server_thread(); + void stop_thread_pool(); + void start_thread_pool(); void serever_run(std::stop_token stoken); CaveCarver& cave_carcer(); @@ -154,6 +164,11 @@ public: bool is_tick_running() const; void tick_running(bool run); + int pool_threads() const; + int max_threads() const; + void change_pool_threads(int threads); + int chunk_load_style() const; + void set_chunk_load_style(int id); }; } // namespace Cubed diff --git a/include/Cubed/tools/thread_pool.hpp b/include/Cubed/tools/thread_pool.hpp new file mode 100644 index 0000000..5b53eaa --- /dev/null +++ b/include/Cubed/tools/thread_pool.hpp @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +namespace Cubed { +class ThreadPool { +private: + std::vector m_workers; + std::queue> m_tasks; + std::mutex m_mtx; + std::condition_variable_any m_cv; + std::atomic m_stopping{false}; + std::atomic m_thread_sum{0}; + +public: + ThreadPool(const ThreadPool&) = delete; + ThreadPool(ThreadPool&&) = delete; + ThreadPool& operator=(const ThreadPool&) = delete; + ThreadPool& operator=(ThreadPool&&) = delete; + explicit ThreadPool(size_t thread_sum) : m_thread_sum(thread_sum) { + for (size_t i = 0; i < thread_sum; i++) { + m_workers.emplace_back([this](std::stop_token stoken) { + while (true) { + std::function task; + { + std::unique_lock lock(m_mtx); + m_cv.wait(lock, stoken, + [this, stoken] { return !m_tasks.empty(); }); + if (stoken.stop_requested() && m_tasks.empty()) { + return; + } + task = std::move(m_tasks.front()); + m_tasks.pop(); + } + task(); + } + }); + } + } + ~ThreadPool() { stop(); } + template auto enqueue(F&& f) { + + using R = std::invoke_result_t; + + auto task = + std::make_shared>(std::forward(f)); + auto fut = task->get_future(); + + { + std::lock_guard lock(m_mtx); + if (m_stopping) + throw std::runtime_error("thread pool stopped"); + m_tasks.emplace([task] { (*task)(); }); + } + m_cv.notify_one(); + return fut; + } + void stop() { + m_stopping = true; + for (auto& w : m_workers) { + w.request_stop(); + } + + m_cv.notify_all(); + + for (auto& w : m_workers) { + if (w.joinable()) { + w.join(); + } + } + } + size_t thread_sum() const { return m_thread_sum.load(); } +}; + +template +void parallel_do(ThreadPool& pool, Iter first, Iter last, size_t max_threads, + F&& f) { + max_threads = std::max(1, max_threads); + max_threads = std::min(max_threads, pool.thread_sum()); + std::decay_t fn(std::forward(f)); + size_t length = std::distance(first, last); + if (!length) { + return; + } + + constexpr size_t MIN_PER_THREAD = 25; + size_t num_blocks = + std::min(max_threads, (length + MIN_PER_THREAD - 1) / MIN_PER_THREAD); + num_blocks = std::max(1, num_blocks); + size_t block_size = (length + num_blocks - 1) / num_blocks; + + std::vector> futures; + futures.reserve(num_blocks - 1); + Iter block_start = first; + for (size_t i = 0; i < num_blocks - 1; ++i) { + Iter block_end = block_start; + auto remain = std::distance(block_start, last); + std::advance(block_end, std::min(block_size, remain)); + + futures.emplace_back(pool.enqueue([block_start, block_end, &fn]() { + for (auto it = block_start; it != block_end; ++it) { + fn(*it); + } + })); + + block_start = block_end; + } + for (auto it = block_start; it != last; ++it) { + fn(*it); + } + + for (auto& fut : futures) { + fut.get(); + } +}; + +} // namespace Cubed diff --git a/lsan.supp b/lsan.supp index 1f9b285..c7419ff 100644 --- a/lsan.supp +++ b/lsan.supp @@ -4,4 +4,5 @@ leak:libpangocairo leak:libdecor-gtk.so leak:libgtk-3.so leak:libwayland-client.so -leak:libglfw.so \ No newline at end of file +leak:libglfw.so +leak:libEGL_nvidia.so \ No newline at end of file diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index f765419..549b06f 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -463,6 +463,29 @@ void DevPanel::show_world_tab_item() { if (ImGui::SliderInt("Render Distance", &rendering_distance, 2, 128)) { m_app.world().rendering_distance(rendering_distance); } + ImGui::Text( + "Pool Threads %d Max Support Threads %d Reserved Threads %d", + m_app.world().pool_threads(), m_app.world().max_threads(), + RESERVED_THREADS); + ImGui::SliderInt("Set Pool Threads", &m_threads, 1, + m_app.world().max_threads()); + ImGui::SameLine(); + if (ImGui::Button("Set")) { + m_app.world().change_pool_threads(m_threads); + } + if (m_threads > m_app.world().max_threads() - RESERVED_THREADS) { + ImGui::TextColored( + ImVec4(1.0f, 1.0f, 0.0f, 1.0f), + "Waring: When the threads in the thread pool exceed \n(maximum " + "threads minus reserved threads), \nit may cause stuttering."); + } + + static const char* chunk_load_style[] = {"Random", "Center"}; + m_chunk_style = m_app.world().chunk_load_style(); + if (ImGui::Combo("ChunkLoadStyle", &m_chunk_style, chunk_load_style, + IM_ARRAYSIZE(chunk_load_style))) { + m_app.world().set_chunk_load_style(m_chunk_style); + } if (ImGui::Button("Rebuild World")) { m_app.world().rebuild_world(); } @@ -542,6 +565,8 @@ void DevPanel::show_player_tab_item() { m_player_profile.pos[1], m_player_profile.pos[2]}); } + ImGui::SliderFloat("Fly Y Speed", &m_player->fly_y_speed(), 0.0f, + 100.0f); ImGui::SliderFloat("Acceleration", &m_player->acceleration(), 1.0f, 200.0f); ImGui::SliderFloat("Deceleration", &m_player->deceleration(), 1.0f, diff --git a/src/gameplay/cave_carver.cpp b/src/gameplay/cave_carver.cpp index 2170b2f..ef4a7f9 100644 --- a/src/gameplay/cave_carver.cpp +++ b/src/gameplay/cave_carver.cpp @@ -49,11 +49,13 @@ void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos, void CaveCarver::cleanup_finished_caves() { std::vector finished_keys; + for (const auto& pair : m_paths) { if (pair.second.is_finished()) { finished_keys.push_back(pair.first); } } + for (const auto& key : finished_keys) { m_paths.erase(key); } @@ -61,4 +63,5 @@ void CaveCarver::cleanup_finished_caves() { int CaveCarver::cave_sum() const { return m_paths.size(); } float& CaveCarver::cave_probability() { return m_cave_probability; } +std::shared_mutex& CaveCarver::path_mutex() { return m_path_mutex; } } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 691cd91..58f5758 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -8,8 +8,8 @@ namespace Cubed { -Chunk::Chunk(World& world, ChunkPos chunk_pos) - : m_chunk_pos(chunk_pos), m_world(world) { +Chunk::Chunk(World& world, ChunkPos chunk_pos, bool temp_chunk) + : m_temp_chunk(temp_chunk), m_chunk_pos(chunk_pos), m_world(world) { for (int i = 0; i < VERTEX_DATA_SUM; i++) { m_vertex_data.emplace_back(m_world); } @@ -299,7 +299,7 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) { int world_nz = world_z + DIR[face].z; auto [neighbor_x, neighbor_z] = - World::chunk_pos(world_nx, world_nz); + World::get_chunk_pos(world_nx, world_nz); auto is_culled = [&](const std::optional>& @@ -455,6 +455,42 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z, } } +void Chunk::gen_chunk() { + if (m_gening.exchange(true)) + return; + m_gening = true; + if (m_blocks.size() != 0) { + Logger::warn( + "Request Generator Chunk {} {} ,but the Blocks size is Not 0", + m_chunk_pos.x, m_chunk_pos.z); + } + std::vector neighbor; + for (int i = 0; i < 4; i++) { + neighbor.emplace_back(m_world, m_chunk_pos + CHUNK_DIR[i], true); + } + for (auto& chunk : neighbor) { + chunk.gen_phase_one(); + chunk.gen_phase_three(); + chunk.gen_phase_five(); + chunk.gen_phase_seven(); + } + gen_phase_one(); + gen_phase_three(); + gen_phase_five(); + + OptionalBlockVectorArray neightbor_blocks; + for (int i = 0; i < 4; i++) { + neightbor_blocks[i] = neighbor[i].get_chunk_blocks(); + } + gen_phase_six(neightbor_blocks); + gen_phase_seven(); + for (int i = 0; i < 4; i++) { + neightbor_blocks[i] = neighbor[i].get_chunk_blocks(); + } + gen_vertex_data(neightbor_blocks); +} // Logger::info("Cross Sum {}", m_cross_vertices_sum.load()); +bool Chunk::is_temp_chunk() const { return m_temp_chunk.load(); } + } // namespace Cubed diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index 76f58a1..4af079f 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -723,18 +723,24 @@ void ChunkGenerator::generate_cave() { auto& paths = cave_carver.paths(); const auto& chunk_pos = m_chunk.chunk_pos(); auto& blocks = m_chunk.blocks(); + { + std::shared_lock lock(cave_carver.path_mutex()); + for (auto& [id, path] : paths) { - for (auto& [id, path] : paths) { - - carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void { - int idx = Chunk::index(x, y, z); - if (blocks[idx] == 7) - return; - if (y < WORLD_SIZE_Y - 1 && blocks[Chunk::index(x, y + 1, z)] == 7) - return; - blocks[idx] = 0; - }); - path.clear_chunk(chunk_pos); + carve_worm(path.points(), chunk_pos, + [&](int x, int y, int z) -> void { + int idx = Chunk::index(x, y, z); + if (blocks[idx] == 7) + return; + if (y < WORLD_SIZE_Y - 1 && + blocks[Chunk::index(x, y + 1, z)] == 7) + return; + blocks[idx] = 0; + }); + if (!m_chunk.is_temp_chunk()) { + path.clear_chunk(chunk_pos); + } + } } } @@ -744,29 +750,36 @@ void ChunkGenerator::generate_river() { auto& paths = river_worm.paths(); const auto& chunk_pos = m_chunk.chunk_pos(); auto& blocks = m_chunk.blocks(); - bool is_river = false; - - for (auto& [id, path] : paths) { - if ((m_chunk.biome() == BiomeType::DESERT) || - (m_chunk.biome() == BiomeType::OCEAN)) { - path.clear_chunk(chunk_pos); - continue; + { + std::shared_lock lock(river_worm.paths_mutex()); + for (auto& [id, path] : paths) { + if ((m_chunk.biome() == BiomeType::DESERT) || + (m_chunk.biome() == BiomeType::OCEAN)) { + if (!m_chunk.is_temp_chunk()) { + path.clear_chunk(chunk_pos); + } + continue; + } + carve_worm(path.points(), chunk_pos, + [&](int x, int y, int z) -> void { + int idx = Chunk::index(x, y, z); + if (y > SEA_LEVEL) { + blocks[idx] = 0; + return; + } + is_river = true; + if (blocks[idx] == 0) { + return; + } + blocks[idx] = 7; + }); + if (!m_chunk.is_temp_chunk()) { + path.clear_chunk(chunk_pos); + } } - carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void { - int idx = Chunk::index(x, y, z); - if (y > SEA_LEVEL) { - blocks[idx] = 0; - return; - } - is_river = true; - if (blocks[idx] == 0) { - return; - } - blocks[idx] = 7; - }); - path.clear_chunk(chunk_pos); } + if (is_river) { m_chunk.biome(RIVER); } diff --git a/src/gameplay/player.cpp b/src/gameplay/player.cpp index c785f80..af9debc 100644 --- a/src/gameplay/player.cpp +++ b/src/gameplay/player.cpp @@ -247,7 +247,7 @@ void Player::update_front_vec(float offset_x, float offset_y) { } void Player::check_player_chunk_transition() { - ChunkPos cur_pos = m_world.chunk_pos(m_player_pos.x, m_player_pos.z); + ChunkPos cur_pos = m_world.get_chunk_pos(m_player_pos.x, m_player_pos.z); if (cur_pos != m_player_chunk_pos) { m_world.need_gen(); m_player_chunk_pos = cur_pos; @@ -372,11 +372,11 @@ void Player::update_move(float delta_time) { if (is_fly) { if (m_move_state.up) { - m_y_speed = 7.5f; + m_y_speed = m_fly_y_speed; } if (m_move_state.down) { - m_y_speed = -7.5f; + m_y_speed = -m_fly_y_speed; } if (!m_move_state.down && !m_move_state.up) { @@ -544,6 +544,7 @@ float& Player::max_speed() { return m_max_speed; } float& Player::acceleration() { return m_acceleration; } float& Player::deceleration() { return m_deceleration; } float& Player::g() { return m_g; } +float& Player::fly_y_speed() { return m_fly_y_speed; } unsigned Player::place_block() const { return m_place_block; }; Gait& Player::gait() { return m_gait; } GameMode& Player::game_mode() { return m_game_mode; } diff --git a/src/gameplay/river_worm.cpp b/src/gameplay/river_worm.cpp index 41c4ca4..1e5ef93 100644 --- a/src/gameplay/river_worm.cpp +++ b/src/gameplay/river_worm.cpp @@ -4,7 +4,7 @@ namespace Cubed { RiverWorm::RiverWorm() {} - +RiverWorm::~RiverWorm() {} RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; } void RiverWorm::init(unsigned world_seed) { @@ -46,11 +46,13 @@ void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos, void RiverWorm::cleanup_finished_rivers() { std::vector finished_keys; + for (const auto& pair : m_paths) { if (pair.second.is_finished()) { finished_keys.push_back(pair.first); } } + for (const auto& key : finished_keys) { m_paths.erase(key); } @@ -58,4 +60,6 @@ void RiverWorm::cleanup_finished_rivers() { int RiverWorm::river_sum() const { return m_paths.size(); } float& RiverWorm::river_probability() { return m_probability; } +std::shared_mutex& RiverWorm::paths_mutex() { return m_paths_mutex; } + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index b8d145a..b738fa9 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -5,10 +5,11 @@ #include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_hash.hpp" -#include #include #include +#include using namespace std::chrono; +using namespace std::chrono_literals; namespace Cubed { @@ -22,6 +23,9 @@ World::World() {} World::~World() { stop_gen_thread(); stop_server_thread(); + wait_all_chunk_tasks(); + stop_thread_pool(); + m_chunks.clear(); { std::lock_guard lk(m_delete_vbo_mutex); @@ -39,6 +43,12 @@ World::~World() { } } +void World::wait_all_chunk_tasks() { + for (auto& [pos, task] : new_chunks) { + task.future.get(); + } +} + bool World::can_move(const AABB& player_box) const { return true; } const std::optional& @@ -77,10 +87,10 @@ void World::init_world() { m_cave_carcer.init(ChunkGenerator::seed()); m_river_worm.init(ChunkGenerator::seed()); m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4); + start_thread_pool(); + auto t1 = std::chrono::system_clock::now(); - Logger::info("Max Support Thread is {}", - std::thread::hardware_concurrency()); // init players m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer")); @@ -102,210 +112,7 @@ void World::init_chunks() { } } -/* -void World::init_chunks() { - - int dis_x = PRE_LOAD_DISTANCE; - int dis_z = PRE_LOAD_DISTANCE; - for (int x = 0; x < dis_x; x++) { - for (int z = 0; z < dis_z; z++) { - int nx = x - dis_x / 2; - int nz = z - dis_z / 2; - ChunkPos pos{nx, nz}; - auto it = m_chunks.find(pos); - if (it == m_chunks.end()) { - m_chunks.emplace(pos, Chunk(*this, pos)); - } - } - } - ChunkHashMap temp_neighbor; - for (int x = 0; x < dis_x + 2; x++) { - for (int z = 0; z < dis_z + 2; z++) { - int nx = x - (dis_x + 2) / 2; - int nz = z - (dis_z + 2) / 2; - ChunkPos pos{nx, nz}; - auto it = m_chunks.find(pos); - if (it == m_chunks.end()) { - auto it = temp_neighbor.find(pos); - if (it == temp_neighbor.end()) { - temp_neighbor.emplace(pos, Chunk(*this, pos)); - } - } - } - } - for (auto& [pos, chunk] : m_chunks) { - chunk.gen_phase_one(); - m_cave_carcer.try_to_add_path(pos, chunk.seed()); - } - for (auto& [pos, chunk] : temp_neighbor) { - chunk.gen_phase_one(); - m_cave_carcer.try_to_add_path(pos, chunk.seed()); - } - - std::array neighbor_chunks; - for (auto& [pos, chunks] : m_chunks) { - for (int i = 0; i < 8; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = m_chunks.find(neighbor_pos); - if (it == m_chunks.end()) { - auto it = temp_neighbor.find(neighbor_pos); - if (it == temp_neighbor.end()) { - neighbor_chunks[i] = nullptr; - ASSERT_MSG(false, "Neighbor Chunk is nullptr"); - } else { - neighbor_chunks[i] = &it->second; - } - continue; - } - neighbor_chunks[i] = &it->second; - } - chunks.gen_phase_two(neighbor_chunks); - } - for (auto& [pos, chunks] : temp_neighbor) { - for (int i = 0; i < 8; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = m_chunks.find(neighbor_pos); - if (it == m_chunks.end()) { - auto it = temp_neighbor.find(neighbor_pos); - if (it == temp_neighbor.end()) { - neighbor_chunks[i] = nullptr; - } else { - neighbor_chunks[i] = &it->second; - } - continue; - } - neighbor_chunks[i] = &it->second; - } - chunks.gen_phase_two(neighbor_chunks); - } - for (auto& [pos, chunks] : m_chunks) { - chunks.gen_phase_three(); - } - for (auto& [pos, chunks] : temp_neighbor) { - chunks.gen_phase_three(); - } - - for (int i = 0; i < 4; i++) { - for (auto& [pos, chunks] : temp_neighbor) { - std::array, 8> - neighbor_chunk_heightmap; - std::array neighbor_biome; - for (int i = 0; i < 4; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = m_chunks.find(neighbor_pos); - if (it == m_chunks.end()) { - auto it = temp_neighbor.find(neighbor_pos); - if (it == temp_neighbor.end()) { - neighbor_chunk_heightmap[i] = std::nullopt; - neighbor_biome[i] = BiomeType::NONE; - } else { - neighbor_chunk_heightmap[i] = - it->second.get_heightmap(); - neighbor_biome[i] = it->second.biome(); - } - - continue; - } - neighbor_chunk_heightmap[i] = it->second.get_heightmap(); - neighbor_biome[i] = it->second.biome(); - } - chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome); - } - for (auto& [pos, chunks] : m_chunks) { - std::array, 8> - neighbor_chunk_heightmap; - std::array neighbor_biome; - for (int i = 0; i < 8; i++) { - - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = m_chunks.find(neighbor_pos); - if (it == m_chunks.end()) { - auto it = temp_neighbor.find(neighbor_pos); - if (it == temp_neighbor.end()) { - neighbor_chunk_heightmap[i] = std::nullopt; - neighbor_biome[i] = BiomeType::NONE; - ASSERT_MSG(false, "Neighbor Chunk is nullptr"); - } else { - neighbor_chunk_heightmap[i] = - it->second.get_heightmap(); - neighbor_biome[i] = it->second.biome(); - } - - continue; - } - neighbor_chunk_heightmap[i] = it->second.get_heightmap(); - neighbor_biome[i] = it->second.biome(); - } - chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome); - } - } - - for (auto& [pos, chunks] : m_chunks) { - chunks.gen_phase_five(); - } - for (auto& [pos, chunks] : temp_neighbor) { - chunks.gen_phase_five(); - } - std::array>, 4> neighbor_block; - for (auto& [pos, chunks] : m_chunks) { - for (int i = 0; i < 4; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = m_chunks.find(neighbor_pos); - if (it == m_chunks.end()) { - auto it = temp_neighbor.find(neighbor_pos); - if (it == temp_neighbor.end()) { - neighbor_block[i] = std::nullopt; - ASSERT_MSG(false, "Neighbor Chunk is nullptr"); - } else { - neighbor_block[i] = it->second.get_chunk_blocks(); - } - - continue; - } - neighbor_block[i] = it->second.get_chunk_blocks(); - } - chunks.gen_phase_six(neighbor_block); - } - for (auto& [pos, chunks] : m_chunks) { - chunks.gen_phase_seven(); - } - - std::atomic sync{0}; - sync.store(1, std::memory_order_release); - sync.load(std::memory_order_acquire); - - m_cave_carcer.cleanup_finished_caves(); - - std::vector pending_gen_data; - pending_gen_data.reserve(m_chunks.size()); - for (auto& [pos, chunk] : m_chunks) { - ChunkRenderData data; - data.chunk = &chunk; - for (int i = 0; i < 4; i++) { - auto it = m_chunks.find(pos + CHUNK_DIR[i]); - if (it != m_chunks.end()) { - data.neighbor_block[i] = &(it->second.get_chunk_blocks()); - } else { - data.neighbor_block[i] = nullptr; - } - } - pending_gen_data.emplace_back(std::move(data)); - } - std::for_each(std::execution::par, pending_gen_data.begin(), - pending_gen_data.end(), [](ChunkRenderData& data) { - if (!data.chunk) { - return; - } - data.chunk->gen_vertex_data(data.neighbor_block); - }); - for (auto& chunk_map : m_chunks) { - auto& [chunk_pos, chunk] = chunk_map; - chunk.upload_to_gpu(); - } -} -*/ - -ChunkPos World::chunk_pos(int world_x, int world_z) { +ChunkPos World::get_chunk_pos(int world_x, int world_z) { int chunk_x, chunk_z; if (world_x < 0) { chunk_x = (world_x + 1) / CHUNK_SIZE - 1; @@ -325,13 +132,14 @@ ChunkPos World::chunk_pos(int world_x, int world_z) { #pragma region ChunkGenerate void World::gen_chunks_internal() { + // Logger::info("gen_chunks_internal"); m_chunk_gen_fraction = 0.0f; m_chunk_gen_finished = false; + ChunkPosSet required_chunks; ChunkPairVector temp_neighbor; - std::vector need_gen_temp_chunks_pos; - compute_required_chunks(required_chunks, temp_neighbor, - need_gen_temp_chunks_pos); + + compute_required_chunks(required_chunks, temp_neighbor); ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!"); @@ -348,256 +156,36 @@ void World::gen_chunks_internal() { } m_chunk_gen_fraction = 0.1f; - - ChunkPairVector new_chunks; - ChunkHashMap new_temp_chunks; for (auto& pos : need_gen_chunks_pos) { - new_chunks.push_back({pos, Chunk(*this, pos)}); + new_chunks.emplace(pos, Chunk(*this, pos)); } - for (auto& pos : need_gen_temp_chunks_pos) { - new_temp_chunks.emplace(pos, Chunk(*this, pos)); - } - ConstChunkMap new_chunks_neighbor; - // affected neighbor - ChunkPtrUpdateList affected_neighbor; - - build_neighbor_context_for_new_chunks(new_chunks_neighbor, - affected_neighbor, new_chunks); - - // build new chunk, but the neighbor in m_chunks also need to re-build - - std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(), - [this](std::pair& new_chunk) { - auto& [pos, chunk] = new_chunk; - chunk.gen_phase_one(); - m_cave_carcer.try_to_add_path(pos, chunk.seed()); - m_river_worm.try_to_add_path(pos, chunk.seed()); - }); - - std::for_each(new_temp_chunks.begin(), new_temp_chunks.end(), - [](std::pair& new_chunk) { - auto& [pos, chunk] = new_chunk; - chunk.gen_phase_one(); - }); - // precompute path to ensure the continuity of the path - std::for_each(std::execution::par, temp_neighbor.begin(), - temp_neighbor.end(), - [this](std::pair& new_chunk) { - auto& [pos, chunk] = new_chunk; - chunk.gen_phase_one(); - m_cave_carcer.try_to_add_path(pos, chunk.seed()); - m_river_worm.try_to_add_path(pos, chunk.seed()); - }); - - m_chunk_gen_fraction = 0.2f; - - /* - std::array neighbor_chunks; - for (auto& [pos, chunks] : new_chunks) { - for (int i = 0; i < 8; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = new_chunks_neighbor.find(neighbor_pos); - if (it == new_chunks_neighbor.end()) { - neighbor_chunks[i] = nullptr; - // ASSERT_MSG(false, "Cant Find Neighbot"); - continue; - } - neighbor_chunks[i] = it->second; - } - chunks.gen_phase_two(neighbor_chunks); - } - */ - - /* - for (auto& [pos, chunks] : temp_neighbor) { - for (int i = 0; i < 8; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = new_chunks_neighbor.find(neighbor_pos); - if (it == new_chunks_neighbor.end()) { - neighbor_chunks[i] = nullptr; - continue; - } - neighbor_chunks[i] = it->second; - } - chunks.gen_phase_two(neighbor_chunks); - } - */ - - m_chunk_gen_fraction = 0.3f; - - std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(), - [](std::pair& pair) { - auto& [pos, chunks] = pair; - chunks.gen_phase_three(); - }); - - for (auto& [pos, chunk] : new_temp_chunks) { - chunk.gen_phase_three(); - } - // for (auto& [pos, chunks] : temp_neighbor) { - // chunks.gen_phase_three(); - // } - - /* - for (int i = 0; i < 4; i++) { - for (auto& [pos, chunks] : temp_neighbor) { - std::array, 8> - neighbor_chunk_heightmap; - // std::lock_guard lk(m_chunks_mutex); - std::array neighbor_biome; - for (int i = 0; i < 8; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = new_chunks_neighbor.find(neighbor_pos); - if (it == new_chunks_neighbor.end()) { - neighbor_chunk_heightmap[i] = std::nullopt; - neighbor_biome[i] = BiomeType::NONE; - continue; - } - neighbor_chunk_heightmap[i] = it->second->get_heightmap(); - neighbor_biome[i] = it->second->biome(); - } - - chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome); - } - for (auto& [pos, chunks] : new_chunks) { - std::array, 8> - neighbor_chunk_heightmap; - // std::lock_guard lk(m_chunks_mutex); - std::array neighbor_biome; - for (int i = 0; i < 8; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = new_chunks_neighbor.find(neighbor_pos); - if (it == new_chunks_neighbor.end()) { - neighbor_chunk_heightmap[i] = std::nullopt; - neighbor_biome[i] = BiomeType::NONE; - ASSERT_MSG(false, "Cant Find Neighbot"); - continue; - } - neighbor_chunk_heightmap[i] = it->second->get_heightmap(); - neighbor_biome[i] = it->second->biome(); - } - - chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome); - } - } - */ - m_chunk_gen_fraction = 0.4f; - - for (auto& [pos, chunks] : new_chunks) { - chunks.gen_phase_five(); - } - m_chunk_gen_fraction = 0.45f; - for (auto& [pos, chunk] : new_temp_chunks) { - chunk.gen_phase_five(); - } - m_chunk_gen_fraction = 0.5f; - /* - for (auto& [pos, chunks] : temp_neighbor) { - chunks.gen_phase_five(); - } - */ - - std::vector> - new_chunks_surface_blend_data(new_chunks.size()); - for (size_t idx = 0; idx < new_chunks.size(); idx++) { - auto& [pos, chunk] = new_chunks[idx]; - new_chunks_surface_blend_data[idx].first = &chunk; - { - // std::lock_guard lk(m_chunks_mutex); - for (int i = 0; i < 4; i++) { - auto neighbor_pos = pos + CHUNK_DIR[i]; - auto it = new_chunks_neighbor.find(neighbor_pos); - if (it == new_chunks_neighbor.end()) { - auto it = new_temp_chunks.find(neighbor_pos); - if (it == new_temp_chunks.end()) { - new_chunks_surface_blend_data[idx].second[i] = - std::nullopt; - Logger::warn( - "Can't find neighbor for chunk surface blend"); - continue; - } - new_chunks_surface_blend_data[idx].second[i] = - it->second.get_chunk_blocks(); - continue; - } - new_chunks_surface_blend_data[idx].second[i] = - it->second->get_chunk_blocks(); - } + auto t1 = system_clock::now(); + { + std::scoped_lock lock{m_cave_carcer.path_mutex(), + m_river_worm.paths_mutex()}; + auto pool_ptr = m_gen_thread_pool.load(); + if (!pool_ptr) { + return; } + parallel_do(*pool_ptr, temp_neighbor.begin(), temp_neighbor.end(), + pool_ptr->thread_sum(), + [this](std::pair& new_chunk) { + auto& [pos, chunk] = new_chunk; + chunk.gen_phase_one(); + m_cave_carcer.try_to_add_path(pos, chunk.seed()); + m_river_worm.try_to_add_path(pos, chunk.seed()); + }); + m_cave_carcer.cleanup_finished_caves(); + m_river_worm.cleanup_finished_rivers(); } - std::for_each( - std::execution::par, new_chunks_surface_blend_data.begin(), - new_chunks_surface_blend_data.end(), - [](std::pair& new_chunk_data) { - auto& [chunk, neighbor_data] = new_chunk_data; - chunk->gen_phase_six(neighbor_data); - }); - - m_chunk_gen_fraction = 0.55f; - std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(), - [](std::pair& new_chunk) { - auto& [pos, chunk] = new_chunk; - chunk.gen_phase_seven(); - }); - - m_chunk_gen_fraction = 0.6f; - - std::vector> - new_chunk_vertices_data(new_chunks.size()); - for (size_t idx = 0; idx < new_chunks.size(); idx++) { - auto& [pos, chunk] = new_chunks[idx]; - new_chunk_vertices_data[idx].first = &chunk; - for (int i = 0; i < 4; i++) { - auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); - if (it != new_chunks_neighbor.end()) { - new_chunk_vertices_data[idx].second[i] = - (it->second->get_chunk_blocks()); - } else { - new_chunk_vertices_data[idx].second[i] = std::nullopt; - } - } - } - - std::for_each( - std::execution::par, new_chunk_vertices_data.begin(), - new_chunk_vertices_data.end(), - [](std::pair& new_chunk_data) { - auto& [chunk, neighbor_data] = new_chunk_data; - chunk->gen_vertex_data(neighbor_data); - }); - - m_chunk_gen_fraction = 0.7f; - - build_neighbor_context_for_affected_neighbors(affected_neighbor, - new_chunks_neighbor); - - m_chunk_gen_fraction = 0.8f; - OptionalBlockVectorArray neighbor_block; - for (auto& [pos, chunk] : affected_neighbor) { - for (int i = 0; i < 4; i++) { - auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); - if (it != new_chunks_neighbor.end()) { - neighbor_block[i] = (it->second->get_chunk_blocks()); - } else { - neighbor_block[i] = std::nullopt; - } - } - chunk->gen_vertex_data(neighbor_block); - chunk->need_upload(); - } - + auto t2 = system_clock::now(); + Logger::info("Temp Neighbor Add Path Consum {}", + duration_cast(t2 - t1)); m_chunk_gen_fraction = 0.9f; - { - std::lock_guard lk(m_new_chunk_queue_mutex); - for (auto& x : new_chunks) { - m_new_chunk_queue.emplace_back(std::move(x)); - } - } - m_cave_carcer.cleanup_finished_caves(); - m_river_worm.cleanup_finished_rivers(); m_chunk_gen_fraction = 1.0f; + submit_new_chunks(); m_chunk_gen_finished = true; } @@ -606,15 +194,14 @@ void World::sync_player_pos(glm::vec3& player_pos) { player_pos = m_gen_player_pos; } -void World::compute_required_chunks( - ChunkPosSet& required_chunks, ChunkPairVector& temp_neighbor, - std::vector& need_gen_temp_chunks_pos) { +void World::compute_required_chunks(ChunkPosSet& required_chunks, + ChunkPairVector& temp_neighbor) { glm::vec3 player_pos; sync_player_pos(player_pos); int x = std::floor(player_pos.x); int z = std::floor(player_pos.z); - auto [chunk_x, chunk_z] = chunk_pos(x, z); + auto [chunk_x, chunk_z] = get_chunk_pos(x, z); int radius = m_rendering_distance; int r2 = radius * radius; required_chunks.reserve(radius * radius); @@ -626,20 +213,6 @@ void World::compute_required_chunks( } } } - int new_radius = radius + 1; - int new_r2 = new_radius * new_radius; - for (int dx = -new_radius; dx <= new_radius; ++dx) { - for (int dz = -new_radius; dz <= new_radius; ++dz) { - if (dx * dx + dz * dz <= new_r2) { - int nx = chunk_x + dx; - int nz = chunk_z + dz; - auto it = required_chunks.find({nx, nz}); - if (it == required_chunks.end()) { - need_gen_temp_chunks_pos.push_back({nx, nz}); - } - } - } - } int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max()); radius = max_path_len / 2; r2 = radius * radius; @@ -647,10 +220,6 @@ void World::compute_required_chunks( for (int dz = -radius; dz <= radius; ++dz) { if (dx * dx + dz * dz <= r2) { ChunkPos pos{chunk_x + dx, chunk_z + dz}; - auto it = required_chunks.find(pos); - if (it != required_chunks.end()) { - continue; - } temp_neighbor.emplace_back(pos, Chunk(*this, pos)); } } @@ -677,37 +246,71 @@ void World::sync_and_collect_missing_chunks( } } -void World::build_neighbor_context_for_new_chunks( - ConstChunkMap& new_chunks_neighbor, ChunkPtrUpdateList& affected_neighbor, - const ChunkPairVector& new_chunks) { - { - std::lock_guard lk(m_chunks_mutex); - for (auto& [pos, chunk] : new_chunks) { - for (auto& dir : CHUNK_DIR) { - auto it = m_chunks.find(pos + dir); - if (it != m_chunks.end()) { - new_chunks_neighbor.insert({it->first, &(it->second)}); - affected_neighbor.push_back({it->first, &(it->second)}); - } +void World::submit_new_chunks() { + using enum ChunkLoadStyle; + std::lock_guard lock(m_new_chunk_mutex); + auto pool_ptr = m_gen_thread_pool.load(); + if (!pool_ptr) { + return; + } + switch (m_chunk_load_style) { + case RANDOM: + for (auto& [pos, task] : new_chunks) { + if (!task.future.valid()) { + task.future = + pool_ptr->enqueue([&task]() { task.chunk.gen_chunk(); }); + } + } + break; + case CENTER: { + std::vector> tasks; + for (auto& [pos, task] : new_chunks) { + if (!task.future.valid()) { + tasks.emplace_back(pos, &task); + } + } + glm::vec3 player_pos; + sync_player_pos(player_pos); + auto dist2 = [player_pos](ChunkPos chunk_pos) { + ChunkPos player_chunk_pos = + get_chunk_pos(player_pos.x, player_pos.z); + float dx = player_chunk_pos.x - chunk_pos.x; + float dz = player_chunk_pos.z - chunk_pos.z; + return dx * dx + dz * dz; + }; + + std::sort(tasks.begin(), tasks.end(), + [&dist2](const auto& a, const auto& b) { + return dist2(a.first) < dist2(b.first); + }); + for (auto& [pos, task] : tasks) { + if (!task->future.valid()) { + task->future = + pool_ptr->enqueue([task]() { task->chunk.gen_chunk(); }); } } } - for (auto& [pos, chunk] : new_chunks) { - new_chunks_neighbor.insert({pos, &chunk}); } } -void World::build_neighbor_context_for_affected_neighbors( - ChunkPtrUpdateList& affected_neighbor, ConstChunkMap& new_chunks_neighbor) { - std::lock_guard lk(m_chunks_mutex); - for (auto& [pos, chunk] : affected_neighbor) { - for (auto& dir : CHUNK_DIR) { - auto it = m_chunks.find(pos + dir); - if (it != m_chunks.end()) { - new_chunks_neighbor.insert({it->first, &(it->second)}); +void World::poll_finished_chunks() { + m_new_finished_chunk.clear(); + std::lock_guard lock(m_new_chunk_mutex); + std::erase_if( + new_chunks, [&](std::pair& pair) { + auto& pending = pair.second; + if (!pending.future.valid()) { + return false; } - } - } + if (pending.future.wait_for(0ms) != std::future_status::ready) { + return false; + } + pending.future.get(); + + m_new_finished_chunk.emplace_back(pair.first, + std::move(pending.chunk)); + return true; + }); } #pragma endregion @@ -754,6 +357,24 @@ void World::stop_server_thread() { } } +void World::stop_thread_pool() { + auto pool_ptr = m_gen_thread_pool.load(); + if (pool_ptr) { + pool_ptr->stop(); + } + m_gen_thread_pool.store(nullptr); + Logger::info("Thread Pool Stopped"); +} + +void World::start_thread_pool() { + int max_thread = std::thread::hardware_concurrency(); + if (m_pool_threads == 0) { + change_pool_threads(max_thread - RESERVED_THREADS); + } else { + change_pool_threads(m_pool_threads); + } +} + void World::serever_run(std::stop_token stoken) { Logger::info("Server Thread Started!"); while (!stoken.stop_requested()) { @@ -767,10 +388,12 @@ void World::serever_run(std::stop_token stoken) { } void World::need_gen() { + if (!m_could_gen) { Logger::warn("It is generating or consuming new chunks"); return; } + m_could_gen = false; { std::lock_guard lk(m_gen_player_pos_mutex); @@ -778,11 +401,12 @@ void World::need_gen() { } m_need_gen_chunk = true; + m_gen_cv.notify_one(); } int World::get_block(const glm::ivec3& block_pos) const { - auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); + auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z); std::lock_guard lk(m_chunks_mutex); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); @@ -800,7 +424,7 @@ int World::get_block(const glm::ivec3& block_pos) const { } bool World::is_solid(const glm::ivec3& block_pos) const { - auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); + auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z); std::lock_guard lk(m_chunks_mutex); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); @@ -822,7 +446,7 @@ bool World::is_solid(const glm::ivec3& block_pos) const { } bool World::can_pass_block(const glm::ivec3& block_pos) const { - auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); + auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z); std::lock_guard lk(m_chunks_mutex); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); @@ -840,21 +464,21 @@ bool World::can_pass_block(const glm::ivec3& block_pos) const { } BlockType World::get_block_tpye(const glm::ivec3& block_pos) const { - auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); + auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z); std::lock_guard lk(m_chunks_mutex); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); if (it == m_chunks.end()) { - Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, - block_pos.z); + // Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, + // block_pos.z); return 0; } const auto& chunk_blocks = it->second.get_chunk_blocks(); auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || z >= CHUNK_SIZE) { - Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, - block_pos.z); + // Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, + // block_pos.z); return 0; } return chunk_blocks[Chunk::index(x, y, z)]; @@ -867,7 +491,7 @@ void World::set_block(const glm::ivec3& block_pos, unsigned id) { world_y = block_pos.y; world_z = block_pos.z; - auto [chunk_x, chunk_z] = chunk_pos(world_x, world_z); + auto [chunk_x, chunk_z] = get_chunk_pos(world_x, world_z); std::lock_guard lk(m_chunks_mutex); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); @@ -890,7 +514,7 @@ void World::set_block(const glm::ivec3& block_pos, unsigned id) { for (const auto& dir : NEIGHBOR_DIRS) { glm::ivec3 neighbor = block_pos + dir; - auto [cx, cz] = chunk_pos(neighbor.x, neighbor.z); + auto [cx, cz] = get_chunk_pos(neighbor.x, neighbor.z); auto it = m_chunks.find({cx, cz}); if (it != m_chunks.end()) { it->second.mark_dirty(); @@ -918,16 +542,9 @@ void World::update(float delta_time) { m_pending_delete_vao.clear(); } - { - std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex); - m_new_chunk.clear(); - for (auto& x : m_new_chunk_queue) { - m_new_chunk.emplace_back(std::move(x)); - } - m_new_chunk_queue.clear(); - } + poll_finished_chunks(); - for (auto& x : m_new_chunk) { + for (auto& x : m_new_finished_chunk) { x.second.upload_to_gpu(); } @@ -936,7 +553,7 @@ void World::update(float delta_time) { std::lock_guard lk(m_chunks_mutex); bool consumed = false; - for (auto& x : m_new_chunk) { + for (auto& x : m_new_finished_chunk) { m_chunks.insert_or_assign(x.first, std::move(x.second)); consumed = true; } @@ -1008,35 +625,23 @@ void World::rebuild_world() { } m_is_rebuilding = true; stop_gen_thread(); + stop_thread_pool(); m_cave_carcer.reload(ChunkGenerator::seed()); m_river_worm.reload(ChunkGenerator::seed()); { - std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex); + std::scoped_lock lk(m_chunks_mutex); m_chunks.clear(); - m_new_chunk_queue.clear(); + m_new_finished_chunk.clear(); } m_could_gen = true; ChunkGenerator::reload(); + start_thread_pool(); start_gen_thread(); need_gen(); m_is_rebuilding = false; } -float World::chunk_gen_fraction() const { return m_chunk_gen_fraction.load(); } - -int World::rendering_distance() const { return m_rendering_distance.load(); } - -void World::rendering_distance(int rendering_distance) { - m_rendering_distance = rendering_distance; -} - -CaveCarver& World::cave_carcer() { return m_cave_carcer; } -RiverWorm& World::river_worm() { return m_river_worm; } -std::vector& World::planes() { return m_planes; } -std::vector& World::render_snapshots() { - return m_render_snapshots; -}; /* glm::vec3 World::sunlight_dir() const { float t = static_cast(m_day_tick) / DAY_TIME; @@ -1071,6 +676,21 @@ glm::vec3 World::sunlight_dir() const { return glm::normalize(-dir); } +float World::chunk_gen_fraction() const { return m_chunk_gen_fraction.load(); } + +int World::rendering_distance() const { return m_rendering_distance.load(); } + +void World::rendering_distance(int rendering_distance) { + m_rendering_distance = rendering_distance; +} + +CaveCarver& World::cave_carcer() { return m_cave_carcer; } +RiverWorm& World::river_worm() { return m_river_worm; } +std::vector& World::planes() { return m_planes; } +std::vector& World::render_snapshots() { + return m_render_snapshots; +}; + TickType World::game_tick() const { return m_game_ticks.load(); } TickType World::day_tick() const { return m_day_tick.load(); } void World::day_tick(TickType tick) { @@ -1082,4 +702,34 @@ void World::per_tick_time(int ms) { m_per_tick_time = ms; } bool World::is_tick_running() const { return m_tick_running.load(); } void World::tick_running(bool run) { m_tick_running = run; } +int World::pool_threads() const { return m_pool_threads.load(); } +int World::max_threads() const { return m_max_threads.load(); } +void World::change_pool_threads(int threads) { + m_max_threads = std::thread::hardware_concurrency(); + if (m_max_threads < 1) { + Logger::warn("Can't Get Max Support Threads, Set Max Threads to 4"); + m_max_threads = 4; + } + int used_thread = std::clamp(threads, 1, m_max_threads.load()); + Logger::info("Create New Thread Pool Use {} Threads", used_thread); + m_gen_thread_pool.store(std::make_shared(used_thread)); + m_pool_threads = used_thread; +} +int World::chunk_load_style() const { + return std::to_underlying(m_chunk_load_style.load()); +} +void World::set_chunk_load_style(int id) { + using enum ChunkLoadStyle; + + switch (id) { + case std::to_underlying(RANDOM): + m_chunk_load_style = RANDOM; + return; + case std::to_underlying(CENTER): + m_chunk_load_style = CENTER; + return; + } + Logger::error("Can,t Find Chunk Load Style Id {}, Nothing Will Do", id); +} + } // namespace Cubed \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp index b10d611..a528ec7 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -419,7 +419,7 @@ void Renderer::render_ui() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - shader.set_loc("mv_matrix", m_ui_m_matrix); + shader.set_loc("m_matrix", m_ui_m_matrix); shader.set_loc("proj_matrix", m_ui_proj); glBindVertexArray(m_vao[3]); @@ -719,7 +719,6 @@ void Renderer::render_world() { normal_block_shader.set_loc("sunlightDir", light_dir_view); normal_block_shader.set_loc("shadowMode", m_shadow_mode); normal_block_shader.set_loc("shader_on", m_shader_on); - normal_block_shader.set_loc("texelsPerUnit", texels_per_unit); normal_block_shader.set_loc("lightSizeUV", static_cast(m_light_size_uv)); normal_block_shader.set_loc("minRadius", m_min_radius); @@ -728,6 +727,8 @@ void Renderer::render_world() { normal_block_shader.set_loc("specularStrength", m_specular_strength); normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos()); normal_block_shader.set_loc("flipY", m_flip_y); + normal_block_shader.set_loc("renderDistance", m_world.rendering_distance()); + normal_block_shader.set_loc("skyColor", m_sky_uniform.sky_top); m_mvp_mat = m_p_mat * m_mv_mat; auto& m_planes = m_world.planes(); @@ -818,7 +819,6 @@ void Renderer::render_world() { accum_shader.set_loc("mv_matrix", m_mv_mat); accum_shader.set_loc("proj_matrix", m_p_mat); accum_shader.set_loc("norm_matrix", m_norm_mat); - accum_shader.set_loc("lightSpaceMatrix", light_space_matrix); accum_shader.set_loc("ambientStrength", m_ambient_strength); accum_shader.set_loc("sunlightColor", m_parallel_light.directional_light_color); @@ -827,14 +827,13 @@ void Renderer::render_world() { accum_shader.set_loc("sunlightDir", light_dir_view); accum_shader.set_loc("shader_on", m_shader_on); accum_shader.set_loc("specularStrength", m_specular_strength); - accum_shader.set_loc("cameraPos", m_camera.get_camera_pos()); }; auto& accum_shader = get_shader("accum"); accum_shader.use(); set_accum_loc(accum_shader); - + accum_shader.set_loc("cameraPos", m_camera.get_camera_pos()); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array()); for (const auto& snapshot : m_render_snapshots) {