From 0e7217778f9f59bf81c53005f33270be654ab249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 29 Aug 2021 20:23:44 +0200 Subject: [PATCH] Implemented disposing for component --- GlitchyEngine/src/World/EcsWorld.bf | 126 +++++++++++++++++- .../src/World/IDisposableComponent.bf | 12 ++ 2 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 GlitchyEngine/src/World/IDisposableComponent.bf diff --git a/GlitchyEngine/src/World/EcsWorld.bf b/GlitchyEngine/src/World/EcsWorld.bf index 1077f45..f2d5ffd 100644 --- a/GlitchyEngine/src/World/EcsWorld.bf +++ b/GlitchyEngine/src/World/EcsWorld.bf @@ -15,8 +15,13 @@ namespace GlitchyEngine.World List _freeIndices = new List() ~ delete _; - typealias ComponentPoolEntry = (uint32 Id, ComponentPool Pool); + typealias DisposeFunction = function void(void* component); + + typealias ComponentPoolEntry = (uint32 Id, ComponentPool Pool, DisposeFunction DisposeFunction); Dictionary _componentPools = new .(); + + /// A list containing all pools whose components need to be disposed before removal. + List _disposingPools = new .() ~ delete _; public ~this() { @@ -35,13 +40,32 @@ namespace GlitchyEngine.World delete _entities; } - /** * Registers a new Component. */ public void Register() where T: struct { - _componentPools.Add(typeof(T), ((uint32)_componentPools.Count, new ComponentPool(sizeof(T), MaxEntities))); + uint32 id = (uint32)_componentPools.Count; + ComponentPool componentPool = new ComponentPool(sizeof(T), MaxEntities); + + _componentPools.Add(typeof(T), (id, componentPool, null)); + } + + /** + * Registers a new Component. + */ + public void Register() where T: struct, IDisposableComponent + { + uint32 id = (uint32)_componentPools.Count; + ComponentPool componentPool = new ComponentPool(sizeof(T), MaxEntities); + DisposeFunction disposeFunction = => T.DisposeComponent; + + _componentPools.Add(typeof(T), (id, componentPool, disposeFunction)); + + // We have to get the component from the dictionary so we get the reference + var poolInDictionary = ref _componentPools[typeof(T)]; + // Add to list of components that need disposing + _disposingPools.Add(&poolInDictionary); } /** @@ -79,12 +103,31 @@ namespace GlitchyEngine.World if(entity != listEntity.ID) return; + DisposeComponents(listEntity); + + // Invalidate entry and increment version listEntity.ID = Entity.CreateEntityID(Entity.InvalidEntity.Index, entity.Version + 1); - + _entities[entity.Index].ComponentMask.Clear(); _freeIndices.Add(entity.Index); + } - // TODO: add "destructor" for components + /// Disposes all components of the given entity. + private void DisposeComponents(BitmaskEntry listEntity) + { + var componentMask = listEntity.ComponentMask; + + for(let poolEntry in _disposingPools) + { + // Check if the entity has this component type + if(componentMask[poolEntry.Id]) + { + // Get pointer to component + void* component = poolEntry.Pool.Get(listEntity.ID.Index); + // Dispose component + poolEntry.DisposeFunction(component); + } + } } /** @@ -103,9 +146,11 @@ namespace GlitchyEngine.World ComponentPoolEntry entry; if(!_componentPools.TryGetValue(typeof(T), out entry)) { - entry = ((uint32)_componentPools.Count, new ComponentPool(sizeof(T), MaxEntities)); + Log.EngineLogger.Error($"Tried to assign unregistered component type {typeof(T)}"); + return null; + //entry = ((uint32)_componentPools.Count, new ComponentPool(sizeof(T), MaxEntities)); - _componentPools.Add(typeof(T), entry); + //_componentPools.Add(typeof(T), entry); } // TODO: maybe assert? @@ -139,6 +184,29 @@ namespace GlitchyEngine.World listEntity.ComponentMask[entry.Id] = false; } + + public void RemoveComponent(Entity entity) where T : struct, IDisposableComponent + { + if(entity.Index > _entities.Count) + return; + + var listEntity = ref _entities[entity.Index]; + if(entity != listEntity.ID) + return; + + ComponentPoolEntry entry; + if(!_componentPools.TryGetValue(typeof(T), out entry)) + return; + + // TODO: maybe assert? + if(!listEntity.ComponentMask[entry.Id]) + return; + + listEntity.ComponentMask[entry.Id] = false; + + // Get component and call dispose + T.DisposeComponent(entry.Pool.Get(entity.Index)); + } public T* GetComponent(Entity entity) where T : struct { @@ -247,5 +315,49 @@ namespace GlitchyEngine.World delete world; } + + /// A component to test automatic disposing of components. + private struct TestDisposingComponent : IDisposableComponent + { + public bool IsDisposed; + + public static void DisposeComponent(void* component) + { + Self* testComponent = (Self*)component; + testComponent.IsDisposed = true; + } + } + + /// Tets if disposing of components works correctly. + [Test] + public static void TestDisposing() + { + EcsWorld world = scope EcsWorld(); + world.Register(); + + // Test dispose on component removal + { + // Create entity with disposable component. + Entity entity = world.NewEntity(); + var component = world.AssignComponent(entity); + component.IsDisposed = false; + + world.RemoveComponent(entity); + + Test.Assert(component.IsDisposed == true, "The component was not disposed when the component was removed!"); + } + + // Test dispose on entity removal + { + // Create entity with disposable component. + Entity entity = world.NewEntity(); + var component = world.AssignComponent(entity); + component.IsDisposed = false; + + world.RemoveEntity(entity); + + Test.Assert(component.IsDisposed == true, "The component was not disposed when the entity was removed!"); + } + } } } diff --git a/GlitchyEngine/src/World/IDisposableComponent.bf b/GlitchyEngine/src/World/IDisposableComponent.bf new file mode 100644 index 0000000..4340615 --- /dev/null +++ b/GlitchyEngine/src/World/IDisposableComponent.bf @@ -0,0 +1,12 @@ +namespace GlitchyEngine.World +{ + /// Provides a mechanism for a component to release resources once it is being removed. + public interface IDisposableComponent + { + /** @brief Disposes of the data of the given component. + * @Note This function will be called when the given component was removed from the entity + * or when the entity itself was removed. + */ + static void DisposeComponent(void* component); + } +}