mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
perf(server): parallelize entity update loop
Use parallel_do to process entities concurrently via the compute pool. Switch get_all_session to a thread-safe tbb::concurrent_vector and make ChunkEntity's ref_count atomic to avoid data races. Also remove the debug pig spawn from world init.
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
#include <entt/entt.hpp>
|
#include <entt/entt.hpp>
|
||||||
#include <tbb/concurrent_hash_map.h>
|
#include <tbb/concurrent_hash_map.h>
|
||||||
#include <tbb/concurrent_queue.h>
|
#include <tbb/concurrent_queue.h>
|
||||||
|
#include <tbb/concurrent_vector.h>
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
class ServerWorld;
|
class ServerWorld;
|
||||||
class Session;
|
class Session;
|
||||||
@@ -46,8 +47,9 @@ private:
|
|||||||
void send_all_entities(std::shared_ptr<Session>& session);
|
void send_all_entities(std::shared_ptr<Session>& session);
|
||||||
void update_ai(entt::entity e);
|
void update_ai(entt::entity e);
|
||||||
void update_move(entt::entity e);
|
void update_move(entt::entity e);
|
||||||
void update_send(entt::entity e,
|
void
|
||||||
std::span<std::shared_ptr<Session>> sessions);
|
update_send(entt::entity e,
|
||||||
|
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions);
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
EntityID create_entity_in_factory(Args&&... args) {
|
EntityID create_entity_in_factory(Args&&... args) {
|
||||||
auto entity = m_registry.create();
|
auto entity = m_registry.create();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <tbb/concurrent_hash_map.h>
|
#include <tbb/concurrent_hash_map.h>
|
||||||
#include <tbb/concurrent_queue.h>
|
#include <tbb/concurrent_queue.h>
|
||||||
#include <tbb/concurrent_unordered_map.h>
|
#include <tbb/concurrent_unordered_map.h>
|
||||||
|
#include <tbb/concurrent_vector.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -92,7 +93,7 @@ public:
|
|||||||
|
|
||||||
int chunk_size() const;
|
int chunk_size() const;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Session>> get_all_session() const;
|
tbb::concurrent_vector<std::shared_ptr<Session>> get_all_session() const;
|
||||||
|
|
||||||
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
|
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
|
||||||
ServerEntityManager& entity_manager();
|
ServerEntityManager& entity_manager();
|
||||||
@@ -114,7 +115,27 @@ private:
|
|||||||
struct ChunkEntity {
|
struct ChunkEntity {
|
||||||
ChunkState state;
|
ChunkState state;
|
||||||
std::shared_ptr<ServerChunk> chunk;
|
std::shared_ptr<ServerChunk> chunk;
|
||||||
uint32_t ref_count = 0;
|
std::atomic<uint32_t> ref_count = 0;
|
||||||
|
ChunkEntity() = default;
|
||||||
|
ChunkEntity(ChunkState s, std::shared_ptr<ServerChunk> c = {})
|
||||||
|
: state(s), chunk(std::move(c)) {}
|
||||||
|
|
||||||
|
ChunkEntity& operator=(ChunkEntity&& o) noexcept {
|
||||||
|
if (this == &o) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = std::exchange(o.state, ServerWorld::ChunkState::NONE);
|
||||||
|
chunk = std::move(o.chunk);
|
||||||
|
ref_count = o.ref_count.exchange(0);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkEntity(ChunkEntity&& o) noexcept
|
||||||
|
: state(std::exchange(o.state, ServerWorld::ChunkState::NONE)),
|
||||||
|
chunk(std::move(o.chunk)), ref_count(o.ref_count.exchange(0)) {}
|
||||||
|
ChunkEntity(const ChunkEntity&) = delete;
|
||||||
|
ChunkEntity& operator=(const ChunkEntity&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ChunkLoadStyle { RANDOM, CENTER };
|
enum class ChunkLoadStyle { RANDOM, CENTER };
|
||||||
|
|||||||
@@ -33,18 +33,31 @@ void ServerEntityManager::update() {
|
|||||||
handle_task();
|
handle_task();
|
||||||
|
|
||||||
auto view = m_registry.view<BaseServerCreature>();
|
auto view = m_registry.view<BaseServerCreature>();
|
||||||
|
|
||||||
|
auto pool = m_world.get_compute_pool();
|
||||||
|
if (!pool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto sessions = m_world.get_all_session();
|
auto sessions = m_world.get_all_session();
|
||||||
|
std::vector<entt::entity> entities;
|
||||||
for (auto e : view) {
|
for (auto e : view) {
|
||||||
const auto& c = view.get<BaseServerCreature>(e);
|
entities.push_back(e);
|
||||||
|
}
|
||||||
|
// parallel block touches disjoint entities only;
|
||||||
|
// structural registry changes stay on the server thread via m_tasks.
|
||||||
|
parallel_do(
|
||||||
|
*pool, entities.begin(), entities.end(), pool->thread_sum(),
|
||||||
|
[this, &sessions](entt::entity e) {
|
||||||
|
const auto& c = m_registry.get<BaseServerCreature>(e);
|
||||||
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
|
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
|
||||||
const auto& entity = m_registry.get<Entity>(e);
|
const auto& entity = m_registry.get<Entity>(e);
|
||||||
destory(entity.id);
|
destory(entity.id);
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
update_ai(e);
|
update_ai(e);
|
||||||
update_move(e);
|
update_move(e);
|
||||||
update_send(e, sessions);
|
update_send(e, sessions);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerEntityManager::update_ai(entt::entity e) {
|
void ServerEntityManager::update_ai(entt::entity e) {
|
||||||
@@ -59,7 +72,8 @@ void ServerEntityManager::update_move(entt::entity e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerEntityManager::update_send(
|
void ServerEntityManager::update_send(
|
||||||
entt::entity e, std::span<std::shared_ptr<Session>> sessions) {
|
entt::entity e,
|
||||||
|
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions) {
|
||||||
|
|
||||||
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
|
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -182,7 +182,6 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid,
|
|||||||
void ServerWorld::init_world(RunMode mode) {
|
void ServerWorld::init_world(RunMode mode) {
|
||||||
m_runmode = mode;
|
m_runmode = mode;
|
||||||
m_entity_manager.init();
|
m_entity_manager.init();
|
||||||
m_entity_manager.add_entity("cubed:pig", {0, 225, 0});
|
|
||||||
register_timer("player disconnect", 5, [this]() {
|
register_timer("player disconnect", 5, [this]() {
|
||||||
std::vector<std::string> disconnect;
|
std::vector<std::string> disconnect;
|
||||||
{
|
{
|
||||||
@@ -319,7 +318,7 @@ void ServerWorld::sync_and_collect_missing_chunks(
|
|||||||
chunk_acc acc;
|
chunk_acc acc;
|
||||||
if (m_chunks.insert(acc, pos)) {
|
if (m_chunks.insert(acc, pos)) {
|
||||||
need_gen_chunks_pos.push_back(pos);
|
need_gen_chunks_pos.push_back(pos);
|
||||||
acc->second = ChunkEntity{ChunkState::GENERATING, nullptr, 0};
|
acc->second = ChunkEntity{ChunkState::GENERATING};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1028,9 +1027,10 @@ void ServerWorld::set_chunk_load_style(int id) {
|
|||||||
|
|
||||||
int ServerWorld::chunk_size() const { return m_chunks.size(); }
|
int ServerWorld::chunk_size() const { return m_chunks.size(); }
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Session>> ServerWorld::get_all_session() const {
|
tbb::concurrent_vector<std::shared_ptr<Session>>
|
||||||
|
ServerWorld::get_all_session() const {
|
||||||
std::shared_lock lock(m_players_mutex);
|
std::shared_lock lock(m_players_mutex);
|
||||||
std::vector<std::shared_ptr<Session>> sessions;
|
tbb::concurrent_vector<std::shared_ptr<Session>> sessions;
|
||||||
for (const auto& [_, player] : m_players) {
|
for (const auto& [_, player] : m_players) {
|
||||||
sessions.emplace_back(player.get_session());
|
sessions.emplace_back(player.get_session());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user