-updated tests:

*updated build scripts
 *removed tls variables from code (needed to support WebAssembly)
 *some mmutils tweaks
 *some fixes
 *pthread TLS thread ID implementation
-added Atomic file (reimplementation of atomics templates for emscripten)
-added emscripten support to ecs.std
This commit is contained in:
Mergul 2019-11-25 20:06:16 +00:00
parent 46de0f6adb
commit 946fbf2934
18 changed files with 443 additions and 229 deletions

88
source/ecs/atomic.d Normal file
View file

@ -0,0 +1,88 @@
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;
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);
}
}
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);
}
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);
}
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

@ -4,7 +4,7 @@ import ecs.entity;
import ecs.std;
import ecs.vector;
import core.atomic;
import ecs.atomic;
import core.stdc.string : memcpy;
/************************************************************************************************************************

View file

@ -7,7 +7,7 @@ import std.algorithm : max;
import std.conv : to;
import std.traits;
import core.atomic;
//import core.atomic;
//import core.stdc.stdlib : qsort;
//import core.stdc.string;
@ -21,6 +21,7 @@ import ecs.simple_vector;
import ecs.std;
import ecs.traits;
import ecs.vector;
import ecs.atomic;
export alias gEM = EntityManager.instance;
export alias gEntityManager = EntityManager.instance;
@ -1119,10 +1120,16 @@ export struct EntityManager
}
}
export void setJobDispachFunc(void delegate(JobGroup) func) nothrow @nogc
export void setMultithreadingCallbacks(void delegate(JobGroup) dispatch_callback, uint delegate() get_id_callback)
{
m_dispatch_jobs = cast(void delegate(JobGroup jobs) nothrow @nogc)dispatch_callback;
m_thread_id_func = cast(uint delegate() nothrow @nogc)get_id_callback;
}
/*export void setJobDispachFunc(void delegate(JobGroup) @nogc nothrow func) nothrow @nogc
{
m_dispatch_jobs = func;
}
}*/
static void alignNum(ref ushort num, ushort alignment) nothrow @nogc pure
{
@ -1502,7 +1509,7 @@ export struct EntityManager
*/
export void removeComponents(EntityID entity_id, ushort[] del_ids) nothrow @nogc
{
ThreadData* data = &threads[thread_id];
ThreadData* data = &threads[threadID];
uint num = cast(uint) del_ids.length;
data.change_entities_list.add(0);
data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]);
@ -1753,7 +1760,7 @@ export struct EntityManager
new_ids[i] = comp.component_id;
}
ThreadData* data = &threads[thread_id];
ThreadData* data = &threads[threadID];
data.change_entities_list.add(cast(ubyte)1u);
data.change_entities_list.add((cast(ubyte*)&entity_id)[0 .. EntityID.sizeof]);
data.change_entities_list.add((cast(ubyte*)&num)[0 .. uint.sizeof]);
@ -1820,7 +1827,7 @@ export struct EntityManager
}
if (new_index == 1)
threads[thread_id].blocks_to_update.add(new_block);
threads[threadID].blocks_to_update.add(new_block);
Entity* new_entity = cast(Entity*) start;
//add_mutex.lock_nothrow();
@ -1870,7 +1877,7 @@ export struct EntityManager
}
if (index == 1)
threads[thread_id].blocks_to_update.add(block);
threads[threadID].blocks_to_update.add(block);
Entity* entity = cast(Entity*) start;
//add_mutex.lock_nothrow();
@ -1961,7 +1968,7 @@ export struct EntityManager
*/
export void removeEntity(EntityID id)
{
threads[thread_id].entities_to_remove.add(id);
threads[threadID].entities_to_remove.add(id);
}
private void __removeEntity(EntityID id) nothrow @nogc
@ -2301,17 +2308,17 @@ export struct EntityManager
commit();
}
private void getThreadID() nothrow @nogc
/*private void getThreadID() nothrow @nogc
{
if (m_thread_id_func)
thread_id = (cast(uint delegate() nothrow @nogc) m_thread_id_func)();
else
thread_id = 0;
}
}*/
void sendEvent(Ev)(EntityID id, Ev event) nothrow @nogc
{
event_manager.sendEvent(id, event, thread_id);
event_manager.sendEvent(id, event, threadID);
}
private void generateDependencies() nothrow @nogc
@ -2722,7 +2729,7 @@ export struct EntityManager
export void execute() nothrow @nogc
{
EntityManager.instance.getThreadID();
//EntityManager.instance.getThreadID();
foreach (ref caller; callers)
{
caller.update();
@ -2789,7 +2796,27 @@ export struct EntityManager
Vector!(SystemCaller*) system_callers;
}
static uint thread_id;
export uint threadID() @nogc nothrow
{
if (m_thread_id_func)
return m_thread_id_func();
else
return 0;
}
/*uint thread_id() @nogc nothrow
{
if (m_thread_id_func)
return (cast(uint delegate() nothrow @nogc) m_thread_id_func)();
else
return 0;
}
void thread_id(uint) @nogc nothrow
{
}*/
//static uint thread_id;
ThreadData[] threads;
@ -2809,8 +2836,8 @@ export struct EntityManager
EventManager event_manager;
void delegate(JobGroup jobs) m_dispatch_jobs;
uint delegate() m_thread_id_func;
void delegate(JobGroup jobs) nothrow @nogc m_dispatch_jobs;
uint delegate() nothrow @nogc m_thread_id_func;
HashMap!(ushort[], EntityInfo*) entities_infos;
HashMap!(char[], ushort) systems_map;

View file

@ -1,12 +1,25 @@
module ecs.std;
//import core.stdc.stdlib : malloc, free, realloc;
//import core.stdc.string : memcpy;
version(Emscripten)version = ECSEmscripten;
import std.traits;
version(WebAssembly)
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);}
@ -17,6 +30,16 @@ version(WebAssembly)
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
{
@ -25,8 +48,10 @@ else
public import core.stdc.stdlib : qsort;
}
version (Windows)
version(ECSEmscripten)
{
}
else version (Windows)
{
import core.sys.windows.windows;
extern(Windows) void* _aligned_malloc(size_t size,size_t alignment) @nogc nothrow @system;
@ -51,10 +76,28 @@ else version (Posix)
import core.sys.posix.stdlib;
}
version(D_betterC)
version(ECSEmscripten)
{
private const uint max_alloca = 10000;
private char[max_alloca] alloca_array;
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
{
@ -64,20 +107,10 @@ version(D_betterC)
return ret;
}
}
else version(WebAssembly)
else
{
private const uint max_alloca = 10000;
private char[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;
}
public import core.stdc.stdlib : alloca;
}
else public import core.stdc.stdlib : alloca;
static struct Mallocator
{
@ -143,7 +176,7 @@ static struct Mallocator
void* ret;
version(Posix)posix_memalign(&ret, alignment, length);//ret = aligned_alloc(alignment, length);
else version(Windows)ret = _aligned_malloc(length, alignment);
else version(WebAssembly)posix_memalign(&ret, alignment, length);//malloc(length);
else version(ECSEmscripten)posix_memalign(&ret, alignment, length);//malloc(length);
else static assert(0, "Unimplemented platform!");
return ret;
}
@ -159,7 +192,7 @@ static struct Mallocator
static if(__traits(hasMember, T, "__dtor"))object.__dtor();
version(Posix)free(cast(void*)object);
else version(Windows)_aligned_free(cast(void*)object);
else version(WebAssembly)free(cast(void*)object);
else version(ECSEmscripten)free(cast(void*)object);
else static assert(0, "Unimplemented platform!");
}
}
@ -167,7 +200,43 @@ static struct Mallocator
struct Mutex
{
version (Windows)
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
{
@ -232,41 +301,5 @@ struct Mutex
private pthread_mutex_t m_handle;
}
else version(WebAssembly)
{
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 0;//return pthread_mutex_trylock(&m_handle) == 0;
}
private int m_handle;
}
else static assert(0, "unsupported platform!");
}