-Systems, Components and Events now must have proper mixin. Mixins are located in ecs.core module. (i.e. mixin ECS.Component)

-Multithreading support:
 *multithreaded update - updateMT(), function generates jobs to execute
 *added dispatch callback function to dispatch generated jobs (setJobDispachFunc)
 *added getID callback for get thread ID by EntityManager
-added Job structure. Job has one function "execute()".
-calling partial info update (required to multithreading)
-multithreaded removeEntity, addCompoenents, removeComponents. Every thread has own data and remove/change lists.
-multithreaded addEntity (WIP)
-fixed issue with duplicating components
-simpler and faster "findBlockWithFreeSpace" function
-CallDataAllocator, allocator for CallDatas (used for Jobs)
-fixed some bugs/issues
This commit is contained in:
Mergul 2018-10-14 16:59:46 +02:00
parent 3a767babc0
commit 5dd24b6462
6 changed files with 519 additions and 135 deletions

29
source/ecs/core.d Normal file
View file

@ -0,0 +1,29 @@
module ecs.core;
public import ecs.manager;
static struct ECS
{
mixin template System(uint jobs_count = 32)
{
__gshared ushort system_id;
EntityManager.Job[] _ecs_jobs;
void __ecsInitialize()
{
import std.experimental.allocator.mallocator;
import std.experimental.allocator;
_ecs_jobs = Mallocator.instance.makeArray!(EntityManager.Job)(jobs_count);
}
}
mixin template Component()
{
__gshared ushort component_id;
}
mixin template Event()
{
__gshared ushort event_id;
}
}

View file

@ -8,6 +8,7 @@ import std.traits;
import core.stdc.stdlib; import core.stdc.stdlib;
import core.stdc.string; import core.stdc.string;
import core.atomic;
import ecs.entity; import ecs.entity;
import ecs.block_allocator; import ecs.block_allocator;
@ -22,11 +23,10 @@ alias SerializeVector = ecs.vector.Vector!ubyte;
class EntityManager class EntityManager
{ {
export static void initialize(uint threads_count)
export static void initialize()
{ {
if (instance is null) if (instance is null)
instance = Mallocator.instance.make!EntityManager; instance = Mallocator.instance.make!EntityManager(threads_count);
} }
export static void destroy() export static void destroy()
@ -42,7 +42,7 @@ class EntityManager
foreach (ref system; instance.systems) foreach (ref system; instance.systems)
{ {
if (system.m_destroy) if (system.m_destroy)
system.m_destroy(system.m_system_pointer); (cast(void function(void*))system.m_destroy)(system.m_system_pointer);
} }
Mallocator.instance.dispose(instance); Mallocator.instance.dispose(instance);
@ -52,8 +52,10 @@ class EntityManager
/************************************************************************************************************************ /************************************************************************************************************************
*Default constructor. *Default constructor.
*/ */
this() this(uint threads_count)
{ {
if(threads_count == 0)threads_count = 0;
threads = Mallocator.instance.makeArray!ThreadData(threads_count);
//event_manager = EventManager(this); //event_manager = EventManager(this);
//event_manager.manager = this; //event_manager.manager = this;
} }
@ -272,11 +274,11 @@ class EntityManager
Sys.EntitiesData, member)) == const(Entity)[])) Sys.EntitiesData, member)) == const(Entity)[]))
{ {
ret ~= "input_data." ~ member ret ~= "input_data." ~ member
~ " = (cast(Entity*) block.dataBegin())[0 .. block.entities_count];"; ~ " = (cast(Entity*) block.dataBegin())[offset .. entities_count];";
} }
else if (member == "length") else if (member == "length")
{ {
ret ~= "input_data." ~ member ~ " = block.entities_count;"; ret ~= "input_data." ~ member ~ " = cast(typeof(input_data.length))(entities_count - offset);";
} }
else else
{ {
@ -292,7 +294,7 @@ class EntityManager
~ opt.to!string ~ "]] != 0)input_data." ~ member ~ opt.to!string ~ "]] != 0)input_data." ~ member
~ " = (cast(ForeachType!(typeof(Sys.EntitiesData." ~ member ~ " = (cast(ForeachType!(typeof(Sys.EntitiesData." ~ member
~ "))*)(cast(void*) block + info.deltas[ data.system.m_optional_components[" ~ "))*)(cast(void*) block + info.deltas[ data.system.m_optional_components["
~ opt.to!string ~ "]]))[0 .. block.entities_count]; ~ opt.to!string ~ "]]))[offset .. entities_count];
else input_data." else input_data."
~ member ~ " = null;"; ~ member ~ " = null;";
opt++; opt++;
@ -310,7 +312,7 @@ class EntityManager
{ {
ret ~= "input_data." ~ member ~ " = (cast(ForeachType!(typeof(Sys.EntitiesData." ~ member ret ~= "input_data." ~ member ~ " = (cast(ForeachType!(typeof(Sys.EntitiesData." ~ member
~ "))*)(cast(void*) block + info.deltas[ data.system.m_components[" ~ "))*)(cast(void*) block + info.deltas[ data.system.m_components["
~ req.to!string ~ "]]))[0 .. block.entities_count];"; ~ req.to!string ~ "]]))[offset .. entities_count];";
req++; req++;
} }
} }
@ -319,22 +321,47 @@ class EntityManager
return ret; return ret;
} }
//pragma(msg,genFillInputData);
static void callUpdate(ref CallData data) static void callUpdate(ref CallData data)
{ {
Sys* s = cast(Sys*) data.system.m_system_pointer; Sys* s = cast(Sys*) data.system.m_system_pointer;
Sys.EntitiesData input_data; Sys.EntitiesData input_data;
EntityInfo* info = data.info; //block.type_info;
EntitiesBlock* block = data.info.first_block; EntitiesBlock* block;
while (block !is null) if (data.first_block)
block = data.first_block;
else
block = info.first_block;
uint offset = data.begin;
uint entities_count;
uint blocks;
if (data.blocks)
blocks = data.blocks;
else
blocks = uint.max;
while (block !is null && blocks > 0)
{ {
EntityInfo* info = block.type_info; if (blocks == 1)
{
if (data.end)
entities_count = data.end;
else entities_count = block.entities_count;
}
else
entities_count = block.entities_count;
mixin(genFillInputData()); mixin(genFillInputData());
s.update(input_data); s.update(input_data);
block = block.next_block; block = block.next_block;
offset = 0;
blocks--;
} }
} }
@ -368,6 +395,8 @@ class EntityManager
system.m_system_pointer = cast(void*) Mallocator.instance.make!Sys; system.m_system_pointer = cast(void*) Mallocator.instance.make!Sys;
system.m_priority = priority; system.m_priority = priority;
(cast(Sys*) system.m_system_pointer).__ecsInitialize();
system.jobs = (cast(Sys*) system.m_system_pointer)._ecs_jobs;
mixin(genCompList()); mixin(genCompList());
@ -383,7 +412,7 @@ class EntityManager
/*if (systems[sys_id].m_destroy) /*if (systems[sys_id].m_destroy)
systems[sys_id].m_destroy(systems[sys_id].m_system_pointer);*/ systems[sys_id].m_destroy(systems[sys_id].m_system_pointer);*/
if (system.m_create) if (system.m_create)
system.m_create(system.m_system_pointer); (cast(void function(void*))system.m_create)(system.m_system_pointer);
systems[sys_id] = system; systems[sys_id] = system;
Sys.system_id = sys_id; Sys.system_id = sys_id;
@ -396,7 +425,7 @@ class EntityManager
systems.add(system); systems.add(system);
if (system.m_create) if (system.m_create)
system.m_create(system.m_system_pointer); (cast(void function(void*))system.m_create)(system.m_system_pointer);
systems[$ - 1].enable(); systems[$ - 1].enable();
@ -538,15 +567,16 @@ class EntityManager
System* sys = &systems[caller.system_id]; System* sys = &systems[caller.system_id];
if (sys.enabled) if (sys.enabled)
{ {
if (sys.m_begin) //if (sys.m_begin)
sys.m_begin(sys.m_system_pointer); // sys.m_begin(sys.m_system_pointer);
foreach (info; caller.infos) foreach (info; caller.infos)
{ {
CallData data = CallData(caller.system_id, sys, info); CallData data = CallData(caller.system_id, sys, info);
(cast(SytemFuncType) data.system.m_update)(data); data.update();
//(cast(SytemFuncType) data.system.m_update)(data);
} }
if (sys.m_end) //if (sys.m_end)
sys.m_end(sys.m_system_pointer); // sys.m_end(sys.m_system_pointer);
} }
} }
} }
@ -557,22 +587,158 @@ class EntityManager
foreach (data; info.callers) foreach (data; info.callers)
{ {
if (data.system.enabled) if (data.system.enabled)
(cast(SytemFuncType) data.system.m_update)(data); //caller(call_data,null); data.update();
//(cast(SytemFuncType) data.system.m_update)(data); //caller(call_data,null);
} }
} }
} }
} }
void updateMT()
{
assert(m_dispatch_jobs,
"Can't update with multithreading without JobDispatch function. Please use setJobDispatchFunc().");
Vector!CallData tmp_datas;
tmp_datas.reserve(8);
foreach (caller; system_callers)
{
System* sys = &systems[caller.system_id];
if (sys.enabled)
{
uint entities_count = 0;
foreach (info; caller.infos)
{
uint blocks_count = info.blocksCount();
if(blocks_count == 0)continue;
if (blocks_count > 1)
entities_count += (blocks_count - 1) * info.max_entities;
entities_count += info.last_block.entities_count;
}
if (!entities_count)
continue;
uint jobs_count = cast(uint) sys.jobs.length;
uint entities_per_job = entities_count / jobs_count + 1;
if (entities_per_job <= 4)
{
jobs_count = entities_count / 4;
if(jobs_count == 0)jobs_count = 1;
entities_per_job = entities_count / jobs_count + 1;
}
uint job_id = 0;
entities_count = 0;
void nextJob()
{
CallData[] callers = m_call_data_allocator.getCallData(cast(uint)tmp_datas.length);
callers[0..$] = tmp_datas[0..$];
tmp_datas.clear();
sys.jobs[job_id].callers = callers;
job_id++;
}
foreach (info; caller.infos)
{
uint blocks_count = info.blocksCount();
EntitiesBlock* first_block = info.first_block;
uint first_elem = 0;
begin:
if (first_block is null || blocks_count == 0)
continue;
if ((blocks_count - 1) * info.max_entities + entities_count
+ info.last_block.entities_count - first_elem >= entities_per_job)
{
int reamaining_entities = (entities_per_job - entities_count - (first_block.entities_count - first_elem));
if(reamaining_entities >= 0)
{
int full_blocks_count = reamaining_entities / info.max_entities;
EntitiesBlock* block = first_block;
foreach (i; 0 .. full_blocks_count + 1)
block = block.next_block;
if (
full_blocks_count * info.max_entities
+ entities_count + (first_block.entities_count - first_elem) >= entities_per_job)
{
CallData data = CallData(caller.system_id, sys, info, first_block,
cast(ushort) (full_blocks_count + 1), cast(ushort) first_elem, 0);
tmp_datas.add(data);
first_elem = 0;
blocks_count -= full_blocks_count + 1;
first_block = block;
}
else
{
entities_count += full_blocks_count * info.max_entities + (first_block.entities_count - first_elem);// - first_elem;
uint last_elem = entities_per_job - entities_count;// + first_elem - 1;
CallData data = CallData(caller.system_id, sys, info, first_block,
cast(ushort)(full_blocks_count + 2),
cast(ushort) first_elem, cast(ushort) last_elem);
tmp_datas.add(data);
first_elem = last_elem;
blocks_count -= full_blocks_count + 1;
assert(first_elem <= block.entities_count);
first_block = block;
}
}
else
{
uint last_elem = entities_per_job - entities_count;
CallData data = CallData(caller.system_id, sys, info, first_block,
1, cast(ushort) first_elem, cast(ushort)(first_elem + last_elem));
tmp_datas.add(data);
first_elem += last_elem;
assert(first_elem <= first_block.entities_count);
}
nextJob();
entities_count = 0;
goto begin;
}
else
{
CallData data = CallData(caller.system_id, sys, info,
first_block, cast(ushort) blocks_count, cast(ushort) first_elem);
tmp_datas.add(data);
entities_count += (blocks_count - 1)
* info.max_entities + info.last_block.entities_count - first_elem;
}
}
nextJob();
m_dispatch_jobs(sys.jobs[0..job_id]);
}
}
}
struct Job
{
CallData[] callers;
void execute()
{
EntityManager.instance.getThreadID();
foreach (ref caller; callers)
{
caller.update();
}
}
}
void setJobDispachFunc(void delegate(Job[]) func)
{
m_dispatch_jobs = func;
}
static void alignNum(ref ushort num, ushort alignment) static void alignNum(ref ushort num, ushort alignment)
{ {
num = cast(ushort)((num + alignment - 1) & (-cast(int) alignment)); //num += alignment - (num & (alignment - 1)); num = cast(ushort)((num + alignment - 1) & (-cast(int) alignment)); //num += alignment - (num & (alignment - 1));
} }
/*static ushort alignedNum(ushort num, ushort alignment)
{
return cast(ushort)((num + alignment - 1) & (-cast(int) alignment));
}*/
extern (C) static int compareUShorts(const void* a, const void* b) extern (C) static int compareUShorts(const void* a, const void* b)
{ {
ushort _a = *cast(ushort*) a; ushort _a = *cast(ushort*) a;
@ -754,7 +920,7 @@ class EntityManager
break; break;
} }
system_callers[index].infos.add(&entity); if(index < system_callers.length)system_callers[index].infos.add(&entity);
/*for (; index < entity.callers.length; index++) /*for (; index < entity.callers.length; index++)
{ {
CallData* caller = &entity.callers[index]; CallData* caller = &entity.callers[index];
@ -822,11 +988,12 @@ class EntityManager
*/ */
export void removeComponents(EntityID entity_id, ushort[] del_ids) export void removeComponents(EntityID entity_id, ushort[] del_ids)
{ {
ThreadData* data = &threads[thread_id];
uint num = cast(uint) del_ids.length; uint num = cast(uint) del_ids.length;
change_entities_list.add(0); data.change_entities_list.add(0);
change_entities_list.add((cast(ubyte*)&entity_id)[0 .. 8]); data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. 8]);
change_entities_list.add((cast(ubyte*)&num)[0 .. 4]); data.change_entities_list.add((cast(ubyte*)&num)[0 .. 4]);
change_entities_list.add(cast(ubyte[]) del_ids); data.change_entities_list.add(cast(ubyte[]) del_ids);
} }
private void __removeComponents(EntityID entity_id, ushort[] del_ids) private void __removeComponents(EntityID entity_id, ushort[] del_ids)
@ -961,34 +1128,42 @@ class EntityManager
uint j = 0; uint j = 0;
uint k = 0; uint k = 0;
foreach (ref id; ids) uint len = 0;
//foreach (ref id; ids)
for(;len<ids.length;len++)
{ {
ushort* id = &ids[len];
if (k >= new_ids.length) if (k >= new_ids.length)
{ {
id = info.components[j++]; if(j >= info.components.length)break;
continue; *id = info.components[j++];
//continue;
} }
if (j >= info.components.length) else if (j >= info.components.length)
{ {
id = new_ids[k++]; *id = new_ids[k++];
continue; //continue;
} }
debug if (new_ids[k] == info.components[j]) else if(new_ids[k] == info.components[j])
assert(0, "Trying to add already existing component!");
if (new_ids[k] < info.components[j])
{ {
id = new_ids[k++]; *id = info.components[j++];
k++;
}
/*debug if (new_ids[k] == info.components[j])
assert(0, "Trying to add already existing component!");*/
else if (new_ids[k] < info.components[j])
{
*id = new_ids[k++];
} }
else else
id = info.components[j++]; *id = info.components[j++];
} }
if(len == info.components.length)return;
EntityInfo* new_info = getEntityInfo(ids); EntityInfo* new_info = getEntityInfo(ids[0..len]);
EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); EntitiesBlock* new_block = findBlockWithFreeSpace(new_info);
//removeEntityNoID(entity, block);
void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof;
Entity* new_entity = cast(Entity*) start; Entity* new_entity = cast(Entity*) start;
@ -1001,10 +1176,10 @@ class EntityManager
uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) >> 3); uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) >> 3);
else else
uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) / EntityID.sizeof()); uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) / EntityID.sizeof());
foreach (ref id; ids) foreach (ref id; ids[0..len])
{ {
void* dst = cast(void*) new_block + new_info.deltas[id] + ( void* dst = cast(void*) new_block + new_info.deltas[id] + (
new_block.entities_count + new_block.added_count) * components[id].size; new_block.entities_count /*+ new_block.added_count*/) * components[id].size;
uint size = components[id].size; uint size = components[id].size;
if (k >= new_ids.length) if (k >= new_ids.length)
{ {
@ -1060,14 +1235,14 @@ class EntityManager
{ {
pointers[i] = &comp; pointers[i] = &comp;
}*/ }*/
ThreadData* data = &threads[thread_id];
change_entities_list.add(1); data.change_entities_list.add(1);
change_entities_list.add((cast(ubyte*)&entity_id)[0 .. 8]); data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. 8]);
change_entities_list.add((cast(ubyte*)&num)[0 .. 4]); data.change_entities_list.add((cast(ubyte*)&num)[0 .. 4]);
change_entities_list.add(cast(ubyte[]) new_ids); data.change_entities_list.add(cast(ubyte[]) new_ids);
static foreach (i, comp; comps) static foreach (i, comp; comps)
{ {
change_entities_list.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); data.change_entities_list.add((cast(ubyte*)&comp)[0 .. comp.sizeof]);
} }
//__addComponents(entity_id, new_ids, pointers); //__addComponents(entity_id, new_ids, pointers);
@ -1093,9 +1268,30 @@ class EntityManager
*/ */
export ref Entity addEntity(EntityTemplate* tmpl) export ref Entity addEntity(EntityTemplate* tmpl)
{ {
EntitiesBlock* block = findBlockWithFreeSpace(tmpl.info);
uint id = (block.entities_count + block.added_count);
EntityInfo* info = tmpl.info; EntityInfo* info = tmpl.info;
EntitiesBlock* last_block = info.last_block;
ushort index = 0;
if(last_block)/*index = */
{
//index = last_block.added_count;
index = last_block.added_count.atomicOp!"+="(1);
}
EntitiesBlock* block = findBlockWithFreeSpace(tmpl.info,index);
if(block != last_block)
{
//index = block.added_count;
/*if(last_block)
{
//last_block.added_count.atomicOp!"-="(1);
last_block.added_count = cast(ushort)(info.max_entities - last_block.entities_count);
}*/
index = block.added_count.atomicOp!"+="(1);
}
//index--;
//index = cast(ushort)(block.added_count - 1);
uint id = (block.entities_count + index - 1);//block.added_count);
//EntityInfo* info = tmpl.info;
void* data_begin = block.dataBegin(); void* data_begin = block.dataBegin();
void* start = data_begin + EntityID.sizeof * id; void* start = data_begin + EntityID.sizeof * id;
@ -1106,23 +1302,45 @@ class EntityManager
tmpl.entity_data.ptr + info.tmpl_deltas[comp], components[comp].size); tmpl.entity_data.ptr + info.tmpl_deltas[comp], components[comp].size);
} }
if (!block.added_count) //if (!block.added_count)
//if(block.added_count == 1)
if(index == 1)
blocks_to_update.add(block); blocks_to_update.add(block);
Entity* entity = cast(Entity*) start; Entity* entity = cast(Entity*) start;
entity.id = id_manager.getNewID(); entity.id = id_manager.getNewID();
entity.updateID(); entity.updateID();
block.added_count++; //block.added_count++;
//import core.atomic;
//block.entities_count++; //block.entities_count++;
return *entity; return *entity;
} }
private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info, uint new_index = 1)
{ {
EntitiesBlock* previous_block; //EntitiesBlock* previous_block;
EntitiesBlock* block = info.last_block; EntitiesBlock* block = info.last_block;
while (1) if(block is null)
{
block = cast(EntitiesBlock*) allocator.getBlock(); //AlignedMallocator.instance.alignedAllocate(page_size, page_size);
*block = EntitiesBlock(info);
block.id = 0;
info.first_block = block;
info.last_block = block;
}
else if(block.entities_count + new_index/*block.added_count*/ > info.max_entities)
{
EntitiesBlock* new_block = cast(EntitiesBlock*) allocator.getBlock(); //AlignedMallocator.instance.alignedAllocate(page_size, page_size);
*new_block = EntitiesBlock(info);
new_block.prev_block = block;
block.next_block = new_block;
new_block.id = cast(ushort)(block.id + 1);
block = new_block;
info.last_block = block;
}//*/
/*while (1)
{ {
if (block is null) if (block is null)
{ {
@ -1143,8 +1361,6 @@ class EntityManager
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
/*if (block.dataDelta() + (
block.entities_count + block.added_count + 1) * info.size > page_size)*/
if (block.entities_count + block.added_count >= block.type_info.max_entities) if (block.entities_count + block.added_count >= block.type_info.max_entities)
{ {
previous_block = block; previous_block = block;
@ -1153,7 +1369,7 @@ class EntityManager
} }
break; // block exists and bounds check passed break; // block exists and bounds check passed
} }//*/
return block; return block;
} }
@ -1166,7 +1382,7 @@ class EntityManager
*/ */
export void removeEntity(EntityID id) export void removeEntity(EntityID id)
{ {
entities_to_remove.add(id); threads[thread_id].entities_to_remove.add(id);
} }
private void __removeEntity(EntityID id) private void __removeEntity(EntityID id)
@ -1236,6 +1452,7 @@ class EntityManager
if (block.prev_block) if (block.prev_block)
{ {
block.prev_block.next_block = null; block.prev_block.next_block = null;
block.prev_block.added_count = 0;
} }
allocator.freeBlock(block); allocator.freeBlock(block);
} }
@ -1253,41 +1470,44 @@ class EntityManager
} }
private void changeEntites() private void changeEntites()
{
foreach(ref thread;threads)
{ {
uint index = 0; uint index = 0;
uint len = cast(uint) change_entities_list.length; uint len = cast(uint) thread.change_entities_list.length;
while (index < len) while (index < len)
{ {
if (!change_entities_list[index++]) if (!thread.change_entities_list[index++])
{ {
EntityID id = *cast(EntityID*)&change_entities_list[index]; EntityID id = *cast(EntityID*)&thread.change_entities_list[index];
index += EntityID.sizeof; index += EntityID.sizeof;
uint num = *cast(uint*)&change_entities_list[index]; uint num = *cast(uint*)&thread.change_entities_list[index];
index += uint.sizeof; index += uint.sizeof;
ushort[] ids = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; ushort[] ids = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num];
ids[0 .. $] = (cast(ushort*)&change_entities_list[index])[0 .. num]; ids[0 .. $] = (cast(ushort*)&thread.change_entities_list[index])[0 .. num];
index += ushort.sizeof * num; index += ushort.sizeof * num;
__removeComponents(id, ids); __removeComponents(id, ids);
} }
else else
{ {
EntityID id = *cast(EntityID*)&change_entities_list[index]; EntityID id = *cast(EntityID*)&thread.change_entities_list[index];
index += EntityID.sizeof; index += EntityID.sizeof;
uint num = *cast(uint*)&change_entities_list[index]; uint num = *cast(uint*)&thread.change_entities_list[index];
index += uint.sizeof; index += uint.sizeof;
ushort[] ids = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; ushort[] ids = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num];
ids[0 .. $] = (cast(ushort*)&change_entities_list[index])[0 .. num]; ids[0 .. $] = (cast(ushort*)&thread.change_entities_list[index])[0 .. num];
index += ushort.sizeof * num; index += ushort.sizeof * num;
void*[] pointers = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; void*[] pointers = (cast(void**) alloca(num * (void*).sizeof))[0 .. num];
foreach (i; 0 .. num) foreach (i; 0 .. num)
{ {
pointers[i] = &change_entities_list[index]; pointers[i] = &thread.change_entities_list[index];
index += components[ids[i]].size; index += components[ids[i]].size;
} }
__addComponents(id, ids, pointers); __addComponents(id, ids, pointers);
} }
} }
change_entities_list.clear(); thread.change_entities_list.clear();
}
} }
private void updateBlocks() private void updateBlocks()
@ -1295,18 +1515,26 @@ class EntityManager
foreach (block; blocks_to_update) foreach (block; blocks_to_update)
{ {
block.entities_count += block.added_count; block.entities_count += block.added_count;
block.added_count = 0; if(block.entities_count > block.type_info.max_entities)
{
block.entities_count = block.type_info.max_entities;
}
//block.added_count = 0;
block.added_count.atomicStore(cast(ushort)0);
} }
blocks_to_update.clear(); blocks_to_update.clear();
} }
private void removeEntities() private void removeEntities()
{ {
foreach (id; entities_to_remove) foreach(i,ref thread;threads)
{
foreach (id; thread.entities_to_remove)
{ {
__removeEntity(id); __removeEntity(id);
} }
entities_to_remove.clear(); thread.entities_to_remove.clear();
}
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -1317,19 +1545,20 @@ class EntityManager
updateBlocks(); updateBlocks();
removeEntities(); removeEntities();
changeEntites(); changeEntites();
m_call_data_allocator.clear();
version (UpdateBySystems) /*version (UpdateBySystems)
{ {
} }
else 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); (cast(void function(void*))system.m_begin)(system.m_system_pointer);
}
} }
//}
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -1337,18 +1566,18 @@ class EntityManager
*/ */
export void end() export void end()
{ {
version (UpdateBySystems) /*version (UpdateBySystems)
{ {
} }
else 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); (cast(void function(void*))system.m_end)(system.m_system_pointer);
}
} }
//}
updateBlocks(); updateBlocks();
removeEntities(); removeEntities();
@ -1357,6 +1586,12 @@ class EntityManager
//clearEvents(); //clearEvents();
} }
private void getThreadID()
{
if(m_thread_id_func)thread_id = m_thread_id_func();
else thread_id = 0;
}
/************************************************************************************************************************ /************************************************************************************************************************
*Component info; *Component info;
*/ */
@ -1387,7 +1622,9 @@ class EntityManager
///Returns number of blocks ///Returns number of blocks
uint blocksCount() uint blocksCount()
{ {
return last_block.id; if(last_block)
return last_block.id + 1;
else return 0;
} }
///entity components ///entity components
@ -1438,7 +1675,8 @@ class EntityManager
///number of entities in block ///number of entities in block
ushort entities_count = 0; ushort entities_count = 0;
///number of new entities in block ///number of new entities in block
ushort added_count = 0; shared ushort added_count = 0;
//ushort added_count = 0;
///block id ///block id
ushort id = 0; ushort id = 0;
///maximum number of entities in block ///maximum number of entities in block
@ -1452,15 +1690,32 @@ class EntityManager
/************************************************************************************************************************ /************************************************************************************************************************
*Structure with data used to calling System calls. *Structure with data used to calling System calls.
*
*<i>first_block</i>, <i>begin</i>, <i>end</i>, <i>blocks</i> parameters are used
*to call partial info update
*/ */
struct CallData struct CallData
{ {
void update()
{
(cast(SytemFuncType) system.m_update)(this);
}
///system ID. Used to update system pointer after system reload. ///system ID. Used to update system pointer after system reload.
uint system_id; uint system_id;
///pointer to used system ///pointer to used system
System* system; System* system;
///poiner to Entity type info ///poiner to Entity type info
EntityManager.EntityInfo* info; EntityManager.EntityInfo* info;
///pointer to first block into process (if 0 then first block will be used)
EntitiesBlock* first_block;
///number of blocks to update (if 0 then update all)
ushort blocks;
///index of first element in first block
ushort begin;
///index of last element in last block
ushort end;
} }
struct SystemCaller struct SystemCaller
@ -1470,6 +1725,16 @@ class EntityManager
Vector!(EntityInfo*) infos; Vector!(EntityInfo*) infos;
} }
struct ThreadData
{
Vector!EntityID entities_to_remove;
Vector!ubyte change_entities_list;
}
static uint thread_id;
ThreadData[] threads;
Vector!(SystemCaller*) system_callers; Vector!(SystemCaller*) system_callers;
alias SytemFuncType = void function(ref EntityManager.CallData data); alias SytemFuncType = void function(ref EntityManager.CallData data);
@ -1489,9 +1754,12 @@ class EntityManager
//EventManager event_manager; //EventManager event_manager;
mixin EventManagerCode; mixin EventManagerCode;
Vector!EntityID entities_to_remove; //Vector!EntityID entities_to_remove;
Vector!(EntitiesBlock*) blocks_to_update; Vector!(EntitiesBlock*) blocks_to_update;
Vector!ubyte change_entities_list; //Vector!ubyte change_entities_list;
void delegate(Job[] jobs) m_dispatch_jobs;
uint delegate() m_thread_id_func;
HashMap!(ushort[], EntityInfo*) entities_infos; HashMap!(ushort[], EntityInfo*) entities_infos;
HashMap!(const(char)[], ushort) systems_map; HashMap!(const(char)[], ushort) systems_map;
@ -1500,6 +1768,60 @@ class EntityManager
Vector!System systems; Vector!System systems;
Vector!ComponentInfo components; Vector!ComponentInfo components;
Vector!EventInfo events; Vector!EventInfo events;
CallDataAllocator m_call_data_allocator;
struct CallDataAllocator
{
struct Block
{
CallData[256] data;
uint allocated = 0;
}
Vector!(Block*) blocks;
uint id;
void clear()
{
if(blocks.length > 0)
foreach (block; blocks[0 .. id + 1])
{
block.allocated = 0;
}
id = 0;
//blocks.clear();
}
CallData[] getCallData(uint num)
{
if (blocks.length == 0)
{
Block* new_block = Mallocator.instance.make!Block;
blocks.add(new_block);
}
/*else if(blocks[$-1].allocated + num >= 256)
{
blocks.add(Block());
}*/
Block* block = blocks[id];
if (block.allocated + num >= 256)
{
id++;
if (id == blocks.length)
{
Block* new_block = Mallocator.instance.make!Block;
blocks.add(new_block);
}
block = blocks[id];
}
CallData[] ret = block.data[block.allocated .. block.allocated + num];
block.allocated += num;
return ret;
}
}
__gshared EntityManager instance = null; __gshared EntityManager instance = null;
} }

View file

@ -3,5 +3,6 @@ module ecs;
public import ecs.manager; public import ecs.manager;
public import ecs.entity; public import ecs.entity;
public import ecs.system; public import ecs.system;
public import ecs.core;
import ecs.id_manager; import ecs.id_manager;
import ecs.events; import ecs.events;

View file

@ -8,6 +8,7 @@ import ecs.manager;
*/ */
struct System struct System
{ {
/************************************************************************************************************************ /************************************************************************************************************************
*Check if system is enabled. *Check if system is enabled.
*/ */
@ -22,7 +23,7 @@ struct System
export void enable() export void enable()
{ {
if (!m_enabled && m_enable) if (!m_enabled && m_enable)
m_enable(m_system_pointer); (cast(void function(void*))m_enable)(m_system_pointer);
m_enabled = true; m_enabled = true;
} }
@ -32,7 +33,7 @@ struct System
export void disable() export void disable()
{ {
if (m_enabled && m_disable) if (m_enabled && m_disable)
m_disable(m_system_pointer); (cast(void function(void*))m_disable)(m_system_pointer);
m_enabled = false; m_enabled = false;
} }
@ -63,17 +64,28 @@ package:
///optional components ///optional components
ushort[] m_optional_components; ushort[] m_optional_components;
EntityManager.Job[] jobs;
//void function(ref EntityManager.CallData data) m_update; //void function(ref EntityManager.CallData data) m_update;
void* m_update; ///workaroud for DMD bug with upper line void* m_update; ///workaroud for DMD bug with upper line
void function(void* system_pointer) m_enable; /*void function(void* system_pointer) m_enable;
void function(void* system_pointer) m_disable; void function(void* system_pointer) m_disable;
void function(void* system_pointer) m_create; void function(void* system_pointer) m_create;
void function(void* system_pointer) m_destroy; void function(void* system_pointer) m_destroy;
void function(void* system_pointer) m_begin; void function(void* system_pointer) m_begin;
void function(void* system_pointer) m_end; void function(void* system_pointer) m_end;*/
void* m_enable;
void* m_disable;
void* m_create;
void* m_destroy;
void* m_begin;
void* m_end;
//void function(ref EntityManager.CallData data) m_initialize; //void function(ref EntityManager.CallData data) m_initialize;
//void function(ref EntityManager.CallData data) m_deinitilize; //void function(ref EntityManager.CallData data) m_deinitilize;

View file

@ -49,9 +49,9 @@ public:
export void removeAll() { export void removeAll() {
if (array !is null) { if (array !is null) {
foreach (ref el; array[0 .. used]) { /*foreach (ref el; array[0 .. used]) {
destroy(el); destroy(el);
} }*/
freeData(cast(void[]) array); freeData(cast(void[]) array);
gVectorsDestroyed++; gVectorsDestroyed++;
} }

View file

@ -5,6 +5,7 @@ import ecs.events;
import ecs.manager; import ecs.manager;
import ecs.system; import ecs.system;
import ecs.attributes; import ecs.attributes;
import ecs.core;
import core.time; import core.time;
import std.stdio; import std.stdio;
@ -14,19 +15,19 @@ int main()
struct TestEvent struct TestEvent
{ {
__gshared ushort event_id; mixin ECS.Event;//__gshared ushort event_id;
int a; int a;
} }
struct TestEvent2 struct TestEvent2
{ {
__gshared ushort event_id; mixin ECS.Event;//__gshared ushort event_id;
float a; float a;
} }
static struct TestComp static struct TestComp
{ {
__gshared ushort component_id; mixin ECS.Component;//__gshared ushort component_id;
int a = 1; int a = 1;
ulong b = 2; ulong b = 2;
@ -43,7 +44,7 @@ int main()
static struct TestComp2 static struct TestComp2
{ {
__gshared ushort component_id; mixin ECS.Component;//__gshared ushort component_id;
int b = 3; int b = 3;
int a = 4; int a = 4;
@ -60,7 +61,7 @@ int main()
static struct TestComp3 static struct TestComp3
{ {
__gshared ushort component_id; mixin ECS.Component;//__gshared ushort component_id;
uint gg = 5; //good game uint gg = 5; //good game
uint bg = 6; //bad game uint bg = 6; //bad game
@ -77,7 +78,7 @@ int main()
static struct TestComp4 static struct TestComp4
{ {
__gshared ushort component_id; mixin ECS.Component;//__gshared ushort component_id;
uint gg = 7; //good game uint gg = 7; //good game
uint bg = 8; //bad game uint bg = 8; //bad game
ulong a = 9; ulong a = 9;
@ -98,7 +99,7 @@ int main()
struct TestSystem struct TestSystem
{ {
__gshared ushort system_id; mixin ECS.System!16;//__gshared ushort system_id;
void onCreate() void onCreate()
{ {
@ -146,7 +147,7 @@ int main()
test2.a = 8; test2.a = 8;
} }
void update(ref EntitiesData data) void update(EntitiesData data)
{ {
foreach(i;0..data.length) foreach(i;0..data.length)
{ {
@ -165,7 +166,7 @@ int main()
struct TestSystemWithHighPriority struct TestSystemWithHighPriority
{ {
__gshared ushort system_id; mixin ECS.System!16;//__gshared ushort system_id;
static struct EntitiesData static struct EntitiesData
{ {
@ -177,7 +178,7 @@ int main()
} }
void update(ref EntitiesData data) void update(EntitiesData data)
{ {
} }
@ -192,7 +193,7 @@ import std.meta;
struct TestSystem2 struct TestSystem2
{ {
__gshared ushort system_id; mixin ECS.System!16;//__gshared ushort system_id;
enum AbsentComponents0 enum AbsentComponents0
{ {
@ -255,6 +256,15 @@ import std.meta;
}*/ }*/
} }
void dispatch(EntityManager.Job[] jobs)
{
foreach(job;jobs)
{
//writeln(job);
job.execute();
}
}
void writeEntityComponents(Entity* entity) void writeEntityComponents(Entity* entity)
{ {
write(entity.id); write(entity.id);
@ -270,7 +280,8 @@ import std.meta;
//writeln((cast(uint*) pp)[0 .. 14], " ", pp); //writeln((cast(uint*) pp)[0 .. 14], " ", pp);
} }
EntityManager.initialize(); EntityManager.initialize(1);
gEM.setJobDispachFunc(&dispatch);
assert(gEM !is null); assert(gEM !is null);
MonoTime time = MonoTime.currTime; MonoTime time = MonoTime.currTime;
@ -358,40 +369,49 @@ import std.meta;
//assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+24)) == EntityID(1,1)); //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)); //assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+48)) == EntityID(1,1));
Entity entity2;
foreach (i; 0 .. 500_000) foreach (i; 0 .. 500_000)
{ {
gEM.addEntity(tmpl); entity2 = gEM.addEntity(tmpl);
gEM.addEntity(tmpl2); gEM.addEntity(tmpl2);
} }
time = MonoTime.currTime; time = MonoTime.currTime;
gEM.begin(); gEM.begin();
//gEM.updateMT();
gEM.update(); gEM.update();
gEM.end(); gEM.end();
dur = (MonoTime.currTime - time).total!"usecs"; dur = (MonoTime.currTime - time).total!"usecs";
writeln("Update: ", dur, " usecs"); writeln("Update: ", dur, " usecs");
writeEntityComponents(gEM.getEntity(entity2.id));
time = MonoTime.currTime; time = MonoTime.currTime;
gEM.begin(); gEM.begin();
gEM.update(); gEM.updateMT();
//gEM.update();
gEM.end(); gEM.end();
dur = (MonoTime.currTime - time).total!"usecs"; dur = (MonoTime.currTime - time).total!"usecs";
writeln("Update: ", dur, " usecs"); writeln("Update: ", dur, " usecs");
writeEntityComponents(gEM.getEntity(entity2.id));
time = MonoTime.currTime; time = MonoTime.currTime;
gEM.begin(); gEM.begin();
gEM.update(); gEM.updateMT();
//gEM.update();
gEM.end(); gEM.end();
dur = (MonoTime.currTime - time).total!"usecs"; dur = (MonoTime.currTime - time).total!"usecs";
writeln("Update: ", dur, " usecs"); writeln("Update: ", dur, " usecs");
writeEntityComponents(gEM.getEntity(entity.id)); writeEntityComponents(gEM.getEntity(entity2.id));
entity = gEM.addEntity(tmpl); entity = gEM.addEntity(tmpl);