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

@ -0,0 +1,239 @@
module bubel.ecs.id_manager;
import bubel.ecs.entity;
import bubel.ecs.std;
import bubel.ecs.vector;
import bubel.ecs.atomic;
import core.stdc.string : memcpy;
/************************************************************************************************************************
IDManager is responsible for assignment and removing IDs. IDs are unique throughtout a whole duration of the program.
*/
struct IDManager
{
/************************************************************************************************************************
Get new ID.
*/
pragma(inline, false) EntityID getNewID() nothrow @nogc
{
int current = m_stack_top.atomicOp!"-="(1) + 1;
if (current < 0)
{
uint add_id = m_last_id.atomicOp!"+="(1) - 1;
if (add_id >= m_ids_array.length)
{
uint local_id = add_id - cast(uint) m_ids_array.length;
uint block_id = local_id >> 16;
if (block_id >= m_blocks_count)
{
add_mutex.lock();
if (block_id >= m_blocks_count)
{
m_blocks[m_blocks_count].alloc();
m_blocks_count++;
}
add_mutex.unlock();
}
}
EntityID id;
id.id = add_id;
id.counter = 0;
return id;
}
uint index = m_free_stack[current];
EntityID id;
id.id = index;
id.counter = m_ids_array[index].counter;
return id;
}
/************************************************************************************************************************
Release ID.
*/
void releaseID(EntityID id) nothrow @nogc
{
optimize();
Data* data = &m_ids_array[id.id];
if (data.counter != id.counter)
return;
data.counter++;
data.entity = null;
m_stack_top.atomicOp!"+="(1);
m_free_stack[m_stack_top] = id.id;
}
/************************************************************************************************************************
Update pointer to entity. The purpose of this function is to ensure that pointer to entity is always correct.
*/
void update(ref Entity entity) nothrow @nogc
{
if (entity.id.id >= cast(uint) m_ids_array.length)
{
uint local_id = entity.id.id - cast(uint) m_ids_array.length;
uint block_id = local_id >> 16;
local_id -= block_id << 16;
m_blocks[block_id].data[local_id].entity = &entity;
}
else //if (entity.id.counter == m_ids_array[entity.id.id].counter)
m_ids_array[entity.id.id].entity = &entity;
}
/************************************************************************************************************************
Returns pointer to entity.
*/
export Entity* getEntityPointer(EntityID id) nothrow @nogc
{
if (id.id >= m_ids_array.length)
{
uint local_id = id.id - cast(uint) m_ids_array.length;
uint block_id = local_id >> 16;
assert(block_id < m_blocks_count);
if (block_id >= m_blocks_count)
return null;
local_id -= block_id << 16;
if (m_blocks[block_id].data[local_id].counter != id.counter)
return null;
return m_blocks[block_id].data[local_id].entity;
}
Data* data = &m_ids_array[id.id];
if (data.counter != id.counter)
return null;
else
return data.entity;
}
/************************************************************************************************************************
Check if entity with specified ID exist.
*/
export bool isExist(EntityID id) nothrow @nogc
{
if (id.id >= m_ids_array.length)
return false;
Data* data = &m_ids_array[id.id];
if (data.entity is null)
return false;
return data.counter == id.counter;
}
/************************************************************************************************************************
Initialize manager.
*/
void initialize() nothrow @nogc
{
m_ids_array = Mallocator.makeArray!Data(65536);
m_free_stack = Mallocator.makeArray!uint(65536);
m_blocks = Mallocator.makeArray!Block(64);
foreach (ref block; m_blocks)
block = Block();
m_blocks_count = 1;
m_blocks[0].alloc();
add_mutex = Mallocator.make!Mutex();
add_mutex.initialize();
getNewID();
optimize();
}
/************************************************************************************************************************
Free manager memory.
*/
void deinitialize() @trusted @nogc nothrow
{
if (m_ids_array)
Mallocator.dispose(m_ids_array);
if (m_free_stack)
Mallocator.dispose(m_free_stack);
if (m_blocks)
{
foreach (ref block; m_blocks)
{
if (block.data)
block.free();
}
Mallocator.dispose(m_blocks);
}
if (add_mutex)
{
add_mutex.destroy();
Mallocator.dispose(add_mutex); //cast(void*)add_mutex); //workaround for compiler bug
add_mutex = null;
}
}
/************************************************************************************************************************
Optimize memory. Must be called if any ID was added and some ID will be removed.
*/
void optimize() nothrow @nogc
{
if (m_stack_top < -1)
m_stack_top = -1;
if (m_last_id > m_ids_array.length)
{
uint begin = cast(uint) m_ids_array.length;
Data[] new_array = Mallocator.makeArray!Data(begin + (m_blocks_count << 16));
memcpy(new_array.ptr, m_ids_array.ptr, m_ids_array.length * Data.sizeof);
Mallocator.dispose(m_ids_array);
m_ids_array = new_array;
uint[] new_stack = Mallocator.makeArray!uint(m_ids_array.length);
memcpy(new_stack.ptr, m_free_stack.ptr, m_free_stack.length * uint.sizeof);
Mallocator.dispose(m_free_stack);
m_free_stack = new_stack;
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);
begin += 65536;
}
memcpy(cast(void*) m_ids_array.ptr + begin * Data.sizeof,
m_blocks[m_blocks_count - 1].data.ptr, (m_last_id - begin) * Data.sizeof);
foreach (ref block; m_blocks[1 .. m_blocks_count])
block.free();
m_blocks_count = 1;
}
}
private static struct Block
{
void alloc() nothrow @nogc
{
assert(data is null);
data = Mallocator.makeArray!Data(65536);
}
void free() nothrow @nogc
{
assert(data !is null);
Mallocator.dispose(data);
data = null;
}
Data[] data = null; //65536
}
private static struct Data
{
uint counter = 0;
//uint next_id = uint.max;
Entity* entity = null;
}
private:
Mutex* add_mutex;
Data[] m_ids_array = null;
uint m_blocks_count = 0;
Block[] m_blocks;
uint[] m_free_stack = null;
align(64) shared uint m_last_id = 0;
align(64) shared int m_stack_top = -1;
}