-multithreading jobs dependencies:
*system has arrays of read only and modified components *new attribute "readonly" usable for variables which should be visible as read only. Const can be used instead for enable checks by compiler. *JobGroup was added. JobGroup contain array of jobs and array of dependencies (JobGroups) *new function generateDependencies() generate exclusion between systems, and then generate dependencies for SystemCallers and JobGroups -fixed issue with jobs generating (empty blocks with only newly added entities was used, and led to crash) -fixed small typo mistake
This commit is contained in:
parent
2824bdb55b
commit
430ce8074c
4 changed files with 246 additions and 28 deletions
|
|
@ -3,4 +3,6 @@ module ecs.attributes;
|
||||||
///Used to mark optional components for system.
|
///Used to mark optional components for system.
|
||||||
enum optional = "optional";
|
enum optional = "optional";
|
||||||
///Used to mark absent components for system. Enum 'AbsentComponents' should be used instead of it.
|
///Used to mark absent components for system. Enum 'AbsentComponents' should be used instead of it.
|
||||||
enum absent = "absent";
|
enum absent = "absent";
|
||||||
|
///Used to mark readonly components for system. "const" can be used insted.
|
||||||
|
enum readonly = "readonly";
|
||||||
|
|
@ -108,11 +108,13 @@ class EntityManager
|
||||||
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;
|
||||||
|
|
||||||
uint req;
|
uint req;
|
||||||
uint opt;
|
uint opt;
|
||||||
uint absent;
|
uint absent;
|
||||||
|
uint read_only;
|
||||||
|
uint modified;
|
||||||
foreach (member; __traits(allMembers, Sys.EntitiesData))
|
foreach (member; __traits(allMembers, Sys.EntitiesData))
|
||||||
{
|
{
|
||||||
if (member == "length" || is(typeof(__traits(getMember,
|
if (member == "length" || is(typeof(__traits(getMember,
|
||||||
|
|
@ -125,27 +127,41 @@ class EntityManager
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
bool has_att = false;
|
bool has_att = false;
|
||||||
|
bool is_read_only = false;
|
||||||
|
int attribs = 0;
|
||||||
|
if (is(CopyConstness!(ForeachType!(typeof(mixin("Sys.EntitiesData." ~ member))),
|
||||||
|
int) == const(int)))
|
||||||
|
{
|
||||||
|
is_read_only = true;
|
||||||
|
}
|
||||||
foreach (att; __traits(getAttributes,
|
foreach (att; __traits(getAttributes,
|
||||||
__traits(getMember, Sys.EntitiesData, member)))
|
__traits(getMember, Sys.EntitiesData, member)))
|
||||||
{
|
{
|
||||||
if (att == "optional")
|
if (att == "optional")
|
||||||
{
|
{
|
||||||
//ret ~= "opt++;";
|
|
||||||
opt++;
|
opt++;
|
||||||
has_att = true;
|
attribs++;
|
||||||
break;
|
//break;
|
||||||
}
|
}
|
||||||
else if (att == "absent")
|
else if (att == "absent")
|
||||||
{
|
{
|
||||||
absent++;
|
absent++;
|
||||||
//ret ~= "absen++;";
|
attribs++;
|
||||||
has_att = true;
|
//break;
|
||||||
break;
|
}
|
||||||
|
if (att == "readonly")
|
||||||
|
{
|
||||||
|
is_read_only = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_att)
|
assert(attribs <= 1,
|
||||||
|
"EntitiesData member can't have both \"@optional\" and \"@absent\".");
|
||||||
|
if (!attribs)
|
||||||
req++;
|
req++;
|
||||||
//ret ~= "req++;";
|
if (is_read_only)
|
||||||
|
read_only++;
|
||||||
|
else
|
||||||
|
modified++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,11 +189,19 @@ class EntityManager
|
||||||
if (absent > 0)
|
if (absent > 0)
|
||||||
ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort("
|
ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort("
|
||||||
~ absent.to!string ~ ");";
|
~ absent.to!string ~ ");";
|
||||||
|
if (read_only > 0)
|
||||||
|
ret ~= "system.m_read_only_components = Mallocator.instance.makeArray!ushort("
|
||||||
|
~ read_only.to!string ~ ");";
|
||||||
|
if (modified > 0)
|
||||||
|
ret ~= "system.m_modified_components = Mallocator.instance.makeArray!ushort("
|
||||||
|
~ modified.to!string ~ ");";
|
||||||
ret ~= "ushort comp;"; //uint opt = 0;uint req = 0;uint absent = 0;";
|
ret ~= "ushort comp;"; //uint opt = 0;uint req = 0;uint absent = 0;";
|
||||||
|
|
||||||
opt = 0;
|
opt = 0;
|
||||||
req = 0;
|
req = 0;
|
||||||
absent = 0;
|
absent = 0;
|
||||||
|
read_only = 0;
|
||||||
|
modified = 0;
|
||||||
|
|
||||||
static if (__traits(hasMember, Sys, "AbsentComponents"))
|
static if (__traits(hasMember, Sys, "AbsentComponents"))
|
||||||
{
|
{
|
||||||
|
|
@ -221,29 +245,46 @@ class EntityManager
|
||||||
.stringof
|
.stringof
|
||||||
~ \".\");";
|
~ \".\");";
|
||||||
|
|
||||||
|
bool is_read_only = false;
|
||||||
bool has_att = false;
|
bool has_att = false;
|
||||||
|
if (is(CopyConstness!(ForeachType!(typeof(mixin("Sys.EntitiesData." ~ member))),
|
||||||
|
int) == const(int)))
|
||||||
|
{
|
||||||
|
is_read_only = true;
|
||||||
|
}
|
||||||
foreach (att; __traits(getAttributes,
|
foreach (att; __traits(getAttributes,
|
||||||
__traits(getMember, Sys.EntitiesData, member)))
|
__traits(getMember, Sys.EntitiesData, member)))
|
||||||
{
|
{
|
||||||
if (att == "optional")
|
if (att == "optional")
|
||||||
{
|
{
|
||||||
ret ~= "system.m_optional_components[" ~ (opt++)
|
ret ~= "system.m_optional_components[" ~ (opt++)
|
||||||
.to!string ~ "] = comp;}";
|
.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++)
|
ret ~= "system.m_absent_components[" ~ (absent++)
|
||||||
.to!string ~ "] = comp;}";
|
.to!string ~ "] = comp;";
|
||||||
has_att = true;
|
has_att = true;
|
||||||
break;
|
//break;
|
||||||
|
}
|
||||||
|
if (att == "readonly")
|
||||||
|
{
|
||||||
|
is_read_only = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_att)
|
if (!has_att)
|
||||||
{
|
{
|
||||||
ret ~= "system.m_components[" ~ (req++).to!string ~ "] = comp;}";
|
ret ~= "system.m_components[" ~ (req++).to!string ~ "] = comp;";
|
||||||
}
|
}
|
||||||
|
if (is_read_only)
|
||||||
|
ret ~= "system.m_read_only_components[" ~ (read_only++)
|
||||||
|
.to!string ~ "] = comp;";
|
||||||
|
else
|
||||||
|
ret ~= "system.m_modified_components[" ~ (modified++)
|
||||||
|
.to!string ~ "] = comp;";
|
||||||
|
ret ~= "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -362,6 +403,8 @@ class EntityManager
|
||||||
else
|
else
|
||||||
entities_count = block.entities_count;
|
entities_count = block.entities_count;
|
||||||
|
|
||||||
|
assert(entities_count <= block.entities_count && offset <= block.entities_count);
|
||||||
|
|
||||||
mixin(genFillInputData());
|
mixin(genFillInputData());
|
||||||
|
|
||||||
s.update(input_data);
|
s.update(input_data);
|
||||||
|
|
@ -616,7 +659,7 @@ class EntityManager
|
||||||
uint entities_count = 0;
|
uint entities_count = 0;
|
||||||
foreach (info; caller.infos)
|
foreach (info; caller.infos)
|
||||||
{
|
{
|
||||||
uint blocks_count = info.blocksCount();
|
uint blocks_count = info.nonEmptyBlocksCount();
|
||||||
if (blocks_count == 0)
|
if (blocks_count == 0)
|
||||||
continue;
|
continue;
|
||||||
if (blocks_count > 1)
|
if (blocks_count > 1)
|
||||||
|
|
@ -653,7 +696,7 @@ class EntityManager
|
||||||
|
|
||||||
foreach (info; caller.infos)
|
foreach (info; caller.infos)
|
||||||
{
|
{
|
||||||
uint blocks_count = info.blocksCount();
|
uint blocks_count = info.nonEmptyBlocksCount();
|
||||||
EntitiesBlock* first_block = info.first_block;
|
EntitiesBlock* first_block = info.first_block;
|
||||||
uint first_elem = 0;
|
uint first_elem = 0;
|
||||||
begin:
|
begin:
|
||||||
|
|
@ -722,7 +765,8 @@ class EntityManager
|
||||||
}
|
}
|
||||||
nextJob();
|
nextJob();
|
||||||
|
|
||||||
m_dispatch_jobs(sys.jobs[0 .. job_id]);
|
caller.job_group.jobs = sys.jobs[0 .. job_id];
|
||||||
|
m_dispatch_jobs(caller.job_group);//sys.jobs[0 .. job_id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -741,7 +785,15 @@ class EntityManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setJobDispachFunc(void delegate(Job[]) func)
|
struct JobGroup
|
||||||
|
{
|
||||||
|
Job[] jobs;
|
||||||
|
JobGroup*[] dependencies;
|
||||||
|
uint id;
|
||||||
|
//uint max_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setJobDispachFunc(void delegate(JobGroup) func)
|
||||||
{
|
{
|
||||||
m_dispatch_jobs = func;
|
m_dispatch_jobs = func;
|
||||||
}
|
}
|
||||||
|
|
@ -1357,7 +1409,7 @@ class EntityManager
|
||||||
entity_block_alloc_mutex.lock_nothrow();
|
entity_block_alloc_mutex.lock_nothrow();
|
||||||
scope (exit)
|
scope (exit)
|
||||||
entity_block_alloc_mutex.unlock_nothrow();
|
entity_block_alloc_mutex.unlock_nothrow();
|
||||||
|
|
||||||
if (info.last_block != null)
|
if (info.last_block != null)
|
||||||
return info.last_block;
|
return info.last_block;
|
||||||
|
|
||||||
|
|
@ -1482,7 +1534,7 @@ class EntityManager
|
||||||
return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(page_size - 1)));
|
return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(page_size - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeEntites()
|
private void changeEntities()
|
||||||
{
|
{
|
||||||
foreach (ref thread; threads)
|
foreach (ref thread; threads)
|
||||||
{
|
{
|
||||||
|
|
@ -1560,7 +1612,7 @@ class EntityManager
|
||||||
{
|
{
|
||||||
updateBlocks();
|
updateBlocks();
|
||||||
removeEntities();
|
removeEntities();
|
||||||
changeEntites();
|
changeEntities();
|
||||||
m_call_data_allocator.clear();
|
m_call_data_allocator.clear();
|
||||||
|
|
||||||
/*version (UpdateBySystems)
|
/*version (UpdateBySystems)
|
||||||
|
|
@ -1597,7 +1649,7 @@ class EntityManager
|
||||||
|
|
||||||
updateBlocks();
|
updateBlocks();
|
||||||
removeEntities();
|
removeEntities();
|
||||||
changeEntites();
|
changeEntities();
|
||||||
|
|
||||||
//clearEvents();
|
//clearEvents();
|
||||||
}
|
}
|
||||||
|
|
@ -1610,6 +1662,146 @@ class EntityManager
|
||||||
thread_id = 0;
|
thread_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*private */
|
||||||
|
void generateDependencies()
|
||||||
|
{
|
||||||
|
foreach (caller; system_callers)
|
||||||
|
{
|
||||||
|
caller.system = &systems[caller.system_id];
|
||||||
|
if(caller.exclusion)Mallocator.instance.dispose(caller.exclusion);
|
||||||
|
if(caller.dependencies)Mallocator.instance.dispose(caller.dependencies);
|
||||||
|
}
|
||||||
|
uint index = 0;
|
||||||
|
SystemCaller*[] exclusion;
|
||||||
|
exclusion = (cast(SystemCaller**) alloca((SystemCaller*).sizeof * system_callers.length))[0
|
||||||
|
.. system_callers.length];
|
||||||
|
foreach (caller; system_callers)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
out_for: foreach (caller2; system_callers)
|
||||||
|
{
|
||||||
|
if (/*caller.system.priority != caller2.system.priority ||*/ caller is caller2)
|
||||||
|
continue;
|
||||||
|
foreach (cmp; caller.system.m_read_only_components)
|
||||||
|
{
|
||||||
|
foreach (cmp2; caller2.system.m_modified_components)
|
||||||
|
{
|
||||||
|
if (cmp == cmp2)
|
||||||
|
{
|
||||||
|
exclusion[index++] = caller2;
|
||||||
|
continue out_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (cmp; caller.system.m_modified_components)
|
||||||
|
{
|
||||||
|
foreach (cmp2; caller2.system.m_read_only_components)
|
||||||
|
{
|
||||||
|
if (cmp == cmp2)
|
||||||
|
{
|
||||||
|
exclusion[index++] = caller2;
|
||||||
|
continue out_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (cmp2; caller2.system.m_modified_components)
|
||||||
|
{
|
||||||
|
if (cmp == cmp2)
|
||||||
|
{
|
||||||
|
exclusion[index++] = caller2;
|
||||||
|
continue out_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(index > 0)caller.exclusion = Mallocator.instance.makeArray(exclusion[0..index]);
|
||||||
|
else caller.exclusion = null;
|
||||||
|
|
||||||
|
/*import std.stdio;
|
||||||
|
write("Exclusive systems for system ", caller.system.name, ": ");
|
||||||
|
foreach (ex; exclusion[0 .. index])
|
||||||
|
write(ex.system.name, " ");
|
||||||
|
writeln();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
extern (C) static int compareSystems(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
SystemCaller* _a = *cast(SystemCaller**) a;
|
||||||
|
SystemCaller* _b = *cast(SystemCaller**) b;
|
||||||
|
if (_a.system.priority < _b.system.priority)
|
||||||
|
return -1;
|
||||||
|
else if (_a.system.priority == _b.system.priority)
|
||||||
|
{
|
||||||
|
if (_a.exclusion.length < _b.exclusion.length)
|
||||||
|
return -1;
|
||||||
|
else if (_a.exclusion.length == _b.exclusion.length)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(system_callers.array.ptr, system_callers.length, (SystemCaller*)
|
||||||
|
.sizeof, &compareSystems);
|
||||||
|
|
||||||
|
/*static struct CallerData
|
||||||
|
{
|
||||||
|
uint id;
|
||||||
|
//bool
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*caller_data = (cast(SystemCaller**) alloca((SystemCaller*).sizeof * system_callers.length))[0
|
||||||
|
.. system_callers.length];*/
|
||||||
|
|
||||||
|
foreach(uint i,caller;system_callers)caller.job_group.id = i;
|
||||||
|
|
||||||
|
int priority = int.min;
|
||||||
|
uint beg = 0;
|
||||||
|
index = 0;
|
||||||
|
foreach(uint i,caller;system_callers)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if(priority == int.min)priority = caller.system.priority;
|
||||||
|
if(priority != caller.system.priority)
|
||||||
|
{
|
||||||
|
foreach(caller2;system_callers[beg..i])
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
priority = caller.system.priority;
|
||||||
|
beg = i;
|
||||||
|
}*/
|
||||||
|
index = 0;
|
||||||
|
foreach(ex;caller.exclusion)
|
||||||
|
{
|
||||||
|
if(ex.job_group.id > caller.job_group.id)continue;
|
||||||
|
|
||||||
|
exclusion[index++] = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index > 0)
|
||||||
|
{
|
||||||
|
caller.dependencies = Mallocator.instance.makeArray(exclusion[0..index]);
|
||||||
|
caller.job_group.dependencies = Mallocator.instance.makeArray!(JobGroup*)(index);
|
||||||
|
|
||||||
|
foreach(j,dep;caller.dependencies)
|
||||||
|
{
|
||||||
|
caller.job_group.dependencies[j] = &dep.job_group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else caller.dependencies = null;
|
||||||
|
|
||||||
|
/*import std.stdio;
|
||||||
|
write("Dependencies for system ", caller.system.name, ": ");
|
||||||
|
foreach (ex; caller.dependencies)
|
||||||
|
write(ex.system.name, " ");
|
||||||
|
writeln();*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/************************************************************************************************************************
|
/************************************************************************************************************************
|
||||||
*Component info;
|
*Component info;
|
||||||
*/
|
*/
|
||||||
|
|
@ -1646,6 +1838,21 @@ class EntityManager
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Returns number of non empty blocks
|
||||||
|
uint nonEmptyBlocksCount()
|
||||||
|
{
|
||||||
|
EntitiesBlock* block = last_block;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (block is null)
|
||||||
|
return 0;
|
||||||
|
if (block.entities_count == 0)
|
||||||
|
block = block.prev_block;
|
||||||
|
else
|
||||||
|
return block.id + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///entity components
|
///entity components
|
||||||
ushort[] components;
|
ushort[] components;
|
||||||
|
|
||||||
|
|
@ -1744,6 +1951,9 @@ class EntityManager
|
||||||
uint system_id;
|
uint system_id;
|
||||||
System* system;
|
System* system;
|
||||||
Vector!(EntityInfo*) infos;
|
Vector!(EntityInfo*) infos;
|
||||||
|
SystemCaller*[] dependencies;
|
||||||
|
SystemCaller*[] exclusion;
|
||||||
|
JobGroup job_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ThreadData
|
struct ThreadData
|
||||||
|
|
@ -1780,7 +1990,7 @@ class EntityManager
|
||||||
//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;
|
void delegate(JobGroup jobs) m_dispatch_jobs;
|
||||||
uint delegate() m_thread_id_func;
|
uint delegate() m_thread_id_func;
|
||||||
|
|
||||||
HashMap!(ushort[], EntityInfo*) entities_infos;
|
HashMap!(ushort[], EntityInfo*) entities_infos;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,10 @@ package:
|
||||||
|
|
||||||
EntityManager.Job[] jobs;
|
EntityManager.Job[] jobs;
|
||||||
|
|
||||||
|
System*[] m_dependencies;
|
||||||
|
ushort[] m_read_only_components;
|
||||||
|
ushort[] m_modified_components;
|
||||||
|
|
||||||
//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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ int main()
|
||||||
size_t length;
|
size_t length;
|
||||||
TestComp[] test;
|
TestComp[] test;
|
||||||
TestComp2[] test2;
|
TestComp2[] test2;
|
||||||
@optional TestComp3[] test3;
|
@readonly @optional const(TestComp3)[] test3;
|
||||||
//@absent TestComp4[] test4;
|
//@absent TestComp4[] test4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +208,7 @@ import std.meta;
|
||||||
static struct EntitiesData
|
static struct EntitiesData
|
||||||
{
|
{
|
||||||
short length;
|
short length;
|
||||||
Entity[] entity;
|
const (Entity)[] entity;
|
||||||
TestComp3[] test;
|
TestComp3[] test;
|
||||||
//@absent TestComp[] testt;
|
//@absent TestComp[] testt;
|
||||||
}
|
}
|
||||||
|
|
@ -256,9 +256,9 @@ import std.meta;
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatch(EntityManager.Job[] jobs)
|
void dispatch(EntityManager.JobGroup jobs)
|
||||||
{
|
{
|
||||||
foreach(job;jobs)
|
foreach(job;jobs.jobs)
|
||||||
{
|
{
|
||||||
//writeln(job);
|
//writeln(job);
|
||||||
job.execute();
|
job.execute();
|
||||||
|
|
@ -366,6 +366,8 @@ import std.meta;
|
||||||
|
|
||||||
gEM.registerSystem!TestSystem2(0);
|
gEM.registerSystem!TestSystem2(0);
|
||||||
|
|
||||||
|
gEM.generateDependencies();
|
||||||
|
|
||||||
//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));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue