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: stages:
- build - build
- test - test
@ -9,25 +13,18 @@ build_code:
stage: build stage: build
image: "registry.gitlab.com/mergul/bubel-ecs:latest" image: "registry.gitlab.com/mergul/bubel-ecs:latest"
script: script:
- mkdir build
- /bin/bash /compile_ecs.sh - /bin/bash /compile_ecs.sh
- cp artifacts/* build/
- cp -r public build/
artifacts: artifacts:
expire_in: 1h expire_in: 1h
paths: paths:
- build - binaries
rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: always
- when: always
allow_failure: true allow_failure: true
test_dmd_debug: test_dmd_debug:
stage: test stage: test
image: frolvlad/alpine-glibc image: frolvlad/alpine-glibc
script: script:
- build/dmd_debug_unittest - binaries/dmd_debug_unittest
artifacts: artifacts:
reports: reports:
junit: test_report.xml junit: test_report.xml
@ -35,7 +32,7 @@ test_dmd:
stage: test stage: test
image: frolvlad/alpine-glibc image: frolvlad/alpine-glibc
script: script:
- build/dmd_release_unittest - binaries/dmd_release_unittest
artifacts: artifacts:
reports: reports:
junit: test_report.xml junit: test_report.xml
@ -43,7 +40,7 @@ test_dmd_betterC:
stage: test stage: test
image: frolvlad/alpine-glibc image: frolvlad/alpine-glibc
script: script:
- build/dmd_release_unittest_bc - binaries/dmd_release_unittest_bc
artifacts: artifacts:
reports: reports:
junit: test_report.xml junit: test_report.xml
@ -56,15 +53,30 @@ coverage_test_dmd:
- build_code - build_code
script: script:
- mkdir reports - mkdir reports
- build/dmd_unittest_cov - binaries/dmd_unittest_cov
after_script: after_script:
- bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c - 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: emscripten:
stage: build_emscripten stage: build_emscripten
image: "registry.gitlab.com/mergul/bubel-ecs/emscripten:latest" image: "registry.gitlab.com/mergul/bubel-ecs/emscripten:latest"
dependencies: dependencies:
- build_code - build_wasm
script: script:
- /bin/bash /build.sh - /bin/bash /build.sh
rules: 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) [![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) [![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: ignore:
- "tests/*" - "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 sampler2D;
precision lowp samplerCube; 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 #ifdef GLES
#if __VERSION__ >290 #if __VERSION__ >290
in mediump vec2 uv; in mediump vec2 uv;
@ -15,7 +40,7 @@ precision lowp samplerCube;
#else #else
varying vec2 uv; varying vec2 uv;
#endif #endif
#endif #endif*/
//layout(binding = 0)uniform sampler2D tex; //layout(binding = 0)uniform sampler2D tex;
@ -23,20 +48,8 @@ uniform sampler2D tex;
//layout(location = 0) out vec4 outColor; //layout(location = 0) out vec4 outColor;
void main() { void main()
{
#ifdef GLES gl_FragColor = TEX(tex,uv) * color;
#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
if(gl_FragColor.a < 0.01)discard; if(gl_FragColor.a < 0.01)discard;
} }

View file

@ -2,12 +2,37 @@ precision highp float;
precision highp int; precision highp int;
precision lowp sampler2D; precision lowp sampler2D;
precision lowp samplerCube; precision lowp samplerCube;
#ifdef GLES #ifdef GLES
#if __VERSION__ >290 #if __VERSION__ >290
layout(location = 0) uniform vec4 matrix_1; #define LOC(x) layout(location = x)
layout(location = 1) uniform vec4 matrix_2; #define ATT in
layout(location = 2) uniform vec4 uv_transform; #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 = 0) in vec2 positions;
layout(location = 1) in vec2 tex_coords; layout(location = 1) in vec2 tex_coords;
@ -43,13 +68,39 @@ precision lowp samplerCube;
varying vec2 uv; varying vec2 uv;
#endif #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 #endif
void main() { void main() {
#ifdef VBO_BATCH
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); 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; uv = tex_coords * uv_transform.zw + uv_transform.xy;
#endif
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-windows-x86_64": ["libs/windows/x64/SDL2","libs/windows/x64/SDL2_Image","libs/windows/x64/cimgui"],
"libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"], "libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"],
"lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"], "lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"],
"dflags-ldc" : [
"--ffast-math"
],
"configurations" : [ "configurations" : [
{ {
"name" : "default", "name" : "default",

View file

@ -1,6 +1,6 @@
module mmutils.thread_pool; module mmutils.thread_pool;
import ecs.atomic; import bubel.ecs.atomic;
//import core.stdc.stdio; //import core.stdc.stdio;
//import core.stdc.stdlib : free, malloc, realloc; //import core.stdc.stdlib : free, malloc, realloc;
@ -16,6 +16,7 @@ version (Posix)version = MM_USE_POSIX_THREADS;
version (WebAssembly) version (WebAssembly)
{ {
version = MM_NO_LOGS;
extern(C) struct FILE extern(C) struct FILE
{ {
@ -32,7 +33,7 @@ else
version (D_BetterC) version (D_BetterC)
{ {
import ecs.std; import bubel.ecs.std;
extern (C) __gshared int _d_eh_personality(int, int, size_t, void*, void*) extern (C) __gshared int _d_eh_personality(int, int, size_t, void*, void*)
{ {
return 0; return 0;
@ -181,6 +182,12 @@ void instructionPause()
__builtin_ia32_pause(); __builtin_ia32_pause();
} }
else version(GNU)
{
import gcc.builtins;
__builtin_ia32_pause();
}
else version (DigitalMars) else version (DigitalMars)
{ {
asm asm
@ -188,7 +195,6 @@ void instructionPause()
rep; rep;
nop; nop;
} }
} }
else else
{ {
@ -799,6 +805,7 @@ struct ThreadPool
alias FlushLogsDelegaste = void delegate(ThreadData* threadData, JobLog[] logs); /// Type of delegate to flush logs 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 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 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: private:
ThreadData*[gMaxThreadsNum] threadsData; /// Data for threads ThreadData*[gMaxThreadsNum] threadsData; /// Data for threads
align(64) shared int threadsNum; /// Number of threads currentlu accepting jobs 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 JobData[4] resumeJobs; /// Dummu jobs to resume some thread
public: 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 jobsDoneCount()
{ {
int sum; int sum;
@ -1141,17 +1188,19 @@ public:
foreach (ref log; logs) 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); char* buffer = cast(char*) malloc(size);
foreach (ref log; logs) 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, size_t charWritten = snprintf(buffer + used, size - used,
`{"name":"%s", "pid":1, "tid":%lld, "ph":"X", "ts":%lld, "dur":%lld }, %s`, `{"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; used += charWritten;
} }
@ -1447,7 +1496,21 @@ private void threadFunc(ThreadData* threadData)
if (data is null) if (data is null)
{ {
// Thread does not have own job and can not steal it, so wait for a job // 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) if (ok)
{ {

View file

@ -6,9 +6,9 @@ import cimgui.cimgui;
import game_core.job_updater; import game_core.job_updater;
import ecs.manager; import bubel.ecs.manager;
import ecs.core; import bubel.ecs.core;
import ecs.std; import bubel.ecs.std;
import ecs_utils.gfx.renderer; import ecs_utils.gfx.renderer;
import ecs_utils.imgui_bind; import ecs_utils.imgui_bind;
@ -58,6 +58,7 @@ struct Launcher
uint style = 3; uint style = 3;
uint entities_count; uint entities_count;
bool multithreading; bool multithreading;
int threads = 1;
ulong timer_freq; ulong timer_freq;
double delta_time; double delta_time;
uint fps; 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) 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(); gui_manager.clear();
//launcher.ent
if(this.end)this.end(); if(this.end)this.end();
@ -102,6 +104,14 @@ struct Launcher
manager.update("clean"); manager.update("clean");
manager.end(); 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(); if(start)start();
this.loop = loop; this.loop = loop;
this.end = end; this.end = end;
@ -259,7 +269,6 @@ void mainLoop(void* arg)
launcher.repeat_time -= range; launcher.repeat_time -= range;
launcher.tool((launcher.mouse.position*launcher.scalling)-launcher.render_position, launcher.used_tool, launcher.tool_size); launcher.tool((launcher.mouse.position*launcher.scalling)-launcher.render_position, launcher.used_tool, launcher.tool_size);
} }
} }
version(WebAssembly) version(WebAssembly)
@ -316,6 +325,22 @@ void mainLoop(void* arg)
if(igMenuItemBool("Multithreading", null, launcher.multithreading, true)) if(igMenuItemBool("Multithreading", null, launcher.multithreading, true))
{ {
launcher.multithreading = !launcher.multithreading; 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)) if(igBeginMenu("Show",true))
{ {
@ -439,7 +464,7 @@ void mainLoop(void* arg)
if(launcher.show_demo_wnd) if(launcher.show_demo_wnd)
{ {
igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, 30), ImGuiCond_Once, ImVec2(0,0)); 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)) if(igBegin("Demo",&launcher.show_demo_wnd,0))
{ {
ImDrawList* draw_list = igGetWindowDrawList(); ImDrawList* draw_list = igGetWindowDrawList();
@ -539,11 +564,14 @@ void mainLoop(void* arg)
launcher.renderer.clear(); launcher.renderer.clear();
double loop_time = launcher.getTime(); double loop_time = launcher.getTime();
launcher.job_updater.pool.tryWaitCount = 10000;
if(launcher.loop && !launcher.loop()) if(launcher.loop && !launcher.loop())
{ {
quit(); quit();
*cast(bool*)arg = false; *cast(bool*)arg = false;
} }
launcher.job_updater.pool.tryWaitCount = 0;
loop_time = launcher.getTime() - loop_time; loop_time = launcher.getTime() - loop_time;
double draw_time = launcher.getTime(); double draw_time = launcher.getTime();
@ -686,10 +714,10 @@ int main(int argc, char** argv)
setStyle(3); setStyle(3);
launcher.job_updater = Mallocator.make!ECSJobUpdater(12); launcher.job_updater = Mallocator.make!ECSJobUpdater(1);
//launcher.job_updater.onCreate(); //launcher.job_updater.onCreate();
EntityManager.initialize(12); EntityManager.initialize(32, 1<<16);
launcher.manager = EntityManager.instance; launcher.manager = EntityManager.instance;
//launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID; //launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID;
@ -709,10 +737,16 @@ int main(int argc, char** argv)
launcher.renderer.initialize(); 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(); launcher.gui_manager = Mallocator.make!GUIManager();
{ {
import demos.simple; 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); launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips);
} }
@ -741,6 +775,8 @@ int main(int argc, char** argv)
} }
} }
EntityManager.destroy();
return 0; return 0;
} }
@ -785,8 +821,16 @@ void loadGFX()
GfxConfig.materials[0].compile(); GfxConfig.materials[0].compile();
GfxConfig.materials[0].bindAttribLocation("positions",0); GfxConfig.materials[0].bindAttribLocation("positions",0);
GfxConfig.materials[0].bindAttribLocation("tex_coords",1); GfxConfig.materials[0].bindAttribLocation("tex_coords",1);
GfxConfig.materials[0].bindAttribLocation("depth",2);
GfxConfig.materials[0].bindAttribLocation("vcolor",3);
GfxConfig.materials[0].link(); 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 = Mallocator.makeArray!(Material.Uniform)(3);
GfxConfig.materials[0].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0); GfxConfig.materials[0].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0);
GfxConfig.materials[0].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_2"), 16); GfxConfig.materials[0].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_2"), 16);

View file

@ -6,11 +6,11 @@ import bindbc.sdl;
import cimgui.cimgui; import cimgui.cimgui;
import ecs.attributes; import bubel.ecs.attributes;
import ecs.core; import bubel.ecs.core;
import ecs.entity; import bubel.ecs.entity;
import ecs.manager; import bubel.ecs.manager;
import ecs.std; import bubel.ecs.std;
import ecs_utils.gfx.texture; import ecs_utils.gfx.texture;
import ecs_utils.math.vector; 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 cimgui.cimgui;
import ecs.attributes; import bubel.ecs.attributes;
import ecs.core; import bubel.ecs.core;
import ecs.entity; import bubel.ecs.entity;
import ecs.manager; import bubel.ecs.manager;
import ecs.std; import bubel.ecs.std;
import ecs_utils.gfx.texture; import ecs_utils.gfx.texture;
import ecs_utils.math.vector; import ecs_utils.math.vector;

View file

@ -6,11 +6,11 @@ import bindbc.sdl;
import cimgui.cimgui; import cimgui.cimgui;
import ecs.attributes; import bubel.ecs.attributes;
import ecs.core; import bubel.ecs.core;
import ecs.entity; import bubel.ecs.entity;
import ecs.manager; import bubel.ecs.manager;
import ecs.std; import bubel.ecs.std;
import ecs_utils.gfx.texture; import ecs_utils.gfx.texture;
import ecs_utils.math.vector; import ecs_utils.math.vector;
@ -47,22 +47,27 @@ struct CTexture
struct DrawSystem struct DrawSystem
{ {
mixin ECS.System!1; mixin ECS.System!32;
struct EntitiesData struct EntitiesData
{ {
uint length; uint length;
//uint thread_id;
uint job_id;
@readonly CTexture[] textures; @readonly CTexture[] textures;
@readonly CLocation[] locations; @readonly CLocation[] locations;
} }
void onUpdate(EntitiesData data) 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) 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)); //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 cimgui.cimgui;
import ecs.attributes; import bubel.ecs.attributes;
import ecs.core; import bubel.ecs.core;
import ecs.entity; import bubel.ecs.entity;
import ecs.manager; import bubel.ecs.manager;
import ecs.std; import bubel.ecs.std;
import ecs.vector; import bubel.ecs.vector;
import ecs_utils.gfx.texture; import ecs_utils.gfx.texture;
import ecs_utils.math.vector; import ecs_utils.math.vector;
import ecs_utils.utils; import ecs_utils.utils;
import std.array : staticArray; //import std.array : staticArray;
enum float px = 1.0/512.0; enum float px = 1.0/512.0;
@ -30,8 +30,9 @@ struct MapElement
empty = 0, empty = 0,
apple = 1, apple = 1,
wall = 2, wall = 2,
snake = 3,
snake_head_up = 5, /* snake_head_up = 5,
snake_head_down = 6, snake_head_down = 6,
snake_head_left = 7, snake_head_left = 7,
snake_head_right = 8, snake_head_right = 8,
@ -44,13 +45,31 @@ struct MapElement
snake_turn_rd = 15, snake_turn_rd = 15,
snake_turn_ru = 16, snake_turn_ru = 16,
snake_vertical = 17, snake_vertical = 17,
snake_horizontal = 18 snake_horizontal = 18*/
} }
Type type; Type type;
EntityID id; 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 struct Snake
{ {
__gshared const (char)* tips = "Use \"WASD\" keys to move."; __gshared const (char)* tips = "Use \"WASD\" keys to move.";
@ -71,6 +90,16 @@ struct Snake
MapElement[map_size * map_size] map; 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) MapElement element(ivec2 pos)
{ {
uint index = pos.x + pos.y * map_size; 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; if(base_pos.x == random_pos.x && base_pos.y == random_pos.y)return;
} }
CILocation* location = apple_tmpl.getComponent!CILocation; //CILocation* location = apple_tmpl.getComponent!CILocation;
*location = random_pos; //*location = random_pos;
Entity* apple = launcher.manager.addEntity(apple_tmpl); //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 struct Animation
@ -184,6 +181,7 @@ struct CSnake
mixin ECS.Component; mixin ECS.Component;
struct Parts struct Parts
{ {
uint length = 0; uint length = 0;
@ -217,6 +215,7 @@ struct CSnake
} }
Parts parts; Parts parts;
CMovement.Direction direction;
} }
struct CApple struct CApple
@ -287,7 +286,7 @@ struct ParticleSystem
{ {
uint length; uint length;
@readonly Entity[] entities; @readonly Entity[] entities;
@readonly CParticle[] particle; CParticle[] particle;
} }
void onUpdate(EntitiesData data) void onUpdate(EntitiesData data)
@ -358,7 +357,7 @@ struct AnimationRenderSystem
{ {
foreach(i;0..data.length) 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; mixin ECS.System!64;
EntityTemplate* destroy_template; EntityTemplate* destroy_template;
CLocation* destroy_location; //CLocation* destroy_location;
CParticleVector* destroy_vector; //CParticleVector* destroy_vector;
struct EntitiesData struct EntitiesData
{ {
@ -383,8 +382,8 @@ struct MoveSystem
void setTemplates() void setTemplates()
{ {
destroy_template = snake.snake_destroy_particle; destroy_template = snake.snake_destroy_particle;
destroy_location = destroy_template.getComponent!CLocation; //destroy_location = destroy_template.getComponent!CLocation;
destroy_vector = destroy_template.getComponent!CParticleVector; //destroy_vector = destroy_template.getComponent!CParticleVector;
} }
void moveLocation(ref CILocation location, CMovement.Direction direction) void moveLocation(ref CILocation location, CMovement.Direction direction)
@ -424,133 +423,56 @@ struct MoveSystem
else .snake.element(MapElement(),location); 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) void onUpdate(EntitiesData data)
{ {
if(data.snakes) if(data.snakes)
{ {
foreach(i; 0..data.length) foreach(i; 0..data.length)
{ {
data.snakes[i].direction = data.movement[i].direction;
ivec2 new_location = data.location[i]; ivec2 new_location = data.location[i];
moveLocation(data.location[i], data.movement[i].direction); moveLocation(data.location[i], data.movement[i].direction);
final switch(snake.element(data.location[i].location).type) 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:
case MapElement.Type.snake_head_down:goto case(MapElement.Type.snake_horizontal); foreach(loc; data.snakes[i].parts)
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)
{ {
destroy_location.x = loc.x * 16; //destroy_location.x = loc.x * 16;
destroy_location.y = loc.y * 16; //destroy_location.y = loc.y * 16;
snake.element(MapElement(MapElement.Type.empty, EntityID()),loc); 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) foreach(j;0..10)
{ {
destroy_location.x = loc.x * 16 + randomf() * 8 - 4; destroy_location.x = loc.x * 16 + randomf() * 8 - 4;
destroy_location.y = loc.y * 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); 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.x = new_location.x * 16;
destroy_location.y = new_location.y * 16; //destroy_location.y = new_location.y * 16;
snake.element(MapElement(MapElement.Type.empty, EntityID()),new_location); 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); launcher.manager.removeEntity(data.entities[i].id);
break; break;
case MapElement.Type.wall:break; case MapElement.Type.wall:break;
//launcher.manager.removeEntity(data.entities[i].id);
//break;
case MapElement.Type.empty: case MapElement.Type.empty:
moveSnake(data.snakes[i], new_location); moveSnake(data.snakes[i], new_location);
final switch(data.movement[i].direction) snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location);
{
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;
}
if(data.snakes[i].parts.length > 1) 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(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]);
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[0]);
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]);
} }
else if(data.snakes[i].parts.length == 1) else if(data.snakes[i].parts.length == 1)
{ {
MapElement.Type elem_type = snakeTail(data.location[i], data.snakes[i].parts[0]); snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]);
snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[0]);
} }
break; break;
case MapElement.Type.apple: case MapElement.Type.apple:
@ -559,29 +481,13 @@ struct MoveSystem
if(data.snakes[i].parts.length > 1) 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(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]);
snake.element(MapElement(elem_type, data.entities[i].id),data.snakes[i].parts[$-1]);
} }
else if(data.snakes[i].parts.length == 1) else if(data.snakes[i].parts.length == 1)
{ {
MapElement.Type elem_type = snakeTail(data.location[i], new_location); snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),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),data.location[i].location);
snake.addApple(); snake.addApple();
break; break;
} }
@ -593,10 +499,10 @@ struct MoveSystem
{ {
final switch(data.movement[i].direction) final switch(data.movement[i].direction)
{ {
case CMovement.Direction.down:data.location[i].location.y -= 1;break; case CMovement.Direction.down:data.location[i].y -= 1;break;
case CMovement.Direction.up:data.location[i].location.y += 1;break; case CMovement.Direction.up:data.location[i].y += 1;break;
case CMovement.Direction.left:data.location[i].location.x -= 1;break; case CMovement.Direction.left:data.location[i].x -= 1;break;
case CMovement.Direction.right:data.location[i].location.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 struct CleanSystem
{ {
mixin ECS.System!64; mixin ECS.System!64;
@ -749,6 +781,8 @@ void snakeStart()
launcher.manager.registerSystem!AnimationSystem(-1); launcher.manager.registerSystem!AnimationSystem(-1);
launcher.manager.registerSystem!ParticleSystem(-1); launcher.manager.registerSystem!ParticleSystem(-1);
launcher.manager.registerSystem!ParticleMovementSystem(-1); launcher.manager.registerSystem!ParticleMovementSystem(-1);
launcher.manager.registerSystem!DrawAppleSystem(99);
launcher.manager.registerSystem!DrawSnakeSystem(101);
launcher.manager.endRegister(); launcher.manager.endRegister();
@ -765,9 +799,9 @@ void snakeStart()
{ {
ushort[4] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id]; ushort[4] components = [CILocation.component_id, CSnake.component_id, CMovement.component_id, CInput.component_id];
snake.snake_tmpl = launcher.manager.allocateTemplate(components); snake.snake_tmpl = launcher.manager.allocateTemplate(components);
CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation; //CILocation* loc_comp = snake.snake_tmpl.getComponent!CILocation;
loc_comp.location = ivec2(2,2); //*loc_comp = ivec2(2,2);
launcher.manager.addEntity(snake.snake_tmpl); launcher.manager.addEntity(snake.snake_tmpl,[CILocation(ivec2(2,2)).ref_].staticArray);
} }
{ {
@ -784,9 +818,9 @@ void snakeStart()
snake.addApple(); snake.addApple();
} }
launcher.gui_manager.addTemplate(snake.snake_tmpl, "Snake"); launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.snake_tmpl), "Snake");
launcher.gui_manager.addTemplate(snake.apple_tmpl, "Apple"); launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.apple_tmpl), "Apple");
launcher.gui_manager.addTemplate(snake.snake_destroy_particle, "Particle"); launcher.gui_manager.addTemplate(launcher.manager.allocateTemplate(snake.snake_destroy_particle), "Particle");
MoveSystem* move_system = launcher.manager.getSystem!MoveSystem(); MoveSystem* move_system = launcher.manager.getSystem!MoveSystem();
move_system.setTemplates(); move_system.setTemplates();
@ -794,7 +828,7 @@ void snakeStart()
/*foreach(i; 0..10) /*foreach(i; 0..10)
foreach(j; 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); launcher.manager.addEntity(simple.tmpl);
}*/ }*/
} }
@ -815,15 +849,15 @@ void snakeTool(vec2 position, Tool tool, int size)
CLocation* location = tmpl.getComponent!CLocation; CLocation* location = tmpl.getComponent!CLocation;
if(location) if(location)
{ {
position.x += (randomf - 0.5) * size; position.x += (randomf() - 0.5) * size;
position.y += (randomf - 0.5) * size; position.y += (randomf() - 0.5) * size;
*location = position; *location = position;
} }
CILocation* ilocation = tmpl.getComponent!CILocation; CILocation* ilocation = tmpl.getComponent!CILocation;
if(ilocation) if(ilocation)
{ {
position.x += (randomf - 0.5) * size; position.x += (randomf() - 0.5) * size;
position.y += (randomf - 0.5) * size; position.y += (randomf() - 0.5) * size;
ivec2 ipos; ivec2 ipos;
ipos.x = cast(int)(position.x / 16); ipos.x = cast(int)(position.x / 16);
ipos.y = cast(int)(position.y / 16); ipos.y = cast(int)(position.y / 16);
@ -878,7 +912,7 @@ bool snakeLoop()
launcher.manager.end(); launcher.manager.end();
snake.drawMap(); //snake.drawMap();
return true; return true;
} }

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,14 +2,17 @@ module ecs_utils.gfx.renderer;
import bindbc.sdl; import bindbc.sdl;
import ecs.std; import bubel.ecs.std;
//import ecs_utils.core : Backend; //import ecs_utils.core : Backend;
import ecs_utils.gfx.buffer; import ecs_utils.gfx.buffer;
import ecs_utils.gfx.texture; import ecs_utils.gfx.texture;
import ecs_utils.math.vector; 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 = ver1;
/*version(ver5)version = vv2; /*version(ver5)version = vv2;
@ -41,9 +44,10 @@ enum RenderTechnique
struct Renderer struct Renderer
{ {
//static SDL_Renderer* main_sdl_renderer; //static SDL_Renderer* main_sdl_renderer;
BlockAllocator allocator;
enum MaxObjects = 1024 * 64 * 4; enum MaxObjects = 1024 * 64 * 4;
enum BufferUsage = GL_STATIC_DRAW; enum BufferUsage = GL_DYNAMIC_DRAW;
//SDL_Window* sdl_window; //SDL_Window* sdl_window;
//SDL_Renderer* sdl_renderer; //SDL_Renderer* sdl_renderer;
@ -53,8 +57,136 @@ struct Renderer
vec2 view_pos = vec2(-1,-1); vec2 view_pos = vec2(-1,-1);
vec2 view_size = 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; //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; Buffer[2] ubos;
int block_alignment = 1; int block_alignment = 1;
int block_max_size = 16384; int block_max_size = 16384;
@ -71,7 +203,7 @@ struct Renderer
Buffer[2] batch_vbo; Buffer[2] batch_vbo;
Buffer[2] batch_ibo; Buffer[2] batch_ibo;
float[] batch_vertices; ubyte[] batch_vertices;
ushort[] batch_indices; ushort[] batch_indices;
Buffer indirect_buffer; Buffer indirect_buffer;
@ -90,8 +222,17 @@ struct Renderer
uint mesh_id; uint mesh_id;
} }
struct DrawCall
{
uint start;
uint count;
}
Vector!DrawCall draw_list;
RenderData[] render_list; RenderData[] render_list;
uint item_id; uint item_id;
uint prepared_items;
uint[] multi_count; uint[] multi_count;
uint[] multi_offset; uint[] multi_offset;
@ -109,6 +250,18 @@ struct Renderer
{ {
//this.technique = __ecs_used_technique; //this.technique = __ecs_used_technique;
__initialize(this); __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_) private static void __initialize_gl(ref Renderer this_)
@ -141,16 +294,16 @@ struct Renderer
case Technique.vbo_batch: case Technique.vbo_batch:
batch_vbo[0].create(); batch_vbo[0].create();
batch_ibo[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_ibo[0].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null);
batch_vbo[1].create(); batch_vbo[1].create();
batch_ibo[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_ibo[1].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null);
batch_vertices = Mallocator.makeArray!float(16*MaxObjects); //batch_vertices = Mallocator.makeArray!ubyte(14*4*MaxObjects);
batch_indices = Mallocator.makeArray!ushort(6*MaxObjects); //batch_indices = Mallocator.makeArray!ushort(6*MaxObjects);
break; break;
case Technique.instanced_attrib_divisor: case Technique.instanced_attrib_divisor:
goto case(Technique.uniform_buffer_indexed); goto case(Technique.uniform_buffer_indexed);
@ -253,6 +406,8 @@ struct Renderer
SDL_Log("Uniform block alignment: %u",block_alignment); SDL_Log("Uniform block alignment: %u",block_alignment);
SDL_Log("Uniform block max size: %u",block_max_size); SDL_Log("Uniform block max size: %u",block_max_size);
SDL_Log("Data offset: %u",data_offset); 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_) /*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; //import core.stdc.string;
with(this_) with(this_)
@ -330,19 +486,20 @@ struct Renderer
data_index += data_offset; data_index += data_offset;
item_id++; 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; import ecs_utils.gfx.config;
short[3] mem = [depth, *cast(short*)&color, *(cast(short*)&color + 1)];
//import core.stdc.string; //import core.stdc.string;
with(this_) with(this_)
{ {
if(item_id >= MaxObjects)return; //if(item_id >= MaxObjects)return;
//pos += view_pos; //pos += view_pos;
size.x *= view_size.x;
size.y *= view_size.y;
pos.x = pos.x * view_size.x + view_pos.x; pos.x = pos.x * view_size.x + view_pos.x;
pos.y = pos.y * view_size.y + view_pos.y;//*/ 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+16,pos.data.ptr,8);
memcpy(ptr+32,coords.data.ptr,16);*/ 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) if(angle == 0)
{ {
batch_vertices[item_id*16] = GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x; size.x *= view_size.x;
batch_vertices[item_id*16+1] = GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y; size.y *= view_size.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; verts[item_id*28] = cast(short)((GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x) * 8191);
batch_vertices[item_id*16+8] = GfxConfig.meshes[mesh_id].vertices[8] * size.x + pos.x; verts[item_id*28+1] = cast(short)((GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y) * 8191);
batch_vertices[item_id*16+9] = GfxConfig.meshes[mesh_id].vertices[9] * size.y + pos.y; verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
batch_vertices[item_id*16+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x + pos.x; verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
batch_vertices[item_id*16+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y + pos.y; 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 else
{ {
//import core.stdc.math; //import core.stdc.math;
float sinn = sinf(angle); float sinx = sinf(angle) * size.x * view_size.y;
float coss = cosf(angle); 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*28] = 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*28+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*28+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*28+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*28+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*28+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*28+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+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; verts[item_id*28] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[0] * cosx + GfxConfig.meshes[mesh_id].vertices[1] * siny) + pos.x) * 8191);
batch_vertices[item_id*16+1] = (GfxConfig.meshes[mesh_id].vertices[1] * coss - GfxConfig.meshes[mesh_id].vertices[0] * sinn) * size.y + pos.y; verts[item_id*28+1] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[1] * cosy - GfxConfig.meshes[mesh_id].vertices[0] * sinx) + pos.y) * 8191);
batch_vertices[item_id*16+4] = (GfxConfig.meshes[mesh_id].vertices[4] * coss + GfxConfig.meshes[mesh_id].vertices[5] * sinn) * size.x + pos.x; verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
batch_vertices[item_id*16+5] = (GfxConfig.meshes[mesh_id].vertices[5] * coss - GfxConfig.meshes[mesh_id].vertices[4] * sinn) * size.y + pos.y; verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
batch_vertices[item_id*16+8] = (GfxConfig.meshes[mesh_id].vertices[8] * coss + GfxConfig.meshes[mesh_id].vertices[9] * sinn) * size.x + pos.x; memcpy(verts.ptr+item_id*28+4,mem.ptr,6);
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+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; /*verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
batch_vertices[item_id*16+3] = GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y; verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
batch_vertices[item_id*16+6] = GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x; verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
batch_vertices[item_id*16+7] = GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y; verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
batch_vertices[item_id*16+10] = GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x; verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
batch_vertices[item_id*16+11] = GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y; verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
batch_vertices[item_id*16+14] = GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x; verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
batch_vertices[item_id*16+15] = GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y; 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); *cast(uint*)&verts[item_id*28+5] = color;
batch_indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id*4); *cast(uint*)&verts[item_id*28+12] = color;
batch_indices[item_id*6+2] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[2] + ind_id*4); *cast(uint*)&verts[item_id*28+19] = color;
batch_indices[item_id*6+3] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[3] + ind_id*4); *cast(uint*)&verts[item_id*28+26] = color;
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); 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] = RenderData(tex,material_id,mesh_id);
render_list[item_id].texture = tex; //render_list[item_id].texture = tex;
render_list[item_id].material_id = material_id; //render_list[item_id].material_id = material_id;
render_list[item_id].mesh_id = mesh_id; //render_list[item_id].mesh_id = mesh_id;
//data_index += 1;//data_offset; //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); glClearColor(0,0,0,0);
glViewport(0,0,this_.resolution.x,this_.resolution.y); glViewport(0,0,this_.resolution.x,this_.resolution.y);
glClear(GL_COLOR_BUFFER_BIT);// | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST); //glDisable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDepthFunc(GL_LESS);
version(WebAssembly)
{
glDepthRangef(0,1);
}
else
{
glDepthRange(0,1);
}
//glDepthRange(0,1);
//glClearDepth(1);
} }
void present() void present()
@ -450,6 +687,10 @@ struct Renderer
private static void __present_gl(ref Renderer this_) private static void __present_gl(ref Renderer this_)
{ {
this_.pushThreadsBlocks();
this_.pushData();
glViewport(0,0,this_.resolution.x,this_.resolution.y); glViewport(0,0,this_.resolution.x,this_.resolution.y);
//glEnable(GL_ALPHA_TEST); //glEnable(GL_ALPHA_TEST);
//glAlphaFunc(GL_GREATER, 0.01); //glAlphaFunc(GL_GREATER, 0.01);
@ -471,14 +712,17 @@ struct Renderer
break; break;
case Technique.vbo_batch: case Technique.vbo_batch:
//if(data_index){ //if(data_index){
batch_vbo[0].bufferSubData(Buffer.BindTarget.array,item_id*4*16,0,batch_vertices.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_ibo[0].bufferSubData(Buffer.BindTarget.element_array,item_id*6*2,0,batch_indices.ptr);
batch_vbo[0].bind(Buffer.BindTarget.array); batch_vbo[0].bind(Buffer.BindTarget.array);
batch_ibo[0].bind(Buffer.BindTarget.element_array); batch_ibo[0].bind(Buffer.BindTarget.element_array);
glVertexAttribPointer(0,2,GL_FLOAT,false,16,null); //glVertexAttribPointer(0,2,GL_SHORT,true,14,null);
glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)8);//} //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; break;
case Technique.instanced_attrib_divisor: case Technique.instanced_attrib_divisor:
ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr); 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]); //glBeginQuery(GL_TIME_ELAPSED, time_queries[0]);
if(technique == Technique.vbo_batch) if(technique == Technique.vbo_batch)
{ {
uint items = item_id/16_384+1; //uint items = item_id/batch_size+1;
foreach(i; 0..items) foreach(i; 0..draw_list.length)
{ {
if(material_id != render_list[i].material_id) if(material_id != render_list[i].material_id)
{ {
@ -591,16 +835,20 @@ struct Renderer
render_list[i].texture.bind(); render_list[i].texture.bind();
} }
uint instance_count = 16_384; /*uint instance_count = batch_size;
if((i+1)*16_384 > item_id) 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(0,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16));
glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)(i*16_384*4*16+8)); // 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); //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(0);
glDisableVertexAttribArray(1); glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
this_.freeBlocks();
/*glUseProgram(0); /*glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/
@ -803,7 +1054,7 @@ struct Renderer
view_pos = (pos - size * 0.5) * view_size; 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_) __present;
__gshared void function(ref Renderer this_) __clear; __gshared void function(ref Renderer this_) __clear;
__gshared void function(ref Renderer this_) __initialize; __gshared void function(ref Renderer this_) __initialize;

View file

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

View file

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

View file

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

View file

@ -2,6 +2,18 @@ module ecs_utils.math.vector;
struct vec2 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 union
{ {
struct struct
@ -75,6 +87,22 @@ struct vec4
float[4] data; 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) vec4 opBinary(string op)(float v)
{ {
static if (op == "+") return vec4(x + v, y + v, z + v, w + v); static if (op == "+") return vec4(x + v, y + v, z + v, w + v);
@ -97,6 +125,27 @@ struct ivec2
int[2] data; 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() vec2 opCast()
{ {
return vec2(x,y); return vec2(x,y);
@ -116,4 +165,20 @@ struct ivec4
} }
int[4] data; 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; 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(); extern(C) int rand();
version(D_BetterC) version(D_BetterC)

View file

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

View file

@ -1,21 +1,21 @@
project('DECS', 'd') project('DECS', 'd')
src = [ src = [
'source/ecs/atomic.d', 'source/bubel/ecs/atomic.d',
'source/ecs/attributes.d', 'source/bubel/ecs/attributes.d',
'source/ecs/block_allocator.d', 'source/bubel/ecs/block_allocator.d',
'source/ecs/core.d', 'source/bubel/ecs/core.d',
'source/ecs/entity.d', 'source/bubel/ecs/entity.d',
'source/ecs/events.d', 'source/bubel/ecs/events.d',
'source/ecs/hash_map.d', 'source/bubel/ecs/hash_map.d',
'source/ecs/id_manager.d', 'source/bubel/ecs/id_manager.d',
'source/ecs/manager.d', 'source/bubel/ecs/manager.d',
'source/ecs/package.d', 'source/bubel/ecs/package.d',
'source/ecs/simple_vector.d', 'source/bubel/ecs/simple_vector.d',
'source/ecs/std.d', 'source/bubel/ecs/std.d',
'source/ecs/system.d', 'source/bubel/ecs/system.d',
'source/ecs/traits.d', 'source/bubel/ecs/traits.d',
'source/ecs/vector.d' 'source/bubel/ecs/vector.d'
] ]
tests_src = [ 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,7 +20,7 @@ Struct EntitiesData
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder. 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. ///Used to mark optional components for system.
enum optional = "optional"; enum optional = "optional";

View file

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

View file

@ -49,10 +49,10 @@ Struct System1
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder. License: BSD 3-clause, see LICENSE file in project root folder.
*/ */
module ecs.core; module bubel.ecs.core;
public import ecs.manager; public import bubel.ecs.manager;
public import ecs.entity; public import bubel.ecs.entity;
/************************************************************************************************************************ /************************************************************************************************************************
Main struct used as namespace for templates. Main struct used as namespace for templates.
@ -74,6 +74,11 @@ static struct ECS
mixin template Component() mixin template Component()
{ {
__gshared ushort component_id = ushort.max; __gshared ushort component_id = ushort.max;
ComponentRef ref_() @nogc nothrow return
{
return ComponentRef(&this, component_id);
}
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -82,7 +87,6 @@ static struct ECS
mixin template Event() mixin template Event()
{ {
__gshared ushort event_id = ushort.max; __gshared ushort event_id = ushort.max;
EntityID entity_id;
} }
/************************************************************************************************************************ /************************************************************************************************************************
@ -92,4 +96,20 @@ static struct ECS
{ {
alias ExcludedComponents = T; 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 Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder. License: BSD 3-clause, see LICENSE file in project root folder.
*/ */
module ecs.entity; module bubel.ecs.entity;
import ecs.system; import bubel.ecs.system;
import ecs.manager; import bubel.ecs.manager;
/************************************************************************************************************************ /************************************************************************************************************************
Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference! 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) if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0)
return null; return null;
static if (EntityID.sizeof == 8) return cast(T*)(cast(void*)block + info.deltas[T.component_id] + block.entityIndex(&this) * T.sizeof);
uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) >> 3); }
else
uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) / EntityID.sizeof()); bool hasComponent(ushort component_id)
return cast(T*)(cast(void*)block + info.deltas[T.component_id] + ind * T.sizeof); {
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]); 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 bubel.ecs.block_allocator;
import ecs.entity; import bubel.ecs.entity;
import ecs.manager; import bubel.ecs.manager;
import ecs.std; import bubel.ecs.std;
import std.algorithm.comparison : max; import std.algorithm.comparison : max;
@ -20,7 +20,7 @@ package struct EventManager
void destroy() nothrow @nogc void destroy() nothrow @nogc
{ {
if(event_block_alloc_mutex) if (event_block_alloc_mutex)
{ {
event_block_alloc_mutex.destroy(); event_block_alloc_mutex.destroy();
Mallocator.dispose(event_block_alloc_mutex); 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 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]; EventData* data = &events[Ev.event_id];
EventBlock* block = data.blocks[block_id]; EventBlock* block = data.blocks[block_id];
//EntityManager.EventInfo* info = &manager.events[Ev.event_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(); event_block_alloc_mutex.lock();
scope (exit) block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock(); event_block_alloc_mutex.unlock();
block = cast(EventBlock*) allocator.getBlock();
*block = EventBlock(); *block = EventBlock();
data.first_blocks[block_id] = block; data.first_blocks[block_id] = block;
data.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(); event_block_alloc_mutex.lock();
scope (exit) EventBlock* new_block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock(); event_block_alloc_mutex.unlock();
EventBlock* new_block = cast(EventBlock*) allocator.getBlock();
*new_block = EventBlock(); *new_block = EventBlock();
block.next = new_block; block.next = new_block;
block = new_block; block = new_block;
data.blocks[block_id] = block; data.blocks[block_id] = block;
} }
Ev* event_array = cast(Ev*)(cast(void*)block + data.data_offset); uint size = Ev.sizeof + EntityID.sizeof;
event_array[block.count] = event; 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++; block.count++;
} }
void swapCurrent() nothrow @nogc void swapCurrent() nothrow @nogc
{ {
uint threads_count = cast(uint)manager.threads.length; uint threads_count = cast(uint) manager.threads.length;
if(current_index == 0)current_index = threads_count; if (current_index == 0)
else 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; EventBlock* block = first_block;
while(block) while (block)
{ {
EventBlock* to_dispose = block; EventBlock* to_dispose = block;
block = block.next; block = block.next;
@ -86,7 +91,7 @@ package struct EventManager
} }
first_block = null; 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; block = null;
} }
@ -96,12 +101,12 @@ package struct EventManager
void clearEvents() nothrow @nogc void clearEvents() nothrow @nogc
{ {
//uint threads_count = cast(uint)manager.threads.length; //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; EventBlock* block = first_block;
while(block) while (block)
{ {
EventBlock* to_dispose = block; EventBlock* to_dispose = block;
block = block.next; block = block.next;
@ -109,7 +114,7 @@ package struct EventManager
} }
first_block = null; first_block = null;
} }
foreach(ref block; event.blocks) foreach (ref block; event.blocks)
{ {
block = null; block = null;
} }
@ -120,23 +125,25 @@ package struct EventManager
{ {
disposeData(); disposeData();
events = Mallocator.makeArray!EventData(manager.events.length); 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.blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2);
event.first_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.data_offset = EventBlock.sizeof; //manager.events[i].
manager.alignNum(event.data_offset, manager.events[i].alignment); 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 private void disposeData() nothrow @nogc
{ {
clearEvents(); clearEvents();
if(events) if (events)
{ {
foreach(ref event;events) foreach (ref event; events)
{ {
Mallocator.dispose(event.blocks); Mallocator.dispose(event.blocks);
Mallocator.dispose(event.first_blocks); Mallocator.dispose(event.first_blocks);

View file

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

View file

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

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 Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder. License: BSD 3-clause, see LICENSE file in project root folder.
*/ */
module ecs.system; module bubel.ecs.system;
import ecs.entity; import bubel.ecs.entity;
import ecs.manager; import bubel.ecs.manager;
/************************************************************************************************************************ /************************************************************************************************************************
System contain data required to proper glue EntityManager with Systems. System contain data required to proper glue EntityManager with Systems.
System callbacks: System callbacks:
$(LIST $(LIST
* void onUpdate(EntitesData); * void onUpdate(EntitesData);
* void onEnable() * void onEnable() - called inside system.enable() function
* void onDisable(); * void onDisable() - called inside system.disable() function
* bool onBegin(); * bool onBegin() - called inside manager.begin()
* void onEnd(); * void onEnd() - called inside manager.end()
* void onCreate() * void onCreate() - called after registration inside registerSystem function
* void onDestroy(); * void onDestroy() - called during re-registration and inside manager destructor
* void onAddEntity(EntitesData); * void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
* void onRemoveEntity(EntitiesData); * void onRemoveEntity(EntitiesData) - called for every entity removed from system update process
* void onChangeEntity(EntitiesData); * void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system
* void handleEvent(Entity*, Event); * void handleEvent(Entity*, Event) - called for every event supported by system
) )
*/ */
struct 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; return m_execute;
} }
/************************************************************************************************************************ /************************************************************************************************************************
Get system priority. Get system id.
*/ */
export ushort id() nothrow @nogc export ushort id() nothrow @nogc
{ {
@ -127,7 +127,10 @@ package:
//System*[] m_dependencies; //System*[] m_dependencies;
ushort[] m_read_only_components; 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; EntityManager.SystemCaller* m_any_system_caller;

View file

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

View file

@ -1,30 +1,36 @@
module ecs.vector; module bubel.ecs.vector;
import core.bitop; import core.bitop;
//import core.stdc.stdlib : free, malloc; //import core.stdc.stdlib : free, malloc;
import ecs.std; import bubel.ecs.std;
//import core.stdc.string : memcpy, memset; //import core.stdc.string : memcpy, memset;
//import std.algorithm : swap; //import std.algorithm : swap;
import std.conv : emplace; import std.conv : emplace;
import std.traits : hasMember, isCopyable, TemplateOf, Unqual; 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; return 1 << bsr(num) + 1;
} }
export __gshared size_t gVectorsCreated = 0; export __gshared size_t gVectorsCreated = 0;
export __gshared size_t gVectorsDestroyed = 0; export __gshared size_t gVectorsDestroyed = 0;
struct Vector(T) { struct Vector(T)
{
T[] array; T[] array;
size_t used; size_t used;
public: public:
export this()(T t) { export this()(T t)
{
add(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); add(t);
} }
@ -42,76 +48,101 @@ public:
@disable this(this); @disable this(this);
export ~this() { export ~this()
{
clear(); clear();
} }
export void clear() { export void clear()
{
removeAll(); removeAll();
} }
export void removeAll() { export void removeAll()
if (array !is null) { {
if (array !is null)
{
/*foreach (ref el; array[0 .. used]) { /*foreach (ref el; array[0 .. used]) {
destroy(el); destroy(el);
}*/ }*/
freeData(cast(void[]) array); //freeData(cast(void[]) array);
freeData((cast(void*) array.ptr)[0 .. array.length * T.sizeof]);
gVectorsDestroyed++; gVectorsDestroyed++;
} }
array = null; array = null;
used = 0; used = 0;
} }
export bool empty() { export bool empty() const
{
return (used == 0); return (used == 0);
} }
export size_t length() { export size_t length() const
{
return used; return used;
} }
export void length(size_t newLength) { export void length(size_t newLength)
if (newLength > used) { {
if (newLength > used)
{
reserve(newLength); reserve(newLength);
foreach (ref el; array[used .. newLength]) { foreach (ref el; array[used .. newLength])
{
emplace(&el); emplace(&el);
} }
} else { }
foreach (ref el; array[newLength .. used]) { else
destroy(el); {
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; used = newLength;
} }
export void reset() { export void reset()
{
used = 0; used = 0;
} }
export void reserve(size_t numElements) { export void reserve(size_t numElements)
if (numElements > array.length) { {
if (numElements > array.length)
{
extend(numElements); extend(numElements);
} }
} }
export size_t capacity() { export size_t capacity()
{
return array.length - used; return array.length - used;
} }
export void extend(size_t newNumOfElements) { export void extend(size_t newNumOfElements)
{
auto oldArray = manualExtend(array, newNumOfElements); auto oldArray = manualExtend(array, newNumOfElements);
if (oldArray !is null) { if (oldArray !is null)
{
freeData(oldArray); freeData(oldArray);
} }
} }
export @nogc void freeData(void[] data) { export @nogc void freeData(void[] data)
{
// 0x0F probably invalid value for pointers and other types // 0x0F probably invalid value for pointers and other types
memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD
free(data.ptr); 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) if (newNumOfElements == 0)
newNumOfElements = 2; newNumOfElements = 2;
if (array.length == 0) if (array.length == 0)
@ -122,22 +153,27 @@ public:
T* memory = cast(T*) malloc(newSize); T* memory = cast(T*) malloc(newSize);
memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize); memcpy(cast(void*) memory, cast(void*) oldArray.ptr, oldSize);
array = memory[0 .. newNumOfElements]; 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; Vector!T duplicate;
duplicate.reserve(used); duplicate.reserve(used);
duplicate ~= array[0 .. used]; duplicate ~= array[0 .. used];
return duplicate; return duplicate;
} }
export bool canAddWithoutRealloc(uint elemNum = 1) { /*export bool canAddWithoutRealloc(uint elemNum = 1)
{
return used + elemNum <= array.length; return used + elemNum <= array.length;
} }*/
export void add()(T t) { export void add()(T t)
if (used >= array.length) { {
if (used >= array.length)
{
extend(nextPow2(used + 1)); extend(nextPow2(used + 1));
} }
emplace(&array[used], t); emplace(&array[used], t);
@ -145,46 +181,62 @@ public:
} }
/// Add element at given position moving others /// 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); assert(pos <= used);
if (used >= array.length) { if (used >= array.length)
{
extend(array.length * 2); extend(array.length * 2);
} }
foreach_reverse (size_t i; pos .. used) { foreach_reverse (size_t i; pos .. used)
{
//swap(array[i + 1], array[i]); //swap(array[i + 1], array[i]);
array[i+1] = array[i]; array[i + 1] = array[i];
} }
emplace(&array[pos], t); emplace(&array[pos], t);
used++; used++;
} }
export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { export void add(X)(X[] t) if (is(Unqual!X == Unqual!T))
if (used + t.length > array.length) { {
if (used + t.length > array.length)
{
extend(nextPow2(used + t.length)); extend(nextPow2(used + t.length));
} }
foreach (i; 0 .. t.length) { foreach (i; 0 .. t.length)
{
emplace(&array[used + i], t[i]); emplace(&array[used + i], t[i]);
} }
used += t.length; used += t.length;
} }
export void remove(size_t elemNum) { export void remove(size_t elemNum)
destroy(array[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]); //swap(array[elemNum], array[used - 1]);
array[elemNum] = array[used - 1]; array[elemNum] = array[used - 1];
used--; used--;
} }
export void removeStable()(size_t elemNum) { export void removeStable()(size_t elemNum)
{
used--; used--;
foreach (i; 0 .. used) { foreach (i; 0 .. used)
{
array[i] = array[i + 1]; array[i] = array[i + 1];
} }
} }
export bool tryRemoveElement()(T elem) { export bool tryRemoveElement()(T elem)
foreach (i, ref el; array[0 .. used]) { {
if (el == elem) { foreach (i, ref el; array[0 .. used])
{
if (el == elem)
{
remove(i); remove(i);
return true; return true;
} }
@ -192,55 +244,66 @@ public:
return false; return false;
} }
export void removeElement()(T elem) { export void removeElement()(T elem)
{
bool ok = tryRemoveElement(elem); bool ok = tryRemoveElement(elem);
assert(ok, "There is no such an element in vector"); 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]"); //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]; 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); assert(y <= used);
return array.ptr[x .. y]; return array.ptr[x .. y];
} }
export size_t opDollar() { export size_t opDollar()
{
return used; return used;
} }
export void opAssign(X)(X[] slice) { export void opAssign(X)(X[] slice)
{
reset(); reset();
this ~= slice; this ~= slice;
} }
export void opOpAssign(string op)(T obj) { export void opOpAssign(string op)(T obj)
{
//static assert(op == "~"); //static assert(op == "~");
add(obj); add(obj);
} }
export void opOpAssign(string op, X)(X[] obj) { export void opOpAssign(string op, X)(X[] obj)
{
//static assert(op == "~"); //static assert(op == "~");
add(obj); add(obj);
} }
export void opIndexAssign()(T obj, size_t elemNum) { export void opIndexAssign()(T obj, size_t elemNum)
{
assert(elemNum < used, "Range viloation"); assert(elemNum < used, "Range viloation");
array[elemNum] = obj; 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"); assert(b <= used && a <= b, "Range viloation");
array.ptr[a .. b] = obj; 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]; 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; module tests.basic;
import ecs.core; import bubel.ecs.core;
import ecs.manager; import bubel.ecs.manager;
import ecs.system; import bubel.ecs.system;
import ecs.attributes; 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 struct CInt
{ {
@ -61,6 +68,11 @@ struct CUnregistered
short value = 12; short value = 12;
} }
struct CFlag
{
mixin ECS.Component;
}
struct LongAddSystem struct LongAddSystem
{ {
mixin ECS.System; mixin ECS.System;
@ -103,6 +115,7 @@ struct EmptySystem
void beforeEveryTest() void beforeEveryTest()
{ {
CUnregistered.component_id = ushort.max;
gEM.initialize(0); gEM.initialize(0);
gEM.beginRegister(); gEM.beginRegister();
@ -112,6 +125,7 @@ void beforeEveryTest()
gEM.registerComponent!CDouble; gEM.registerComponent!CDouble;
gEM.registerComponent!CLong; gEM.registerComponent!CLong;
gEM.registerComponent!CShort; gEM.registerComponent!CShort;
gEM.registerComponent!CFlag;
gEM.endRegister(); gEM.endRegister();
} }
@ -121,14 +135,33 @@ void afterEveryTest()
gEM.destroy(); 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") @("AddEntity")
unittest unittest
{ {
ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray);
EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); assert(tmpl_.info.components.length == 3);
assert(tmpl_.info.components.length == 2); assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof));
assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CInt);
assert(tmpl_.getComponent!CFloat); assert(tmpl_.getComponent!CFloat);
assert(tmpl_.getComponent!CFlag);
assert(!tmpl_.getComponent!CLong); assert(!tmpl_.getComponent!CLong);
assert(!tmpl_.getComponent!CUnregistered); assert(!tmpl_.getComponent!CUnregistered);
assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CInt == 1);
@ -146,6 +179,67 @@ unittest
assert(entity2.getComponent!CFloat); assert(entity2.getComponent!CFloat);
assert(*entity2.getComponent!CInt == 2); assert(*entity2.getComponent!CInt == 2);
assert(*entity2.getComponent!CFloat == 2.0); 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 //allocate templates
@ -156,32 +250,48 @@ unittest
ushort[2] ids = [CInt.component_id, CFloat.component_id]; ushort[2] ids = [CInt.component_id, CFloat.component_id];
EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); EntityTemplate* tmpl_ = gEM.allocateTemplate(ids);
EntityTemplate* tmpl_d = gEM.allocateTemplate([CFloat.component_id, CInt.component_id, CFloat.component_id].staticArray); 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_d.info == tmpl_.info);
assert(tmpl_cp.info == tmpl_cp.info);
assert(tmpl_.info.components.length == 2); assert(tmpl_.info.components.length == 2);
assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CInt);
assert(tmpl_.getComponent!CFloat); assert(tmpl_.getComponent!CFloat);
assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CInt == 1);
assert(*tmpl_.getComponent!CFloat == 2.0); 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!CInt = 4;
*tmpl_.getComponent!CFloat = 5.0; *tmpl_.getComponent!CFloat = 5.0;
//allocate template from template with additional component //allocate template from template with additional components
ushort[1] ids2 = [CDouble.component_id]; ushort[2] ids2 = [CDouble.component_id,CFlag.component_id];
EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2); 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!CInt);
assert(tmpl_2.getComponent!CFloat); assert(tmpl_2.getComponent!CFloat);
assert(tmpl_2.getComponent!CDouble); assert(tmpl_2.getComponent!CDouble);
assert(tmpl_2.getComponent!CFlag);
assert(*tmpl_2.getComponent!CInt == 4); assert(*tmpl_2.getComponent!CInt == 4);
assert(*tmpl_2.getComponent!CFloat == 5.0); assert(*tmpl_2.getComponent!CFloat == 5.0);
assert(*tmpl_2.getComponent!CDouble == 3.0); assert(*tmpl_2.getComponent!CDouble == 3.0);
assert(tmpl_.info.blocksCount() == 0);
Entity* entity = gEM.addEntity(tmpl_); 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 //apply entity changes
gEM.commit(); gEM.commit();
assert(tmpl_.info.blocksCount() == 0);
//allocate template as entity copy //allocate template as entity copy
EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id); EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id);
assert(tmpl_3.info.components.length == 3); assert(tmpl_3.info.components.length == 3);
@ -202,18 +312,20 @@ unittest
assert(*tmpl_4.getComponent!CFloat == 2.0); assert(*tmpl_4.getComponent!CFloat == 2.0);
assert(*tmpl_4.getComponent!CDouble == 3.0); assert(*tmpl_4.getComponent!CDouble == 3.0);
//allocate template from template with two additional component //allocate template from template with three additional component
ushort[2] ids3 = [CDouble.component_id, CLong.component_id]; ushort[3] ids3 = [CDouble.component_id, CLong.component_id, CShort.component_id];
EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_, ids3); EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_2, ids3);
assert(tmpl_5.info.components.length == 4); assert(tmpl_5.info.components.length == 6);
assert(tmpl_5.getComponent!CInt); assert(tmpl_5.getComponent!CInt);
assert(tmpl_5.getComponent!CFloat); assert(tmpl_5.getComponent!CFloat);
assert(tmpl_5.getComponent!CDouble); assert(tmpl_5.getComponent!CDouble);
assert(tmpl_5.getComponent!CLong); assert(tmpl_5.getComponent!CLong);
assert(tmpl_5.getComponent!CShort);
assert(*tmpl_5.getComponent!CInt == 4); assert(*tmpl_5.getComponent!CInt == 4);
assert(*tmpl_5.getComponent!CFloat == 5.0); assert(*tmpl_5.getComponent!CFloat == 5.0);
assert(*tmpl_5.getComponent!CDouble == 3.0); assert(*tmpl_5.getComponent!CDouble == 3.0);
assert(*tmpl_5.getComponent!CLong == 10); assert(*tmpl_5.getComponent!CLong == 10);
assert(*tmpl_5.getComponent!CShort == 12);
//allocate template from template without one component //allocate template from template without one component
ushort[1] rem_ids = [CFloat.component_id]; ushort[1] rem_ids = [CFloat.component_id];
@ -224,7 +336,7 @@ unittest
//allocate template from template without one component and two additional //allocate template from template without one component and two additional
EntityTemplate* tmpl_7 = gEM.allocateTemplate(tmpl_, ids3, rem_ids); 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!CInt);
assert(tmpl_7.getComponent!CDouble); assert(tmpl_7.getComponent!CDouble);
assert(tmpl_7.getComponent!CLong); assert(tmpl_7.getComponent!CLong);
@ -481,6 +593,10 @@ unittest
gEM.endRegister(); gEM.endRegister();
assert(gEM.getPass("custom"));
assert(!gEM.getPass("custommm"));
LongAddSystem* system = gEM.getSystem!LongAddSystem; LongAddSystem* system = gEM.getSystem!LongAddSystem;
assert(system !is null); assert(system !is null);
assert(system.updates_count == 0); assert(system.updates_count == 0);
@ -1116,9 +1232,9 @@ unittest
assert(*entity2.getComponent!CShort == 12); assert(*entity2.getComponent!CShort == 12);
gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,10)); gEM.sendEvent(id,ETest2(10));
gEM.sendEvent(id2,ETest()); gEM.sendEvent(id2,ETest());
gEM.sendEvent(id2,ETest2(id2,12)); gEM.sendEvent(id2,ETest2(12));
gEM.commit(); gEM.commit();
assert(ETest2.destory == 2); assert(ETest2.destory == 2);
@ -1129,20 +1245,20 @@ unittest
gEM.addComponents(id, CInt(2), CShort(1)); gEM.addComponents(id, CInt(2), CShort(1));
gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,2)); gEM.sendEvent(id,ETest2(2));
gEM.commit(); gEM.commit();
assert(ETest2.destory == 3); assert(ETest2.destory == 3);
entity = gEM.getEntity(id); entity = gEM.getEntity(id);
assert(*entity.getComponent!CLong == 66); assert(*entity.getComponent!CLong == 66);
assert(*entity.getComponent!CInt == 36); assert(*entity.getComponent!CInt == 2);//36);
//test for multiple event blocks //test for multiple event blocks
long result = *entity.getComponent!CLong; long result = *entity.getComponent!CLong;
foreach(i;0..10000) foreach(i;0..10000)
{ {
gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest());
gEM.sendEvent(id,ETest2(id,4)); gEM.sendEvent(id,ETest2(4));
result += 16; result += 16;
result += 8; result += 8;
} }
@ -1221,3 +1337,246 @@ unittest
gEM.end(); 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; module tests.id_manager;
import ecs.id_manager; import bubel.ecs.id_manager;
import ecs.entity; import bubel.ecs.entity;
unittest 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.stdc.string;
import core.sys.posix.setjmp; import core.sys.posix.setjmp;
import ecs.vector; import bubel.ecs.vector;
import ecs.simple_vector; import bubel.ecs.simple_vector;
import ecs.std; import bubel.ecs.std;
import std.traits;
import tests.time;
version (LDC)
{
import ldc.attributes;
}
else
{
enum optStrategy = 0;
}
enum int ASSERTED = 123; enum int ASSERTED = 123;
enum string OUT_FILE = "test_report.xml"; enum string OUT_FILE = "test_report.xml";
@ -95,6 +108,8 @@ struct TestRunner(Args...)
write(test.name); write(test.name);
write("\" classname=\""); write("\" classname=\"");
write(suite.name); write(suite.name);
write("\" time=\"");
write(cast(double)test.time*0.000001);
write("\">"); write("\">");
if (test.msg) if (test.msg)
{ {
@ -120,23 +135,44 @@ struct TestRunner(Args...)
write("</testsuites>"); write("</testsuites>");
} }
@(optStrategy,"none")
void runTests(string[] include = null, string[] exclude = null) void runTests(string[] include = null, string[] exclude = null)
{ {
foreach (i, module_; Args) foreach (i, module_; Args)
{ {
TestSuite* suite = &suites[i]; TestSuite* suite = &suites[i];
suite.name = module_.stringof; suite.name = module_.stringof;
void function() before;
void function() after;
foreach (index, unittest_; __traits(getUnitTests, module_)) foreach (index, unittest_; __traits(getUnitTests, module_))
{ {
enum attributes = __traits(getAttributes, unittest_); enum attributes = __traits(getAttributes, unittest_);
static if (attributes.length != 0) static if (attributes.length != 0)
{
foreach(attr_id, attr; attributes)
{
static if(isFunctionPointer!(attr)){}
else static if(attr == "_tr_before")
{
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()));
}
else
{ {
if (include.length > 0) if (include.length > 0)
{ {
bool matched = false; bool matched = false;
foreach (str; include) foreach (str; include)
{ {
if (match(str, attributes[0])) if (match(str, attr))
{ {
matched = true; matched = true;
break; break;
@ -145,7 +181,7 @@ struct TestRunner(Args...)
foreach (str; exclude) foreach (str; exclude)
{ {
if (match(str, attributes[0])) if (match(str, attr))
{ {
matched = false; matched = false;
break; break;
@ -159,6 +195,8 @@ struct TestRunner(Args...)
} }
} }
} }
}
}
else if (include.length > 0) else if (include.length > 0)
{ {
suite.skipped++; suite.skipped++;
@ -172,8 +210,9 @@ struct TestRunner(Args...)
else else
test.name = attributes[0]; test.name = attributes[0];
static if (__traits(hasMember, module_, "beforeEveryTest")) static if (__traits(hasMember, module_, "beforeEveryTest") && __traits(compiles, module_.beforeEveryTest()))
module_.beforeEveryTest(); module_.beforeEveryTest();
if(before)before();
version(D_BetterC) version(D_BetterC)
{ {
@ -189,8 +228,10 @@ struct TestRunner(Args...)
} }
else else
{ {
long time = Time.getUSecTime();
unittest_(); unittest_();
test.passed = true; test.passed = true;
test.time = cast(int)(Time.getUSecTime() - time);
} }
} }
else else
@ -215,7 +256,7 @@ struct TestRunner(Args...)
else else
suite.failed++; suite.failed++;
suite.tests ~= test; suite.tests ~= test;
static if (__traits(hasMember, module_, "afterEveryTest")) static if (__traits(hasMember, module_, "afterEveryTest") && __traits(compiles, module_.beforeEveryTest()))
module_.afterEveryTest(); module_.afterEveryTest();
} }
passed += suite.passed; passed += suite.passed;
@ -281,6 +322,14 @@ struct TestRunner(Args...)
junit.add(cast(ubyte[]) txt); 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) void write(uint num)
{ {
ubyte[20] buffer; ubyte[20] buffer;
@ -304,6 +353,8 @@ version (notBetterC)
version (D_Coverage) extern (C) void dmd_coverDestPath(string path); version (D_Coverage) extern (C) void dmd_coverDestPath(string path);
} }
enum before = "_tr_before";
void extractStrings(ref Vector!string container, string str) void extractStrings(ref Vector!string container, string str)
{ {
uint s = 0; 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[]); runner.runTests(include[], exclude[]);

View file

@ -3,12 +3,12 @@ module tests.tests;
import std.experimental.allocator; import std.experimental.allocator;
import std.experimental.allocator.mallocator;*/ import std.experimental.allocator.mallocator;*/
import ecs.entity; import bubel.ecs.entity;
import ecs.events; import bubel.ecs.events;
import ecs.manager; import bubel.ecs.manager;
import ecs.system; import bubel.ecs.system;
import ecs.attributes; import bubel.ecs.attributes;
import ecs.core; import bubel.ecs.core;
version (WebAssembly) version (WebAssembly)
{ {
@ -714,7 +714,7 @@ else:
//foreach(i; 0..1_000_000)gEM.removeEntity(gEM.addEntity(tmpl).id); //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] EntityID[] idss = Mallocator.makeArray!EntityID(5000); //[5000]
//scope(exit)Mallocator.dispose(idss); //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; module tests.vector;
import ecs.simple_vector; import bubel.ecs.simple_vector;
//import ecs.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") @("simple-vector")
unittest unittest
@ -33,3 +42,38 @@ unittest
assert(vector2[1023] == 'a'); assert(vector2[1023] == 'a');
assert(vector2[1024] == 'b'); 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);
}