-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:
Mergul 2018-10-20 11:42:29 +02:00
parent 2824bdb55b
commit 430ce8074c
4 changed files with 246 additions and 28 deletions

View file

@ -3,4 +3,6 @@ module ecs.attributes;
///Used to mark optional components for system.
enum optional = "optional";
///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";

View file

@ -108,11 +108,13 @@ class EntityManager
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 opt;
uint absent;
uint read_only;
uint modified;
foreach (member; __traits(allMembers, Sys.EntitiesData))
{
if (member == "length" || is(typeof(__traits(getMember,
@ -125,27 +127,41 @@ class EntityManager
{
{
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,
__traits(getMember, Sys.EntitiesData, member)))
{
if (att == "optional")
{
//ret ~= "opt++;";
opt++;
has_att = true;
break;
attribs++;
//break;
}
else if (att == "absent")
{
absent++;
//ret ~= "absen++;";
has_att = true;
break;
attribs++;
//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++;
//ret ~= "req++;";
if (is_read_only)
read_only++;
else
modified++;
}
}
}
@ -173,11 +189,19 @@ class EntityManager
if (absent > 0)
ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort("
~ 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;";
opt = 0;
req = 0;
absent = 0;
read_only = 0;
modified = 0;
static if (__traits(hasMember, Sys, "AbsentComponents"))
{
@ -221,29 +245,46 @@ class EntityManager
.stringof
~ \".\");";
bool is_read_only = 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,
__traits(getMember, Sys.EntitiesData, member)))
{
if (att == "optional")
{
ret ~= "system.m_optional_components[" ~ (opt++)
.to!string ~ "] = comp;}";
.to!string ~ "] = comp;";
has_att = true;
break;
//break;
}
else if (att == "absent")
{
ret ~= "system.m_absent_components[" ~ (absent++)
.to!string ~ "] = comp;}";
.to!string ~ "] = comp;";
has_att = true;
break;
//break;
}
if (att == "readonly")
{
is_read_only = true;
}
}
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
entities_count = block.entities_count;
assert(entities_count <= block.entities_count && offset <= block.entities_count);
mixin(genFillInputData());
s.update(input_data);
@ -616,7 +659,7 @@ class EntityManager
uint entities_count = 0;
foreach (info; caller.infos)
{
uint blocks_count = info.blocksCount();
uint blocks_count = info.nonEmptyBlocksCount();
if (blocks_count == 0)
continue;
if (blocks_count > 1)
@ -653,7 +696,7 @@ class EntityManager
foreach (info; caller.infos)
{
uint blocks_count = info.blocksCount();
uint blocks_count = info.nonEmptyBlocksCount();
EntitiesBlock* first_block = info.first_block;
uint first_elem = 0;
begin:
@ -722,7 +765,8 @@ class EntityManager
}
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;
}
@ -1357,7 +1409,7 @@ class EntityManager
entity_block_alloc_mutex.lock_nothrow();
scope (exit)
entity_block_alloc_mutex.unlock_nothrow();
if (info.last_block != null)
return info.last_block;
@ -1482,7 +1534,7 @@ class EntityManager
return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(page_size - 1)));
}
private void changeEntites()
private void changeEntities()
{
foreach (ref thread; threads)
{
@ -1560,7 +1612,7 @@ class EntityManager
{
updateBlocks();
removeEntities();
changeEntites();
changeEntities();
m_call_data_allocator.clear();
/*version (UpdateBySystems)
@ -1597,7 +1649,7 @@ class EntityManager
updateBlocks();
removeEntities();
changeEntites();
changeEntities();
//clearEvents();
}
@ -1610,6 +1662,146 @@ class EntityManager
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;
*/
@ -1646,6 +1838,21 @@ class EntityManager
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
ushort[] components;
@ -1744,6 +1951,9 @@ class EntityManager
uint system_id;
System* system;
Vector!(EntityInfo*) infos;
SystemCaller*[] dependencies;
SystemCaller*[] exclusion;
JobGroup job_group;
}
struct ThreadData
@ -1780,7 +1990,7 @@ class EntityManager
//Vector!(EntitiesBlock*) blocks_to_update;
//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;
HashMap!(ushort[], EntityInfo*) entities_infos;

View file

@ -66,6 +66,10 @@ package:
EntityManager.Job[] jobs;
System*[] m_dependencies;
ushort[] m_read_only_components;
ushort[] m_modified_components;
//void function(ref EntityManager.CallData data) m_update;
void* m_update; ///workaroud for DMD bug with upper line

View file

@ -131,7 +131,7 @@ int main()
size_t length;
TestComp[] test;
TestComp2[] test2;
@optional TestComp3[] test3;
@readonly @optional const(TestComp3)[] test3;
//@absent TestComp4[] test4;
}
@ -208,7 +208,7 @@ import std.meta;
static struct EntitiesData
{
short length;
Entity[] entity;
const (Entity)[] entity;
TestComp3[] test;
//@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);
job.execute();
@ -366,6 +366,8 @@ import std.meta;
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+48)) == EntityID(1,1));