-removed launcher.manager (gEntityManager used instead)
-removed somee comments, unneded code -added some comments/documentation
This commit is contained in:
parent
3b954b732b
commit
27154c809e
10 changed files with 583 additions and 688 deletions
|
|
@ -13,33 +13,39 @@ import app : launcher;
|
|||
|
||||
import bindbc.sdl;
|
||||
|
||||
//position component
|
||||
struct CLocation
|
||||
{
|
||||
//adds some extra functionality. Not required. Will be probably removed from library in the future.
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;
|
||||
alias value this;//use component as it value
|
||||
|
||||
//default values work properly
|
||||
vec2 value = vec2(0);
|
||||
}
|
||||
|
||||
//scale component
|
||||
struct CScale
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;///use component as it value
|
||||
alias value this;//use component as it value
|
||||
|
||||
vec2 value = vec2(16,16);
|
||||
}
|
||||
|
||||
//rotation component
|
||||
struct CRotation
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;///use component as it value
|
||||
alias value this;//use component as it value
|
||||
|
||||
float value = 0;
|
||||
}
|
||||
|
||||
//depth component. Entity with higher depth will be rendered on top
|
||||
struct CDepth
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -49,6 +55,7 @@ struct CDepth
|
|||
short value;
|
||||
}
|
||||
|
||||
//color component
|
||||
struct CColor
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -58,6 +65,7 @@ struct CColor
|
|||
@GUIColor uint value;
|
||||
}
|
||||
|
||||
//component used for selection
|
||||
struct CSelected
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -65,11 +73,13 @@ struct CSelected
|
|||
bool value = false;
|
||||
}
|
||||
|
||||
//component indicating that entity should receive input from mouse, keyboard, etc.
|
||||
struct CInput
|
||||
{
|
||||
mixin ECS.Component;
|
||||
}
|
||||
|
||||
//component with damping value
|
||||
struct CDamping
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -79,6 +89,7 @@ struct CDamping
|
|||
@GUIRange(0,9) byte value = 1;
|
||||
}
|
||||
|
||||
//velocity component
|
||||
struct CVelocity
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -88,6 +99,7 @@ struct CVelocity
|
|||
vec2 value = vec2(0,0);
|
||||
}
|
||||
|
||||
//factor which is used for velocity masking
|
||||
struct CVelocityFactor
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -97,30 +109,35 @@ struct CVelocityFactor
|
|||
vec2 value = vec2(1);
|
||||
}
|
||||
|
||||
//flag indicating that entity is static and shouldn't be updated by most systems in every frame
|
||||
struct CStatic
|
||||
{
|
||||
mixin ECS.Component;
|
||||
}
|
||||
|
||||
//system which slowing down entities
|
||||
struct DampingSystem
|
||||
{
|
||||
//system will generate up to 32 jobs
|
||||
mixin ECS.System!32;
|
||||
|
||||
struct EntitiesData
|
||||
{
|
||||
uint length;
|
||||
const (Entity)[] entity;
|
||||
@readonly CDamping[] damping;
|
||||
CVelocity[] velocity;
|
||||
const (Entity)[] entity; //entity is readonly
|
||||
@readonly CDamping[] damping;//damping is readonly. Marking with @readonly will help multithreading algorithm
|
||||
CVelocity[] velocity;//velocity is wirtable as it will be modified for entities in this system
|
||||
}
|
||||
|
||||
//20 predefined damping speeds. Gives possibility to store damping as single byte.
|
||||
float[20] damp = 0;
|
||||
|
||||
bool onBegin()
|
||||
{
|
||||
//calculate damping values
|
||||
foreach(i;0..20)
|
||||
{
|
||||
damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1);
|
||||
damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.deltaTime*0.1);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -130,11 +147,13 @@ struct DampingSystem
|
|||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
//constantly slow down entity
|
||||
data.velocity[i] = data.velocity[i] * damp[data.damping[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//system used for entity movement
|
||||
struct MoveSystem
|
||||
{
|
||||
mixin ECS.System!64;
|
||||
|
|
@ -144,23 +163,24 @@ struct MoveSystem
|
|||
uint length;
|
||||
CLocation[] location;
|
||||
@readonly CVelocity[] velocity;
|
||||
@optional @readonly CVelocityFactor[] vel_factor;
|
||||
@optional @readonly CVelocityFactor[] vel_factor;//CVeclocityFactor is not required so entites without this component will be also updated
|
||||
}
|
||||
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
//split into two loops for two types of entities. Doing it in "normal" way by testing data.vel_factor inside loop in every iteration will be probably compiled as same machine code in release build (it works in LDC)
|
||||
if(data.vel_factor)
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.delta_time;
|
||||
data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.deltaTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.location[i] += data.velocity[i] * launcher.delta_time;
|
||||
data.location[i] += data.velocity[i] * launcher.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -228,32 +248,13 @@ struct InputMovementSystem
|
|||
*/
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
/*if(move_vector.x == 0)
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.textures[i].coords = vec4(0*px,80*px,48*px,32*px);
|
||||
}
|
||||
//return;
|
||||
}*/
|
||||
//move every entity using movement vector
|
||||
//if(move_vector.x != 0 || move_vector.y != 0)
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.velocity[i] += move_vector * launcher.delta_time * 0.005;
|
||||
data.velocity[i] += move_vector * launcher.deltaTime * 0.005;
|
||||
if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5;
|
||||
else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5;
|
||||
if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5;
|
||||
else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5;
|
||||
//data.locations[i].x += move_vector.x * launcher.delta_time * 0.25;
|
||||
//data.locations[i].y += move_vector.y * launcher.delta_time * 0.25;
|
||||
//if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px);
|
||||
//else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px);
|
||||
}
|
||||
/*else
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.velocity[i] = vec2(0,0);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,8 @@ module game_core.job_updater;
|
|||
import bubel.ecs.std;
|
||||
import bubel.ecs.vector;
|
||||
import bubel.ecs.atomic;
|
||||
|
||||
import ecs_utils.utils;
|
||||
|
||||
//import core.time;
|
||||
import bubel.ecs.manager;
|
||||
|
||||
import mmutils.thread_pool;
|
||||
|
||||
version(LDC)
|
||||
|
|
@ -21,8 +18,6 @@ else
|
|||
}
|
||||
}
|
||||
|
||||
//import supre.core.call_graph_generator;
|
||||
|
||||
struct ECSJobUpdater
|
||||
{
|
||||
|
||||
|
|
@ -33,30 +28,15 @@ struct ECSJobUpdater
|
|||
|
||||
~this()
|
||||
{
|
||||
//wait for end of jobs
|
||||
pool.waitThreads();
|
||||
//pool.unregistExternalThread(thread_data);
|
||||
//dispose jobs array
|
||||
if(jobs)Mallocator.dispose(jobs);
|
||||
//free TLS data
|
||||
version(WebAssembly)pthread_key_delete(tls_key);
|
||||
else version(Android)pthread_key_delete(tls_key);
|
||||
}
|
||||
|
||||
version(WebAssembly)
|
||||
{
|
||||
__gshared pthread_key_t tls_key;
|
||||
}
|
||||
else version(Android)
|
||||
{
|
||||
__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;
|
||||
|
||||
struct Group
|
||||
{
|
||||
~this() nothrow
|
||||
|
|
@ -65,33 +45,40 @@ struct ECSJobUpdater
|
|||
}
|
||||
|
||||
JobsGroup group;
|
||||
JobData[1024] jobs;
|
||||
JobCaller[1024] callers;
|
||||
//each group can have up to 128 jobs
|
||||
JobData[128] jobs;
|
||||
JobCaller[128] callers;
|
||||
uint count = 0;
|
||||
string name;
|
||||
|
||||
//mmutils.ThreadPool uses system of dependency where dependencies are added for child groups.
|
||||
//Parent group has atomic counter and after completition it will add job groups dependant on it.
|
||||
void dependantOn(Group* dependency)
|
||||
{
|
||||
group.dependantOn(&dependency.group);
|
||||
}
|
||||
|
||||
//add group to pool
|
||||
void start()
|
||||
{
|
||||
group.thPool.addGroupAsynchronous(&group);
|
||||
}
|
||||
|
||||
//add jobs slice to group structure
|
||||
void build(ThreadPool* pool)
|
||||
{
|
||||
group.thPool = pool;
|
||||
group.jobs = jobs[0..count];
|
||||
}
|
||||
|
||||
//clear jobs
|
||||
void clear()
|
||||
{
|
||||
group = JobsGroup("name",null);
|
||||
count = 0;
|
||||
}
|
||||
|
||||
//add single job to group
|
||||
void add(JobCaller caller)
|
||||
{
|
||||
callers[count] = caller;
|
||||
|
|
@ -100,15 +87,10 @@ struct ECSJobUpdater
|
|||
}
|
||||
}
|
||||
|
||||
Group[] jobs;
|
||||
Vector!(Group*) call_jobs;
|
||||
Group last_job;
|
||||
JobData[1] groupEndJobs;
|
||||
|
||||
//TrackData[32] trackers;
|
||||
|
||||
//initialize thread pool and data
|
||||
void onCreate(uint threads_count)
|
||||
{
|
||||
//create TLS for Android and WebAsssembly
|
||||
version(WebAssembly)pthread_key_create(&tls_key, null);
|
||||
else version(Android)pthread_key_create(&tls_key, null);
|
||||
|
||||
|
|
@ -119,6 +101,7 @@ struct ECSJobUpdater
|
|||
jobs = Mallocator.makeArray!Group(256);
|
||||
}
|
||||
|
||||
//this function are providingn ThreadID to ECS. BubelECS is expecting ThreadID to be linear ID in range (0;ThreadsCount)
|
||||
uint getThreadID() @nogc nothrow
|
||||
{
|
||||
version(WebAssembly)return cast(int)pthread_getspecific(tls_key);
|
||||
|
|
@ -126,9 +109,9 @@ struct ECSJobUpdater
|
|||
else return thread_id;
|
||||
}
|
||||
|
||||
//clear jobs data
|
||||
void begin()
|
||||
{
|
||||
job_id = 0;
|
||||
call_jobs.clear();
|
||||
|
||||
foreach(ref job;jobs)
|
||||
|
|
@ -139,68 +122,57 @@ struct ECSJobUpdater
|
|||
last_job.clear();
|
||||
}
|
||||
|
||||
void clearTracker()
|
||||
{
|
||||
//foreach(ref tracker;trackers)tracker.clear();
|
||||
}
|
||||
|
||||
@optStrategy("none")
|
||||
void nop()
|
||||
{
|
||||
int i;
|
||||
i++;
|
||||
}
|
||||
|
||||
//@optStrategy("none")
|
||||
//execute jobs
|
||||
void call()
|
||||
{
|
||||
//if there is no work return
|
||||
if(last_job.group.getDependenciesWaitCount() == 0)return;
|
||||
if(call_jobs.length == 0)return;
|
||||
|
||||
//JobData[1] groupEndJobs;
|
||||
//set last job
|
||||
groupEndJobs[0] = JobData(&releaseMainThread, "Stop Threads", null, null);
|
||||
|
||||
//add job to group
|
||||
last_job.group.jobs = groupEndJobs;
|
||||
//set thread pool pointer
|
||||
last_job.group.thPool = &pool;
|
||||
//last job should be called on main thread. It prevent some issues with death loops.
|
||||
last_job.group.executeOnThreadNum = 0;
|
||||
|
||||
//start jobs without dependencies
|
||||
foreach(job;call_jobs)
|
||||
{
|
||||
job.start();
|
||||
}
|
||||
|
||||
/*while(atomicLoad(ret) == 1)//!cas(&ret,0,1))
|
||||
{
|
||||
nop();
|
||||
version(WebAssembly)//emscripten_main_thread_process_queued_calls();
|
||||
}//*/
|
||||
|
||||
|
||||
//add main thread to pool. It will be released in last job.
|
||||
thread_data.threadStartFunc();
|
||||
}
|
||||
|
||||
//callback that will release main thread
|
||||
void releaseMainThread(ThreadData* th_data, JobData* data)
|
||||
{
|
||||
//atomicStore(ret,0);
|
||||
pool.releaseExternalThreads();
|
||||
}
|
||||
|
||||
static struct JobCaller
|
||||
{
|
||||
//ECS job
|
||||
EntityManager.Job* job;
|
||||
//pointer to parent
|
||||
ECSJobUpdater* updater;
|
||||
//job ID
|
||||
uint id;
|
||||
|
||||
//called by external thread
|
||||
void callJob(ThreadData* th_data, JobData* data)
|
||||
{
|
||||
|
||||
//uint job_id = updater.getThreadID();
|
||||
//updater.trackers[job_id].begin(id);
|
||||
version(WebAssembly)
|
||||
{
|
||||
//updater.thread_id = th_data.threadId;
|
||||
pthread_setspecific(tls_key, cast(void*)th_data.threadId);
|
||||
if(th_data.threadId == 0)
|
||||
{
|
||||
//this emscripten call is required to make multithreading working
|
||||
emscripten_main_thread_process_queued_calls();
|
||||
job.execute();
|
||||
emscripten_main_thread_process_queued_calls();
|
||||
|
|
@ -214,23 +186,27 @@ struct ECSJobUpdater
|
|||
}
|
||||
else
|
||||
{
|
||||
//set thread id
|
||||
updater.thread_id = th_data.threadId;
|
||||
//execture job. It's the function from BubelECS
|
||||
job.execute();
|
||||
}
|
||||
//atomicOp!"-="(updater.jobs_count,1);
|
||||
//updater.trackers[job_id].end();
|
||||
}
|
||||
}
|
||||
|
||||
//this is callback passed to EntityManager. EntityManager will call this for every jobs group. Every system will generate one group.
|
||||
void dispatch(EntityManager.JobGroup group)
|
||||
{
|
||||
//check if group isn't empty
|
||||
if(group.jobs.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//add name for job. Used for traces.
|
||||
jobs[group.id].name = cast(string)group.caller.system.name;
|
||||
|
||||
//add jobs to group
|
||||
foreach(ref job;group.jobs)
|
||||
{
|
||||
uint index = 0;
|
||||
|
|
@ -242,21 +218,51 @@ struct ECSJobUpdater
|
|||
jobs[group.id].add(caller);
|
||||
}
|
||||
|
||||
//build group
|
||||
jobs[group.id].build(&pool);
|
||||
|
||||
uint deps = cast(uint)group.dependencies.length;
|
||||
|
||||
//add dependencies
|
||||
foreach(dep;group.dependencies)
|
||||
{
|
||||
if(jobs[dep.id].count && dep.caller.system.willExecute && dep.caller.system.enabled)jobs[group.id].dependantOn(&jobs[dep.id]);
|
||||
else deps--;
|
||||
}
|
||||
|
||||
//set as job without dependencies if it hasn't any
|
||||
if(deps == 0)
|
||||
{
|
||||
call_jobs.add(&jobs[group.id]);
|
||||
}
|
||||
|
||||
//last job is dependant on all jobs so it will be called after everything will be finished
|
||||
last_job.dependantOn(&jobs[group.id]);
|
||||
}
|
||||
|
||||
//Webassembly version works properly only when there is no thread local data (static variables).
|
||||
//Because of that I'm using pthread tls instead of D. TLS is used only for storing ThreadID
|
||||
version(WebAssembly)
|
||||
{
|
||||
__gshared pthread_key_t tls_key;
|
||||
}
|
||||
else version(Android)
|
||||
{
|
||||
__gshared pthread_key_t tls_key;
|
||||
}
|
||||
else static uint thread_id = 0;
|
||||
|
||||
//thread pool
|
||||
ThreadPool pool;
|
||||
//thread data used for main thread
|
||||
ThreadData* thread_data;
|
||||
|
||||
//array of jobs
|
||||
Group[] jobs;
|
||||
//list of jobs which should be called on frame start as they have no dependencies
|
||||
Vector!(Group*) call_jobs;
|
||||
//last job group is used for releasing main thread from pool
|
||||
Group last_job;
|
||||
//last_job group has one job
|
||||
JobData[1] groupEndJobs;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue