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:
parent
2ddb97e9ce
commit
024356df9b
62 changed files with 5918 additions and 1673 deletions
239
source/bubel/ecs/id_manager.d
Normal file
239
source/bubel/ecs/id_manager.d
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue