-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:
parent
fddfc78ec1
commit
3a767babc0
3 changed files with 232 additions and 92 deletions
3
dub.json
3
dub.json
|
|
@ -10,6 +10,9 @@
|
|||
"dflags-posix-ldc": [
|
||||
"-defaultlib=phobos2-ldc,druntime-ldc"
|
||||
],
|
||||
"versions": [
|
||||
"UpdateBySystems"
|
||||
],
|
||||
"configurations" : [
|
||||
{
|
||||
"name" : "library",
|
||||
|
|
|
|||
|
|
@ -89,25 +89,27 @@ class EntityManager
|
|||
{
|
||||
static if (isFunction!(__traits(getMember, Sys.EntitiesData, member)))
|
||||
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(typeof(__traits(getMember,Sys.EntitiesData, member)).sizeof > 1,"EntitiesData 'length' member can't be byte or ubyte.");
|
||||
static assert(isIntegral!(typeof(__traits(getMember, Sys.EntitiesData,
|
||||
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,
|
||||
Sys.EntitiesData, member)))))
|
||||
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 opt;
|
||||
uint absent;
|
||||
foreach (member; __traits(allMembers, Sys.EntitiesData))
|
||||
{
|
||||
if (member == "length" || is(typeof(__traits(getMember, Sys.EntitiesData,
|
||||
member)) == Entity[]) || is(typeof(__traits(getMember,
|
||||
if (member == "length" || is(typeof(__traits(getMember,
|
||||
Sys.EntitiesData, member)) == Entity[]) || is(typeof(__traits(getMember,
|
||||
Sys.EntitiesData, member)) == const(Entity)[]))
|
||||
{
|
||||
|
||||
|
|
@ -134,52 +136,66 @@ class EntityManager
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!has_att)req++;
|
||||
//ret ~= "req++;";
|
||||
if (!has_att)
|
||||
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)) &&
|
||||
isExpressions!(Sys.AbsentComponents))
|
||||
else static if (__traits(compiles, allSameType!(string, typeof(Sys.AbsentComponents)))
|
||||
&& allSameType!(string, typeof(Sys.AbsentComponents))
|
||||
&& isExpressions!(Sys.AbsentComponents))
|
||||
{
|
||||
absent += Sys.AbsentComponents.length;
|
||||
}
|
||||
}
|
||||
|
||||
if(req > 0)ret ~= "system.m_components = Mallocator.instance.makeArray!ushort("~req.to!string~");";
|
||||
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;";
|
||||
if (req > 0)
|
||||
ret ~= "system.m_components = Mallocator.instance.makeArray!ushort("
|
||||
~ req.to!string ~ ");";
|
||||
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;
|
||||
req = 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.");
|
||||
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)) &&
|
||||
isExpressions!(Sys.AbsentComponents))
|
||||
else static if (__traits(compiles, allSameType!(string, typeof(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))
|
||||
{
|
||||
if (member == "length" || is(typeof(__traits(getMember, Sys.EntitiesData,
|
||||
member)) == Entity[]) || is(typeof(__traits(getMember,
|
||||
if (member == "length" || is(typeof(__traits(getMember,
|
||||
Sys.EntitiesData, member)) == Entity[]) || is(typeof(__traits(getMember,
|
||||
Sys.EntitiesData, member)) == const(Entity)[]))
|
||||
{
|
||||
|
||||
|
|
@ -204,20 +220,22 @@ class EntityManager
|
|||
{
|
||||
if (att == "optional")
|
||||
{
|
||||
ret ~= "system.m_optional_components["~(opt++).to!string~"] = comp;}";
|
||||
ret ~= "system.m_optional_components[" ~ (opt++)
|
||||
.to!string ~ "] = comp;}";
|
||||
has_att = true;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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
|
||||
~ " = (cast(Entity*) block.dataBegin())[0 .. block.entities_count];";
|
||||
}
|
||||
else if(member == "length")
|
||||
else if (member == "length")
|
||||
{
|
||||
ret ~= "input_data." ~ member
|
||||
~ " = block.entities_count;";
|
||||
ret ~= "input_data." ~ member ~ " = block.entities_count;";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -392,6 +409,28 @@ class EntityManager
|
|||
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();
|
||||
|
|
@ -492,12 +531,34 @@ class EntityManager
|
|||
*/
|
||||
export void update()
|
||||
{
|
||||
foreach (info; &entities_infos.byValue)
|
||||
version (UpdateBySystems)
|
||||
{
|
||||
foreach (data; info.callers)
|
||||
foreach (caller; system_callers)
|
||||
{
|
||||
if (data.system.enabled)
|
||||
(cast(SytemFuncType) data.system.m_update)(data); //caller(call_data,null);
|
||||
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 (data; info.callers)
|
||||
{
|
||||
if (data.system.enabled)
|
||||
(cast(SytemFuncType) data.system.m_update)(data); //caller(call_data,null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -630,6 +691,16 @@ class EntityManager
|
|||
addEntityCaller(*info, i);
|
||||
}
|
||||
|
||||
version (UpdateBySystems)
|
||||
{
|
||||
foreach (uint i, ref system; systems)
|
||||
{
|
||||
if (system.m_update is null)
|
||||
continue;
|
||||
addSystemCaller(*info, i);
|
||||
}
|
||||
}
|
||||
|
||||
updateEntityCallers();
|
||||
|
||||
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];
|
||||
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 (id2; entity.components)
|
||||
{
|
||||
if(id == id2)return;
|
||||
if (id == id2)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -668,10 +740,56 @@ class EntityManager
|
|||
{
|
||||
foreach (i2, id2; entity.components)
|
||||
{
|
||||
if (id2 == id)goto is_;
|
||||
if (id2 == id)
|
||||
goto is_;
|
||||
}
|
||||
return;
|
||||
is_:
|
||||
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;
|
||||
is_:
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
|
|
@ -1002,7 +1120,7 @@ class EntityManager
|
|||
private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info)
|
||||
{
|
||||
EntitiesBlock* previous_block;
|
||||
EntitiesBlock* block = info.first_with_free_space;
|
||||
EntitiesBlock* block = info.last_block;
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
|
@ -1021,7 +1139,7 @@ class EntityManager
|
|||
block.prev_block = previous_block;
|
||||
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
|
||||
}
|
||||
// check if there is enought space
|
||||
|
|
@ -1034,7 +1152,6 @@ class EntityManager
|
|||
continue;
|
||||
}
|
||||
|
||||
info.first_with_free_space = block;
|
||||
break; // block exists and bounds check passed
|
||||
}
|
||||
|
||||
|
|
@ -1071,11 +1188,7 @@ class EntityManager
|
|||
void* data_begin = block.dataBegin();
|
||||
EntityInfo* info = block.type_info;
|
||||
|
||||
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;
|
||||
info.last_block.entities_count--;
|
||||
|
||||
static if (EntityID.sizeof == 8)
|
||||
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)
|
||||
{
|
||||
info.last_block = block.prev_block;
|
||||
if (info.first_block is block)
|
||||
{
|
||||
info.first_block = block.next_block;
|
||||
}
|
||||
if (info.first_with_free_space is block)
|
||||
{
|
||||
info.first_with_free_space = block.next_block; //info.first_block;
|
||||
info.first_block = null;
|
||||
}
|
||||
if (block.prev_block)
|
||||
{
|
||||
block.prev_block.next_block = block.next_block;
|
||||
}
|
||||
if (block.next_block)
|
||||
{
|
||||
block.next_block.prev_block = block.prev_block;
|
||||
block.prev_block.next_block = null;
|
||||
}
|
||||
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,10 +1317,18 @@ class EntityManager
|
|||
updateBlocks();
|
||||
removeEntities();
|
||||
changeEntites();
|
||||
foreach (ref system; instance.systems)
|
||||
|
||||
version (UpdateBySystems)
|
||||
{
|
||||
if (system.m_begin)
|
||||
system.m_begin(system.m_system_pointer);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ref system; instance.systems)
|
||||
{
|
||||
if (system.m_begin)
|
||||
system.m_begin(system.m_system_pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1223,11 +1337,19 @@ class EntityManager
|
|||
*/
|
||||
export void end()
|
||||
{
|
||||
foreach (ref system; instance.systems)
|
||||
version (UpdateBySystems)
|
||||
{
|
||||
if (system.m_end)
|
||||
system.m_end(system.m_system_pointer);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ref system; instance.systems)
|
||||
{
|
||||
if (system.m_end)
|
||||
system.m_end(system.m_system_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
updateBlocks();
|
||||
removeEntities();
|
||||
changeEntites();
|
||||
|
|
@ -1262,6 +1384,12 @@ class EntityManager
|
|||
*/
|
||||
struct EntityInfo
|
||||
{
|
||||
///Returns number of blocks
|
||||
uint blocksCount()
|
||||
{
|
||||
return last_block.id;
|
||||
}
|
||||
|
||||
///entity components
|
||||
ushort[] components;
|
||||
|
||||
|
|
@ -1279,8 +1407,8 @@ class EntityManager
|
|||
|
||||
///pointer to first block/page
|
||||
EntitiesBlock* first_block;
|
||||
///a hint for allocations
|
||||
EntitiesBlock* first_with_free_space; // a hint for allocations, should have empty space in it but doesn't have to
|
||||
///pointer to last block
|
||||
EntitiesBlock* last_block;
|
||||
///array of CallData. Contain data for System calls.
|
||||
Vector!(CallData) callers;
|
||||
}
|
||||
|
|
@ -1335,6 +1463,15 @@ class EntityManager
|
|||
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 sendSelfEvent = instance.event_manager.sendSelfEvent;
|
||||
|
|
@ -1357,7 +1494,7 @@ class EntityManager
|
|||
Vector!ubyte change_entities_list;
|
||||
|
||||
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) events_map;
|
||||
Vector!System systems;
|
||||
|
|
|
|||
|
|
@ -324,14 +324,14 @@ import std.meta;
|
|||
|
||||
//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();
|
||||
foreach (j; 0 .. 1_000)
|
||||
foreach (j; 0 .. 5_000)
|
||||
idss[j] = gEM.addEntity(tmpl).id;
|
||||
foreach (j; 0 .. 1_000)
|
||||
foreach (j; 0 .. 5_000)
|
||||
gEM.removeEntity(idss[j]);
|
||||
gEM.end();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue