A concurrent component-based entity architecture for game development

Author:Ferdinand Majerech
Supervisor:RNDr. Jozef Jirásek, PhD.

Previously on SAI

Issues with existing ECS's

Issues with existing ECS's

Tharsis

Tharsis

Should have all the good stuff

  • Data defined entities with no programming
  • Easy to modify entities at runtime
  • Lightweight components
  • Entity as a dumb ID
  • Data in Components, logic in Systems (called Processes)

Tharsis

New stuff (goals)

  • Process run order must not matter
    • May even change at runtime due to scheduling
  • Automatic treading unless specified by the user
    • Lock only when absolutely necessary
    • Work with varying core counts (up to ... many)
    • Schedule to evenly spread the load, avoid spikes
  • No voidpointery macroy stuff in the API
    • Type-safe
  • Generated code to exploit Process specifics for optimization
    • Aka 'don't do what you don't have to'

Tharsis

More stuff turned out to be necessary

  • Compile-time constraints on Processes, Components, etc.
    • Easier than hard-wiring a game for multiple cores
    • Not as convenient as working with an ECS in a single thread
  • Resource management designed to minimize locking
    • Immutable resources
  • MultiComponents
    • This is where the RDBMS analogy breaks down
    • Surprisingly convenient

Past and future

Past

  • Game state (components) from the previous game update
  • Processes can read, but not write, past data
    • Past data is immutable during a game update

Past and future

Future

  • Game state created during the current game update
  • Written by Processes
  • A Process can only write one component type
    • Massive constraint, but doesn't seem to kill maintainability
  • Components are be removed by not adding them to future state

Past and future

Threading

Threading - scheduling

Threading - overhead

Common approach to multi-threading in games

_images/common_threading.png

Common approach to multi-threading in games

A game running in Tharsis

_images/tharsis_threading.png

A game running in Tharsis

Basic memory layout

_images/tharsis_memory.png

Basic memory layout

High-level time slice

_images/tharsis_time_1.png

High-level time slice

Process execution - time slice

_images/tharsis_time_2.png

Process execution - time slice

Process execution - data read-write

_images/tharsis_onthefly.png

Game update pseudocode - no threads

(Reminder)

foreach(system; systems):
    ulong flags = system.componentFlags();
    uint[flags.length] componentIndices;
    componentIndices[] = 0;

    for(uint entityIndex = 0; entityIndex < entityIDs.length; entityIndex++):
        incrementComponentIndices(componentFlags[entityIndex], componentIndices);

        if(componentFlags[entityIndex] && flags):
            system.process(getComponents(componentIndices));

        increment component indices corresponding to flags

Game update pseudocode - Tharsis

parallel_foreach(thread; threads):

    foreach(process; thread):
        uint[process.PastComponents.length] pastComponentIndices;
        componentIndices[] = 0;
        uint futureIndex = 0;

        foreach(entityIndex; 0 .. entityIDs.length):
            updatePastComponentIndices(entityIndex, pastComponentIndices);

            if(matchComponentCounts(entityIndex)):
                futureComponent = &futureComponents(process)[futureIndex];
                system.process(getPastComponents(componentIndices),
                               futureComponent);

                if(futureComponent != NULL):
                    futureComponentCounts(process)[entityIndex] = 1;
                    ++futureIndex;

Resources

ResourceManagers

ResourceManagers

Resources - note about disposal

Probably not enough time for

Potential issues

Sources

TODO

Future possibilities (not planned yet)

Thank you