From 2024a33ee9dc0b90cc2bc5d223ae1161872f987f Mon Sep 17 00:00:00 2001 From: Relintai Date: Fri, 7 Feb 2025 10:08:22 +0100 Subject: [PATCH] Implemented chunk material invalidation support for TerrainLibraryMergerPCM. --- .../terraman/editor/terrain_world_editor.cpp | 4 +- .../library/terrain_library_merger_pcm.cpp | 59 ++++++++++++++++++- .../terraman/world/jobs/terrain_prop_job.cpp | 9 +-- .../world/jobs/terrain_terrain_job.cpp | 26 +------- modules/terraman/world/terrain_chunk.cpp | 42 +++++++++++++ modules/terraman/world/terrain_chunk.h | 12 ++++ modules/terraman/world/terrain_world.cpp | 9 ++- modules/terraman/world/terrain_world.h | 2 +- 8 files changed, 125 insertions(+), 38 deletions(-) diff --git a/modules/terraman/editor/terrain_world_editor.cpp b/modules/terraman/editor/terrain_world_editor.cpp index ff41d436e..5d4149191 100644 --- a/modules/terraman/editor/terrain_world_editor.cpp +++ b/modules/terraman/editor/terrain_world_editor.cpp @@ -425,7 +425,7 @@ void TerrainWorldEditor::isolevel_brush_draw(const Vector3 &p_world_position) { } } - _world->set_voxels_at_world_data_position(draw_data, _isolevel_brush_channel, true, _isolevel_brush_allow_create_chunks); + _world->set_voxels_at_world_data_position(draw_data, _isolevel_brush_channel, true, _isolevel_brush_allow_create_chunks, false); } void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) { @@ -471,7 +471,7 @@ void TerrainWorldEditor::paint_brush_draw(const Vector3 &p_world_position) { } } - _world->set_voxels_at_world_data_position(draw_data, _paint_brush_channel, true, _paint_brush_allow_create_chunks); + _world->set_voxels_at_world_data_position(draw_data, _paint_brush_channel, true, _paint_brush_allow_create_chunks, true); } void TerrainWorldEditor::edit(TerrainWorld *p_world) { diff --git a/modules/terraman/library/terrain_library_merger_pcm.cpp b/modules/terraman/library/terrain_library_merger_pcm.cpp index 0f0c0c882..2d92f522e 100644 --- a/modules/terraman/library/terrain_library_merger_pcm.cpp +++ b/modules/terraman/library/terrain_library_merger_pcm.cpp @@ -69,6 +69,12 @@ void TerrainLibraryMergerPCM::_material_cache_get_key(Ref chunk) { return; } + int old_key = 0; + + if (chunk->material_cache_key_has()) { + old_key = chunk->material_cache_key_get(); + } + Vector surfaces; uint32_t size = chunk->get_data_size(); @@ -98,6 +104,10 @@ void TerrainLibraryMergerPCM::_material_cache_get_key(Ref chunk) { chunk->material_cache_key_set(0); chunk->material_cache_key_has_set(false); + if (old_key != 0) { + material_cache_unref(old_key); + } + return; } @@ -111,9 +121,18 @@ void TerrainLibraryMergerPCM::_material_cache_get_key(Ref chunk) { int hash = static_cast(hstr.hash()); + if (old_key != 0 && old_key == hash) { + chunk->material_cache_key_invalid_set(false); + return; + } + chunk->material_cache_key_set(hash); chunk->material_cache_key_has_set(true); + if (old_key != 0) { + material_cache_unref(old_key); + } + _material_cache_mutex.lock(); if (_material_cache.has(hash)) { @@ -198,7 +217,7 @@ void TerrainLibraryMergerPCM::_material_cache_unref(const int key) { // This is needed, because when duplicating materials the RenderingServer apparently // needs synchronization with the main thread. So if _material_cache_unref holds the mutex // and is duplicating the materials, trying to get the lock from the main thread will deadlock - // the game. This can happen when chungs are spawned and despawned really fast. + // the game. This can happen when chunks are spawned and despawned really fast. // E.g. when flying around in the editor. MessageQueue::get_singleton()->push_call(this, "_material_cache_unref", key, 1); return; @@ -237,6 +256,12 @@ void TerrainLibraryMergerPCM::_liquid_material_cache_get_key(Ref c return; } + int old_key = 0; + + if (chunk->liquid_material_cache_key_has()) { + old_key = chunk->liquid_material_cache_key_get(); + } + Vector surfaces; uint32_t size = chunk->get_data_size(); @@ -266,6 +291,10 @@ void TerrainLibraryMergerPCM::_liquid_material_cache_get_key(Ref c chunk->liquid_material_cache_key_set(0); chunk->liquid_material_cache_key_has_set(false); + if (old_key != 0) { + liquid_material_cache_unref(old_key); + } + return; } @@ -279,9 +308,18 @@ void TerrainLibraryMergerPCM::_liquid_material_cache_get_key(Ref c int hash = static_cast(hstr.hash()); + if (old_key != 0 && old_key == hash) { + chunk->liquid_material_cache_key_invalid_set(false); + return; + } + chunk->liquid_material_cache_key_set(hash); chunk->liquid_material_cache_key_has_set(true); + if (old_key != 0) { + liquid_material_cache_unref(old_key); + } + _liquid_material_cache_mutex.lock(); if (_liquid_material_cache.has(hash)) { @@ -397,6 +435,12 @@ void TerrainLibraryMergerPCM::_liquid_material_cache_unref(const int key) { //Props void TerrainLibraryMergerPCM::_prop_material_cache_get_key(Ref chunk) { + int old_key = 0; + + if (chunk->prop_material_cache_key_has()) { + old_key = chunk->prop_material_cache_key_get(); + } + Vector props; /* @@ -454,6 +498,10 @@ void TerrainLibraryMergerPCM::_prop_material_cache_get_key(Ref chu chunk->prop_material_cache_key_set(0); chunk->prop_material_cache_key_has_set(false); + if (old_key != 0) { + prop_material_cache_unref(old_key); + } + return; } @@ -467,9 +515,18 @@ void TerrainLibraryMergerPCM::_prop_material_cache_get_key(Ref chu int hash = static_cast(hstr.hash()); + if (old_key != 0 && old_key == hash) { + chunk->prop_material_cache_key_invalid_set(false); + return; + } + chunk->prop_material_cache_key_set(hash); chunk->prop_material_cache_key_has_set(true); + if (old_key != 0) { + prop_material_cache_unref(old_key); + } + _prop_material_cache_mutex.lock(); if (_prop_material_cache.has(hash)) { diff --git a/modules/terraman/world/jobs/terrain_prop_job.cpp b/modules/terraman/world/jobs/terrain_prop_job.cpp index 79fce1661..ac2ed282a 100644 --- a/modules/terraman/world/jobs/terrain_prop_job.cpp +++ b/modules/terraman/world/jobs/terrain_prop_job.cpp @@ -354,7 +354,7 @@ void TerrainPropJob::phase_setup() { } if (library->supports_caching()) { - if (!_chunk->prop_material_cache_key_has()) { + if (!_chunk->prop_material_cache_key_has() || _chunk->prop_material_cache_key_invalid_get()) { library->prop_material_cache_get_key(_chunk); if (!_chunk->prop_material_cache_key_has()) { @@ -366,13 +366,6 @@ void TerrainPropJob::phase_setup() { Ref cache = library->prop_material_cache_get(_chunk->prop_material_cache_key_get()); - //Note: without threadpool and threading none of this can happen, as cache will get initialized the first time a thread requests it! - while (!cache->get_initialized()) { - //Means it's currently merging the atlases on a different thread. - //Let's just wait - OS::get_singleton()->delay_usec(100); - } - #ifdef MODULE_MESH_DATA_RESOURCE_ENABLED for (int i = 0; i < _chunk->mesh_data_resource_get_count(); ++i) { Ref tex = _chunk->mesh_data_resource_get_texture(i); diff --git a/modules/terraman/world/jobs/terrain_terrain_job.cpp b/modules/terraman/world/jobs/terrain_terrain_job.cpp index 62de682b2..9690ba823 100644 --- a/modules/terraman/world/jobs/terrain_terrain_job.cpp +++ b/modules/terraman/world/jobs/terrain_terrain_job.cpp @@ -113,34 +113,12 @@ void TerrainTerrainJob::phase_library_setup() { } if (lib->supports_caching()) { - if (!_chunk->material_cache_key_has()) { + if (!_chunk->material_cache_key_has() || _chunk->material_cache_key_invalid_get()) { lib->material_cache_get_key(_chunk); - } else { - Ref cache = lib->material_cache_get(_chunk->material_cache_key_get()); - - if (cache.is_valid()) { - //Note: without threadpool and threading none of this can happen, as cache will get initialized the first time a thread requests it! - while (!cache->get_initialized()) { - //Means it's currently merging the atlases on a different thread. - //Let's just wait - OS::get_singleton()->delay_usec(100); - } - } } - if (!_chunk->liquid_material_cache_key_has()) { + if (!_chunk->liquid_material_cache_key_has() || _chunk->liquid_material_cache_key_invalid_get()) { lib->liquid_material_cache_get_key(_chunk); - } else { - Ref cache = lib->liquid_material_cache_get(_chunk->liquid_material_cache_key_get()); - - if (cache.is_valid()) { - //Note: without threadpool and threading none of this can happen, as cache will get initialized the first time a thread requests it! - while (!cache->get_initialized()) { - //Means it's currently merging the atlases on a different thread. - //Let's just wait - OS::get_singleton()->delay_usec(100); - } - } } } diff --git a/modules/terraman/world/terrain_chunk.cpp b/modules/terraman/world/terrain_chunk.cpp index f8cea2b1b..76a73a49c 100644 --- a/modules/terraman/world/terrain_chunk.cpp +++ b/modules/terraman/world/terrain_chunk.cpp @@ -200,6 +200,8 @@ int TerrainChunk::material_cache_key_get() const { } void TerrainChunk::material_cache_key_set(const int value) { _material_cache_key = value; + + _material_cache_key_invalid = false; } bool TerrainChunk::material_cache_key_has() const { @@ -209,11 +211,20 @@ void TerrainChunk::material_cache_key_has_set(const bool value) { _material_cache_key_has = value; } +bool TerrainChunk::material_cache_key_invalid_get() const { + return _material_cache_key_invalid; +} +void TerrainChunk::material_cache_key_invalid_set(const bool value) { + _material_cache_key_invalid = value; +} + int TerrainChunk::liquid_material_cache_key_get() const { return _liquid_material_cache_key; } void TerrainChunk::liquid_material_cache_key_set(const int value) { _liquid_material_cache_key = value; + + _liquid_material_cache_key_invalid = false; } bool TerrainChunk::liquid_material_cache_key_has() const { @@ -223,11 +234,20 @@ void TerrainChunk::liquid_material_cache_key_has_set(const bool value) { _liquid_material_cache_key_has = value; } +bool TerrainChunk::liquid_material_cache_key_invalid_get() const { + return _liquid_material_cache_key_invalid; +} +void TerrainChunk::liquid_material_cache_key_invalid_set(const bool value) { + _liquid_material_cache_key_invalid = value; +} + int TerrainChunk::prop_material_cache_key_get() const { return _prop_material_cache_key; } void TerrainChunk::prop_material_cache_key_set(const int value) { _prop_material_cache_key = value; + + _prop_material_cache_key_invalid = false; } bool TerrainChunk::prop_material_cache_key_has() const { @@ -237,6 +257,13 @@ void TerrainChunk::prop_material_cache_key_has_set(const bool value) { _prop_material_cache_key_has = value; } +bool TerrainChunk::prop_material_cache_key_invalid_get() const { + return _prop_material_cache_key_invalid; +} +void TerrainChunk::prop_material_cache_key_invalid_set(const bool value) { + _prop_material_cache_key_invalid = value; +} + Ref TerrainChunk::get_library() { return _library; } @@ -1150,12 +1177,15 @@ TerrainChunk::TerrainChunk() { _material_cache_key = 0; _material_cache_key_has = false; + _material_cache_key_invalid = false; _liquid_material_cache_key = 0; _liquid_material_cache_key_has = false; + _liquid_material_cache_key_invalid = false; _prop_material_cache_key = 0; _prop_material_cache_key_has = false; + _prop_material_cache_key_invalid = false; _current_job = -1; @@ -1464,6 +1494,10 @@ void TerrainChunk::_bind_methods() { ClassDB::bind_method(D_METHOD("material_cache_key_has_set"), &TerrainChunk::material_cache_key_has_set); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "material_cache_key_has", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "material_cache_key_has_set", "material_cache_key_has_get"); + ClassDB::bind_method(D_METHOD("material_cache_key_invalid_get"), &TerrainChunk::material_cache_key_invalid_get); + ClassDB::bind_method(D_METHOD("material_cache_key_invalid_set"), &TerrainChunk::material_cache_key_invalid_set); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "material_cache_key_invalid", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "material_cache_key_invalid_set", "material_cache_key_invalid_get"); + ClassDB::bind_method(D_METHOD("liquid_material_cache_key_get"), &TerrainChunk::liquid_material_cache_key_get); ClassDB::bind_method(D_METHOD("liquid_material_cache_key_set"), &TerrainChunk::liquid_material_cache_key_set); ADD_PROPERTY(PropertyInfo(Variant::INT, "liquid_material_cache_key", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "liquid_material_cache_key_set", "liquid_material_cache_key_get"); @@ -1472,6 +1506,10 @@ void TerrainChunk::_bind_methods() { ClassDB::bind_method(D_METHOD("liquid_material_cache_key_has_set"), &TerrainChunk::liquid_material_cache_key_has_set); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "liquid_material_cache_key_has", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "liquid_material_cache_key_has_set", "liquid_material_cache_key_has_get"); + ClassDB::bind_method(D_METHOD("liquid_material_cache_key_invalid_get"), &TerrainChunk::liquid_material_cache_key_invalid_get); + ClassDB::bind_method(D_METHOD("liquid_material_cache_key_invalid_set"), &TerrainChunk::liquid_material_cache_key_invalid_set); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "liquid_material_cache_key_invalid", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "liquid_material_cache_key_invalid_set", "liquid_material_cache_key_invalid_get"); + ClassDB::bind_method(D_METHOD("prop_material_cache_key_get"), &TerrainChunk::prop_material_cache_key_get); ClassDB::bind_method(D_METHOD("prop_material_cache_key_set"), &TerrainChunk::prop_material_cache_key_set); ADD_PROPERTY(PropertyInfo(Variant::INT, "prop_material_cache_key", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "prop_material_cache_key_set", "prop_material_cache_key_get"); @@ -1480,6 +1518,10 @@ void TerrainChunk::_bind_methods() { ClassDB::bind_method(D_METHOD("prop_material_cache_key_has_set"), &TerrainChunk::prop_material_cache_key_has_set); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prop_material_cache_key_has", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "prop_material_cache_key_has_set", "prop_material_cache_key_has_get"); + ClassDB::bind_method(D_METHOD("prop_material_cache_key_invalid_get"), &TerrainChunk::prop_material_cache_key_invalid_get); + ClassDB::bind_method(D_METHOD("prop_material_cache_key_invalid_set"), &TerrainChunk::prop_material_cache_key_invalid_set); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prop_material_cache_key_invalid", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "prop_material_cache_key_invalid_set", "prop_material_cache_key_invalid_get"); + ClassDB::bind_method(D_METHOD("get_library"), &TerrainChunk::get_library); ClassDB::bind_method(D_METHOD("set_library", "value"), &TerrainChunk::set_library); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "TerrainLibrary"), "set_library", "get_library"); diff --git a/modules/terraman/world/terrain_chunk.h b/modules/terraman/world/terrain_chunk.h index b71b7bb48..72d89b7df 100644 --- a/modules/terraman/world/terrain_chunk.h +++ b/modules/terraman/world/terrain_chunk.h @@ -140,18 +140,27 @@ public: bool material_cache_key_has() const; void material_cache_key_has_set(const bool value); + bool material_cache_key_invalid_get() const; + void material_cache_key_invalid_set(const bool value); + int liquid_material_cache_key_get() const; void liquid_material_cache_key_set(const int value); bool liquid_material_cache_key_has() const; void liquid_material_cache_key_has_set(const bool value); + bool liquid_material_cache_key_invalid_get() const; + void liquid_material_cache_key_invalid_set(const bool value); + int prop_material_cache_key_get() const; void prop_material_cache_key_set(const int value); bool prop_material_cache_key_has() const; void prop_material_cache_key_has_set(const bool value); + bool prop_material_cache_key_invalid_get() const; + void prop_material_cache_key_invalid_set(const bool value); + Ref get_library(); void set_library(const Ref &value); @@ -381,12 +390,15 @@ protected: int _material_cache_key; bool _material_cache_key_has; + bool _material_cache_key_invalid; int _liquid_material_cache_key; bool _liquid_material_cache_key_has; + bool _liquid_material_cache_key_invalid; int _prop_material_cache_key; bool _prop_material_cache_key_has; + bool _prop_material_cache_key_invalid; float _world_height; diff --git a/modules/terraman/world/terrain_world.cpp b/modules/terraman/world/terrain_world.cpp index a52da68a6..88699d18a 100644 --- a/modules/terraman/world/terrain_world.cpp +++ b/modules/terraman/world/terrain_world.cpp @@ -1037,7 +1037,7 @@ Ref TerrainWorld::get_or_create_chunk_at_world_data_position(const return chunk_get_or_create(x, z); } -void TerrainWorld::set_voxels_at_world_data_position(const Array &p_data, const int p_channel_index, const bool p_immediate_build, const bool p_allow_creating_chunks) { +void TerrainWorld::set_voxels_at_world_data_position(const Array &p_data, const int p_channel_index, const bool p_immediate_build, const bool p_allow_creating_chunks, const bool p_invalidate_texture_caches) { ERR_FAIL_COND(p_data.size() % 2 != 0); // TODO rework this so it works directly with ints. @@ -1143,6 +1143,11 @@ void TerrainWorld::set_voxels_at_world_data_position(const Array &p_data, const for (HashSet>::Iterator iter = chunks_to_rebuild.begin(); iter.valid(); iter.next()) { Ref chunk = iter.key(); + if (p_invalidate_texture_caches) { + chunk->material_cache_key_invalid_set(true); + chunk->liquid_material_cache_key_invalid_set(true); + } + if (p_immediate_build) { chunk->build_immediate(); } else { @@ -1527,7 +1532,7 @@ void TerrainWorld::_bind_methods() { ClassDB::bind_method(D_METHOD("set_voxel_at_world_data_position", "world_data_position", "data", "channel_index", "rebuild", "allow_creating_chunks "), &TerrainWorld::set_voxel_at_world_data_position, DEFVAL(true), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_chunk_at_world_data_position", "world_data_position"), &TerrainWorld::get_chunk_at_world_data_position); ClassDB::bind_method(D_METHOD("get_or_create_chunk_at_world_data_position", "world_data_position"), &TerrainWorld::get_or_create_chunk_at_world_data_position); - ClassDB::bind_method(D_METHOD("set_voxels_at_world_data_position", "data", "channel_index", "immediate_build", "allow_creating_chunks"), &TerrainWorld::set_voxels_at_world_data_position, DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("set_voxels_at_world_data_position", "data", "channel_index", "immediate_build", "allow_creating_chunks", "invalidate_texture_caches"), &TerrainWorld::set_voxels_at_world_data_position, DEFVAL(false), DEFVAL(true), DEFVAL(true)); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::INT, "ret"), "_get_channel_index_info", PropertyInfo(Variant::INT, "channel_type", PROPERTY_HINT_ENUM, BINDING_STRING_CHANNEL_TYPE_INFO))); diff --git a/modules/terraman/world/terrain_world.h b/modules/terraman/world/terrain_world.h index 0106d2ef6..54468e86f 100644 --- a/modules/terraman/world/terrain_world.h +++ b/modules/terraman/world/terrain_world.h @@ -198,7 +198,7 @@ public: void set_voxel_at_world_data_position(const Vector2i &world_data_position, const uint8_t data, const int channel_index, const bool p_immediate_build = true, const bool allow_creating_chunks = true); Ref get_chunk_at_world_data_position(const Vector2i &world_data_position); Ref get_or_create_chunk_at_world_data_position(const Vector2i &world_data_position); - void set_voxels_at_world_data_position(const Array &p_data, const int p_channel_index, const bool p_immediate_build = false, const bool p_allow_creating_chunks = true); + void set_voxels_at_world_data_position(const Array &p_data, const int p_channel_index, const bool p_immediate_build = false, const bool p_allow_creating_chunks = true, const bool p_invalidate_texture_caches = true); int get_channel_index_info(const ChannelTypeInfo channel_type);