Cleanups and unification of storage protocol

This commit is contained in:
Christian Treffs 2017-10-31 12:18:07 +01:00
parent 4ada634130
commit 555e2088bb
7 changed files with 74 additions and 117 deletions

View File

@ -5,82 +5,82 @@
// Created by Christian Treffs on 28.10.17.
//
private let pow2: [Int] = [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296
]
private func nearestToPow2(_ value: Int) -> Int {
let exp = (value.bitWidth-value.leadingZeroBitCount)
return pow2[exp]
}
public protocol ManagedContiguousArrayProtocol: class {
public protocol UniformStorage: class {
associatedtype Element
static var chunkSize: Int { get }
init(minCount: Int)
associatedtype Index
var count: Int { get }
func insert(_ element: Element, at index: Int)
func has(_ index: Int) -> Bool
func get(at index: Int) -> Element?
func remove(at index: Int)
func add(_ element: Element, at index: Index)
func has(_ index: Index) -> Bool
func get(at index: Index) -> Element?
func remove(at index: Index)
func clear(keepingCapacity: Bool)
}
public class ManagedContiguousArray: ManagedContiguousArrayProtocol {
public class ManagedContiguousArray: UniformStorage {
public static var chunkSize: Int = 4096
public typealias Index = Int
public typealias Element = Any
var _count: Int = 0
var _size: Int = 0
var _store: ContiguousArray<Element?> = []
public required init(minCount: Int = chunkSize) {
public init(minCount: Int = chunkSize) {
_store = ContiguousArray<Element?>(repeating: nil, count: minCount)
}
deinit {
clear()
}
public var count: Int {
return _count
return _size
}
public func insert(_ element: Element, at index: Int) {
public func add(_ element: Element, at index: Index) {
if needsToGrow(index) {
grow(including: index)
}
if _store[index] == nil {
_count += 1
_size += 1
}
_store[index] = element
}
public func has(_ index: Int) -> Bool {
public func has(_ index: Index) -> Bool {
if _store.count <= index { return false }
return _store[index] != nil
}
public func get(at index: Int) -> Element? {
public func get(at index: Index) -> Element? {
return _store[index]
}
public func remove(at index: Int) {
public func remove(at index: Index) {
if _store[index] != nil {
_count -= 1
_size -= 1
}
_store[index] = nil
if _size == 0 {
clear()
}
return _store[index] = nil
}
internal func needsToGrow(_ index: Int) -> Bool {
public func clear(keepingCapacity: Bool = false) {
_size = 0
_store.removeAll(keepingCapacity: keepingCapacity)
}
internal func needsToGrow(_ index: Index) -> Bool {
return index > _store.count - 1
}
internal func grow(including index: Int) {
//var t = Timer()
//t.start()
internal func grow(including index: Index) {
let newCapacity: Int = nearest(to: index)
let count: Int = newCapacity-_store.count
//_store.reserveCapacity(newCapacity)
for _ in 0..<count {
let newCount: Int = newCapacity-_store.count
for _ in 0..<newCount {
_store.append(nil)
}
//t.stop()
//print("did grow to \(newCapacity) in \(t.milliSeconds)ms")
}
internal func nearest(to index: Int) -> Int {
internal func nearest(to index: Index) -> Int {
let delta = Float(index) / Float(ManagedContiguousArray.chunkSize)
let multiplier = Int(delta) + 1
return multiplier * ManagedContiguousArray.chunkSize
@ -88,44 +88,10 @@ public class ManagedContiguousArray: ManagedContiguousArrayProtocol {
}
public class ContiguousComponentArray: ManagedContiguousArray {
public typealias Element = Component
public typealias Element = Component
public typealias Index = EntityIndex
}
public class ContiguousEntityIdArray: ManagedContiguousArray {
public typealias Element = EntityIdentifier
}
/*
public func insert(_ element: Element, at entityIdx: EntityIndex) {
super.insert(element, at: entityIdx)
}
public func has(_ entityIdx: EntityIndex) -> Bool {
if _store.count <= entityIdx { return false }
return _store[entityIdx] != nil
}
public func get(at entityIdx: EntityIndex) -> Element? {
return _store[entityIdx]
}
public func remove(at entityIdx: EntityIndex) {
return _store[entityIdx] = nil
}
fileprivate func needsToGrow(_ entityId: EntityIndex) -> Bool {
return entityId > _store.count - 1
}
fileprivate func grow(to minIndex: Int) {
let newCapacity: Int = nearestToPow2(minIndex)
let count: Int = newCapacity-_store.count
let nilElements: ContiguousArray<Element?> = ContiguousArray<Element?>.init(repeating: nil, count: count)
_store.reserveCapacity(newCapacity)
_store.append(contentsOf: nilElements)
}
*/

View File

@ -17,7 +17,7 @@ extension Nexus {
public func has(componentId: ComponentIdentifier, entityIdx: EntityIndex) -> Bool {
guard let uniforms = componentsByType[componentId] else { return false }
return uniforms.contains(entityIdx)
return uniforms.has(entityIdx)
}
public func count(components entityId: EntityIdentifier) -> Int {
@ -39,12 +39,10 @@ extension Nexus {
// TODO: replace component?! copy properties?!
return
}
if componentsByType[componentId] != nil {
componentsByType[componentId]!.add(component, with: entityIdx)
} else {
componentsByType[componentId] = SparseComponentSet()
componentsByType[componentId]!.add(component, with: entityIdx)
if componentsByType[componentId] == nil {
componentsByType[componentId] = UniformComponents()
}
componentsByType[componentId]!.add(component, at: entityIdx)
// assigns the component id to the entity id
if componentIdsByEntity[entityIdx] != nil {
@ -70,8 +68,8 @@ extension Nexus {
}
public func get(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? {
guard let uniformComponents: SparseComponentSet = componentsByType[componentId] else { return nil }
return uniformComponents.get(at: entityId.index)
guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil }
return uniformComponents.get(at: entityId.index) as? Component
}
public func get<C>(for entityId: EntityIdentifier) -> C? where C: Component {
@ -80,7 +78,7 @@ extension Nexus {
}
fileprivate func get<C>(componentId: ComponentIdentifier, entityIdx: EntityIndex) -> C? where C: Component {
guard let uniformComponents: SparseComponentSet = componentsByType[componentId] else { return nil }
guard let uniformComponents: UniformComponents = componentsByType[componentId] else { return nil }
return uniformComponents.get(at: entityIdx) as? C
}
@ -94,7 +92,7 @@ extension Nexus {
let hash: EntityComponentHash = componentId.hashValue(using: entityIdx)
// MARK: delete component instance
componentsByType[componentId]?.remove(entityIdx)
componentsByType[componentId]?.remove(at: entityIdx)
// MARK: unassign component
guard let removeIndex: ComponentIdsByEntityIndex = componentIdsByEntityLookup.removeValue(forKey: hash) else {

View File

@ -9,7 +9,7 @@
public typealias EntityComponentHash = Int
public typealias ComponentIdsByEntityIndex = Int
public typealias ComponentTypeHash = Int // component object identifier hash value
//public typealias UniformComponents = SparseComponentSet
public typealias UniformComponents = ContiguousComponentArray
public typealias ComponentIdentifiers = ContiguousArray<ComponentIdentifier>
public typealias ComponentSet = Set<ComponentIdentifier>
@ -28,7 +28,7 @@ public class Nexus {
/// - Key: component type identifier
/// - Value: each element is a component instance of the same type (uniform). New component instances are appended.
var componentsByType: [ComponentIdentifier: SparseComponentSet]
var componentsByType: [ComponentIdentifier: UniformComponents]
/// - Key: entity id as index
/// - Value: each element is a component identifier associated with this entity

View File

@ -5,8 +5,10 @@
// Created by Christian Treffs on 30.10.17.
//
public class SparseComponentSet {
public typealias Element = Component
public class SparseSet: UniformStorage {
public typealias Element = Any
public typealias Index = Int
fileprivate typealias ComponentIdx = Int
fileprivate var size: Int = 0
fileprivate var dense: ContiguousArray<Pair?>
@ -26,42 +28,37 @@ public class SparseComponentSet {
internal var capacitySparse: Int { return sparse.capacity }
internal var capacityDense: Int { return dense.capacity }
public func contains(_ entityIdx: EntityIndex ) -> Bool {
return sparse[entityIdx] != nil &&
sparse[entityIdx]! < count &&
dense[sparse[entityIdx]!] != nil
public func has(_ index: EntityIndex) -> Bool {
return sparse[index] ?? Int.max < count &&
dense[sparse[index]!] != nil
}
@discardableResult
public func add(_ element: Element, with entityIdx: EntityIndex ) -> Bool {
if contains(entityIdx) { return false }
sparse[entityIdx] = count
let entry: Pair = Pair(key: entityIdx, value: element)
public func add(_ element: Element, at index: EntityIndex) {
if has(index) { return }
sparse[index] = count
let entry: Pair = Pair(key: index, value: element)
dense.append(entry)
size += 1
return true
}
public func get(at entityIdx: EntityIndex) -> Element? {
guard contains(entityIdx) else { return nil }
guard has(entityIdx) else { return nil }
return dense[sparse[entityIdx]!]!.value
}
@discardableResult
public func remove(_ entityIdx: EntityIndex ) -> Element? {
guard contains(entityIdx) else { return nil }
let compIdx: ComponentIdx = sparse[entityIdx]!
public func remove(at index: EntityIndex) {
guard has(index) else { return }
let compIdx: ComponentIdx = sparse[index]!
let lastIdx: ComponentIdx = count-1
dense.swapAt(compIdx, lastIdx)
sparse[entityIdx] = nil
sparse[index] = nil
let swapped: Pair = dense[compIdx]!
sparse[swapped.key] = compIdx
let removed: Pair = dense.popLast()!!
_ = dense.popLast()!!
size -= 1
if size == 0 {
clear(keepingCapacity: false)
}
return removed.value
}
public func clear(keepingCapacity: Bool = false) {
@ -72,7 +69,7 @@ public class SparseComponentSet {
}
extension SparseComponentSet: Sequence {
extension SparseSet: Sequence {
public func makeIterator() -> AnyIterator<Element> {
var iterator = dense.makeIterator()
@ -81,3 +78,8 @@ extension SparseComponentSet: Sequence {
}
}
}
public class SparseComponentSet: SparseSet {
public typealias Element = Component
public typealias Index = EntityIndex
}

View File

@ -47,11 +47,10 @@ class ExampleSystem {
}
func update(deltaT: Double) {
family.iterate(components: Position.self, Velocity.self, Name.self) { (_, positionGetter, velocityGetter, nameGetter) in
family.iterate(components: Position.self, Velocity.self, Name.self) { (_, positionGetter, velocityGetter, _) in
let position: Position = positionGetter!
let velocity: Velocity = velocityGetter!
let name: Name? = nameGetter
position.x *= 2
velocity.a *= 2

View File

@ -162,12 +162,4 @@ class NexusTests: XCTestCase {
}
func testComponentStorage() {
let nexus = Nexus()
let a = nexus.create()
let b = nexus.create()
let c = nexus.create()
}
}

View File

@ -16,7 +16,7 @@ class SparseComponentSetTests: XCTestCase {
let num: Int = 100
for i in 0..<num {
s.add(Position(x: i, y: i), with: EntityIndex(i))
s.add(Position(x: i, y: i), at: EntityIndex(i))
}
XCTAssert(s.count == num)
@ -28,7 +28,7 @@ class SparseComponentSetTests: XCTestCase {
}
for i in 0..<num {
s.remove(EntityIndex(i))
s.remove(at: EntityIndex(i))
}
XCTAssert(s.count == 0)