Demos #10

Merged
Mergul merged 39 commits from Demos into master 2020-05-28 18:48:45 +02:00
16 changed files with 988 additions and 544 deletions
Showing only changes of commit 6929f5a748 - Show all commits

View file

@ -9,9 +9,9 @@ License: BSD 3-clause, see LICENSE file in project root folder.
*/ */
module bubel.ecs.atomic; module bubel.ecs.atomic;
version(Emscripten)version = ECSEmscripten; version (Emscripten) version = ECSEmscripten;
version(ECSEmscripten) version (ECSEmscripten)
{ {
import std.traits; import std.traits;
@ -24,74 +24,109 @@ version(ECSEmscripten)
seq seq
} }
extern(C) ubyte emscripten_atomic_cas_u8(void *addr, ubyte oldVal, ubyte 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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); static if (is(T == byte) || is(T == ubyte))
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); return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val,
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); cast(Unqual!T) mod) + 1);
else static assert(0); 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); static if (is(T == byte) || is(T == ubyte))
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); return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val,
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); cast(Unqual!T) mod) - 1);
else static assert(0); 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; 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); static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte))
else static if(is(UT == short) || is(UT == ushort))emscripten_atomic_store_u16(cast(void*)&val, cast(UT)newval); emscripten_atomic_store_u8(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 if (is(UT == short) || is(UT == ushort))
else static assert(0); 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; alias UT = Unqual!T;
static if(is(UT == bool))return emscripten_atomic_load_u8(cast(const void*)&val) != 0; static if (is(UT == bool))
else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_load_u8(cast(const void*)&val); return emscripten_atomic_load_u8(cast(const void*)&val) != 0;
else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_load_u16(cast(const void*)&val); else static if (is(UT == byte) || is(UT == ubyte))
else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_load_u32(cast(const void*)&val); return emscripten_atomic_load_u8(cast(const void*)&val);
else static assert(0); 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; 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; static if (is(UT == bool))
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; return emscripten_atomic_cas_u8(cast(void*) here,
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; 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 if (is(UT == byte) || is(UT == ubyte))
else static assert(0); 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 else
{ {
public import core.atomic; public import core.atomic;
} }

View file

@ -25,4 +25,4 @@ module bubel.ecs.attributes;
///Used to mark optional components for system. ///Used to mark optional components for system.
enum optional = "optional"; enum optional = "optional";
///Used to mark readonly components for system. "const" can be used insted. ///Used to mark readonly components for system. "const" can be used insted.
enum readonly = "readonly"; enum readonly = "readonly";

View file

@ -35,7 +35,7 @@ struct BlockAllocator
*/ */
void freeBlock(void* block) nothrow @nogc void freeBlock(void* block) nothrow @nogc
{ {
*cast(void**)block = next_block; *cast(void**) block = next_block;
next_block = block; next_block = block;
} }
@ -44,9 +44,9 @@ struct BlockAllocator
*/ */
void freeMemory() nothrow @nogc void freeMemory() nothrow @nogc
{ {
while(pointers) while (pointers)
{ {
foreach(i;0..pointers.numberof) foreach (i; 0 .. pointers.numberof)
{ {
Mallocator.alignDispose(pointers.blocks[i]); Mallocator.alignDispose(pointers.blocks[i]);
} }
@ -60,12 +60,14 @@ private:
void allocBlock() nothrow @nogc void allocBlock() nothrow @nogc
{ {
next_block = cast(void*) Mallocator.alignAlloc( next_block = cast(void*) Mallocator.alignAlloc(block_size * blocks_in_allocation,
block_size * blocks_in_allocation, block_size); block_size);
if(next_block is null)assert(0); if (next_block is null)
assert(0);
if(pointers is null)pointers = Mallocator.make!BlockPointers; if (pointers is null)
if(pointers.numberof >= 32) pointers = Mallocator.make!BlockPointers;
if (pointers.numberof >= 32)
{ {
BlockPointers* new_pointers = Mallocator.make!BlockPointers; BlockPointers* new_pointers = Mallocator.make!BlockPointers;
new_pointers.next_pointers = pointers; new_pointers.next_pointers = pointers;
@ -78,8 +80,7 @@ private:
void** pointer = cast(void**)(next_block + i * block_size); void** pointer = cast(void**)(next_block + i * block_size);
*pointer = next_block + (i + 1) * block_size; *pointer = next_block + (i + 1) * block_size;
} }
void** pointer = cast(void**)( void** pointer = cast(void**)(next_block + (blocks_in_allocation - 1) * block_size);
next_block + (blocks_in_allocation - 1) * block_size);
*pointer = null; *pointer = null;
} }

View file

@ -75,19 +75,18 @@ static struct ECS
{ {
__gshared ushort component_id = ushort.max; __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). 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; __gshared ushort event_id = ushort.max;
EntityID entity_id;
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -113,4 +112,4 @@ static struct ECS
{ {
alias WritableDependencies = T; alias WritableDependencies = T;
} }
} }

View file

@ -39,11 +39,7 @@ struct Entity
if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0)
return null; return null;
static if (EntityID.sizeof == 8) return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof);
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);
} }
bool hasComponent(ushort component_id) bool hasComponent(ushort component_id)
@ -58,10 +54,7 @@ struct Entity
{ {
EntityMeta meta; EntityMeta meta;
meta.block = gEM.getMetaData(&this); meta.block = gEM.getMetaData(&this);
static if (EntityID.sizeof == 8) meta.index = meta.block.entityIndex(&this);
meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) >> 3);
else
meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) / EntityID.sizeof());
return meta; return meta;
} }
} }

View file

@ -20,7 +20,7 @@ package struct EventManager
void destroy() nothrow @nogc void destroy() nothrow @nogc
{ {
if(event_block_alloc_mutex) if (event_block_alloc_mutex)
{ {
event_block_alloc_mutex.destroy(); event_block_alloc_mutex.destroy();
Mallocator.dispose(event_block_alloc_mutex); 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 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]; EventData* data = &events[Ev.event_id];
EventBlock* block = data.blocks[block_id]; EventBlock* block = data.blocks[block_id];
//EntityManager.EventInfo* info = &manager.events[Ev.event_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(); event_block_alloc_mutex.lock();
block = cast(EventBlock*) allocator.getBlock(); block = cast(EventBlock*) allocator.getBlock();
@ -48,35 +48,42 @@ package struct EventManager
data.blocks[block_id] = block; data.blocks[block_id] = block;
} }
if(block.count >= data.max_events) if (block.count >= data.max_events)
{ {
event_block_alloc_mutex.lock(); event_block_alloc_mutex.lock();
EventBlock* new_block = cast(EventBlock*) allocator.getBlock(); EventBlock* new_block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock(); event_block_alloc_mutex.unlock();
*new_block = EventBlock(); *new_block = EventBlock();
block.next = new_block; block.next = new_block;
block = new_block; block = new_block;
data.blocks[block_id] = block; data.blocks[block_id] = block;
} }
Ev* event_array = cast(Ev*)(cast(void*)block + data.data_offset); uint size = Ev.sizeof + EntityID.sizeof;
event_array[block.count] = event; 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++; block.count++;
} }
void swapCurrent() nothrow @nogc void swapCurrent() nothrow @nogc
{ {
uint threads_count = cast(uint)manager.threads.length; uint threads_count = cast(uint) manager.threads.length;
if(current_index == 0)current_index = threads_count; if (current_index == 0)
else 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; EventBlock* block = first_block;
while(block) while (block)
{ {
EventBlock* to_dispose = block; EventBlock* to_dispose = block;
block = block.next; block = block.next;
@ -84,7 +91,7 @@ package struct EventManager
} }
first_block = null; 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; block = null;
} }
@ -94,12 +101,12 @@ package struct EventManager
void clearEvents() nothrow @nogc void clearEvents() nothrow @nogc
{ {
//uint threads_count = cast(uint)manager.threads.length; //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; EventBlock* block = first_block;
while(block) while (block)
{ {
EventBlock* to_dispose = block; EventBlock* to_dispose = block;
block = block.next; block = block.next;
@ -107,7 +114,7 @@ package struct EventManager
} }
first_block = null; first_block = null;
} }
foreach(ref block; event.blocks) foreach (ref block; event.blocks)
{ {
block = null; block = null;
} }
@ -118,23 +125,25 @@ package struct EventManager
{ {
disposeData(); disposeData();
events = Mallocator.makeArray!EventData(manager.events.length); 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.blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2);
event.first_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.data_offset = EventBlock.sizeof; //manager.events[i].
manager.alignNum(event.data_offset, manager.events[i].alignment); 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 private void disposeData() nothrow @nogc
{ {
clearEvents(); clearEvents();
if(events) if (events)
{ {
foreach(ref event;events) foreach (ref event; events)
{ {
Mallocator.dispose(event.blocks); Mallocator.dispose(event.blocks);
Mallocator.dispose(event.first_blocks); Mallocator.dispose(event.first_blocks);
@ -166,7 +175,7 @@ package struct EventManager
ushort max_events; ushort max_events;
EventBlock*[] blocks; EventBlock*[] blocks;
EventBlock*[] first_blocks; EventBlock*[] first_blocks;
//EventBlock*[] current_blocks; //EventBlock*[] current_blocks;
} }

View file

@ -11,36 +11,44 @@ private enum HASH_EMPTY = 0;
private enum HASH_DELETED = 0x1; private enum HASH_DELETED = 0x1;
private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1; private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1;
export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc { export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc
static if (isIntegral!(T)) { {
static if (isIntegral!(T))
{
return hashInt(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 // 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 >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb; x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
x = x ^ (x >> 31); x = x ^ (x >> 31);
return x; return x;
} }
struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc)
{
alias Key = KeyPar; alias Key = KeyPar;
alias Value = ValuePar; alias Value = ValuePar;
nothrow: nothrow:
enum rehashFactor = 0.75; enum rehashFactor = 0.75;
enum size_t getIndexEmptyValue = size_t.max; enum size_t getIndexEmptyValue = size_t.max;
static struct KeyVal { static struct KeyVal
{
Key key; Key key;
Value value; Value value;
} }
static struct Bucket { static struct Bucket
{
ulong hash; ulong hash;
KeyVal keyValue; KeyVal keyValue;
} }
@ -49,58 +57,74 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
size_t length; // Used to compute loadFactor size_t length; // Used to compute loadFactor
size_t markerdDeleted; size_t markerdDeleted;
export void clear() { export void clear()
{
elements.clear(); elements.clear();
length = 0; length = 0;
markerdDeleted = 0; markerdDeleted = 0;
} }
export void reset() { export void reset()
{
elements.reset(); elements.reset();
length = 0; length = 0;
markerdDeleted = 0; markerdDeleted = 0;
} }
export bool isIn(ref Key el) { export bool isIn(ref Key el)
{
return getIndex(el) != getIndexEmptyValue; return getIndex(el) != getIndexEmptyValue;
} }
export bool isIn(Key el) { export bool isIn(Key el)
{
return getIndex(el) != getIndexEmptyValue; return getIndex(el) != getIndexEmptyValue;
} }
export Value* getPtr()(auto ref Key k) { export Value* getPtr()(auto ref Key k)
{
size_t index = getIndex(k); size_t index = getIndex(k);
if (index == getIndexEmptyValue) { if (index == getIndexEmptyValue)
{
return null; return null;
} else { }
else
{
return &elements[index].keyValue.value; 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); size_t index = getIndex(k);
assert(index != getIndexEmptyValue); assert(index != getIndexEmptyValue);
return elements[index].keyValue.value; return elements[index].keyValue.value;
} }
deprecated("Use get with second parameter.") export auto ref Value getDefault()( 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); 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); size_t index = getIndex(k);
if (index == getIndexEmptyValue) { if (index == getIndexEmptyValue)
{
return defaultValue; return defaultValue;
} else { }
else
{
return elements[index].keyValue.value; 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); size_t index = getIndex(k);
if (index == getIndexEmptyValue) { if (index == getIndexEmptyValue)
{
add(k, defaultValue); add(k, defaultValue);
} }
index = getIndex(k); 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); size_t index = getIndex(el);
if (index == getIndexEmptyValue) { if (index == getIndexEmptyValue)
{
return false; return false;
} }
length--; length--;
@ -120,28 +146,34 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return true; return true;
} }
export void remove(Key el) { export void remove(Key el)
{
bool ok = tryRemove(el); bool ok = tryRemove(el);
assert(ok); assert(ok);
} }
export ref Value opIndex()(auto ref Key key) { export ref Value opIndex()(auto ref Key key)
{
return get(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); 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); size_t index = getIndex(key);
if (index != getIndexEmptyValue) { if (index != getIndexEmptyValue)
{
elements[index].keyValue.value = value; elements[index].keyValue.value = value;
return; return;
} }
if (getLoadFactor(length + 1) > rehashFactor if (getLoadFactor(length + 1) > rehashFactor
|| getLoadFactor(length + markerdDeleted) > rehashFactor) { || getLoadFactor(length + markerdDeleted) > rehashFactor)
{
rehash(); rehash();
} }
length++; length++;
@ -150,10 +182,13 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
immutable size_t rotateMask = elements.length - 1; immutable size_t rotateMask = elements.length - 1;
index = hash & rotateMask; // Starting point index = hash & rotateMask; // Starting point
while (true) { while (true)
{
Bucket* gr = &elements[index]; Bucket* gr = &elements[index];
if ((gr.hash & HASH_FILLED_MARK) == 0) { if ((gr.hash & HASH_FILLED_MARK) == 0)
if (gr.hash == HASH_DELETED) { {
if (gr.hash == HASH_DELETED)
{
markerdDeleted--; markerdDeleted--;
} }
gr.hash = hash; gr.hash = hash;
@ -171,15 +206,18 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
//int numA; //int numA;
//int numB; //int numB;
export size_t getIndex(Key el) { export size_t getIndex(Key el)
{
return getIndex(el); return getIndex(el);
} }
export size_t getIndex(ref Key el) { export size_t getIndex(ref Key el)
{
mixin(doNotInline); mixin(doNotInline);
immutable size_t groupsLength = elements.length; immutable size_t groupsLength = elements.length;
if (groupsLength == 0) { if (groupsLength == 0)
{
return getIndexEmptyValue; return getIndexEmptyValue;
} }
@ -188,13 +226,16 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
size_t index = hash & rotateMask; // Starting point size_t index = hash & rotateMask; // Starting point
//numA++; //numA++;
while (true) { while (true)
{
//numB++; //numB++;
Bucket* gr = &elements[index]; Bucket* gr = &elements[index];
if (gr.hash == hash && gr.keyValue.key == el) { if (gr.hash == hash && gr.keyValue.key == el)
{
return index; return index;
} }
if (gr.hash == HASH_EMPTY) { if (gr.hash == HASH_EMPTY)
{
return getIndexEmptyValue; return getIndexEmptyValue;
} }
@ -204,21 +245,26 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
} }
export float getLoadFactor(size_t forElementsNum) { export float getLoadFactor(size_t forElementsNum)
if (elements.length == 0) { {
if (elements.length == 0)
{
return 1; return 1;
} }
return cast(float) forElementsNum / (elements.length); return cast(float) forElementsNum / (elements.length);
} }
export void rehash()() { export void rehash()()
{
mixin(doNotInline); mixin(doNotInline);
// Get all elements // Get all elements
Vector!KeyVal allElements; Vector!KeyVal allElements;
allElements.reserve(elements.length); allElements.reserve(elements.length);
foreach (ref Bucket el; elements) { foreach (ref Bucket el; elements)
if ((el.hash & HASH_FILLED_MARK) == 0) { {
if ((el.hash & HASH_FILLED_MARK) == 0)
{
el.hash = HASH_EMPTY; el.hash = HASH_EMPTY;
continue; 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 elements.length = (elements.length ? elements.length : 4) << 1; // Power of two, initially 8 elements
} }
// Insert elements // Insert elements
foreach (i, ref el; allElements) { foreach (i, ref el; allElements)
{
add(el.key, el.value); add(el.key, el.value);
} }
length = allElements.length; length = allElements.length;
@ -240,19 +288,29 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
} }
// foreach support // foreach support
export int opApply(DG)(scope DG dg) { export int opApply(DG)(scope DG dg)
{
int result; int result;
foreach (ref Bucket gr; elements) { foreach (ref Bucket gr; elements)
if ((gr.hash & HASH_FILLED_MARK) == 0) { {
if ((gr.hash & HASH_FILLED_MARK) == 0)
{
continue; continue;
} }
static if (isForeachDelegateWithTypes!(DG, Key)) { static if (isForeachDelegateWithTypes!(DG, Key))
{
result = dg(gr.keyValue.key); result = dg(gr.keyValue.key);
} else static if (isForeachDelegateWithTypes!(DG, Value)) { }
else static if (isForeachDelegateWithTypes!(DG, Value))
{
result = dg(gr.keyValue.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); result = dg(gr.keyValue.key, gr.keyValue.value);
} else { }
else
{
static assert(0); static assert(0);
} }
if (result) if (result)
@ -263,9 +321,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result; return result;
} }
export int byKey(scope int delegate(Key k) nothrow dg) { export int byKey(scope int delegate(Key k) nothrow dg)
{
int result; int result;
foreach (ref Key k; this) { foreach (ref Key k; this)
{
result = dg(k); result = dg(k);
if (result) if (result)
break; break;
@ -273,9 +333,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result; 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; int result;
foreach (ref Value v; this) { foreach (ref Value v; this)
{
result = dg(v); result = dg(v);
if (result) if (result)
break; break;
@ -283,13 +345,15 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result; 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; int result;
foreach (ref Key k, ref Value v; this) { foreach (ref Key k, ref Value v; this)
{
result = dg(k, v); result = dg(k, v);
if (result) if (result)
break; break;
} }
return result; return result;
} }
} }

View file

@ -18,7 +18,7 @@ struct IDManager
pragma(inline, false) EntityID getNewID() nothrow @nogc pragma(inline, false) EntityID getNewID() nothrow @nogc
{ {
int current = m_stack_top.atomicOp!"-="(1) + 1; int current = m_stack_top.atomicOp!"-="(1) + 1;
if(current < 0) if (current < 0)
{ {
uint add_id = m_last_id.atomicOp!"+="(1) - 1; uint add_id = m_last_id.atomicOp!"+="(1) - 1;
@ -29,7 +29,7 @@ struct IDManager
if (block_id >= m_blocks_count) if (block_id >= m_blocks_count)
{ {
add_mutex.lock(); add_mutex.lock();
if(block_id >= m_blocks_count) if (block_id >= m_blocks_count)
{ {
m_blocks[m_blocks_count].alloc(); m_blocks[m_blocks_count].alloc();
m_blocks_count++; m_blocks_count++;
@ -112,9 +112,11 @@ struct IDManager
*/ */
export bool isExist(EntityID id) nothrow @nogc 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]; 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; return data.counter == id.counter;
} }
@ -126,7 +128,8 @@ struct IDManager
m_ids_array = Mallocator.makeArray!Data(65536); m_ids_array = Mallocator.makeArray!Data(65536);
m_free_stack = Mallocator.makeArray!uint(65536); m_free_stack = Mallocator.makeArray!uint(65536);
m_blocks = Mallocator.makeArray!Block(64); 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_count = 1;
m_blocks[0].alloc(); m_blocks[0].alloc();
@ -142,20 +145,23 @@ struct IDManager
*/ */
void deinitialize() @trusted @nogc nothrow void deinitialize() @trusted @nogc nothrow
{ {
if(m_ids_array)Mallocator.dispose(m_ids_array); if (m_ids_array)
if(m_free_stack)Mallocator.dispose(m_free_stack); Mallocator.dispose(m_ids_array);
if(m_blocks) 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); Mallocator.dispose(m_blocks);
} }
if(add_mutex) if (add_mutex)
{ {
add_mutex.destroy(); 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; add_mutex = null;
} }
} }
@ -165,27 +171,31 @@ struct IDManager
*/ */
void optimize() nothrow @nogc void optimize() nothrow @nogc
{ {
if(m_stack_top < -1)m_stack_top = -1; if (m_stack_top < -1)
if(m_last_id > m_ids_array.length) 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)); 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); memcpy(new_array.ptr, m_ids_array.ptr, m_ids_array.length * Data.sizeof);
Mallocator.dispose(m_ids_array); Mallocator.dispose(m_ids_array);
m_ids_array = new_array; m_ids_array = new_array;
uint[] new_stack = Mallocator.makeArray!uint(m_ids_array.length); 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); Mallocator.dispose(m_free_stack);
m_free_stack = new_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; 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); memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof,
foreach(ref block;m_blocks[1..m_blocks_count])block.free(); 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; m_blocks_count = 1;
} }
} }
@ -217,12 +227,12 @@ struct IDManager
private: private:
Mutex* add_mutex; Mutex* add_mutex;
Data[] m_ids_array = null; Data[] m_ids_array = null;
uint m_blocks_count = 0; uint m_blocks_count = 0;
Block[] m_blocks; Block[] m_blocks;
uint[] m_free_stack = null; uint[] m_free_stack = null;
align(64) shared uint m_last_id = 0; align(64) shared uint m_last_id = 0;
align(64) shared int m_stack_top = -1; align(64) shared int m_stack_top = -1;
} }

View file

@ -126,7 +126,7 @@ export struct EntityManager
//if(info.components)Mallocator.dispose(info.components); //if(info.components)Mallocator.dispose(info.components);
Mallocator.dispose(info); Mallocator.dispose(info);
} }
foreach (UpdatePass* pass; passes) foreach (UpdatePass* pass; passes)
{ {
@ -402,7 +402,7 @@ export struct EntityManager
Sys* data_system = cast(Sys*) data.system_pointer; Sys* data_system = cast(Sys*) data.system_pointer;
Type* event = cast(Type*) data.event; 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) void setEventCallers(Sys)(ref System system)
@ -474,7 +474,7 @@ export struct EntityManager
{ {
//continue; //continue;
} }
else else
{ {
string name; string name;
static if (isArray!MemberType) static if (isArray!MemberType)
@ -1018,8 +1018,7 @@ export struct EntityManager
static if (hasMember!(Sys.EntitiesData, "job_id")) static if (hasMember!(Sys.EntitiesData, "job_id"))
{ {
input_data.job_id = cast(typeof(input_data.job_id)) data input_data.job_id = cast(typeof(input_data.job_id)) data.job_id;
.job_id;
} }
//s.onUpdate(input_data); //s.onUpdate(input_data);
@ -1052,8 +1051,7 @@ export struct EntityManager
static if (hasMember!(Sys.EntitiesData, "job_id")) static if (hasMember!(Sys.EntitiesData, "job_id"))
{ {
input_data.job_id = cast(typeof(input_data.job_id)) data input_data.job_id = cast(typeof(input_data.job_id)) data.job_id;
.job_id;
} }
(cast(typeof(&__traits(getOverloads, s, (cast(typeof(&__traits(getOverloads, s,
@ -1146,7 +1144,8 @@ export struct EntityManager
foreach (iii, comp_info; components_info.readonlyDeps) 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) version (D_BetterC)
assert(comp != ushort.max, assert(comp != ushort.max,
"Can't register system \"" ~ Sys.stringof "Can't register system \"" ~ Sys.stringof
@ -1156,7 +1155,7 @@ export struct EntityManager
~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\".");
system.m_readonly_dependencies[iii] = comp; system.m_readonly_dependencies[iii] = comp;
} }
foreach (iii, comp_info; components_info.writableDeps) foreach (iii, comp_info; components_info.writableDeps)
{ {
ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); 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) if (sys_id < systems.length)
{ {
systems[sys_id].disable(); 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) if (system.m_create)
(cast(void function(void*)) system.m_create)(system.m_system_pointer); (cast(void function(void*)) system.m_create)(system.m_system_pointer);
@ -1236,7 +1237,7 @@ export struct EntityManager
export void registerDependency(const(char)[] name) 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.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.alignment = Comp.alignof; //8;
info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof); info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof);
*cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); *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."); "Can't call function with system which hasn't EntitesData structure.");
static assert(__traits(hasMember, Sys, "onUpdate"), static assert(__traits(hasMember, Sys, "onUpdate"),
"Can't call function with system which hasn't onUpdate function callback."); "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."); static assert(__traits(hasMember, Sys, "system_id"), "Sys must be system type.");
System* system = getSystem(Sys.system_id); System* system = getSystem(Sys.system_id);
@ -1612,6 +1618,8 @@ export struct EntityManager
//fill components with default data //fill components with default data
foreach (comp; info.components) foreach (comp; info.components)
{ {
if (components[comp].size == 0)
continue;
memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp],
components[comp].init_data.ptr, components[comp].size); components[comp].init_data.ptr, components[comp].size);
} }
@ -1621,6 +1629,8 @@ export struct EntityManager
ushort index = block.entityIndex(entity); ushort index = block.entityIndex(entity);
foreach (comp; info.components) foreach (comp; info.components)
{ {
if (components[comp].size == 0)
continue;
memcpy(cast(void*) temp.entity_data.ptr + info.tmpl_deltas[comp], memcpy(cast(void*) temp.entity_data.ptr + info.tmpl_deltas[comp],
cast(void*) block + info.deltas[comp] + components[comp].size * index, cast(void*) block + info.deltas[comp] + components[comp].size * index,
components[comp].size); components[comp].size);
@ -1669,6 +1679,8 @@ export struct EntityManager
//fill components with default data //fill components with default data
foreach (comp; info.components) foreach (comp; info.components)
{ {
if (components[comp].size == 0)
continue;
memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp],
components[comp].init_data.ptr, components[comp].size); 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 //fill components with default data and copy from base template
foreach (comp; info.components) 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], memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp],
base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp], base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp],
components[comp].size); components[comp].size);
} }
else //fill with default data else //fill with default data
{ {
if (components[comp].size == 0)
continue;
memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp],
components[comp].init_data.ptr, components[comp].size); 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_add_info = Mallocator.makeArray!(EntityInfo*)(instance.components.length);
info.comp_rem_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*)(info.deltas.length);
foreach (comp; info.components) foreach (comp; info.components)
{ {
@ -1894,11 +1911,11 @@ export struct EntityManager
} }
add_len++; add_len++;
//move elements after new listener //move elements after new listener
if(add_len < tmp_add.length) if (add_len < tmp_add.length)
for (int k = add_len; k > j; k--) for (int k = add_len; k > j; k--)
{ {
tmp_add[k] = tmp_add[k - 1]; tmp_add[k] = tmp_add[k - 1];
} }
//assign listener //assign listener
tmp_add[j] = cast(ushort) i; tmp_add[j] = cast(ushort) i;
} }
@ -1914,11 +1931,11 @@ export struct EntityManager
} }
rem_len++; rem_len++;
//move elements after new listener //move elements after new listener
if(rem_len < tmp_add.length) if (rem_len < tmp_add.length)
for (int k = rem_len; k > j; k--) for (int k = rem_len; k > j; k--)
{ {
tmp_rem[k] = tmp_rem[k - 1]; tmp_rem[k] = tmp_rem[k - 1];
} }
//assign listener //assign listener
tmp_rem[j] = cast(ushort) i; tmp_rem[j] = cast(ushort) i;
} }
@ -1934,11 +1951,11 @@ export struct EntityManager
} }
ch_len++; ch_len++;
//move elements after new listener //move elements after new listener
if(ch_len < tmp_add.length) if (ch_len < tmp_add.length)
for (int k = ch_len; k > j; k--) for (int k = ch_len; k > j; k--)
{ {
tmp_ch[k] = tmp_ch[k - 1]; tmp_ch[k] = tmp_ch[k - 1];
} }
//assign listener //assign listener
tmp_ch[j] = cast(ushort) i; tmp_ch[j] = cast(ushort) i;
} }
@ -2160,6 +2177,8 @@ export struct EntityManager
foreach (comp; new_info.components) foreach (comp; new_info.components)
{ {
uint comp_size = components[comp].size; 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, 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); 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]) 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; 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) 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++; j++;
} }
else if (j >= info.components.length || id == new_ids[k]) 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++; k++;
} }
else else
{ {
assert(id != new_ids[0]); 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++; j++;
} }
} }
@ -2376,28 +2400,30 @@ export struct EntityManager
//__addComponents(entity_id, new_ids, pointers); //__addComponents(entity_id, new_ids, pointers);
ComponentRef[num] _comps; ComponentRef[num] _comps;
static foreach(i, comp; comps) static foreach (i, comp; comps)
{ {
_comps[i] = comp.ref_; _comps[i] = comp.ref_;
} }
addComponents(entity_id, _comps); addComponents(entity_id, _comps);
} }
export void addComponents(const EntityID entity_id, ComponentRef[] comps) nothrow @nogc 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]; ThreadData* data = &threads[threadID];
data.changeEntitiesList.add(cast(ubyte) 1u); data.changeEntitiesList.add(cast(ubyte) 1u);
data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]); data.changeEntitiesList.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]);
data.changeEntitiesList.add((cast(ubyte*)&num)[0 .. uint.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]); 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); /*data.changeEntitiesList.add(cast(ubyte[]) new_ids);
static foreach (i, comp; comps) static foreach (i, comp; comps)
@ -2449,14 +2475,15 @@ export struct EntityManager
foreach (i, comp; info.components) foreach (i, comp; info.components)
{ {
memcpy(cast(void*) new_block + info.deltas[comp] + components[comp].size * new_id, ushort size = components[comp].size;
cast(void*) block + info.deltas[comp] + components[comp].size * index, if (size != 0)
components[comp].size); 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) if (components[comp].create_callback)
{ {
components[comp].create_callback(cast( components[comp].create_callback(
void*) block + info.deltas[comp] + new_id * components[comp].size); cast(void*) block + info.deltas[comp] + new_id * size);
} }
} }
@ -2552,20 +2579,21 @@ export struct EntityManager
foreach (comp; info.components) foreach (comp; info.components)
{ {
uint size = components[comp].size; uint size = components[comp].size;
memcpy(cast(void*) block + info.deltas[comp] + size * id, if (size != 0)
tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); 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]; ushort delta = info.deltas[comp.component_id];
if(delta != ushort.max) if (delta != ushort.max)
{ {
uint size = components[comp.component_id].size; uint size = components[comp.component_id].size;
memcpy(cast(void*) block + delta + size * id, if (size != 0)
comp.ptr, size); memcpy(cast(void*) block + delta + size * id, comp.ptr, size);
} }
} }
} }
@ -2678,8 +2706,10 @@ export struct EntityManager
{ {
//get entity and block meta data pointers //get entity and block meta data pointers
Entity* entity = id_manager.getEntityPointer(id); Entity* entity = id_manager.getEntityPointer(id);
if (entity is null) if (entity is null)
return; //return if entity doesn't exist return; //return if entity doesn't exist
EntitiesBlock* block = getMetaData(entity); EntitiesBlock* block = getMetaData(entity);
EntityInfo* info = block.type_info; EntityInfo* info = block.type_info;
@ -2700,6 +2730,9 @@ export struct EntityManager
{ {
EntityInfo* info = block.type_info; EntityInfo* info = block.type_info;
if (info.last_block.added_count)
updateBlock(info.last_block);
info.last_block.entities_count--; info.last_block.entities_count--;
uint pos = block.entityIndex(entity); uint pos = block.entityIndex(entity);
@ -2720,9 +2753,11 @@ export struct EntityManager
{ {
foreach (comp; info.components) foreach (comp; info.components)
{ {
uint size = components[comp].size;
if (size == 0)
continue;
void* src = cast(void*) info.last_block + info.deltas[comp]; void* src = cast(void*) info.last_block + info.deltas[comp];
void* dst = cast(void*) 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); memcpy(dst + pos * size, src + info.last_block.entities_count * size, size);
} }
@ -2768,7 +2803,8 @@ export struct EntityManager
{ {
uint index = 0; uint index = 0;
uint len = cast(uint) thread.changeEntitiesListPrev.length; 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]; void*[32] pointers; // = (cast(void**) alloca(num * (void*).sizeof))[0 .. num];
while (index < len) while (index < len)
{ {
@ -2798,7 +2834,7 @@ export struct EntityManager
pointers[i] = &thread.changeEntitiesListPrev[index]; pointers[i] = &thread.changeEntitiesListPrev[index];
index += components[ids[i]].size; index += components[ids[i]].size;
} }
__addComponents(id, ids, pointers[0 .. num]); __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); (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() private bool updateBlocks()
{ {
bool has_work = false; bool has_work = false;
@ -2892,22 +2945,11 @@ export struct EntityManager
foreach (ref ThreadData thread; threads) foreach (ref ThreadData thread; threads)
{ {
//thread.swapToUpdate(); //thread.swapToUpdate();
if(thread.blockToUpdatePrev.length)has_work = true; if (thread.blockToUpdatePrev.length)
has_work = true;
foreach (block; thread.blockToUpdatePrev) foreach (block; thread.blockToUpdatePrev)
{ {
EntityInfo* info = block.type_info; updateBlock(block);
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);
}
} }
thread.blockToUpdatePrev.clear(); thread.blockToUpdatePrev.clear();
} }
@ -2920,7 +2962,8 @@ export struct EntityManager
//foreach (ref ThreadData thread; threads)thread.swapToRemove(); //foreach (ref ThreadData thread; threads)thread.swapToRemove();
foreach (ref ThreadData thread; threads) foreach (ref ThreadData thread; threads)
{ {
if(thread.entitiesToRemovePrev.length)has_work = true; if (thread.entitiesToRemovePrev.length)
has_work = true;
foreach (id; thread.entitiesToRemovePrev) foreach (id; thread.entitiesToRemovePrev)
{ {
__removeEntity(id); __removeEntity(id);
@ -2936,59 +2979,60 @@ export struct EntityManager
// bool empty = true; // bool empty = true;
//while (1) //while (1)
//{ //{
//event_manager.swapCurrent(); //event_manager.swapCurrent();
uint current_index; uint current_index;
if (event_manager.current_index == 0) if (event_manager.current_index == 0)
current_index = cast(uint) threads.length; current_index = cast(uint) threads.length;
else else
current_index = 0; current_index = 0;
foreach (i, event; event_manager.events) 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 EventManager.EventBlock* block = first_block;
.. current_index + threads.length]) if (block)
has_work = true;
// {
// has_work = true;
// //empty = false;
// }
while (block)
{ {
EventManager.EventBlock* block = first_block; EventCallData call_data;
if (block)has_work = true; void* event_pointer = cast(void*) block + event.data_offset;
// { foreach (j; 0 .. block.count)
// has_work = true;
// //empty = false;
// }
while (block)
{ {
EventCallData call_data; call_data.event = event_pointer + EntityID.sizeof;
void* event_pointer = cast(void*) block + event.data_offset; EntityID entity_id = *cast(EntityID*)(event_pointer);
foreach (j; 0 .. block.count) Entity* entity = id_manager.getEntityPointer(entity_id);
if (entity)
{ {
call_data.event = event_pointer; call_data.block = getMetaData(entity);
EntityID entity_id = *cast(EntityID*) event_pointer; call_data.id = call_data.block.entityIndex(entity);
Entity* entity = id_manager.getEntityPointer(entity_id); call_data.entity = entity;
if (entity)
{
call_data.block = getMetaData(entity);
call_data.id = call_data.block.entityIndex(entity);
foreach (caller; events[i].callers) foreach (caller; events[i].callers)
{ {
if ( if (call_data.block.type_info.systems[caller.system.m_id] == false
call_data.block.type_info.systems[caller.system.m_id] || !caller.system.enabled || !caller.system.willExecute)
== false) continue;
continue; call_data.system_pointer = caller.system.m_system_pointer;
call_data.system_pointer = caller.system.m_system_pointer; (cast(void function(ref EventCallData) nothrow @nogc) caller
(cast(void function( .callback)(call_data);
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; // if (empty)
// empty = true; // break;
// empty = true;
//} //}
return has_work; return has_work;
} }
@ -2996,7 +3040,7 @@ export struct EntityManager
private void swapData() nothrow @nogc private void swapData() nothrow @nogc
{ {
event_manager.swapCurrent(); event_manager.swapCurrent();
foreach(ref ThreadData thread; threads) foreach (ref ThreadData thread; threads)
{ {
thread.swapData(); thread.swapData();
} }
@ -3005,13 +3049,13 @@ export struct EntityManager
export void commit() export void commit()
{ {
bool has_work = true; bool has_work = true;
while(has_work) while (has_work)
{ {
swapData(); swapData();
has_work = false; has_work = false;
has_work |= updateEvents(); has_work |= updateEvents();
id_manager.optimize(); id_manager.optimize();
has_work |= updateBlocks(); has_work |= updateBlocks();
has_work |= changeEntities(); 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); ushort id = getPassID(name);
if(id == ushort.max)return null; if (id == ushort.max)
return null;
return passes[id]; return passes[id];
} }
ushort getPassID(const (char)[] name) ushort getPassID(const(char)[] name)
{ {
return passes_map.get(name, ushort.max); return passes_map.get(name, ushort.max);
} }
@ -3267,6 +3312,7 @@ export struct EntityManager
EntitiesBlock* block; EntitiesBlock* block;
void* system_pointer; void* system_pointer;
void* event; void* event;
Entity* entity;
ushort id; ushort id;
} }
@ -3357,9 +3403,9 @@ export struct EntityManager
return new_info; 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*)( EntityInfo*[] new_infos = Mallocator.makeArray!(EntityInfo*)(
instance.components.length, &this); instance.components.length, &this);
@ -3371,7 +3417,7 @@ export struct EntityManager
Mallocator.dispose(comp_rem_info); Mallocator.dispose(comp_rem_info);
} }
comp_rem_info = new_infos; comp_rem_info = new_infos;
} }*/
if (comp_rem_info[id]) if (comp_rem_info[id])
return comp_rem_info[id]; return comp_rem_info[id];
@ -3386,8 +3432,9 @@ export struct EntityManager
ids[len++] = comp; ids[len++] = comp;
} }
} }
if (len == components.length) assert(len != components.length);
return &this; //if (len == components.length)
// return &this;
assert(len == components.length - 1); assert(len == components.length - 1);
@ -3455,23 +3502,14 @@ export struct EntityManager
*/ */
struct EntitiesBlock 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 ///return pointer to first element in block
export void* dataBegin() nothrow @nogc pure export void* dataBegin() nothrow @nogc pure return
{ {
ushort dif = EntitiesBlock.sizeof; ushort dif = EntitiesBlock.sizeof;
return cast(void*)&this + dif; 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) static if (EntityID.sizeof == 8)
return cast(ushort)((cast(void*) entity - dataBegin()) >> 3); return cast(ushort)((cast(void*) entity - dataBegin()) >> 3);
@ -3593,32 +3631,32 @@ export struct EntityManager
struct ThreadData struct ThreadData
{ {
ref Vector!EntityID entitesToRemove() @nogc nothrow ref Vector!EntityID entitesToRemove() @nogc nothrow return
{ {
return entities_to_remove[data_index]; return entities_to_remove[data_index];
} }
ref SimpleVector changeEntitiesList() @nogc nothrow ref SimpleVector changeEntitiesList() @nogc nothrow return
{ {
return change_entities_list[data_index]; 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]; 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]; 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]; 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]; return blocks_to_update[1 - data_index];
} }

View file

@ -7,4 +7,4 @@ public import bubel.ecs.system;
import bubel.ecs.events; import bubel.ecs.events;
import bubel.ecs.id_manager; import bubel.ecs.id_manager;
import bubel.ecs.std; import bubel.ecs.std;

View file

@ -16,10 +16,12 @@ struct SimpleVector
///Add element to vector ///Add element to vector
void add(ubyte el) nothrow @nogc void add(ubyte el) nothrow @nogc
{ {
while(used >= data.length) while (used >= data.length)
{ {
if(data is null)data = Mallocator.makeArray!ubyte(1024); if (data is null)
else data = Mallocator.expandArray(data,data.length); data = Mallocator.makeArray!ubyte(1024);
else
data = Mallocator.expandArray(data, data.length);
} }
data[used++] = el; data[used++] = el;
} }
@ -27,10 +29,12 @@ struct SimpleVector
///Add array of elements to vector ///Add array of elements to vector
void add(ubyte[] el) nothrow @nogc 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); if (data is null)
else data = Mallocator.expandArray(data,data.length); data = Mallocator.makeArray!ubyte(1024);
else
data = Mallocator.expandArray(data, data.length);
} }
memcpy(data.ptr + used, el.ptr, el.length); memcpy(data.ptr + used, el.ptr, el.length);
used += el.length; used += el.length;
@ -44,23 +48,23 @@ struct SimpleVector
export ref ubyte opIndex(size_t pos) nothrow @nogc 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 ///set vector length to 0
void clear() nothrow @nogc void clear() nothrow @nogc
@ -70,4 +74,4 @@ struct SimpleVector
ubyte[] data = null; ubyte[] data = null;
size_t used = 0; size_t used = 0;
} }

View file

@ -7,45 +7,50 @@ License: BSD 3-clause, see LICENSE file in project root folder.
*/ */
module bubel.ecs.std; module bubel.ecs.std;
version(Emscripten)version = ECSEmscripten; version (Emscripten) version = ECSEmscripten;
import std.traits; import std.traits;
version(ECSEmscripten) version (ECSEmscripten)
{ {
extern(C) struct pthread_mutex_t extern (C) struct pthread_mutex_t
{ {
union union
{ {
int[6] __i; int[6] __i;
void[6] *__p; 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) struct pthread_mutexattr_t
extern(C) void exit (int status) nothrow @nogc; {
extern(C) void __assert(const(char)* msg, const(char)* file, uint line) { exit(-20);} uint __attr;
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 memcmp(const void* s1, const void* s2, size_t size);
extern(C) int pthread_mutex_trylock(pthread_mutex_t *mutex) @nogc nothrow; extern (C) void exit(int status) nothrow @nogc;
extern(C) int pthread_mutex_unlock(pthread_mutex_t *mutex) @nogc nothrow; extern (C) void __assert(const(char)* msg, const(char)* file, uint line)
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; exit(-20);
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) 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 else
@ -55,23 +60,24 @@ else
public import core.stdc.stdlib : qsort; public import core.stdc.stdlib : qsort;
} }
version(ECSEmscripten) version (ECSEmscripten)
{ {
} }
else version (Windows) else version (Windows)
{ {
import core.sys.windows.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; 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)
version (LDC)
{ {
/*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system; /*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system;
alias alloca = __alloca;*/ alias alloca = __alloca;*/
extern(Windows) void ___chkstk_ms() @nogc nothrow @system; extern (Windows) void ___chkstk_ms() @nogc nothrow @system;
extern(Windows) void __chkstk() extern (Windows) void __chkstk()
{ {
___chkstk_ms(); ___chkstk_ms();
} }
@ -80,17 +86,18 @@ else version (Windows)
else version (Posix) else version (Posix)
{ {
import core.sys.posix.pthread; 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 byte[max_alloca] alloca_array;
private __gshared uint alloca_pos = 0; 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]; void* ret = &alloca_array[alloca_pos];
alloca_pos += length; alloca_pos += length;
return ret; return ret;
@ -101,28 +108,44 @@ version(ECSEmscripten)
return null; 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 __gshared byte[max_alloca] alloca_array;
private uint alloca_pos = 0; 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]; void* ret = &alloca_array[alloca_pos];
alloca_pos += length; alloca_pos += length;
return ret; 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; public import core.stdc.stdlib : alloca;
} }
@ -131,13 +154,13 @@ static struct Mallocator
{ {
static T[] makeArray(T)(size_t length) nothrow @nogc 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; static immutable T init = T.init;
foreach(i;0..ret.length) foreach (i; 0 .. ret.length)
{ {
memcpy(&ret[i], &init, T.sizeof); memcpy(&ret[i], &init, T.sizeof);
} }
@ -145,7 +168,8 @@ static struct Mallocator
else else
{ {
static import std.conv; static import std.conv;
foreach(i;0..ret.length)
foreach (i; 0 .. ret.length)
{ {
std.conv.emplace(&ret[i]); std.conv.emplace(&ret[i]);
} }
@ -155,78 +179,94 @@ static struct Mallocator
static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc
{ {
T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length]; T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length];
foreach(ref v; ret)v = initializer; foreach (ref v; ret)
v = initializer;
return ret; return ret;
} }
static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc
{ {
size_t new_length = array.length + length; 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 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); 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]; foreach (i, ref v; ret)
v = array[i];
return ret; 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 import std.conv;
static if(__traits(isPOD, T))
static if (__traits(isPOD, T))
{ {
static immutable T init = T.init; static immutable T init = T.init;
memcpy(ret, &init, T.sizeof); 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; return ret;
} }
static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc
{ {
void* ret; void* ret;
version(Posix)posix_memalign(&ret, alignment, length);//ret = aligned_alloc(alignment, length); version (Posix)
else version(Windows)ret = _aligned_malloc(length, alignment); posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length);
else version(ECSEmscripten)posix_memalign(&ret, alignment, length);//malloc(length); else version (Windows)
else static assert(0, "Unimplemented platform!"); ret = _aligned_malloc(length, alignment);
else version (ECSEmscripten)
posix_memalign(&ret, alignment, length); //malloc(length);
else
static assert(0, "Unimplemented platform!");
return ret; return ret;
} }
static void dispose(T)(T object) nothrow @nogc static void dispose(T)(T object) nothrow @nogc
{ {
static if(__traits(hasMember, T, "__xdtor"))object.__xdtor(); static if (__traits(hasMember, T, "__xdtor"))
else static if(__traits(hasMember, T, "__dtor"))object.__dtor(); object.__xdtor();
free(cast(void*)object); else static if (__traits(hasMember, T, "__dtor"))
object.__dtor();
free(cast(void*) object);
} }
static void alignDispose(T)(T object) static void alignDispose(T)(T object)
{ {
static if(__traits(hasMember, T, "__xdtor"))object.__xdtor(); static if (__traits(hasMember, T, "__xdtor"))
else static if(__traits(hasMember, T, "__dtor"))object.__dtor(); object.__xdtor();
version(Posix)free(cast(void*)object); else static if (__traits(hasMember, T, "__dtor"))
else version(Windows)_aligned_free(cast(void*)object); object.__dtor();
else version(ECSEmscripten)free(cast(void*)object); version (Posix)
else static assert(0, "Unimplemented platform!"); 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 struct Mutex
{ {
version(ECSEmscripten) version (ECSEmscripten)
{ {
void initialize() nothrow @nogc void initialize() nothrow @nogc
{ {
pthread_mutexattr_t attr = void; pthread_mutexattr_t attr = void;
//pthread_mutexattr_init(&attr); //pthread_mutexattr_init(&attr);
//pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //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); //pthread_mutexattr_destroy(&attr);
} }
@ -257,7 +297,7 @@ struct Mutex
{ {
void initialize() nothrow @nogc void initialize() nothrow @nogc
{ {
InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_handle); InitializeCriticalSection(cast(CRITICAL_SECTION*)&m_handle);
} }
void destroy() nothrow @nogc void destroy() nothrow @nogc
@ -280,7 +320,7 @@ struct Mutex
return TryEnterCriticalSection(&m_handle) != 0; return TryEnterCriticalSection(&m_handle) != 0;
} }
CRITICAL_SECTION m_handle; CRITICAL_SECTION m_handle;
} }
else version (Posix) else version (Posix)
{ {
@ -289,9 +329,9 @@ struct Mutex
pthread_mutexattr_t attr = void; pthread_mutexattr_t attr = void;
pthread_mutexattr_init(&attr); pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 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); pthread_mutexattr_destroy(&attr);
} }
@ -318,5 +358,6 @@ struct Mutex
private pthread_mutex_t m_handle; private pthread_mutex_t m_handle;
} }
else static assert(0, "unsupported platform!"); else
} static assert(0, "unsupported platform!");
}

View file

@ -1,30 +1,36 @@
module bubel.ecs.vector; module bubel.ecs.vector;
import core.bitop; import core.bitop;
//import core.stdc.stdlib : free, malloc; //import core.stdc.stdlib : free, malloc;
import bubel.ecs.std; import bubel.ecs.std;
//import core.stdc.string : memcpy, memset; //import core.stdc.string : memcpy, memset;
//import std.algorithm : swap; //import std.algorithm : swap;
import std.conv : emplace; import std.conv : emplace;
import std.traits : hasMember, isCopyable, TemplateOf, Unqual; 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; return 1 << bsr(num) + 1;
} }
export __gshared size_t gVectorsCreated = 0; export __gshared size_t gVectorsCreated = 0;
export __gshared size_t gVectorsDestroyed = 0; export __gshared size_t gVectorsDestroyed = 0;
struct Vector(T) { struct Vector(T)
{
T[] array; T[] array;
size_t used; size_t used;
public: public:
export this()(T t) { export this()(T t)
{
add(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); add(t);
} }
@ -42,79 +48,101 @@ public:
@disable this(this); @disable this(this);
export ~this() { export ~this()
{
clear(); clear();
} }
export void clear() { export void clear()
{
removeAll(); removeAll();
} }
export void removeAll() { export void removeAll()
if (array !is null) { {
if (array !is null)
{
/*foreach (ref el; array[0 .. used]) { /*foreach (ref el; array[0 .. used]) {
destroy(el); destroy(el);
}*/ }*/
//freeData(cast(void[]) array); //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++; gVectorsDestroyed++;
} }
array = null; array = null;
used = 0; used = 0;
} }
export bool empty() const { export bool empty() const
{
return (used == 0); return (used == 0);
} }
export size_t length() const { export size_t length() const
{
return used; return used;
} }
export void length(size_t newLength) { export void length(size_t newLength)
if (newLength > used) { {
if (newLength > used)
{
reserve(newLength); reserve(newLength);
foreach (ref el; array[used .. newLength]) { foreach (ref el; array[used .. newLength])
{
emplace(&el); emplace(&el);
} }
} else { }
foreach (ref el; array[newLength .. used]) { else
{
foreach (ref el; array[newLength .. used])
{
//destroy(el); //destroy(el);
static if(__traits(hasMember, T, "__xdtor"))el.__xdtor(); static if (__traits(hasMember, T, "__xdtor"))
else static if(__traits(hasMember, T, "__dtor"))el.__dtor(); el.__xdtor();
else static if (__traits(hasMember, T, "__dtor"))
el.__dtor();
} }
} }
used = newLength; used = newLength;
} }
export void reset() { export void reset()
{
used = 0; used = 0;
} }
export void reserve(size_t numElements) { export void reserve(size_t numElements)
if (numElements > array.length) { {
if (numElements > array.length)
{
extend(numElements); extend(numElements);
} }
} }
export size_t capacity() { export size_t capacity()
{
return array.length - used; return array.length - used;
} }
export void extend(size_t newNumOfElements) { export void extend(size_t newNumOfElements)
{
auto oldArray = manualExtend(array, newNumOfElements); auto oldArray = manualExtend(array, newNumOfElements);
if (oldArray !is null) { if (oldArray !is null)
{
freeData(oldArray); freeData(oldArray);
} }
} }
export @nogc void freeData(void[] data) { export @nogc void freeData(void[] data)
{
// 0x0F probably invalid value for pointers and other types // 0x0F probably invalid value for pointers and other types
memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD
free(data.ptr); 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) if (newNumOfElements == 0)
newNumOfElements = 2; newNumOfElements = 2;
if (array.length == 0) if (array.length == 0)
@ -126,22 +154,26 @@ public:
memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize); memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize);
array = memory[0 .. newNumOfElements]; array = memory[0 .. newNumOfElements];
//return cast(void[]) oldArray; //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; Vector!T duplicate;
duplicate.reserve(used); duplicate.reserve(used);
duplicate ~= array[0 .. used]; duplicate ~= array[0 .. used];
return duplicate; return duplicate;
} }
export bool canAddWithoutRealloc(uint elemNum = 1) { export bool canAddWithoutRealloc(uint elemNum = 1)
{
return used + elemNum <= array.length; return used + elemNum <= array.length;
} }
export void add()(T t) { export void add()(T t)
if (used >= array.length) { {
if (used >= array.length)
{
extend(nextPow2(used + 1)); extend(nextPow2(used + 1));
} }
emplace(&array[used], t); emplace(&array[used], t);
@ -149,48 +181,62 @@ public:
} }
/// Add element at given position moving others /// 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); assert(pos <= used);
if (used >= array.length) { if (used >= array.length)
{
extend(array.length * 2); extend(array.length * 2);
} }
foreach_reverse (size_t i; pos .. used) { foreach_reverse (size_t i; pos .. used)
{
//swap(array[i + 1], array[i]); //swap(array[i + 1], array[i]);
array[i+1] = array[i]; array[i + 1] = array[i];
} }
emplace(&array[pos], t); emplace(&array[pos], t);
used++; used++;
} }
export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { export void add(X)(X[] t) if (is(Unqual!X == Unqual!T))
if (used + t.length > array.length) { {
if (used + t.length > array.length)
{
extend(nextPow2(used + t.length)); extend(nextPow2(used + t.length));
} }
foreach (i; 0 .. t.length) { foreach (i; 0 .. t.length)
{
emplace(&array[used + i], t[i]); emplace(&array[used + i], t[i]);
} }
used += t.length; used += t.length;
} }
export void remove(size_t elemNum) { export void remove(size_t elemNum)
{
//destroy(array[elemNum]); //destroy(array[elemNum]);
static if(__traits(hasMember, T, "__xdtor"))array[elemNum].__xdtor(); static if (__traits(hasMember, T, "__xdtor"))
else static if(__traits(hasMember, T, "__dtor"))array[elemNum].__dtor(); array[elemNum].__xdtor();
else static if (__traits(hasMember, T, "__dtor"))
array[elemNum].__dtor();
//swap(array[elemNum], array[used - 1]); //swap(array[elemNum], array[used - 1]);
array[elemNum] = array[used - 1]; array[elemNum] = array[used - 1];
used--; used--;
} }
export void removeStable()(size_t elemNum) { export void removeStable()(size_t elemNum)
{
used--; used--;
foreach (i; 0 .. used) { foreach (i; 0 .. used)
{
array[i] = array[i + 1]; array[i] = array[i + 1];
} }
} }
export bool tryRemoveElement()(T elem) { export bool tryRemoveElement()(T elem)
foreach (i, ref el; array[0 .. used]) { {
if (el == elem) { foreach (i, ref el; array[0 .. used])
{
if (el == elem)
{
remove(i); remove(i);
return true; return true;
} }
@ -198,55 +244,66 @@ public:
return false; return false;
} }
export void removeElement()(T elem) { export void removeElement()(T elem)
{
bool ok = tryRemoveElement(elem); bool ok = tryRemoveElement(elem);
assert(ok, "There is no such an element in vector"); 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]"); //debug assert(elemNum < used, "Range violation [index]");
return *cast(T*)&array.ptr[elemNum]; return *cast(T*)&array.ptr[elemNum];
} }
export auto opSlice() { export auto opSlice()
{
return array.ptr[0 .. used]; 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); assert(y <= used);
return array.ptr[x .. y]; return array.ptr[x .. y];
} }
export size_t opDollar() { export size_t opDollar()
{
return used; return used;
} }
export void opAssign(X)(X[] slice) { export void opAssign(X)(X[] slice)
{
reset(); reset();
this ~= slice; this ~= slice;
} }
export void opOpAssign(string op)(T obj) { export void opOpAssign(string op)(T obj)
{
//static assert(op == "~"); //static assert(op == "~");
add(obj); add(obj);
} }
export void opOpAssign(string op, X)(X[] obj) { export void opOpAssign(string op, X)(X[] obj)
{
//static assert(op == "~"); //static assert(op == "~");
add(obj); add(obj);
} }
export void opIndexAssign()(T obj, size_t elemNum) { export void opIndexAssign()(T obj, size_t elemNum)
{
assert(elemNum < used, "Range viloation"); assert(elemNum < used, "Range viloation");
array[elemNum] = obj; 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"); assert(b <= used && a <= b, "Range viloation");
array.ptr[a .. b] = obj; 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]; return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used];
} }
} }

View file

@ -68,6 +68,11 @@ struct CUnregistered
short value = 12; short value = 12;
} }
struct CFlag
{
mixin ECS.Component;
}
struct LongAddSystem struct LongAddSystem
{ {
mixin ECS.System; mixin ECS.System;
@ -110,6 +115,7 @@ struct EmptySystem
void beforeEveryTest() void beforeEveryTest()
{ {
CUnregistered.component_id = ushort.max;
gEM.initialize(0); gEM.initialize(0);
gEM.beginRegister(); gEM.beginRegister();
@ -119,6 +125,7 @@ void beforeEveryTest()
gEM.registerComponent!CDouble; gEM.registerComponent!CDouble;
gEM.registerComponent!CLong; gEM.registerComponent!CLong;
gEM.registerComponent!CShort; gEM.registerComponent!CShort;
gEM.registerComponent!CFlag;
gEM.endRegister(); gEM.endRegister();
} }
@ -131,11 +138,12 @@ void afterEveryTest()
@("AddEntity") @("AddEntity")
unittest unittest
{ {
ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray);
EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); assert(tmpl_.info.components.length == 3);
assert(tmpl_.info.components.length == 2); assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof));
assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CInt);
assert(tmpl_.getComponent!CFloat); assert(tmpl_.getComponent!CFloat);
assert(tmpl_.getComponent!CFlag);
assert(!tmpl_.getComponent!CLong); assert(!tmpl_.getComponent!CLong);
assert(!tmpl_.getComponent!CUnregistered); assert(!tmpl_.getComponent!CUnregistered);
assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CInt == 1);
@ -154,13 +162,66 @@ unittest
assert(*entity2.getComponent!CInt == 2); assert(*entity2.getComponent!CInt == 2);
assert(*entity2.getComponent!CFloat == 2.0); assert(*entity2.getComponent!CFloat == 2.0);
CInt cint = CInt(10); //CInt cint = CInt(10);
CLong clong; //CLong clong;
Entity* entity3 = gEM.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray); //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!CInt);
assert(entity3.getComponent!CFloat); assert(entity3.getComponent!CFloat);
assert(*entity3.getComponent!CInt == 10); assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0); 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 //allocate templates
@ -171,32 +232,48 @@ unittest
ushort[2] ids = [CInt.component_id, CFloat.component_id]; ushort[2] ids = [CInt.component_id, CFloat.component_id];
EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); EntityTemplate* tmpl_ = gEM.allocateTemplate(ids);
EntityTemplate* tmpl_d = gEM.allocateTemplate([CFloat.component_id, CInt.component_id, CFloat.component_id].staticArray); 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_d.info == tmpl_.info);
assert(tmpl_cp.info == tmpl_cp.info);
assert(tmpl_.info.components.length == 2); assert(tmpl_.info.components.length == 2);
assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CInt);
assert(tmpl_.getComponent!CFloat); assert(tmpl_.getComponent!CFloat);
assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CInt == 1);
assert(*tmpl_.getComponent!CFloat == 2.0); 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!CInt = 4;
*tmpl_.getComponent!CFloat = 5.0; *tmpl_.getComponent!CFloat = 5.0;
//allocate template from template with additional component //allocate template from template with additional components
ushort[1] ids2 = [CDouble.component_id]; ushort[2] ids2 = [CDouble.component_id,CFlag.component_id];
EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2); 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!CInt);
assert(tmpl_2.getComponent!CFloat); assert(tmpl_2.getComponent!CFloat);
assert(tmpl_2.getComponent!CDouble); assert(tmpl_2.getComponent!CDouble);
assert(tmpl_2.getComponent!CFlag);
assert(*tmpl_2.getComponent!CInt == 4); assert(*tmpl_2.getComponent!CInt == 4);
assert(*tmpl_2.getComponent!CFloat == 5.0); assert(*tmpl_2.getComponent!CFloat == 5.0);
assert(*tmpl_2.getComponent!CDouble == 3.0); assert(*tmpl_2.getComponent!CDouble == 3.0);
assert(tmpl_.info.blocksCount() == 0);
Entity* entity = gEM.addEntity(tmpl_); 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 //apply entity changes
gEM.commit(); gEM.commit();
assert(tmpl_.info.blocksCount() == 0);
//allocate template as entity copy //allocate template as entity copy
EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id); EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id);
assert(tmpl_3.info.components.length == 3); assert(tmpl_3.info.components.length == 3);
@ -217,18 +294,20 @@ unittest
assert(*tmpl_4.getComponent!CFloat == 2.0); assert(*tmpl_4.getComponent!CFloat == 2.0);
assert(*tmpl_4.getComponent!CDouble == 3.0); assert(*tmpl_4.getComponent!CDouble == 3.0);
//allocate template from template with two additional component //allocate template from template with three additional component
ushort[2] ids3 = [CDouble.component_id, CLong.component_id]; ushort[3] ids3 = [CDouble.component_id, CLong.component_id, CShort.component_id];
EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_, ids3); EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_2, ids3);
assert(tmpl_5.info.components.length == 4); assert(tmpl_5.info.components.length == 6);
assert(tmpl_5.getComponent!CInt); assert(tmpl_5.getComponent!CInt);
assert(tmpl_5.getComponent!CFloat); assert(tmpl_5.getComponent!CFloat);
assert(tmpl_5.getComponent!CDouble); assert(tmpl_5.getComponent!CDouble);
assert(tmpl_5.getComponent!CLong); assert(tmpl_5.getComponent!CLong);
assert(tmpl_5.getComponent!CShort);
assert(*tmpl_5.getComponent!CInt == 4); assert(*tmpl_5.getComponent!CInt == 4);
assert(*tmpl_5.getComponent!CFloat == 5.0); assert(*tmpl_5.getComponent!CFloat == 5.0);
assert(*tmpl_5.getComponent!CDouble == 3.0); assert(*tmpl_5.getComponent!CDouble == 3.0);
assert(*tmpl_5.getComponent!CLong == 10); assert(*tmpl_5.getComponent!CLong == 10);
assert(*tmpl_5.getComponent!CShort == 12);
//allocate template from template without one component //allocate template from template without one component
ushort[1] rem_ids = [CFloat.component_id]; ushort[1] rem_ids = [CFloat.component_id];
@ -239,7 +318,7 @@ unittest
//allocate template from template without one component and two additional //allocate template from template without one component and two additional
EntityTemplate* tmpl_7 = gEM.allocateTemplate(tmpl_, ids3, rem_ids); 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!CInt);
assert(tmpl_7.getComponent!CDouble); assert(tmpl_7.getComponent!CDouble);
assert(tmpl_7.getComponent!CLong); assert(tmpl_7.getComponent!CLong);
@ -496,6 +575,10 @@ unittest
gEM.endRegister(); gEM.endRegister();
assert(gEM.getPass("custom"));
assert(!gEM.getPass("custommm"));
LongAddSystem* system = gEM.getSystem!LongAddSystem; LongAddSystem* system = gEM.getSystem!LongAddSystem;
assert(system !is null); assert(system !is null);
assert(system.updates_count == 0); assert(system.updates_count == 0);
@ -1131,9 +1214,9 @@ unittest
assert(*entity2.getComponent!CShort == 12); assert(*entity2.getComponent!CShort == 12);
gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,10)); gEM.sendEvent(id,ETest2(10));
gEM.sendEvent(id2,ETest()); gEM.sendEvent(id2,ETest());
gEM.sendEvent(id2,ETest2(id2,12)); gEM.sendEvent(id2,ETest2(12));
gEM.commit(); gEM.commit();
assert(ETest2.destory == 2); assert(ETest2.destory == 2);
@ -1144,7 +1227,7 @@ unittest
gEM.addComponents(id, CInt(2), CShort(1)); gEM.addComponents(id, CInt(2), CShort(1));
gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,2)); gEM.sendEvent(id,ETest2(2));
gEM.commit(); gEM.commit();
assert(ETest2.destory == 3); assert(ETest2.destory == 3);
@ -1157,7 +1240,7 @@ unittest
foreach(i;0..10000) foreach(i;0..10000)
{ {
gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,4)); gEM.sendEvent(id,ETest2(4));
result += 16; result += 16;
result += 8; 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.beginRegister();
gEM.registerSystem!TestSystem(0); 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.beginRegister();
gEM.registerDependency(TestDependency); gEM.registerDependency(TestDependency);

142
tests/bugs.d Normal file
View file

@ -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();
}

View file

@ -210,7 +210,7 @@ struct TestRunner(Args...)
else else
test.name = attributes[0]; test.name = attributes[0];
static if (__traits(hasMember, module_, "beforeEveryTest")) static if (__traits(hasMember, module_, "beforeEveryTest") && __traits(compiles, module_.beforeEveryTest()))
module_.beforeEveryTest(); module_.beforeEveryTest();
if(before)before(); if(before)before();
@ -256,7 +256,7 @@ struct TestRunner(Args...)
else else
suite.failed++; suite.failed++;
suite.tests ~= test; suite.tests ~= test;
static if (__traits(hasMember, module_, "afterEveryTest")) static if (__traits(hasMember, module_, "afterEveryTest") && __traits(compiles, module_.beforeEveryTest()))
module_.afterEveryTest(); module_.afterEveryTest();
} }
passed += suite.passed; 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[]); runner.runTests(include[], exclude[]);