LargeIslandSplitter.h 8.1 KB

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