Components

  1. CPU model review
    1. Execute instructions sequentially
    2. Computational operations fixed cost
    3. Random memory accesses are expensive
    4. Unpredictable branches are expensive
  2. SoA review
    1. Object organization for an array of things, not each individual one
    2. e.g. Particles (with arrays inside), not Particle[]
    3. Avoid cache pollution with unneeded data
    4. More predictable cache access
    5. Can group stuff that is always used together.
  3. Components
    1. Replace with sparse arrays / maps
      1. Map Entity ID to Data
      2. std::map<id, data> = red/black tree
      3. std::unordered_map<id, data> = hash map (C++ 11)
    2. Empty = not that type, not that data
      • Replace booleans, enums & inheritance with existence
    3. Most processing = iterate over existing elements, not all entities
      1. Avoids ifs & branches: only process active data
      2. C++ maps allow exactly that
    4. Cache efficiency
      1. Packed array of data
      2. map<id, component_index>
      3. Related: Briggs & Torczon sparse set
        1. Sparse[key] = index
        2. Dense[index] = key
        3. Optional Data[index] = dense data
        4. Size = count(dense)
        5. Lookup(key):
          • Sparse[key] < Size && Dense[ Sparse[key] ] == key
          • Don't need to 0-fill, rely on key match
        6. Insert:
          • Sparse[key] = Size
          • Dense[Size++] = key
        7. Clear: Size = 0
    5. Object is just functions operating on component arrays
      1. In the limit, just Entity ID and existence in component arrays
      2. Function = transform on data
    6. Component structs
      1. Exposed data part of interface, don't use accessor functions
      2. Member functions are about processing, not access protection.
  4. Existence Processing
    1. Common code for SoA and AoS: loop, test condition, operate
    2. Replace with loop over dense list of objects
      1. Fewer conditionals
      2. Don't bring objects into cache unless you will use them
    3. Also
  5. Design patterns
    1. Factory
      1. Generate object
      2. For component: Get ID, insert into component tables
    2. Singleton
      1. Singleton = global (with defined creation time)
      2. Globals common in DOP
        1. All component tables
        2. Sizes and max IDs
      3. Globals ≠ bad
        1. Most DOP programmers consider global temporaries bad
        2. Global better than pointers passed through classes
        3. Pointers can be invalid, and just as hard to track
  6. Data Oriented Design patterns
    1. In-place transform
      • Loop over elements in sparse array, updating state
    2. A to B transform
      1. Loop over damaged characters, updating dead characters
      2. Non-list A: Traverse scene graph, generating render bins
    3. Generative transform
      • Procedurally generate list
    4. Sorted table
      1. Keep sorted or sort for use
      2. Often bubble sort is effective for incremental changes
    5. Multi-sorted
      1. Sort by multiple criteria
      2. Sort is just list of keys in order used
    6. Gatherer: Merge tables
    7. Tasker
      1. Break into parallel parts.
      2. Often combine with gather
    8. Dispatcher
      1. Process table, create sub tables