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

View file

@ -1,3 +1,7 @@
default:
artifacts:
expire_in: 1 day
stages:
- build
- test
@ -9,25 +13,18 @@ build_code:
stage: build
image: "registry.gitlab.com/mergul/bubel-ecs:latest"
script:
- mkdir build
- /bin/bash /compile_ecs.sh
- cp artifacts/* build/
- cp -r public build/
artifacts:
expire_in: 1h
paths:
- build
rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: always
- when: always
- binaries
allow_failure: true
test_dmd_debug:
stage: test
image: frolvlad/alpine-glibc
script:
- build/dmd_debug_unittest
- binaries/dmd_debug_unittest
artifacts:
reports:
junit: test_report.xml
@ -35,7 +32,7 @@ test_dmd:
stage: test
image: frolvlad/alpine-glibc
script:
- build/dmd_release_unittest
- binaries/dmd_release_unittest
artifacts:
reports:
junit: test_report.xml
@ -43,7 +40,7 @@ test_dmd_betterC:
stage: test
image: frolvlad/alpine-glibc
script:
- build/dmd_release_unittest_bc
- binaries/dmd_release_unittest_bc
artifacts:
reports:
junit: test_report.xml
@ -56,15 +53,30 @@ coverage_test_dmd:
- build_code
script:
- mkdir reports
- build/dmd_unittest_cov
- binaries/dmd_unittest_cov
after_script:
- bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c
build_wasm:
stage: build
image: "registry.gitlab.com/mergul/bubel-ecs:latest"
script:
- /bin/bash /compile_wasm.sh
- /bin/bash /gen_doc.sh
artifacts:
expire_in: 1h
paths:
- build
rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: always
allow_failure: true
emscripten:
stage: build_emscripten
image: "registry.gitlab.com/mergul/bubel-ecs/emscripten:latest"
dependencies:
- build_code
- build_wasm
script:
- /bin/bash /build.sh
rules:

182
README.md
View file

@ -1,5 +1,183 @@
# Dynamic Entity Component System
# Bubel Entity Component System
[![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master)
[![codecov](https://codecov.io/gl/Mergul/bubel-ecs/branch/master/graph/badge.svg?token=Unm0TJhFoW)](https://codecov.io/gl/Mergul/bubel-ecs)
Entity-Component-System implementation in D language.
BubelECS is Entity-Component-System architectural pattern implementation in D language.
Library aims to delivery fast and flexible architecture for developing games. Library is **@nogc** and **betterC** compatible. WASM is supported thorugh Emscripten.
Project haven't any external dependencies.
BubelECS was tested on Linux, Windows, Android and WASM.
**Currently library is in beta stage so some significant API changes can appear.**
## Design
For core information about Entity-Component-System architectural pattern please read definition described at [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system).
Main design principles are:
* **Data oriented design** - components memory is packed into tight block so iterating over entity components is cache friendly
* **Fast and safe EntityID** - every entity is referenced by its unique ID. Accessing by ID is safe even if entity don'y exist. Access by ID is constant time operation.
* **Multithreading support** - whole library was developed with multithreading in mind. Adding components is thread-friendly so you get usable EntityID as early as possible. Operations like adding or removing components are deferred to end of frame. Dependencies between systems parallel execution are generated automatically.
* **Good logic separation** - system needs information only about components which it's use, there is no relation between systems. Systems can be compiled as multiple separate libraries and used together.
* **Flexible execution model** - system iterate over entities which meet specified conditions. Components can be marked as required, optional or excluded. Systems are exectued in specific order determined by system priority.
* **Builtin events handling** - library has builtin support for event handlig to makes easier communication between different entities.
* **Hot-reload** - hot-reloading for systems should be as easy as possible. In other words library should give functionality to support hot-reload of systems and components with minimal work. **(WIP!)**.
There are some assumptions that should be considered when developing application:
* Iterating over components is fastest way of access data so it's should be main way of making calculations.
* Using of direct access and events should be used very wisely and minimized.
* Direct component access is faster than events, because events must buffer memory and call multiple system handler callbacks.
* Components can be used to marking entities, assingment to systems and changing logic of entity in runtime. Using too much component based marking can lead to memory fragmentation and performence drop.
* Systems give great opporunity to separate game logic. Systems can be enabled and disabled easly in runtime. It's can be used to enabling some debug systems if needed. Additionaly this concept makes game logic changes easier to deal with. If you develop your application wisely it should be trivial to change some core logic by changing only several systems or adding some new systems. Every entity can easily takes some behaviour from different entity type by adding several components.
### Features
* ECS architectural pattern
* Data oriented design
* Safe identifiers (EntityID)
* EntityTemplates
* Basic events handling
* Easy systems ordering
* Automatic multithreaded jobs generating
* Runtime and fast components adding and removing
* Listeners for adding and removing entity components inside systems
* Update passes
* Calling custom callbacks for system entity groups
* betterC compatibility
* Emscripten compatibility
### Planned
* Worlds - every world works as separate environment. Entities don't with entities from different world. Systems and components should be registered for every world separately.
* Hot-reload support - currently only reloading systems (their functions) works. There is possibility to serialize every entity and system, but it's should be provided by library itself with special callbacks and helpers. Additionaly planned is system which helps with taking new EntityID from serialized identifiers.
* External dependencies - ability to provide dependencies between system which isn't related to components.
* Static components - this components will have different memory model and can be accessed only directly. It will be slower to access but won't trigger memory copy when component is added. It should fix problem with big components which isn't needed by systems iteration callbacks or is required rarely.
* Better EventManager - there are several optimization and improvements that can be added in the future.
* More demos and examples - demo appliaction is very basic now, but in future more minigames and sanbox mode (opportunity to mix many components and systems) are planned.
* C API - in highly depends on amount of work required. Makes possible to use library from different languages.
* GPU compute - idea in draft stage. Special components and systems whose data wolud be on GPU memory.
* More smaller improvements...
For more information about design and usage feel free to read [documentation](https://mergul.gitlab.io/bubel-ecs/ecs.html)**(WIP)** and [WIKI](https://gitlab.com/Mergul/bubel-ecs/-/wikis/home).
## Build Instructions
To build library you needs recent D compiler and optionally Emscripten (with python) to build web version. Currenlty tested are: LDC, DMD and GDC. \
Supported build systems are DUB and Meson.
##### DUB
```shell
#available configurations: library, dynlib, library-betterC, dynlib-betterC
dub build -c library -b release
```
##### Meson
```shell
#use '-DbetterC=true ' to build betterC code
meson build . #add '--buildtype=release' to build release code
cd build
ninja
```
##### Emscripten
```shell
python compile_wasm.py -opt
```
##### Documentation
```shell
adrdox -i source/bubel/ecs/ -o docs/ -s skeleton.html
```
For more detailed build options please check documentation for used build system.
## Demos
Repository contain demo application. You can check demo [online](https://mergul.gitlab.io/bubel-ecs/ecs_demo.html) or build it form source code using DUB. \
Online demo support multithreading and page tries to check if client support WASM multithreading or not and loads properly JS and WASM code. It was tested on Chrome, Firefox, Opera, Brave on Linux and Android. On firefox there is problem with multithreaded version so if demo don't works please try to disable shared_memory in browser flags.
## Code example
```d
struct Position
{
mixin ECS.Components; //makes struct component
float x;
float y;
}
struct Velocity
{
mixin ECS.Components;
//default values works
float x = 0.1;
float y = 1;
}
struct StaticFlag
{
mixin ECS.Components;
}
struct UpdateSystem
{
mixin ECS.System; //makes struct system
ECS.ExcludedComponents!(StaticFlag); //prevents static entities from update
struct EntitiesData
{
int length; //entities count
@readonly Entity[] entities; //entities arrays, entity contain ID only
Position[] positions; //positions array
@readonly Velocity[] velocities; //veocities array, readonly (Multithreading tag)
}
void onUpdate(EntitiesData data) //update callback, called multiple times per frame for every entities types
{
foreach(i; 0..data.length) //iterate over entities
{
data.positions[i].x += data.velocities[i].x * dt;
data.positions[i].y += data.velocities[i].y * dt;
}
}
}
void main()
{
manager.beginRegister();
//register components
manager.registerComponent!Position;
manager.registerComponent!Velocity;
manager.registerComponent!StaticFlag;
//register system with priority 0
manager.registerSystem!UpdateSystem(0);
manager.endRegister();
//allocate template
EntityTemplate* tmpl = manager.allocateEmplate([Velocity.component_id, Position.component_id].staticArray);
scope (exit) manager.freeTemplate(tmpl);
//gets pointer to template component data
Position* position = tmpl.getComponent!Position;
foreach(i; 0 .. 100)
{
position.x = i % 10;
position.y = i / 10;
manager.addEntity(tmpl);
}
manager.begin(); //start frame
manager.update(); //update all systems, there onUpdate callbacks are called
manager.end(); //end frame
}
```
## Links
Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \
Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html

View file

@ -1,3 +1,10 @@
ignore:
- "tests/*"
- "**/traits*"
- "**/traits*"
coverage:
status:
project:
default:
threshold: 5
patch: off

View file

@ -3,6 +3,31 @@ precision mediump float;
precision lowp sampler2D;
precision lowp samplerCube;
#ifdef GLES
#define TEX(x,y) texture2D(x,y)
#if __VERSION__ >290
#define M_IN in mediump
#define L_IN in lowp
#else
#define M_IN varying mediump
#define L_IN varying lowp
#endif
#else
#define TEX(x,y) texture(x,y)
#if __VERSION__ > 320
#define M_IN in
#define L_IN in
#else
#define M_IN varying
#define L_IN varying
#endif
#endif
M_IN vec2 uv;
M_IN vec4 color;
/*
#ifdef GLES
#if __VERSION__ >290
in mediump vec2 uv;
@ -15,7 +40,7 @@ precision lowp samplerCube;
#else
varying vec2 uv;
#endif
#endif
#endif*/
//layout(binding = 0)uniform sampler2D tex;
@ -23,20 +48,8 @@ uniform sampler2D tex;
//layout(location = 0) out vec4 outColor;
void main() {
#ifdef GLES
#if __VERSION__ >290
gl_FragColor = texture(tex,uv);
#else
gl_FragColor = texture2D(tex,uv);
#endif
#else
#if __VERSION__ > 320
gl_FragColor = texture(tex,uv);
#else
gl_FragColor = texture2D(tex,uv);
#endif
#endif
void main()
{
gl_FragColor = TEX(tex,uv) * color;
if(gl_FragColor.a < 0.01)discard;
}

View file

@ -2,12 +2,37 @@ precision highp float;
precision highp int;
precision lowp sampler2D;
precision lowp samplerCube;
#ifdef GLES
#if __VERSION__ >290
layout(location = 0) uniform vec4 matrix_1;
layout(location = 1) uniform vec4 matrix_2;
layout(location = 2) uniform vec4 uv_transform;
#define LOC(x) layout(location = x)
#define ATT in
#define M_OUT out mediump
#define L_OUT out lowp
#else
#define LOC(x)
#define ATT attribute
#define M_OUT varying mediump
#define L_OUT varying lowp
#endif
#else
#if __VERSION__ > 320
#define LOC(x) layout(location = x)
#define ATT in
#define M_OUT out
#define L_OUT out
#else
#define LOC(x)
#define ATT attribute
#define M_OUT varying
#define L_OUT varying
#endif
#endif
/*
#ifdef GLES
#if __VERSION__ >290
uniform vec4 matrix_1;
uniform vec4 matrix_2;
uniform vec4 uv_transform;
layout(location = 0) in vec2 positions;
layout(location = 1) in vec2 tex_coords;
@ -43,13 +68,39 @@ precision lowp samplerCube;
varying vec2 uv;
#endif
#endif*/
#define VBO_BATCH 1
M_OUT vec2 uv;
L_OUT vec4 color;
LOC(0) ATT vec2 positions;
LOC(1) ATT vec2 tex_coords;
#ifdef VBO_BATCH
LOC(2) ATT float depth;
LOC(3) ATT vec4 vcolor;
#else
uniform vec4 matrix_1;
uniform vec4 matrix_2;
uniform vec4 uv_transform;
uniform vec4 vcolor;
float depth = matrix_2.z;
#endif
void main() {
#ifdef VBO_BATCH
vec3 position = vec3(positions*4.0,1.0);
uv = tex_coords;
#else
vec3 position = mat3(matrix_1.x,matrix_1.y,0,matrix_1.z,matrix_1.w,0,matrix_2.xy,1.0) * vec3(positions,1.0);
uv = tex_coords * uv_transform.zw + uv_transform.xy;
#endif
vec3 position = mat3(matrix_1.x,matrix_1.y,0,matrix_1.z,matrix_1.w,0,matrix_2.xy,1) * vec3(positions,1.0);
uv = tex_coords * uv_transform.zw + uv_transform.xy;
gl_Position = vec4(position.xy,0,1.0);
color = vcolor * 2.0;
gl_Position = vec4(position.xy,depth,1.0);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before After
Before After

View file

@ -18,6 +18,9 @@
"libs-windows-x86_64": ["libs/windows/x64/SDL2","libs/windows/x64/SDL2_Image","libs/windows/x64/cimgui"],
"libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"],
"lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"],
"dflags-ldc" : [
"--ffast-math"
],
"configurations" : [
{
"name" : "default",

View file

@ -1,6 +1,6 @@
module mmutils.thread_pool;
import ecs.atomic;
import bubel.ecs.atomic;
//import core.stdc.stdio;
//import core.stdc.stdlib : free, malloc, realloc;
@ -16,6 +16,7 @@ version (Posix)version = MM_USE_POSIX_THREADS;
version (WebAssembly)
{
version = MM_NO_LOGS;
extern(C) struct FILE
{
@ -32,7 +33,7 @@ else
version (D_BetterC)
{
import ecs.std;
import bubel.ecs.std;
extern (C) __gshared int _d_eh_personality(int, int, size_t, void*, void*)
{
return 0;
@ -181,6 +182,12 @@ void instructionPause()
__builtin_ia32_pause();
}
else version(GNU)
{
import gcc.builtins;
__builtin_ia32_pause();
}
else version (DigitalMars)
{
asm
@ -188,7 +195,6 @@ void instructionPause()
rep;
nop;
}
}
else
{
@ -799,6 +805,7 @@ struct ThreadPool
alias FlushLogsDelegaste = void delegate(ThreadData* threadData, JobLog[] logs); /// Type of delegate to flush logs
FlushLogsDelegaste onFlushLogs; /// User custom delegate to flush logs, if overriden defaultFlushLogs will be used. Can be sset after initialize() call
int logsCacheNum; /// Number of log cache entries. Should be set before setThreadsNum is called
int tryWaitCount = 2000; ///Number of times which tryWait are called before timedWait call. Higher value sets better response but takes CPU time even if there are no jobs.
private:
ThreadData*[gMaxThreadsNum] threadsData; /// Data for threads
align(64) shared int threadsNum; /// Number of threads currentlu accepting jobs
@ -808,6 +815,46 @@ private:
JobData[4] resumeJobs; /// Dummu jobs to resume some thread
public:
static int getCPUCoresCount()
{
version(Windows)
{
import core.sys.windows.winbase : SYSTEM_INFO, GetSystemInfo;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
}
else version (linux)
{
version(D_BetterC)
{
import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
return cast(int)sysconf(_SC_NPROCESSORS_ONLN);
}
else
{
import core.sys.linux.sched : CPU_COUNT, cpu_set_t, sched_getaffinity;
import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
cpu_set_t set = void;
if (sched_getaffinity(0, cpu_set_t.sizeof, &set) == 0)
{
int count = CPU_COUNT(&set);
if (count > 0)
return cast(uint) count;
}
return cast(int)sysconf(_SC_NPROCESSORS_ONLN);
}
}
else version(Posix)
{
import core.sys.posix.unistd;
return cast(int)sysconf(_SC_NPROCESSORS_ONLN);
}
else return -1;
}
int jobsDoneCount()
{
int sum;
@ -1141,17 +1188,19 @@ public:
foreach (ref log; logs)
{
size += log.name.length; // size of name
size += log.name.length + 1; // size of name
}
char* buffer = cast(char*) malloc(size);
foreach (ref log; logs)
{
char[100] name_buffer;
name_buffer[0 .. log.name.length] = log.name;
name_buffer[log.name.length] = 0;
size_t charWritten = snprintf(buffer + used, size - used,
`{"name":"%s", "pid":1, "tid":%lld, "ph":"X", "ts":%lld, "dur":%lld }, %s`,
log.name.ptr, threadData.threadId + 1, log.time, log.duration, "\n".ptr);
name_buffer.ptr, threadData.threadId + 1, log.time, log.duration, "\n".ptr);
used += charWritten;
}
@ -1447,7 +1496,21 @@ private void threadFunc(ThreadData* threadData)
if (data is null)
{
// Thread does not have own job and can not steal it, so wait for a job
bool ok = threadData.semaphore.timedWait(1_000 + !acceptJobs * 10_000);
int tryWait = 0;
//bool ok = threadData.semaphore.timedWait(1_000 + !acceptJobs * 10_000);
bool ok = true;
while(!threadData.semaphore.tryWait())
{
tryWait++;
if(tryWait>threadPool.tryWaitCount)
{
ok = false;
break;
}
static foreach(i;0..10)instructionPause();
}
if(!ok)ok = threadData.semaphore.timedWait(1_000 + !acceptJobs * 10_000);
if (ok)
{

View file

@ -6,9 +6,9 @@ import cimgui.cimgui;
import game_core.job_updater;
import ecs.manager;
import ecs.core;
import ecs.std;
import bubel.ecs.manager;
import bubel.ecs.core;
import bubel.ecs.std;
import ecs_utils.gfx.renderer;
import ecs_utils.imgui_bind;
@ -58,6 +58,7 @@ struct Launcher
uint style = 3;
uint entities_count;
bool multithreading;
int threads = 1;
ulong timer_freq;
double delta_time;
uint fps;
@ -95,6 +96,7 @@ struct Launcher
void switchDemo(void function() start, bool function() loop, void function() end, void function(SDL_Event*) event, void function(vec2, Tool, int) tool, const (char)* tips)
{
gui_manager.clear();
//launcher.ent
if(this.end)this.end();
@ -102,6 +104,14 @@ struct Launcher
manager.update("clean");
manager.end();
foreach(ref system; manager.systems)
{
if(system.id != CountSystem.system_id && system.id != CleanSystem.system_id)system.disable();
}
/*launcher.manager.getSystem(CountSystem.system_id).enable();
launcher.manager.getSystem(CleanSystem.system_id).enable();//*/
if(start)start();
this.loop = loop;
this.end = end;
@ -259,7 +269,6 @@ void mainLoop(void* arg)
launcher.repeat_time -= range;
launcher.tool((launcher.mouse.position*launcher.scalling)-launcher.render_position, launcher.used_tool, launcher.tool_size);
}
}
version(WebAssembly)
@ -316,6 +325,22 @@ void mainLoop(void* arg)
if(igMenuItemBool("Multithreading", null, launcher.multithreading, true))
{
launcher.multithreading = !launcher.multithreading;
if(launcher.multithreading)
{
launcher.job_updater.pool.setThreadsNum(launcher.threads);
}
else
{
launcher.job_updater.pool.setThreadsNum(1);
}
}
igSetNextItemWidth(0);
igLabelText("Threads:",null);
igSameLine(0,4);
if(igSliderInt("##Threads",&launcher.threads, 1, 32, null))//"Multithreading", null, launcher.multithreading, true))
{
launcher.job_updater.pool.setThreadsNum(launcher.threads);
//launcher.threads = !launcher.multithreading;
}
if(igBeginMenu("Show",true))
{
@ -439,7 +464,7 @@ void mainLoop(void* arg)
if(launcher.show_demo_wnd)
{
igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, 30), ImGuiCond_Once, ImVec2(0,0));
igSetNextWindowSize(ImVec2(250, 250), ImGuiCond_Once);
igSetNextWindowSize(ImVec2(250, 500), ImGuiCond_Once);
if(igBegin("Demo",&launcher.show_demo_wnd,0))
{
ImDrawList* draw_list = igGetWindowDrawList();
@ -539,11 +564,14 @@ void mainLoop(void* arg)
launcher.renderer.clear();
double loop_time = launcher.getTime();
launcher.job_updater.pool.tryWaitCount = 10000;
if(launcher.loop && !launcher.loop())
{
quit();
*cast(bool*)arg = false;
}
launcher.job_updater.pool.tryWaitCount = 0;
loop_time = launcher.getTime() - loop_time;
double draw_time = launcher.getTime();
@ -686,10 +714,10 @@ int main(int argc, char** argv)
setStyle(3);
launcher.job_updater = Mallocator.make!ECSJobUpdater(12);
launcher.job_updater = Mallocator.make!ECSJobUpdater(1);
//launcher.job_updater.onCreate();
EntityManager.initialize(12);
EntityManager.initialize(32, 1<<16);
launcher.manager = EntityManager.instance;
//launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID;
@ -709,10 +737,16 @@ int main(int argc, char** argv)
launcher.renderer.initialize();
import mmutils.thread_pool : ThreadPool;
launcher.threads = ThreadPool.getCPUCoresCount();
if(launcher.threads < 2)launcher.threads = 2;
launcher.gui_manager = Mallocator.make!GUIManager();
{
import demos.simple;
import demos.space_invaders;
// launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,&spaceInvadersTool,SpaceInvaders.tips);
launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips);
}
@ -741,6 +775,8 @@ int main(int argc, char** argv)
}
}
EntityManager.destroy();
return 0;
}
@ -785,7 +821,15 @@ void loadGFX()
GfxConfig.materials[0].compile();
GfxConfig.materials[0].bindAttribLocation("positions",0);
GfxConfig.materials[0].bindAttribLocation("tex_coords",1);
GfxConfig.materials[0].bindAttribLocation("depth",2);
GfxConfig.materials[0].bindAttribLocation("vcolor",3);
GfxConfig.materials[0].link();
/* import std.stdio;
writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions"));
writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords"));
writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth"));
writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/
GfxConfig.materials[0].data.uniforms = Mallocator.makeArray!(Material.Uniform)(3);
GfxConfig.materials[0].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0);

View file

@ -6,11 +6,11 @@ import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import bubel.ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;

View file

@ -1,19 +0,0 @@
module demos.chipmunk2d;
import app;
import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
import ecs_utils.utils;
extern(C):

View file

@ -1,19 +0,0 @@
module demos.events;
import app;
import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
import ecs_utils.utils;
extern(C):

View file

@ -1,19 +0,0 @@
module demos.flag_component;
import app;
import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
import ecs_utils.utils;
extern(C):

View file

@ -6,11 +6,11 @@ import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import bubel.ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;

View file

@ -6,11 +6,11 @@ import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import bubel.ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
@ -47,22 +47,27 @@ struct CTexture
struct DrawSystem
{
mixin ECS.System!1;
mixin ECS.System!32;
struct EntitiesData
{
uint length;
//uint thread_id;
uint job_id;
@readonly CTexture[] textures;
@readonly CLocation[] locations;
}
void onUpdate(EntitiesData data)
{
if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached
foreach(i; 0..data.length)
{
launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), 0, 0 , 0);
launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), cast(ushort)(data.locations[i].y), 0x80808080, 0, 0, 0, data.job_id);
// launcher.renderer.draw(data.textures[i].tex, data.locations[i].location, vec2(16,16), vec4(0,0,1,1), 0, 0x80808080, 0, 0, 0, data.job_id);
//draw(renderer, data.textures[i].tex, data.locations[i], vec2(32,32), vec4(0,0,1,1));
}
//if(data.thread_id == 0)launcher.renderer.pushData();
}
}

View file

@ -6,18 +6,18 @@ import bindbc.sdl;
import cimgui.cimgui;
import ecs.attributes;
import ecs.core;
import ecs.entity;
import ecs.manager;
import ecs.std;
import ecs.vector;
import bubel.ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import bubel.ecs.vector;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
import ecs_utils.utils;
import std.array : staticArray;
//import std.array : staticArray;
enum float px = 1.0/512.0;
@ -30,8 +30,9 @@ struct MapElement
empty = 0,
apple = 1,
wall = 2,
snake = 3,
snake_head_up = 5,
/* snake_head_up = 5,
snake_head_down = 6,
snake_head_left = 7,
snake_head_right = 8,
@ -44,13 +45,31 @@ struct MapElement
snake_turn_rd = 15,
snake_turn_ru = 16,
snake_vertical = 17,
snake_horizontal = 18
snake_horizontal = 18*/
}
Type type;
EntityID id;
}
enum SnakePart : ubyte
{
head_up = 0,
head_down = 1,
head_left = 2,
head_right = 3,
tail_up = 4,
tail_down = 5,
tail_left = 6,
tail_right = 7,
turn_ld = 8,
turn_lu = 9,
turn_rd = 10,
turn_ru = 11,
vertical = 12,
horizontal = 13
}
struct Snake
{
__gshared const (char)* tips = "Use \"WASD\" keys to move.";
@ -71,6 +90,16 @@ struct Snake
MapElement[map_size * map_size] map;
~this() @nogc nothrow
{
if(snake_destroy_particle_frames)Mallocator.dispose(snake_destroy_particle_frames);
if(smoke_frames)Mallocator.dispose(smoke_frames);
if(apple_tmpl)launcher.manager.freeTemplate(apple_tmpl);
if(snake_tmpl)launcher.manager.freeTemplate(snake_tmpl);
if(snake_destroy_particle)launcher.manager.freeTemplate(snake_destroy_particle);
texture.destory();
}
MapElement element(ivec2 pos)
{
uint index = pos.x + pos.y * map_size;
@ -100,43 +129,11 @@ struct Snake
}
if(base_pos.x == random_pos.x && base_pos.y == random_pos.y)return;
}
CILocation* location = apple_tmpl.getComponent!CILocation;
*location = random_pos;
Entity* apple = launcher.manager.addEntity(apple_tmpl);
//CILocation* location = apple_tmpl.getComponent!CILocation;
//*location = random_pos;
//Entity* apple =
launcher.manager.addEntity(apple_tmpl,[CILocation(random_pos).ref_].staticArray);
}
void drawMap()
{
foreach(x; 0 .. map_size)
{
foreach(y; 0 .. map_size)
{
switch(element(ivec2(x,y)).type)
{
case MapElement.Type.apple:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_head_up:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(48*px,112*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_head_down:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(48*px,144*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_head_left:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,128*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_head_right:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(32*px,128*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_tail_up:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(16*px,112*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_tail_down:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,112*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_tail_left:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(32*px,112*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_tail_right:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,144*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_turn_ld:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(64*px,128*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_turn_lu:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(32*px,144*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_turn_rd:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(16*px,144*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_turn_ru:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(64*px,112*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_vertical:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(16*px,128*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.snake_horizontal:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(48*px,128*px,16*px,16*px), 0, 0 , 0);break;
case MapElement.Type.wall:launcher.renderer.draw(texture, vec2(x*16,y*16), vec2(16,16), vec4(0,0,1,1), 0, 0 , 0);break;
default:break;
}
}
}
}
}
struct Animation
@ -184,6 +181,7 @@ struct CSnake
mixin ECS.Component;
struct Parts
{
uint length = 0;
@ -217,6 +215,7 @@ struct CSnake
}
Parts parts;
CMovement.Direction direction;
}
struct CApple
@ -287,7 +286,7 @@ struct ParticleSystem
{
uint length;
@readonly Entity[] entities;
@readonly CParticle[] particle;
CParticle[] particle;
}
void onUpdate(EntitiesData data)
@ -358,7 +357,7 @@ struct AnimationRenderSystem
{
foreach(i;0..data.length)
{
launcher.renderer.draw(snake.texture, cast(vec2)cast(ivec2)data.location[i].location, vec2(16,16), data.animation[i].frames[cast(int)(data.animation[i].time)], 0, 0 , 0);
launcher.renderer.draw(snake.texture, cast(vec2)cast(ivec2)data.location[i].location, vec2(16,16), data.animation[i].frames[cast(int)(data.animation[i].time)], -1, 0x80808080);
}
}
}
@ -368,8 +367,8 @@ struct MoveSystem
mixin ECS.System!64;
EntityTemplate* destroy_template;
CLocation* destroy_location;
CParticleVector* destroy_vector;
//CLocation* destroy_location;
//CParticleVector* destroy_vector;
struct EntitiesData
{
@ -383,8 +382,8 @@ struct MoveSystem
void setTemplates()
{
destroy_template = snake.snake_destroy_particle;
destroy_location = destroy_template.getComponent!CLocation;
destroy_vector = destroy_template.getComponent!CParticleVector;
//destroy_location = destroy_template.getComponent!CLocation;
//destroy_vector = destroy_template.getComponent!CParticleVector;
}
void moveLocation(ref CILocation location, CMovement.Direction direction)
@ -424,133 +423,56 @@ struct MoveSystem
else .snake.element(MapElement(),location);
}
static CMovement.Direction getDirection(ivec2 p1, ivec2 p2)
{
if(p1.x - p2.x == -1)return CMovement.direction.right;
else if(p1.x - p2.x == 1)return CMovement.direction.left;
else if(p1.y - p2.y == -1)return CMovement.direction.up;
else if(p1.y - p2.y == 1)return CMovement.direction.down;
else if(p1.x - p2.x > 1)return CMovement.direction.right;
else if(p1.x - p2.x < -1)return CMovement.direction.left;
else if(p1.y - p2.y > 1)return CMovement.direction.up;
else return CMovement.direction.down;
}
static MapElement.Type snakePart(ivec2 p1, ivec2 p2, ivec2 p3)
{
CMovement.Direction direction = getDirection(p1, p2);
CMovement.Direction direction2 = getDirection(p1, p3);
uint case_ = direction*4 + direction2;
final switch(case_)
{
case 0:return MapElement.Type.snake_horizontal;
case 1:return MapElement.Type.snake_horizontal;
case 2:return MapElement.Type.snake_turn_lu;
case 3:return MapElement.Type.snake_turn_ru;
case 4:return MapElement.Type.snake_horizontal;
case 5:return MapElement.Type.snake_horizontal;
case 6:return MapElement.Type.snake_turn_ld;
case 7:return MapElement.Type.snake_turn_rd;
case 8:return MapElement.Type.snake_turn_lu;
case 9:return MapElement.Type.snake_turn_ld;
case 10:return MapElement.Type.snake_vertical;
case 11:return MapElement.Type.snake_vertical;
case 12:return MapElement.Type.snake_turn_ru;
case 13:return MapElement.Type.snake_turn_rd;
case 14:return MapElement.Type.snake_vertical;
case 15:return MapElement.Type.snake_vertical;
}
}
static MapElement.Type snakeTail(ivec2 p1, ivec2 p2)
{
CMovement.Direction direction = getDirection(p1, p2);
final switch(direction)
{
case CMovement.Direction.up:return MapElement.Type.snake_tail_up;
case CMovement.Direction.down:return MapElement.Type.snake_tail_down;
case CMovement.Direction.left:return MapElement.Type.snake_tail_left;
case CMovement.Direction.right:return MapElement.Type.snake_tail_right;
}
}
void onUpdate(EntitiesData data)
{
if(data.snakes)
{
foreach(i; 0..data.length)
{
data.snakes[i].direction = data.movement[i].direction;
ivec2 new_location = data.location[i];
moveLocation(data.location[i], data.movement[i].direction);
final switch(snake.element(data.location[i].location).type)
{
case MapElement.Type.snake_head_up:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_head_down:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_head_left:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_head_right:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_tail_up:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_tail_down:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_tail_left:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_tail_right:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_turn_ld:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_turn_lu:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_turn_rd:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_turn_ru:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_vertical:goto case(MapElement.Type.snake_horizontal);
case MapElement.Type.snake_horizontal:
foreach(ivec2 loc; data.snakes[i].parts)
case MapElement.Type.snake:
foreach(loc; data.snakes[i].parts)
{
destroy_location.x = loc.x * 16;
destroy_location.y = loc.y * 16;
//destroy_location.x = loc.x * 16;
//destroy_location.y = loc.y * 16;
snake.element(MapElement(MapElement.Type.empty, EntityID()),loc);
launcher.manager.addEntity(snake.snake_destroy_particle);
launcher.manager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(loc * 16)).ref_].staticArray);
CLocation destroy_location;
foreach(j;0..10)
{
destroy_location.x = loc.x * 16 + randomf() * 8 - 4;
destroy_location.y = loc.y * 16 + randomf() * 8 - 4;
destroy_vector.velocity = vec2(randomf(),randomf())*0.4-0.2;
//destroy_vector.velocity = vec2(randomf(),randomf())*0.4-0.2;
snake.element(MapElement(MapElement.Type.empty, EntityID()),loc);
launcher.manager.addEntity(snake.snake_destroy_particle);
launcher.manager.addEntity(snake.snake_destroy_particle, [destroy_location.ref_, CParticleVector(vec2(randomf(),randomf())*0.4-0.2).ref_].staticArray);
}
}
destroy_location.x = new_location.x * 16;
destroy_location.y = new_location.y * 16;
//destroy_location.x = new_location.x * 16;
//destroy_location.y = new_location.y * 16;
snake.element(MapElement(MapElement.Type.empty, EntityID()),new_location);
launcher.manager.addEntity(snake.snake_destroy_particle);
launcher.manager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(new_location * 16)).ref_].staticArray);
launcher.manager.removeEntity(data.entities[i].id);
break;
case MapElement.Type.wall:break;
//launcher.manager.removeEntity(data.entities[i].id);
//break;
case MapElement.Type.empty:
moveSnake(data.snakes[i], new_location);
final switch(data.movement[i].direction)
{
case CMovement.Direction.up:
snake.element(MapElement(MapElement.Type.snake_head_up, data.entities[i].id),data.location[i].location);
break;
case CMovement.Direction.right:
snake.element(MapElement(MapElement.Type.snake_head_right, data.entities[i].id),data.location[i].location);
break;
case CMovement.Direction.down:
snake.element(MapElement(MapElement.Type.snake_head_down, data.entities[i].id),data.location[i].location);
break;
case CMovement.Direction.left:
snake.element(MapElement(MapElement.Type.snake_head_left, data.entities[i].id),data.location[i].location);
break;
}
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location);
if(data.snakes[i].parts.length > 1)
{
MapElement.Type elem_type = snakePart(data.snakes[i].parts[$-1],data.location[i],data.snakes[i].parts[$-2]);
snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[$-1]);
elem_type = snakeTail(data.snakes[i].parts[1], data.snakes[i].parts[0]);
snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[0]);
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]);
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]);
}
else if(data.snakes[i].parts.length == 1)
{
MapElement.Type elem_type = snakeTail(data.location[i], data.snakes[i].parts[0]);
snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[0]);
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]);
}
break;
case MapElement.Type.apple:
@ -559,29 +481,13 @@ struct MoveSystem
if(data.snakes[i].parts.length > 1)
{
MapElement.Type elem_type = snakePart(data.snakes[i].parts[$-1],data.location[i],data.snakes[i].parts[$-2]);
snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[$-1]);
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]);
}
else if(data.snakes[i].parts.length == 1)
{
MapElement.Type elem_type = snakeTail(data.location[i], new_location);
snake.element(MapElement(elem_type, data.entities[i].id),new_location);
}
final switch(data.movement[i].direction)
{
case CMovement.Direction.up:
snake.element(MapElement(MapElement.Type.snake_head_up, data.entities[i].id),data.location[i].location);
break;
case CMovement.Direction.right:
snake.element(MapElement(MapElement.Type.snake_head_right, data.entities[i].id),data.location[i].location);
break;
case CMovement.Direction.down:
snake.element(MapElement(MapElement.Type.snake_head_down, data.entities[i].id),data.location[i].location);
break;
case CMovement.Direction.left:
snake.element(MapElement(MapElement.Type.snake_head_left, data.entities[i].id),data.location[i].location);
break;
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),new_location);
}
snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location);
snake.addApple();
break;
}
@ -593,10 +499,10 @@ struct MoveSystem
{
final switch(data.movement[i].direction)
{
case CMovement.Direction.down:data.location[i].location.y -= 1;break;
case CMovement.Direction.up:data.location[i].location.y += 1;break;
case CMovement.Direction.left:data.location[i].location.x -= 1;break;
case CMovement.Direction.right:data.location[i].location.x += 1;break;
case CMovement.Direction.down:data.location[i].y -= 1;break;
case CMovement.Direction.up:data.location[i].y += 1;break;
case CMovement.Direction.left:data.location[i].x -= 1;break;
case CMovement.Direction.right:data.location[i].x += 1;break;
}
}
}
@ -699,6 +605,132 @@ struct FixSnakeDirectionSystem
}
}
struct DrawAppleSystem
{
mixin ECS.System!1;
struct EntitiesData
{
uint length;
@readonly CILocation[] location;
const (CApple)[] apple;
}
void onUpdate(EntitiesData data)
{
foreach(i; 0..data.location.length)
{
launcher.renderer.draw(snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0x80808080, 0);
}
}
}
struct DrawSnakeSystem
{
mixin ECS.System!1;
struct EntitiesData
{
uint length;
@readonly CILocation[] location;
const (CSnake)[] snake;
}
static CMovement.Direction getDirection(ivec2 p1, ivec2 p2)
{
if(p1.x - p2.x == -1)return CMovement.direction.right;
else if(p1.x - p2.x == 1)return CMovement.direction.left;
else if(p1.y - p2.y == -1)return CMovement.direction.up;
else if(p1.y - p2.y == 1)return CMovement.direction.down;
else if(p1.x - p2.x > 1)return CMovement.direction.right;
else if(p1.x - p2.x < -1)return CMovement.direction.left;
else if(p1.y - p2.y > 1)return CMovement.direction.up;
else return CMovement.direction.down;
}
static SnakePart snakePart(ivec2 p1, ivec2 p2, ivec2 p3)
{
CMovement.Direction direction = getDirection(p1, p2);
CMovement.Direction direction2 = getDirection(p1, p3);
uint case_ = direction*4 + direction2;
final switch(case_)
{
case 0:return SnakePart.horizontal;
case 1:return SnakePart.horizontal;
case 2:return SnakePart.turn_lu;
case 3:return SnakePart.turn_ru;
case 4:return SnakePart.horizontal;
case 5:return SnakePart.horizontal;
case 6:return SnakePart.turn_ld;
case 7:return SnakePart.turn_rd;
case 8:return SnakePart.turn_lu;
case 9:return SnakePart.turn_ld;
case 10:return SnakePart.vertical;
case 11:return SnakePart.vertical;
case 12:return SnakePart.turn_ru;
case 13:return SnakePart.turn_rd;
case 14:return SnakePart.vertical;
case 15:return SnakePart.vertical;
}
}
static SnakePart snakeTail(ivec2 p1, ivec2 p2)
{
CMovement.Direction direction = getDirection(p1, p2);
final switch(direction)
{
case CMovement.Direction.up:return SnakePart.tail_up;
case CMovement.Direction.down:return SnakePart.tail_down;
case CMovement.Direction.left:return SnakePart.tail_left;
case CMovement.Direction.right:return SnakePart.tail_right;
}
}
static void drawElement(ivec2 loc, SnakePart part)
{
final switch(cast(ubyte)part)
{
case SnakePart.tail_up:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,112,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.tail_down:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(0,112,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.tail_left:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(32,112,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.tail_right:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(0,144,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.turn_ld:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(64,128,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.turn_lu:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(32,144,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.turn_rd:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,144,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.turn_ru:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(64,112,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.vertical:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(16,128,16,16)*px, 0, 0x80808080, 0);break;
case SnakePart.horizontal:launcher.renderer.draw(.snake.texture, cast(vec2)loc, vec2(16,16), vec4(48,128,16,16)*px, 0, 0x80808080, 0);break;
}
}
void onUpdate(EntitiesData data)
{
foreach(i; 0..data.length)
{
const (CSnake)* snake = &data.snake[i];
scope vec2 loc = cast(vec2)(data.location[i].location * 16);
final switch(snake.direction)
{
case CMovement.Direction.up:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(48,112,16,16)*px, 0, 0x80808080, 0);break;
case CMovement.Direction.down:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(48,144,16,16)*px, 0, 0x80808080, 0);break;
case CMovement.Direction.left:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,128,16,16)*px, 0, 0x80808080, 0);break;
case CMovement.Direction.right:launcher.renderer.draw(.snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(32,128,16,16)*px, 0, 0x80808080, 0);break;
}
if(snake.parts.length >1)
{
foreach(j;1..snake.parts.length - 1)drawElement(snake.parts[j]*16, snakePart(snake.parts[j], snake.parts[j+1], snake.parts[j-1]));
drawElement(snake.parts[$-1]*16, snakePart(snake.parts[$-1], data.location[i], snake.parts[$-2]));
drawElement(snake.parts[0]*16, snakeTail(snake.parts[1], snake.parts[0]));
}
else if(snake.parts.length == 1)
{
drawElement(snake.parts[0]*16, snakeTail(data.location[i], snake.parts[0]));
}
}
}
}
struct CleanSystem
{
mixin ECS.System!64;
@ -749,6 +781,8 @@ void snakeStart()
launcher.manager.registerSystem!AnimationSystem(-1);
launcher.manager.registerSystem!ParticleSystem(-1);
launcher.manager.registerSystem!ParticleMovementSystem(-1);
launcher.manager.registerSystem!DrawAppleSystem(99);
launcher.manager.registerSystem!DrawSnakeSystem(101);
launcher.manager.endRegister();
@ -765,9 +799,9 @@ void snakeStart()
{
ushort[4] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id];
snake.snake_tmpl = launcher.manager.allocateTemplate(components);
CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation;
loc_comp.location = ivec2(2,2);
launcher.manager.addEntity(snake.snake_tmpl);
//CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation;
//*loc_comp = ivec2(2,2);
launcher.manager.addEntity(snake.snake_tmpl,[CILocation(ivec2(2,2)).ref_].staticArray);
}
{
@ -784,9 +818,9 @@ void snakeStart()
snake.addApple();
}
launcher.gui_manager.addTemplate(snake.snake_tmpl, "Snake");
launcher.gui_manager.addTemplate(snake.apple_tmpl, "Apple");
launcher.gui_manager.addTemplate(snake.snake_destroy_particle, "Particle");
launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.snake_tmpl), "Snake");
launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.apple_tmpl), "Apple");
launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.snake_destroy_particle), "Particle");
MoveSystem* move_system = launcher.manager.getSystem!MoveSystem();
move_system.setTemplates();
@ -794,7 +828,7 @@ void snakeStart()
/*foreach(i; 0..10)
foreach(j; 0..10)
{
loc_comp.location = vec2(i*32+64,j*32+64);
loc_compation = vec2(i*32+64,j*32+64);
launcher.manager.addEntity(simple.tmpl);
}*/
}
@ -815,15 +849,15 @@ void snakeTool(vec2 position, Tool tool, int size)
CLocation* location = tmpl.getComponent!CLocation;
if(location)
{
position.x += (randomf - 0.5) * size;
position.y += (randomf - 0.5) * size;
position.x += (randomf() - 0.5) * size;
position.y += (randomf() - 0.5) * size;
*location = position;
}
CILocation* ilocation = tmpl.getComponent!CILocation;
if(ilocation)
{
position.x += (randomf - 0.5) * size;
position.y += (randomf - 0.5) * size;
position.x += (randomf() - 0.5) * size;
position.y += (randomf() - 0.5) * size;
ivec2 ipos;
ipos.x = cast(int)(position.x / 16);
ipos.y = cast(int)(position.y / 16);
@ -878,7 +912,7 @@ bool snakeLoop()
launcher.manager.end();
snake.drawMap();
//snake.drawMap();
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,13 @@
module game_core.job_updater;
import ecs.std;
import ecs.vector;
import ecs.atomic;
import bubel.ecs.std;
import bubel.ecs.vector;
import bubel.ecs.atomic;
import ecs_utils.utils;
//import core.time;
import ecs.manager;
import bubel.ecs.manager;
import mmutils.thread_pool;
version(LDC)
@ -63,6 +63,7 @@ struct ECSJobUpdater
JobData[1024] jobs;
JobCaller[1024] callers;
uint count = 0;
string name;
void dependantOn(Group* dependency)
{
@ -89,7 +90,7 @@ struct ECSJobUpdater
void add(JobCaller caller)
{
callers[count] = caller;
jobs[count] = JobData(&callers[count].callJob,"hmm");
jobs[count] = JobData(&callers[count].callJob,name);
count++;
}
}
@ -216,6 +217,8 @@ struct ECSJobUpdater
return;
}
jobs[group.id].name = cast(string)group.caller.system.name;
foreach(ref job;group.jobs)
{
uint index = 0;
@ -233,7 +236,7 @@ struct ECSJobUpdater
foreach(dep;group.dependencies)
{
if(jobs[dep.id].count && dep.caller.system.execute && dep.caller.system.enabled)jobs[group.id].dependantOn(&jobs[dep.id]);
if(jobs[dep.id].count && dep.caller.system.willExecute && dep.caller.system.enabled)jobs[group.id].dependantOn(&jobs[dep.id]);
else deps--;
}

View file

@ -4,10 +4,10 @@ import app;
import cimgui.cimgui;
import ecs.std;
import ecs.system;
import ecs.vector;
import ecs.entity;
import bubel.ecs.std;
import bubel.ecs.system;
import bubel.ecs.vector;
import bubel.ecs.entity;
import gui.system;
import gui.template_;
@ -30,6 +30,7 @@ struct GUIManager
systems.clear();
templates.clear();
selected_tempalte = 0;
}
EntityTemplate* getSelectedTemplate()

View file

@ -1,6 +1,6 @@
module gui.system;
import ecs.system;
import bubel.ecs.system;
struct SystemGUI
{

View file

@ -1,6 +1,6 @@
module gui.template_;
import ecs.entity;
import bubel.ecs.entity;
struct TemplateGUI
{

View file

@ -1,6 +1,6 @@
module ecs_utils.gfx.buffer;
import ecs.std;
import bubel.ecs.std;
import glad.gl.gl;
import glad.gl.gles2;

View file

@ -2,7 +2,7 @@ module ecs_utils.gfx.config;
import bindbc.sdl;
import ecs.std;
import bubel.ecs.std;
import ecs_utils.gfx.material;
import ecs_utils.gfx.mesh;
@ -18,7 +18,7 @@ enum LayerType
sorted
}
import ecs.vector;
import bubel.ecs.vector;
static struct GfxConfig
{

View file

@ -2,7 +2,7 @@ module ecs_utils.gfx.material;
import bindbc.sdl;
import ecs.std;
import bubel.ecs.std;
import ecs_utils.gfx.shader;

View file

@ -2,7 +2,7 @@ module ecs_utils.gfx.mesh;
import bindbc.sdl;
import ecs.std;
import bubel.ecs.std;
import ecs_utils.gfx.buffer;

View file

@ -2,14 +2,17 @@ module ecs_utils.gfx.renderer;
import bindbc.sdl;
import ecs.std;
import bubel.ecs.std;
//import ecs_utils.core : Backend;
import ecs_utils.gfx.buffer;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
import glad.gl.gl;
import bubel.ecs.block_allocator;
import bubel.ecs.vector;
version(WebAssembly)import glad.gl.gles2;
else import glad.gl.gl;
version = ver1;
/*version(ver5)version = vv2;
@ -41,9 +44,10 @@ enum RenderTechnique
struct Renderer
{
//static SDL_Renderer* main_sdl_renderer;
BlockAllocator allocator;
enum MaxObjects = 1024 * 64 * 4;
enum BufferUsage = GL_STATIC_DRAW;
enum BufferUsage = GL_DYNAMIC_DRAW;
//SDL_Window* sdl_window;
//SDL_Renderer* sdl_renderer;
@ -53,8 +57,136 @@ struct Renderer
vec2 view_pos = vec2(-1,-1);
vec2 view_size = vec2(1,1);
enum block_size = 2^^16;
enum batch_size = block_size/68;//963;//16_384;
//uint[2] time_queries;
struct VertexBlock
{
enum max_items = batch_size;//963;
byte[] batch_vertices;
ushort[] batch_indices;
void* memory;
uint items = 0;
}
Mutex* get_block_mutex;
Mutex* block_stack_mutex;
VertexBlock getBlock()
{
VertexBlock block;
get_block_mutex.lock();
block.memory = allocator.getBlock();
get_block_mutex.unlock();
block.batch_vertices = (cast(byte*)block.memory)[0 .. VertexBlock.max_items * 4 * 14];
block.batch_indices = (cast(ushort*)block.memory)[VertexBlock.max_items * 4 * 7 .. VertexBlock.max_items * (4 * 7 + 6)];
return block;
}
Vector!VertexBlock blocks;
uint current_block = 0;
uint render_blocks = 0;
void pushBlock(VertexBlock block)
{
block_stack_mutex.lock();
prepared_items += block.items;
blocks.add(block);
render_blocks++;
block_stack_mutex.unlock();
}
bool isRemainingBlocks()
{
if(render_blocks <= current_block)return false;
return true;
}
VertexBlock fetchBlock()
{
block_stack_mutex.lock();
VertexBlock block = blocks[current_block];
current_block++;
block_stack_mutex.unlock();
return block;
}
void freeBlocks()
{
/*block_stack_mutex.lock();
render_blocks = 0;
current_block = 0;
foreach(VertexBlock block; blocks)
{
allocator.freeBlock(block.memory);
}
blocks.clear;
prepared_items=0;
draw_list.clear();
block_stack_mutex.unlock();*/
foreach(ref Thread thread; threads)
{
foreach(VertexBlock block; thread.blocks)
{
allocator.freeBlock(block.memory);
}
thread.blocks.clear();
}
render_blocks = 0;
current_block = 0;
prepared_items = 0;
draw_list.clear();
}
void pushData()
{
foreach(ref Thread thread; threads)
{
foreach(VertexBlock block; thread.blocks)
{
uint items = block.items;
if(items + item_id >= MaxObjects)items = MaxObjects - item_id;
batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr);
batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr);
draw_list.add(DrawCall(item_id,items));
item_id += items;
}
//thread.blocks.clear();
}
//if(!isRemainingBlocks())return;
/* while(isRemainingBlocks())
{
VertexBlock block = fetchBlock();
uint items = block.items;
if(items + item_id >= MaxObjects)items = MaxObjects - item_id;
batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr);
batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr);
draw_list.add(DrawCall(item_id,items));
item_id += items;
}*/
}
void pushThreadsBlocks()
{
foreach(i, ref Thread thread; threads)
{
//pushBlock(thread.block);
thread.blocks.add(thread.block);
thread.block = getBlock();
}
}
struct Thread
{
//Vector!VertexBlock block;
RenderData[] render_list;
VertexBlock block;
Vector!VertexBlock blocks;
}
Thread[] threads;
Buffer[2] ubos;
int block_alignment = 1;
int block_max_size = 16384;
@ -71,7 +203,7 @@ struct Renderer
Buffer[2] batch_vbo;
Buffer[2] batch_ibo;
float[] batch_vertices;
ubyte[] batch_vertices;
ushort[] batch_indices;
Buffer indirect_buffer;
@ -90,8 +222,17 @@ struct Renderer
uint mesh_id;
}
struct DrawCall
{
uint start;
uint count;
}
Vector!DrawCall draw_list;
RenderData[] render_list;
uint item_id;
uint prepared_items;
uint[] multi_count;
uint[] multi_offset;
@ -109,6 +250,18 @@ struct Renderer
{
//this.technique = __ecs_used_technique;
__initialize(this);
get_block_mutex = Mallocator.make!Mutex();
block_stack_mutex = Mallocator.make!Mutex();
get_block_mutex.initialize();
block_stack_mutex.initialize();
threads = Mallocator.makeArray!Thread(32);
foreach(ref Thread thread;threads)
{
thread.block = getBlock();
}
}
private static void __initialize_gl(ref Renderer this_)
@ -141,16 +294,16 @@ struct Renderer
case Technique.vbo_batch:
batch_vbo[0].create();
batch_ibo[0].create();
batch_vbo[0].bufferData(Buffer.BindTarget.array,16,4*MaxObjects,BufferUsage,null);
batch_vbo[0].bufferData(Buffer.BindTarget.array,14,4*MaxObjects,BufferUsage,null);
batch_ibo[0].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null);
batch_vbo[1].create();
batch_ibo[1].create();
batch_vbo[1].bufferData(Buffer.BindTarget.array,16,4*MaxObjects,BufferUsage,null);
batch_vbo[1].bufferData(Buffer.BindTarget.array,14,4*MaxObjects,BufferUsage,null);
batch_ibo[1].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null);
batch_vertices = Mallocator.makeArray!float(16*MaxObjects);
batch_indices = Mallocator.makeArray!ushort(6*MaxObjects);
//batch_vertices = Mallocator.makeArray!ubyte(14*4*MaxObjects);
//batch_indices = Mallocator.makeArray!ushort(6*MaxObjects);
break;
case Technique.instanced_attrib_divisor:
goto case(Technique.uniform_buffer_indexed);
@ -253,6 +406,8 @@ struct Renderer
SDL_Log("Uniform block alignment: %u",block_alignment);
SDL_Log("Uniform block max size: %u",block_max_size);
SDL_Log("Data offset: %u",data_offset);
allocator = BlockAllocator(block_size, 32);
}
}
@ -261,12 +416,13 @@ struct Renderer
}
void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, float angle = 0, uint material_id = 0, uint mesh_id = 0)
void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, short depth = 0, uint color = uint.max, float angle = 0, uint material_id = 0, uint mesh_id = 0, uint thread_id = 0)
{
__draw(this,tex,pos,size,coords,angle,material_id,mesh_id);
if(prepared_items >= MaxObjects)return;
__draw(this,tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id);
}
private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id)
private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id)
{
/*with(this_)
{
@ -286,7 +442,7 @@ struct Renderer
}*/
}
private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id)
private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id)
{
//import core.stdc.string;
with(this_)
@ -330,19 +486,20 @@ struct Renderer
data_index += data_offset;
item_id++;
prepared_items++;
}
}
private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id)
private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id = 0)
{
import ecs_utils.gfx.config;
short[3] mem = [depth, *cast(short*)&color, *(cast(short*)&color + 1)];
//import core.stdc.string;
with(this_)
{
if(item_id >= MaxObjects)return;
//if(item_id >= MaxObjects)return;
//pos += view_pos;
size.x *= view_size.x;
size.y *= view_size.y;
pos.x = pos.x * view_size.x + view_pos.x;
pos.y = pos.y * view_size.y + view_pos.y;//*/
@ -355,67 +512,134 @@ struct Renderer
memcpy(ptr+16,pos.data.ptr,8);
memcpy(ptr+32,coords.data.ptr,16);*/
short[] verts = cast(short[])threads[thread_id].block.batch_vertices;
uint item_id = threads[thread_id].block.items;
if(angle == 0)
{
batch_vertices[item_id*16] = GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x;
batch_vertices[item_id*16+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y;
batch_vertices[item_id*16+4] = GfxConfig.meshes[mesh_id].vertices[4] * size.x + pos.x;
batch_vertices[item_id*16+5] = GfxConfig.meshes[mesh_id].vertices[5] * size.y + pos.y;
batch_vertices[item_id*16+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x + pos.x;
batch_vertices[item_id*16+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y + pos.y;
batch_vertices[item_id*16+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x + pos.x;
batch_vertices[item_id*16+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y + pos.y;
size.x *= view_size.x;
size.y *= view_size.y;
verts[item_id*28] = cast(short)((GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x) * 8191);
verts[item_id*28+1] = cast(short)((GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y) * 8191);
verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+4,mem.ptr,6);
verts[item_id*28+7] = cast(short)((GfxConfig.meshes[mesh_id].vertices[4] * size.x + pos.x) * 8191);
verts[item_id*28+8] = cast(short)((GfxConfig.meshes[mesh_id].vertices[5] * size.y + pos.y) * 8191);
verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+11,mem.ptr,6);
verts[item_id*28+14] = cast(short)((GfxConfig.meshes[mesh_id].vertices[8] * size.x + pos.x) * 8191);
verts[item_id*28+15] = cast(short)((GfxConfig.meshes[mesh_id].vertices[9] * size.y + pos.y) * 8191);
verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+18,mem.ptr,6);
verts[item_id*28+21] = cast(short)((GfxConfig.meshes[mesh_id].vertices[12] * size.x + pos.x) * 8191);
verts[item_id*28+22] = cast(short)((GfxConfig.meshes[mesh_id].vertices[13] * size.y + pos.y) * 8191);
verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+25,mem.ptr,6);
}
else
{
//import core.stdc.math;
float sinn = sinf(angle);
float coss = cosf(angle);
float sinx = sinf(angle) * size.x * view_size.y;
float cosx = cosf(angle) * size.x * view_size.x;
float siny = sinf(angle) * size.y * view_size.x;
float cosy = cosf(angle) * size.y * view_size.y;
/*batch_vertices[item_id*16] = GfxConfig.meshes[mesh_id].vertices[0] * size.x;
batch_vertices[item_id*16+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y;
batch_vertices[item_id*16+4] = GfxConfig.meshes[mesh_id].vertices[4] * size.x;
batch_vertices[item_id*16+5] = GfxConfig.meshes[mesh_id].vertices[5] * size.y;
batch_vertices[item_id*16+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x;
batch_vertices[item_id*16+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y;
batch_vertices[item_id*16+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x;
batch_vertices[item_id*16+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y;*/
/*batch_vertices[item_id*28] = GfxConfig.meshes[mesh_id].vertices[0] * size.x;
batch_vertices[item_id*28+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y;
batch_vertices[item_id*28+4] = GfxConfig.meshes[mesh_id].vertices[4] * size.x;
batch_vertices[item_id*28+5] = GfxConfig.meshes[mesh_id].vertices[5] * size.y;
batch_vertices[item_id*28+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x;
batch_vertices[item_id*28+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y;
batch_vertices[item_id*28+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x;
batch_vertices[item_id*28+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y;*/
batch_vertices[item_id*16] = (GfxConfig.meshes[mesh_id].vertices[0] * coss + GfxConfig.meshes[mesh_id].vertices[1] * sinn) * size.x + pos.x;
batch_vertices[item_id*16+1] = (GfxConfig.meshes[mesh_id].vertices[1] * coss - GfxConfig.meshes[mesh_id].vertices[0] * sinn) * size.y + pos.y;
batch_vertices[item_id*16+4] = (GfxConfig.meshes[mesh_id].vertices[4] * coss + GfxConfig.meshes[mesh_id].vertices[5] * sinn) * size.x + pos.x;
batch_vertices[item_id*16+5] = (GfxConfig.meshes[mesh_id].vertices[5] * coss - GfxConfig.meshes[mesh_id].vertices[4] * sinn) * size.y + pos.y;
batch_vertices[item_id*16+8] = (GfxConfig.meshes[mesh_id].vertices[8] * coss + GfxConfig.meshes[mesh_id].vertices[9] * sinn) * size.x + pos.x;
batch_vertices[item_id*16+9] = (GfxConfig.meshes[mesh_id].vertices[9] * coss - GfxConfig.meshes[mesh_id].vertices[8] * sinn) * size.y + pos.y;
batch_vertices[item_id*16+12] = (GfxConfig.meshes[mesh_id].vertices[12] * coss + GfxConfig.meshes[mesh_id].vertices[13] * sinn) * size.x + pos.x;
batch_vertices[item_id*16+13] = (GfxConfig.meshes[mesh_id].vertices[13] * coss - GfxConfig.meshes[mesh_id].vertices[12] * sinn) * size.y + pos.y;
verts[item_id*28] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[0] * cosx + GfxConfig.meshes[mesh_id].vertices[1] * siny) + pos.x) * 8191);
verts[item_id*28+1] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[1] * cosy - GfxConfig.meshes[mesh_id].vertices[0] * sinx) + pos.y) * 8191);
verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+4,mem.ptr,6);
verts[item_id*28+7] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[4] * cosx + GfxConfig.meshes[mesh_id].vertices[5] * siny) + pos.x) * 8191);
verts[item_id*28+8] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[5] * cosy - GfxConfig.meshes[mesh_id].vertices[4] * sinx) + pos.y) * 8191);
verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+11,mem.ptr,6);
verts[item_id*28+14] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[8] * cosx + GfxConfig.meshes[mesh_id].vertices[9] * siny) + pos.x) * 8191);
verts[item_id*28+15] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[9] * cosy - GfxConfig.meshes[mesh_id].vertices[8] * sinx) + pos.y) * 8191);
verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+18,mem.ptr,6);
verts[item_id*28+21] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[12] * cosx + GfxConfig.meshes[mesh_id].vertices[13] * siny) + pos.x) * 8191);
verts[item_id*28+22] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[13] * cosy - GfxConfig.meshes[mesh_id].vertices[12] * sinx) + pos.y) * 8191);
verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);
memcpy(verts.ptr+item_id*28+25,mem.ptr,6);
}
batch_vertices[item_id*16+2] = GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x;
batch_vertices[item_id*16+3] = GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y;
batch_vertices[item_id*16+6] = GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x;
batch_vertices[item_id*16+7] = GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y;
batch_vertices[item_id*16+10] = GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x;
batch_vertices[item_id*16+11] = GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y;
batch_vertices[item_id*16+14] = GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x;
batch_vertices[item_id*16+15] = GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y;
/*verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);*/
uint ind_id = item_id % 16_384;
/*verts[item_id*28+4] = depth;
verts[item_id*28+11] = depth;
verts[item_id*28+18] = depth;
verts[item_id*28+25] = depth;
batch_indices[item_id*6] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[0] + ind_id*4);
batch_indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id*4);
batch_indices[item_id*6+2] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[2] + ind_id*4);
batch_indices[item_id*6+3] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[3] + ind_id*4);
batch_indices[item_id*6+4] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[4] + ind_id*4);
batch_indices[item_id*6+5] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[5] + ind_id*4);
*cast(uint*)&verts[item_id*28+5] = color;
*cast(uint*)&verts[item_id*28+12] = color;
*cast(uint*)&verts[item_id*28+19] = color;
*cast(uint*)&verts[item_id*28+26] = color;
memcpy(verts.ptr+item_id*28+4,mem.ptr,6);
memcpy(verts.ptr+item_id*28+11,mem.ptr,6);
memcpy(verts.ptr+item_id*28+18,mem.ptr,6);
memcpy(verts.ptr+item_id*28+25,mem.ptr,6);*/
uint ind_id = (item_id % batch_size)*4;
ushort[] indices = threads[thread_id].block.batch_indices;
indices[item_id*6] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[0] + ind_id);
indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id);
indices[item_id*6+2] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[2] + ind_id);
indices[item_id*6+3] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[3] + ind_id);
indices[item_id*6+4] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[4] + ind_id);
indices[item_id*6+5] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[5] + ind_id);
//render_list[item_id] = RenderData(tex,material_id,mesh_id);
render_list[item_id].texture = tex;
render_list[item_id].material_id = material_id;
render_list[item_id].mesh_id = mesh_id;
//render_list[item_id].texture = tex;
//render_list[item_id].material_id = material_id;
//render_list[item_id].mesh_id = mesh_id;
//data_index += 1;//data_offset;
item_id++;
threads[thread_id].block.items++;
if(threads[thread_id].block.items >= VertexBlock.max_items)
{
//pushBlock(threads[thread_id].block);
threads[thread_id].blocks.add(threads[thread_id].block);
threads[thread_id].block = getBlock();
}
}
}
@ -433,9 +657,22 @@ struct Renderer
{
glClearColor(0,0,0,0);
glViewport(0,0,this_.resolution.x,this_.resolution.y);
glClear(GL_COLOR_BUFFER_BIT);// | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glDisable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDepthFunc(GL_LESS);
version(WebAssembly)
{
glDepthRangef(0,1);
}
else
{
glDepthRange(0,1);
}
//glDepthRange(0,1);
//glClearDepth(1);
}
void present()
@ -450,6 +687,10 @@ struct Renderer
private static void __present_gl(ref Renderer this_)
{
this_.pushThreadsBlocks();
this_.pushData();
glViewport(0,0,this_.resolution.x,this_.resolution.y);
//glEnable(GL_ALPHA_TEST);
//glAlphaFunc(GL_GREATER, 0.01);
@ -471,14 +712,17 @@ struct Renderer
break;
case Technique.vbo_batch:
//if(data_index){
batch_vbo[0].bufferSubData(Buffer.BindTarget.array,item_id*4*16,0,batch_vertices.ptr);
batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,item_id*6*2,0,batch_indices.ptr);
//batch_vbo[0].bufferSubData(Buffer.BindTarget.array,item_id*4*14,0,batch_vertices.ptr);
//batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,item_id*6*2,0,batch_indices.ptr);
batch_vbo[0].bind(Buffer.BindTarget.array);
batch_ibo[0].bind(Buffer.BindTarget.element_array);
glVertexAttribPointer(0,2,GL_FLOAT,false,16,null);
glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)8);//}
//glVertexAttribPointer(0,2,GL_SHORT,true,14,null);
//glVertexAttribPointer(1,2,GL_SHORT,true,14,cast(void*)4);//}
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
//glVertexAttribPointer(2,1,GL_SHORT,true,14,cast(void*)6);//}
break;
case Technique.instanced_attrib_divisor:
ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr);
@ -575,8 +819,8 @@ struct Renderer
//glBeginQuery(GL_TIME_ELAPSED, time_queries[0]);
if(technique == Technique.vbo_batch)
{
uint items = item_id/16_384+1;
foreach(i; 0..items)
//uint items = item_id/batch_size+1;
foreach(i; 0..draw_list.length)
{
if(material_id != render_list[i].material_id)
{
@ -591,16 +835,20 @@ struct Renderer
render_list[i].texture.bind();
}
uint instance_count = 16_384;
if((i+1)*16_384 > item_id)
/*uint instance_count = batch_size;
if((i+1)*batch_size > item_id)
{
instance_count = item_id%16_384;
}
instance_count = item_id%batch_size;
}*/
glVertexAttribPointer(0,2,GL_FLOAT,false,16,cast(void*)(i*16_384*4*16));
glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)(i*16_384*4*16+8));
// glVertexAttribPointer(0,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16));
// glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16+8));
glVertexAttribPointer(0,2,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14));
glVertexAttribPointer(1,2,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14+4));
glVertexAttribPointer(2,1,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14+8));
glVertexAttribPointer(3,4,GL_UNSIGNED_BYTE,true,14,cast(void*)(draw_list[i].start*4*14+10));
glDrawElements(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2));
glDrawElements(GL_TRIANGLES,draw_list[i].count*6,GL_UNSIGNED_SHORT,cast(void*)(draw_list[i].start*6*2));
//glDrawElementsBaseVertex(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2),i*16_384*4);
}
@ -783,6 +1031,9 @@ struct Renderer
}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
this_.freeBlocks();
/*glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/
@ -803,7 +1054,7 @@ struct Renderer
view_pos = (pos - size * 0.5) * view_size;
}
__gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, float angle, uint material_id, uint mesh_id) __draw;
__gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) __draw;
__gshared void function(ref Renderer this_) __present;
__gshared void function(ref Renderer this_) __clear;
__gshared void function(ref Renderer this_) __initialize;

View file

@ -2,7 +2,7 @@ module ecs_utils.gfx.shader;
import bindbc.sdl;
import ecs.std;
import bubel.ecs.std;
import glad.gl.gl;

View file

@ -2,7 +2,7 @@ module ecs_utils.gfx.texture;
import bindbc.sdl;
import ecs.std;
import bubel.ecs.std;
import ecs_utils.math.vector;
@ -54,6 +54,8 @@ struct Texture
data.data = Mallocator.makeArray!ubyte(surf.w*surf.h*surf.format.BytesPerPixel);
data.data[0..$] = (cast(ubyte*)surf.pixels)[0..data.data.length];
SDL_FreeSurface(surf);
glGenTextures(1, &data.gl_handle);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,data.gl_handle);
@ -78,11 +80,12 @@ struct Texture
glBindTexture(GL_TEXTURE_2D, data.gl_handle);
}
void destory()
void destory() @nogc nothrow
{
if(data)
{
glDeleteTextures(1, &data.gl_handle);
if(data.data)Mallocator.dispose(data.data);
Mallocator.dispose(data);
data = null;
}

View file

@ -1,6 +1,6 @@
module ecs_utils.gfx.vertex;
import ecs.std;
import bubel.ecs.std;
struct Vertex
{

View file

@ -2,6 +2,18 @@ module ecs_utils.math.vector;
struct vec2
{
this(float v) @nogc nothrow
{
x = v;
y = v;
}
this(float x, float y) @nogc nothrow
{
this.x = x;
this.y = y;
}
union
{
struct
@ -75,6 +87,22 @@ struct vec4
float[4] data;
}
this(float v) @nogc nothrow
{
x = v;
y = v;
z = v;
w = v;
}
this(float x, float y, float z, float w) @nogc nothrow
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
vec4 opBinary(string op)(float v)
{
static if (op == "+") return vec4(x + v, y + v, z + v, w + v);
@ -96,6 +124,27 @@ struct ivec2
}
int[2] data;
}
this(int v) @nogc nothrow
{
x = v;
y = v;
}
this(int x, int y) @nogc nothrow
{
this.x = x;
this.y = y;
}
ivec2 opBinary(string op, T)(T v)
{
static if (op == "+") return ivec2(x + v, y + v);
else static if (op == "-") return ivec2(x - v, y - v);
else static if (op == "*") return ivec2(x * v, y * v);
else static if (op == "/") return ivec2(x / v, y / v);
else static assert(0, "Operator "~op~" not implemented");
}
vec2 opCast()
{
@ -116,4 +165,20 @@ struct ivec4
}
int[4] data;
}
this(int v) @nogc nothrow
{
x = v;
y = v;
z = v;
w = v;
}
this(int x, int y, int z, int w) @nogc nothrow
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}

View file

@ -21,7 +21,19 @@ float randomRangef(float min, float max)
return rand()%4096;
}*/
extern(C) int printf(scope const char* format, ...) @nogc nothrow @system;
version(GNU)
{
public import core.stdc.stdio : printf;
pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
{
return a;
}
}
else
{
extern(C) int printf(scope const char* format, ...) @nogc nothrow @system;
public import std.array : staticArray;
}
extern(C) int rand();
version(D_BetterC)

View file

@ -74,6 +74,9 @@
"dflags": [
"-betterC",
"-defaultlib="
],
"dflags-gdc": [
"-fno-druntime"
]
},
{
@ -88,9 +91,7 @@
],
"dflags-gdc": [
"-fno-druntime",
"-fvisibility=hidden"
],
"lflags-gdc": [
"-fvisibility=hidden",
"-lpthread"
]
},
@ -105,9 +106,7 @@
"-betterC"
],
"dflags-gdc": [
"-fno-druntime"
],
"lflags-gdc": [
"-fno-druntime",
"-lpthread"
]
},
@ -118,6 +117,9 @@
"-betterC",
"-unittest"
],
"dflags-gdc": [
"-fno-druntime"
],
"sourcePaths": ["source/","tests/"],
"mainSourceFile":"tests/runner.d",
"excludedSourceFiles":[

View file

@ -1,21 +1,21 @@
project('DECS', 'd')
src = [
'source/ecs/atomic.d',
'source/ecs/attributes.d',
'source/ecs/block_allocator.d',
'source/ecs/core.d',
'source/ecs/entity.d',
'source/ecs/events.d',
'source/ecs/hash_map.d',
'source/ecs/id_manager.d',
'source/ecs/manager.d',
'source/ecs/package.d',
'source/ecs/simple_vector.d',
'source/ecs/std.d',
'source/ecs/system.d',
'source/ecs/traits.d',
'source/ecs/vector.d'
'source/bubel/ecs/atomic.d',
'source/bubel/ecs/attributes.d',
'source/bubel/ecs/block_allocator.d',
'source/bubel/ecs/core.d',
'source/bubel/ecs/entity.d',
'source/bubel/ecs/events.d',
'source/bubel/ecs/hash_map.d',
'source/bubel/ecs/id_manager.d',
'source/bubel/ecs/manager.d',
'source/bubel/ecs/package.d',
'source/bubel/ecs/simple_vector.d',
'source/bubel/ecs/std.d',
'source/bubel/ecs/system.d',
'source/bubel/ecs/traits.d',
'source/bubel/ecs/vector.d'
]
tests_src = [

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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);
}