I haven’t spent time lately writing to the blog, so there are a lot of additions I have made. Mainly I have added units, unit manager (added, removed and then added again), bullets, player, buildings. I also created the Lua interface for loading materials.
Since I am working on splitting the high and low level AI to at least 1 thread the functionality of the unit is important. In order to remove as many conflicts as possible the unit essentially has two control structures for the actions it can take. An example of this would be accelerate: Accelerate(DIRECTION dir) and Accelerate(float dt). The first of these is called by the NPC, player, etc. This function sets the direction to be used in Accelerate(float dt). During the update of the unit, in the main thread, the Accelerate(float dt) function is called to do all of the actual work. Updating the controlled unit, is not the objective of the AI, which is why it can be done during the main thread.
In order for the AI to have access to add new units to the game world a unit manager was created. Initially the NPC manager kept track of the units. Once this was split off on to a separate thread it caused issues. This was mainly due to a unit being halfway updated and then being rendered in the main thread. To counter act this the unit manager loops through all of the units during update in the main thread. Additionally when the NPC manager needs a new unit it grabs one from the unit manager. The unit does not get added to the scene until the next update of the unit manager. This removes issues with adding or removing units in the middle of an update cycle.
When I started to write my threading system I immediately went off track from my initial plans. Mainly this was due to a realization my current method makes much more sense to me. My current system revolves around a scheduler, thread, task and task manager. The scheduler is my control for the rest of the systems, it controls when the threads stop, start, pause, get tasks, and where they recieve the tasks. It sits on a separate thread allocating work to worker threads. Upon initialization the scheduler determines the processor count of the system and allocates processors – 1 threads. Each thread is given wake, sleep and terminate events which allow for control by the scheduler. When the scheduler runs it loops until it is asked to terminate. During the run period it tells each thread to wake up and complete the set of queued tasks. Once this is complete the threads go back to sleep and the scheduler allocates new tasks.
Threads created by the scheduler complete tasks set in the task manager. A thread is awake and completing tasks as long as the task manager has a task available. Once all the tasks are complete, the task signals it is sleeping, and waits for the scheduler to signal there is more work. The ITask interface can be implemented by a class, allowing for it to be added to the task manager. Tasks are added to the manager through the scheduler.
As this is a work in progress I will add more information later. On the upside, currently I am seeing a nice frame rate improvement: 100-120fps (threaded), 40-60fps (non-threaded).
Post a Comment