From 6929f5a7487a1c96239d03c256456f2952dab448 Mon Sep 17 00:00:00 2001 From: Mergul Date: Wed, 27 May 2020 17:03:44 +0200 Subject: [PATCH] Mostly bugfix update + empty components support and remove EntityID from Event structure -empty components now take no memory, so flag components is now far better -added test for critical bug -fixed critical bug with adding/removing entities form inside events -fixed small bug with TestRunner -improve basic tests -fixed betterC compilation on DMD -remove EntityID form Event structure -added "return" attribute to some functions -moved some code from Tempalte side to actual implementation -fixed bug with EntityTemplate copying -commented out some possibliy unused code -use code formatter --- source/bubel/ecs/atomic.d | 125 +++++++---- source/bubel/ecs/attributes.d | 2 +- source/bubel/ecs/block_allocator.d | 21 +- source/bubel/ecs/core.d | 9 +- source/bubel/ecs/entity.d | 11 +- source/bubel/ecs/events.d | 63 +++--- source/bubel/ecs/hash_map.d | 186 ++++++++++------ source/bubel/ecs/id_manager.d | 54 +++-- source/bubel/ecs/manager.d | 330 ++++++++++++++++------------- source/bubel/ecs/package.d | 2 +- source/bubel/ecs/simple_vector.d | 40 ++-- source/bubel/ecs/std.d | 215 +++++++++++-------- source/bubel/ecs/vector.d | 173 ++++++++++----- tests/basic.d | 153 ++++++++----- tests/bugs.d | 142 +++++++++++++ tests/runner.d | 6 +- 16 files changed, 988 insertions(+), 544 deletions(-) create mode 100644 tests/bugs.d diff --git a/source/bubel/ecs/atomic.d b/source/bubel/ecs/atomic.d index 9155c8f..8ea43df 100644 --- a/source/bubel/ecs/atomic.d +++ b/source/bubel/ecs/atomic.d @@ -9,9 +9,9 @@ License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.atomic; -version(Emscripten)version = ECSEmscripten; +version (Emscripten) version = ECSEmscripten; -version(ECSEmscripten) +version (ECSEmscripten) { import std.traits; @@ -24,74 +24,109 @@ version(ECSEmscripten) seq } - extern(C) ubyte emscripten_atomic_cas_u8(void *addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_cas_u16(void *addr, ushort oldVal, ushort newVal) @nogc nothrow pure; - extern(C) uint emscripten_atomic_cas_u32(void *addr, uint oldVal, uint newVal) @nogc nothrow pure; + extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure; + extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure; - extern(C) ubyte emscripten_atomic_load_u8(const void *addr) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_load_u16(const void *addr) @nogc nothrow pure; - extern(C) uint emscripten_atomic_load_u32(const void *addr) @nogc nothrow pure; + extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure; + extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure; - extern(C) ubyte emscripten_atomic_store_u8(void *addr, ubyte val) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_store_u16(void *addr, ushort val) @nogc nothrow pure; - extern(C) uint emscripten_atomic_store_u32(void *addr, uint val) @nogc nothrow pure; + extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure; - extern(C) ubyte emscripten_atomic_add_u8(void *addr, ubyte val) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_add_u16(void *addr, ushort val) @nogc nothrow pure; - extern(C) uint emscripten_atomic_add_u32(void *addr, uint val) @nogc nothrow pure; + extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure; - extern(C) ubyte emscripten_atomic_sub_u8(void *addr, ubyte val) @nogc nothrow pure; - extern(C) ushort emscripten_atomic_sub_u16(void *addr, ushort val) @nogc nothrow pure; - extern(C) uint emscripten_atomic_sub_u32(void *addr, uint val) @nogc nothrow pure; + extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure; public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) { - static if(op == "+=") + static if (op == "+=") { - static if(is(T == byte) || is(T == ubyte))return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, cast(Unqual!T)mod) + 1); - else static if(is(T == short) || is(T == ushort))return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, cast(Unqual!T)mod) + 1); - else static if(is(T == int) || is(T == uint))return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, cast(Unqual!T)mod) + 1); - else static assert(0); + static if (is(T == byte) || is(T == ubyte)) + return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else static if (is(T == short) || is(T == ushort)) + return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else static if (is(T == int) || is(T == uint)) + return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else + static assert(0); } - else static if(op == "-=") + else static if (op == "-=") { - static if(is(T == byte) || is(T == ubyte))return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, cast(Unqual!T)mod) - 1); - else static if(is(T == short) || is(T == ushort))return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, cast(Unqual!T)mod) - 1); - else static if(is(T == int) || is(T == uint))return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, cast(Unqual!T)mod) - 1); - else static assert(0); + static if (is(T == byte) || is(T == ubyte)) + return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else static if (is(T == short) || is(T == ushort)) + return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else static if (is(T == int) || is(T == uint)) + return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else + static assert(0); } } - public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) + public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, + V newval) { alias UT = Unqual!T; - static if(is(UT == bool) || is(UT == byte) || is(UT == ubyte))emscripten_atomic_store_u8(cast(void*)&val, cast(UT)newval); - else static if(is(UT == short) || is(UT == ushort))emscripten_atomic_store_u16(cast(void*)&val, cast(UT)newval); - else static if(is(UT == int) || is(UT == uint))emscripten_atomic_store_u32(cast(void*)&val, cast(UT)newval); - else static assert(0); + static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte)) + emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval); + else static if (is(UT == short) || is(UT == ushort)) + emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval); + else static if (is(UT == int) || is(UT == uint)) + emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval); + else + static assert(0); } - public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref const T val) + public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( + ref const T val) { alias UT = Unqual!T; - static if(is(UT == bool))return emscripten_atomic_load_u8(cast(const void*)&val) != 0; - else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_load_u8(cast(const void*)&val); - else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_load_u16(cast(const void*)&val); - else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_load_u32(cast(const void*)&val); - else static assert(0); + static if (is(UT == bool)) + return emscripten_atomic_load_u8(cast(const void*)&val) != 0; + else static if (is(UT == byte) || is(UT == ubyte)) + return emscripten_atomic_load_u8(cast(const void*)&val); + else static if (is(UT == short) || is(UT == ushort)) + return emscripten_atomic_load_u16(cast(const void*)&val); + else static if (is(UT == int) || is(UT == uint)) + return emscripten_atomic_load_u32(cast(const void*)&val); + else + static assert(0); } - public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) + public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, + MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) { alias UT = Unqual!T; - static if(is(UT == bool))return emscripten_atomic_cas_u8(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_cas_u8(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_cas_u16(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_cas_u32(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis; - else static assert(0); + static if (is(UT == bool)) + return emscripten_atomic_cas_u8(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == byte) || is(UT == ubyte)) + return emscripten_atomic_cas_u8(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == short) || is(UT == ushort)) + return emscripten_atomic_cas_u16(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == int) || is(UT == uint)) + return emscripten_atomic_cas_u32(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else + static assert(0); } } else { public import core.atomic; -} \ No newline at end of file +} diff --git a/source/bubel/ecs/attributes.d b/source/bubel/ecs/attributes.d index 2bc0aec..d094aad 100644 --- a/source/bubel/ecs/attributes.d +++ b/source/bubel/ecs/attributes.d @@ -25,4 +25,4 @@ module bubel.ecs.attributes; ///Used to mark optional components for system. enum optional = "optional"; ///Used to mark readonly components for system. "const" can be used insted. -enum readonly = "readonly"; \ No newline at end of file +enum readonly = "readonly"; diff --git a/source/bubel/ecs/block_allocator.d b/source/bubel/ecs/block_allocator.d index 5a2d02c..d3070c4 100644 --- a/source/bubel/ecs/block_allocator.d +++ b/source/bubel/ecs/block_allocator.d @@ -35,7 +35,7 @@ struct BlockAllocator */ void freeBlock(void* block) nothrow @nogc { - *cast(void**)block = next_block; + *cast(void**) block = next_block; next_block = block; } @@ -44,9 +44,9 @@ struct BlockAllocator */ void freeMemory() nothrow @nogc { - while(pointers) + while (pointers) { - foreach(i;0..pointers.numberof) + foreach (i; 0 .. pointers.numberof) { Mallocator.alignDispose(pointers.blocks[i]); } @@ -60,12 +60,14 @@ private: void allocBlock() nothrow @nogc { - next_block = cast(void*) Mallocator.alignAlloc( - block_size * blocks_in_allocation, block_size); - if(next_block is null)assert(0); + next_block = cast(void*) Mallocator.alignAlloc(block_size * blocks_in_allocation, + block_size); + if (next_block is null) + assert(0); - if(pointers is null)pointers = Mallocator.make!BlockPointers; - if(pointers.numberof >= 32) + if (pointers is null) + pointers = Mallocator.make!BlockPointers; + if (pointers.numberof >= 32) { BlockPointers* new_pointers = Mallocator.make!BlockPointers; new_pointers.next_pointers = pointers; @@ -78,8 +80,7 @@ private: void** pointer = cast(void**)(next_block + i * block_size); *pointer = next_block + (i + 1) * block_size; } - void** pointer = cast(void**)( - next_block + (blocks_in_allocation - 1) * block_size); + void** pointer = cast(void**)(next_block + (blocks_in_allocation - 1) * block_size); *pointer = null; } diff --git a/source/bubel/ecs/core.d b/source/bubel/ecs/core.d index 4a90928..c032d21 100644 --- a/source/bubel/ecs/core.d +++ b/source/bubel/ecs/core.d @@ -75,19 +75,18 @@ static struct ECS { __gshared ushort component_id = ushort.max; - ComponentRef ref_() @nogc nothrow + ComponentRef ref_() @nogc nothrow return { - return ComponentRef(&this,component_id); + return ComponentRef(&this, component_id); } } /************************************************************************************************************************ Mark structure as Event. Should be added on top of structure (before any data). */ - mixin template Event() + mixin template Event() { __gshared ushort event_id = ushort.max; - EntityID entity_id; } /************************************************************************************************************************ @@ -113,4 +112,4 @@ static struct ECS { alias WritableDependencies = T; } -} \ No newline at end of file +} diff --git a/source/bubel/ecs/entity.d b/source/bubel/ecs/entity.d index f24ddf3..6a64d50 100644 --- a/source/bubel/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -39,11 +39,7 @@ struct Entity if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) return null; - static if (EntityID.sizeof == 8) - uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) >> 3); - else - uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) / EntityID.sizeof()); - return cast(T*)(cast(void*)block + info.deltas[T.component_id] + ind * T.sizeof); + return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof); } bool hasComponent(ushort component_id) @@ -58,10 +54,7 @@ struct Entity { EntityMeta meta; meta.block = gEM.getMetaData(&this); - static if (EntityID.sizeof == 8) - meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) >> 3); - else - meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) / EntityID.sizeof()); + meta.index = meta.block.entityIndex(&this); return meta; } } diff --git a/source/bubel/ecs/events.d b/source/bubel/ecs/events.d index 25b864d..64e0c79 100644 --- a/source/bubel/ecs/events.d +++ b/source/bubel/ecs/events.d @@ -20,7 +20,7 @@ package struct EventManager void destroy() nothrow @nogc { - if(event_block_alloc_mutex) + if (event_block_alloc_mutex) { event_block_alloc_mutex.destroy(); Mallocator.dispose(event_block_alloc_mutex); @@ -30,14 +30,14 @@ package struct EventManager export void sendEvent(Ev)(EntityID id, Ev event, uint thread_id = 0) nothrow @nogc { - uint block_id = current_index+thread_id; + uint block_id = current_index + thread_id; EventData* data = &events[Ev.event_id]; EventBlock* block = data.blocks[block_id]; //EntityManager.EventInfo* info = &manager.events[Ev.event_id]; - event.entity_id = id; + //event.entity_id = id; - if(block is null) + if (block is null) { event_block_alloc_mutex.lock(); block = cast(EventBlock*) allocator.getBlock(); @@ -48,35 +48,42 @@ package struct EventManager data.blocks[block_id] = block; } - if(block.count >= data.max_events) + if (block.count >= data.max_events) { event_block_alloc_mutex.lock(); EventBlock* new_block = cast(EventBlock*) allocator.getBlock(); event_block_alloc_mutex.unlock(); - + *new_block = EventBlock(); block.next = new_block; block = new_block; data.blocks[block_id] = block; } - Ev* event_array = cast(Ev*)(cast(void*)block + data.data_offset); - event_array[block.count] = event; + uint size = Ev.sizeof + EntityID.sizeof; + void* ptr = cast(void*) block + data.data_offset + block.count * size; + *cast(EntityID*)ptr = id; + *cast(Ev*)(ptr + EntityID.sizeof) = event; + //Ev* event_array = cast(Ev*)(cast(void*) block + data.data_offset); + //event_array[block.count] = event; block.count++; } void swapCurrent() nothrow @nogc { - uint threads_count = cast(uint)manager.threads.length; - if(current_index == 0)current_index = threads_count; - else current_index = 0; + uint threads_count = cast(uint) manager.threads.length; + if (current_index == 0) + current_index = threads_count; + else + current_index = 0; - foreach(ref event;events) + foreach (ref event; events) { - foreach(ref first_block; event.first_blocks[current_index .. current_index + threads_count]) + foreach (ref first_block; event.first_blocks[current_index + .. current_index + threads_count]) { EventBlock* block = first_block; - while(block) + while (block) { EventBlock* to_dispose = block; block = block.next; @@ -84,7 +91,7 @@ package struct EventManager } first_block = null; } - foreach(ref block; event.blocks[current_index .. current_index + threads_count]) + foreach (ref block; event.blocks[current_index .. current_index + threads_count]) { block = null; } @@ -94,12 +101,12 @@ package struct EventManager void clearEvents() nothrow @nogc { //uint threads_count = cast(uint)manager.threads.length; - foreach(ref event;events) + foreach (ref event; events) { - foreach(ref first_block; event.first_blocks) + foreach (ref first_block; event.first_blocks) { EventBlock* block = first_block; - while(block) + while (block) { EventBlock* to_dispose = block; block = block.next; @@ -107,7 +114,7 @@ package struct EventManager } first_block = null; } - foreach(ref block; event.blocks) + foreach (ref block; event.blocks) { block = null; } @@ -118,23 +125,25 @@ package struct EventManager { disposeData(); events = Mallocator.makeArray!EventData(manager.events.length); - foreach(i,ref event;events) + foreach (i, ref event; events) { - event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2); - event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2); - event.data_offset = EventBlock.sizeof;//manager.events[i]. + event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2); + event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2); + event.data_offset = EventBlock.sizeof; //manager.events[i]. manager.alignNum(event.data_offset, manager.events[i].alignment); - event.max_events = cast(ushort)((events_block_size - event.data_offset) / manager.events[i].size); + uint size = manager.events[i].size + EntityID.sizeof; + event.max_events = cast(ushort)( + (events_block_size - event.data_offset) / size); } } private void disposeData() nothrow @nogc { clearEvents(); - if(events) + if (events) { - foreach(ref event;events) + foreach (ref event; events) { Mallocator.dispose(event.blocks); Mallocator.dispose(event.first_blocks); @@ -166,7 +175,7 @@ package struct EventManager ushort max_events; EventBlock*[] blocks; EventBlock*[] first_blocks; - + //EventBlock*[] current_blocks; } diff --git a/source/bubel/ecs/hash_map.d b/source/bubel/ecs/hash_map.d index 1f7a1f8..6ae6a79 100755 --- a/source/bubel/ecs/hash_map.d +++ b/source/bubel/ecs/hash_map.d @@ -11,36 +11,44 @@ private enum HASH_EMPTY = 0; private enum HASH_DELETED = 0x1; private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1; -export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc { - static if (isIntegral!(T)) { +export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc +{ + static if (isIntegral!(T)) + { return hashInt(t); - } else { - return 0;//hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts + } + else + { + return 0; //hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts } } // Can turn bad hash function to good one -export ulong hashInt(ulong x) nothrow @nogc @safe { +export ulong hashInt(ulong x) nothrow @nogc @safe +{ x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb; x = x ^ (x >> 31); return x; } -struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { +struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) +{ alias Key = KeyPar; alias Value = ValuePar; - nothrow: +nothrow: enum rehashFactor = 0.75; enum size_t getIndexEmptyValue = size_t.max; - static struct KeyVal { + static struct KeyVal + { Key key; Value value; } - static struct Bucket { + static struct Bucket + { ulong hash; KeyVal keyValue; } @@ -49,58 +57,74 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { size_t length; // Used to compute loadFactor size_t markerdDeleted; - export void clear() { + export void clear() + { elements.clear(); length = 0; markerdDeleted = 0; } - export void reset() { + export void reset() + { elements.reset(); length = 0; markerdDeleted = 0; } - export bool isIn(ref Key el) { + export bool isIn(ref Key el) + { return getIndex(el) != getIndexEmptyValue; } - export bool isIn(Key el) { + export bool isIn(Key el) + { return getIndex(el) != getIndexEmptyValue; } - export Value* getPtr()(auto ref Key k) { + export Value* getPtr()(auto ref Key k) + { size_t index = getIndex(k); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { return null; - } else { + } + else + { return &elements[index].keyValue.value; } } - export ref Value get()(auto ref Key k) { + export ref Value get()(auto ref Key k) + { size_t index = getIndex(k); assert(index != getIndexEmptyValue); return elements[index].keyValue.value; } deprecated("Use get with second parameter.") export auto ref Value getDefault()( - auto ref Key k, auto ref Value defaultValue) { + auto ref Key k, auto ref Value defaultValue) + { return get(k, defaultValue); } - export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) { + export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) + { size_t index = getIndex(k); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { return defaultValue; - } else { + } + else + { return elements[index].keyValue.value; } } - export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) { + export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) + { size_t index = getIndex(k); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { add(k, defaultValue); } index = getIndex(k); @@ -109,9 +133,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - export bool tryRemove(Key el) { + export bool tryRemove(Key el) + { size_t index = getIndex(el); - if (index == getIndexEmptyValue) { + if (index == getIndexEmptyValue) + { return false; } length--; @@ -120,28 +146,34 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return true; } - export void remove(Key el) { + export void remove(Key el) + { bool ok = tryRemove(el); assert(ok); } - export ref Value opIndex()(auto ref Key key) { + export ref Value opIndex()(auto ref Key key) + { return get(key); } - export void opIndexAssign()(auto ref Value value, auto ref Key key) { + export void opIndexAssign()(auto ref Value value, auto ref Key key) + { add(key, value); } - export void add()(auto ref Key key, auto ref Value value) { + export void add()(auto ref Key key, auto ref Value value) + { size_t index = getIndex(key); - if (index != getIndexEmptyValue) { + if (index != getIndexEmptyValue) + { elements[index].keyValue.value = value; return; } if (getLoadFactor(length + 1) > rehashFactor - || getLoadFactor(length + markerdDeleted) > rehashFactor) { + || getLoadFactor(length + markerdDeleted) > rehashFactor) + { rehash(); } length++; @@ -150,10 +182,13 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { immutable size_t rotateMask = elements.length - 1; index = hash & rotateMask; // Starting point - while (true) { + while (true) + { Bucket* gr = &elements[index]; - if ((gr.hash & HASH_FILLED_MARK) == 0) { - if (gr.hash == HASH_DELETED) { + if ((gr.hash & HASH_FILLED_MARK) == 0) + { + if (gr.hash == HASH_DELETED) + { markerdDeleted--; } gr.hash = hash; @@ -171,15 +206,18 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { //int numA; //int numB; - export size_t getIndex(Key el) { + export size_t getIndex(Key el) + { return getIndex(el); } - export size_t getIndex(ref Key el) { + export size_t getIndex(ref Key el) + { mixin(doNotInline); immutable size_t groupsLength = elements.length; - if (groupsLength == 0) { + if (groupsLength == 0) + { return getIndexEmptyValue; } @@ -188,13 +226,16 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { size_t index = hash & rotateMask; // Starting point //numA++; - while (true) { + while (true) + { //numB++; Bucket* gr = &elements[index]; - if (gr.hash == hash && gr.keyValue.key == el) { + if (gr.hash == hash && gr.keyValue.key == el) + { return index; } - if (gr.hash == HASH_EMPTY) { + if (gr.hash == HASH_EMPTY) + { return getIndexEmptyValue; } @@ -204,21 +245,26 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - export float getLoadFactor(size_t forElementsNum) { - if (elements.length == 0) { + export float getLoadFactor(size_t forElementsNum) + { + if (elements.length == 0) + { return 1; } return cast(float) forElementsNum / (elements.length); } - export void rehash()() { + export void rehash()() + { mixin(doNotInline); // Get all elements Vector!KeyVal allElements; allElements.reserve(elements.length); - foreach (ref Bucket el; elements) { - if ((el.hash & HASH_FILLED_MARK) == 0) { + foreach (ref Bucket el; elements) + { + if ((el.hash & HASH_FILLED_MARK) == 0) + { el.hash = HASH_EMPTY; continue; } @@ -227,12 +273,14 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - if (getLoadFactor(length + 1) > rehashFactor) { // Reallocate + if (getLoadFactor(length + 1) > rehashFactor) + { // Reallocate elements.length = (elements.length ? elements.length : 4) << 1; // Power of two, initially 8 elements } // Insert elements - foreach (i, ref el; allElements) { + foreach (i, ref el; allElements) + { add(el.key, el.value); } length = allElements.length; @@ -240,19 +288,29 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } // foreach support - export int opApply(DG)(scope DG dg) { + export int opApply(DG)(scope DG dg) + { int result; - foreach (ref Bucket gr; elements) { - if ((gr.hash & HASH_FILLED_MARK) == 0) { + foreach (ref Bucket gr; elements) + { + if ((gr.hash & HASH_FILLED_MARK) == 0) + { continue; } - static if (isForeachDelegateWithTypes!(DG, Key)) { + static if (isForeachDelegateWithTypes!(DG, Key)) + { result = dg(gr.keyValue.key); - } else static if (isForeachDelegateWithTypes!(DG, Value)) { + } + else static if (isForeachDelegateWithTypes!(DG, Value)) + { result = dg(gr.keyValue.value); - } else static if (isForeachDelegateWithTypes!(DG, Key, Value)) { + } + else static if (isForeachDelegateWithTypes!(DG, Key, Value)) + { result = dg(gr.keyValue.key, gr.keyValue.value); - } else { + } + else + { static assert(0); } if (result) @@ -263,9 +321,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - export int byKey(scope int delegate(Key k) nothrow dg) { + export int byKey(scope int delegate(Key k) nothrow dg) + { int result; - foreach (ref Key k; this) { + foreach (ref Key k; this) + { result = dg(k); if (result) break; @@ -273,9 +333,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - export int byValue(scope int delegate(ref Value k) nothrow dg) { + export int byValue(scope int delegate(ref Value k) nothrow dg) + { int result; - foreach (ref Value v; this) { + foreach (ref Value v; this) + { result = dg(v); if (result) break; @@ -283,13 +345,15 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) { + export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) + { int result; - foreach (ref Key k, ref Value v; this) { + foreach (ref Key k, ref Value v; this) + { result = dg(k, v); if (result) break; } return result; } -} \ No newline at end of file +} diff --git a/source/bubel/ecs/id_manager.d b/source/bubel/ecs/id_manager.d index 4c3a2c3..084a947 100644 --- a/source/bubel/ecs/id_manager.d +++ b/source/bubel/ecs/id_manager.d @@ -18,7 +18,7 @@ struct IDManager pragma(inline, false) EntityID getNewID() nothrow @nogc { int current = m_stack_top.atomicOp!"-="(1) + 1; - if(current < 0) + if (current < 0) { uint add_id = m_last_id.atomicOp!"+="(1) - 1; @@ -29,7 +29,7 @@ struct IDManager if (block_id >= m_blocks_count) { add_mutex.lock(); - if(block_id >= m_blocks_count) + if (block_id >= m_blocks_count) { m_blocks[m_blocks_count].alloc(); m_blocks_count++; @@ -112,9 +112,11 @@ struct IDManager */ export bool isExist(EntityID id) nothrow @nogc { - if(id.id >= m_ids_array.length)return false; + if (id.id >= m_ids_array.length) + return false; Data* data = &m_ids_array[id.id]; - if(data.entity is null)return false; + if (data.entity is null) + return false; return data.counter == id.counter; } @@ -126,7 +128,8 @@ struct IDManager m_ids_array = Mallocator.makeArray!Data(65536); m_free_stack = Mallocator.makeArray!uint(65536); m_blocks = Mallocator.makeArray!Block(64); - foreach(ref block;m_blocks)block = Block(); + foreach (ref block; m_blocks) + block = Block(); m_blocks_count = 1; m_blocks[0].alloc(); @@ -142,20 +145,23 @@ struct IDManager */ void deinitialize() @trusted @nogc nothrow { - if(m_ids_array)Mallocator.dispose(m_ids_array); - if(m_free_stack)Mallocator.dispose(m_free_stack); - if(m_blocks) + if (m_ids_array) + Mallocator.dispose(m_ids_array); + if (m_free_stack) + Mallocator.dispose(m_free_stack); + if (m_blocks) { - foreach(ref block;m_blocks) + foreach (ref block; m_blocks) { - if(block.data)block.free(); + if (block.data) + block.free(); } Mallocator.dispose(m_blocks); } - if(add_mutex) + if (add_mutex) { add_mutex.destroy(); - Mallocator.dispose(add_mutex);//cast(void*)add_mutex); //workaround for compiler bug + Mallocator.dispose(add_mutex); //cast(void*)add_mutex); //workaround for compiler bug add_mutex = null; } } @@ -165,27 +171,31 @@ struct IDManager */ void optimize() nothrow @nogc { - if(m_stack_top < -1)m_stack_top = -1; - if(m_last_id > m_ids_array.length) + if (m_stack_top < -1) + m_stack_top = -1; + if (m_last_id > m_ids_array.length) { - uint begin = cast(uint)m_ids_array.length; + uint begin = cast(uint) m_ids_array.length; Data[] new_array = Mallocator.makeArray!Data(begin + (m_blocks_count << 16)); memcpy(new_array.ptr, m_ids_array.ptr, m_ids_array.length * Data.sizeof); Mallocator.dispose(m_ids_array); m_ids_array = new_array; uint[] new_stack = Mallocator.makeArray!uint(m_ids_array.length); - memcpy(new_stack.ptr,m_free_stack.ptr,m_free_stack.length * uint.sizeof); + memcpy(new_stack.ptr, m_free_stack.ptr, m_free_stack.length * uint.sizeof); Mallocator.dispose(m_free_stack); m_free_stack = new_stack; - foreach(block;m_blocks[0..m_blocks_count-1]) + foreach (block; m_blocks[0 .. m_blocks_count - 1]) { - memcpy(cast(void*)m_ids_array.ptr + begin * Data.sizeof, block.data.ptr, 65536 * Data.sizeof); + memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof, + block.data.ptr, 65536 * Data.sizeof); begin += 65536; } - memcpy(cast(void*)m_ids_array.ptr + begin * Data.sizeof, m_blocks[m_blocks_count-1].data.ptr, (m_last_id - begin) * Data.sizeof); - foreach(ref block;m_blocks[1..m_blocks_count])block.free(); + memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof, + m_blocks[m_blocks_count - 1].data.ptr, (m_last_id - begin) * Data.sizeof); + foreach (ref block; m_blocks[1 .. m_blocks_count]) + block.free(); m_blocks_count = 1; } } @@ -217,12 +227,12 @@ struct IDManager private: Mutex* add_mutex; - + Data[] m_ids_array = null; uint m_blocks_count = 0; Block[] m_blocks; uint[] m_free_stack = null; - + align(64) shared uint m_last_id = 0; align(64) shared int m_stack_top = -1; } diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 3ac16e8..cf3712d 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -126,7 +126,7 @@ export struct EntityManager //if(info.components)Mallocator.dispose(info.components); Mallocator.dispose(info); - } + } foreach (UpdatePass* pass; passes) { @@ -402,7 +402,7 @@ export struct EntityManager Sys* data_system = cast(Sys*) data.system_pointer; Type* event = cast(Type*) data.event; - data_system.handleEvent(gEM.getEntity(event.entity_id), *event); + data_system.handleEvent(data.entity, *event); } void setEventCallers(Sys)(ref System system) @@ -474,7 +474,7 @@ export struct EntityManager { //continue; } - else + else { string name; static if (isArray!MemberType) @@ -1018,8 +1018,7 @@ export struct EntityManager static if (hasMember!(Sys.EntitiesData, "job_id")) { - input_data.job_id = cast(typeof(input_data.job_id)) data - .job_id; + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; } //s.onUpdate(input_data); @@ -1052,8 +1051,7 @@ export struct EntityManager static if (hasMember!(Sys.EntitiesData, "job_id")) { - input_data.job_id = cast(typeof(input_data.job_id)) data - .job_id; + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; } (cast(typeof(&__traits(getOverloads, s, @@ -1146,7 +1144,8 @@ export struct EntityManager foreach (iii, comp_info; components_info.readonlyDeps) { - ushort comp = external_dependencies_map.get(cast(const (char)[]) comp_info.type, ushort.max); + ushort comp = external_dependencies_map.get(cast(const(char)[]) comp_info.type, + ushort.max); version (D_BetterC) assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof @@ -1156,7 +1155,7 @@ export struct EntityManager ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); system.m_readonly_dependencies[iii] = comp; } - + foreach (iii, comp_info; components_info.writableDeps) { ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); @@ -1174,7 +1173,9 @@ export struct EntityManager if (sys_id < systems.length) { systems[sys_id].disable(); - if(systems[sys_id].m_destroy)(cast(void function(void*)) systems[sys_id].m_destroy)(systems[sys_id].m_system_pointer); + if (systems[sys_id].m_destroy) + (cast(void function(void*)) systems[sys_id].m_destroy)( + systems[sys_id].m_system_pointer); if (system.m_create) (cast(void function(void*)) system.m_create)(system.m_system_pointer); @@ -1236,7 +1237,7 @@ export struct EntityManager export void registerDependency(const(char)[] name) { - return external_dependencies_map.add(name, cast(ushort)external_dependencies_map.length); + return external_dependencies_map.add(name, cast(ushort) external_dependencies_map.length); } /************************************************************************************************************************ @@ -1274,7 +1275,10 @@ export struct EntityManager info.create_callback = &callCreate; } - info.size = Comp.sizeof; + static if (Comp.sizeof == 1 && Fields!(Comp).length == 0) + info.size = 0; + else + info.size = Comp.sizeof; info.alignment = Comp.alignof; //8; info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof); *cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); @@ -1340,7 +1344,9 @@ export struct EntityManager "Can't call function with system which hasn't EntitesData structure."); static assert(__traits(hasMember, Sys, "onUpdate"), "Can't call function with system which hasn't onUpdate function callback."); - static assert(is(SetFunctionAttributes!(T,functionLinkage!(s.onUpdate), functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), "Function must match system update function."); + static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate), + functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), + "Function must match system update function."); static assert(__traits(hasMember, Sys, "system_id"), "Sys must be system type."); System* system = getSystem(Sys.system_id); @@ -1612,6 +1618,8 @@ export struct EntityManager //fill components with default data foreach (comp; info.components) { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], components[comp].init_data.ptr, components[comp].size); } @@ -1621,6 +1629,8 @@ export struct EntityManager ushort index = block.entityIndex(entity); foreach (comp; info.components) { + if (components[comp].size == 0) + continue; memcpy(cast(void*) temp.entity_data.ptr + info.tmpl_deltas[comp], cast(void*) block + info.deltas[comp] + components[comp].size * index, components[comp].size); @@ -1669,6 +1679,8 @@ export struct EntityManager //fill components with default data foreach (comp; info.components) { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], components[comp].init_data.ptr, components[comp].size); } @@ -1736,14 +1748,19 @@ export struct EntityManager //fill components with default data and copy from base template foreach (comp; info.components) { - if (comp < base_tmpl.info.deltas.length && base_tmpl.info.deltas[comp] != ushort.max) //copy data from base component - { + if (comp < base_tmpl.info.tmpl_deltas.length + && base_tmpl.info.tmpl_deltas[comp] != ushort.max) //copy data from base component + { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp], components[comp].size); } else //fill with default data { + if (components[comp].size == 0) + continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], components[comp].init_data.ptr, components[comp].size); } @@ -1833,8 +1850,8 @@ export struct EntityManager } info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length); - info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length, - info); + //info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length); + info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); foreach (comp; info.components) { @@ -1894,11 +1911,11 @@ export struct EntityManager } add_len++; //move elements after new listener - if(add_len < tmp_add.length) - for (int k = add_len; k > j; k--) - { - tmp_add[k] = tmp_add[k - 1]; - } + if (add_len < tmp_add.length) + for (int k = add_len; k > j; k--) + { + tmp_add[k] = tmp_add[k - 1]; + } //assign listener tmp_add[j] = cast(ushort) i; } @@ -1914,11 +1931,11 @@ export struct EntityManager } rem_len++; //move elements after new listener - if(rem_len < tmp_add.length) - for (int k = rem_len; k > j; k--) - { - tmp_rem[k] = tmp_rem[k - 1]; - } + if (rem_len < tmp_add.length) + for (int k = rem_len; k > j; k--) + { + tmp_rem[k] = tmp_rem[k - 1]; + } //assign listener tmp_rem[j] = cast(ushort) i; } @@ -1934,11 +1951,11 @@ export struct EntityManager } ch_len++; //move elements after new listener - if(ch_len < tmp_add.length) - for (int k = ch_len; k > j; k--) - { - tmp_ch[k] = tmp_ch[k - 1]; - } + if (ch_len < tmp_add.length) + for (int k = ch_len; k > j; k--) + { + tmp_ch[k] = tmp_ch[k - 1]; + } //assign listener tmp_ch[j] = cast(ushort) i; } @@ -2160,6 +2177,8 @@ export struct EntityManager foreach (comp; new_info.components) { uint comp_size = components[comp].size; + if (comp_size == 0) + continue; memcpy(cast(void*) new_block + new_info.deltas[comp] + new_block.entities_count * comp_size, cast(void*) block + info.deltas[comp] + ind * comp_size, comp_size); } @@ -2296,24 +2315,29 @@ export struct EntityManager foreach (id; new_info.components) //ids[0 .. len]) { - void* dst = cast(void*) new_block + new_info.deltas[id] + ( - new_block.entities_count) * components[id].size; uint size = components[id].size; + void* dst = void; + if (size != 0) + dst = cast(void*) new_block + new_info.deltas[id] + (new_block.entities_count) + * size; if (k >= new_ids.length) { - memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); + if (size != 0) + memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); j++; } else if (j >= info.components.length || id == new_ids[k]) { - memcpy(dst, data_pointers[k], size); + if (size != 0) + memcpy(dst, data_pointers[k], size); k++; } else { assert(id != new_ids[0]); - memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); + if (size != 0) + memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); j++; } } @@ -2376,28 +2400,30 @@ export struct EntityManager //__addComponents(entity_id, new_ids, pointers); ComponentRef[num] _comps; - static foreach(i, comp; comps) + static foreach (i, comp; comps) { _comps[i] = comp.ref_; } addComponents(entity_id, _comps); - + } export void addComponents(const EntityID entity_id, ComponentRef[] comps) nothrow @nogc { - uint num = cast(uint)comps.length; + uint num = cast(uint) comps.length; ThreadData* data = &threads[threadID]; data.changeEntitiesList.add(cast(ubyte) 1u); data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.sizeof]); - foreach(ref_; comps) + foreach (ref_; comps) { data.changeEntitiesList.add((cast(ubyte*)&ref_.component_id)[0 .. ushort.sizeof]); } - foreach(ref_; comps) + foreach (ref_; comps) { - data.changeEntitiesList.add((cast(ubyte*)ref_.ptr)[0 .. components[ref_.component_id].size]); + if (components[ref_.component_id].size != 0) + data.changeEntitiesList.add( + (cast(ubyte*) ref_.ptr)[0 .. components[ref_.component_id].size]); } /*data.changeEntitiesList.add(cast(ubyte[]) new_ids); static foreach (i, comp; comps) @@ -2449,14 +2475,15 @@ export struct EntityManager foreach (i, comp; info.components) { - memcpy(cast(void*) new_block + info.deltas[comp] + components[comp].size * new_id, - cast(void*) block + info.deltas[comp] + components[comp].size * index, - components[comp].size); + ushort size = components[comp].size; + if (size != 0) + memcpy(cast(void*) new_block + info.deltas[comp] + size * new_id, + cast(void*) block + info.deltas[comp] + size * index, size); if (components[comp].create_callback) { - components[comp].create_callback(cast( - void*) block + info.deltas[comp] + new_id * components[comp].size); + components[comp].create_callback( + cast(void*) block + info.deltas[comp] + new_id * size); } } @@ -2552,20 +2579,21 @@ export struct EntityManager foreach (comp; info.components) { uint size = components[comp].size; - memcpy(cast(void*) block + info.deltas[comp] + size * id, - tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); + if (size != 0) + memcpy(cast(void*) block + info.deltas[comp] + size * id, + tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); } - foreach(comp; replacement) + foreach (comp; replacement) { - if(comp.component_id < info.deltas.length) + if (comp.component_id < info.deltas.length) { ushort delta = info.deltas[comp.component_id]; - if(delta != ushort.max) + if (delta != ushort.max) { uint size = components[comp.component_id].size; - memcpy(cast(void*) block + delta + size * id, - comp.ptr, size); + if (size != 0) + memcpy(cast(void*) block + delta + size * id, comp.ptr, size); } } } @@ -2678,8 +2706,10 @@ export struct EntityManager { //get entity and block meta data pointers Entity* entity = id_manager.getEntityPointer(id); + if (entity is null) return; //return if entity doesn't exist + EntitiesBlock* block = getMetaData(entity); EntityInfo* info = block.type_info; @@ -2700,6 +2730,9 @@ export struct EntityManager { EntityInfo* info = block.type_info; + if (info.last_block.added_count) + updateBlock(info.last_block); + info.last_block.entities_count--; uint pos = block.entityIndex(entity); @@ -2720,9 +2753,11 @@ export struct EntityManager { foreach (comp; info.components) { + uint size = components[comp].size; + if (size == 0) + continue; void* src = cast(void*) info.last_block + info.deltas[comp]; void* dst = cast(void*) block + info.deltas[comp]; - uint size = components[comp].size; memcpy(dst + pos * size, src + info.last_block.entities_count * size, size); } @@ -2768,7 +2803,8 @@ export struct EntityManager { uint index = 0; uint len = cast(uint) thread.changeEntitiesListPrev.length; - if(len)has_work = true; + if (len) + has_work = true; void*[32] pointers; // = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; while (index < len) { @@ -2798,7 +2834,7 @@ export struct EntityManager pointers[i] = &thread.changeEntitiesListPrev[index]; index += components[ids[i]].size; } - + __addComponents(id, ids, pointers[0 .. num]); } } @@ -2885,6 +2921,23 @@ export struct EntityManager (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_change_entity)(data); } + private void updateBlock(EntitiesBlock* block) @nogc nothrow + { + EntityInfo* info = block.type_info; + ushort entities_count = block.entities_count; + block.entities_count += block.added_count; + if (block.entities_count > block.type_info.max_entities) + { + block.entities_count = block.type_info.max_entities; + } + block.added_count.atomicStore(cast(ushort) 0); + + if (info.add_listeners) + { + callAddEntityListeners(info, block, entities_count, block.entities_count); + } + } + private bool updateBlocks() { bool has_work = false; @@ -2892,22 +2945,11 @@ export struct EntityManager foreach (ref ThreadData thread; threads) { //thread.swapToUpdate(); - if(thread.blockToUpdatePrev.length)has_work = true; + if (thread.blockToUpdatePrev.length) + has_work = true; foreach (block; thread.blockToUpdatePrev) { - EntityInfo* info = block.type_info; - ushort entities_count = block.entities_count; - block.entities_count += block.added_count; - if (block.entities_count > block.type_info.max_entities) - { - block.entities_count = block.type_info.max_entities; - } - block.added_count.atomicStore(cast(ushort) 0); - - if (info.add_listeners) - { - callAddEntityListeners(info, block, entities_count, block.entities_count); - } + updateBlock(block); } thread.blockToUpdatePrev.clear(); } @@ -2920,7 +2962,8 @@ export struct EntityManager //foreach (ref ThreadData thread; threads)thread.swapToRemove(); foreach (ref ThreadData thread; threads) { - if(thread.entitiesToRemovePrev.length)has_work = true; + if (thread.entitiesToRemovePrev.length) + has_work = true; foreach (id; thread.entitiesToRemovePrev) { __removeEntity(id); @@ -2936,59 +2979,60 @@ export struct EntityManager // bool empty = true; //while (1) //{ - //event_manager.swapCurrent(); - uint current_index; - if (event_manager.current_index == 0) - current_index = cast(uint) threads.length; - else - current_index = 0; - foreach (i, event; event_manager.events) + //event_manager.swapCurrent(); + uint current_index; + if (event_manager.current_index == 0) + current_index = cast(uint) threads.length; + else + current_index = 0; + foreach (i, event; event_manager.events) + { + foreach (first_block; event.first_blocks[current_index .. current_index + threads + .length]) { - foreach (first_block; event.first_blocks[current_index - .. current_index + threads.length]) + EventManager.EventBlock* block = first_block; + if (block) + has_work = true; + // { + // has_work = true; + // //empty = false; + // } + while (block) { - EventManager.EventBlock* block = first_block; - if (block)has_work = true; - // { - // has_work = true; - // //empty = false; - // } - while (block) + EventCallData call_data; + void* event_pointer = cast(void*) block + event.data_offset; + foreach (j; 0 .. block.count) { - EventCallData call_data; - void* event_pointer = cast(void*) block + event.data_offset; - foreach (j; 0 .. block.count) + call_data.event = event_pointer + EntityID.sizeof; + EntityID entity_id = *cast(EntityID*)(event_pointer); + Entity* entity = id_manager.getEntityPointer(entity_id); + if (entity) { - call_data.event = event_pointer; - EntityID entity_id = *cast(EntityID*) event_pointer; - Entity* entity = id_manager.getEntityPointer(entity_id); - if (entity) - { - call_data.block = getMetaData(entity); - call_data.id = call_data.block.entityIndex(entity); + call_data.block = getMetaData(entity); + call_data.id = call_data.block.entityIndex(entity); + call_data.entity = entity; - foreach (caller; events[i].callers) - { - if ( - call_data.block.type_info.systems[caller.system.m_id] - == false) - continue; - call_data.system_pointer = caller.system.m_system_pointer; - (cast(void function( - ref EventCallData) nothrow @nogc) caller.callback)( - call_data); - } + foreach (caller; events[i].callers) + { + if (call_data.block.type_info.systems[caller.system.m_id] == false + || !caller.system.enabled || !caller.system.willExecute) + continue; + call_data.system_pointer = caller.system.m_system_pointer; + (cast(void function(ref EventCallData) nothrow @nogc) caller + .callback)(call_data); } - if(events[i].destroy_callback)events[i].destroy_callback(event_pointer); - event_pointer += events[i].size; } - block = block.next; + if (events[i].destroy_callback) + events[i].destroy_callback(event_pointer); + event_pointer += events[i].size + EntityID.sizeof; } + block = block.next; } } - // if (empty) - // break; - // empty = true; + } + // if (empty) + // break; + // empty = true; //} return has_work; } @@ -2996,7 +3040,7 @@ export struct EntityManager private void swapData() nothrow @nogc { event_manager.swapCurrent(); - foreach(ref ThreadData thread; threads) + foreach (ref ThreadData thread; threads) { thread.swapData(); } @@ -3005,13 +3049,13 @@ export struct EntityManager export void commit() { bool has_work = true; - while(has_work) - { + while (has_work) + { swapData(); has_work = false; has_work |= updateEvents(); - + id_manager.optimize(); has_work |= updateBlocks(); has_work |= changeEntities(); @@ -3222,14 +3266,15 @@ export struct EntityManager } } - const (UpdatePass)* getPass(const (char)[] name) + const(UpdatePass)* getPass(const(char)[] name) { ushort id = getPassID(name); - if(id == ushort.max)return null; + if (id == ushort.max) + return null; return passes[id]; } - ushort getPassID(const (char)[] name) + ushort getPassID(const(char)[] name) { return passes_map.get(name, ushort.max); } @@ -3267,6 +3312,7 @@ export struct EntityManager EntitiesBlock* block; void* system_pointer; void* event; + Entity* entity; ushort id; } @@ -3357,9 +3403,9 @@ export struct EntityManager return new_info; } - EntityInfo* getNewInfoRemove(ushort id) + EntityInfo* getNewInfoRemove(ushort id) return { - if (comp_rem_info.length <= id) + /*if (comp_rem_info.length <= id) { EntityInfo*[] new_infos = Mallocator.makeArray!(EntityInfo*)( instance.components.length, &this); @@ -3371,7 +3417,7 @@ export struct EntityManager Mallocator.dispose(comp_rem_info); } comp_rem_info = new_infos; - } + }*/ if (comp_rem_info[id]) return comp_rem_info[id]; @@ -3386,8 +3432,9 @@ export struct EntityManager ids[len++] = comp; } } - if (len == components.length) - return &this; + assert(len != components.length); + //if (len == components.length) + // return &this; assert(len == components.length - 1); @@ -3455,23 +3502,14 @@ export struct EntityManager */ struct EntitiesBlock { - ///return distance (in bytes) from begin of block to data - ///TODO: probably to remove. It's used by old code if I remeber correctly. - /*export uint dataDelta() nothrow @nogc pure - { - ushort dif = EntitiesBlock.sizeof; - alignNum(dif, type_info.alignment); - return dif; - }*/ - ///return pointer to first element in block - export void* dataBegin() nothrow @nogc pure + export void* dataBegin() nothrow @nogc pure return { ushort dif = EntitiesBlock.sizeof; return cast(void*)&this + dif; } - export ushort entityIndex(Entity* entity) nothrow @nogc pure + export ushort entityIndex(const(Entity)* entity) nothrow @nogc pure { static if (EntityID.sizeof == 8) return cast(ushort)((cast(void*) entity - dataBegin()) >> 3); @@ -3593,32 +3631,32 @@ export struct EntityManager struct ThreadData { - ref Vector!EntityID entitesToRemove() @nogc nothrow + ref Vector!EntityID entitesToRemove() @nogc nothrow return { return entities_to_remove[data_index]; } - ref SimpleVector changeEntitiesList() @nogc nothrow + ref SimpleVector changeEntitiesList() @nogc nothrow return { return change_entities_list[data_index]; } - ref Vector!(EntitiesBlock*) blockToUpdate() @nogc nothrow + ref Vector!(EntitiesBlock*) blockToUpdate() @nogc nothrow return { return blocks_to_update[data_index]; } - - ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow + + ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow return { return entities_to_remove[1 - data_index]; } - ref SimpleVector changeEntitiesListPrev() @nogc nothrow + ref SimpleVector changeEntitiesListPrev() @nogc nothrow return { return change_entities_list[1 - data_index]; } - ref Vector!(EntitiesBlock*) blockToUpdatePrev() @nogc nothrow + ref Vector!(EntitiesBlock*) blockToUpdatePrev() @nogc nothrow return { return blocks_to_update[1 - data_index]; } diff --git a/source/bubel/ecs/package.d b/source/bubel/ecs/package.d index ee3f62b..51da325 100644 --- a/source/bubel/ecs/package.d +++ b/source/bubel/ecs/package.d @@ -7,4 +7,4 @@ public import bubel.ecs.system; import bubel.ecs.events; import bubel.ecs.id_manager; -import bubel.ecs.std; \ No newline at end of file +import bubel.ecs.std; diff --git a/source/bubel/ecs/simple_vector.d b/source/bubel/ecs/simple_vector.d index 1de026e..bb4b610 100644 --- a/source/bubel/ecs/simple_vector.d +++ b/source/bubel/ecs/simple_vector.d @@ -16,10 +16,12 @@ struct SimpleVector ///Add element to vector void add(ubyte el) nothrow @nogc { - while(used >= data.length) + while (used >= data.length) { - if(data is null)data = Mallocator.makeArray!ubyte(1024); - else data = Mallocator.expandArray(data,data.length); + if (data is null) + data = Mallocator.makeArray!ubyte(1024); + else + data = Mallocator.expandArray(data, data.length); } data[used++] = el; } @@ -27,10 +29,12 @@ struct SimpleVector ///Add array of elements to vector void add(ubyte[] el) nothrow @nogc { - while(used + el.length >= data.length) + while (used + el.length >= data.length) { - if(data is null)data = Mallocator.makeArray!ubyte(1024); - else data = Mallocator.expandArray(data,data.length); + if (data is null) + data = Mallocator.makeArray!ubyte(1024); + else + data = Mallocator.expandArray(data, data.length); } memcpy(data.ptr + used, el.ptr, el.length); used += el.length; @@ -44,23 +48,23 @@ struct SimpleVector export ref ubyte opIndex(size_t pos) nothrow @nogc { - return data[pos]; - } + return data[pos]; + } - export ubyte[] opSlice() nothrow @nogc + export ubyte[] opSlice() nothrow @nogc { - return data[0 .. used]; - } + return data[0 .. used]; + } - export ubyte[] opSlice(size_t x, size_t y) nothrow @nogc + export ubyte[] opSlice(size_t x, size_t y) nothrow @nogc { - return data[x .. y]; - } + return data[x .. y]; + } - export size_t opDollar() nothrow @nogc + export size_t opDollar() nothrow @nogc { - return used; - } + return used; + } ///set vector length to 0 void clear() nothrow @nogc @@ -70,4 +74,4 @@ struct SimpleVector ubyte[] data = null; size_t used = 0; -} \ No newline at end of file +} diff --git a/source/bubel/ecs/std.d b/source/bubel/ecs/std.d index 6658639..a7dd846 100644 --- a/source/bubel/ecs/std.d +++ b/source/bubel/ecs/std.d @@ -7,45 +7,50 @@ License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.std; -version(Emscripten)version = ECSEmscripten; +version (Emscripten) version = ECSEmscripten; import std.traits; -version(ECSEmscripten) +version (ECSEmscripten) { - extern(C) struct pthread_mutex_t - { - union - { - int[6] __i; - void[6] *__p; + extern (C) struct pthread_mutex_t + { + union + { + int[6] __i; + void[6]* __p; } - } - - extern(C) struct pthread_mutexattr_t - { - uint __attr; } - extern(C) int memcmp (const void *s1, const void *s2, size_t size); - extern(C) void exit (int status) nothrow @nogc; - extern(C) void __assert(const(char)* msg, const(char)* file, uint line) { exit(-20);} - extern(C) void free(void*) @nogc nothrow @system; - extern(C) void* malloc(size_t size) @nogc nothrow @system; - extern(C) void* realloc(void*, size_t size) @nogc nothrow @system; - extern(C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system; - extern(C) void* memset(void*, int val, size_t size) @nogc nothrow @system; - extern(C) int posix_memalign(void**, size_t, size_t) @nogc nothrow @system; - extern(C) void qsort(void* base, size_t num, size_t size, int function(const void*,const void*) compar) @nogc nothrow @system; + extern (C) struct pthread_mutexattr_t + { + uint __attr; + } - extern(C) int pthread_mutex_lock(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) int pthread_mutex_trylock(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) int pthread_mutex_unlock(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) void pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) @nogc nothrow; - extern(C) void pthread_mutexattr_destroy(pthread_mutexattr_t *attr) @nogc nothrow; - extern(C) int pthread_mutexattr_init(pthread_mutexattr_t *attr) @nogc nothrow; - extern(C) int pthread_mutex_destroy(pthread_mutex_t *mutex) @nogc nothrow; - extern(C) int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) @nogc nothrow; + extern (C) int memcmp(const void* s1, const void* s2, size_t size); + extern (C) void exit(int status) nothrow @nogc; + extern (C) void __assert(const(char)* msg, const(char)* file, uint line) + { + exit(-20); + } + + extern (C) void free(void*) @nogc nothrow @system; + extern (C) void* malloc(size_t size) @nogc nothrow @system; + extern (C) void* realloc(void*, size_t size) @nogc nothrow @system; + extern (C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system; + extern (C) void* memset(void*, int val, size_t size) @nogc nothrow @system; + extern (C) int posix_memalign(void**, size_t, size_t) @nogc nothrow @system; + extern (C) void qsort(void* base, size_t num, size_t size, + int function(const void*, const void*) compar) @nogc nothrow @system; + + extern (C) int pthread_mutex_lock(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) int pthread_mutex_trylock(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) int pthread_mutex_unlock(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) void pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) @nogc nothrow; + extern (C) void pthread_mutexattr_destroy(pthread_mutexattr_t* attr) @nogc nothrow; + extern (C) int pthread_mutexattr_init(pthread_mutexattr_t* attr) @nogc nothrow; + extern (C) int pthread_mutex_destroy(pthread_mutex_t* mutex) @nogc nothrow; + extern (C) int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) @nogc nothrow; } else @@ -55,23 +60,24 @@ else public import core.stdc.stdlib : qsort; } -version(ECSEmscripten) +version (ECSEmscripten) { } else version (Windows) { import core.sys.windows.windows; - extern(Windows) void* _aligned_malloc(size_t size,size_t alignment) @nogc nothrow @system; - extern(Windows) void _aligned_free(void* ptr) @nogc nothrow @system; - - version(LDC) + + extern (Windows) void* _aligned_malloc(size_t size, size_t alignment) @nogc nothrow @system; + extern (Windows) void _aligned_free(void* ptr) @nogc nothrow @system; + + version (LDC) { /*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system; alias alloca = __alloca;*/ - extern(Windows) void ___chkstk_ms() @nogc nothrow @system; - - extern(Windows) void __chkstk() + extern (Windows) void ___chkstk_ms() @nogc nothrow @system; + + extern (Windows) void __chkstk() { ___chkstk_ms(); } @@ -80,17 +86,18 @@ else version (Windows) else version (Posix) { import core.sys.posix.pthread; - import core.sys.posix.stdlib; + import core.sys.posix.stdlib : posix_memalign; } -version(ECSEmscripten) +version (ECSEmscripten) { - private const uint max_alloca = 10000; + private const uint max_alloca = 10000; private __gshared byte[max_alloca] alloca_array; private __gshared uint alloca_pos = 0; - export extern(C) void* alloca(size_t length) @nogc nothrow + export extern (C) void* alloca(size_t length) @nogc nothrow { - if(alloca_pos + length > max_alloca)alloca_pos = 0; + if (alloca_pos + length > max_alloca) + alloca_pos = 0; void* ret = &alloca_array[alloca_pos]; alloca_pos += length; return ret; @@ -101,28 +108,44 @@ version(ECSEmscripten) return null; }*/ } -else version(D_BetterC) +else version (D_BetterC) { - private const uint max_alloca = 10000; + private const uint max_alloca = 10000; private __gshared byte[max_alloca] alloca_array; private uint alloca_pos = 0; - export extern(C) void* alloca(size_t length) @nogc nothrow + export extern (C) void* __alloca(size_t length) @nogc nothrow { - if(alloca_pos + length > max_alloca)alloca_pos = 0; + if (alloca_pos + length > max_alloca) + alloca_pos = 0; void* ret = &alloca_array[alloca_pos]; alloca_pos += length; return ret; } - version(GNU) + alias alloca = __alloca; + + version (DigitalMars) { - extern(C) void __gdc_personality_v0() + export extern (C) float* _memsetFloat(float* p, float value, size_t count) @nogc nothrow + { + float* pstart = p; + float* ptop; + + for (ptop = &p[count]; p < ptop; p++) + *p = value; + return pstart; + } + } + + version (GNU) + { + extern (C) void __gdc_personality_v0() { } } } -else +else { public import core.stdc.stdlib : alloca; } @@ -131,13 +154,13 @@ static struct Mallocator { static T[] makeArray(T)(size_t length) nothrow @nogc { - T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length]; + T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length]; - static if(__traits(isPOD, T)) + static if (__traits(isPOD, T)) { static immutable T init = T.init; - - foreach(i;0..ret.length) + + foreach (i; 0 .. ret.length) { memcpy(&ret[i], &init, T.sizeof); } @@ -145,7 +168,8 @@ static struct Mallocator else { static import std.conv; - foreach(i;0..ret.length) + + foreach (i; 0 .. ret.length) { std.conv.emplace(&ret[i]); } @@ -155,78 +179,94 @@ static struct Mallocator static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc { - T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length]; - foreach(ref v; ret)v = initializer; + T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length]; + foreach (ref v; ret) + v = initializer; return ret; } static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc { size_t new_length = array.length + length; - return (cast(T*)realloc(array.ptr, T.sizeof * new_length))[0 .. new_length]; + return (cast(T*) realloc(array.ptr, T.sizeof * new_length))[0 .. new_length]; } static T[] makeArray(T)(T[] array) nothrow @nogc { - T[] ret = (cast(T*)malloc(T.sizeof * array.length))[0 .. array.length];//Mallocator.makeArray!(T)(array.length); - foreach(i, ref v;ret)v = array[i]; + T[] ret = (cast(T*) malloc(T.sizeof * array.length))[0 .. array.length]; //Mallocator.makeArray!(T)(array.length); + foreach (i, ref v; ret) + v = array[i]; return ret; } - static T* make(T, Args...)(Args args) + static T* make(T, Args...)(Args args) { - T* ret = cast(T*)malloc(T.sizeof); + T* ret = cast(T*) malloc(T.sizeof); static import std.conv; - static if(__traits(isPOD, T)) + + static if (__traits(isPOD, T)) { static immutable T init = T.init; memcpy(ret, &init, T.sizeof); } - else static if(is(T == struct))std.conv.emplace(ret, args); + else static if (is(T == struct)) + std.conv.emplace(ret, args); return ret; } static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc { void* ret; - version(Posix)posix_memalign(&ret, alignment, length);//ret = aligned_alloc(alignment, length); - else version(Windows)ret = _aligned_malloc(length, alignment); - else version(ECSEmscripten)posix_memalign(&ret, alignment, length);//malloc(length); - else static assert(0, "Unimplemented platform!"); + version (Posix) + posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length); + else version (Windows) + ret = _aligned_malloc(length, alignment); + else version (ECSEmscripten) + posix_memalign(&ret, alignment, length); //malloc(length); + else + static assert(0, "Unimplemented platform!"); return ret; } static void dispose(T)(T object) nothrow @nogc { - static if(__traits(hasMember, T, "__xdtor"))object.__xdtor(); - else static if(__traits(hasMember, T, "__dtor"))object.__dtor(); - free(cast(void*)object); + static if (__traits(hasMember, T, "__xdtor")) + object.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + object.__dtor(); + free(cast(void*) object); } static void alignDispose(T)(T object) { - static if(__traits(hasMember, T, "__xdtor"))object.__xdtor(); - else static if(__traits(hasMember, T, "__dtor"))object.__dtor(); - version(Posix)free(cast(void*)object); - else version(Windows)_aligned_free(cast(void*)object); - else version(ECSEmscripten)free(cast(void*)object); - else static assert(0, "Unimplemented platform!"); + static if (__traits(hasMember, T, "__xdtor")) + object.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + object.__dtor(); + version (Posix) + free(cast(void*) object); + else version (Windows) + _aligned_free(cast(void*) object); + else version (ECSEmscripten) + free(cast(void*) object); + else + static assert(0, "Unimplemented platform!"); } } struct Mutex { - version(ECSEmscripten) + version (ECSEmscripten) { void initialize() nothrow @nogc { pthread_mutexattr_t attr = void; //pthread_mutexattr_init(&attr); - + //pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(cast(pthread_mutex_t*) &m_handle, &attr); + pthread_mutex_init(cast(pthread_mutex_t*)&m_handle, &attr); //pthread_mutexattr_destroy(&attr); } @@ -257,7 +297,7 @@ struct Mutex { void initialize() nothrow @nogc { - InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_handle); + InitializeCriticalSection(cast(CRITICAL_SECTION*)&m_handle); } void destroy() nothrow @nogc @@ -280,7 +320,7 @@ struct Mutex return TryEnterCriticalSection(&m_handle) != 0; } - CRITICAL_SECTION m_handle; + CRITICAL_SECTION m_handle; } else version (Posix) { @@ -289,9 +329,9 @@ struct Mutex pthread_mutexattr_t attr = void; pthread_mutexattr_init(&attr); - + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(cast(pthread_mutex_t*) &m_handle, &attr); + pthread_mutex_init(cast(pthread_mutex_t*)&m_handle, &attr); pthread_mutexattr_destroy(&attr); } @@ -318,5 +358,6 @@ struct Mutex private pthread_mutex_t m_handle; } - else static assert(0, "unsupported platform!"); -} \ No newline at end of file + else + static assert(0, "unsupported platform!"); +} diff --git a/source/bubel/ecs/vector.d b/source/bubel/ecs/vector.d index 1ec8a93..ca6cd6c 100644 --- a/source/bubel/ecs/vector.d +++ b/source/bubel/ecs/vector.d @@ -1,30 +1,36 @@ module bubel.ecs.vector; import core.bitop; + //import core.stdc.stdlib : free, malloc; import bubel.ecs.std; + //import core.stdc.string : memcpy, memset; //import std.algorithm : swap; import std.conv : emplace; import std.traits : hasMember, isCopyable, TemplateOf, Unqual; -export @nogc @safe nothrow pure size_t nextPow2(size_t num) { +export @nogc @safe nothrow pure size_t nextPow2(size_t num) +{ return 1 << bsr(num) + 1; } export __gshared size_t gVectorsCreated = 0; export __gshared size_t gVectorsDestroyed = 0; -struct Vector(T) { +struct Vector(T) +{ T[] array; size_t used; public: - export this()(T t) { + export this()(T t) + { add(t); } - export this(X)(X[] t) if (is(Unqual!X == Unqual!T)) { + export this(X)(X[] t) if (is(Unqual!X == Unqual!T)) + { add(t); } @@ -42,79 +48,101 @@ public: @disable this(this); - export ~this() { + export ~this() + { clear(); } - export void clear() { + export void clear() + { removeAll(); } - export void removeAll() { - if (array !is null) { + export void removeAll() + { + if (array !is null) + { /*foreach (ref el; array[0 .. used]) { destroy(el); }*/ //freeData(cast(void[]) array); - freeData((cast(void*)array.ptr)[0 .. array.length * T.sizeof]); + freeData((cast(void*) array.ptr)[0 .. array.length * T.sizeof]); gVectorsDestroyed++; } array = null; used = 0; } - export bool empty() const { + export bool empty() const + { return (used == 0); } - export size_t length() const { + export size_t length() const + { return used; } - export void length(size_t newLength) { - if (newLength > used) { + export void length(size_t newLength) + { + if (newLength > used) + { reserve(newLength); - foreach (ref el; array[used .. newLength]) { + foreach (ref el; array[used .. newLength]) + { emplace(&el); } - } else { - foreach (ref el; array[newLength .. used]) { + } + else + { + foreach (ref el; array[newLength .. used]) + { //destroy(el); - static if(__traits(hasMember, T, "__xdtor"))el.__xdtor(); - else static if(__traits(hasMember, T, "__dtor"))el.__dtor(); + static if (__traits(hasMember, T, "__xdtor")) + el.__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + el.__dtor(); } } used = newLength; } - export void reset() { + export void reset() + { used = 0; } - export void reserve(size_t numElements) { - if (numElements > array.length) { + export void reserve(size_t numElements) + { + if (numElements > array.length) + { extend(numElements); } } - export size_t capacity() { + export size_t capacity() + { return array.length - used; } - export void extend(size_t newNumOfElements) { + export void extend(size_t newNumOfElements) + { auto oldArray = manualExtend(array, newNumOfElements); - if (oldArray !is null) { + if (oldArray !is null) + { freeData(oldArray); } } - export @nogc void freeData(void[] data) { + export @nogc void freeData(void[] data) + { // 0x0F probably invalid value for pointers and other types memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD free(data.ptr); } - export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) { + export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) + { if (newNumOfElements == 0) newNumOfElements = 2; if (array.length == 0) @@ -126,22 +154,26 @@ public: memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize); array = memory[0 .. newNumOfElements]; //return cast(void[]) oldArray; - return (cast(void*)oldArray.ptr)[0 .. oldArray.length * T.sizeof]; + return (cast(void*) oldArray.ptr)[0 .. oldArray.length * T.sizeof]; } - export Vector!T copy()() { + export Vector!T copy()() + { Vector!T duplicate; duplicate.reserve(used); duplicate ~= array[0 .. used]; return duplicate; } - export bool canAddWithoutRealloc(uint elemNum = 1) { + export bool canAddWithoutRealloc(uint elemNum = 1) + { return used + elemNum <= array.length; } - export void add()(T t) { - if (used >= array.length) { + export void add()(T t) + { + if (used >= array.length) + { extend(nextPow2(used + 1)); } emplace(&array[used], t); @@ -149,48 +181,62 @@ public: } /// Add element at given position moving others - export void add()(T t, size_t pos) { + export void add()(T t, size_t pos) + { assert(pos <= used); - if (used >= array.length) { + if (used >= array.length) + { extend(array.length * 2); } - foreach_reverse (size_t i; pos .. used) { + foreach_reverse (size_t i; pos .. used) + { //swap(array[i + 1], array[i]); - array[i+1] = array[i]; + array[i + 1] = array[i]; } emplace(&array[pos], t); used++; } - export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { - if (used + t.length > array.length) { + export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) + { + if (used + t.length > array.length) + { extend(nextPow2(used + t.length)); } - foreach (i; 0 .. t.length) { + foreach (i; 0 .. t.length) + { emplace(&array[used + i], t[i]); } used += t.length; } - export void remove(size_t elemNum) { + export void remove(size_t elemNum) + { //destroy(array[elemNum]); - static if(__traits(hasMember, T, "__xdtor"))array[elemNum].__xdtor(); - else static if(__traits(hasMember, T, "__dtor"))array[elemNum].__dtor(); + static if (__traits(hasMember, T, "__xdtor")) + array[elemNum].__xdtor(); + else static if (__traits(hasMember, T, "__dtor")) + array[elemNum].__dtor(); //swap(array[elemNum], array[used - 1]); array[elemNum] = array[used - 1]; used--; } - export void removeStable()(size_t elemNum) { + export void removeStable()(size_t elemNum) + { used--; - foreach (i; 0 .. used) { + foreach (i; 0 .. used) + { array[i] = array[i + 1]; } } - export bool tryRemoveElement()(T elem) { - foreach (i, ref el; array[0 .. used]) { - if (el == elem) { + export bool tryRemoveElement()(T elem) + { + foreach (i, ref el; array[0 .. used]) + { + if (el == elem) + { remove(i); return true; } @@ -198,55 +244,66 @@ public: return false; } - export void removeElement()(T elem) { + export void removeElement()(T elem) + { bool ok = tryRemoveElement(elem); assert(ok, "There is no such an element in vector"); } - export ref T opIndex(size_t elemNum) const { + export ref T opIndex(size_t elemNum) const + { //debug assert(elemNum < used, "Range violation [index]"); return *cast(T*)&array.ptr[elemNum]; } - export auto opSlice() { + export auto opSlice() + { return array.ptr[0 .. used]; } - export T[] opSlice(size_t x, size_t y) { + export T[] opSlice(size_t x, size_t y) + { assert(y <= used); return array.ptr[x .. y]; } - export size_t opDollar() { + export size_t opDollar() + { return used; } - export void opAssign(X)(X[] slice) { + export void opAssign(X)(X[] slice) + { reset(); this ~= slice; } - export void opOpAssign(string op)(T obj) { + export void opOpAssign(string op)(T obj) + { //static assert(op == "~"); add(obj); } - export void opOpAssign(string op, X)(X[] obj) { + export void opOpAssign(string op, X)(X[] obj) + { //static assert(op == "~"); add(obj); } - export void opIndexAssign()(T obj, size_t elemNum) { + export void opIndexAssign()(T obj, size_t elemNum) + { assert(elemNum < used, "Range viloation"); array[elemNum] = obj; } - export void opSliceAssign()(T[] obj, size_t a, size_t b) { + export void opSliceAssign()(T[] obj, size_t a, size_t b) + { assert(b <= used && a <= b, "Range viloation"); array.ptr[a .. b] = obj; } - export bool opEquals()(auto ref const Vector!(T) r) const { + export bool opEquals()(auto ref const Vector!(T) r) const + { return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used]; } -} \ No newline at end of file +} diff --git a/tests/basic.d b/tests/basic.d index ab9acec..b8e1d2c 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -68,6 +68,11 @@ struct CUnregistered short value = 12; } +struct CFlag +{ + mixin ECS.Component; +} + struct LongAddSystem { mixin ECS.System; @@ -110,6 +115,7 @@ struct EmptySystem void beforeEveryTest() { + CUnregistered.component_id = ushort.max; gEM.initialize(0); gEM.beginRegister(); @@ -119,6 +125,7 @@ void beforeEveryTest() gEM.registerComponent!CDouble; gEM.registerComponent!CLong; gEM.registerComponent!CShort; + gEM.registerComponent!CFlag; gEM.endRegister(); } @@ -131,11 +138,12 @@ void afterEveryTest() @("AddEntity") unittest { - ushort[2] ids = [CInt.component_id, CFloat.component_id]; - EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); - assert(tmpl_.info.components.length == 2); + EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + assert(tmpl_.info.components.length == 3); + assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof)); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); + assert(tmpl_.getComponent!CFlag); assert(!tmpl_.getComponent!CLong); assert(!tmpl_.getComponent!CUnregistered); assert(*tmpl_.getComponent!CInt == 1); @@ -154,13 +162,66 @@ unittest assert(*entity2.getComponent!CInt == 2); assert(*entity2.getComponent!CFloat == 2.0); - CInt cint = CInt(10); - CLong clong; - Entity* entity3 = gEM.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray); + //CInt cint = CInt(10); + //CLong clong; + //Entity* entity3 = gEM.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray); + Entity* entity3 = gEM.addEntity(tmpl_, [CInt(10).ref_, CLong().ref_, CFlag().ref_].staticArray); + EntityID id = entity3.id; assert(entity3.getComponent!CInt); assert(entity3.getComponent!CFloat); assert(*entity3.getComponent!CInt == 10); assert(*entity3.getComponent!CFloat == 2.0); + + gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_]); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CInt); + assert(entity3.getComponent!CFloat); + assert(entity3.getComponent!CFlag); + assert(entity3.getComponent!CShort); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + assert(*entity3.getComponent!CShort == 2); + + gEM.removeComponents(entity3.id, [CFlag().component_id,CShort(2).component_id]); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CInt); + assert(entity3.getComponent!CFloat); + assert(!entity3.getComponent!CFlag); + assert(!entity3.getComponent!CShort); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + + gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_]); + gEM.removeComponents(entity3.id, [CUnregistered.component_id]); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CInt); + assert(entity3.getComponent!CFloat); + assert(entity3.getComponent!CFlag); + assert(entity3.getComponent!CShort); + assert(*entity3.getComponent!CInt == 10); + assert(*entity3.getComponent!CFloat == 2.0); + assert(*entity3.getComponent!CShort == 2); + + gEM.beginRegister(); + + gEM.registerComponent!CUnregistered; + + gEM.endRegister(); + + gEM.addComponents(entity3.id, [CUnregistered(4).ref_]); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(entity3.getComponent!CUnregistered); + assert(*entity3.getComponent!CUnregistered == 4); + + gEM.removeComponents(entity3.id, [CUnregistered.component_id]); + gEM.commit(); + entity3 = gEM.getEntity(id); + assert(!entity3.getComponent!CUnregistered); + } //allocate templates @@ -171,32 +232,48 @@ unittest ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); EntityTemplate* tmpl_d = gEM.allocateTemplate([CFloat.component_id, CInt.component_id, CFloat.component_id].staticArray); + EntityTemplate* tmpl_cp = gEM.allocateTemplate(tmpl_); assert(tmpl_d.info == tmpl_.info); + assert(tmpl_cp.info == tmpl_cp.info); assert(tmpl_.info.components.length == 2); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); + assert(tmpl_cp.getComponent!CFloat); + assert(tmpl_cp.getComponent!CInt); + assert(tmpl_.getComponent!CInt != tmpl_cp.getComponent!CInt); + assert(tmpl_.getComponent!CFloat != tmpl_cp.getComponent!CFloat); + assert(*tmpl_.getComponent!CInt == *tmpl_cp.getComponent!CInt); + assert(*tmpl_.getComponent!CFloat == *tmpl_cp.getComponent!CFloat); *tmpl_.getComponent!CInt = 4; *tmpl_.getComponent!CFloat = 5.0; - //allocate template from template with additional component - ushort[1] ids2 = [CDouble.component_id]; + //allocate template from template with additional components + ushort[2] ids2 = [CDouble.component_id,CFlag.component_id]; EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2); - assert(tmpl_2.info.components.length == 3); + assert(tmpl_2.info.components.length == 4); assert(tmpl_2.getComponent!CInt); assert(tmpl_2.getComponent!CFloat); assert(tmpl_2.getComponent!CDouble); + assert(tmpl_2.getComponent!CFlag); assert(*tmpl_2.getComponent!CInt == 4); assert(*tmpl_2.getComponent!CFloat == 5.0); assert(*tmpl_2.getComponent!CDouble == 3.0); + assert(tmpl_.info.blocksCount() == 0); + Entity* entity = gEM.addEntity(tmpl_); - gEM.addComponents(entity.id, CDouble(8.0)); + gEM.addComponents(entity.id, CFloat(100)); + gEM.addComponents(entity.id, CDouble(8.0), CFloat(100)); + + assert(tmpl_.info.blocksCount() == 1); //apply entity changes gEM.commit(); + assert(tmpl_.info.blocksCount() == 0); + //allocate template as entity copy EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id); assert(tmpl_3.info.components.length == 3); @@ -217,18 +294,20 @@ unittest assert(*tmpl_4.getComponent!CFloat == 2.0); assert(*tmpl_4.getComponent!CDouble == 3.0); - //allocate template from template with two additional component - ushort[2] ids3 = [CDouble.component_id, CLong.component_id]; - EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_, ids3); - assert(tmpl_5.info.components.length == 4); + //allocate template from template with three additional component + ushort[3] ids3 = [CDouble.component_id, CLong.component_id, CShort.component_id]; + EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_2, ids3); + assert(tmpl_5.info.components.length == 6); assert(tmpl_5.getComponent!CInt); assert(tmpl_5.getComponent!CFloat); assert(tmpl_5.getComponent!CDouble); assert(tmpl_5.getComponent!CLong); + assert(tmpl_5.getComponent!CShort); assert(*tmpl_5.getComponent!CInt == 4); assert(*tmpl_5.getComponent!CFloat == 5.0); assert(*tmpl_5.getComponent!CDouble == 3.0); assert(*tmpl_5.getComponent!CLong == 10); + assert(*tmpl_5.getComponent!CShort == 12); //allocate template from template without one component ushort[1] rem_ids = [CFloat.component_id]; @@ -239,7 +318,7 @@ unittest //allocate template from template without one component and two additional EntityTemplate* tmpl_7 = gEM.allocateTemplate(tmpl_, ids3, rem_ids); - assert(tmpl_7.info.components.length == 3); + assert(tmpl_7.info.components.length == 4); assert(tmpl_7.getComponent!CInt); assert(tmpl_7.getComponent!CDouble); assert(tmpl_7.getComponent!CLong); @@ -496,6 +575,10 @@ unittest gEM.endRegister(); + assert(gEM.getPass("custom")); + assert(!gEM.getPass("custommm")); + + LongAddSystem* system = gEM.getSystem!LongAddSystem; assert(system !is null); assert(system.updates_count == 0); @@ -1131,9 +1214,9 @@ unittest assert(*entity2.getComponent!CShort == 12); gEM.sendEvent(id,ETest()); - gEM.sendEvent(id,ETest2(id,10)); + gEM.sendEvent(id,ETest2(10)); gEM.sendEvent(id2,ETest()); - gEM.sendEvent(id2,ETest2(id2,12)); + gEM.sendEvent(id2,ETest2(12)); gEM.commit(); assert(ETest2.destory == 2); @@ -1144,7 +1227,7 @@ unittest gEM.addComponents(id, CInt(2), CShort(1)); gEM.sendEvent(id,ETest()); - gEM.sendEvent(id,ETest2(id,2)); + gEM.sendEvent(id,ETest2(2)); gEM.commit(); assert(ETest2.destory == 3); @@ -1157,7 +1240,7 @@ unittest foreach(i;0..10000) { gEM.sendEvent(id,ETest()); - gEM.sendEvent(id,ETest2(id,4)); + gEM.sendEvent(id,ETest2(4)); result += 16; result += 8; } @@ -1321,22 +1404,6 @@ unittest } } - void func1(TestSystem.EntitiesData entities) - { - foreach(i;0 .. entities.length) - { - entities.int_[i] += entities.int_[i] / 2; - } - } - - void func2(TestSystem.EntitiesData entities) - { - foreach(i;0 .. entities.length) - { - entities.int_[i] += 8; - } - } - gEM.beginRegister(); gEM.registerSystem!TestSystem(0); @@ -1462,22 +1529,6 @@ unittest } } - void func1(TestSystem.EntitiesData entities) - { - foreach(i;0 .. entities.length) - { - entities.int_[i] += entities.int_[i] / 2; - } - } - - void func2(TestSystem.EntitiesData entities) - { - foreach(i;0 .. entities.length) - { - entities.int_[i] += 8; - } - } - gEM.beginRegister(); gEM.registerDependency(TestDependency); diff --git a/tests/bugs.d b/tests/bugs.d new file mode 100644 index 0000000..49e3b12 --- /dev/null +++ b/tests/bugs.d @@ -0,0 +1,142 @@ +module tests.bugs; + +import tests.basic; + +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.system; +import bubel.ecs.attributes; + +version(GNU) +{ + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } +} +else import std.array : staticArray; + +@("Bug0001") +unittest +{ + struct Event1 + { + mixin ECS.Event; + + EntityID id; + } + + struct Event2 + { + mixin ECS.Event; + } + + struct System1 + { + mixin ECS.System; + + struct EntitiesData + { + CInt[] int_; + } + + EntityTemplate* tmpl; + EntityID id; + + void onCreate() + { + tmpl = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray); + } + + void onDestroy() + { + gEM.freeTemplate(tmpl); + } + + void handleEvent(Entity* entity, Event1 event) + { + gEM.removeEntity(event.id); + gEM.sendEvent(entity.id,Event2()); + } + + void handleEvent(Entity* entity, Event2 event) + { + id = gEM.addEntity(tmpl,[CInt(2).ref_, CLong(8).ref_].staticArray).id; + } + } + + struct System2 + { + mixin ECS.System; + + struct EntitiesData + { + Entity[] entity; + } + + ///check if every entity was removed correctly + void onUpdate(EntitiesData data) + { + assert(0); + } + } + + struct System3 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + Entity[] entity; + } + + ///remove every entity + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length)gEM.removeEntity(data.entity[i].id); + } + } + + gEM.initialize(0); + + gEM.beginRegister(); + + gEM.registerComponent!CInt; + gEM.registerComponent!CFloat; + gEM.registerComponent!CDouble; + gEM.registerComponent!CLong; + gEM.registerComponent!CShort; + gEM.registerComponent!CFlag; + + gEM.registerEvent!Event1; + gEM.registerEvent!Event2; + + gEM.registerSystem!System1(0); + gEM.registerSystem!System2(-200); + gEM.registerSystem!System3(-200); + + gEM.endRegister(); + + EntityTemplate* tmpl = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray); + EntityID id = gEM.addEntity(tmpl,[CLong(10).ref_, CInt(6).ref_].staticArray).id; + EntityID id2 = gEM.addEntity(tmpl,[CInt(4).ref_].staticArray).id; + gEM.freeTemplate(tmpl); + gEM.commit(); + + gEM.sendEvent(id2, Event1(id)); + + gEM.getSystem(System2.system_id).disable(); + + gEM.begin(); + gEM.update(); + gEM.end(); + + gEM.getSystem(System2.system_id).enable(); + + gEM.begin(); + gEM.update(); + gEM.end(); + + gEM.destroy(); +} \ No newline at end of file diff --git a/tests/runner.d b/tests/runner.d index f299664..b14dafe 100644 --- a/tests/runner.d +++ b/tests/runner.d @@ -210,7 +210,7 @@ struct TestRunner(Args...) else test.name = attributes[0]; - static if (__traits(hasMember, module_, "beforeEveryTest")) + static if (__traits(hasMember, module_, "beforeEveryTest") && __traits(compiles, module_.beforeEveryTest())) module_.beforeEveryTest(); if(before)before(); @@ -256,7 +256,7 @@ struct TestRunner(Args...) else suite.failed++; suite.tests ~= test; - static if (__traits(hasMember, module_, "afterEveryTest")) + static if (__traits(hasMember, module_, "afterEveryTest") && __traits(compiles, module_.beforeEveryTest())) module_.afterEveryTest(); } passed += suite.passed; @@ -420,7 +420,7 @@ extern (C) int main(int argc, char** args) } } - TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf) runner; + TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf, tests.bugs) runner; runner.runTests(include[], exclude[]);