Implemented chunk material invalidation support for TerrainLibraryMergerPCM.

This commit is contained in:
Relintai 2025-02-07 10:08:22 +01:00
parent e11e75e5ea
commit 2024a33ee9
8 changed files with 125 additions and 38 deletions

View File

@ -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) {

View File

@ -69,6 +69,12 @@ void TerrainLibraryMergerPCM::_material_cache_get_key(Ref<TerrainChunk> chunk) {
return;
}
int old_key = 0;
if (chunk->material_cache_key_has()) {
old_key = chunk->material_cache_key_get();
}
Vector<uint8_t> surfaces;
uint32_t size = chunk->get_data_size();
@ -98,6 +104,10 @@ void TerrainLibraryMergerPCM::_material_cache_get_key(Ref<TerrainChunk> 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<TerrainChunk> chunk) {
int hash = static_cast<int>(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<TerrainChunk> c
return;
}
int old_key = 0;
if (chunk->liquid_material_cache_key_has()) {
old_key = chunk->liquid_material_cache_key_get();
}
Vector<uint8_t> surfaces;
uint32_t size = chunk->get_data_size();
@ -266,6 +291,10 @@ void TerrainLibraryMergerPCM::_liquid_material_cache_get_key(Ref<TerrainChunk> 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<TerrainChunk> c
int hash = static_cast<int>(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<TerrainChunk> chunk) {
int old_key = 0;
if (chunk->prop_material_cache_key_has()) {
old_key = chunk->prop_material_cache_key_get();
}
Vector<uint64_t> props;
/*
@ -454,6 +498,10 @@ void TerrainLibraryMergerPCM::_prop_material_cache_get_key(Ref<TerrainChunk> 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<TerrainChunk> chu
int hash = static_cast<int>(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)) {

View File

@ -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<TerrainMaterialCache> 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<Texture> tex = _chunk->mesh_data_resource_get_texture(i);

View File

@ -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<TerrainMaterialCache> 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<TerrainMaterialCache> 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);
}
}
}
}

View File

@ -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<TerrainLibrary> 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");

View File

@ -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<TerrainLibrary> get_library();
void set_library(const Ref<TerrainLibrary> &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;

View File

@ -1037,7 +1037,7 @@ Ref<TerrainChunk> 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<Ref<TerrainChunk>>::Iterator iter = chunks_to_rebuild.begin(); iter.valid(); iter.next()) {
Ref<TerrainChunk> 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)));

View File

@ -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<TerrainChunk> get_chunk_at_world_data_position(const Vector2i &world_data_position);
Ref<TerrainChunk> 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);