From 43f8755a39da1d795a01dc916365973ffeef4f26 Mon Sep 17 00:00:00 2001 From: Mergul Date: Fri, 22 May 2020 15:44:31 +0200 Subject: [PATCH] Fixed ECS bug related to adding/removing entities inside onAdd/onRemove entity callback Now whole committing process is called in specific order: - UpdateBlocks - ChangeEntities - RemoveEntities - HandleEvents Whole process is repeated until there will be no more changes to commit --- source/bubel/ecs/manager.d | 178 +++++++++++++++++++++++++------------ 1 file changed, 123 insertions(+), 55 deletions(-) diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 7b6a983..dc3b9f5 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -2074,10 +2074,10 @@ export struct EntityManager { ThreadData* data = &threads[threadID]; uint num = cast(uint) del_ids.length; - data.change_entities_list.add(0); - data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); - data.change_entities_list.add((cast(ubyte*)&num)[0 .. uint.sizeof]); - data.change_entities_list.add((cast(ubyte*) del_ids.ptr)[0 .. num * 2]); + data.changeEntitiesList.add(0); + data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); + data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); + data.changeEntitiesList.add((cast(ubyte*) del_ids.ptr)[0 .. num * 2]); } private void __removeComponents(EntityID entity_id, ushort[] del_ids) @@ -2349,13 +2349,13 @@ export struct EntityManager } ThreadData* data = &threads[threadID]; - data.change_entities_list.add(cast(ubyte) 1u); - data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); - data.change_entities_list.add((cast(ubyte*)&num)[0 .. uint.sizeof]); - data.change_entities_list.add(cast(ubyte[]) new_ids); + data.changeEntitiesList.add(cast(ubyte) 1u); + data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); + data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); + data.changeEntitiesList.add(cast(ubyte[]) new_ids); static foreach (i, comp; comps) { - data.change_entities_list.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); + data.changeEntitiesList.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); } //__addComponents(entity_id, new_ids, pointers); @@ -2416,7 +2416,7 @@ export struct EntityManager } if (new_index == 1) - threads[threadID].blocks_to_update.add(new_block); + threads[threadID].blockToUpdate.add(new_block); Entity* new_entity = cast(Entity*) start; //add_mutex.lock_nothrow(); @@ -2466,7 +2466,7 @@ export struct EntityManager } if (index == 1) - threads[threadID].blocks_to_update.add(block); + threads[threadID].blockToUpdate.add(block); Entity* entity = cast(Entity*) start; //add_mutex.lock_nothrow(); @@ -2557,7 +2557,7 @@ export struct EntityManager */ export void removeEntity(EntityID id) { - threads[threadID].entities_to_remove.add(id); + threads[threadID].entitesToRemove.add(id); } private void __removeEntity(EntityID id) nothrow @nogc @@ -2646,47 +2646,51 @@ export struct EntityManager return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(m_page_size - 1))); } - private void changeEntities() + private bool changeEntities() { - foreach (ref thread; threads) + bool has_work = false; + //foreach (ref ThreadData thread; threads)thread.swapToChange(); + foreach (ref ThreadData thread; threads) { uint index = 0; - uint len = cast(uint) thread.change_entities_list.length; + uint len = cast(uint) thread.changeEntitiesListPrev.length; + if(len)has_work = true; void*[32] pointers; // = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; while (index < len) { - if (!thread.change_entities_list[index++]) + if (!thread.changeEntitiesListPrev[index++]) { - EntityID id = *cast(EntityID*)&thread.change_entities_list[index]; + EntityID id = *cast(EntityID*)&thread.changeEntitiesListPrev[index]; index += EntityID.sizeof; - uint num = *cast(uint*)&thread.change_entities_list[index]; + uint num = *cast(uint*)&thread.changeEntitiesListPrev[index]; index += uint.sizeof; ushort[] ids; // = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; - ids = (cast(ushort*)&thread.change_entities_list[index])[0 .. num]; + ids = (cast(ushort*)&thread.changeEntitiesListPrev[index])[0 .. num]; index += ushort.sizeof * num; __removeComponents(id, ids); } else { - EntityID id = *cast(EntityID*)&thread.change_entities_list[index]; + EntityID id = *cast(EntityID*)&thread.changeEntitiesListPrev[index]; index += EntityID.sizeof; - uint num = *cast(uint*)&thread.change_entities_list[index]; + uint num = *cast(uint*)&thread.changeEntitiesListPrev[index]; index += uint.sizeof; ushort[] ids; // = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; - ids = (cast(ushort*)&thread.change_entities_list[index])[0 .. num]; + ids = (cast(ushort*)&thread.changeEntitiesListPrev[index])[0 .. num]; index += ushort.sizeof * num; //void*[] pointers = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; foreach (i; 0 .. num) { - pointers[i] = &thread.change_entities_list[index]; + pointers[i] = &thread.changeEntitiesListPrev[index]; index += components[ids[i]].size; } __addComponents(id, ids, pointers[0 .. num]); } } - thread.change_entities_list.clear(); + thread.changeEntitiesListPrev.clear(); } + return has_work; } private void callAddEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow @@ -2767,11 +2771,15 @@ export struct EntityManager (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_change_entity)(data); } - private void updateBlocks() + private bool updateBlocks() { - foreach (ref thread; threads) + bool has_work = false; + //foreach (ref ThreadData thread; threads)thread.swapToUpdate(); + foreach (ref ThreadData thread; threads) { - foreach (block; thread.blocks_to_update) + //thread.swapToUpdate(); + if(thread.blockToUpdatePrev.length)has_work = true; + foreach (block; thread.blockToUpdatePrev) { EntityInfo* info = block.type_info; ushort entities_count = block.entities_count; @@ -2786,30 +2794,35 @@ export struct EntityManager { callAddEntityListeners(info, block, entities_count, block.entities_count); } - } - thread.blocks_to_update.clear(); + thread.blockToUpdatePrev.clear(); } + return has_work; } - private void removeEntities() nothrow @nogc + private bool removeEntities() nothrow @nogc { - foreach (i, ref thread; threads) + bool has_work = false; + //foreach (ref ThreadData thread; threads)thread.swapToRemove(); + foreach (ref ThreadData thread; threads) { - foreach (id; thread.entities_to_remove) + if(thread.entitiesToRemovePrev.length)has_work = true; + foreach (id; thread.entitiesToRemovePrev) { __removeEntity(id); } - thread.entities_to_remove.clear(); + thread.entitiesToRemovePrev.clear(); } + return has_work; } - private void updateEvents() nothrow @nogc + private bool updateEvents() nothrow @nogc { - bool empty = true; - while (1) - { - event_manager.swapCurrent(); + bool has_work = false; + // bool empty = true; + //while (1) + //{ + //event_manager.swapCurrent(); uint current_index; if (event_manager.current_index == 0) current_index = cast(uint) threads.length; @@ -2821,8 +2834,11 @@ export struct EntityManager .. current_index + threads.length]) { EventManager.EventBlock* block = first_block; - if (block) - empty = false; + if (block)has_work = true; + // { + // has_work = true; + // //empty = false; + // } while (block) { EventCallData call_data; @@ -2856,23 +2872,37 @@ export struct EntityManager } } } - if (empty) - break; - empty = true; + // if (empty) + // break; + // empty = true; + //} + return has_work; + } + + private void swapData() nothrow @nogc + { + event_manager.swapCurrent(); + foreach(ref ThreadData thread; threads) + { + thread.swapData(); } } export void commit() { - id_manager.optimize(); - updateBlocks(); - changeEntities(); - - updateEvents(); + bool has_work = true; + while(has_work) + { + swapData(); - id_manager.optimize(); - updateBlocks(); - removeEntities(); + has_work = false; + id_manager.optimize(); + has_work |= updateBlocks(); + has_work |= changeEntities(); + has_work |= removeEntities(); + + has_work |= updateEvents(); + } event_manager.clearEvents(); } @@ -3445,10 +3475,48 @@ export struct EntityManager struct ThreadData { - Vector!EntityID entities_to_remove; - //Vector!ubyte change_entities_list; - SimpleVector change_entities_list; - Vector!(EntitiesBlock*) blocks_to_update; + ref Vector!EntityID entitesToRemove() @nogc nothrow + { + return entities_to_remove[data_index]; + } + + ref SimpleVector changeEntitiesList() @nogc nothrow + { + return change_entities_list[data_index]; + } + + ref Vector!(EntitiesBlock*) blockToUpdate() @nogc nothrow + { + return blocks_to_update[data_index]; + } + + ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow + { + return entities_to_remove[1 - data_index]; + } + + ref SimpleVector changeEntitiesListPrev() @nogc nothrow + { + return change_entities_list[1 - data_index]; + } + + ref Vector!(EntitiesBlock*) blockToUpdatePrev() @nogc nothrow + { + return blocks_to_update[1 - data_index]; + } + + private: + + void swapData() @nogc nothrow + { + data_index = cast(ubyte)(1 - data_index); + } + + Vector!EntityID[2] entities_to_remove; + SimpleVector[2] change_entities_list; + Vector!(EntitiesBlock*)[2] blocks_to_update; + + ubyte data_index = 0; } export struct UpdatePass