Make client ChunkManager thread safe
This commit is contained in:
parent
db5fa9ca21
commit
999db35a85
@ -41,13 +41,23 @@ void ThreadHelper::update() {
|
||||
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) {
|
||||
callback();
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
acquire();
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,8 @@ namespace polygun::engine {
|
||||
~ThreadHelper() = default;
|
||||
|
||||
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 cleanup();
|
||||
|
@ -51,8 +51,8 @@ ChunkManager::ChunkManager(engine::Engine* engine, engine::TextureAtlas& texture
|
||||
m_pending_chunks(),
|
||||
m_loaded_chunks(new ClientChunk*[VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE]),
|
||||
m_unloaded_chunks(),
|
||||
m_updated_chunks(),
|
||||
m_dummy_chunk(engine, this, 0.0f),
|
||||
m_updated_chunks(0),
|
||||
m_greedy_chunk_shader(engine->get_master_renderer()->create_shader()),
|
||||
m_chunk_shader(engine->get_master_renderer()->create_shader()),
|
||||
m_network_manager(nullptr),
|
||||
@ -101,7 +101,7 @@ void ChunkManager::on_packet(network::NetworkPacket& packet) {
|
||||
packet.read(chunk);
|
||||
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_updated_chunks++;
|
||||
m_updated_chunks.push_back(chunk);
|
||||
break;
|
||||
}
|
||||
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++) {
|
||||
ClientChunk* const chunk = m_unloaded_chunks[i];
|
||||
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;
|
||||
m_unloaded_chunks.erase(m_unloaded_chunks.begin()+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->add_node(node, node_pos_to_local_node_pos(node_pos));
|
||||
chunk->set_modified(true);
|
||||
m_updated_chunks.push_back(static_cast<ClientChunk*>(chunk));
|
||||
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) {
|
||||
if(!chunk->has_mesh()) {
|
||||
delete chunk;
|
||||
return;
|
||||
}
|
||||
chunk->begin_animated_unload();
|
||||
m_unloaded_chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
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++) {
|
||||
if(m_loaded_chunks[i]) {
|
||||
m_loaded_chunks[i]->get_update_guard().lock();
|
||||
delete m_loaded_chunks[i];
|
||||
m_loaded_chunks[i] = nullptr;
|
||||
}
|
||||
}
|
||||
for(ClientChunk* chunk : m_unloaded_chunks)
|
||||
for(ClientChunk* chunk : m_unloaded_chunks) {
|
||||
chunk->get_update_guard().lock();
|
||||
delete chunk;
|
||||
}
|
||||
m_unloaded_chunks.clear();
|
||||
}
|
||||
|
||||
void ChunkManager::mesh_update_thread_func() {
|
||||
while(m_mesh_update_thread_running) {
|
||||
if(m_pending_chunks.empty()) {
|
||||
for(unsigned i = 0; i <VIEW_AREA_SIZE*VIEW_AREA_SIZE*VIEW_AREA_SIZE; i++) {
|
||||
acquire();
|
||||
ClientChunk* const chunk = m_loaded_chunks[i];
|
||||
if(!chunk) {
|
||||
release();
|
||||
continue;
|
||||
}
|
||||
if(!m_pending_chunks.empty() || m_updated_chunks.empty())
|
||||
continue;
|
||||
for(unsigned i = 0; i<m_updated_chunks.size(); i++) {
|
||||
ClientChunk* const chunk = m_updated_chunks[i];
|
||||
if(!chunk->get_update_guard().try_lock())
|
||||
continue;
|
||||
|
||||
for(unsigned char i = 0; i < engine::NodeSide::NODE_SIDE_COUNT; i++) {
|
||||
const engine::NodeSide node_side = static_cast<engine::NodeSide>(i);
|
||||
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)));
|
||||
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--;
|
||||
for(unsigned char i = 0; i < engine::NodeSide::NODE_SIDE_COUNT; i++) {
|
||||
const engine::NodeSide node_side = static_cast<engine::NodeSide>(i);
|
||||
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)));
|
||||
chunk->set_neighbour(node_side, client_chunk);
|
||||
}
|
||||
}
|
||||
|
||||
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/world/chunk_manager_base.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "game/world/client_chunk.hpp"
|
||||
|
||||
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 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; }
|
||||
|
||||
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;
|
||||
ClientChunk** m_loaded_chunks;
|
||||
std::vector<ClientChunk*> m_unloaded_chunks;
|
||||
std::vector<ClientChunk*> m_updated_chunks;
|
||||
ClientChunk m_dummy_chunk;
|
||||
unsigned m_updated_chunks;
|
||||
renderer::Shader* m_greedy_chunk_shader;
|
||||
renderer::Shader* m_chunk_shader;
|
||||
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) :
|
||||
Chunk(),
|
||||
m_chunk_manager(chunk_manager),
|
||||
m_update_guard(new std::mutex),
|
||||
m_mesh(nullptr),
|
||||
m_mesh_create_callback_index(0),
|
||||
m_texture_unit_size(texture_unit_size),
|
||||
m_loading_opacity(0.0f),
|
||||
m_sun_lightmap_needs_update(true),
|
||||
m_unloaded(false),
|
||||
m_copy(false),
|
||||
m_neighbours(),
|
||||
m_chunk_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();
|
||||
});
|
||||
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));
|
||||
}
|
||||
|
||||
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() {
|
||||
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) {
|
||||
|
@ -27,6 +27,8 @@ SOFTWARE.
|
||||
|
||||
#include "common/world/chunk.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "game/engine/node_side.hpp"
|
||||
|
||||
namespace polygun::renderer {
|
||||
@ -47,6 +49,7 @@ namespace polygun::world {
|
||||
class ClientChunk final : public Chunk {
|
||||
public:
|
||||
ClientChunk(engine::Engine* engine, ChunkManager* chunk_manager, float texture_unit_size);
|
||||
ClientChunk(const ClientChunk& other);
|
||||
virtual ~ClientChunk() override;
|
||||
|
||||
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;
|
||||
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;
|
||||
std::mutex& get_update_guard() { return *m_update_guard; }
|
||||
bool has_mesh() const { return m_mesh; }
|
||||
float get_loading_opacity() const { return m_loading_opacity; }
|
||||
bool sun_lightmap_needs_update() const { return m_sun_lightmap_needs_update; }
|
||||
|
||||
private:
|
||||
ChunkManager* m_chunk_manager;
|
||||
std::mutex* m_update_guard;
|
||||
renderer::Mesh* m_mesh;
|
||||
unsigned m_mesh_create_callback_index;
|
||||
float m_texture_unit_size;
|
||||
float m_loading_opacity;
|
||||
bool m_sun_lightmap_needs_update;
|
||||
bool m_unloaded;
|
||||
bool m_copy;
|
||||
const ClientChunk* m_neighbours[6];
|
||||
// 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];
|
||||
|
Loading…
x
Reference in New Issue
Block a user