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

135
tests/access_perf.d Normal file
View file

@ -0,0 +1,135 @@
module tests.access_perf;
import tests.runner;
import bubel.ecs.core;
import bubel.ecs.manager;
import bubel.ecs.entity;
version(GNU)
{
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else import std.array : staticArray;
import core.stdc.stdio;
struct CLong
{
mixin ECS.Component;
alias value this;
long value = 10;
}
struct CInt
{
mixin ECS.Component;
alias value this;
int value = 10;
}
struct CUInt
{
mixin ECS.Component;
alias value this;
uint value = 12;
}
struct CBig
{
mixin ECS.Component;
uint[32] data;
}
EntityTemplate* tmpl;
void beforeEveryTest()
{
gEM.initialize(0);
gEM.beginRegister();
gEM.registerComponent!CLong;
gEM.registerComponent!CInt;
gEM.registerComponent!CUInt;
gEM.registerComponent!CBig;
gEM.endRegister();
tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray);
foreach(i; 0 .. 100_000)gEM.addEntity(tmpl);
}
void afterEveryTest()
{
if(tmpl)gEM.freeTemplate(tmpl);
tmpl = null;
gEM.destroy();
}
@("DirectAccess100k1comp")
unittest
{
foreach(i;0..25000)
{
Entity* entity = gEM.getEntity(EntityID(i*4+1,0));
CUInt* comp1 = entity.getComponent!CUInt;
comp1.value = 4;
}
}
@("DirectAccess100k4comp")
unittest
{
foreach(i;0..25000)
{
Entity* entity = gEM.getEntity(EntityID(i*4+1,0));
CUInt* comp1 = entity.getComponent!CUInt;
comp1.value = 4;
CInt* comp2 = entity.getComponent!CInt;
comp2.value = 3;
CLong* comp3 = entity.getComponent!CLong;
comp3.value = 1;
CBig* comp4 = entity.getComponent!CBig;
comp4.data[0] = 2;
}
}
@("DirectAccess100k1compWithMeta")
unittest
{
foreach(i;0..25000)
{
Entity* entity = gEM.getEntity(EntityID(i*4+1,0));
EntityMeta meta = entity.getMeta();
CUInt* comp1 = meta.getComponent!CUInt;
comp1.value = 4;
}
}
@("DirectAccess100k4compWithMeta")
unittest
{
foreach(i;0..25000)
{
Entity* entity = gEM.getEntity(EntityID(i*4+1,0));
EntityMeta meta = entity.getMeta();
CUInt* comp1 = meta.getComponent!CUInt;
comp1.value = 4;
CInt* comp2 = meta.getComponent!CInt;
comp2.value = 3;
CLong* comp3 = meta.getComponent!CLong;
comp3.value = 1;
CBig* comp4 = meta.getComponent!CBig;
comp4.data[0] = 2;
}
}

View file

@ -1,11 +1,18 @@
module tests.basic;
import ecs.core;
import ecs.manager;
import ecs.system;
import ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.manager;
import bubel.ecs.system;
import bubel.ecs.attributes;
import std.array : staticArray;
version(GNU)
{
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else import std.array : staticArray;
struct CInt
{
@ -61,6 +68,11 @@ struct CUnregistered
short value = 12;
}
struct CFlag
{
mixin ECS.Component;
}
struct LongAddSystem
{
mixin ECS.System;
@ -103,6 +115,7 @@ struct EmptySystem
void beforeEveryTest()
{
CUnregistered.component_id = ushort.max;
gEM.initialize(0);
gEM.beginRegister();
@ -112,6 +125,7 @@ void beforeEveryTest()
gEM.registerComponent!CDouble;
gEM.registerComponent!CLong;
gEM.registerComponent!CShort;
gEM.registerComponent!CFlag;
gEM.endRegister();
}
@ -121,14 +135,33 @@ void afterEveryTest()
gEM.destroy();
}
@("EntityMeta")
unittest
{
EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray);
Entity* entity = gEM.addEntity(tmpl_);
EntityMeta meta = entity.getMeta();
assert(meta.hasComponent(CInt.component_id));
assert(meta.getComponent!CInt);
assert(meta.hasComponent(CFloat.component_id));
assert(meta.getComponent!CFloat);
assert(!meta.getComponent!CLong);
assert(!meta.hasComponent(CLong.component_id));
assert(!meta.getComponent!CUnregistered);
assert(!meta.hasComponent(CUnregistered.component_id));
assert(*meta.getComponent!CInt == 1);
assert(*meta.getComponent!CFloat == 2.0);
}
@("AddEntity")
unittest
{
ushort[2] ids = [CInt.component_id, CFloat.component_id];
EntityTemplate* tmpl_ = gEM.allocateTemplate(ids);
assert(tmpl_.info.components.length == 2);
EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray);
assert(tmpl_.info.components.length == 3);
assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof));
assert(tmpl_.getComponent!CInt);
assert(tmpl_.getComponent!CFloat);
assert(tmpl_.getComponent!CFlag);
assert(!tmpl_.getComponent!CLong);
assert(!tmpl_.getComponent!CUnregistered);
assert(*tmpl_.getComponent!CInt == 1);
@ -146,6 +179,67 @@ unittest
assert(entity2.getComponent!CFloat);
assert(*entity2.getComponent!CInt == 2);
assert(*entity2.getComponent!CFloat == 2.0);
//CInt cint = CInt(10);
//CLong clong;
//Entity* entity3 = gEM.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray);
Entity* entity3 = gEM.addEntity(tmpl_, [CInt(10).ref_, CLong().ref_, CFlag().ref_].staticArray);
EntityID id = entity3.id;
assert(entity3.hasComponent(CInt.component_id));
assert(entity3.hasComponent(CFloat.component_id));
assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0);
gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray);
gEM.commit();
entity3 = gEM.getEntity(id);
assert(entity3.getComponent!CInt);
assert(entity3.getComponent!CFloat);
assert(entity3.getComponent!CFlag);
assert(entity3.getComponent!CShort);
assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0);
assert(*entity3.getComponent!CShort == 2);
gEM.removeComponents(entity3.id, [CFlag().component_id,CShort(2).component_id].staticArray);
gEM.commit();
entity3 = gEM.getEntity(id);
assert(entity3.getComponent!CInt);
assert(entity3.getComponent!CFloat);
assert(!entity3.getComponent!CFlag);
assert(!entity3.getComponent!CShort);
assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0);
gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray);
gEM.removeComponents(entity3.id, [CUnregistered.component_id].staticArray);
gEM.commit();
entity3 = gEM.getEntity(id);
assert(entity3.getComponent!CInt);
assert(entity3.getComponent!CFloat);
assert(entity3.getComponent!CFlag);
assert(entity3.getComponent!CShort);
assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0);
assert(*entity3.getComponent!CShort == 2);
gEM.beginRegister();
gEM.registerComponent!CUnregistered;
gEM.endRegister();
gEM.addComponents(entity3.id, [CUnregistered(4).ref_].staticArray);
gEM.commit();
entity3 = gEM.getEntity(id);
assert(entity3.getComponent!CUnregistered);
assert(*entity3.getComponent!CUnregistered == 4);
gEM.removeComponents(entity3.id, [CUnregistered.component_id].staticArray);
gEM.commit();
entity3 = gEM.getEntity(id);
assert(!entity3.getComponent!CUnregistered);
}
//allocate templates
@ -156,32 +250,48 @@ unittest
ushort[2] ids = [CInt.component_id, CFloat.component_id];
EntityTemplate* tmpl_ = gEM.allocateTemplate(ids);
EntityTemplate* tmpl_d = gEM.allocateTemplate([CFloat.component_id, CInt.component_id, CFloat.component_id].staticArray);
EntityTemplate* tmpl_cp = gEM.allocateTemplate(tmpl_);
assert(tmpl_d.info == tmpl_.info);
assert(tmpl_cp.info == tmpl_cp.info);
assert(tmpl_.info.components.length == 2);
assert(tmpl_.getComponent!CInt);
assert(tmpl_.getComponent!CFloat);
assert(*tmpl_.getComponent!CInt == 1);
assert(*tmpl_.getComponent!CFloat == 2.0);
assert(tmpl_cp.getComponent!CFloat);
assert(tmpl_cp.getComponent!CInt);
assert(tmpl_.getComponent!CInt != tmpl_cp.getComponent!CInt);
assert(tmpl_.getComponent!CFloat != tmpl_cp.getComponent!CFloat);
assert(*tmpl_.getComponent!CInt == *tmpl_cp.getComponent!CInt);
assert(*tmpl_.getComponent!CFloat == *tmpl_cp.getComponent!CFloat);
*tmpl_.getComponent!CInt = 4;
*tmpl_.getComponent!CFloat = 5.0;
//allocate template from template with additional component
ushort[1] ids2 = [CDouble.component_id];
//allocate template from template with additional components
ushort[2] ids2 = [CDouble.component_id,CFlag.component_id];
EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2);
assert(tmpl_2.info.components.length == 3);
assert(tmpl_2.info.components.length == 4);
assert(tmpl_2.getComponent!CInt);
assert(tmpl_2.getComponent!CFloat);
assert(tmpl_2.getComponent!CDouble);
assert(tmpl_2.getComponent!CFlag);
assert(*tmpl_2.getComponent!CInt == 4);
assert(*tmpl_2.getComponent!CFloat == 5.0);
assert(*tmpl_2.getComponent!CDouble == 3.0);
assert(tmpl_.info.blocksCount() == 0);
Entity* entity = gEM.addEntity(tmpl_);
gEM.addComponents(entity.id, CDouble(8.0));
gEM.addComponents(entity.id, CFloat(100));
gEM.addComponents(entity.id, CDouble(8.0), CFloat(100));
assert(tmpl_.info.blocksCount() == 1);
//apply entity changes
gEM.commit();
assert(tmpl_.info.blocksCount() == 0);
//allocate template as entity copy
EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id);
assert(tmpl_3.info.components.length == 3);
@ -202,18 +312,20 @@ unittest
assert(*tmpl_4.getComponent!CFloat == 2.0);
assert(*tmpl_4.getComponent!CDouble == 3.0);
//allocate template from template with two additional component
ushort[2] ids3 = [CDouble.component_id, CLong.component_id];
EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_, ids3);
assert(tmpl_5.info.components.length == 4);
//allocate template from template with three additional component
ushort[3] ids3 = [CDouble.component_id, CLong.component_id, CShort.component_id];
EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_2, ids3);
assert(tmpl_5.info.components.length == 6);
assert(tmpl_5.getComponent!CInt);
assert(tmpl_5.getComponent!CFloat);
assert(tmpl_5.getComponent!CDouble);
assert(tmpl_5.getComponent!CLong);
assert(tmpl_5.getComponent!CShort);
assert(*tmpl_5.getComponent!CInt == 4);
assert(*tmpl_5.getComponent!CFloat == 5.0);
assert(*tmpl_5.getComponent!CDouble == 3.0);
assert(*tmpl_5.getComponent!CLong == 10);
assert(*tmpl_5.getComponent!CShort == 12);
//allocate template from template without one component
ushort[1] rem_ids = [CFloat.component_id];
@ -224,7 +336,7 @@ unittest
//allocate template from template without one component and two additional
EntityTemplate* tmpl_7 = gEM.allocateTemplate(tmpl_, ids3, rem_ids);
assert(tmpl_7.info.components.length == 3);
assert(tmpl_7.info.components.length == 4);
assert(tmpl_7.getComponent!CInt);
assert(tmpl_7.getComponent!CDouble);
assert(tmpl_7.getComponent!CLong);
@ -481,6 +593,10 @@ unittest
gEM.endRegister();
assert(gEM.getPass("custom"));
assert(!gEM.getPass("custommm"));
LongAddSystem* system = gEM.getSystem!LongAddSystem;
assert(system !is null);
assert(system.updates_count == 0);
@ -1116,9 +1232,9 @@ unittest
assert(*entity2.getComponent!CShort == 12);
gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,10));
gEM.sendEvent(id,ETest2(10));
gEM.sendEvent(id2,ETest());
gEM.sendEvent(id2,ETest2(id2,12));
gEM.sendEvent(id2,ETest2(12));
gEM.commit();
assert(ETest2.destory == 2);
@ -1129,20 +1245,20 @@ unittest
gEM.addComponents(id, CInt(2), CShort(1));
gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,2));
gEM.sendEvent(id,ETest2(2));
gEM.commit();
assert(ETest2.destory == 3);
entity = gEM.getEntity(id);
assert(*entity.getComponent!CLong == 66);
assert(*entity.getComponent!CInt == 36);
assert(*entity.getComponent!CInt == 2);//36);
//test for multiple event blocks
long result = *entity.getComponent!CLong;
foreach(i;0..10000)
{
gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,4));
gEM.sendEvent(id,ETest2(4));
result += 16;
result += 8;
}
@ -1220,4 +1336,247 @@ unittest
assert(*entity2.getComponent!CInt == 13);
gEM.end();
}
@("SystemDependencies")
unittest
{
struct TestSystem
{
mixin ECS.System;
struct EntitiesData
{
uint length;
@readonly CInt[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem2
{
mixin ECS.System;
struct EntitiesData
{
uint length;
CInt[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem3
{
mixin ECS.System;
struct EntitiesData
{
uint length;
@readonly CInt[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem4
{
mixin ECS.System;
struct EntitiesData
{
uint length;
CInt[] int_;
CLong[] long_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem5
{
mixin ECS.System;
struct EntitiesData
{
uint length;
@readonly CLong[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
gEM.beginRegister();
gEM.registerSystem!TestSystem(0);
gEM.registerSystem!TestSystem2(1);
gEM.registerSystem!TestSystem3(2);
gEM.registerSystem!TestSystem4(3);
gEM.registerSystem!TestSystem5(4);
gEM.endRegister();
const (EntityManager.UpdatePass)* pass = gEM.getPass("update");
assert(pass != null);
assert(pass.system_callers.length == 5);
assert(pass.system_callers[0].system_id == TestSystem.system_id);
assert(pass.system_callers[1].system_id == TestSystem2.system_id);
assert(pass.system_callers[2].system_id == TestSystem3.system_id);
assert(pass.system_callers[3].system_id == TestSystem4.system_id);
assert(pass.system_callers[4].system_id == TestSystem5.system_id);
assert(pass.system_callers[0].dependencies.length == 0);
assert(pass.system_callers[1].dependencies.length == 1);
assert(pass.system_callers[2].dependencies.length == 1);
assert(pass.system_callers[3].dependencies.length == 3);
assert(pass.system_callers[4].dependencies.length == 1);
assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id);
assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id);
assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id);
assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id);
assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id);
assert(pass.system_callers[4].dependencies[0].system_id == TestSystem4.system_id);
}
@("ExternalSystemDependencies")
unittest
{
enum TestDependency = "TestDepencency";
struct TestSystem
{
mixin ECS.System;
mixin ECS.ReadOnlyDependencies!(TestDependency);
struct EntitiesData
{
uint length;
@readonly CInt[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem2
{
mixin ECS.System;
mixin ECS.WritableDependencies!(TestDependency);
struct EntitiesData
{
uint length;
@readonly CInt[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem3
{
mixin ECS.System;
mixin ECS.ReadOnlyDependencies!(TestDependency);
struct EntitiesData
{
uint thread_id;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem4
{
mixin ECS.System;
mixin ECS.WritableDependencies!(TestDependency);
struct EntitiesData
{
uint length;
@readonly CInt[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
struct TestSystem5
{
mixin ECS.System;
mixin ECS.ReadOnlyDependencies!(TestDependency);
struct EntitiesData
{
uint length;
@readonly CLong[] int_;
}
void onUpdate(EntitiesData entities)
{
}
}
gEM.beginRegister();
gEM.registerDependency(TestDependency);
gEM.registerSystem!TestSystem(0);
gEM.registerSystem!TestSystem2(1);
gEM.registerSystem!TestSystem3(2);
gEM.registerSystem!TestSystem4(3);
gEM.registerSystem!TestSystem5(4);
gEM.endRegister();
const (EntityManager.UpdatePass)* pass = gEM.getPass("update");
assert(pass != null);
assert(pass.system_callers.length == 5);
assert(pass.system_callers[0].system_id == TestSystem.system_id);
assert(pass.system_callers[1].system_id == TestSystem2.system_id);
assert(pass.system_callers[2].system_id == TestSystem3.system_id);
assert(pass.system_callers[3].system_id == TestSystem4.system_id);
assert(pass.system_callers[4].system_id == TestSystem5.system_id);
assert(pass.system_callers[0].dependencies.length == 0);
assert(pass.system_callers[1].dependencies.length == 1);
assert(pass.system_callers[2].dependencies.length == 1);
assert(pass.system_callers[3].dependencies.length == 3);
assert(pass.system_callers[4].dependencies.length == 2);
assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id);
assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id);
assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id);
assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id);
assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id);
assert(pass.system_callers[4].dependencies[0].system_id == TestSystem2.system_id);
assert(pass.system_callers[4].dependencies[1].system_id == TestSystem4.system_id);
}

142
tests/bugs.d Normal file
View file

@ -0,0 +1,142 @@
module tests.bugs;
import tests.basic;
import bubel.ecs.core;
import bubel.ecs.manager;
import bubel.ecs.system;
import bubel.ecs.attributes;
version(GNU)
{
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else import std.array : staticArray;
@("Bug0001")
unittest
{
struct Event1
{
mixin ECS.Event;
EntityID id;
}
struct Event2
{
mixin ECS.Event;
}
struct System1
{
mixin ECS.System;
struct EntitiesData
{
CInt[] int_;
}
EntityTemplate* tmpl;
EntityID id;
void onCreate()
{
tmpl = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray);
}
void onDestroy()
{
gEM.freeTemplate(tmpl);
}
void handleEvent(Entity* entity, Event1 event)
{
gEM.removeEntity(event.id);
gEM.sendEvent(entity.id,Event2());
}
void handleEvent(Entity* entity, Event2 event)
{
id = gEM.addEntity(tmpl).id;
}
}
struct System2
{
mixin ECS.System;
struct EntitiesData
{
Entity[] entity;
}
///check if every entity was removed correctly
void onUpdate(EntitiesData data)
{
assert(0);
}
}
struct System3
{
mixin ECS.System;
struct EntitiesData
{
uint length;
Entity[] entity;
}
///remove every entity
void onUpdate(EntitiesData data)
{
foreach(i;0..data.length)gEM.removeEntity(data.entity[i].id);
}
}
gEM.initialize(0);
gEM.beginRegister();
gEM.registerComponent!CInt;
gEM.registerComponent!CFloat;
gEM.registerComponent!CDouble;
gEM.registerComponent!CLong;
gEM.registerComponent!CShort;
gEM.registerComponent!CFlag;
gEM.registerEvent!Event1;
gEM.registerEvent!Event2;
gEM.registerSystem!System1(0);
gEM.registerSystem!System2(-200);
gEM.registerSystem!System3(-200);
gEM.endRegister();
EntityTemplate* tmpl = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray);
EntityID id = gEM.addEntity(tmpl,[CLong(10).ref_, CInt(6).ref_].staticArray).id;
EntityID id2 = gEM.addEntity(tmpl,[CInt(4).ref_].staticArray).id;
gEM.freeTemplate(tmpl);
gEM.commit();
gEM.sendEvent(id2, Event1(id));
gEM.getSystem(System2.system_id).disable();
gEM.begin();
gEM.update();
gEM.end();
gEM.getSystem(System2.system_id).enable();
gEM.begin();
gEM.update();
gEM.end();
gEM.destroy();
}

View file

@ -1,7 +1,7 @@
module tests.id_manager;
import ecs.id_manager;
import ecs.entity;
import bubel.ecs.id_manager;
import bubel.ecs.entity;
unittest
{

53
tests/map.d Normal file
View file

@ -0,0 +1,53 @@
module tests.map;
import bubel.ecs.hash_map;
version(GNU)
{
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else import std.array : staticArray;
@("HashMap")
unittest
{
HashMap!(string, int) map;
assert(map.length == 0);
map.add("asd",1);
assert(map.length == 1);
map.clear();
assert(map.length == 0);
map.add("asd",1);
assert(map.length == 1);
map.reset();
assert(map.length == 0);
map.add("asd",1);
string asd = "asd";
assert(map.isIn("asd"));
assert(map.isIn(asd));
assert(!map.isIn("asdf"));
map.tryRemove("asdf");
map.tryRemove("asd");
assert(map.length == 0);
map.add("asdf",1);
map.add("asd",2);
assert(map["asd"] == 2);
assert(map["asdf"] == 1);
assert(map.length == 2);
map.tryRemove("asdf");
assert(map.length == 1);
map.remove("asd");
assert(map.length == 0);
map.add("asd",1);
map.add("asdwwghe",6);
foreach(k,v;&map.byKeyValue)
{
assert(map[k] == v);
}
}

185
tests/perf.d Normal file
View file

@ -0,0 +1,185 @@
module tests.perf;
import tests.runner;
import bubel.ecs.core;
import bubel.ecs.manager;
import bubel.ecs.entity;
version(GNU)
{
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else import std.array : staticArray;
import core.stdc.stdio;
struct CLong
{
mixin ECS.Component;
alias value this;
long value = 10;
}
struct CShort
{
mixin ECS.Component;
alias value this;
short value = 12;
}
struct CInt
{
mixin ECS.Component;
alias value this;
int value = 10;
}
struct CUInt
{
mixin ECS.Component;
alias value this;
uint value = 12;
}
struct CBig
{
mixin ECS.Component;
uint[32] data;
}
EntityTemplate* tmpl;
void beforeEveryTest()
{
gEM.initialize(0);
gEM.beginRegister();
gEM.registerComponent!CLong;
gEM.registerComponent!CShort;
gEM.registerComponent!CInt;
gEM.registerComponent!CUInt;
gEM.registerComponent!CBig;
gEM.endRegister();
tmpl = null;
}
void afterEveryTest()
{
if(tmpl)gEM.freeTemplate(tmpl);
tmpl = null;
gEM.destroy();
}
void smallTmpl()
{
tmpl = gEM.allocateTemplate([CShort.component_id].staticArray);
}
void bigTmpl()
{
tmpl = gEM.allocateTemplate([CBig.component_id].staticArray);
}
void multiSmallTmpl()
{
tmpl = gEM.allocateTemplate([CShort.component_id, CLong.component_id, CInt.component_id, CUInt.component_id].staticArray);
}
void multiBigTmpl()
{
tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray);
}
@("AddEntities100k1comp2b") @(before, &smallTmpl)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
@("AddEntities100k1comp128b") @(before, &bigTmpl)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
@("AddEntities100k4comp18b") @(before, &multiSmallTmpl)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
@("AddEntities100k4comp144b") @(before, &multiBigTmpl)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
void allocDealloc100k()
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
gEM.commit();
foreach(i; 0..100_000)gEM.removeEntity(EntityID(i + 1,0));
gEM.commit();
}
void smallTmplPreAlloc()
{
tmpl = gEM.allocateTemplate([CShort.component_id].staticArray);
allocDealloc100k();
}
void bigTmplPreAlloc()
{
tmpl = gEM.allocateTemplate([CBig.component_id].staticArray);
allocDealloc100k();
}
void multiSmallTmplPreAlloc()
{
tmpl = gEM.allocateTemplate([CShort.component_id, CLong.component_id, CInt.component_id, CUInt.component_id].staticArray);
allocDealloc100k();
}
void multiBigTmplPreAlloc()
{
tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray);
allocDealloc100k();
}
@("AddEntities100k1comp2bPreAlloc") @(before, &smallTmplPreAlloc)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
@("AddEntities100k1comp128bPreAlloc") @(before, &bigTmplPreAlloc)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
@("AddEntities100k4comp18bPreAlloc") @(before, &multiSmallTmplPreAlloc)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}
@("AddEntities100k4comp144bPreAlloc") @(before, &multiBigTmplPreAlloc)
unittest
{
foreach(i; 0..100_000)gEM.addEntity(tmpl);
}

View file

@ -5,9 +5,22 @@ import core.stdc.stdio;
import core.stdc.string;
import core.sys.posix.setjmp;
import ecs.vector;
import ecs.simple_vector;
import ecs.std;
import bubel.ecs.vector;
import bubel.ecs.simple_vector;
import bubel.ecs.std;
import std.traits;
import tests.time;
version (LDC)
{
import ldc.attributes;
}
else
{
enum optStrategy = 0;
}
enum int ASSERTED = 123;
enum string OUT_FILE = "test_report.xml";
@ -95,6 +108,8 @@ struct TestRunner(Args...)
write(test.name);
write("\" classname=\"");
write(suite.name);
write("\" time=\"");
write(cast(double)test.time*0.000001);
write("\">");
if (test.msg)
{
@ -120,42 +135,65 @@ struct TestRunner(Args...)
write("</testsuites>");
}
@(optStrategy,"none")
void runTests(string[] include = null, string[] exclude = null)
{
foreach (i, module_; Args)
{
TestSuite* suite = &suites[i];
suite.name = module_.stringof;
void function() before;
void function() after;
foreach (index, unittest_; __traits(getUnitTests, module_))
{
enum attributes = __traits(getAttributes, unittest_);
static if (attributes.length != 0)
{
if (include.length > 0)
foreach(attr_id, attr; attributes)
{
bool matched = false;
foreach (str; include)
static if(isFunctionPointer!(attr)){}
else static if(attr == "_tr_before")
{
if (match(str, attributes[0]))
{
matched = true;
break;
}
static assert(attr_id+1 < attributes.length);
enum attr2 = attributes[attr_id + 1];
//static assert(__traits(hasMember, module_, attr2));
//alias func = __traits(getMember, module_, attr2);
//attr2();
before = attr2;
//static assert(is(typeof(__traits(getMember, module_, attr2)) == void function()));
}
foreach (str; exclude)
else
{
if (match(str, attributes[0]))
if (include.length > 0)
{
matched = false;
break;
}
}
bool matched = false;
foreach (str; include)
{
if (match(str, attr))
{
matched = true;
break;
}
}
if (!matched)
{
suite.skipped++;
continue;
foreach (str; exclude)
{
if (match(str, attr))
{
matched = false;
break;
}
}
if (!matched)
{
suite.skipped++;
continue;
}
}
}
}
}
@ -172,8 +210,9 @@ struct TestRunner(Args...)
else
test.name = attributes[0];
static if (__traits(hasMember, module_, "beforeEveryTest"))
static if (__traits(hasMember, module_, "beforeEveryTest") && __traits(compiles, module_.beforeEveryTest()))
module_.beforeEveryTest();
if(before)before();
version(D_BetterC)
{
@ -189,8 +228,10 @@ struct TestRunner(Args...)
}
else
{
long time = Time.getUSecTime();
unittest_();
test.passed = true;
test.time = cast(int)(Time.getUSecTime() - time);
}
}
else
@ -215,7 +256,7 @@ struct TestRunner(Args...)
else
suite.failed++;
suite.tests ~= test;
static if (__traits(hasMember, module_, "afterEveryTest"))
static if (__traits(hasMember, module_, "afterEveryTest") && __traits(compiles, module_.beforeEveryTest()))
module_.afterEveryTest();
}
passed += suite.passed;
@ -281,6 +322,14 @@ struct TestRunner(Args...)
junit.add(cast(ubyte[]) txt);
}
void write(double num)
{
ubyte[40] buffer;
int len;
len = sprintf(cast(char*) buffer.ptr, "%2.8lf", num);
junit.add(buffer[0 .. len]);
}
void write(uint num)
{
ubyte[20] buffer;
@ -304,6 +353,8 @@ version (notBetterC)
version (D_Coverage) extern (C) void dmd_coverDestPath(string path);
}
enum before = "_tr_before";
void extractStrings(ref Vector!string container, string str)
{
uint s = 0;
@ -369,7 +420,7 @@ extern (C) int main(int argc, char** args)
}
}
TestRunner!(tests.runner, tests.id_manager, tests.vector, tests.basic) runner;
TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf, tests.bugs, tests.map) runner;
runner.runTests(include[], exclude[]);

View file

@ -3,12 +3,12 @@ module tests.tests;
import std.experimental.allocator;
import std.experimental.allocator.mallocator;*/
import ecs.entity;
import ecs.events;
import ecs.manager;
import ecs.system;
import ecs.attributes;
import ecs.core;
import bubel.ecs.entity;
import bubel.ecs.events;
import bubel.ecs.manager;
import bubel.ecs.system;
import bubel.ecs.attributes;
import bubel.ecs.core;
version (WebAssembly)
{
@ -714,7 +714,7 @@ else:
//foreach(i; 0..1_000_000)gEM.removeEntity(gEM.addEntity(tmpl).id);
import ecs.std;
import bubel.ecs.std;
EntityID[] idss = Mallocator.makeArray!EntityID(5000); //[5000]
//scope(exit)Mallocator.dispose(idss);

66
tests/time.d Normal file
View file

@ -0,0 +1,66 @@
module tests.time;
version (WebAssembly)
{
alias int time_t;
alias int clockid_t;
enum CLOCK_REALTIME = 0;
struct timespec
{
time_t tv_sec;
int tv_nsec;
}
extern (C) int clock_gettime(clockid_t, timespec*) @nogc nothrow @system;
struct Time
{
static long getUSecTime()
{
time_t time;
timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
//time = spec.tv_sec;
return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; //time / 1000_000;
}
}
}
else version (Windows)
{
import core.stdc.stdio : printf;
import core.sys.windows.windows;
struct Time
{
static long getUSecTime()
{
LARGE_INTEGER time, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&time);
return time.QuadPart / (freq.QuadPart / 1000_000);
}
}
}
else version (Posix)
{
import core.stdc.stdio : printf;
import core.sys.posix.time;
struct Time
{
static long getUSecTime()
{
time_t time;
timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
//time = spec.tv_sec;
return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; //time / 1000_000;
}
}
}

View file

@ -1,7 +1,16 @@
module tests.vector;
import ecs.simple_vector;
//import ecs.vector;
import bubel.ecs.simple_vector;
import bubel.ecs.vector;
version(GNU)
{
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else import std.array : staticArray;
@("simple-vector")
unittest
@ -33,3 +42,38 @@ unittest
assert(vector2[1023] == 'a');
assert(vector2[1024] == 'b');
}
@("Vector")
unittest
{
struct G
{
int a;
}
Vector!G vector;
assert(vector.empty());
vector.add(G(1));
assert(!vector.empty());
vector.clear();
assert(vector.empty());
vector.add(G(1));
assert(!vector.empty());
vector.reset();
assert(vector.empty());
vector.add(G(1));
vector.add([G(2),G(5)].staticArray);
assert(vector.length == 3);
assert(vector.capacity == 1);
Vector!G vector2;
vector2.add([G(1),G(2),G(5)].staticArray);
assert(vector == vector2);
vector2.remove(1);
assert(vector != vector2);
assert(vector2.length == 2);
assert(vector2[1] == G(5));
vector2.add(G(2),1);
assert(vector == vector2);
}