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 : ecsID; 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[ecsID!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; }