Skip to content

Commit 1adfabc

Browse files
committed
implement deferred update + sparse
1 parent 303d2cb commit 1adfabc

3 files changed

Lines changed: 131 additions & 52 deletions

File tree

Frent/Updating/AttributeUpdateFilter.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ private void MultiThreadedUpdate()
148148
{
149149
const int LargeArchetypeThreshold = 16;
150150

151+
_updateCount!.Value = 0;
152+
_mulithreadedExceptions!.Clear();
153+
151154
var archetypes = _matchedArchetypes.AsSpan();
152155

153156
int largeCount = 0;
@@ -171,7 +174,7 @@ private void MultiThreadedUpdate()
171174
}
172175

173176
FrentMultithread.MultipleArchetypeWorkItem.UnsafeQueueWork(
174-
_world, _smallArchetypeUpdateRecords!, _methods, _updateCount!);
177+
_world, _smallArchetypeUpdateRecords!, _methods, _updateCount, _mulithreadedExceptions);
175178

176179
int maxChunkSize = Math.Max(largeCount / Environment.ProcessorCount, 256);
177180

@@ -181,19 +184,22 @@ private void MultiThreadedUpdate()
181184
for (int i = 0; i < entityCount; i += maxChunkSize)
182185
{
183186
FrentMultithread.SingleArchetypeWorkItem.UnsafeQueueWork(
184-
_world, archetypeRecord, _methods, _updateCount!,
187+
_world, archetypeRecord, _methods, _updateCount, _mulithreadedExceptions,
185188
start: i,
186189
count: Math.Min(maxChunkSize, entityCount - i));
187190
}
188191
}
189192

190193
Span<SparseUpdateMethod> sparseMethods = _sparseMethods.AsSpan(0, _sparseMethodsCount);
191194

192-
for (int i = 0; i < sparseMethods.Length; i++)
195+
for (int i = 0; i < sparseMethods.Length;)
193196
{
194197
ComponentSparseSetBase set = sparseMethods[i].SparseSet;
195198
if (set.Count == 0)
199+
{
200+
i++;
196201
continue;
202+
}
197203

198204
int start = i;
199205
do
@@ -202,7 +208,18 @@ private void MultiThreadedUpdate()
202208
} while (i < sparseMethods.Length && set == sparseMethods[i].SparseSet);
203209

204210
ArraySegment<SparseUpdateMethod> methods = new(_sparseMethods, start, i - start);
205-
FrentMultithread.SparseSetWorkItem.UnsafeQueueWork(_world, methods, _updateCount!);
211+
FrentMultithread.SparseSetWorkItem.UnsafeQueueWork(_world, methods, _updateCount, _mulithreadedExceptions);
212+
}
213+
214+
SpinWait spinWait = new();
215+
while (Volatile.Read(ref _updateCount.Value) != 0)
216+
{
217+
spinWait.SpinOnce();
218+
}
219+
220+
if (_mulithreadedExceptions.Count != 0)
221+
{
222+
throw new AggregateException(_mulithreadedExceptions);
206223
}
207224
}
208225

@@ -477,4 +494,4 @@ public void GetOneOrOther(
477494
metadataIndex = (int)_metadataIndexRoot;
478495
}
479496
}
480-
}
497+
}

Frent/Updating/SingleComponentUpdateFilter.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,16 @@ public SingleComponentUpdateFilter(World world, ComponentID component)
4646
_sparseSet = world.WorldSparseSetTable[component.SparseIndex];
4747

4848
_allRunners = new IRunner[methods.Length];
49-
_normalRunners = new IRunner[methods.Length - filters.Length];
50-
_filteredRunners = filters.Length == 0 ? [] : new (IDTypeFilter Filter, IRunner Runner)[filters.Length];
49+
50+
int filteredRunnerCount = 0;
51+
for (int i = 0; i < methods.Length; i++)
52+
{
53+
if ((uint)i < (uint)filters.Length && filters[i] is { } f && f != IDTypeFilter.None)
54+
filteredRunnerCount++;
55+
}
56+
57+
_normalRunners = new IRunner[methods.Length - filteredRunnerCount];
58+
_filteredRunners = filteredRunnerCount == 0 ? [] : new (IDTypeFilter Filter, IRunner Runner)[filteredRunnerCount];
5159

5260
int nIndex = 0;
5361
int fIndex = 0;
@@ -269,13 +277,19 @@ public void UpdateSubset(ReadOnlySpan<ArchetypeDeferredUpdateRecord> archetypes,
269277
{
270278
foreach ((Archetype archetype, Archetype _, int initalEntityCount) in archetypes)
271279
{
272-
Array buffer = archetype.Components.UnsafeArrayIndex(archetype.GetComponentIndex(componentID)).Buffer;
273-
int entityCount = archetype.EntityCount;
280+
int componentIndex = archetype.GetComponentIndex(componentID);
281+
if (componentIndex == 0)
282+
continue;
283+
284+
Array buffer = archetype.Components.UnsafeArrayIndex(componentIndex).Buffer;
285+
int entitiesToUpdate = archetype.EntityCount - initalEntityCount;
286+
if (entitiesToUpdate == 0)
287+
continue;
274288

275289
// average joe methods
276290
foreach (var runner in _normalRunners)
277291
{
278-
runner.RunArchetypical(buffer, archetype, world, 0, entityCount);
292+
runner.RunArchetypical(buffer, archetype, world, initalEntityCount, entitiesToUpdate);
279293
}
280294

281295
foreach ((IDTypeFilter filter, IRunner runner) in _filteredRunners)
@@ -285,15 +299,17 @@ public void UpdateSubset(ReadOnlySpan<ArchetypeDeferredUpdateRecord> archetypes,
285299
if (!filter.FilterArchetype(archetype))
286300
continue;
287301

288-
runner.RunArchetypical(buffer, archetype, world, 0, entityCount);
302+
runner.RunArchetypical(buffer, archetype, world, initalEntityCount, entitiesToUpdate);
289303
}
290304
}
291305
}
292306
else
293307
{
294-
throw new NotImplementedException();
308+
int entityId = 0;
309+
foreach (var runner in _allRunners)
310+
runner.RunSparseSubset(_sparseSet, world, ids, ref entityId);
295311
}
296312
}
297313

298314
record struct ArchetypeRecord(Archetype Archetype, int StorageIndex, int Length /*number of runners in the _filteredRunnersPerArchetype to update*/);
299-
}
315+
}

Frent/Updating/Threading/FrentMultithreadWorkItem.cs

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ private static readonly
2424
private World? _world;
2525
private ArraySegment<SparseUpdateMethod> _update;
2626
private StrongBox<int>? _counter;
27+
private Stack<Exception>? _exceptions;
2728

28-
public static void UnsafeQueueWork(World world, ArraySegment<SparseUpdateMethod> method, StrongBox<int> counter)
29+
public static void UnsafeQueueWork(World world, ArraySegment<SparseUpdateMethod> method, StrongBox<int> counter, Stack<Exception> exceptions)
2930
{
3031
if (method.Count == 0)
3132
return;
@@ -42,25 +43,39 @@ public static void UnsafeQueueWork(World world, ArraySegment<SparseUpdateMethod>
4243
workItem._world = world;
4344
workItem._update = method;
4445
workItem._counter = counter;
46+
workItem._exceptions = exceptions;
4547

4648
ThreadPool.UnsafeQueueUserWorkItem(static o =>
4749
{
4850
SparseSetWorkItem workItem = UnsafeExtensions.UnsafeCast<SparseSetWorkItem>(o!);
4951

50-
Span<SparseUpdateMethod> methods = workItem._update.AsSpan();
51-
ComponentSparseSetBase set = methods[0].SparseSet;
52+
try
53+
{
54+
Span<SparseUpdateMethod> methods = workItem._update.AsSpan();
55+
ComponentSparseSetBase set = methods[0].SparseSet;
5256

53-
foreach (SparseUpdateMethod method in methods)
57+
foreach (SparseUpdateMethod method in methods)
58+
{
59+
int entityId = 0;
60+
method.Runner.RunSparse(set, workItem._world!, ref entityId);
61+
}
62+
}
63+
catch (Exception e)
5464
{
55-
int entityId = 0;
56-
method.Runner.RunSparse(set, workItem._world!, ref entityId);
65+
lock (workItem._exceptions!)
66+
{
67+
workItem._exceptions.Push(e);
68+
}
69+
}
70+
finally
71+
{
72+
Interlocked.Decrement(ref workItem._counter!.Value);
5773
}
58-
59-
Interlocked.Decrement(ref workItem._counter!.Value);
6074

6175
workItem._world = default;
6276
workItem._update = default;
6377
workItem._counter = default;
78+
workItem._exceptions = default;
6479

6580
lock (s_poolLock)
6681
{
@@ -85,8 +100,9 @@ private static readonly
85100
private Stack<ArchetypeUpdateSpan>? _archetypes;
86101
private ArchetypeUpdateMethod[]? _componentStorageBases;
87102
private StrongBox<int>? _counter;
103+
private Stack<Exception>? _exceptions;
88104

89-
public static void UnsafeQueueWork(World world, Stack<ArchetypeUpdateSpan> archetypes, ArchetypeUpdateMethod[] componentStorageBases, StrongBox<int> counter)
105+
public static void UnsafeQueueWork(World world, Stack<ArchetypeUpdateSpan> archetypes, ArchetypeUpdateMethod[] componentStorageBases, StrongBox<int> counter, Stack<Exception> exceptions)
90106
{
91107
Interlocked.Increment(ref counter.Value);
92108

@@ -101,34 +117,48 @@ public static void UnsafeQueueWork(World world, Stack<ArchetypeUpdateSpan> arche
101117
workItem._componentStorageBases = componentStorageBases;
102118
workItem._world = world;
103119
workItem._counter = counter;
120+
workItem._exceptions = exceptions;
104121

105122
ThreadPool.UnsafeQueueUserWorkItem(static o =>
106123
{
107124
MultipleArchetypeWorkItem workItem = UnsafeExtensions.UnsafeCast<MultipleArchetypeWorkItem>(o!);
108125

109-
World world = workItem._world!;
110-
111-
while (workItem._archetypes!.TryPop(out var record))
126+
try
112127
{
113-
(Archetype archetype, int start, int count) = record;
114-
115-
Span<ArchetypeUpdateMethod> methods = workItem._componentStorageBases.AsSpan(start, count);
116-
ref ComponentStorageRecord storageStart = ref MemoryMarshal.GetArrayDataReference(archetype.Components);
128+
World world = workItem._world!;
117129

118-
foreach (var method in methods)
130+
while (workItem._archetypes!.TryPop(out var record))
119131
{
120-
Debug.Assert(method.Index < archetype.Components.Length);
132+
(Archetype archetype, int start, int count) = record;
121133

122-
method.Runner.RunArchetypical(Unsafe.Add(ref storageStart, method.Index).Buffer, archetype, world, 0, archetype.EntityCount);
134+
Span<ArchetypeUpdateMethod> methods = workItem._componentStorageBases.AsSpan(start, count);
135+
ref ComponentStorageRecord storageStart = ref MemoryMarshal.GetArrayDataReference(archetype.Components);
136+
137+
foreach (var method in methods)
138+
{
139+
Debug.Assert(method.Index < archetype.Components.Length);
140+
141+
method.Runner.RunArchetypical(Unsafe.Add(ref storageStart, method.Index).Buffer, archetype, world, 0, archetype.EntityCount);
142+
}
123143
}
124144
}
125-
126-
Interlocked.Decrement(ref workItem._counter!.Value);
145+
catch (Exception e)
146+
{
147+
lock (workItem._exceptions!)
148+
{
149+
workItem._exceptions.Push(e);
150+
}
151+
}
152+
finally
153+
{
154+
Interlocked.Decrement(ref workItem._counter!.Value);
155+
}
127156

128157
workItem._archetypes = default;
129158
workItem._componentStorageBases = default;
130159
workItem._world = default;
131160
workItem._counter = default;
161+
workItem._exceptions = default;
132162

133163
lock (s_poolLock)
134164
{
@@ -153,6 +183,7 @@ private static readonly
153183
private ArchetypeUpdateSpan _archetypeRecord;
154184
private ArchetypeUpdateMethod[]? _components;
155185
private StrongBox<int>? _counter;
186+
private Stack<Exception>? _exceptions;
156187
private int _start;
157188
private int _count;
158189

@@ -161,6 +192,7 @@ public static void UnsafeQueueWork(
161192
ArchetypeUpdateSpan archetypeUpdateRecord,
162193
ArchetypeUpdateMethod[] componentStorageBases,
163194
StrongBox<int> counter,
195+
Stack<Exception> exceptions,
164196
int start,
165197
int count)
166198
{
@@ -177,40 +209,54 @@ public static void UnsafeQueueWork(
177209
workItem._components = componentStorageBases;
178210
workItem._world = world;
179211
workItem._counter = counter;
212+
workItem._exceptions = exceptions;
180213
workItem._start = start;
181214
workItem._count = count;
182215

183216
ThreadPool.UnsafeQueueUserWorkItem(static o =>
184217
{
185218
SingleArchetypeWorkItem frentMultithreadWorkItem = UnsafeExtensions.UnsafeCast<SingleArchetypeWorkItem>(o!);
186219

187-
World world = frentMultithreadWorkItem._world!;
188-
(Archetype archetype, int start, int count) = frentMultithreadWorkItem._archetypeRecord;
189-
Span<ArchetypeUpdateMethod> methods = frentMultithreadWorkItem._components.AsSpan(start, count);
220+
try
221+
{
222+
World world = frentMultithreadWorkItem._world!;
223+
(Archetype archetype, int start, int count) = frentMultithreadWorkItem._archetypeRecord;
224+
Span<ArchetypeUpdateMethod> methods = frentMultithreadWorkItem._components.AsSpan(start, count);
225+
226+
int archetypeStart = frentMultithreadWorkItem._start;
227+
int archetypeCount = frentMultithreadWorkItem._count;
190228

191-
int archetypeStart = frentMultithreadWorkItem._start;
192-
int archetypeCount = frentMultithreadWorkItem._count;
229+
ref ComponentStorageRecord storageStart = ref MemoryMarshal.GetArrayDataReference(archetype.Components);
193230

194-
ref ComponentStorageRecord storageStart = ref MemoryMarshal.GetArrayDataReference(archetype.Components);
231+
foreach (var method in methods)
232+
{
233+
Debug.Assert(method.Index < archetype.Components.Length);
195234

196-
foreach (var method in methods)
235+
method.Runner.RunArchetypical(
236+
Unsafe.Add(ref storageStart, method.Index).Buffer,
237+
archetype,
238+
world,
239+
archetypeStart,
240+
archetypeCount);
241+
}
242+
}
243+
catch (Exception e)
197244
{
198-
Debug.Assert(method.Index < archetype.Components.Length);
199-
200-
method.Runner.RunArchetypical(
201-
Unsafe.Add(ref storageStart, method.Index).Buffer,
202-
archetype,
203-
world,
204-
archetypeStart,
205-
archetypeCount);
245+
lock (frentMultithreadWorkItem._exceptions!)
246+
{
247+
frentMultithreadWorkItem._exceptions.Push(e);
248+
}
249+
}
250+
finally
251+
{
252+
Interlocked.Decrement(ref frentMultithreadWorkItem._counter!.Value);
206253
}
207-
208-
Interlocked.Decrement(ref frentMultithreadWorkItem._counter!.Value);
209254

210255
frentMultithreadWorkItem._archetypeRecord = default;
211256
frentMultithreadWorkItem._components = default;
212257
frentMultithreadWorkItem._world = default;
213258
frentMultithreadWorkItem._counter = default;
259+
frentMultithreadWorkItem._exceptions = default;
214260

215261
lock (s_poolLock)
216262
{
@@ -221,4 +267,4 @@ public static void UnsafeQueueWork(
221267
}
222268

223269

224-
}
270+
}

0 commit comments

Comments
 (0)