Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/LargeIslandSplitter.h
9906 views
1
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
2
// SPDX-License-Identifier: MIT
3
4
#pragma once
5
6
#include <Jolt/Core/NonCopyable.h>
7
#include <Jolt/Core/Atomics.h>
8
9
JPH_NAMESPACE_BEGIN
10
11
class Body;
12
class BodyID;
13
class IslandBuilder;
14
class TempAllocator;
15
class Constraint;
16
class BodyManager;
17
class ContactConstraintManager;
18
class CalculateSolverSteps;
19
20
/// Assigns bodies in large islands to multiple groups that can run in parallel
21
///
22
/// This basically implements what is described in: High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al.
23
/// See: http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf section "PARALLELIZATION METHODOLOGY"
24
class LargeIslandSplitter : public NonCopyable
25
{
26
private:
27
using SplitMask = uint32;
28
29
public:
30
static constexpr uint cNumSplits = sizeof(SplitMask) * 8;
31
static constexpr uint cNonParallelSplitIdx = cNumSplits - 1;
32
static constexpr uint cLargeIslandTreshold = 128; ///< If the number of constraints + contacts in an island is larger than this, we will try to split the island
33
34
/// Status code for retrieving a batch
35
enum class EStatus
36
{
37
WaitingForBatch, ///< Work is expected to be available later
38
BatchRetrieved, ///< Work is being returned
39
AllBatchesDone, ///< No further work is expected from this
40
};
41
42
/// Describes a split of constraints and contacts
43
struct Split
44
{
45
inline uint GetNumContacts() const { return mContactBufferEnd - mContactBufferBegin; }
46
inline uint GetNumConstraints() const { return mConstraintBufferEnd - mConstraintBufferBegin; }
47
inline uint GetNumItems() const { return GetNumContacts() + GetNumConstraints(); }
48
49
uint32 mContactBufferBegin; ///< Begin of the contact buffer (offset relative to mContactAndConstraintIndices)
50
uint32 mContactBufferEnd; ///< End of the contact buffer
51
52
uint32 mConstraintBufferBegin; ///< Begin of the constraint buffer (offset relative to mContactAndConstraintIndices)
53
uint32 mConstraintBufferEnd; ///< End of the constraint buffer
54
};
55
56
/// Structure that describes the resulting splits from the large island splitter
57
class Splits
58
{
59
public:
60
inline uint GetNumSplits() const
61
{
62
return mNumSplits;
63
}
64
65
inline void GetConstraintsInSplit(uint inSplitIndex, uint32 &outConstraintsBegin, uint32 &outConstraintsEnd) const
66
{
67
const Split &split = mSplits[inSplitIndex];
68
outConstraintsBegin = split.mConstraintBufferBegin;
69
outConstraintsEnd = split.mConstraintBufferEnd;
70
}
71
72
inline void GetContactsInSplit(uint inSplitIndex, uint32 &outContactsBegin, uint32 &outContactsEnd) const
73
{
74
const Split &split = mSplits[inSplitIndex];
75
outContactsBegin = split.mContactBufferBegin;
76
outContactsEnd = split.mContactBufferEnd;
77
}
78
79
/// Reset current status so that no work can be picked up from this split
80
inline void ResetStatus()
81
{
82
mStatus.store(StatusItemMask, memory_order_relaxed);
83
}
84
85
/// Make the first batch available to other threads
86
inline void StartFirstBatch()
87
{
88
uint split_index = mNumSplits > 0? 0 : cNonParallelSplitIdx;
89
mStatus.store(uint64(split_index) << StatusSplitShift, memory_order_release);
90
}
91
92
/// Fetch the next batch to process
93
EStatus FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration);
94
95
/// Mark a batch as processed
96
void MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch);
97
98
enum EIterationStatus : uint64
99
{
100
StatusIterationMask = 0xffff000000000000,
101
StatusIterationShift = 48,
102
StatusSplitMask = 0x0000ffff00000000,
103
StatusSplitShift = 32,
104
StatusItemMask = 0x00000000ffffffff,
105
};
106
107
static inline int sGetIteration(uint64 inStatus)
108
{
109
return int((inStatus & StatusIterationMask) >> StatusIterationShift);
110
}
111
112
static inline uint sGetSplit(uint64 inStatus)
113
{
114
return uint((inStatus & StatusSplitMask) >> StatusSplitShift);
115
}
116
117
static inline uint sGetItem(uint64 inStatus)
118
{
119
return uint(inStatus & StatusItemMask);
120
}
121
122
Split mSplits[cNumSplits]; ///< Data per split
123
uint32 mIslandIndex; ///< Index of the island that was split
124
uint mNumSplits; ///< Number of splits that were created (excluding the non-parallel split)
125
int mNumIterations; ///< Number of iterations to do
126
int mNumVelocitySteps; ///< Number of velocity steps to do (cached for 2nd sub step)
127
int mNumPositionSteps; ///< Number of position steps to do
128
atomic<uint64> mStatus; ///< Status of the split, see EIterationStatus
129
atomic<uint> mItemsProcessed; ///< Number of items that have been marked as processed
130
};
131
132
public:
133
/// Destructor
134
~LargeIslandSplitter();
135
136
/// Prepare the island splitter by allocating memory
137
void Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator);
138
139
/// Assign two bodies to a split. Returns the split index.
140
uint AssignSplit(const Body *inBody1, const Body *inBody2);
141
142
/// Force a body to be in a non parallel split. Returns the split index.
143
uint AssignToNonParallelSplit(const Body *inBody);
144
145
/// Splits up an island, the created splits will be added to the list of batches and can be fetched with FetchNextBatch. Returns false if the island did not need splitting.
146
bool SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator);
147
148
/// Fetch the next batch to process, returns a handle in outSplitIslandIndex that must be provided to MarkBatchProcessed when complete
149
EStatus FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration);
150
151
/// Mark a batch as processed
152
void MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch);
153
154
/// Get the island index of the island that was split for a particular split island index
155
inline uint32 GetIslandIndex(uint inSplitIslandIndex) const
156
{
157
JPH_ASSERT(inSplitIslandIndex < mNumSplitIslands);
158
return mSplitIslands[inSplitIslandIndex].mIslandIndex;
159
}
160
161
/// Prepare the island splitter for iterating over the split islands again for position solving. Marks all batches as startable.
162
void PrepareForSolvePositions();
163
164
/// Reset the island splitter
165
void Reset(TempAllocator *inTempAllocator);
166
167
private:
168
static constexpr uint cSplitCombineTreshold = 32; ///< If the number of constraints + contacts in a split is lower than this, we will merge this split into the 'non-parallel split'
169
static constexpr uint cBatchSize = 16; ///< Number of items to process in a constraint batch
170
171
uint32 mNumActiveBodies = 0; ///< Cached number of active bodies
172
173
SplitMask * mSplitMasks = nullptr; ///< Bits that indicate for each body in the BodyManager::mActiveBodies list which split they already belong to
174
175
uint32 * mContactAndConstraintsSplitIdx = nullptr; ///< Buffer to store the split index per constraint or contact
176
uint32 * mContactAndConstraintIndices = nullptr; ///< Buffer to store the ordered constraint indices per split
177
uint mContactAndConstraintsSize = 0; ///< Total size of mContactAndConstraintsSplitIdx and mContactAndConstraintIndices
178
atomic<uint> mContactAndConstraintsNextFree { 0 }; ///< Next element that is free in both buffers
179
180
uint mNumSplitIslands = 0; ///< Total number of islands that required splitting
181
Splits * mSplitIslands = nullptr; ///< List of islands that required splitting
182
atomic<uint> mNextSplitIsland = 0; ///< Next split island to pick from mSplitIslands
183
};
184
185
JPH_NAMESPACE_END
186
187