-call update by Systems instead of EntityTypes (version UpdateBySystems)

-memory packing (every block in EntityType except last one are always full)
This commit is contained in:
Mergul 2018-10-10 18:35:20 +02:00
parent fddfc78ec1
commit 3a767babc0
3 changed files with 232 additions and 92 deletions

View file

@ -10,6 +10,9 @@
"dflags-posix-ldc": [ "dflags-posix-ldc": [
"-defaultlib=phobos2-ldc,druntime-ldc" "-defaultlib=phobos2-ldc,druntime-ldc"
], ],
"versions": [
"UpdateBySystems"
],
"configurations" : [ "configurations" : [
{ {
"name" : "library", "name" : "library",

View file

@ -89,25 +89,27 @@ class EntityManager
{ {
static if (isFunction!(__traits(getMember, Sys.EntitiesData, member))) static if (isFunction!(__traits(getMember, Sys.EntitiesData, member)))
static assert(0, "EntitiesData can't have any function!"); static assert(0, "EntitiesData can't have any function!");
else static if(member == "length") else static if (member == "length")
{ {
static assert(isIntegral!(typeof(__traits(getMember,Sys.EntitiesData, member))),"EntitiesData 'length' member must be integral type."); static assert(isIntegral!(typeof(__traits(getMember, Sys.EntitiesData,
static assert(typeof(__traits(getMember,Sys.EntitiesData, member)).sizeof > 1,"EntitiesData 'length' member can't be byte or ubyte."); member))), "EntitiesData 'length' member must be integral type.");
static assert(typeof(__traits(getMember, Sys.EntitiesData, member))
.sizeof > 1, "EntitiesData 'length' member can't be byte or ubyte.");
} }
else static if (!(isArray!(typeof(__traits(getMember, else static if (!(isArray!(typeof(__traits(getMember,
Sys.EntitiesData, member))))) Sys.EntitiesData, member)))))
static assert(0, "EntitiesData members should be arrays of elements!"); static assert(0, "EntitiesData members should be arrays of elements!");
} }
string ret;// = "ushort comp;uint req;uint opt;uint absent;"; string ret; // = "ushort comp;uint req;uint opt;uint absent;";
uint req; uint req;
uint opt; uint opt;
uint absent; uint absent;
foreach (member; __traits(allMembers, Sys.EntitiesData)) foreach (member; __traits(allMembers, Sys.EntitiesData))
{ {
if (member == "length" || is(typeof(__traits(getMember, Sys.EntitiesData, if (member == "length" || is(typeof(__traits(getMember,
member)) == Entity[]) || is(typeof(__traits(getMember, Sys.EntitiesData, member)) == Entity[]) || is(typeof(__traits(getMember,
Sys.EntitiesData, member)) == const(Entity)[])) Sys.EntitiesData, member)) == const(Entity)[]))
{ {
@ -134,52 +136,66 @@ class EntityManager
break; break;
} }
} }
if (!has_att)req++; if (!has_att)
req++;
//ret ~= "req++;"; //ret ~= "req++;";
} }
} }
} }
static if(__traits(hasMember, Sys, "AbsentComponents")) static if (__traits(hasMember, Sys, "AbsentComponents"))
{ {
static if(is(Sys.AbsentComponents == enum)) static if (is(Sys.AbsentComponents == enum))
{ {
absent += (Fields!(Sys.AbsentComponents)).length;//static assert(0,"Enum AbsentComponents are not implemented yet."); absent += (Fields!(Sys.AbsentComponents)).length; //static assert(0,"Enum AbsentComponents are not implemented yet.");
} }
else static if(__traits(compiles,allSameType!(string,typeof(Sys.AbsentComponents))) && allSameType!(string,typeof(Sys.AbsentComponents)) && else static if (__traits(compiles, allSameType!(string, typeof(Sys.AbsentComponents)))
isExpressions!(Sys.AbsentComponents)) && allSameType!(string, typeof(Sys.AbsentComponents))
&& isExpressions!(Sys.AbsentComponents))
{ {
absent += Sys.AbsentComponents.length; absent += Sys.AbsentComponents.length;
} }
} }
if(req > 0)ret ~= "system.m_components = Mallocator.instance.makeArray!ushort("~req.to!string~");"; if (req > 0)
if(opt > 0)ret ~= "system.m_optional_components = Mallocator.instance.makeArray!ushort("~opt.to!string~");"; ret ~= "system.m_components = Mallocator.instance.makeArray!ushort("
if(absent > 0)ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort("~absent.to!string~");"; ~ req.to!string ~ ");";
ret ~= "ushort comp;";//uint opt = 0;uint req = 0;uint absent = 0;"; if (opt > 0)
ret ~= "system.m_optional_components = Mallocator.instance.makeArray!ushort("
~ opt.to!string ~ ");";
if (absent > 0)
ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort("
~ absent.to!string ~ ");";
ret ~= "ushort comp;"; //uint opt = 0;uint req = 0;uint absent = 0;";
opt = 0; opt = 0;
req = 0; req = 0;
absent = 0; absent = 0;
static if(__traits(hasMember, Sys, "AbsentComponents")) static if (__traits(hasMember, Sys, "AbsentComponents"))
{ {
static if(is(Sys.AbsentComponents == enum)) static if (is(Sys.AbsentComponents == enum))
{ {
//static assert(0,"Enum AbsentComponents are not implemented yet."); //static assert(0,"Enum AbsentComponents are not implemented yet.");
foreach(str;Fields!(Sys.AbsentComponents))ret ~= "system.m_absent_components["~(absent++).to!string~"] = components_map.get(\""~str.stringof~"\", ushort.max);"; foreach (str; Fields!(Sys.AbsentComponents))
ret ~= "system.m_absent_components[" ~ (absent++)
.to!string ~ "] = components_map.get(\""
~ str.stringof ~ "\", ushort.max);";
} }
else static if(__traits(compiles,allSameType!(string,typeof(Sys.AbsentComponents))) && allSameType!(string,typeof(Sys.AbsentComponents)) && else static if (__traits(compiles, allSameType!(string, typeof(Sys.AbsentComponents)))
isExpressions!(Sys.AbsentComponents)) && allSameType!(string, typeof(Sys.AbsentComponents))
&& isExpressions!(Sys.AbsentComponents))
{ {
foreach(str;Sys.AbsentComponents)ret ~= "system.m_absent_components["~(absent++).to!string~"] = components_map.get(\""~str~"\", ushort.max);"; foreach (str; Sys.AbsentComponents)
ret ~= "system.m_absent_components[" ~ (absent++)
.to!string ~ "] = components_map.get(\"" ~ str ~ "\", ushort.max);";
} }
} }
foreach (member; __traits(allMembers, Sys.EntitiesData)) foreach (member; __traits(allMembers, Sys.EntitiesData))
{ {
if (member == "length" || is(typeof(__traits(getMember, Sys.EntitiesData, if (member == "length" || is(typeof(__traits(getMember,
member)) == Entity[]) || is(typeof(__traits(getMember, Sys.EntitiesData, member)) == Entity[]) || is(typeof(__traits(getMember,
Sys.EntitiesData, member)) == const(Entity)[])) Sys.EntitiesData, member)) == const(Entity)[]))
{ {
@ -204,20 +220,22 @@ class EntityManager
{ {
if (att == "optional") if (att == "optional")
{ {
ret ~= "system.m_optional_components["~(opt++).to!string~"] = comp;}"; ret ~= "system.m_optional_components[" ~ (opt++)
.to!string ~ "] = comp;}";
has_att = true; has_att = true;
break; break;
} }
else if (att == "absent") else if (att == "absent")
{ {
ret ~= "system.m_absent_components["~(absent++).to!string~"] = comp;}"; ret ~= "system.m_absent_components[" ~ (absent++)
.to!string ~ "] = comp;}";
has_att = true; has_att = true;
break; break;
} }
} }
if (!has_att) if (!has_att)
{ {
ret ~= "system.m_components["~(req++).to!string~"] = comp;}"; ret ~= "system.m_components[" ~ (req++).to!string ~ "] = comp;}";
} }
} }
} }
@ -256,10 +274,9 @@ class EntityManager
ret ~= "input_data." ~ member ret ~= "input_data." ~ member
~ " = (cast(Entity*) block.dataBegin())[0 .. block.entities_count];"; ~ " = (cast(Entity*) block.dataBegin())[0 .. block.entities_count];";
} }
else if(member == "length") else if (member == "length")
{ {
ret ~= "input_data." ~ member ret ~= "input_data." ~ member ~ " = block.entities_count;";
~ " = block.entities_count;";
} }
else else
{ {
@ -392,6 +409,28 @@ class EntityManager
addEntityCaller(*info, cast(uint) systems.length - 1); addEntityCaller(*info, cast(uint) systems.length - 1);
} }
} }
version (UpdateBySystems)
{
bool added = false;
foreach (i, ref caller; system_callers)
{
if (systems[caller.system_id].priority > priority)
{
SystemCaller* sys_caller = Mallocator.instance.make!SystemCaller;
sys_caller.system_id = Sys.system_id;
system_callers.add(sys_caller, i);
added = true;
break;
}
}
if (!added)
{
SystemCaller* sys_caller = Mallocator.instance.make!SystemCaller;
sys_caller.system_id = Sys.system_id;
system_callers.add(sys_caller);
}
}
} }
updateEntityCallers(); updateEntityCallers();
@ -491,6 +530,27 @@ class EntityManager
*Update systems. Should be called only between begin() and end(). *Update systems. Should be called only between begin() and end().
*/ */
export void update() export void update()
{
version (UpdateBySystems)
{
foreach (caller; system_callers)
{
System* sys = &systems[caller.system_id];
if (sys.enabled)
{
if (sys.m_begin)
sys.m_begin(sys.m_system_pointer);
foreach (info; caller.infos)
{
CallData data = CallData(caller.system_id, sys, info);
(cast(SytemFuncType) data.system.m_update)(data);
}
if (sys.m_end)
sys.m_end(sys.m_system_pointer);
}
}
}
else
{ {
foreach (info; &entities_infos.byValue) foreach (info; &entities_infos.byValue)
{ {
@ -501,6 +561,7 @@ class EntityManager
} }
} }
} }
}
static void alignNum(ref ushort num, ushort alignment) static void alignNum(ref ushort num, ushort alignment)
{ {
@ -630,6 +691,16 @@ class EntityManager
addEntityCaller(*info, i); addEntityCaller(*info, i);
} }
version (UpdateBySystems)
{
foreach (uint i, ref system; systems)
{
if (system.m_update is null)
continue;
addSystemCaller(*info, i);
}
}
updateEntityCallers(); updateEntityCallers();
entities_infos.add(info.components, info); entities_infos.add(info.components, info);
@ -648,18 +719,19 @@ class EntityManager
} }
} }
export void addEntityCaller(ref EntityInfo entity, uint system_id) export void addSystemCaller(ref EntityInfo entity, uint system_id)
{ {
System* system = &systems[system_id]; System* system = &systems[system_id];
CallData call_data = CallData(system_id, system, &entity); //CallData call_data = CallData(system_id, system, &entity);
if(system.m_absent_components) if (system.m_absent_components)
{ {
foreach (id; system.m_absent_components) foreach (id; system.m_absent_components)
{ {
foreach (id2; entity.components) foreach (id2; entity.components)
{ {
if(id == id2)return; if (id == id2)
return;
} }
} }
} }
@ -668,7 +740,53 @@ class EntityManager
{ {
foreach (i2, id2; entity.components) foreach (i2, id2; entity.components)
{ {
if (id2 == id)goto is_; if (id2 == id)
goto is_;
}
return;
is_:
}
uint index = 0;
for (; index < system_callers.length; index++)
{
if (system_callers[index].system_id == system_id)
break;
}
system_callers[index].infos.add(&entity);
/*for (; index < entity.callers.length; index++)
{
CallData* caller = &entity.callers[index];
if (caller.system.priority >= call_data.system.priority)
break;
}
entity.callers.add(call_data, index);*/
}
export void addEntityCaller(ref EntityInfo entity, uint system_id)
{
System* system = &systems[system_id];
CallData call_data = CallData(system_id, system, &entity);
if (system.m_absent_components)
{
foreach (id; system.m_absent_components)
{
foreach (id2; entity.components)
{
if (id == id2)
return;
}
}
}
foreach (id; system.m_components)
{
foreach (i2, id2; entity.components)
{
if (id2 == id)
goto is_;
} }
return; return;
is_: is_:
@ -1002,7 +1120,7 @@ class EntityManager
private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info)
{ {
EntitiesBlock* previous_block; EntitiesBlock* previous_block;
EntitiesBlock* block = info.first_with_free_space; EntitiesBlock* block = info.last_block;
while (1) while (1)
{ {
@ -1021,7 +1139,7 @@ class EntityManager
block.prev_block = previous_block; block.prev_block = previous_block;
block.id = cast(ushort)(previous_block.id + 1); block.id = cast(ushort)(previous_block.id + 1);
} }
info.first_with_free_space = block; info.last_block = block;
break; // new block certainly has free space break; // new block certainly has free space
} }
// check if there is enought space // check if there is enought space
@ -1034,7 +1152,6 @@ class EntityManager
continue; continue;
} }
info.first_with_free_space = block;
break; // block exists and bounds check passed break; // block exists and bounds check passed
} }
@ -1071,11 +1188,7 @@ class EntityManager
void* data_begin = block.dataBegin(); void* data_begin = block.dataBegin();
EntityInfo* info = block.type_info; EntityInfo* info = block.type_info;
block.entities_count--; info.last_block.entities_count--;
//set "first_with_free_space" if should it be
if (info.first_with_free_space.id > block.id)
info.first_with_free_space = block;
static if (EntityID.sizeof == 8) static if (EntityID.sizeof == 8)
uint pos = cast(uint)((cast(void*) entity - data_begin) >> 3); uint pos = cast(uint)((cast(void*) entity - data_begin) >> 3);
@ -1096,43 +1209,36 @@ class EntityManager
} }
} }
if(block !is info.last_block || pos != block.entities_count)
{
foreach (comp; info.components)
{
void* src = cast(void*) info.last_block + info.deltas[comp];
void* dst = cast(void*) block + info.deltas[comp];
uint size = components[comp].size;
memcpy(dst + pos * size, src + info.last_block.entities_count * size, size);
}
block = info.last_block;
entity.id = *cast(EntityID*)(block.dataBegin() + block.entities_count * EntityID.sizeof);
entity.updateID();
}
block = info.last_block;
if (block.entities_count == 0) if (block.entities_count == 0)
{ {
info.last_block = block.prev_block;
if (info.first_block is block) if (info.first_block is block)
{ {
info.first_block = block.next_block; info.first_block = null;
}
if (info.first_with_free_space is block)
{
info.first_with_free_space = block.next_block; //info.first_block;
} }
if (block.prev_block) if (block.prev_block)
{ {
block.prev_block.next_block = block.next_block; block.prev_block.next_block = null;
}
if (block.next_block)
{
block.next_block.prev_block = block.prev_block;
} }
allocator.freeBlock(block); allocator.freeBlock(block);
return;
} }
if (pos == block.entities_count)
return;
foreach (comp; info.components)
{
void* ptr = cast(void*) block + info.deltas[comp];
uint size = components[comp].size;
memcpy(ptr + pos * size, ptr + block.entities_count * size, size);
}
entity.id = *cast(EntityID*)(data_begin + block.entities_count * EntityID.sizeof);
//update pointer for moved entity ID
//entity = cast(Entity*) dst;
entity.updateID();
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -1211,23 +1317,39 @@ class EntityManager
updateBlocks(); updateBlocks();
removeEntities(); removeEntities();
changeEntites(); changeEntites();
version (UpdateBySystems)
{
}
else
{
foreach (ref system; instance.systems) foreach (ref system; instance.systems)
{ {
if (system.m_begin) if (system.m_begin)
system.m_begin(system.m_system_pointer); system.m_begin(system.m_system_pointer);
} }
} }
}
/************************************************************************************************************************ /************************************************************************************************************************
*End of update process. Should be called after every update function. *End of update process. Should be called after every update function.
*/ */
export void end() export void end()
{
version (UpdateBySystems)
{
}
else
{ {
foreach (ref system; instance.systems) foreach (ref system; instance.systems)
{ {
if (system.m_end) if (system.m_end)
system.m_end(system.m_system_pointer); system.m_end(system.m_system_pointer);
} }
}
updateBlocks(); updateBlocks();
removeEntities(); removeEntities();
changeEntites(); changeEntites();
@ -1262,6 +1384,12 @@ class EntityManager
*/ */
struct EntityInfo struct EntityInfo
{ {
///Returns number of blocks
uint blocksCount()
{
return last_block.id;
}
///entity components ///entity components
ushort[] components; ushort[] components;
@ -1279,8 +1407,8 @@ class EntityManager
///pointer to first block/page ///pointer to first block/page
EntitiesBlock* first_block; EntitiesBlock* first_block;
///a hint for allocations ///pointer to last block
EntitiesBlock* first_with_free_space; // a hint for allocations, should have empty space in it but doesn't have to EntitiesBlock* last_block;
///array of CallData. Contain data for System calls. ///array of CallData. Contain data for System calls.
Vector!(CallData) callers; Vector!(CallData) callers;
} }
@ -1335,6 +1463,15 @@ class EntityManager
EntityManager.EntityInfo* info; EntityManager.EntityInfo* info;
} }
struct SystemCaller
{
uint system_id;
System* system;
Vector!(EntityInfo*) infos;
}
Vector!(SystemCaller*) system_callers;
alias SytemFuncType = void function(ref EntityManager.CallData data); alias SytemFuncType = void function(ref EntityManager.CallData data);
//alias sendSelfEvent = instance.event_manager.sendSelfEvent; //alias sendSelfEvent = instance.event_manager.sendSelfEvent;
@ -1357,7 +1494,7 @@ class EntityManager
Vector!ubyte change_entities_list; Vector!ubyte change_entities_list;
HashMap!(ushort[], EntityInfo*) entities_infos; HashMap!(ushort[], EntityInfo*) entities_infos;
HashMap!(const (char)[], ushort) systems_map; HashMap!(const(char)[], ushort) systems_map;
HashMap!(string, ushort) components_map; HashMap!(string, ushort) components_map;
HashMap!(string, ushort) events_map; HashMap!(string, ushort) events_map;
Vector!System systems; Vector!System systems;

View file

@ -324,14 +324,14 @@ import std.meta;
//foreach(i; 0..1_000_000)gEM.removeEntity(gEM.addEntity(tmpl).id); //foreach(i; 0..1_000_000)gEM.removeEntity(gEM.addEntity(tmpl).id);
EntityID[1000] idss; EntityID[5000] idss;
foreach (i; 0 .. 1_000) foreach (i; 0 .. 200)
{ {
gEM.begin(); gEM.begin();
foreach (j; 0 .. 1_000) foreach (j; 0 .. 5_000)
idss[j] = gEM.addEntity(tmpl).id; idss[j] = gEM.addEntity(tmpl).id;
foreach (j; 0 .. 1_000) foreach (j; 0 .. 5_000)
gEM.removeEntity(idss[j]); gEM.removeEntity(idss[j]);
gEM.end(); gEM.end();
} }