Make client ChunkManager thread safe
This commit is contained in:
parent
db5fa9ca21
commit
999db35a85
@ -41,13 +41,23 @@ void ThreadHelper::update() {
|
|||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadHelper::invoke_on_main_thread(const std::function<void()>& callback) {
|
unsigned ThreadHelper::invoke_on_main_thread(const std::function<void()>& callback) {
|
||||||
if(std::this_thread::get_id()==g_main_thread_id) {
|
if(std::this_thread::get_id()==g_main_thread_id) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
acquire();
|
acquire();
|
||||||
m_main_thread_callbacks.push_back(callback);
|
m_main_thread_callbacks.push_back(callback);
|
||||||
|
const unsigned callback_index = m_main_thread_callbacks.size();
|
||||||
|
release();
|
||||||
|
return callback_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadHelper::cancel_main_thread_callback(unsigned index) {
|
||||||
|
if(index==0)
|
||||||
|
return;
|
||||||
|
acquire();
|
||||||
|
m_main_thread_callbacks.erase(m_main_thread_callbacks.begin()+index-1);
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ namespace polygun::engine {
|
|||||||
~ThreadHelper() = default;
|
~ThreadHelper() = default;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
void invoke_on_main_thread(const std::function<void()>& callback);
|
unsigned invoke_on_main_thread(const std::function<void()>& callback);
|
||||||
|
void cancel_main_thread_callback(unsigned index);
|
||||||
|
|
||||||
static void create();
|
static void create();
|
||||||
static void cleanup();
|
static void cleanup();
|
||||||
|
@ -51,8 +51,8 @@ ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture
|
|||||||
m_pending_chunks(),
|
m_pending_chunks(),
|
||||||
m_loaded_chunks(new ClientChunk*[VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE]),
|
m_loaded_chunks(new ClientChunk*[VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE]),
|
||||||
m_unloaded_chunks(),
|
m_unloaded_chunks(),
|
||||||
|
m_updated_chunks(),
|
||||||
m_dummy_chunk(engine, this, 0.0f),
|
m_dummy_chunk(engine, this, 0.0f),
|
||||||
m_updated_chunks(0),
|
|
||||||
m_greedy_chunk_shader(engine->get_master_renderer()->create_shader()),
|
m_greedy_chunk_shader(engine->get_master_renderer()->create_shader()),
|
||||||
m_chunk_shader(engine->get_master_renderer()->create_shader()),
|
m_chunk_shader(engine->get_master_renderer()->create_shader()),
|
||||||
m_network_manager(nullptr),
|
m_network_manager(nullptr),
|
||||||
@ -101,7 +101,7 @@ void ChunkManager::on_packet(network::NetworkPacket& packet) {
|
|||||||
packet.read(chunk);
|
packet.read(chunk);
|
||||||
LOG_VERBOSE("Received data for chunk at %d %d %d", pos[0], pos[1], pos[2]);
|
LOG_VERBOSE("Received data for chunk at %d %d %d", pos[0], pos[1], pos[2]);
|
||||||
m_loaded_chunks[local_chunk_pos[2]*VIEW_AREA_SIZE*VIEW_AREA_SIZE+local_chunk_pos[1]*VIEW_AREA_SIZE+local_chunk_pos[0]] = chunk;
|
m_loaded_chunks[local_chunk_pos[2]*VIEW_AREA_SIZE*VIEW_AREA_SIZE+local_chunk_pos[1]*VIEW_AREA_SIZE+local_chunk_pos[0]] = chunk;
|
||||||
m_updated_chunks++;
|
m_updated_chunks.push_back(chunk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case network::NetworkPacketID::PACKET_INVALIDATE_MAP:
|
case network::NetworkPacketID::PACKET_INVALIDATE_MAP:
|
||||||
@ -237,7 +237,7 @@ void ChunkManager::update_chunks(float delta) {
|
|||||||
for(size_t i = 0; i<m_unloaded_chunks.size(); i++) {
|
for(size_t i = 0; i<m_unloaded_chunks.size(); i++) {
|
||||||
ClientChunk* const chunk = m_unloaded_chunks[i];
|
ClientChunk* const chunk = m_unloaded_chunks[i];
|
||||||
chunk->update(delta);
|
chunk->update(delta);
|
||||||
if(chunk->get_loading_opacity()<=0.0f) {
|
if(m_updated_chunks.empty() && chunk->get_loading_opacity()<=0.0f && chunk->get_update_guard().try_lock()) {
|
||||||
delete chunk;
|
delete chunk;
|
||||||
m_unloaded_chunks.erase(m_unloaded_chunks.begin()+i);
|
m_unloaded_chunks.erase(m_unloaded_chunks.begin()+i);
|
||||||
i--;
|
i--;
|
||||||
@ -332,6 +332,7 @@ void ChunkManager::add_node(uint16_t node, const math::Vector3i& node_pos) {
|
|||||||
Chunk* const chunk = get_chunk_with_node(node_pos);
|
Chunk* const chunk = get_chunk_with_node(node_pos);
|
||||||
chunk->add_node(node, node_pos_to_local_node_pos(node_pos));
|
chunk->add_node(node, node_pos_to_local_node_pos(node_pos));
|
||||||
chunk->set_modified(true);
|
chunk->set_modified(true);
|
||||||
|
m_updated_chunks.push_back(static_cast<ClientChunk*>(chunk));
|
||||||
static_cast<ClientChunk*>(chunk)->set_sun_lightmap_needs_update(true);
|
static_cast<ClientChunk*>(chunk)->set_sun_lightmap_needs_update(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,55 +356,53 @@ Chunk* ChunkManager::get_chunk(const math::Vector3i& pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ChunkManager::unload_chunk_animated(ClientChunk* chunk) {
|
void ChunkManager::unload_chunk_animated(ClientChunk* chunk) {
|
||||||
if(!chunk->has_mesh()) {
|
|
||||||
delete chunk;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chunk->begin_animated_unload();
|
chunk->begin_animated_unload();
|
||||||
m_unloaded_chunks.push_back(chunk);
|
m_unloaded_chunks.push_back(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkManager::unload_all_chunks() {
|
void ChunkManager::unload_all_chunks() {
|
||||||
|
while(!m_updated_chunks.empty());
|
||||||
for(size_t i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
|
for(size_t i = 0; i<VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
|
||||||
if(m_loaded_chunks[i]) {
|
if(m_loaded_chunks[i]) {
|
||||||
|
m_loaded_chunks[i]->get_update_guard().lock();
|
||||||
delete m_loaded_chunks[i];
|
delete m_loaded_chunks[i];
|
||||||
m_loaded_chunks[i] = nullptr;
|
m_loaded_chunks[i] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(ClientChunk* chunk : m_unloaded_chunks)
|
for(ClientChunk* chunk : m_unloaded_chunks) {
|
||||||
|
chunk->get_update_guard().lock();
|
||||||
delete chunk;
|
delete chunk;
|
||||||
|
}
|
||||||
m_unloaded_chunks.clear();
|
m_unloaded_chunks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkManager::mesh_update_thread_func() {
|
void ChunkManager::mesh_update_thread_func() {
|
||||||
while(m_mesh_update_thread_running) {
|
while(m_mesh_update_thread_running) {
|
||||||
if(m_pending_chunks.empty()) {
|
if(!m_pending_chunks.empty() || m_updated_chunks.empty())
|
||||||
for(unsigned i = 0; i <VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
|
continue;
|
||||||
acquire();
|
for(unsigned i = 0; i<m_updated_chunks.size(); i++) {
|
||||||
ClientChunk* const chunk = m_loaded_chunks[i];
|
ClientChunk* const chunk = m_updated_chunks[i];
|
||||||
if(!chunk) {
|
if(!chunk->get_update_guard().try_lock())
|
||||||
release();
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned char i = 0; i < engine::NodeSide::NODE_SIDE_COUNT; i++) {
|
for(unsigned char i = 0; i < engine::NodeSide::NODE_SIDE_COUNT; i++) {
|
||||||
const engine::NodeSide node_side = static_cast<engine::NodeSide>(i);
|
const engine::NodeSide node_side = static_cast<engine::NodeSide>(i);
|
||||||
if(!chunk->get_neighbour(node_side)) {
|
if(!chunk->get_neighbour(node_side)) {
|
||||||
const ClientChunk* const client_chunk = static_cast<const ClientChunk*>(get_chunk(chunk->get_pos() + engine::get_node_side_offset(node_side)));
|
const ClientChunk* const client_chunk = static_cast<const ClientChunk*>(get_chunk(chunk->get_pos()+engine::get_node_side_offset(node_side)));
|
||||||
chunk->set_neighbour(node_side, client_chunk);
|
chunk->set_neighbour(node_side, client_chunk);
|
||||||
}
|
|
||||||
}
|
|
||||||
release();
|
|
||||||
|
|
||||||
if(chunk->sun_lightmap_needs_update())
|
|
||||||
generate_column_sun_lightmap(math::Vector2i{chunk->get_pos()[0], chunk->get_pos()[2]});
|
|
||||||
|
|
||||||
if(chunk->is_modified()) {
|
|
||||||
chunk->generate_mesh(m_engine, m_content_registry);
|
|
||||||
if(!chunk->is_modified())
|
|
||||||
m_updated_chunks--;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(chunk->sun_lightmap_needs_update())
|
||||||
|
generate_column_sun_lightmap(math::Vector2i{chunk->get_pos()[0], chunk->get_pos()[2]});
|
||||||
|
|
||||||
|
if(chunk->has_mesh()) {
|
||||||
|
chunk->generate_mesh(m_engine, m_content_registry);
|
||||||
|
m_updated_chunks.erase(m_updated_chunks.begin()+i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk->get_update_guard().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,6 +28,9 @@ SOFTWARE.
|
|||||||
#include "common/thread_safe.hpp"
|
#include "common/thread_safe.hpp"
|
||||||
#include "common/world/chunk_manager_base.hpp"
|
#include "common/world/chunk_manager_base.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "game/world/client_chunk.hpp"
|
#include "game/world/client_chunk.hpp"
|
||||||
|
|
||||||
namespace polygun::engine {
|
namespace polygun::engine {
|
||||||
@ -62,7 +65,7 @@ namespace polygun::world {
|
|||||||
void try_fill_node(const math::Vector3i& from, const math::Vector3i& to, uint16_t node);
|
void try_fill_node(const math::Vector3i& from, const math::Vector3i& to, uint16_t node);
|
||||||
|
|
||||||
void request_chunk(const math::Vector3i& pos);
|
void request_chunk(const math::Vector3i& pos);
|
||||||
unsigned get_updated_chunks() const { return m_updated_chunks; }
|
unsigned get_updated_chunks() const { return m_updated_chunks.size(); }
|
||||||
void set_network_manager(network::NetworkManager* manager) { m_network_manager = manager; }
|
void set_network_manager(network::NetworkManager* manager) { m_network_manager = manager; }
|
||||||
|
|
||||||
virtual void add_node(uint16_t node, const math::Vector3i& node_pos) override;
|
virtual void add_node(uint16_t node, const math::Vector3i& node_pos) override;
|
||||||
@ -77,8 +80,8 @@ namespace polygun::world {
|
|||||||
std::vector<math::Vector3i> m_pending_chunks;
|
std::vector<math::Vector3i> m_pending_chunks;
|
||||||
ClientChunk** m_loaded_chunks;
|
ClientChunk** m_loaded_chunks;
|
||||||
std::vector<ClientChunk*> m_unloaded_chunks;
|
std::vector<ClientChunk*> m_unloaded_chunks;
|
||||||
|
std::vector<ClientChunk*> m_updated_chunks;
|
||||||
ClientChunk m_dummy_chunk;
|
ClientChunk m_dummy_chunk;
|
||||||
unsigned m_updated_chunks;
|
|
||||||
renderer::Shader* m_greedy_chunk_shader;
|
renderer::Shader* m_greedy_chunk_shader;
|
||||||
renderer::Shader* m_chunk_shader;
|
renderer::Shader* m_chunk_shader;
|
||||||
network::NetworkManager* m_network_manager;
|
network::NetworkManager* m_network_manager;
|
||||||
|
@ -38,16 +38,19 @@ using namespace polygun::world;
|
|||||||
ClientChunk::ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size) :
|
ClientChunk::ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size) :
|
||||||
Chunk(),
|
Chunk(),
|
||||||
m_chunk_manager(chunk_manager),
|
m_chunk_manager(chunk_manager),
|
||||||
|
m_update_guard(new std::mutex),
|
||||||
m_mesh(nullptr),
|
m_mesh(nullptr),
|
||||||
|
m_mesh_create_callback_index(0),
|
||||||
m_texture_unit_size(texture_unit_size),
|
m_texture_unit_size(texture_unit_size),
|
||||||
m_loading_opacity(0.0f),
|
m_loading_opacity(0.0f),
|
||||||
m_sun_lightmap_needs_update(true),
|
m_sun_lightmap_needs_update(true),
|
||||||
m_unloaded(false),
|
m_unloaded(false),
|
||||||
|
m_copy(false),
|
||||||
m_neighbours(),
|
m_neighbours(),
|
||||||
m_chunk_lightmap(),
|
m_chunk_lightmap(),
|
||||||
m_chunk_sun_lightmap()
|
m_chunk_sun_lightmap()
|
||||||
{
|
{
|
||||||
engine::ThreadHelper::get().invoke_on_main_thread([this, engine]() {
|
m_mesh_create_callback_index = engine::ThreadHelper::get().invoke_on_main_thread([this, engine]() {
|
||||||
m_mesh = engine->get_master_renderer()->create_mesh();
|
m_mesh = engine->get_master_renderer()->create_mesh();
|
||||||
});
|
});
|
||||||
memset(m_neighbours, 0, sizeof(m_neighbours));
|
memset(m_neighbours, 0, sizeof(m_neighbours));
|
||||||
@ -55,8 +58,34 @@ ClientChunk::ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, fl
|
|||||||
memset(m_chunk_sun_lightmap, 0, sizeof(m_chunk_sun_lightmap));
|
memset(m_chunk_sun_lightmap, 0, sizeof(m_chunk_sun_lightmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClientChunk::ClientChunk(const ClientChunk& other) :
|
||||||
|
Chunk(),
|
||||||
|
m_chunk_manager(other.m_chunk_manager),
|
||||||
|
m_update_guard(other.m_update_guard),
|
||||||
|
m_mesh(other.m_mesh),
|
||||||
|
m_mesh_create_callback_index(),
|
||||||
|
m_texture_unit_size(other.m_texture_unit_size),
|
||||||
|
m_loading_opacity(other.m_loading_opacity),
|
||||||
|
m_sun_lightmap_needs_update(other.m_sun_lightmap_needs_update),
|
||||||
|
m_unloaded(other.m_unloaded),
|
||||||
|
m_copy(true),
|
||||||
|
m_neighbours(),
|
||||||
|
m_chunk_lightmap(),
|
||||||
|
m_chunk_sun_lightmap()
|
||||||
|
{
|
||||||
|
memcpy(m_neighbours, other.m_neighbours, sizeof(m_neighbours));
|
||||||
|
memcpy(m_chunk_lightmap, other.m_chunk_lightmap, sizeof(m_chunk_lightmap));
|
||||||
|
memcpy(m_chunk_sun_lightmap, other.m_chunk_sun_lightmap, sizeof(m_chunk_sun_lightmap));
|
||||||
|
}
|
||||||
|
|
||||||
ClientChunk::~ClientChunk() {
|
ClientChunk::~ClientChunk() {
|
||||||
delete m_mesh;
|
if(m_copy)
|
||||||
|
return;
|
||||||
|
if(m_mesh)
|
||||||
|
delete m_mesh;
|
||||||
|
else
|
||||||
|
engine::ThreadHelper::get().cancel_main_thread_callback(m_mesh_create_callback_index);
|
||||||
|
delete m_update_guard;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientChunk::render(renderer::MeshRenderer* mesh_renderer, renderer::Texture* atlas_texture) {
|
void ClientChunk::render(renderer::MeshRenderer* mesh_renderer, renderer::Texture* atlas_texture) {
|
||||||
|
@ -27,6 +27,8 @@ SOFTWARE.
|
|||||||
|
|
||||||
#include "common/world/chunk.hpp"
|
#include "common/world/chunk.hpp"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "game/engine/node_side.hpp"
|
#include "game/engine/node_side.hpp"
|
||||||
|
|
||||||
namespace polygun::renderer {
|
namespace polygun::renderer {
|
||||||
@ -47,6 +49,7 @@ namespace polygun::world {
|
|||||||
class ClientChunk final : public Chunk {
|
class ClientChunk final : public Chunk {
|
||||||
public:
|
public:
|
||||||
ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size);
|
ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size);
|
||||||
|
ClientChunk(const ClientChunk& other);
|
||||||
virtual ~ClientChunk() override;
|
virtual ~ClientChunk() override;
|
||||||
|
|
||||||
void render(renderer::MeshRenderer* mesh_renderer, renderer::Texture* atlas_texture);
|
void render(renderer::MeshRenderer* mesh_renderer, renderer::Texture* atlas_texture);
|
||||||
@ -62,17 +65,21 @@ namespace polygun::world {
|
|||||||
uint8_t get_emitted_light_value(const math::Vector3i& pos, engine::NodeSide side) const;
|
uint8_t get_emitted_light_value(const math::Vector3i& pos, engine::NodeSide side) const;
|
||||||
math::RGBColor get_sunlight_value(math::Vector3i pos, engine::NodeSide side) const;
|
math::RGBColor get_sunlight_value(math::Vector3i pos, engine::NodeSide side) const;
|
||||||
math::RGBColor get_light_value(const math::Vector3i& pos, engine::NodeSide side) const;
|
math::RGBColor get_light_value(const math::Vector3i& pos, engine::NodeSide side) const;
|
||||||
|
std::mutex& get_update_guard() { return *m_update_guard; }
|
||||||
bool has_mesh() const { return m_mesh; }
|
bool has_mesh() const { return m_mesh; }
|
||||||
float get_loading_opacity() const { return m_loading_opacity; }
|
float get_loading_opacity() const { return m_loading_opacity; }
|
||||||
bool sun_lightmap_needs_update() const { return m_sun_lightmap_needs_update; }
|
bool sun_lightmap_needs_update() const { return m_sun_lightmap_needs_update; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChunkManager* m_chunk_manager;
|
ChunkManager* m_chunk_manager;
|
||||||
|
std::mutex* m_update_guard;
|
||||||
renderer::Mesh* m_mesh;
|
renderer::Mesh* m_mesh;
|
||||||
|
unsigned m_mesh_create_callback_index;
|
||||||
float m_texture_unit_size;
|
float m_texture_unit_size;
|
||||||
float m_loading_opacity;
|
float m_loading_opacity;
|
||||||
bool m_sun_lightmap_needs_update;
|
bool m_sun_lightmap_needs_update;
|
||||||
bool m_unloaded;
|
bool m_unloaded;
|
||||||
|
bool m_copy;
|
||||||
const ClientChunk* m_neighbours[6];
|
const ClientChunk* m_neighbours[6];
|
||||||
// Each light value is in 0-15 range so 2 node faces are stored in one byte
|
// Each light value is in 0-15 range so 2 node faces are stored in one byte
|
||||||
uint8_t m_chunk_lightmap[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE*3];
|
uint8_t m_chunk_lightmap[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE*3];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user