-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
|
|
@ -4,3 +4,5 @@ module ecs.attributes;
|
|||
enum optional = "optional";
|
||||
///Used to mark absent components for system. Enum 'AbsentComponents' should be used instead of it.
|
||||
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!");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue