diff --git a/source/ecs/entity.d b/source/ecs/entity.d index 0639b78..0096dc3 100644 --- a/source/ecs/entity.d +++ b/source/ecs/entity.d @@ -1,5 +1,7 @@ module ecs.entity; +import ecs.manager; + struct EntityID { uint id; @@ -13,6 +15,6 @@ struct Entity struct EntityTemplate { - uint[] components; - Entity* entity; + ubyte[] entity_data; + EntityManager.EntityInfo* info; } \ No newline at end of file diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 332f524..a718231 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -1,11 +1,17 @@ module ecs.manager; -import std.experimental.allocator.mallocator : Mallocator; +import std.experimental.allocator.mallocator : Mallocator, AlignedMallocator; import std.experimental.allocator; import std.traits; +import std.algorithm : max; +import std.conv : to; + +import core.stdc.string; import ecs.system; import ecs.entity; +import ecs.vector; +import ecs.hash_map; alias gEM = EntityManager.instance; @@ -19,94 +25,219 @@ class EntityManager void registerSystem(Sys)(int priority) { + alias types = Parameters!(Sys.update); + + static string genCall()() + { + string ret = "s.update("; + foreach(i,param;Parameters!(Sys.update)) + { + ret ~= "*cast(types["~i.to!string~"]*)(data_pointer + data.deltas["~i.to!string~"]),"; + } + ret ~= ");"; + return ret; + } + + static string genCompList()() + { + string ret; + foreach(i,param;Parameters!(Sys.update)) + { + ret ~= "system.components["~i.to!string~"] = components_map.get(types["~i.to!string~"].stringof);\n"; + } + return ret; + } + static void callUpdate(ref CallData data, void* entity) { - static if(hasMember!(Sys,"update")) + static if (hasMember!(Sys, "update")) { - Sys* s = cast(Sys*)data.system.system_pointer; - s.update(); + Sys* s = cast(Sys*) data.system.system_pointer; + + EntitiesBlock* block = data.info.first_block; + void* data_pointer = block.dataBegin(); + mixin(genCall()); + } } - + System system; - static if(hasMember!(Sys,"update")) + static if (hasMember!(Sys, "update")) { system.update = &callUpdate; } - system.system_pointer = cast(void*)Mallocator.instance.make!Sys; + system.system_pointer = cast(void*) Mallocator.instance.make!Sys; - if(systems is null) + system.components = Mallocator.instance.makeArray!uint(types.length); + mixin(genCompList()); + + if (systems is null) { systems = Mallocator.instance.makeArray!System(1); systems[0] = system; } else { - Mallocator.instance.expandArray(systems,1); - systems[$-1] = system; + Mallocator.instance.expandArray(systems, 1); + systems[$ - 1] = system; } } void registerComponent(Comp)() { - uint size = Comp.sizeof; + ushort size = Comp.sizeof; ComponentInfo info; info.size = size; - info.aligment = 8; + info.aligment = Comp.alignof;//8; - if(components is null) + if (components is null) { components = Mallocator.instance.makeArray!ComponentInfo(1); components[0] = info; } else { - Mallocator.instance.expandArray(components,1); - components[$-1] = info; + Mallocator.instance.expandArray(components, 1); + components[$ - 1] = info; } + + components_map.add(Comp.stringof,cast(uint)(components.length-1)); } void update() { - foreach(ref system;systems) + foreach(info;&entities_infos.byValue) { - if(system.update is null)continue; - CallData call_data = CallData(&system,null); - system.update(call_data,null); + foreach(data;info.callers) + { + data.system.update(data,null);//caller(call_data,null); + } } } - EntityTemplate* allocateTemplate(uint[] components_ids) + static void alignNum(ref ushort num, ushort aligment) + { + int reminder = num % aligment; + if (reminder != 0) + { + num += aligment - reminder; + } + } + + EntityTemplate* allocateTemplate(ushort[] components_ids) { - uint size = 0; - foreach(id;components_ids) + EntityInfo* info = entities_infos.get(components_ids, null); + if (info is null) { - size += components[id].size; + info = Mallocator.instance.make!EntityInfo; + + info.components = Mallocator.instance.makeArray(components_ids); + info.deltas = Mallocator.instance.makeArray!ushort(components_ids.length); + + info.size = EntityID.sizeof; + info.alignment = EntityID.alignof; + + foreach (i, id; components_ids) + { + info.alignment = max(info.alignment,components[id].aligment); + alignNum(info.size, components[id].aligment); + info.deltas[i] = info.size; + info.size += components[id].size; + } + alignNum(info.size, info.alignment); + + foreach(ref system;systems) + { + if(system.update is null)continue; + CallData call_data = CallData(&system,info, null); + call_data.deltas = Mallocator.instance.makeArray!ushort(system.components.length); + foreach(i,id;system.components) + { + foreach(i2,id2;info.components) + { + if(id2 == id) + { + call_data.deltas[i] = info.deltas[i2]; + break; + } + } + } + info.callers.add(call_data); + } + + entities_infos.add(info.components,info); } - size += EntityID.sizeof; - - ubyte[] entity_data = Mallocator.instance.makeArray!ubyte(size); EntityTemplate* temp = Mallocator.instance.make!EntityTemplate; - temp.components = components_ids; - temp.entity = cast(Entity*)entity_data.ptr; + temp.entity_data = Mallocator.instance.makeArray!ubyte(info.size); + temp.info = info; return temp; } + void freeTemplate(EntityTemplate* template_) + { + Mallocator.instance.dispose(template_.entity_data); + Mallocator.instance.dispose(template_); + } + + void addEntity(EntityTemplate* tmpl) + { + if(tmpl.info.first_block is null) + { + tmpl.info.first_block = cast(EntitiesBlock*) AlignedMallocator.instance.alignedAllocate(4096,4096); + *tmpl.info.first_block = EntitiesBlock(tmpl.info); + } + + void* start = tmpl.info.first_block.dataBegin() + tmpl.info.first_block.entities_count * tmpl.info.size; + memcpy(start, tmpl.entity_data.ptr, tmpl.info.size); + tmpl.info.first_block.entities_count++; + } + struct CallData { System* system; - uint[] deltas; + EntityInfo* info; + ushort[] deltas; } - struct ComponentInfo + struct ComponentInfo { - int size; - int aligment; + ushort size; + ushort aligment; } + struct EntityInfo + { + ushort[] components; + ushort[] deltas; + ushort alignment; + ushort size; + EntitiesBlock* first_block; + Vector!(CallData) callers; + } + + struct EntitiesBlock + { + + void* dataBegin() + { + ushort dif = EntitiesBlock.sizeof; + alignNum(dif,type_data.alignment); + return cast(void*)&this + dif; + } + + EntityInfo* type_data; + uint entities_count; + EntitiesBlock* next_block; + ///there is a loooot of data (4kB, pure magic) + } + + alias SytemFuncType = void function(ref EntityManager.CallData data, void* entity); + + HashMap!(ushort[], EntityInfo*) entities_infos; + HashMap!(string,uint) components_map; System[] systems; ComponentInfo[] components; __gshared EntityManager instance; -} \ No newline at end of file +} diff --git a/source/ecs/string_intern.d b/source/ecs/string_intern.d index d2c96e4..49dc21b 100644 --- a/source/ecs/string_intern.d +++ b/source/ecs/string_intern.d @@ -124,7 +124,7 @@ unittest { } unittest { - import mutils.container.hash_map : HashMap; + import ecs.hash_map : HashMap; HashMap!(StringIntern, StringIntern) map; diff --git a/source/ecs/system.d b/source/ecs/system.d index 57a33cf..14adefe 100644 --- a/source/ecs/system.d +++ b/source/ecs/system.d @@ -12,5 +12,7 @@ struct System ///pointer to system implementation void* system_pointer; + uint[] components; + void function(ref EntityManager.CallData data, void* entity) update; } \ No newline at end of file diff --git a/tests/tests.d b/tests/tests.d index 4168e5c..73acda3 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -32,6 +32,23 @@ unittest } } + struct TestComp2 + { + __gshared static int component_id; + short b; + ubyte a; + + static void serializeComponent(ref TestComp comp, SerializeVector output) + { + + } + + static void deserializeComponent(ref TestComp comp, ubyte[] data) + { + + } + } + struct TestSystem { @@ -40,12 +57,35 @@ unittest } - void update()//ref TestComp comp) + void update(ref TestComp test, ref TestComp2 test2)//ref TestComp comp) { - //comp.a+=1000; - //comp.b+=2000; - import std.stdio; - writeln("Jakis tekst!"); + import std.stdio; + writeln("Jakis tekst! ",test.b); + test.a+=1000; + test.b+=2000; + writeln("Jakis tekst! ",test.b); + test2.b += 2; + test2.a = 1; + writeln("Jakis tekst! ",test2.b); + } + + void handleEvent(Event event, ref TestComp comp) + { + + } + } + + struct TestSystem2 + { + + void initialize(ref TestComp comp) + { + + } + + void update(ref TestComp2 test2)//ref TestComp comp) + { + test2.b += 1000; } void handleEvent(Event event, ref TestComp comp) @@ -57,13 +97,26 @@ unittest EntityManager.initialize(); assert(gEM !is null); - + gEM.registerComponent!TestComp2; gEM.registerComponent!TestComp; + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(0); - uint[1] ids = [0]; + ushort[2] ids = [0,1]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); + *cast(EntityID*)tmpl.entity_data.ptr = EntityID(1,1); - gEM.registerSystem!TestSystem(0); + gEM.addEntity(tmpl); + gEM.addEntity(tmpl); + + //assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+24)) == EntityID(1,1)); + //assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+48)) == EntityID(1,1)); gEM.update(); + gEM.update(); + + import std.stdio; + writeln((cast(uint*)tmpl.info.first_block)[0..48]); + + gEM.freeTemplate(tmpl); } \ No newline at end of file