Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/LargeIslandSplitter.h
9906 views
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe1// SPDX-License-Identifier: MIT23#pragma once45#include <Jolt/Core/NonCopyable.h>6#include <Jolt/Core/Atomics.h>78JPH_NAMESPACE_BEGIN910class Body;11class BodyID;12class IslandBuilder;13class TempAllocator;14class Constraint;15class BodyManager;16class ContactConstraintManager;17class CalculateSolverSteps;1819/// Assigns bodies in large islands to multiple groups that can run in parallel20///21/// This basically implements what is described in: High-Performance Physical Simulations on Next-Generation Architecture with Many Cores by Chen et al.22/// See: http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf section "PARALLELIZATION METHODOLOGY"23class LargeIslandSplitter : public NonCopyable24{25private:26using SplitMask = uint32;2728public:29static constexpr uint cNumSplits = sizeof(SplitMask) * 8;30static constexpr uint cNonParallelSplitIdx = cNumSplits - 1;31static constexpr uint cLargeIslandTreshold = 128; ///< If the number of constraints + contacts in an island is larger than this, we will try to split the island3233/// Status code for retrieving a batch34enum class EStatus35{36WaitingForBatch, ///< Work is expected to be available later37BatchRetrieved, ///< Work is being returned38AllBatchesDone, ///< No further work is expected from this39};4041/// Describes a split of constraints and contacts42struct Split43{44inline uint GetNumContacts() const { return mContactBufferEnd - mContactBufferBegin; }45inline uint GetNumConstraints() const { return mConstraintBufferEnd - mConstraintBufferBegin; }46inline uint GetNumItems() const { return GetNumContacts() + GetNumConstraints(); }4748uint32 mContactBufferBegin; ///< Begin of the contact buffer (offset relative to mContactAndConstraintIndices)49uint32 mContactBufferEnd; ///< End of the contact buffer5051uint32 mConstraintBufferBegin; ///< Begin of the constraint buffer (offset relative to mContactAndConstraintIndices)52uint32 mConstraintBufferEnd; ///< End of the constraint buffer53};5455/// Structure that describes the resulting splits from the large island splitter56class Splits57{58public:59inline uint GetNumSplits() const60{61return mNumSplits;62}6364inline void GetConstraintsInSplit(uint inSplitIndex, uint32 &outConstraintsBegin, uint32 &outConstraintsEnd) const65{66const Split &split = mSplits[inSplitIndex];67outConstraintsBegin = split.mConstraintBufferBegin;68outConstraintsEnd = split.mConstraintBufferEnd;69}7071inline void GetContactsInSplit(uint inSplitIndex, uint32 &outContactsBegin, uint32 &outContactsEnd) const72{73const Split &split = mSplits[inSplitIndex];74outContactsBegin = split.mContactBufferBegin;75outContactsEnd = split.mContactBufferEnd;76}7778/// Reset current status so that no work can be picked up from this split79inline void ResetStatus()80{81mStatus.store(StatusItemMask, memory_order_relaxed);82}8384/// Make the first batch available to other threads85inline void StartFirstBatch()86{87uint split_index = mNumSplits > 0? 0 : cNonParallelSplitIdx;88mStatus.store(uint64(split_index) << StatusSplitShift, memory_order_release);89}9091/// Fetch the next batch to process92EStatus FetchNextBatch(uint32 &outConstraintsBegin, uint32 &outConstraintsEnd, uint32 &outContactsBegin, uint32 &outContactsEnd, bool &outFirstIteration);9394/// Mark a batch as processed95void MarkBatchProcessed(uint inNumProcessed, bool &outLastIteration, bool &outFinalBatch);9697enum EIterationStatus : uint6498{99StatusIterationMask = 0xffff000000000000,100StatusIterationShift = 48,101StatusSplitMask = 0x0000ffff00000000,102StatusSplitShift = 32,103StatusItemMask = 0x00000000ffffffff,104};105106static inline int sGetIteration(uint64 inStatus)107{108return int((inStatus & StatusIterationMask) >> StatusIterationShift);109}110111static inline uint sGetSplit(uint64 inStatus)112{113return uint((inStatus & StatusSplitMask) >> StatusSplitShift);114}115116static inline uint sGetItem(uint64 inStatus)117{118return uint(inStatus & StatusItemMask);119}120121Split mSplits[cNumSplits]; ///< Data per split122uint32 mIslandIndex; ///< Index of the island that was split123uint mNumSplits; ///< Number of splits that were created (excluding the non-parallel split)124int mNumIterations; ///< Number of iterations to do125int mNumVelocitySteps; ///< Number of velocity steps to do (cached for 2nd sub step)126int mNumPositionSteps; ///< Number of position steps to do127atomic<uint64> mStatus; ///< Status of the split, see EIterationStatus128atomic<uint> mItemsProcessed; ///< Number of items that have been marked as processed129};130131public:132/// Destructor133~LargeIslandSplitter();134135/// Prepare the island splitter by allocating memory136void Prepare(const IslandBuilder &inIslandBuilder, uint32 inNumActiveBodies, TempAllocator *inTempAllocator);137138/// Assign two bodies to a split. Returns the split index.139uint AssignSplit(const Body *inBody1, const Body *inBody2);140141/// Force a body to be in a non parallel split. Returns the split index.142uint AssignToNonParallelSplit(const Body *inBody);143144/// 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.145bool SplitIsland(uint32 inIslandIndex, const IslandBuilder &inIslandBuilder, const BodyManager &inBodyManager, const ContactConstraintManager &inContactManager, Constraint **inActiveConstraints, CalculateSolverSteps &ioStepsCalculator);146147/// Fetch the next batch to process, returns a handle in outSplitIslandIndex that must be provided to MarkBatchProcessed when complete148EStatus FetchNextBatch(uint &outSplitIslandIndex, uint32 *&outConstraintsBegin, uint32 *&outConstraintsEnd, uint32 *&outContactsBegin, uint32 *&outContactsEnd, bool &outFirstIteration);149150/// Mark a batch as processed151void MarkBatchProcessed(uint inSplitIslandIndex, const uint32 *inConstraintsBegin, const uint32 *inConstraintsEnd, const uint32 *inContactsBegin, const uint32 *inContactsEnd, bool &outLastIteration, bool &outFinalBatch);152153/// Get the island index of the island that was split for a particular split island index154inline uint32 GetIslandIndex(uint inSplitIslandIndex) const155{156JPH_ASSERT(inSplitIslandIndex < mNumSplitIslands);157return mSplitIslands[inSplitIslandIndex].mIslandIndex;158}159160/// Prepare the island splitter for iterating over the split islands again for position solving. Marks all batches as startable.161void PrepareForSolvePositions();162163/// Reset the island splitter164void Reset(TempAllocator *inTempAllocator);165166private:167static 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'168static constexpr uint cBatchSize = 16; ///< Number of items to process in a constraint batch169170uint32 mNumActiveBodies = 0; ///< Cached number of active bodies171172SplitMask * mSplitMasks = nullptr; ///< Bits that indicate for each body in the BodyManager::mActiveBodies list which split they already belong to173174uint32 * mContactAndConstraintsSplitIdx = nullptr; ///< Buffer to store the split index per constraint or contact175uint32 * mContactAndConstraintIndices = nullptr; ///< Buffer to store the ordered constraint indices per split176uint mContactAndConstraintsSize = 0; ///< Total size of mContactAndConstraintsSplitIdx and mContactAndConstraintIndices177atomic<uint> mContactAndConstraintsNextFree { 0 }; ///< Next element that is free in both buffers178179uint mNumSplitIslands = 0; ///< Total number of islands that required splitting180Splits * mSplitIslands = nullptr; ///< List of islands that required splitting181atomic<uint> mNextSplitIsland = 0; ///< Next split island to pick from mSplitIslands182};183184JPH_NAMESPACE_END185186187