• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlange-42 / arche / 12635930476

06 Jan 2025 04:07PM CUT coverage: 100.0%. Remained the same
12635930476

push

github

web-flow
Use mask pointers in all tests and benchmarks (#460)

2 of 2 new or added lines in 1 file covered. (100.0%)

6538 of 6538 relevant lines covered (100.0%)

115498.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/ecs/archetype_node.go
1
package ecs
2

3
import (
4
        "reflect"
5
        "unsafe"
6

7
        "github.com/mlange-42/arche/ecs/stats"
8
)
9

10
// archNode is a node in the archetype graph.
11
type archNode struct {
12
        *nodeData
13
        Mask        Mask // Mask of the archetype
14
        Relation    ID
15
        HasRelation bool
16
        IsActive    bool
17
}
18

19
type nodeData struct {
20
        archetype         *archetype            // The single archetype for nodes without entity relation
21
        archetypeMap      map[Entity]*archetype // Mapping from relation targets to archetypes
22
        zeroPointer       unsafe.Pointer        // Points to zeroValue for fast access
23
        Types             []reflect.Type        // Component type per column
24
        Ids               []ID                  // List of component IDs
25
        freeIndices       []int32               // Indices of free/inactive archetypes
26
        zeroValue         []byte                // Used as source for setting storage to zero
27
        archetypes        pagedSlice[archetype] // Storage for archetypes in nodes with entity relation
28
        archetypeData     pagedSlice[archetypeData]
29
        neighbors         idMap[*archNode] // Mapping from component ID to add/remove, to the resulting archetype
30
        compIsPointer     *Mask            // Mapping from component IDs to whether they are or contain pointers.
31
        capacityIncrement uint32           // Capacity increment
32
}
33

34
func newNodeData(compIsPointer *Mask) nodeData {
1,390✔
35
        return nodeData{compIsPointer: compIsPointer}
1,390✔
36
}
1,390✔
37

38
// Creates a new archNode
39
func newArchNode(mask Mask, data *nodeData, relation ID, hasRelation bool, capacityIncrement int, components []componentType) archNode {
1,400✔
40
        var arch map[Entity]*archetype
1,400✔
41
        if hasRelation {
1,454✔
42
                arch = map[Entity]*archetype{}
54✔
43
        }
54✔
44
        ids := make([]ID, len(components))
1,400✔
45
        types := make([]reflect.Type, len(components))
1,400✔
46

1,400✔
47
        var maxSize uintptr = 0
1,400✔
48
        prev := -1
1,400✔
49
        for i, c := range components {
6,880✔
50
                if int(c.ID.id) <= prev {
5,481✔
51
                        panic("component arguments must be sorted by ID")
1✔
52
                }
53
                prev = int(c.ID.id)
5,479✔
54

5,479✔
55
                ids[i] = c.ID
5,479✔
56
                types[i] = c.Type
5,479✔
57
                size, align := c.Type.Size(), uintptr(c.Type.Align())
5,479✔
58
                size = (size + (align - 1)) / align * align
5,479✔
59
                if size > maxSize {
6,723✔
60
                        maxSize = size
1,244✔
61
                }
1,244✔
62
        }
63

64
        var zeroValue []byte
1,399✔
65
        var zeroPointer unsafe.Pointer
1,399✔
66
        if maxSize > 0 {
2,635✔
67
                zeroValue = make([]byte, maxSize)
1,236✔
68
                zeroPointer = unsafe.Pointer(&zeroValue[0])
1,236✔
69
        }
1,236✔
70

71
        data.Ids = ids
1,399✔
72
        data.Types = types
1,399✔
73
        data.archetypeMap = arch
1,399✔
74
        data.capacityIncrement = uint32(capacityIncrement)
1,399✔
75
        data.zeroValue = zeroValue
1,399✔
76
        data.zeroPointer = zeroPointer
1,399✔
77
        data.neighbors = newIDMap[*archNode]()
1,399✔
78

1,399✔
79
        return archNode{
1,399✔
80
                nodeData:    data,
1,399✔
81
                Mask:        mask,
1,399✔
82
                Relation:    relation,
1,399✔
83
                HasRelation: hasRelation,
1,399✔
84
        }
1,399✔
85
}
86

87
// Matches the archetype node against a filter.
88
// Ignores the relation target.
89
func (a *archNode) Matches(f Filter) bool {
81,551✔
90
        return f.Matches(&a.Mask)
81,551✔
91
}
81,551✔
92

93
// Archetypes of the node.
94
// Returns a single wrapped archetype if there are no relations.
95
// Returns nil if the node has no archetype(s).
96
func (a *archNode) Archetypes() archetypes {
155,079✔
97
        if a.archetype == nil {
205,377✔
98
                return &a.archetypes
50,298✔
99
        }
50,298✔
100
        return singleArchetype{Archetype: a.archetype}
104,781✔
101
}
102

103
// GetArchetype returns the archetype for the given relation target.
104
//
105
// The target is ignored if the node has no relation component.
106
func (a *archNode) GetArchetype(target Entity) (*archetype, bool) {
1,067,094✔
107
        if a.HasRelation {
1,100,151✔
108
                arch, ok := a.archetypeMap[target]
33,057✔
109
                return arch, ok
33,057✔
110
        }
33,057✔
111
        return a.archetype, a.archetype != nil
1,034,037✔
112
}
113

114
// SetArchetype sets the archetype for a node without a relation.
115
//
116
// Do not use on nodes without a relation component!
117
func (a *archNode) SetArchetype(arch *archetype) {
1,299✔
118
        a.archetype = arch
1,299✔
119
}
1,299✔
120

121
// CreateArchetype creates a new archetype in nodes with relation component.
122
func (a *archNode) CreateArchetype(layouts uint8, target Entity) *archetype {
27,616✔
123
        var arch *archetype
27,616✔
124
        var archIndex int32
27,616✔
125
        lenFree := len(a.freeIndices)
27,616✔
126
        if lenFree > 0 {
55,020✔
127
                archIndex = a.freeIndices[lenFree-1]
27,404✔
128
                arch = a.archetypes.Get(archIndex)
27,404✔
129
                a.freeIndices = a.freeIndices[:lenFree-1]
27,404✔
130
                arch.Activate(target, archIndex)
27,404✔
131
        } else {
27,616✔
132
                a.archetypes.Add(archetype{})
212✔
133
                a.archetypeData.Add(archetypeData{})
212✔
134
                archIndex := a.archetypes.Len() - 1
212✔
135
                arch = a.archetypes.Get(archIndex)
212✔
136
                arch.Init(a, a.archetypeData.Get(archIndex), archIndex, true, layouts, target)
212✔
137
        }
212✔
138
        a.archetypeMap[target] = arch
27,616✔
139
        return arch
27,616✔
140
}
141

142
func (a *archNode) ExtendArchetypeLayouts(count uint8) {
9✔
143
        if !a.IsActive {
10✔
144
                return
1✔
145
        }
1✔
146

147
        if !a.HasRelation {
14✔
148
                a.archetype.ExtendLayouts(count)
6✔
149
                return
6✔
150
        }
6✔
151

152
        lenArches := a.archetypes.Len()
2✔
153
        var j int32
2✔
154
        for j = 0; j < lenArches; j++ {
4✔
155
                arch := a.archetypes.Get(j)
2✔
156
                arch.ExtendLayouts(count)
2✔
157
        }
2✔
158
}
159

160
// RemoveArchetype de-activates an archetype.
161
// The archetype will be re-used by CreateArchetype.
162
func (a *archNode) RemoveArchetype(arch *archetype) {
27,427✔
163
        delete(a.archetypeMap, arch.RelationTarget)
27,427✔
164
        idx := arch.index
27,427✔
165
        a.freeIndices = append(a.freeIndices, idx)
27,427✔
166
        a.archetypes.Get(idx).Deactivate()
27,427✔
167
}
27,427✔
168

169
// Reset resets the archetypes in this node.
170
// Relation archetypes with non-zero target are de-activated for re-use.
171
func (a *archNode) Reset(cache *Cache) {
97✔
172
        if !a.IsActive {
102✔
173
                return
5✔
174
        }
5✔
175
        if !a.HasRelation {
156✔
176
                a.archetype.Reset()
64✔
177
                return
64✔
178
        }
64✔
179

180
        lenArches := a.archetypes.Len()
28✔
181
        var j int32
28✔
182
        for j = 0; j < lenArches; j++ {
2,439✔
183
                arch := a.archetypes.Get(j)
2,411✔
184
                if !arch.IsActive() {
2,413✔
185
                        continue
2✔
186
                }
187
                if !arch.RelationTarget.IsZero() {
4,816✔
188
                        a.RemoveArchetype(arch)
2,407✔
189
                        cache.removeArchetype(arch)
2,407✔
190
                } else {
2,409✔
191
                        arch.Reset()
2✔
192
                }
2✔
193
        }
194
}
195

196
// Stats generates statistics for an archetype node.
197
func (a *archNode) Stats(reg *componentRegistry) stats.Node {
13✔
198
        ids := a.Ids
13✔
199
        aCompCount := len(ids)
13✔
200
        aTypes := make([]reflect.Type, aCompCount)
13✔
201
        for j, id := range ids {
24✔
202
                aTypes[j], _ = reg.ComponentType(id.id)
11✔
203
        }
11✔
204

205
        arches := a.Archetypes()
13✔
206
        var numArches int32
13✔
207
        cap := 0
13✔
208
        count := 0
13✔
209
        memory := 0
13✔
210
        var archStats []stats.Archetype
13✔
211
        if arches != nil {
26✔
212
                numArches = arches.Len()
13✔
213
                archStats = make([]stats.Archetype, numArches)
13✔
214
                var i int32
13✔
215
                for i = 0; i < numArches; i++ {
125✔
216
                        archStats[i] = arches.Get(i).Stats(reg)
112✔
217
                        stats := &archStats[i]
112✔
218
                        cap += stats.Capacity
112✔
219
                        count += stats.Size
112✔
220
                        memory += stats.Memory
112✔
221
                }
112✔
222
        }
223

224
        memPerEntity := 0
13✔
225
        intIDs := make([]uint8, len(ids))
13✔
226
        for j, id := range ids {
24✔
227
                intIDs[j] = id.id
11✔
228
                memPerEntity += int(aTypes[j].Size())
11✔
229
        }
11✔
230

231
        return stats.Node{
13✔
232
                ArchetypeCount:       int(numArches),
13✔
233
                ActiveArchetypeCount: int(numArches) - len(a.freeIndices),
13✔
234
                IsActive:             a.IsActive,
13✔
235
                HasRelation:          a.HasRelation,
13✔
236
                Components:           aCompCount,
13✔
237
                ComponentIDs:         intIDs,
13✔
238
                ComponentTypes:       aTypes,
13✔
239
                Memory:               memory,
13✔
240
                MemoryPerEntity:      memPerEntity,
13✔
241
                Size:                 count,
13✔
242
                Capacity:             cap,
13✔
243
                Archetypes:           archStats,
13✔
244
        }
13✔
245
}
246

247
// UpdateStats updates statistics for an archetype node.
248
func (a *archNode) UpdateStats(stats *stats.Node, reg *componentRegistry) {
150,022✔
249
        if !a.IsActive {
150,023✔
250
                return
1✔
251
        }
1✔
252

253
        arches := a.Archetypes()
150,021✔
254

150,021✔
255
        if !stats.IsActive {
150,022✔
256
                temp := a.Stats(reg)
1✔
257
                *stats = temp
1✔
258
                return
1✔
259
        }
1✔
260

261
        cap := 0
150,020✔
262
        count := 0
150,020✔
263
        memory := 0
150,020✔
264

150,020✔
265
        cntOld := int32(len(stats.Archetypes))
150,020✔
266
        cntNew := int32(arches.Len())
150,020✔
267
        var i int32
150,020✔
268
        for i = 0; i < cntOld; i++ {
5,249,944✔
269
                arch := &stats.Archetypes[i]
5,099,924✔
270
                arches.Get(i).UpdateStats(stats, arch, reg)
5,099,924✔
271
                cap += arch.Capacity
5,099,924✔
272
                count += arch.Size
5,099,924✔
273
                memory += arch.Memory
5,099,924✔
274
        }
5,099,924✔
275
        for i = cntOld; i < cntNew; i++ {
150,021✔
276
                arch := arches.Get(i).Stats(reg)
1✔
277
                stats.Archetypes = append(stats.Archetypes, arch)
1✔
278
                cap += arch.Capacity
1✔
279
                count += arch.Size
1✔
280
                memory += arch.Memory
1✔
281
        }
1✔
282

283
        stats.IsActive = true
150,020✔
284
        stats.ArchetypeCount = int(cntNew)
150,020✔
285
        stats.ActiveArchetypeCount = int(cntNew) - len(a.freeIndices)
150,020✔
286
        stats.Capacity = cap
150,020✔
287
        stats.Size = count
150,020✔
288
        stats.Memory = memory
150,020✔
289
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc