bubel-ecs/source/bubel/ecs/events.d
2022-07-09 20:27:01 +00:00

193 lines
5.6 KiB
D

/************************************************************************************************************************
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.events;
import bubel.ecs.block_allocator;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import bubel.ecs.traits : becsID;
import std.algorithm.comparison : max;
package struct EventManager
{
void initialize(EntityManager* m) nothrow @nogc
{
allocator = BlockAllocator(events_block_size, events_blocks_in_allocation);
event_block_alloc_mutex = Mallocator.make!Mutex;
event_block_alloc_mutex.initialize();
manager = m;
}
void destroy() nothrow @nogc
{
if (event_block_alloc_mutex)
{
event_block_alloc_mutex.destroy();
Mallocator.dispose(event_block_alloc_mutex);
event_block_alloc_mutex = null;
}
}
export void sendEvent(Ev)(EntityID id, Ev event, uint thread_id = 0) nothrow @nogc
{
uint block_id = current_index + thread_id;
EventData* data = &events[becsID!Ev];
EventBlock* block = data.blocks[block_id];
//EntityManager.EventInfo* info = &manager.events[Ev.event_id];
//event.entity_id = id;
if (block is null)
{
event_block_alloc_mutex.lock();
block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock();
*block = EventBlock();
data.first_blocks[block_id] = block;
data.blocks[block_id] = block;
}
if (block.count >= data.max_events)
{
event_block_alloc_mutex.lock();
EventBlock* new_block = cast(EventBlock*) allocator.getBlock();
event_block_alloc_mutex.unlock();
*new_block = EventBlock();
block.next = new_block;
block = new_block;
data.blocks[block_id] = block;
}
uint size = Ev.sizeof + EntityID.sizeof;
void* ptr = cast(void*) block + data.data_offset + block.count * size;
*cast(EntityID*)ptr = id;
*cast(Ev*)(ptr + EntityID.sizeof) = event;
//Ev* event_array = cast(Ev*)(cast(void*) block + data.data_offset);
//event_array[block.count] = event;
block.count++;
}
void swapCurrent() nothrow @nogc
{
uint threads_count = cast(uint) manager.threads.length;
if (current_index == 0)
current_index = threads_count;
else
current_index = 0;
foreach (ref event; events)
{
foreach (ref first_block; event.first_blocks[current_index
.. current_index + threads_count])
{
EventBlock* block = first_block;
while (block)
{
EventBlock* to_dispose = block;
block = block.next;
allocator.freeBlock(to_dispose);
}
first_block = null;
}
foreach (ref block; event.blocks[current_index .. current_index + threads_count])
{
block = null;
}
}
}
void clearEvents() nothrow @nogc
{
//uint threads_count = cast(uint)manager.threads.length;
foreach (ref event; events)
{
foreach (ref first_block; event.first_blocks)
{
EventBlock* block = first_block;
while (block)
{
EventBlock* to_dispose = block;
block = block.next;
allocator.freeBlock(to_dispose);
}
first_block = null;
}
foreach (ref block; event.blocks)
{
block = null;
}
}
}
void allocateData(uint threads_count) nothrow @nogc
{
disposeData();
events = Mallocator.makeArray!EventData(manager.events.length);
foreach (i, ref event; events)
{
event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2);
event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count * 2);
event.data_offset = EventBlock.sizeof; //manager.events[i].
manager.alignNum(event.data_offset, manager.events[i].alignment);
uint size = manager.events[i].size + EntityID.sizeof;
event.max_events = cast(ushort)(
(events_block_size - event.data_offset) / size);
}
}
private void disposeData() nothrow @nogc
{
clearEvents();
if (events)
{
foreach (ref event; events)
{
Mallocator.dispose(event.blocks);
Mallocator.dispose(event.first_blocks);
}
Mallocator.dispose(events);
}
allocator.freeMemory();
}
~this() nothrow @nogc
{
disposeData();
}
///Single page size. Must be power of two.
enum events_block_size = 1 << 14;
///Number of pages in block.
enum events_blocks_in_allocation = 128;
struct EventBlock
{
EventBlock* next;
ushort count = 0;
}
struct EventData
{
ushort data_offset;
ushort max_events;
EventBlock*[] blocks;
EventBlock*[] first_blocks;
//EventBlock*[] current_blocks;
}
uint current_index = 0;
EventData[] events;
Mutex* event_block_alloc_mutex;
BlockAllocator allocator;
EntityManager* manager;
}