Common update:

-added multiple new function to allocate template and add entity
-updated README.md (complete initial version)
-empty components now don't take memory
-fixedd small bug with TestRunner
-added many new tests (HashMap, Vector, EntityMeta, ...)
-added default hashing function to HashMap
-fixed critical bug with adding entities
-fixed small bug with adding entity with remplacement components
-added asserts into code to better bug detection
-small performance improvement for events
-added ComponentRef structure which contain data pointer and componentID
-remove EntityID from Event structure
-now events are handled before removing entiteis
-fixed GDC compilation
-fixed rendering of rotated sprites
-added weapons as separate entities to space ship and others
-added Tower enemy to SpaceInvaders demo
-added Boss to SpaceInvaders demo (boss has four tower attached to it)
-Boss towers shoot multiple bullets upon death
-fixed critical bug with demos switching
-fixed critical bug related to adding/removing entities form inside onAdd/onRemove entity callback
-added animation support
-added particles sypport and particles for firing and explostions, and more
-multithreaded rendering now has same rendering order as singlethreaded
-application automaticallu detect host CPU threads count
-added upgrades to SPaceInvaders demo
-fixed texture memory freeing
-improved documentation
-improved multithreaded performance
-improve shader code
-fixed registration issue
-some additional performance improvements
-added depth and colors to rendering parameters
-jobs now has names corresponding to their systems
-change execute() -> willExecute()
-added EntityMeta structure to speedup getting fetching components form entity
-improved multithreading rendering
-added possibility tio change number of threads runtime
-added bullets collision detection in SpaceInvaders demo
-some CI changes
-added VBO batch rendering (current default, no render mode switch yet)
-fixed camera positioning calculation
-fixed buffer issue with WebGL
-added viewport scalling (at least 300 pixels height). Pixels are scalled if screen is bigger.
-center demos gameplay area
-added fullpage html template for Emscripten build
-added many new sprites to atlas
-fixed critical bug with CPU usage in multithreaded mode
-snake render tile coresponding to body part
-snake is destroyed after collision and emit some particles
-added some functionality to vectors
-fixed documentation issue in Manager.d
-more minor code changes and cleanup
This commit is contained in:
Dawid Masiukiewicz 2020-05-28 16:48:42 +00:00
parent 2ddb97e9ce
commit 024356df9b
62 changed files with 5918 additions and 1673 deletions

132
source/bubel/ecs/atomic.d Normal file
View file

@ -0,0 +1,132 @@
/************************************************************************************************************************
It's internal code. Can be used for atomics if emscripten backend will be used.
This module contain atomic operations which include support for emscripten atomics functions.
Emscripten functions are contained in API similar to druntime.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.atomic;
version (Emscripten) version = ECSEmscripten;
version (ECSEmscripten)
{
import std.traits;
enum MemoryOrder
{
acq,
acq_rel,
raw,
rel,
seq
}
extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure;
extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure;
extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure;
extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure;
extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure;
extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure;
public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod)
{
static if (op == "+=")
{
static if (is(T == byte) || is(T == ubyte))
return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val,
cast(Unqual!T) mod) + 1);
else static if (is(T == short) || is(T == ushort))
return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val,
cast(Unqual!T) mod) + 1);
else static if (is(T == int) || is(T == uint))
return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val,
cast(Unqual!T) mod) + 1);
else
static assert(0);
}
else static if (op == "-=")
{
static if (is(T == byte) || is(T == ubyte))
return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val,
cast(Unqual!T) mod) - 1);
else static if (is(T == short) || is(T == ushort))
return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val,
cast(Unqual!T) mod) - 1);
else static if (is(T == int) || is(T == uint))
return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val,
cast(Unqual!T) mod) - 1);
else
static assert(0);
}
}
public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val,
V newval)
{
alias UT = Unqual!T;
static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte))
emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval);
else static if (is(UT == short) || is(UT == ushort))
emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval);
else static if (is(UT == int) || is(UT == uint))
emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval);
else
static assert(0);
}
public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(
ref const T val)
{
alias UT = Unqual!T;
static if (is(UT == bool))
return emscripten_atomic_load_u8(cast(const void*)&val) != 0;
else static if (is(UT == byte) || is(UT == ubyte))
return emscripten_atomic_load_u8(cast(const void*)&val);
else static if (is(UT == short) || is(UT == ushort))
return emscripten_atomic_load_u16(cast(const void*)&val);
else static if (is(UT == int) || is(UT == uint))
return emscripten_atomic_load_u32(cast(const void*)&val);
else
static assert(0);
}
public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq,
MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis)
{
alias UT = Unqual!T;
static if (is(UT == bool))
return emscripten_atomic_cas_u8(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else static if (is(UT == byte) || is(UT == ubyte))
return emscripten_atomic_cas_u8(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else static if (is(UT == short) || is(UT == ushort))
return emscripten_atomic_cas_u16(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else static if (is(UT == int) || is(UT == uint))
return emscripten_atomic_cas_u32(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else
static assert(0);
}
}
else
{
public import core.atomic;
}

View file

@ -20,9 +20,9 @@ Struct EntitiesData
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.attributes;
module bubel.ecs.attributes;
///Used to mark optional components for system.
enum optional = "optional";
///Used to mark readonly components for system. "const" can be used insted.
enum readonly = "readonly";
enum readonly = "readonly";

View file

@ -6,10 +6,10 @@ Module contain memory allocator.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.block_allocator;
module bubel.ecs.block_allocator;
import ecs.manager;
import ecs.std;
import bubel.ecs.manager;
import bubel.ecs.std;
/************************************************************************************************************************
Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated.
@ -35,7 +35,7 @@ struct BlockAllocator
*/
void freeBlock(void* block) nothrow @nogc
{
*cast(void**)block = next_block;
*cast(void**) block = next_block;
next_block = block;
}
@ -44,9 +44,9 @@ struct BlockAllocator
*/
void freeMemory() nothrow @nogc
{
while(pointers)
while (pointers)
{
foreach(i;0..pointers.numberof)
foreach (i; 0 .. pointers.numberof)
{
Mallocator.alignDispose(pointers.blocks[i]);
}
@ -60,11 +60,14 @@ private:
void allocBlock() nothrow @nogc
{
next_block = cast(void*) Mallocator.alignAlloc(
block_size * blocks_in_allocation, block_size);
next_block = cast(void*) Mallocator.alignAlloc(block_size * blocks_in_allocation,
block_size);
if (next_block is null)
assert(0);
if(pointers is null)pointers = Mallocator.make!BlockPointers;
if(pointers.numberof >= 32)
if (pointers is null)
pointers = Mallocator.make!BlockPointers;
if (pointers.numberof >= 32)
{
BlockPointers* new_pointers = Mallocator.make!BlockPointers;
new_pointers.next_pointers = pointers;
@ -77,8 +80,7 @@ private:
void** pointer = cast(void**)(next_block + i * block_size);
*pointer = next_block + (i + 1) * block_size;
}
void** pointer = cast(void**)(
next_block + (blocks_in_allocation - 1) * block_size);
void** pointer = cast(void**)(next_block + (blocks_in_allocation - 1) * block_size);
*pointer = null;
}

View file

@ -49,10 +49,10 @@ Struct System1
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.core;
module bubel.ecs.core;
public import ecs.manager;
public import ecs.entity;
public import bubel.ecs.manager;
public import bubel.ecs.entity;
/************************************************************************************************************************
Main struct used as namespace for templates.
@ -74,15 +74,19 @@ static struct ECS
mixin template Component()
{
__gshared ushort component_id = ushort.max;
ComponentRef ref_() @nogc nothrow return
{
return ComponentRef(&this, component_id);
}
}
/************************************************************************************************************************
Mark structure as Event. Should be added on top of structure (before any data).
*/
mixin template Event()
mixin template Event()
{
__gshared ushort event_id = ushort.max;
EntityID entity_id;
}
/************************************************************************************************************************
@ -92,4 +96,20 @@ static struct ECS
{
alias ExcludedComponents = T;
}
}
/************************************************************************************************************************
Make list of readonly ependencies. This template get strings as arguments. Should be added inside System structure.
*/
mixin template ReadOnlyDependencies(T...)
{
alias ReadOnlyDependencies = T;
}
/************************************************************************************************************************
Make list of writable ependencies. This template get strings as arguments. Should be added inside System structure.
*/
mixin template WritableDependencies(T...)
{
alias WritableDependencies = T;
}
}

View file

@ -4,10 +4,10 @@ Entity module.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.entity;
module bubel.ecs.entity;
import ecs.system;
import ecs.manager;
import bubel.ecs.system;
import bubel.ecs.manager;
/************************************************************************************************************************
Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference!
@ -39,11 +39,44 @@ struct Entity
if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0)
return null;
static if (EntityID.sizeof == 8)
uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) >> 3);
else
uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) / EntityID.sizeof());
return cast(T*)(cast(void*)block + info.deltas[T.component_id] + ind * T.sizeof);
return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof);
}
bool hasComponent(ushort component_id)
{
EntityManager.EntitiesBlock* block = gEM.getMetaData(&this);
EntityManager.EntityInfo* info = block.type_info;
if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false;
return true;
}
EntityMeta getMeta()
{
EntityMeta meta;
meta.block = gEM.getMetaData(&this);
meta.index = meta.block.entityIndex(&this);
return meta;
}
}
struct EntityMeta
{
EntityManager.EntitiesBlock* block;
ushort index;
T* getComponent(T)() const
{
const (EntityManager.EntityInfo)* info = block.type_info;
if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0)
return null;
return cast(T*)(cast(void*)block + block.type_info.deltas[T.component_id] + index * T.sizeof);
}
bool hasComponent(ushort component_id)
{
EntityManager.EntityInfo* info = block.type_info;
if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false;
return true;
}
}
@ -74,3 +107,14 @@ export struct EntityTemplate
return cast(T*)(entity_data.ptr + info.tmpl_deltas[T.component_id]);
}
}
/************************************************************************************************************************
ComponentRef contain component ID and pointer to it. It used to add component data to entity.
*/
export struct ComponentRef
{
///pointer to component
void* ptr;
///component index
ushort component_id;
}

View file

@ -1,9 +1,9 @@
module ecs.events;
module bubel.ecs.events;
import ecs.block_allocator;
import ecs.entity;
import ecs.manager;
import ecs.std;
import bubel.ecs.block_allocator;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import std.algorithm.comparison : max;
@ -20,7 +20,7 @@ package struct EventManager
void destroy() nothrow @nogc
{
if(event_block_alloc_mutex)
if (event_block_alloc_mutex)
{
event_block_alloc_mutex.destroy();
Mallocator.dispose(event_block_alloc_mutex);
@ -30,55 +30,60 @@ package struct EventManager
export void sendEvent(Ev)(EntityID id, Ev event, uint thread_id = 0) nothrow @nogc
{
uint block_id = current_index+thread_id;
uint block_id = current_index + thread_id;
EventData* data = &events[Ev.event_id];
EventBlock* block = data.blocks[block_id];
//EntityManager.EventInfo* info = &manager.events[Ev.event_id];
event.entity_id = id;
//event.entity_id = id;
if(block is null)
if (block is null)
{
event_block_alloc_mutex.lock();
scope (exit)
event_block_alloc_mutex.unlock();
block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock();
*block = EventBlock();
data.first_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();
scope (exit)
event_block_alloc_mutex.unlock();
EventBlock* new_block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock();
*new_block = EventBlock();
block.next = new_block;
block = new_block;
data.blocks[block_id] = block;
}
Ev* event_array = cast(Ev*)(cast(void*)block + data.data_offset);
event_array[block.count] = event;
uint size = Ev.sizeof + EntityID.sizeof;
void* ptr = cast(void*) block + data.data_offset + block.count * size;
*cast(EntityID*)ptr = id;
*cast(Ev*)(ptr + EntityID.sizeof) = event;
//Ev* event_array = cast(Ev*)(cast(void*) block + data.data_offset);
//event_array[block.count] = event;
block.count++;
}
void swapCurrent() nothrow @nogc
{
uint threads_count = cast(uint)manager.threads.length;
if(current_index == 0)current_index = threads_count;
else current_index = 0;
uint threads_count = cast(uint) manager.threads.length;
if (current_index == 0)
current_index = threads_count;
else
current_index = 0;
foreach(ref event;events)
foreach (ref event; events)
{
foreach(ref first_block; event.first_blocks[current_index .. current_index + threads_count])
foreach (ref first_block; event.first_blocks[current_index
.. current_index + threads_count])
{
EventBlock* block = first_block;
while(block)
while (block)
{
EventBlock* to_dispose = block;
block = block.next;
@ -86,7 +91,7 @@ package struct EventManager
}
first_block = null;
}
foreach(ref block; event.blocks[current_index .. current_index + threads_count])
foreach (ref block; event.blocks[current_index .. current_index + threads_count])
{
block = null;
}
@ -96,12 +101,12 @@ package struct EventManager
void clearEvents() nothrow @nogc
{
//uint threads_count = cast(uint)manager.threads.length;
foreach(ref event;events)
foreach (ref event; events)
{
foreach(ref first_block; event.first_blocks)
foreach (ref first_block; event.first_blocks)
{
EventBlock* block = first_block;
while(block)
while (block)
{
EventBlock* to_dispose = block;
block = block.next;
@ -109,7 +114,7 @@ package struct EventManager
}
first_block = null;
}
foreach(ref block; event.blocks)
foreach (ref block; event.blocks)
{
block = null;
}
@ -120,23 +125,25 @@ package struct EventManager
{
disposeData();
events = Mallocator.makeArray!EventData(manager.events.length);
foreach(i,ref event;events)
foreach (i, ref event; events)
{
event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2);
event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2);
event.data_offset = EventBlock.sizeof;//manager.events[i].
event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2);
event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2);
event.data_offset = EventBlock.sizeof; //manager.events[i].
manager.alignNum(event.data_offset, manager.events[i].alignment);
event.max_events = cast(ushort)((events_block_size - event.data_offset) / manager.events[i].size);
uint size = manager.events[i].size + EntityID.sizeof;
event.max_events = cast(ushort)(
(events_block_size - event.data_offset) / size);
}
}
private void disposeData() nothrow @nogc
{
clearEvents();
if(events)
if (events)
{
foreach(ref event;events)
foreach (ref event; events)
{
Mallocator.dispose(event.blocks);
Mallocator.dispose(event.first_blocks);
@ -168,7 +175,7 @@ package struct EventManager
ushort max_events;
EventBlock*[] blocks;
EventBlock*[] first_blocks;
//EventBlock*[] current_blocks;
}

View file

@ -1,9 +1,9 @@
module ecs.hash_map;
module bubel.ecs.hash_map;
import std.traits;
import ecs.vector;
import ecs.traits;
import bubel.ecs.vector;
import bubel.ecs.traits;
enum doNotInline = "version(DigitalMars)pragma(inline,false);version(LDC)pragma(LDC_never_inline);";
@ -11,36 +11,55 @@ private enum HASH_EMPTY = 0;
private enum HASH_DELETED = 0x1;
private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1;
export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc {
static if (isIntegral!(T)) {
export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc
{
static if (isIntegral!(T))
{
return hashInt(t);
} else {
return 0;//hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts
}
else
{
static if(isArray!T)return hashInt(hash((cast(byte*)t.ptr)[0 .. t.length * ForeachType!(T).sizeof]));
else return hashInt(hash((cast(byte*)&t)[0 .. T.sizeof])); //hashInt(t.hashOf); // hashOf is not giving proper distribution between H1 and H2 hash parts
}
}
ulong hash(byte[] array) nothrow @nogc
{
ulong hash = 0;
foreach(c;array)
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
// Can turn bad hash function to good one
export ulong hashInt(ulong x) nothrow @nogc @safe {
export ulong hashInt(ulong x) nothrow @nogc @safe
{
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
x = x ^ (x >> 31);
return x;
}
struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc)
{
alias Key = KeyPar;
alias Value = ValuePar;
nothrow:
nothrow:
enum rehashFactor = 0.75;
enum size_t getIndexEmptyValue = size_t.max;
static struct KeyVal {
static struct KeyVal
{
Key key;
Value value;
}
static struct Bucket {
static struct Bucket
{
ulong hash;
KeyVal keyValue;
}
@ -49,58 +68,74 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
size_t length; // Used to compute loadFactor
size_t markerdDeleted;
export void clear() {
export void clear()
{
elements.clear();
length = 0;
markerdDeleted = 0;
}
export void reset() {
export void reset()
{
elements.reset();
length = 0;
markerdDeleted = 0;
}
export bool isIn(ref Key el) {
export bool isIn(ref Key el)
{
return getIndex(el) != getIndexEmptyValue;
}
export bool isIn(Key el) {
export bool isIn(Key el)
{
return getIndex(el) != getIndexEmptyValue;
}
export Value* getPtr()(auto ref Key k) {
export Value* getPtr()(auto ref Key k)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
return null;
} else {
}
else
{
return &elements[index].keyValue.value;
}
}
export ref Value get()(auto ref Key k) {
export ref Value get()(auto ref Key k)
{
size_t index = getIndex(k);
assert(index != getIndexEmptyValue);
return elements[index].keyValue.value;
}
deprecated("Use get with second parameter.") export auto ref Value getDefault()(
auto ref Key k, auto ref Value defaultValue) {
auto ref Key k, auto ref Value defaultValue)
{
return get(k, defaultValue);
}
export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) {
export auto ref Value get()(auto ref Key k, auto ref Value defaultValue)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
return defaultValue;
} else {
}
else
{
return elements[index].keyValue.value;
}
}
export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) {
export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
add(k, defaultValue);
}
index = getIndex(k);
@ -109,9 +144,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
export bool tryRemove(Key el) {
export bool tryRemove(Key el)
{
size_t index = getIndex(el);
if (index == getIndexEmptyValue) {
if (index == getIndexEmptyValue)
{
return false;
}
length--;
@ -120,28 +157,34 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return true;
}
export void remove(Key el) {
export void remove(Key el)
{
bool ok = tryRemove(el);
assert(ok);
}
export ref Value opIndex()(auto ref Key key) {
export ref Value opIndex()(auto ref Key key)
{
return get(key);
}
export void opIndexAssign()(auto ref Value value, auto ref Key key) {
export void opIndexAssign()(auto ref Value value, auto ref Key key)
{
add(key, value);
}
export void add()(auto ref Key key, auto ref Value value) {
export void add()(auto ref Key key, auto ref Value value)
{
size_t index = getIndex(key);
if (index != getIndexEmptyValue) {
if (index != getIndexEmptyValue)
{
elements[index].keyValue.value = value;
return;
}
if (getLoadFactor(length + 1) > rehashFactor
|| getLoadFactor(length + markerdDeleted) > rehashFactor) {
|| getLoadFactor(length + markerdDeleted) > rehashFactor)
{
rehash();
}
length++;
@ -150,10 +193,13 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
immutable size_t rotateMask = elements.length - 1;
index = hash & rotateMask; // Starting point
while (true) {
while (true)
{
Bucket* gr = &elements[index];
if ((gr.hash & HASH_FILLED_MARK) == 0) {
if (gr.hash == HASH_DELETED) {
if ((gr.hash & HASH_FILLED_MARK) == 0)
{
if (gr.hash == HASH_DELETED)
{
markerdDeleted--;
}
gr.hash = hash;
@ -171,15 +217,18 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
//int numA;
//int numB;
export size_t getIndex(Key el) {
export size_t getIndex(Key el)
{
return getIndex(el);
}
export size_t getIndex(ref Key el) {
export size_t getIndex(ref Key el)
{
mixin(doNotInline);
immutable size_t groupsLength = elements.length;
if (groupsLength == 0) {
if (groupsLength == 0)
{
return getIndexEmptyValue;
}
@ -188,13 +237,16 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
size_t index = hash & rotateMask; // Starting point
//numA++;
while (true) {
while (true)
{
//numB++;
Bucket* gr = &elements[index];
if (gr.hash == hash && gr.keyValue.key == el) {
if (gr.hash == hash && gr.keyValue.key == el)
{
return index;
}
if (gr.hash == HASH_EMPTY) {
if (gr.hash == HASH_EMPTY)
{
return getIndexEmptyValue;
}
@ -204,21 +256,26 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
export float getLoadFactor(size_t forElementsNum) {
if (elements.length == 0) {
export float getLoadFactor(size_t forElementsNum)
{
if (elements.length == 0)
{
return 1;
}
return cast(float) forElementsNum / (elements.length);
}
export void rehash()() {
export void rehash()()
{
mixin(doNotInline);
// Get all elements
Vector!KeyVal allElements;
allElements.reserve(elements.length);
foreach (ref Bucket el; elements) {
if ((el.hash & HASH_FILLED_MARK) == 0) {
foreach (ref Bucket el; elements)
{
if ((el.hash & HASH_FILLED_MARK) == 0)
{
el.hash = HASH_EMPTY;
continue;
}
@ -227,12 +284,14 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
if (getLoadFactor(length + 1) > rehashFactor) { // Reallocate
if (getLoadFactor(length + 1) > rehashFactor)
{ // Reallocate
elements.length = (elements.length ? elements.length : 4) << 1; // Power of two, initially 8 elements
}
// Insert elements
foreach (i, ref el; allElements) {
foreach (i, ref el; allElements)
{
add(el.key, el.value);
}
length = allElements.length;
@ -240,19 +299,29 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
}
// foreach support
export int opApply(DG)(scope DG dg) {
export int opApply(DG)(scope DG dg)
{
int result;
foreach (ref Bucket gr; elements) {
if ((gr.hash & HASH_FILLED_MARK) == 0) {
foreach (ref Bucket gr; elements)
{
if ((gr.hash & HASH_FILLED_MARK) == 0)
{
continue;
}
static if (isForeachDelegateWithTypes!(DG, Key)) {
static if (isForeachDelegateWithTypes!(DG, Key))
{
result = dg(gr.keyValue.key);
} else static if (isForeachDelegateWithTypes!(DG, Value)) {
}
else static if (isForeachDelegateWithTypes!(DG, Value))
{
result = dg(gr.keyValue.value);
} else static if (isForeachDelegateWithTypes!(DG, Key, Value)) {
}
else static if (isForeachDelegateWithTypes!(DG, Key, Value))
{
result = dg(gr.keyValue.key, gr.keyValue.value);
} else {
}
else
{
static assert(0);
}
if (result)
@ -263,9 +332,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result;
}
export int byKey(scope int delegate(Key k) nothrow dg) {
export int byKey(scope int delegate(Key k) nothrow dg)
{
int result;
foreach (ref Key k; this) {
foreach (ref Key k; this)
{
result = dg(k);
if (result)
break;
@ -273,9 +344,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result;
}
export int byValue(scope int delegate(ref Value k) nothrow dg) {
export int byValue(scope int delegate(ref Value k) nothrow dg)
{
int result;
foreach (ref Value v; this) {
foreach (ref Value v; this)
{
result = dg(v);
if (result)
break;
@ -283,13 +356,15 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) {
return result;
}
export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) {
export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg)
{
int result;
foreach (ref Key k, ref Value v; this) {
foreach (ref Key k, ref Value v; this)
{
result = dg(k, v);
if (result)
break;
}
return result;
}
}
}

View file

@ -1,10 +1,10 @@
module ecs.id_manager;
module bubel.ecs.id_manager;
import ecs.entity;
import ecs.std;
import ecs.vector;
import bubel.ecs.entity;
import bubel.ecs.std;
import bubel.ecs.vector;
import ecs.atomic;
import bubel.ecs.atomic;
import core.stdc.string : memcpy;
/************************************************************************************************************************
@ -17,12 +17,8 @@ struct IDManager
*/
pragma(inline, false) EntityID getNewID() nothrow @nogc
{
//uint current = m_next_id;
//uint next;// = m_ids_array[m_next_id].next_id;
//begin:
//if (current == uint.max)//> m_last_id)
int current = m_stack_top.atomicOp!"-="(1) + 1;
if(current < 0)
if (current < 0)
{
uint add_id = m_last_id.atomicOp!"+="(1) - 1;
@ -33,7 +29,7 @@ struct IDManager
if (block_id >= m_blocks_count)
{
add_mutex.lock();
if(block_id >= m_blocks_count)
if (block_id >= m_blocks_count)
{
m_blocks[m_blocks_count].alloc();
m_blocks_count++;
@ -48,29 +44,11 @@ struct IDManager
return id;
}
//current += 1;
uint index = m_free_stack[current];
EntityID id;
id.id = index;
id.counter = m_ids_array[index].counter;
//current = m_ids_array[index].next_id;
return id;
/*next = m_ids_array[current].next_id;
if(cas(&m_next_id,current,next))
{
EntityID id;
id.id = current;
id.counter = m_ids_array[current].counter;
m_next_id = m_ids_array[current].next_id;
return id;
}
else
{
current = next;
goto begin;
}*/
}
/************************************************************************************************************************
@ -78,13 +56,12 @@ struct IDManager
*/
void releaseID(EntityID id) nothrow @nogc
{
optimize();
Data* data = &m_ids_array[id.id];
if (data.counter != id.counter)
return;
data.counter++;
//data.next_id = m_next_id;
data.entity = null;
///m_next_id = id.id;
m_stack_top.atomicOp!"+="(1);
m_free_stack[m_stack_top] = id.id;
@ -136,9 +113,11 @@ struct IDManager
*/
export bool isExist(EntityID id) nothrow @nogc
{
if(id.id >= m_ids_array.length)return false;
if (id.id >= m_ids_array.length)
return false;
Data* data = &m_ids_array[id.id];
if(data.entity is null)return false;
if (data.entity is null)
return false;
return data.counter == id.counter;
}
@ -150,7 +129,8 @@ struct IDManager
m_ids_array = Mallocator.makeArray!Data(65536);
m_free_stack = Mallocator.makeArray!uint(65536);
m_blocks = Mallocator.makeArray!Block(64);
foreach(ref block;m_blocks)block = Block();
foreach (ref block; m_blocks)
block = Block();
m_blocks_count = 1;
m_blocks[0].alloc();
@ -166,20 +146,23 @@ struct IDManager
*/
void deinitialize() @trusted @nogc nothrow
{
if(m_ids_array)Mallocator.dispose(m_ids_array);
if(m_free_stack)Mallocator.dispose(m_free_stack);
if(m_blocks)
if (m_ids_array)
Mallocator.dispose(m_ids_array);
if (m_free_stack)
Mallocator.dispose(m_free_stack);
if (m_blocks)
{
foreach(ref block;m_blocks)
foreach (ref block; m_blocks)
{
if(block.data)block.free();
if (block.data)
block.free();
}
Mallocator.dispose(m_blocks);
}
if(add_mutex)
if (add_mutex)
{
add_mutex.destroy();
Mallocator.dispose(add_mutex);//cast(void*)add_mutex); //workaround for compiler bug
Mallocator.dispose(add_mutex); //cast(void*)add_mutex); //workaround for compiler bug
add_mutex = null;
}
}
@ -189,27 +172,31 @@ struct IDManager
*/
void optimize() nothrow @nogc
{
if(m_stack_top < -1)m_stack_top = -1;
if(m_last_id > m_ids_array.length)
if (m_stack_top < -1)
m_stack_top = -1;
if (m_last_id > m_ids_array.length)
{
uint begin = cast(uint)m_ids_array.length;
uint begin = cast(uint) m_ids_array.length;
Data[] new_array = Mallocator.makeArray!Data(begin + (m_blocks_count << 16));
memcpy(new_array.ptr, m_ids_array.ptr, m_ids_array.length * Data.sizeof);
Mallocator.dispose(m_ids_array);
m_ids_array = new_array;
uint[] new_stack = Mallocator.makeArray!uint(m_ids_array.length);
memcpy(new_stack.ptr,m_free_stack.ptr,m_free_stack.length * uint.sizeof);
memcpy(new_stack.ptr, m_free_stack.ptr, m_free_stack.length * uint.sizeof);
Mallocator.dispose(m_free_stack);
m_free_stack = new_stack;
foreach(block;m_blocks[0..m_blocks_count-1])
foreach (block; m_blocks[0 .. m_blocks_count - 1])
{
memcpy(cast(void*)m_ids_array.ptr + begin * Data.sizeof, block.data.ptr, 65536 * Data.sizeof);
memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof,
block.data.ptr, 65536 * Data.sizeof);
begin += 65536;
}
memcpy(cast(void*)m_ids_array.ptr + begin * Data.sizeof, m_blocks[m_blocks_count-1].data.ptr, (m_last_id - begin) * Data.sizeof);
foreach(ref block;m_blocks[1..m_blocks_count])block.free();
memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof,
m_blocks[m_blocks_count - 1].data.ptr, (m_last_id - begin) * Data.sizeof);
foreach (ref block; m_blocks[1 .. m_blocks_count])
block.free();
m_blocks_count = 1;
}
}
@ -241,15 +228,12 @@ struct IDManager
private:
Mutex* add_mutex;
//shared uint m_next_id = 0;
//shared uint m_last_id = 0;
Data[] m_ids_array = null;
uint m_blocks_count = 0;
Block[] m_blocks;
//shared int m_stack_top = -1;
uint[] m_free_stack = null;
align(64) shared uint m_last_id = 0;
align(64) shared int m_stack_top = -1;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
module ecs;
public import bubel.ecs.core;
public import bubel.ecs.entity;
public import bubel.ecs.manager;
public import bubel.ecs.system;
import bubel.ecs.events;
import bubel.ecs.id_manager;
import bubel.ecs.std;

View file

@ -0,0 +1,77 @@
module bubel.ecs.simple_vector;
import bubel.ecs.std;
//import core.stdc.string;
/************************************************************************************************************************
Vector for byte data. Simpler than standard template-based implementation designed for better performance. \
Rellocates 1024 elements at once instead of doubling size.
*/
struct SimpleVector
{
@disable this(this);
///Add element to vector
void add(ubyte el) nothrow @nogc
{
while (used >= data.length)
{
if (data is null)
data = Mallocator.makeArray!ubyte(1024);
else
data = Mallocator.expandArray(data, data.length);
}
data[used++] = el;
}
///Add array of elements to vector
void add(ubyte[] el) nothrow @nogc
{
while (used + el.length >= data.length)
{
if (data is null)
data = Mallocator.makeArray!ubyte(1024);
else
data = Mallocator.expandArray(data, data.length);
}
memcpy(data.ptr + used, el.ptr, el.length);
used += el.length;
}
///Return vector length
size_t length() nothrow @nogc
{
return used;
}
export ref ubyte opIndex(size_t pos) nothrow @nogc
{
return data[pos];
}
export ubyte[] opSlice() nothrow @nogc
{
return data[0 .. used];
}
export ubyte[] opSlice(size_t x, size_t y) nothrow @nogc
{
return data[x .. y];
}
export size_t opDollar() nothrow @nogc
{
return used;
}
///set vector length to 0
void clear() nothrow @nogc
{
used = 0;
}
ubyte[] data = null;
size_t used = 0;
}

363
source/bubel/ecs/std.d Normal file
View file

@ -0,0 +1,363 @@
/************************************************************************************************************************
It's internal code!
This module contain implementation of standard functionality.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.std;
version (Emscripten) version = ECSEmscripten;
import std.traits;
version (ECSEmscripten)
{
extern (C) struct pthread_mutex_t
{
union
{
int[6] __i;
void[6]* __p;
}
}
extern (C) struct pthread_mutexattr_t
{
uint __attr;
}
extern (C) int memcmp(const void* s1, const void* s2, size_t size);
extern (C) void exit(int status) nothrow @nogc;
extern (C) void __assert(const(char)* msg, const(char)* file, uint line)
{
exit(-20);
}
extern (C) void free(void*) @nogc nothrow @system;
extern (C) void* malloc(size_t size) @nogc nothrow @system;
extern (C) void* realloc(void*, size_t size) @nogc nothrow @system;
extern (C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system;
extern (C) void* memset(void*, int val, size_t size) @nogc nothrow @system;
extern (C) int posix_memalign(void**, size_t, size_t) @nogc nothrow @system;
extern (C) void qsort(void* base, size_t num, size_t size,
int function(const void*, const void*) compar) @nogc nothrow @system;
extern (C) 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
{
public import core.stdc.stdlib : malloc, free, realloc;
public import core.stdc.string : memcpy, memset;
public import core.stdc.stdlib : qsort;
}
version (ECSEmscripten)
{
}
else version (Windows)
{
import core.sys.windows.windows;
extern (Windows) void* _aligned_malloc(size_t size, size_t alignment) @nogc nothrow @system;
extern (Windows) void _aligned_free(void* ptr) @nogc nothrow @system;
version (LDC)
{
/*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system;
alias alloca = __alloca;*/
extern (Windows) void ___chkstk_ms() @nogc nothrow @system;
extern (Windows) void __chkstk()
{
___chkstk_ms();
}
}
}
else version (Posix)
{
import core.sys.posix.pthread;
import core.sys.posix.stdlib : posix_memalign;
}
version (ECSEmscripten)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
private __gshared uint alloca_pos = 0;
export extern (C) void* alloca(size_t length) @nogc nothrow
{
if (alloca_pos + length > max_alloca)
alloca_pos = 0;
void* ret = &alloca_array[alloca_pos];
alloca_pos += length;
return ret;
}
//extern(C) void* alloca(size_t size) @nogc nothrow;
/*export extern(C) void* alloca(size_t length) @nogc nothrow
{
return null;
}*/
}
else version (D_BetterC)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
private uint alloca_pos = 0;
export extern (C) void* __alloca(size_t length) @nogc nothrow
{
if (alloca_pos + length > max_alloca)
alloca_pos = 0;
void* ret = &alloca_array[alloca_pos];
alloca_pos += length;
return ret;
}
alias alloca = __alloca;
version (DigitalMars)
{
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
{
public import core.stdc.stdlib : alloca;
}
static struct Mallocator
{
static T[] makeArray(T)(size_t length) nothrow @nogc
{
T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length];
static if (__traits(isPOD, T))
{
static immutable T init = T.init;
foreach (i; 0 .. ret.length)
{
memcpy(&ret[i], &init, T.sizeof);
}
}
else
{
static import std.conv;
foreach (i; 0 .. ret.length)
{
std.conv.emplace(&ret[i]);
}
}
return ret;
}
static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc
{
T[] ret = (cast(T*) malloc(T.sizeof * length))[0 .. length];
foreach (ref v; ret)
v = initializer;
return ret;
}
static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc
{
size_t new_length = array.length + length;
return (cast(T*) realloc(array.ptr, T.sizeof * new_length))[0 .. new_length];
}
static T[] makeArray(T)(T[] array) nothrow @nogc
{
T[] ret = (cast(T*) malloc(T.sizeof * array.length))[0 .. array.length]; //Mallocator.makeArray!(T)(array.length);
foreach (i, ref v; ret)
v = array[i];
return ret;
}
static T* make(T, Args...)(Args args)
{
T* ret = cast(T*) malloc(T.sizeof);
static import std.conv;
static if (__traits(isPOD, T))
{
static immutable T init = T.init;
memcpy(ret, &init, T.sizeof);
}
else static if (is(T == struct))
std.conv.emplace(ret, args);
return ret;
}
static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc
{
void* ret;
version (Posix)
posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length);
else version (Windows)
ret = _aligned_malloc(length, alignment);
else version (ECSEmscripten)
posix_memalign(&ret, alignment, length); //malloc(length);
else
static assert(0, "Unimplemented platform!");
return ret;
}
static void dispose(T)(T object) nothrow @nogc
{
static if (__traits(hasMember, T, "__xdtor"))
object.__xdtor();
else static if (__traits(hasMember, T, "__dtor"))
object.__dtor();
free(cast(void*) object);
}
static void alignDispose(T)(T object)
{
static if (__traits(hasMember, T, "__xdtor"))
object.__xdtor();
else static if (__traits(hasMember, T, "__dtor"))
object.__dtor();
version (Posix)
free(cast(void*) object);
else version (Windows)
_aligned_free(cast(void*) object);
else version (ECSEmscripten)
free(cast(void*) object);
else
static assert(0, "Unimplemented platform!");
}
}
struct Mutex
{
version (ECSEmscripten)
{
void initialize() nothrow @nogc
{
pthread_mutexattr_t attr = void;
//pthread_mutexattr_init(&attr);
//pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(cast(pthread_mutex_t*)&m_handle, &attr);
//pthread_mutexattr_destroy(&attr);
}
void destroy() nothrow @nogc
{
pthread_mutex_destroy(&m_handle);
}
void lock() nothrow @nogc
{
pthread_mutex_lock(&m_handle);
}
void unlock() nothrow @nogc
{
pthread_mutex_unlock(&m_handle);
}
int tryLock() nothrow @nogc
{
return pthread_mutex_trylock(&m_handle) == 0;
}
private pthread_mutex_t m_handle;
}
else version (Windows)
{
void initialize() nothrow @nogc
{
InitializeCriticalSection(cast(CRITICAL_SECTION*)&m_handle);
}
void destroy() nothrow @nogc
{
DeleteCriticalSection(&m_handle);
}
void lock() nothrow @nogc
{
EnterCriticalSection(&m_handle);
}
void unlock() nothrow @nogc
{
LeaveCriticalSection(&m_handle);
}
int tryLock() nothrow @nogc
{
return TryEnterCriticalSection(&m_handle) != 0;
}
CRITICAL_SECTION m_handle;
}
else version (Posix)
{
void initialize() nothrow @nogc
{
pthread_mutexattr_t attr = void;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(cast(pthread_mutex_t*)&m_handle, &attr);
pthread_mutexattr_destroy(&attr);
}
void destroy() nothrow @nogc
{
pthread_mutex_destroy(&m_handle);
}
void lock() nothrow @nogc
{
pthread_mutex_lock(&m_handle);
}
void unlock() nothrow @nogc
{
pthread_mutex_unlock(&m_handle);
}
int tryLock() nothrow @nogc
{
return pthread_mutex_trylock(&m_handle) == 0;
}
private pthread_mutex_t m_handle;
}
else
static assert(0, "unsupported platform!");
}

View file

@ -4,26 +4,26 @@ System module.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.system;
module bubel.ecs.system;
import ecs.entity;
import ecs.manager;
import bubel.ecs.entity;
import bubel.ecs.manager;
/************************************************************************************************************************
System contain data required to proper glue EntityManager with Systems.
System callbacks:
$(LIST
* void onUpdate(EntitesData);
* void onEnable()
* void onDisable();
* bool onBegin();
* void onEnd();
* void onCreate()
* void onDestroy();
* void onAddEntity(EntitesData);
* void onRemoveEntity(EntitiesData);
* void onChangeEntity(EntitiesData);
* void handleEvent(Entity*, Event);
* void onEnable() - called inside system.enable() function
* void onDisable() - called inside system.disable() function
* bool onBegin() - called inside manager.begin()
* void onEnd() - called inside manager.end()
* void onCreate() - called after registration inside registerSystem function
* void onDestroy() - called during re-registration and inside manager destructor
* void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
* void onRemoveEntity(EntitiesData) - called for every entity removed from system update process
* void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system
* void handleEvent(Entity*, Event) - called for every event supported by system
)
*/
struct System
@ -66,15 +66,15 @@ struct System
}
/************************************************************************************************************************
Get system priority.
Get if system will be executed during current frame. Should be checked after manager.begin(). Its value is setted as result of manager.onBegin() callback.
*/
export bool execute() nothrow @nogc
export bool willExecute() nothrow @nogc
{
return m_execute;
}
/************************************************************************************************************************
Get system priority.
Get system id.
*/
export ushort id() nothrow @nogc
{
@ -127,7 +127,10 @@ package:
//System*[] m_dependencies;
ushort[] m_read_only_components;
ushort[] m_modified_components;
ushort[] m_writable_components;
ushort[] m_readonly_dependencies;
ushort[] m_writable_dependencies;
EntityManager.SystemCaller* m_any_system_caller;

View file

@ -1,4 +1,4 @@
module ecs.traits;
module bubel.ecs.traits;
import std.traits;

View file

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

View file

@ -1,97 +0,0 @@
/************************************************************************************************************************
It's internal code. Can be used for atomics if emscripten backend will be used.
This module contain atomic operations which include support for emscripten atomics functions.
Emscripten functions are contained in API similar to druntime.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.atomic;
version(Emscripten)version = ECSEmscripten;
version(ECSEmscripten)
{
import std.traits;
enum MemoryOrder
{
acq,
acq_rel,
raw,
rel,
seq
}
extern(C) ubyte emscripten_atomic_cas_u8(void *addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure;
extern(C) ushort emscripten_atomic_cas_u16(void *addr, ushort oldVal, ushort newVal) @nogc nothrow pure;
extern(C) uint emscripten_atomic_cas_u32(void *addr, uint oldVal, uint newVal) @nogc nothrow pure;
extern(C) ubyte emscripten_atomic_load_u8(const void *addr) @nogc nothrow pure;
extern(C) ushort emscripten_atomic_load_u16(const void *addr) @nogc nothrow pure;
extern(C) uint emscripten_atomic_load_u32(const void *addr) @nogc nothrow pure;
extern(C) ubyte emscripten_atomic_store_u8(void *addr, ubyte val) @nogc nothrow pure;
extern(C) ushort emscripten_atomic_store_u16(void *addr, ushort val) @nogc nothrow pure;
extern(C) uint emscripten_atomic_store_u32(void *addr, uint val) @nogc nothrow pure;
extern(C) ubyte emscripten_atomic_add_u8(void *addr, ubyte val) @nogc nothrow pure;
extern(C) ushort emscripten_atomic_add_u16(void *addr, ushort val) @nogc nothrow pure;
extern(C) uint emscripten_atomic_add_u32(void *addr, uint val) @nogc nothrow pure;
extern(C) ubyte emscripten_atomic_sub_u8(void *addr, ubyte val) @nogc nothrow pure;
extern(C) ushort emscripten_atomic_sub_u16(void *addr, ushort val) @nogc nothrow pure;
extern(C) uint emscripten_atomic_sub_u32(void *addr, uint val) @nogc nothrow pure;
public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod)
{
static if(op == "+=")
{
static if(is(T == byte) || is(T == ubyte))return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, cast(Unqual!T)mod) + 1);
else static if(is(T == short) || is(T == ushort))return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, cast(Unqual!T)mod) + 1);
else static if(is(T == int) || is(T == uint))return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, cast(Unqual!T)mod) + 1);
else static assert(0);
}
else static if(op == "-=")
{
static if(is(T == byte) || is(T == ubyte))return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, cast(Unqual!T)mod) - 1);
else static if(is(T == short) || is(T == ushort))return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, cast(Unqual!T)mod) - 1);
else static if(is(T == int) || is(T == uint))return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, cast(Unqual!T)mod) - 1);
else static assert(0);
}
}
public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval)
{
alias UT = Unqual!T;
static if(is(UT == bool) || is(UT == byte) || is(UT == ubyte))emscripten_atomic_store_u8(cast(void*)&val, cast(UT)newval);
else static if(is(UT == short) || is(UT == ushort))emscripten_atomic_store_u16(cast(void*)&val, cast(UT)newval);
else static if(is(UT == int) || is(UT == uint))emscripten_atomic_store_u32(cast(void*)&val, cast(UT)newval);
else static assert(0);
}
public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref const T val)
{
alias UT = Unqual!T;
static if(is(UT == bool))return emscripten_atomic_load_u8(cast(const void*)&val) != 0;
else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_load_u8(cast(const void*)&val);
else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_load_u16(cast(const void*)&val);
else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_load_u32(cast(const void*)&val);
else static assert(0);
}
public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis)
{
alias UT = Unqual!T;
static if(is(UT == bool))return emscripten_atomic_cas_u8(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis;
else static if(is(UT == byte) || is(UT == ubyte))return emscripten_atomic_cas_u8(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis;
else static if(is(UT == short) || is(UT == ushort))return emscripten_atomic_cas_u16(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis;
else static if(is(UT == int) || is(UT == uint))return emscripten_atomic_cas_u32(cast(void*)here, cast(Unqual!T)ifThis, cast(Unqual!T)writeThis) == ifThis;
else static assert(0);
}
}
else
{
public import core.atomic;
}

View file

@ -1,10 +0,0 @@
module ecs;
public import ecs.core;
public import ecs.entity;
public import ecs.manager;
public import ecs.system;
import ecs.events;
import ecs.id_manager;
import ecs.std;

View file

@ -1,65 +0,0 @@
module ecs.simple_vector;
import ecs.std;
//import core.stdc.string;
struct SimpleVector
{
@disable this(this);
void add(ubyte el) nothrow @nogc
{
while(used >= data.length)
{
if(data is null)data = Mallocator.makeArray!ubyte(1024);
else data = Mallocator.expandArray(data,data.length);
}
data[used++] = el;
}
void add(ubyte[] el) nothrow @nogc
{
while(used + el.length >= data.length)
{
if(data is null)data = Mallocator.makeArray!ubyte(1024);
else data = Mallocator.expandArray(data,data.length);
}
memcpy(data.ptr + used, el.ptr, el.length);
used += el.length;
}
size_t length() nothrow @nogc
{
return used;
}
export ref ubyte opIndex(size_t pos) nothrow @nogc
{
return data[pos];
}
export ubyte[] opSlice() nothrow @nogc
{
return data[0 .. used];
}
export ubyte[] opSlice(size_t x, size_t y) nothrow @nogc
{
return data[x .. y];
}
export size_t opDollar() nothrow @nogc
{
return used;
}
void clear() nothrow @nogc
{
used = 0;
}
ubyte[] data = null;
size_t used = 0;
}

View file

@ -1,314 +0,0 @@
/************************************************************************************************************************
It's internal code!
This module contain implementation of standard functionality.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs.std;
version(Emscripten)version = ECSEmscripten;
import std.traits;
version(ECSEmscripten)
{
extern(C) struct pthread_mutex_t
{
union
{
int[6] __i;
void[6] *__p;
}
}
extern(C) struct pthread_mutexattr_t
{
uint __attr;
}
extern(C) int memcmp (const void *s1, const void *s2, size_t size);
extern(C) void exit (int status) nothrow @nogc;
extern(C) void __assert(const(char)* msg, const(char)* file, uint line) { exit(-20);}
extern(C) void free(void*) @nogc nothrow @system;
extern(C) void* malloc(size_t size) @nogc nothrow @system;
extern(C) void* realloc(void*, size_t size) @nogc nothrow @system;
extern(C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system;
extern(C) void* memset(void*, int val, size_t size) @nogc nothrow @system;
extern(C) int posix_memalign(void**, size_t, size_t) @nogc nothrow @system;
extern(C) void qsort(void* base, size_t num, size_t size, int function(const void*,const void*) compar) @nogc nothrow @system;
extern(C) 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
{
public import core.stdc.stdlib : malloc, free, realloc;
public import core.stdc.string : memcpy, memset;
public import core.stdc.stdlib : qsort;
}
version(ECSEmscripten)
{
}
else version (Windows)
{
import core.sys.windows.windows;
extern(Windows) void* _aligned_malloc(size_t size,size_t alignment) @nogc nothrow @system;
extern(Windows) void _aligned_free(void* ptr) @nogc nothrow @system;
version(LDC)
{
/*extern(Windows) void* __alloca(size_t size) @nogc nothrow @system;
alias alloca = __alloca;*/
extern(Windows) void ___chkstk_ms() @nogc nothrow @system;
extern(Windows) void __chkstk()
{
___chkstk_ms();
}
}
}
else version (Posix)
{
import core.sys.posix.pthread;
import core.sys.posix.stdlib;
}
version(ECSEmscripten)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
private __gshared uint alloca_pos = 0;
export extern(C) void* alloca(size_t length) @nogc nothrow
{
if(alloca_pos + length > max_alloca)alloca_pos = 0;
void* ret = &alloca_array[alloca_pos];
alloca_pos += length;
return ret;
}
//extern(C) void* alloca(size_t size) @nogc nothrow;
/*export extern(C) void* alloca(size_t length) @nogc nothrow
{
return null;
}*/
}
else version(D_BetterC)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
private uint alloca_pos = 0;
export extern(C) void* alloca(size_t length) @nogc nothrow
{
if(alloca_pos + length > max_alloca)alloca_pos = 0;
void* ret = &alloca_array[alloca_pos];
alloca_pos += length;
return ret;
}
}
else
{
public import core.stdc.stdlib : alloca;
}
static struct Mallocator
{
static T[] makeArray(T)(size_t length) nothrow @nogc
{
T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length];
static if(__traits(isPOD, T))
{
static immutable T init = T.init;
foreach(i;0..ret.length)
{
memcpy(&ret[i], &init, T.sizeof);
}
}
else
{
static import std.conv;
foreach(i;0..ret.length)
{
std.conv.emplace(&ret[i]);
}
}
return ret;
}
static T[] makeArray(T)(size_t length, T initializer) nothrow @nogc
{
T[] ret = (cast(T*)malloc(T.sizeof * length))[0 .. length];
foreach(ref v; ret)v = initializer;
return ret;
}
static T[] expandArray(T)(T[] array, size_t length) nothrow @nogc
{
size_t new_length = array.length + length;
return (cast(T*)realloc(array.ptr, T.sizeof * new_length))[0 .. new_length];
}
static T[] makeArray(T)(T[] array) nothrow @nogc
{
T[] ret = (cast(T*)malloc(T.sizeof * array.length))[0 .. array.length];//Mallocator.makeArray!(T)(array.length);
foreach(i, ref v;ret)v = array[i];
return ret;
}
static T* make(T, Args...)(Args args)
{
T* ret = cast(T*)malloc(T.sizeof);
static import std.conv;
static if(__traits(isPOD, T))
{
static immutable T init = T.init;
memcpy(ret, &init, T.sizeof);
}
else static if(is(T == struct))std.conv.emplace(ret, args);
return ret;
}
static void* alignAlloc(size_t length, size_t alignment) nothrow @nogc
{
void* ret;
version(Posix)posix_memalign(&ret, alignment, length);//ret = aligned_alloc(alignment, length);
else version(Windows)ret = _aligned_malloc(length, alignment);
else version(ECSEmscripten)posix_memalign(&ret, alignment, length);//malloc(length);
else static assert(0, "Unimplemented platform!");
return ret;
}
static void dispose(T)(T object) nothrow @nogc
{
static if(__traits(hasMember, T, "__xdtor"))object.__xdtor();
else static if(__traits(hasMember, T, "__dtor"))object.__dtor();
free(cast(void*)object);
}
static void alignDispose(T)(T object)
{
static if(__traits(hasMember, T, "__xdtor"))object.__xdtor();
else static if(__traits(hasMember, T, "__dtor"))object.__dtor();
version(Posix)free(cast(void*)object);
else version(Windows)_aligned_free(cast(void*)object);
else version(ECSEmscripten)free(cast(void*)object);
else static assert(0, "Unimplemented platform!");
}
}
struct Mutex
{
version(ECSEmscripten)
{
void initialize() nothrow @nogc
{
pthread_mutexattr_t attr = void;
//pthread_mutexattr_init(&attr);
//pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(cast(pthread_mutex_t*) &m_handle, &attr);
//pthread_mutexattr_destroy(&attr);
}
void destroy() nothrow @nogc
{
pthread_mutex_destroy(&m_handle);
}
void lock() nothrow @nogc
{
pthread_mutex_lock(&m_handle);
}
void unlock() nothrow @nogc
{
pthread_mutex_unlock(&m_handle);
}
int tryLock() nothrow @nogc
{
return pthread_mutex_trylock(&m_handle) == 0;
}
private pthread_mutex_t m_handle;
}
else version (Windows)
{
void initialize() nothrow @nogc
{
InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_handle);
}
void destroy() nothrow @nogc
{
DeleteCriticalSection(&m_handle);
}
void lock() nothrow @nogc
{
EnterCriticalSection(&m_handle);
}
void unlock() nothrow @nogc
{
LeaveCriticalSection(&m_handle);
}
int tryLock() nothrow @nogc
{
return TryEnterCriticalSection(&m_handle) != 0;
}
CRITICAL_SECTION m_handle;
}
else version (Posix)
{
void initialize() nothrow @nogc
{
pthread_mutexattr_t attr = void;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(cast(pthread_mutex_t*) &m_handle, &attr);
pthread_mutexattr_destroy(&attr);
}
void destroy() nothrow @nogc
{
pthread_mutex_destroy(&m_handle);
}
void lock() nothrow @nogc
{
pthread_mutex_lock(&m_handle);
}
void unlock() nothrow @nogc
{
pthread_mutex_unlock(&m_handle);
}
int tryLock() nothrow @nogc
{
return pthread_mutex_trylock(&m_handle) == 0;
}
private pthread_mutex_t m_handle;
}
else static assert(0, "unsupported platform!");
}