diff --git a/source/ecs/attributes.d b/source/ecs/attributes.d index 4068a06..6b45dae 100644 --- a/source/ecs/attributes.d +++ b/source/ecs/attributes.d @@ -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"; \ No newline at end of file +enum absent = "absent"; +///Used to mark readonly components for system. "const" can be used insted. +enum readonly = "readonly"; \ No newline at end of file diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 2249a20..9132a39 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -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; diff --git a/source/ecs/system.d b/source/ecs/system.d index 2ac96fb..28e2e23 100644 --- a/source/ecs/system.d +++ b/source/ecs/system.d @@ -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 diff --git a/tests/tests.d b/tests/tests.d index c9fb25a..ee27305 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -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));