From 946fbf2934bfe08023b20317873eaf99cfaa4880 Mon Sep 17 00:00:00 2001 From: Mergul Date: Mon, 25 Nov 2019 20:06:16 +0000 Subject: [PATCH] -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 --- compile_wasm.py | 11 +- demos/compile_wasm.py | 18 ++- demos/external/sources/glad/gl/funcs.d | 28 ++-- demos/external/sources/glad/gl/loader.d | 2 +- demos/external/sources/mmutils/thread_pool.d | 114 +++++++++----- demos/source/app.d | 23 +-- demos/source/demos/simple.d | 5 +- demos/source/demos/snake.d | 4 +- demos/source/demos/space_invaders.d | 2 - demos/source/game_core/job_updater.d | 88 ++++++++--- demos/utils/source/ecs_utils/gfx/texture.d | 2 +- demos/utils/source/ecs_utils/imgui_bind.d | 14 +- demos/utils/source/ecs_utils/utils.d | 9 ++ source/ecs/atomic.d | 88 +++++++++++ source/ecs/id_manager.d | 2 +- source/ecs/manager.d | 57 +++++-- source/ecs/std.d | 149 +++++++++++-------- tests/tests.d | 56 ++----- 18 files changed, 443 insertions(+), 229 deletions(-) create mode 100644 source/ecs/atomic.d diff --git a/compile_wasm.py b/compile_wasm.py index e808354..3bd1afd 100644 --- a/compile_wasm.py +++ b/compile_wasm.py @@ -24,16 +24,16 @@ def compile(sources, output): for f in files: ldc_cmd += f + ' ' - print ldc_cmd + print(ldc_cmd) if os.system(ldc_cmd): exit(0) - print + print() shared_flags = '' clean = 0 emc_flags = '' -ldc_flags = '' +ldc_flags = '--d-version=ECSEmscripten ' import_paths = ['source','tests'] build_tests = 0 @@ -69,6 +69,8 @@ for arg in sys.argv[1:]: shared_flags += '-O3 ' ldc_flags += '-release -enable-inlining ' emc_flags += '--llvm-lto 3 -s SIMD=1 ' + elif(arg == '-pthread'): + emc_flags += '-s PTHREAD_POOL_SIZE=16 -s USE_PTHREADS=1 ' else: print('unknown argument: ' + arg) exit() @@ -84,7 +86,8 @@ emcc_cmd = 'emcc -v ' + shared_flags + emc_flags + '-s ALLOW_MEMORY_GROWTH=1 -s #-s ALLOW_MEMORY_GROWTH=1 emcc_cmd += 'ecs.bc tests.bc' +#emcc_cmd += 'tests.bc' -print emcc_cmd +print(emcc_cmd) os.system(emcc_cmd) diff --git a/demos/compile_wasm.py b/demos/compile_wasm.py index 9e3f46a..c985693 100644 --- a/demos/compile_wasm.py +++ b/demos/compile_wasm.py @@ -13,7 +13,7 @@ def compile(sources, output): if file_extension == '.d' and filename != 'package': files.append(os.path.join(r, file)) - ldc_cmd = 'ldc2 ' + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' ' + ldc_cmd = compiler + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' ' for path in sources: ldc_cmd += '-I' + path + ' ' @@ -31,12 +31,14 @@ def compile(sources, output): print() +compiler = 'ldc2 ' +#compiler = 'ldmd2 -vtls ' shared_flags = '' clean = 0 demo = 0 sources = ['tests', 'source'] emc_flags = '-s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="[\'png\']" --preload-file assets ' -ldc_flags = '--d-version=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS ' +ldc_flags = '--d-version=ECSEmscripten --d-version=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS ' import_paths = ['external/sources', 'external/imports', 'external/wasm_imports', '../source', 'utils/source', 'simple/source'] for arg in sys.argv[1:]: @@ -60,7 +62,7 @@ for arg in sys.argv[1:]: shared_flags += '-g ' elif(arg == '-g4'): ldc_flags += '-g ' - emc_flags += '-g4 ' + emc_flags += '-g4 --source-map-base ./ ' elif(arg == '--llvm-lto'): emc_flags += '--llvm-lto 3 ' elif(arg == '--simd'): @@ -69,12 +71,12 @@ for arg in sys.argv[1:]: shared_flags += '-O3 ' ldc_flags += '-release -enable-inlining ' emc_flags += '--llvm-lto 3 -s SIMD=1 ' + elif(arg == '-quiet'): + emc_flags += "-Wl,--no-check-features " elif(arg == '--clean'): clean = 1 elif(arg == '-pthread'): - shared_flags += '-O3 ' - ldc_flags += '-release -enable-inlining ' - emc_flags += '-s PTHREAD_POOL_SIZE=16 -s USE_PTHREADS=1 ' + emc_flags += '-s USE_PTHREADS=1 ' elif(arg == '--demo=simple'): demo = 0 else: @@ -90,8 +92,8 @@ compile(['source'], 'demo.bc') if clean or os.path.exists('../ecs.bc') == 0 or os.path.isfile('../ecs.bc') == 0: compile(['../source'], '../ecs.bc') -emcc_cmd = 'emcc -v ' + shared_flags + emc_flags + '-s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 -s TOTAL_MEMORY=256MB -s WASM_MEM_MAX=1024MB -s MALLOC=dlmalloc -s WASM=1 -o index.html ' -#-s ALLOW_MEMORY_GROWTH=1 -s PROXY_TO_PTHREAD=1 +emcc_cmd = 'emcc -v ' + shared_flags + emc_flags + '-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 -s ALLOW_MEMORY_GROWTH=1 -s WASM_MEM_MAX=2048MB -s MALLOC=dlmalloc -s WASM=1 -o index.html ' +#-s ALLOW_MEMORY_GROWTH=1 -s PROXY_TO_PTHREAD=1 -Wl,--no-check-features -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s TOTAL_MEMORY=512MB emcc_cmd += '../ecs.bc ' emcc_cmd += 'utils.bc ' diff --git a/demos/external/sources/glad/gl/funcs.d b/demos/external/sources/glad/gl/funcs.d index 928cd83..bfad207 100644 --- a/demos/external/sources/glad/gl/funcs.d +++ b/demos/external/sources/glad/gl/funcs.d @@ -2,20 +2,20 @@ module glad.gl.funcs; private import glad.gl.types; -bool GL_VERSION_1_0; -bool GL_VERSION_1_1; -bool GL_VERSION_1_2; -bool GL_VERSION_1_3; -bool GL_VERSION_1_4; -bool GL_VERSION_1_5; -bool GL_VERSION_2_0; -bool GL_VERSION_2_1; -bool GL_VERSION_3_0; -bool GL_VERSION_3_1; -bool GL_VERSION_3_2; -bool GL_VERSION_3_3; -bool GL_ES_VERSION_2_0; -bool GL_ES_VERSION_3_0; +__gshared bool GL_VERSION_1_0; +__gshared bool GL_VERSION_1_1; +__gshared bool GL_VERSION_1_2; +__gshared bool GL_VERSION_1_3; +__gshared bool GL_VERSION_1_4; +__gshared bool GL_VERSION_1_5; +__gshared bool GL_VERSION_2_0; +__gshared bool GL_VERSION_2_1; +__gshared bool GL_VERSION_3_0; +__gshared bool GL_VERSION_3_1; +__gshared bool GL_VERSION_3_2; +__gshared bool GL_VERSION_3_3; +__gshared bool GL_ES_VERSION_2_0; +__gshared bool GL_ES_VERSION_3_0; nothrow @nogc extern(System) { alias fp_glCullFace = void function(GLenum); alias fp_glFrontFace = void function(GLenum); diff --git a/demos/external/sources/glad/gl/loader.d b/demos/external/sources/glad/gl/loader.d index 35eca05..d640c61 100644 --- a/demos/external/sources/glad/gl/loader.d +++ b/demos/external/sources/glad/gl/loader.d @@ -109,7 +109,7 @@ bool gladLoadGL() { return status; } -static struct GLVersion { static int major = 0; static int minor = 0; } +__gshared struct GLVersion { __gshared int major = 0; __gshared int minor = 0; } private extern(C) char* strstr(const(char)*, const(char)*) @nogc; private extern(C) int strcmp(const(char)*, const(char)*) @nogc; private extern(C) int strncmp(const(char)*, const(char)*, size_t) @nogc; diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index eb4d53f..a5fbf91 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -1,12 +1,11 @@ module mmutils.thread_pool; -import core.atomic; +import ecs.atomic; + //import core.stdc.stdio; //import core.stdc.stdlib : free, malloc, realloc; //import core.stdc.string : memcpy; - - //import std.stdio; import std.algorithm : map; @@ -108,6 +107,9 @@ version (WebAssembly) } extern(C) int clock_gettime(clockid_t, timespec*) @nogc nothrow @system; + + extern(C) double emscripten_get_now() @nogc nothrow @system; + } /// High precison timer @@ -122,13 +124,14 @@ long useconds() return t.tv_sec * 1_000_000 + t.tv_usec;*/ - time_t time; - timespec spec; + //time_t time; + //timespec spec; - clock_gettime(CLOCK_REALTIME, &spec); + //lock_gettime(CLOCK_REALTIME, &spec); + return cast(long)(emscripten_get_now() * 1000.0); //time = spec.tv_sec; - return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; + //return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; } else version (Posix) { @@ -190,6 +193,22 @@ void instructionPause() static assert(0); } } + else version(WebAssembly) + { + version(LDC) + { + import ldc.attributes; + @optStrategy("none") + static void nop() + { + int i; + i++; +} + nop(); + } + else static assert(0); + } + else static assert(0); } ////////////////////////////////////////////// @@ -198,18 +217,17 @@ void instructionPause() version (MM_USE_POSIX_THREADS) { - version (Posix) - { - import core.sys.posix.pthread; - import core.sys.posix.semaphore; - } - else version (WebAssembly) + version (WebAssembly) { extern(C): //alias uint time_t; struct pthread_attr_t { - + union + { + int[10] __i; + uint[10] __s; + } } struct pthread_t @@ -230,7 +248,11 @@ version (MM_USE_POSIX_THREADS) void pthread_exit(void *retval); // semaphore.h - alias sem_t = void*; + //alias sem_t = void*; + struct sem_t + { + shared int[4] __val; + } int sem_init(sem_t*, int, uint); int sem_wait(sem_t*); int sem_trywait(sem_t*); @@ -240,6 +262,11 @@ version (MM_USE_POSIX_THREADS) //import core.sys.posix.pthread; //import core.sys.posix.semaphore; } + else version (Posix) + { + import core.sys.posix.pthread; + import core.sys.posix.semaphore; + } else version (Windows) { extern (C): @@ -317,7 +344,7 @@ version (MM_USE_POSIX_THREADS) void post() { int ret = sem_post(&mutex); - assert(ret == 0); + assert(ret >= 0); } void destroy() @@ -362,6 +389,9 @@ else version(D_BetterC) { version(Posix) { + import core.sys.posix.pthread; + import core.sys.posix.semaphore; + struct Semaphore { sem_t mutex; @@ -619,7 +649,7 @@ else ///////////////// ThreadPool ///////////////// ////////////////////////////////////////////// -private enum gMaxThreadsNum = 128; +private enum gMaxThreadsNum = 32; alias JobDelegate = void delegate(ThreadData*, JobData*); @@ -634,7 +664,7 @@ struct JobLog /// First in first out queue with atomic lock struct JobQueue { - alias LockType = long; + alias LockType = int; align(64) shared LockType lock; /// Lock for accesing list of Jobs align(64) JobData* first; /// Fist element in list of Jobs @@ -849,7 +879,8 @@ public: threadData.threadPool = &this; threadData.semaphore.initialize(); threadData.externalThread = true; - threadData.acceptJobs = true; + atomicStore(threadData.acceptJobs, true); + //threadData.acceptJobs = true; int threadNum = atomicOp!"+="(threadsNum, 1) - 1; @@ -866,18 +897,19 @@ public: void unregistExternalThread(ThreadData* threadData) { lockThreadsData(); - scope (exit) - unlockThreadsData(); + //scope (exit) + // unlockThreadsData(); disposeThreadData(threadData); + unlockThreadsData(); } /// Allows external threads to return from threadStartFunc void releaseExternalThreads() { lockThreadsData(); - scope (exit) - unlockThreadsData(); + //scope (exit) + // unlockThreadsData(); // Release external threads (including main thread) foreach (i, ref ThreadData* th; threadsData) @@ -891,14 +923,15 @@ public: addJobsRange(rng, cast(int) i); atomicStore(th.end, true); } + unlockThreadsData(); } /// Waits for all threads to finish and joins them (excluding external threads) void waitThreads() { lockThreadsData(); - scope (exit) - unlockThreadsData(); + //scope (exit) + // unlockThreadsData(); foreach (i, ref ThreadData* th; threadsData) { if (th is null) @@ -915,6 +948,7 @@ public: th.thread.join(); disposeThreadData(th); } + unlockThreadsData(); } /// Sets number of threads to accept new jobs @@ -927,8 +961,8 @@ public: assert(num > 0); lockThreadsData(); - scope (exit) - unlockThreadsData(); + //scope (exit) + // unlockThreadsData(); foreach (i, ref ThreadData* th; threadsData) { @@ -947,14 +981,15 @@ public: th = makeThreadData(); th.threadPool = &this; th.threadId = cast(int) i; - th.acceptJobs = true; + atomicStore(th.acceptJobs, true); + //th.acceptJobs = true; th.semaphore.initialize(); th.thread.start(&th.threadStartFunc); } atomicStore(threadsNum, num); - + unlockThreadsData(); } /// Adds job to be executed by thread pool, such a job won't be synchronized with any group or job @@ -1012,8 +1047,9 @@ public: { assert(rng[0].group == threadData.group); } + atomicOp!"+="(rng[0].group.jobsToBeDoneCount, cast(int) rng.length); - int threadsNumLocal = threadsNum; + int threadsNumLocal = atomicLoad(threadsNum); int part = cast(int) rng.length / threadsNumLocal; if (part > 0) { @@ -1040,13 +1076,13 @@ public: void addGroupAsynchronous(JobsGroup* group) { group.thPool = &this; + if (group.jobs.length == 0) { // Immediately call group end group.onGroupFinish(); return; } - group.setUpJobs(); auto rng = group.jobs[].map!((ref a) => &a); addJobsRange(rng, group.executeOnThreadNum); @@ -1066,8 +1102,8 @@ public: void flushAllLogs() { lockThreadsData(); - scope (exit) - unlockThreadsData(); + //scope (exit) + // unlockThreadsData(); foreach (thNum; 0 .. atomicLoad(threadsNum)) { ThreadData* th = threadsData[thNum]; @@ -1081,6 +1117,7 @@ public: onThreadFlushLogs(th); } + unlockThreadsData(); } /// Default implementation of flushing logs @@ -1161,7 +1198,7 @@ private: foreach (i; 0 .. 1_000) { - if (threadNum >= threadsNum) + if (threadNum >= atomicLoad(threadsNum)) { threadNum = 0; atomicStore(threadSelector, 0); @@ -1287,7 +1324,10 @@ public: this.name = name; this.jobs = jobs; this.executeOnThreadNum = executeOnThreadNum; - jobsToBeDoneCount = 0; + //jobsToBeDoneCount = 0; + //dependenciesWaitCount = 0; + atomicStore(jobsToBeDoneCount,0); + atomicStore(dependenciesWaitCount,0); } ~this() nothrow @@ -1330,6 +1370,7 @@ private: if (spawnedByGroup) { auto num = atomicOp!"-="(spawnedByGroup.jobsToBeDoneCount, 1); + assert(num >= 0); if (num == 0) { spawnedByGroup.onGroupFinish(); @@ -1438,7 +1479,8 @@ private void threadFunc(ThreadData* threadData) } } - threadData.end = false; + //threadData.end = false; + atomicStore(threadData.end, false); assert(threadData.jobsQueue.empty()); } diff --git a/demos/source/app.d b/demos/source/app.d index 10592ac..137ac1a 100644 --- a/demos/source/app.d +++ b/demos/source/app.d @@ -160,14 +160,14 @@ struct CleanSystem void mainLoop(void* arg) { - static double time = 0; + __gshared double time = 0; launcher.delta_time = launcher.getTime() - time; time = launcher.getTime(); if(launcher.delta_time > 1000)launcher.delta_time = 1000; - static uint temp_fps = 0; - static double fps_time = 0; + __gshared uint temp_fps = 0; + __gshared double fps_time = 0; temp_fps++; fps_time += launcher.delta_time; while(fps_time > 1000) @@ -295,7 +295,7 @@ void mainLoop(void* arg) { launcher.setStyle(2); } - else if(igMenuItemBool("Super",null,launcher.style == 3,true)) + else if(igMenuItemBool("Default",null,launcher.style == 3,true)) { launcher.setStyle(3); } @@ -466,8 +466,8 @@ void mainLoop(void* arg) launcher.renderer.present(); draw_time = launcher.getTime() - draw_time; - static float plot_time = 0; - static uint plot_samples = 0; + __gshared float plot_time = 0; + __gshared uint plot_samples = 0; plot_time += launcher.delta_time; plot_samples++; launcher.plot_values[100].delta_time += launcher.delta_time; @@ -602,14 +602,15 @@ int main(int argc, char** argv) setStyle(3); - launcher.job_updater = Mallocator.make!ECSJobUpdater(8); + launcher.job_updater = Mallocator.make!ECSJobUpdater(12); //launcher.job_updater.onCreate(); - EntityManager.initialize(8); + EntityManager.initialize(12); launcher.manager = EntityManager.instance; - launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID; - launcher.manager.setJobDispachFunc(&launcher.job_updater.dispatch); + //launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID; + //launcher.manager.setJobDispachFunc(&launcher.job_updater.dispatch); + launcher.manager.setMultithreadingCallbacks(&launcher.job_updater.dispatch, &launcher.job_updater.getThreadID); launcher.manager.beginRegister(); @@ -645,7 +646,7 @@ int main(int argc, char** argv) //int result = emscripten_request_fullscreen_strategy(null, true, &strategy); //emscripten_enter_soft_fullscreen(null, &strategy); //printf("Fullscreen request: %s.\n", emscripten_result_to_string(result)); - emscripten_set_main_loop_arg(&mainLoop, null, -1, 1); + emscripten_set_main_loop_arg(&mainLoop, null, 0, 1); } else { diff --git a/demos/source/demos/simple.d b/demos/source/demos/simple.d index 9340458..9dfa83b 100644 --- a/demos/source/demos/simple.d +++ b/demos/source/demos/simple.d @@ -86,7 +86,7 @@ struct MoveSystem } } -Simple* simple; +__gshared Simple* simple; void simpleStart() { @@ -149,7 +149,7 @@ bool simpleLoop() { if(launcher.getKeyState(SDL_SCANCODE_SPACE)) { - spawnEntity(); + foreach(i;0..10000)spawnEntity(); } @@ -159,7 +159,6 @@ bool simpleLoop() launcher.job_updater.begin(); launcher.manager.updateMT(); launcher.job_updater.call(); - launcher.multithreading = false; } else { diff --git a/demos/source/demos/snake.d b/demos/source/demos/snake.d index 19f08ad..9df3fcc 100644 --- a/demos/source/demos/snake.d +++ b/demos/source/demos/snake.d @@ -409,7 +409,7 @@ struct CleanSystem } } -Snake* snake; +__gshared Snake* snake; void snakeStart() { @@ -500,7 +500,7 @@ bool snakeLoop() float delta_time = launcher.delta_time; if(delta_time > 2000)delta_time = 2000; - static float time = 0; + __gshared float time = 0; if(launcher.getKeyState(SDL_SCANCODE_SPACE))time += delta_time * 3; else time += delta_time; diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index 2d8ebc0..3ef90c4 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -746,8 +746,6 @@ bool spaceInvadersLoop() launcher.job_updater.begin(); launcher.manager.updateMT(); launcher.job_updater.call(); - launcher.multithreading = false; - printf("Disable mt\n"); } else { diff --git a/demos/source/game_core/job_updater.d b/demos/source/game_core/job_updater.d index 26cd8e3..46cd609 100644 --- a/demos/source/game_core/job_updater.d +++ b/demos/source/game_core/job_updater.d @@ -2,6 +2,7 @@ module game_core.job_updater; import ecs.std; import ecs.vector; +import ecs.atomic; import ecs_utils.utils; @@ -9,6 +10,17 @@ import ecs_utils.utils; import ecs.manager; import mmutils.thread_pool; +version(LDC) +{ + import ldc.attributes; +} +else +{ + struct optStrategy { + string strategy; + } +} + //import supre.core.call_graph_generator; struct ECSJobUpdater @@ -24,14 +36,21 @@ struct ECSJobUpdater pool.waitThreads(); //pool.unregistExternalThread(thread_data); if(jobs)Mallocator.dispose(jobs); + version(WebAssembly)pthread_key_delete(tls_key); } + version(WebAssembly) + { + __gshared pthread_key_t tls_key; + } + else static uint thread_id = 0; + ThreadPool pool; ThreadData* thread_data; int job_id = 0; int no_dep_count = 0; - static uint thread_id = 0; + //static uint thread_id = 0; struct Group { @@ -78,21 +97,25 @@ struct ECSJobUpdater Group[] jobs; Vector!(Group*) call_jobs; Group last_job; + JobData[1] groupEndJobs; //TrackData[32] trackers; void onCreate(uint threads_count) { + version(WebAssembly)pthread_key_create(&tls_key, null); + pool.initialize(); - //thread_data = pool.registerExternalThread(); + thread_data = pool.registerExternalThread(); pool.setThreadsNum(threads_count); jobs = Mallocator.makeArray!Group(256); } - uint getThreadID() nothrow + uint getThreadID() @nogc nothrow { - return thread_id; + version(WebAssembly)return cast(int)pthread_getspecific(tls_key); + else return thread_id; } void begin() @@ -113,36 +136,44 @@ struct ECSJobUpdater //foreach(ref tracker;trackers)tracker.clear(); } + @optStrategy("none") + void nop() + { + int i; + i++; + } + + //@optStrategy("none") void call() { if(last_job.group.getDependenciesWaitCount() == 0)return; if(call_jobs.length == 0)return; - JobData[1] groupEndJobs; - groupEndJobs[0] = JobData(&releaseMainThread, "Stop Threads"); + //JobData[1] groupEndJobs; + groupEndJobs[0] = JobData(&releaseMainThread, "Stop Threads", null, null); last_job.group.jobs = groupEndJobs; + last_job.group.thPool = &pool; last_job.group.executeOnThreadNum = 0; foreach(job;call_jobs) { job.start(); } - ret = false; - while(!ret) + + /*while(atomicLoad(ret) == 1)//!cas(&ret,0,1)) { - printf("SDL\n"); - } - printf("NonSDL\n"); - //thread_data.threadStartFunc(); + nop(); + version(WebAssembly)//emscripten_main_thread_process_queued_calls(); + }//*/ - int i = 10; + thread_data.threadStartFunc(); } -shared bool ret = false; + void releaseMainThread(ThreadData* th_data, JobData* data) { - //pool.releaseExternalThreads(); - ret = true; + //atomicStore(ret,0); + pool.releaseExternalThreads(); } static struct JobCaller @@ -153,10 +184,27 @@ shared bool ret = false; void callJob(ThreadData* th_data, JobData* data) { - updater.thread_id = th_data.threadId; - uint job_id = updater.getThreadID(); + + //uint job_id = updater.getThreadID(); //updater.trackers[job_id].begin(id); - job.execute(); + version(WebAssembly) + { + //updater.thread_id = th_data.threadId; + pthread_setspecific(tls_key, cast(void*)th_data.threadId); + if(th_data.threadId == 0) + { + emscripten_main_thread_process_queued_calls(); + job.execute(); + emscripten_main_thread_process_queued_calls(); + } + else job.execute(); + } + else + { + updater.thread_id = th_data.threadId; + job.execute(); + } + //atomicOp!"-="(updater.jobs_count,1); //updater.trackers[job_id].end(); } } @@ -178,7 +226,7 @@ shared bool ret = false; caller.id = index; jobs[group.id].add(caller); } - + jobs[group.id].build(&pool); uint deps = cast(uint)group.dependencies.length; diff --git a/demos/utils/source/ecs_utils/gfx/texture.d b/demos/utils/source/ecs_utils/gfx/texture.d index 6f41fe2..4407123 100644 --- a/demos/utils/source/ecs_utils/gfx/texture.d +++ b/demos/utils/source/ecs_utils/gfx/texture.d @@ -87,7 +87,7 @@ struct Texture } } - static bool function(ref Texture this_, const char[] path) __load; + __gshared bool function(ref Texture this_, const char[] path) __load; struct Data { diff --git a/demos/utils/source/ecs_utils/imgui_bind.d b/demos/utils/source/ecs_utils/imgui_bind.d index 3e84328..b6572cb 100644 --- a/demos/utils/source/ecs_utils/imgui_bind.d +++ b/demos/utils/source/ecs_utils/imgui_bind.d @@ -28,12 +28,12 @@ import cimgui.cimgui; extern(C): -SDL_Window* g_Window; -ulong g_Time; -bool[3] g_MousePressed; -SDL_Cursor*[ImGuiMouseCursor_COUNT] g_MouseCursors; -char* g_ClipboardTextData; -GLuint g_FontTexture = 0; +__gshared SDL_Window* g_Window; +__gshared ulong g_Time; +__gshared bool[3] g_MousePressed; +__gshared SDL_Cursor*[ImGuiMouseCursor_COUNT] g_MouseCursors; +__gshared char* g_ClipboardTextData; +__gshared GLuint g_FontTexture = 0; const (char)* ImGuiImplSDL2GetClipboardText(void*) { @@ -280,7 +280,7 @@ static void ImGui_ImplSDL2_UpdateGamepads() } -private long frequency; +__gshared private long frequency; void ImGuiImplSDL2NewFrame(SDL_Window* window) { diff --git a/demos/utils/source/ecs_utils/utils.d b/demos/utils/source/ecs_utils/utils.d index aa223eb..4c143fd 100644 --- a/demos/utils/source/ecs_utils/utils.d +++ b/demos/utils/source/ecs_utils/utils.d @@ -50,6 +50,12 @@ version(D_BetterC) version(WebAssembly) { + alias pthread_key_t = uint; + + extern (C) int pthread_key_create(pthread_key_t *, void* function(void *)) @nogc nothrow; + extern (C) int pthread_key_delete(pthread_key_t) @nogc nothrow; + extern (C) void* pthread_getspecific(pthread_key_t) @nogc nothrow; + extern (C) int pthread_setspecific(pthread_key_t, const void *) @nogc nothrow; enum EMSCRIPTEN_RESULT_SUCCESS = 0; enum EMSCRIPTEN_RESULT_DEFERRED = 1; @@ -109,6 +115,9 @@ version(WebAssembly) extern (C) void emscripten_cancel_main_loop(); extern (C) int emscripten_request_fullscreen_strategy(const char *target, bool deferUntilInEventHandler, const EmscriptenFullscreenStrategy *fullscreenStrategy); extern (C) int emscripten_enter_soft_fullscreen(const char *target, const EmscriptenFullscreenStrategy *fullscreenStrategy); + extern (C) void emscripten_main_thread_process_queued_calls(); + extern (C) void emscripten_pause_main_loop(); + extern (C) void emscripten_resume_main_loop(); alias int time_t; alias int clockid_t; diff --git a/source/ecs/atomic.d b/source/ecs/atomic.d new file mode 100644 index 0000000..1ab78af --- /dev/null +++ b/source/ecs/atomic.d @@ -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; +} \ No newline at end of file diff --git a/source/ecs/id_manager.d b/source/ecs/id_manager.d index 1804390..ebd7679 100644 --- a/source/ecs/id_manager.d +++ b/source/ecs/id_manager.d @@ -4,7 +4,7 @@ import ecs.entity; import ecs.std; import ecs.vector; -import core.atomic; +import ecs.atomic; import core.stdc.string : memcpy; /************************************************************************************************************************ diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 8d461fd..b80e0e7 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -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; diff --git a/source/ecs/std.d b/source/ecs/std.d index c8b7ecd..81eed0a 100644 --- a/source/ecs/std.d +++ b/source/ecs/std.d @@ -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!"); } \ No newline at end of file diff --git a/tests/tests.d b/tests/tests.d index 2e5e3e6..703f391 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -10,22 +10,9 @@ import ecs.system; import ecs.attributes; import ecs.core; - - version(WebAssembly) { extern(C) int printf(scope const char* format, ...) @nogc nothrow @system; - /*{ - return 0; - }*/ - //import core.stdc.stdio : printf; - /*struct Time - { - static long getUSecTime() - { - return 0; - } - }*/ alias int time_t; alias int clockid_t; @@ -52,11 +39,6 @@ version(WebAssembly) //time = spec.tv_sec; return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000;//time / 1000_000; - - /*LARGE_INTEGER time, freq; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&time); - return time.QuadPart / (freq.QuadPart / 1000_000);*/ } } @@ -92,11 +74,6 @@ else version(Posix) //time = spec.tv_sec; return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000;//time / 1000_000; - - /*LARGE_INTEGER time, freq; - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&time); - return time.QuadPart / (freq.QuadPart / 1000_000);*/ } } } @@ -380,11 +357,6 @@ struct TestSystemWithHighPriority { } - - /*void handleEvent(Event event, ref TestComp comp) - { - - }*/ } struct Sys1 @@ -468,12 +440,7 @@ struct TestSystem2 //TestComp* tt; } - /*void handleEvent(EventInput input) - { - - }*/ - - void handleEvent(/*EventInput input, */Entity* entity, ref TestEvent event) + void handleEvent(Entity* entity, ref TestEvent event) { TestComp3* test = entity.getComponent!TestComp3; test.bg = event.a; @@ -482,7 +449,7 @@ struct TestSystem2 gEM.sendEvent(entity.id, event2); } - void handleEvent(/*EventInput input, */Entity* entity, ref TestEvent2 event) + void handleEvent(Entity* entity, ref TestEvent2 event) { TestComp3* test = entity.getComponent!TestComp3; test.gg = cast(uint) event.a; @@ -532,17 +499,11 @@ struct TestSystem2 } } - /*void handleEvent(Event event, ref TestComp comp) - { - - }*/ } extern(C) int main() { - - void dispatch(EntityManager.JobGroup jobs) nothrow @nogc { foreach (job; jobs.jobs) @@ -552,6 +513,11 @@ extern(C) int main() } } + uint getID() nothrow @nogc + { + return 0; + } + void writeEntityComponents(Entity* entity) { @@ -574,12 +540,10 @@ extern(C) int main() ////writeln((cast(uint*) pp)[0 .. 14], " ", pp); } - /*int a = 0; - if(!a)assert(0);*/ - EntityManager.initialize(1); - gEM.setJobDispachFunc(&dispatch); + //gEM.setJobDispachFunc(&dispatch); + gEM.setMultithreadingCallbacks(&dispatch, &getID); //assert(gEM !is null); gEM.beginRegister(); @@ -612,7 +576,7 @@ extern(C) int main() gEM.registerSystem!Sys2(-100); gEM.registerSystem!Sys3(-2); //gEM.registerSystem!TestSystemWithHighPriority(100); - //gEM.registerSystem!TestSystem2(0);*/ + //gEM.registerSystem!TestSystem2(0); gEM.endRegister(); /*dur = (MonoTime.currTime - time).total!"usecs";