summaryrefslogtreecommitdiff
path: root/libs/ode-0.16.1/ode/src/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ode-0.16.1/ode/src/util.cpp')
-rw-r--r--libs/ode-0.16.1/ode/src/util.cpp1231
1 files changed, 1231 insertions, 0 deletions
diff --git a/libs/ode-0.16.1/ode/src/util.cpp b/libs/ode-0.16.1/ode/src/util.cpp
new file mode 100644
index 0000000..17b9e8a
--- /dev/null
+++ b/libs/ode-0.16.1/ode/src/util.cpp
@@ -0,0 +1,1231 @@
+/*************************************************************************
+ * *
+ * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
+ * All rights reserved. Email: russ@q12.org Web: www.q12.org *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of EITHER: *
+ * (1) The GNU Lesser General Public License as published by the Free *
+ * Software Foundation; either version 2.1 of the License, or (at *
+ * your option) any later version. The text of the GNU Lesser *
+ * General Public License is included with this library in the *
+ * file LICENSE.TXT. *
+ * (2) The BSD-style license that is included with this library in *
+ * the file LICENSE-BSD.TXT. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
+ * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
+ * *
+ *************************************************************************/
+
+#include <ode/ode.h>
+#include "config.h"
+#include "util.h"
+#include "objects.h"
+#include "joints/joint.h"
+#include "threadingutils.h"
+
+#include <new>
+
+
+#define dMIN(A,B) ((A)>(B) ? (B) : (A))
+#define dMAX(A,B) ((B)>(A) ? (B) : (A))
+
+
+//****************************************************************************
+// Malloc based world stepping memory manager
+
+/*extern */dxWorldProcessMemoryManager g_WorldProcessMallocMemoryManager(dAlloc, dRealloc, dFree);
+/*extern */dxWorldProcessMemoryReserveInfo g_WorldProcessDefaultReserveInfo(dWORLDSTEP_RESERVEFACTOR_DEFAULT, dWORLDSTEP_RESERVESIZE_DEFAULT);
+
+
+//****************************************************************************
+// dxWorldProcessContext
+
+const char *const dxWorldProcessContext::m_aszContextMutexNames[dxPCM__MAX] =
+{
+ "Stepper Arena Obtain Lock" , // dxPCM_STEPPER_ARENA_OBTAIN,
+ "Joint addLimot Serialize Lock" , // dxPCM_STEPPER_ADDLIMOT_SERIALIZE
+ "Stepper StepBody Serialize Lock" , // dxPCM_STEPPER_STEPBODY_SERIALIZE,
+};
+
+dxWorldProcessContext::dxWorldProcessContext():
+ m_pmaIslandsArena(NULL),
+ m_pmaStepperArenas(NULL),
+ m_pswObjectsAllocWorld(NULL),
+ m_pmgStepperMutexGroup(NULL),
+ m_pcwIslandsSteppingWait(NULL)
+{
+ // Do nothing
+}
+
+dxWorldProcessContext::~dxWorldProcessContext()
+{
+ dIASSERT((m_pswObjectsAllocWorld != NULL) == (m_pmgStepperMutexGroup != NULL));
+ dIASSERT((m_pswObjectsAllocWorld != NULL) == (m_pcwIslandsSteppingWait != NULL));
+
+ if (m_pswObjectsAllocWorld != NULL)
+ {
+ m_pswObjectsAllocWorld->FreeMutexGroup(m_pmgStepperMutexGroup);
+ // m_pswObjectsAllocWorld->FreeThreadedCallWait(m_pcwIslandsSteppingWait); -- The stock call wait can not be freed
+ }
+
+ dxWorldProcessMemArena *pmaStepperArenas = m_pmaStepperArenas;
+ if (pmaStepperArenas != NULL)
+ {
+ FreeArenasList(pmaStepperArenas);
+ }
+
+ if (m_pmaIslandsArena != NULL)
+ {
+ dxWorldProcessMemArena::FreeMemArena(m_pmaIslandsArena);
+ }
+}
+
+void dxWorldProcessContext::CleanupWorldReferences(dxWorld *pswWorldInstance)
+{
+ dIASSERT((m_pswObjectsAllocWorld != NULL) == (m_pmgStepperMutexGroup != NULL));
+ dIASSERT((m_pswObjectsAllocWorld != NULL) == (m_pcwIslandsSteppingWait != NULL));
+
+ if (m_pswObjectsAllocWorld == pswWorldInstance)
+ {
+ m_pswObjectsAllocWorld->FreeMutexGroup(m_pmgStepperMutexGroup);
+ // m_pswObjectsAllocWorld->FreeThreadedCallWait(m_pcwIslandsSteppingWait); -- The stock call wait can not be freed
+
+ m_pswObjectsAllocWorld = NULL;
+ m_pmgStepperMutexGroup = NULL;
+ m_pcwIslandsSteppingWait = NULL;
+ }
+}
+
+bool dxWorldProcessContext::EnsureStepperSyncObjectsAreAllocated(dxWorld *pswWorldInstance)
+{
+ dIASSERT((m_pswObjectsAllocWorld != NULL) == (m_pmgStepperMutexGroup != NULL));
+ dIASSERT((m_pswObjectsAllocWorld != NULL) == (m_pcwIslandsSteppingWait != NULL));
+
+ bool bResult = false;
+
+ dMutexGroupID pmbStepperMutexGroup = NULL;
+ bool bStepperMutexGroupAllocated = false;
+
+ do
+ {
+ if (m_pswObjectsAllocWorld == NULL)
+ {
+ pmbStepperMutexGroup = pswWorldInstance->AllocMutexGroup(dxPCM__MAX, m_aszContextMutexNames);
+ if (pmbStepperMutexGroup == NULL)
+ {
+ break;
+ }
+
+ bStepperMutexGroupAllocated = true;
+
+ dCallWaitID pcwIslandsSteppingWait = pswWorldInstance->AllocateOrRetrieveStockCallWaitID();
+ if (pcwIslandsSteppingWait == NULL)
+ {
+ break;
+ }
+
+ m_pswObjectsAllocWorld = pswWorldInstance;
+ m_pmgStepperMutexGroup = pmbStepperMutexGroup;
+ m_pcwIslandsSteppingWait = pcwIslandsSteppingWait;
+ }
+
+ bResult = true;
+ }
+ while (false);
+
+ if (!bResult)
+ {
+ if (bStepperMutexGroupAllocated)
+ {
+ pswWorldInstance->FreeMutexGroup(pmbStepperMutexGroup);
+ }
+ }
+
+ return bResult;
+}
+
+
+dxWorldProcessMemArena *dxWorldProcessContext::ObtainStepperMemArena()
+{
+ dxWorldProcessMemArena *pmaArenaInstance = NULL;
+
+ while (true)
+ {
+ dxWorldProcessMemArena *pmaRawArenasHead = GetStepperArenasHead();
+ if (pmaRawArenasHead == NULL)
+ {
+ break;
+ }
+
+ // Extraction must be locked so that other thread does not "steal" head arena,
+ // use it and then reinsert back with a different "next"
+ dxMutexGroupLockHelper lhLockHelper(m_pswObjectsAllocWorld, m_pmgStepperMutexGroup, dxPCM_STEPPER_ARENA_OBTAIN);
+
+ dxWorldProcessMemArena *pmaArenasHead = GetStepperArenasHead(); // Arenas head must be re-extracted after mutex has been locked
+ bool bExchangeResult = pmaArenasHead != NULL && TryExtractingStepperArenasHead(pmaArenasHead);
+
+ lhLockHelper.UnlockMutex();
+
+ if (bExchangeResult)
+ {
+ pmaArenasHead->ResetState();
+ pmaArenaInstance = pmaArenasHead;
+ break;
+ }
+ }
+
+ return pmaArenaInstance;
+}
+
+void dxWorldProcessContext::ReturnStepperMemArena(dxWorldProcessMemArena *pmaArenaInstance)
+{
+ while (true)
+ {
+ dxWorldProcessMemArena *pmaArenasHead = GetStepperArenasHead();
+ pmaArenaInstance->SetNextMemArena(pmaArenasHead);
+
+ if (TryInsertingStepperArenasHead(pmaArenaInstance, pmaArenasHead))
+ {
+ break;
+ }
+ }
+}
+
+
+dxWorldProcessMemArena *dxWorldProcessContext::ReallocateIslandsMemArena(sizeint nMemoryRequirement,
+ const dxWorldProcessMemoryManager *pmmMemortManager, float fReserveFactor, unsigned uiReserveMinimum)
+{
+ dxWorldProcessMemArena *pmaExistingArena = GetIslandsMemArena();
+ dxWorldProcessMemArena *pmaNewMemArena = dxWorldProcessMemArena::ReallocateMemArena(pmaExistingArena, nMemoryRequirement, pmmMemortManager, fReserveFactor, uiReserveMinimum);
+ SetIslandsMemArena(pmaNewMemArena);
+
+ pmaNewMemArena->ResetState();
+
+ return pmaNewMemArena;
+}
+
+bool dxWorldProcessContext::ReallocateStepperMemArenas(
+ dxWorld *world, unsigned nIslandThreadsCount, sizeint nMemoryRequirement,
+ const dxWorldProcessMemoryManager *pmmMemortManager, float fReserveFactor, unsigned uiReserveMinimum)
+{
+ dxWorldProcessMemArena *pmaRebuiltArenasHead = NULL, *pmaRebuiltArenasTail = NULL;
+ dxWorldProcessMemArena *pmaExistingArenas = GetStepperArenasList();
+ unsigned nArenasToProcess = nIslandThreadsCount;
+
+ (void)world; // unused
+
+ // NOTE!
+ // The list is reallocated in a way to assure the largest arenas are at end
+ // and if number of threads decreases they will be freed first of all.
+
+ while (true)
+ {
+ if (nArenasToProcess == 0)
+ {
+ FreeArenasList(pmaExistingArenas);
+ break;
+ }
+
+ dxWorldProcessMemArena *pmaOldMemArena = pmaExistingArenas;
+
+ if (pmaExistingArenas != NULL)
+ {
+ pmaExistingArenas = pmaExistingArenas->GetNextMemArena();
+ }
+ else
+ {
+ // If existing arenas ended, terminate and erase tail so that new arenas
+ // would be appended to list head.
+ if (pmaRebuiltArenasTail != NULL)
+ {
+ pmaRebuiltArenasTail->SetNextMemArena(NULL);
+ pmaRebuiltArenasTail = NULL;
+ }
+ }
+
+ dxWorldProcessMemArena *pmaNewMemArena = dxWorldProcessMemArena::ReallocateMemArena(pmaOldMemArena, nMemoryRequirement, pmmMemortManager, fReserveFactor, uiReserveMinimum);
+
+ if (pmaNewMemArena != NULL)
+ {
+ // Append reallocated arenas to list tail while old arenas still exist...
+ if (pmaRebuiltArenasTail != NULL)
+ {
+ pmaRebuiltArenasTail->SetNextMemArena(pmaNewMemArena);
+ pmaRebuiltArenasTail = pmaNewMemArena;
+ }
+ else if (pmaRebuiltArenasHead == NULL)
+ {
+ pmaRebuiltArenasHead = pmaNewMemArena;
+ pmaRebuiltArenasTail = pmaNewMemArena;
+ }
+ // ...and append them to list head if those are additional arenas created
+ else
+ {
+ pmaNewMemArena->SetNextMemArena(pmaRebuiltArenasHead);
+ pmaRebuiltArenasHead = pmaNewMemArena;
+ }
+
+ --nArenasToProcess;
+ }
+ else if (pmaOldMemArena == NULL)
+ {
+ break;
+ }
+ }
+
+ if (pmaRebuiltArenasTail != NULL)
+ {
+ pmaRebuiltArenasTail->SetNextMemArena(NULL);
+ }
+
+ SetStepperArenasList(pmaRebuiltArenasHead);
+
+ bool bResult = nArenasToProcess == 0;
+ return bResult;
+}
+
+void dxWorldProcessContext::FreeArenasList(dxWorldProcessMemArena *pmaExistingArenas)
+{
+ while (pmaExistingArenas != NULL)
+ {
+ dxWorldProcessMemArena *pmaCurrentMemArena = pmaExistingArenas;
+ pmaExistingArenas = pmaExistingArenas->GetNextMemArena();
+
+ dxWorldProcessMemArena::FreeMemArena(pmaCurrentMemArena);
+ }
+}
+
+dxWorldProcessMemArena *dxWorldProcessContext::GetStepperArenasHead() const
+{
+ return m_pmaStepperArenas;
+}
+
+bool dxWorldProcessContext::TryExtractingStepperArenasHead(dxWorldProcessMemArena *pmaHeadInstance)
+{
+ dxWorldProcessMemArena *pmaNextInstance = pmaHeadInstance->GetNextMemArena();
+ return ThrsafeCompareExchangePointer((volatile atomicptr *)&m_pmaStepperArenas, (atomicptr)pmaHeadInstance, (atomicptr)pmaNextInstance);
+}
+
+bool dxWorldProcessContext::TryInsertingStepperArenasHead(dxWorldProcessMemArena *pmaArenaInstance, dxWorldProcessMemArena *pmaExistingHead)
+{
+ return ThrsafeCompareExchangePointer((volatile atomicptr *)&m_pmaStepperArenas, (atomicptr)pmaExistingHead, (atomicptr)pmaArenaInstance);
+}
+
+
+void dxWorldProcessContext::LockForAddLimotSerialization()
+{
+ m_pswObjectsAllocWorld->LockMutexGroupMutex(m_pmgStepperMutexGroup, dxPCM_STEPPER_ADDLIMOT_SERIALIZE);
+}
+
+void dxWorldProcessContext::UnlockForAddLimotSerialization()
+{
+ m_pswObjectsAllocWorld->UnlockMutexGroupMutex(m_pmgStepperMutexGroup, dxPCM_STEPPER_ADDLIMOT_SERIALIZE);
+}
+
+
+void dxWorldProcessContext::LockForStepbodySerialization()
+{
+ m_pswObjectsAllocWorld->LockMutexGroupMutex(m_pmgStepperMutexGroup, dxPCM_STEPPER_STEPBODY_SERIALIZE);
+}
+
+void dxWorldProcessContext::UnlockForStepbodySerialization()
+{
+ m_pswObjectsAllocWorld->UnlockMutexGroupMutex(m_pmgStepperMutexGroup, dxPCM_STEPPER_STEPBODY_SERIALIZE);
+}
+
+
+//****************************************************************************
+// Threading call contexts
+
+struct dxSingleIslandCallContext;
+
+struct dxIslandsProcessingCallContext
+{
+ dxIslandsProcessingCallContext(dxWorld *world, const dxWorldProcessIslandsInfo &islandsInfo, dReal stepSize, dstepper_fn_t stepper):
+ m_world(world), m_islandsInfo(islandsInfo), m_stepSize(stepSize), m_stepper(stepper),
+ m_groupReleasee(NULL), m_islandToProcessStorage(0), m_stepperAllowedThreads(0)
+ {
+ }
+
+ void AssignGroupReleasee(dCallReleaseeID groupReleasee) { m_groupReleasee = groupReleasee; }
+ void SetStepperAllowedThreads(unsigned allowedThreadsLimit) { m_stepperAllowedThreads = allowedThreadsLimit; }
+
+ static int ThreadedProcessGroup_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee);
+ bool ThreadedProcessGroup();
+
+ static int ThreadedProcessJobStart_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee);
+ void ThreadedProcessJobStart();
+
+ static int ThreadedProcessIslandSearch_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee);
+ void ThreadedProcessIslandSearch(dxSingleIslandCallContext *stepperCallContext);
+
+ static int ThreadedProcessIslandStepper_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee);
+ void ThreadedProcessIslandStepper(dxSingleIslandCallContext *stepperCallContext);
+
+ sizeint ObtainNextIslandToBeProcessed(sizeint islandsCount);
+
+ dxWorld *const m_world;
+ dxWorldProcessIslandsInfo const &m_islandsInfo;
+ dReal const m_stepSize;
+ dstepper_fn_t const m_stepper;
+ dCallReleaseeID m_groupReleasee;
+ sizeint volatile m_islandToProcessStorage;
+ unsigned m_stepperAllowedThreads;
+};
+
+
+struct dxSingleIslandCallContext
+{
+ dxSingleIslandCallContext(dxIslandsProcessingCallContext *islandsProcessingContext,
+ dxWorldProcessMemArena *stepperArena, void *arenaInitialState,
+ dxBody *const *islandBodiesStart, dxJoint *const *islandJointsStart):
+ m_islandsProcessingContext(islandsProcessingContext), m_islandIndex(0),
+ m_stepperArena(stepperArena), m_arenaInitialState(arenaInitialState),
+ m_stepperCallContext(islandsProcessingContext->m_world, islandsProcessingContext->m_stepSize, islandsProcessingContext->m_stepperAllowedThreads, stepperArena, islandBodiesStart, islandJointsStart)
+ {
+ }
+
+ void AssignIslandSearchProgress(sizeint islandIndex)
+ {
+ m_islandIndex = islandIndex;
+ }
+
+ void AssignIslandSelection(dxBody *const *islandBodiesStart, dxJoint *const *islandJointsStart,
+ unsigned islandBodiesCount, unsigned islandJointsCount)
+ {
+ m_stepperCallContext.AssignIslandSelection(islandBodiesStart, islandJointsStart, islandBodiesCount, islandJointsCount);
+ }
+
+ dxBody *const *GetSelectedIslandBodiesEnd() const { return m_stepperCallContext.GetSelectedIslandBodiesEnd(); }
+ dxJoint *const *GetSelectedIslandJointsEnd() const { return m_stepperCallContext.GetSelectedIslandJointsEnd(); }
+
+ void RestoreSavedMemArenaStateForStepper()
+ {
+ m_stepperArena->RestoreState(m_arenaInitialState);
+ }
+
+ void AssignStepperCallFinalReleasee(dCallReleaseeID finalReleasee)
+ {
+ m_stepperCallContext.AssignStepperCallFinalReleasee(finalReleasee);
+ }
+
+ dxIslandsProcessingCallContext *m_islandsProcessingContext;
+ sizeint m_islandIndex;
+ dxWorldProcessMemArena *m_stepperArena;
+ void *m_arenaInitialState;
+ dxStepperProcessingCallContext m_stepperCallContext;
+};
+
+
+//****************************************************************************
+// Auto disabling
+
+void dInternalHandleAutoDisabling (dxWorld *world, dReal stepsize)
+{
+ dxBody *bb;
+ for ( bb=world->firstbody; bb; bb=(dxBody*)bb->next )
+ {
+ // don't freeze objects mid-air (patch 1586738)
+ if ( bb->firstjoint == NULL ) continue;
+
+ // nothing to do unless this body is currently enabled and has
+ // the auto-disable flag set
+ if ( (bb->flags & (dxBodyAutoDisable|dxBodyDisabled)) != dxBodyAutoDisable ) continue;
+
+ // if sampling / threshold testing is disabled, we can never sleep.
+ if ( bb->adis.average_samples == 0 ) continue;
+
+ //
+ // see if the body is idle
+ //
+
+#ifndef dNODEBUG
+ // sanity check
+ if ( bb->average_counter >= bb->adis.average_samples )
+ {
+ dUASSERT( bb->average_counter < bb->adis.average_samples, "buffer overflow" );
+
+ // something is going wrong, reset the average-calculations
+ bb->average_ready = 0; // not ready for average calculation
+ bb->average_counter = 0; // reset the buffer index
+ }
+#endif // dNODEBUG
+
+ // sample the linear and angular velocity
+ bb->average_lvel_buffer[bb->average_counter][0] = bb->lvel[0];
+ bb->average_lvel_buffer[bb->average_counter][1] = bb->lvel[1];
+ bb->average_lvel_buffer[bb->average_counter][2] = bb->lvel[2];
+ bb->average_avel_buffer[bb->average_counter][0] = bb->avel[0];
+ bb->average_avel_buffer[bb->average_counter][1] = bb->avel[1];
+ bb->average_avel_buffer[bb->average_counter][2] = bb->avel[2];
+ bb->average_counter++;
+
+ // buffer ready test
+ if ( bb->average_counter >= bb->adis.average_samples )
+ {
+ bb->average_counter = 0; // fill the buffer from the beginning
+ bb->average_ready = 1; // this body is ready now for average calculation
+ }
+
+ int idle = 0; // Assume it's in motion unless we have samples to disprove it.
+
+ // enough samples?
+ if ( bb->average_ready )
+ {
+ idle = 1; // Initial assumption: IDLE
+
+ // the sample buffers are filled and ready for calculation
+ dVector3 average_lvel, average_avel;
+
+ // Store first velocity samples
+ average_lvel[0] = bb->average_lvel_buffer[0][0];
+ average_avel[0] = bb->average_avel_buffer[0][0];
+ average_lvel[1] = bb->average_lvel_buffer[0][1];
+ average_avel[1] = bb->average_avel_buffer[0][1];
+ average_lvel[2] = bb->average_lvel_buffer[0][2];
+ average_avel[2] = bb->average_avel_buffer[0][2];
+
+ // If we're not in "instantaneous mode"
+ if ( bb->adis.average_samples > 1 )
+ {
+ // add remaining velocities together
+ for ( unsigned int i = 1; i < bb->adis.average_samples; ++i )
+ {
+ average_lvel[0] += bb->average_lvel_buffer[i][0];
+ average_avel[0] += bb->average_avel_buffer[i][0];
+ average_lvel[1] += bb->average_lvel_buffer[i][1];
+ average_avel[1] += bb->average_avel_buffer[i][1];
+ average_lvel[2] += bb->average_lvel_buffer[i][2];
+ average_avel[2] += bb->average_avel_buffer[i][2];
+ }
+
+ // make average
+ dReal r1 = dReal( 1.0 ) / dReal( bb->adis.average_samples );
+
+ average_lvel[0] *= r1;
+ average_avel[0] *= r1;
+ average_lvel[1] *= r1;
+ average_avel[1] *= r1;
+ average_lvel[2] *= r1;
+ average_avel[2] *= r1;
+ }
+
+ // threshold test
+ dReal av_lspeed, av_aspeed;
+ av_lspeed = dCalcVectorDot3( average_lvel, average_lvel );
+ if ( av_lspeed > bb->adis.linear_average_threshold )
+ {
+ idle = 0; // average linear velocity is too high for idle
+ }
+ else
+ {
+ av_aspeed = dCalcVectorDot3( average_avel, average_avel );
+ if ( av_aspeed > bb->adis.angular_average_threshold )
+ {
+ idle = 0; // average angular velocity is too high for idle
+ }
+ }
+ }
+
+ // if it's idle, accumulate steps and time.
+ // these counters won't overflow because this code doesn't run for disabled bodies.
+ if (idle) {
+ bb->adis_stepsleft--;
+ bb->adis_timeleft -= stepsize;
+ }
+ else {
+ // Reset countdowns
+ bb->adis_stepsleft = bb->adis.idle_steps;
+ bb->adis_timeleft = bb->adis.idle_time;
+ }
+
+ // disable the body if it's idle for a long enough time
+ if ( bb->adis_stepsleft <= 0 && bb->adis_timeleft <= 0 )
+ {
+ bb->flags |= dxBodyDisabled; // set the disable flag
+
+ // disabling bodies should also include resetting the velocity
+ // should prevent jittering in big "islands"
+ bb->lvel[0] = 0;
+ bb->lvel[1] = 0;
+ bb->lvel[2] = 0;
+ bb->avel[0] = 0;
+ bb->avel[1] = 0;
+ bb->avel[2] = 0;
+ }
+ }
+}
+
+
+//****************************************************************************
+// body rotation
+
+// return sin(x)/x. this has a singularity at 0 so special handling is needed
+// for small arguments.
+
+static inline dReal sinc (dReal x)
+{
+ // if |x| < 1e-4 then use a taylor series expansion. this two term expansion
+ // is actually accurate to one LS bit within this range if double precision
+ // is being used - so don't worry!
+ if (dFabs(x) < 1.0e-4) return REAL(1.0) - x*x*REAL(0.166666666666666666667);
+ else return dSin(x)/x;
+}
+
+
+// given a body b, apply its linear and angular rotation over the time
+// interval h, thereby adjusting its position and orientation.
+
+void dxStepBody (dxBody *b, dReal h)
+{
+ // cap the angular velocity
+ if (b->flags & dxBodyMaxAngularSpeed) {
+ const dReal max_ang_speed = b->max_angular_speed;
+ const dReal aspeed = dCalcVectorDot3( b->avel, b->avel );
+ if (aspeed > max_ang_speed*max_ang_speed) {
+ const dReal coef = max_ang_speed/dSqrt(aspeed);
+ dScaleVector3(b->avel, coef);
+ }
+ }
+ // end of angular velocity cap
+
+
+ // handle linear velocity
+ for (unsigned int j=0; j<3; j++) b->posr.pos[j] += h * b->lvel[j];
+
+ if (b->flags & dxBodyFlagFiniteRotation) {
+ dVector3 irv; // infitesimal rotation vector
+ dQuaternion q; // quaternion for finite rotation
+
+ if (b->flags & dxBodyFlagFiniteRotationAxis) {
+ // split the angular velocity vector into a component along the finite
+ // rotation axis, and a component orthogonal to it.
+ dVector3 frv; // finite rotation vector
+ dReal k = dCalcVectorDot3 (b->finite_rot_axis,b->avel);
+ frv[0] = b->finite_rot_axis[0] * k;
+ frv[1] = b->finite_rot_axis[1] * k;
+ frv[2] = b->finite_rot_axis[2] * k;
+ irv[0] = b->avel[0] - frv[0];
+ irv[1] = b->avel[1] - frv[1];
+ irv[2] = b->avel[2] - frv[2];
+
+ // make a rotation quaternion q that corresponds to frv * h.
+ // compare this with the full-finite-rotation case below.
+ h *= REAL(0.5);
+ dReal theta = k * h;
+ q[0] = dCos(theta);
+ dReal s = sinc(theta) * h;
+ q[1] = frv[0] * s;
+ q[2] = frv[1] * s;
+ q[3] = frv[2] * s;
+ }
+ else {
+ // make a rotation quaternion q that corresponds to w * h
+ dReal wlen = dSqrt (b->avel[0]*b->avel[0] + b->avel[1]*b->avel[1] +
+ b->avel[2]*b->avel[2]);
+ h *= REAL(0.5);
+ dReal theta = wlen * h;
+ q[0] = dCos(theta);
+ dReal s = sinc(theta) * h;
+ q[1] = b->avel[0] * s;
+ q[2] = b->avel[1] * s;
+ q[3] = b->avel[2] * s;
+ }
+
+ // do the finite rotation
+ dQuaternion q2;
+ dQMultiply0 (q2,q,b->q);
+ for (unsigned int j=0; j<4; j++) b->q[j] = q2[j];
+
+ // do the infitesimal rotation if required
+ if (b->flags & dxBodyFlagFiniteRotationAxis) {
+ dReal dq[4];
+ dWtoDQ (irv,b->q,dq);
+ for (unsigned int j=0; j<4; j++) b->q[j] += h * dq[j];
+ }
+ }
+ else {
+ // the normal way - do an infitesimal rotation
+ dReal dq[4];
+ dWtoDQ (b->avel,b->q,dq);
+ for (unsigned int j=0; j<4; j++) b->q[j] += h * dq[j];
+ }
+
+ // normalize the quaternion and convert it to a rotation matrix
+ dNormalize4 (b->q);
+ dQtoR (b->q,b->posr.R);
+
+ // notify all attached geoms that this body has moved
+ dxWorldProcessContext *world_process_context = b->world->unsafeGetWorldProcessingContext();
+ for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) {
+ world_process_context->LockForStepbodySerialization();
+ dGeomMoved (geom);
+ world_process_context->UnlockForStepbodySerialization();
+ }
+
+ // notify the user
+ if (b->moved_callback != NULL) {
+ b->moved_callback(b);
+ }
+
+ // damping
+ if (b->flags & dxBodyLinearDamping) {
+ const dReal lin_threshold = b->dampingp.linear_threshold;
+ const dReal lin_speed = dCalcVectorDot3( b->lvel, b->lvel );
+ if ( lin_speed > lin_threshold) {
+ const dReal k = 1 - b->dampingp.linear_scale;
+ dScaleVector3(b->lvel, k);
+ }
+ }
+ if (b->flags & dxBodyAngularDamping) {
+ const dReal ang_threshold = b->dampingp.angular_threshold;
+ const dReal ang_speed = dCalcVectorDot3( b->avel, b->avel );
+ if ( ang_speed > ang_threshold) {
+ const dReal k = 1 - b->dampingp.angular_scale;
+ dScaleVector3(b->avel, k);
+ }
+ }
+}
+
+
+//****************************************************************************
+// island processing
+
+enum dxISLANDSIZESELEMENT
+{
+ dxISE_BODIES_COUNT,
+ dxISE_JOINTS_COUNT,
+
+ dxISE__MAX
+};
+
+// This estimates dynamic memory requirements for dxProcessIslands
+static sizeint EstimateIslandProcessingMemoryRequirements(dxWorld *world)
+{
+ sizeint res = 0;
+
+ sizeint islandcounts = dEFFICIENT_SIZE((sizeint)(unsigned)world->nb * 2 * sizeof(int));
+ res += islandcounts;
+
+ sizeint bodiessize = dEFFICIENT_SIZE((sizeint)(unsigned)world->nb * sizeof(dxBody*));
+ sizeint jointssize = dEFFICIENT_SIZE((sizeint)(unsigned)world->nj * sizeof(dxJoint*));
+ res += bodiessize + jointssize;
+
+ sizeint sesize = (bodiessize < jointssize) ? bodiessize : jointssize;
+ res += sesize;
+
+ return res;
+}
+
+static sizeint BuildIslandsAndEstimateStepperMemoryRequirements(
+ dxWorldProcessIslandsInfo &islandsinfo, dxWorldProcessMemArena *memarena,
+ dxWorld *world, dReal stepsize, dmemestimate_fn_t stepperestimate)
+{
+ sizeint maxreq = 0;
+
+ // handle auto-disabling of bodies
+ dInternalHandleAutoDisabling (world,stepsize);
+
+ unsigned int nb = world->nb, nj = world->nj;
+ // Make array for island body/joint counts
+ unsigned int *islandsizes = memarena->AllocateArray<unsigned int>(2 * (sizeint)nb);
+ unsigned int *sizescurr;
+
+ // make arrays for body and joint lists (for a single island) to go into
+ dxBody **body = memarena->AllocateArray<dxBody *>(nb);
+ dxJoint **joint = memarena->AllocateArray<dxJoint *>(nj);
+
+ BEGIN_STATE_SAVE(memarena, stackstate) {
+ // allocate a stack of unvisited bodies in the island. the maximum size of
+ // the stack can be the lesser of the number of bodies or joints, because
+ // new bodies are only ever added to the stack by going through untagged
+ // joints. all the bodies in the stack must be tagged!
+ unsigned int stackalloc = (nj < nb) ? nj : nb;
+ dxBody **stack = memarena->AllocateArray<dxBody *>(stackalloc);
+
+ {
+ // set all body/joint tags to 0
+ for (dxBody *b=world->firstbody; b; b=(dxBody*)b->next) b->tag = 0;
+ for (dxJoint *j=world->firstjoint; j; j=(dxJoint*)j->next) j->tag = 0;
+ }
+
+ sizescurr = islandsizes;
+ dxBody **bodystart = body;
+ dxJoint **jointstart = joint;
+ for (dxBody *bb=world->firstbody; bb; bb=(dxBody*)bb->next) {
+ // get bb = the next enabled, untagged body, and tag it
+ if (!bb->tag) {
+ if (!(bb->flags & dxBodyDisabled)) {
+ bb->tag = 1;
+
+ dxBody **bodycurr = bodystart;
+ dxJoint **jointcurr = jointstart;
+
+ // tag all bodies and joints starting from bb.
+ *bodycurr++ = bb;
+
+ unsigned int stacksize = 0;
+ dxBody *b = bb;
+
+ while (true) {
+ // traverse and tag all body's joints, add untagged connected bodies
+ // to stack
+ for (dxJointNode *n=b->firstjoint; n; n=n->next) {
+ dxJoint *njoint = n->joint;
+ if (!njoint->tag) {
+ if (njoint->isEnabled()) {
+ njoint->tag = 1;
+ *jointcurr++ = njoint;
+
+ dxBody *nbody = n->body;
+ // Body disabled flag is not checked here. This is how auto-enable works.
+ if (nbody && nbody->tag <= 0) {
+ nbody->tag = 1;
+ // Make sure all bodies are in the enabled state.
+ nbody->flags &= ~dxBodyDisabled;
+ stack[stacksize++] = nbody;
+ }
+ } else {
+ njoint->tag = -1; // Used in Step to prevent search over disabled joints (not needed for QuickStep so far)
+ }
+ }
+ }
+ dIASSERT(stacksize <= (unsigned int)world->nb);
+ dIASSERT(stacksize <= (unsigned int)world->nj);
+
+ if (stacksize == 0) {
+ break;
+ }
+
+ b = stack[--stacksize]; // pop body off stack
+ *bodycurr++ = b; // put body on body list
+ }
+
+ unsigned int bcount = (unsigned int)(bodycurr - bodystart);
+ unsigned int jcount = (unsigned int)(jointcurr - jointstart);
+ dIASSERT((sizeint)(bodycurr - bodystart) <= (sizeint)UINT_MAX);
+ dIASSERT((sizeint)(jointcurr - jointstart) <= (sizeint)UINT_MAX);
+
+ sizescurr[dxISE_BODIES_COUNT] = bcount;
+ sizescurr[dxISE_JOINTS_COUNT] = jcount;
+ sizescurr += dxISE__MAX;
+
+ sizeint islandreq = stepperestimate(bodystart, bcount, jointstart, jcount);
+ maxreq = (maxreq > islandreq) ? maxreq : islandreq;
+
+ bodystart = bodycurr;
+ jointstart = jointcurr;
+ } else {
+ bb->tag = -1; // Not used so far (assigned to retain consistency with joints)
+ }
+ }
+ }
+ } END_STATE_SAVE(memarena, stackstate);
+
+# ifndef dNODEBUG
+ // if debugging, check that all objects (except for disabled bodies,
+ // unconnected joints, and joints that are connected to disabled bodies)
+ // were tagged.
+ {
+ for (dxBody *b=world->firstbody; b; b=(dxBody*)b->next) {
+ if (b->flags & dxBodyDisabled) {
+ if (b->tag > 0) dDebug (0,"disabled body tagged");
+ }
+ else {
+ if (b->tag <= 0) dDebug (0,"enabled body not tagged");
+ }
+ }
+ for (dxJoint *j=world->firstjoint; j; j=(dxJoint*)j->next) {
+ if ( (( j->node[0].body && (j->node[0].body->flags & dxBodyDisabled)==0 ) ||
+ (j->node[1].body && (j->node[1].body->flags & dxBodyDisabled)==0) )
+ &&
+ j->isEnabled() ) {
+ if (j->tag <= 0) dDebug (0,"attached enabled joint not tagged");
+ }
+ else {
+ if (j->tag > 0) dDebug (0,"unattached or disabled joint tagged");
+ }
+ }
+ }
+# endif
+
+ sizeint islandcount = ((sizeint)(sizescurr - islandsizes) / dxISE__MAX);
+ islandsinfo.AssignInfo(islandcount, islandsizes, body, joint);
+
+ return maxreq;
+}
+
+static unsigned EstimateIslandProcessingSimultaneousCallsMaximumCount(unsigned activeThreadCount, unsigned islandsAllowedThreadCount,
+ unsigned stepperAllowedThreadCount, dmaxcallcountestimate_fn_t maxCallCountEstimator)
+{
+ unsigned stepperCallsMaximum = maxCallCountEstimator(activeThreadCount, stepperAllowedThreadCount);
+ unsigned islandsIntermediateCallsMaximum = (1 + 2); // ThreadedProcessIslandSearch_Callback + (ThreadedProcessIslandStepper_Callback && ThreadedProcessIslandSearch_Callback)
+
+ unsigned result =
+ 1 // ThreadedProcessGroup_Callback
+ + islandsAllowedThreadCount * dMAX(stepperCallsMaximum, islandsIntermediateCallsMaximum)
+ + dMIN(islandsAllowedThreadCount, (unsigned)(activeThreadCount - islandsAllowedThreadCount)) // ThreadedProcessJobStart_Callback
+ /*...the end*/;
+ return result;
+}
+
+// this groups all joints and bodies in a world into islands. all objects
+// in an island are reachable by going through connected bodies and joints.
+// each island can be simulated separately.
+// note that joints that are not attached to anything will not be included
+// in any island, an so they do not affect the simulation.
+//
+// this function starts new island from unvisited bodies. however, it will
+// never start a new islands from a disabled body. thus islands of disabled
+// bodies will not be included in the simulation. disabled bodies are
+// re-enabled if they are found to be part of an active island.
+bool dxProcessIslands (dxWorld *world, const dxWorldProcessIslandsInfo &islandsInfo,
+ dReal stepSize, dstepper_fn_t stepper, dmaxcallcountestimate_fn_t maxCallCountEstimator)
+{
+ bool result = false;
+
+ dxIslandsProcessingCallContext callContext(world, islandsInfo, stepSize, stepper);
+
+ do {
+ dxStepWorkingMemory *wmem = world->wmem;
+ dIASSERT(wmem != NULL);
+ dxWorldProcessContext *context = wmem->GetWorldProcessingContext();
+ dIASSERT(context != NULL);
+ dCallWaitID pcwGroupCallWait = context->GetIslandsSteppingWait();
+
+ int summaryFault = 0;
+
+ unsigned activeThreadCount;
+ const unsigned islandsAllowedThreadCount = world->calculateIslandProcessingMaxThreadCount(&activeThreadCount);
+ dIASSERT(islandsAllowedThreadCount != 0);
+ dIASSERT(activeThreadCount >= islandsAllowedThreadCount);
+
+ unsigned stepperAllowedThreadCount = islandsAllowedThreadCount; // For now, set stepper allowed threads equal to island stepping threads
+
+ unsigned simultaneousCallsCount = EstimateIslandProcessingSimultaneousCallsMaximumCount(activeThreadCount, islandsAllowedThreadCount, stepperAllowedThreadCount, maxCallCountEstimator);
+ if (!world->PreallocateResourcesForThreadedCalls(simultaneousCallsCount)) {
+ break;
+ }
+
+ dCallReleaseeID groupReleasee;
+ // First post a group call with dependency count set to number of expected threads
+ world->PostThreadedCall(&summaryFault, &groupReleasee, islandsAllowedThreadCount, NULL, pcwGroupCallWait,
+ &dxIslandsProcessingCallContext::ThreadedProcessGroup_Callback, (void *)&callContext, 0, "World Islands Stepping Group");
+
+ callContext.AssignGroupReleasee(groupReleasee);
+ callContext.SetStepperAllowedThreads(stepperAllowedThreadCount);
+
+ // Summary fault flag may be omitted as any failures will automatically propagate to dependent releasee (i.e. to groupReleasee)
+ world->PostThreadedCallsGroup(NULL, islandsAllowedThreadCount, groupReleasee,
+ &dxIslandsProcessingCallContext::ThreadedProcessJobStart_Callback, (void *)&callContext, "World Islands Stepping Start");
+
+ // Wait until group completes (since jobs were the dependencies of the group the group is going to complete only after all the jobs end)
+ world->WaitThreadedCallExclusively(NULL, pcwGroupCallWait, NULL, "World Islands Stepping Wait");
+
+ if (summaryFault != 0) {
+ break;
+ }
+
+ result = true;
+ }
+ while (false);
+
+ return result;
+}
+
+
+int dxIslandsProcessingCallContext::ThreadedProcessGroup_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee)
+{
+ (void)callInstanceIndex; // unused
+ (void)callThisReleasee; // unused
+ return static_cast<dxIslandsProcessingCallContext *>(callContext)->ThreadedProcessGroup();
+}
+
+bool dxIslandsProcessingCallContext::ThreadedProcessGroup()
+{
+ // Do nothing - it's just a wrapper call
+ return true;
+}
+
+int dxIslandsProcessingCallContext::ThreadedProcessJobStart_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee)
+{
+ (void)callInstanceIndex; // unused
+ (void)callThisReleasee; // unused
+ static_cast<dxIslandsProcessingCallContext *>(callContext)->ThreadedProcessJobStart();
+ return true;
+}
+
+void dxIslandsProcessingCallContext::ThreadedProcessJobStart()
+{
+ dxWorldProcessContext *context = m_world->unsafeGetWorldProcessingContext();
+
+ dxWorldProcessMemArena *stepperArena = context->ObtainStepperMemArena();
+ dIASSERT(stepperArena != NULL && stepperArena->IsStructureValid());
+
+ const dxWorldProcessIslandsInfo &islandsInfo = m_islandsInfo;
+ dxBody *const *islandBodiesStart = islandsInfo.GetBodiesArray();
+ dxJoint *const *islandJointsStart = islandsInfo.GetJointsArray();
+
+ dxSingleIslandCallContext *stepperCallContext = (dxSingleIslandCallContext *)stepperArena->AllocateBlock(sizeof(dxSingleIslandCallContext));
+ // Save area state after context allocation to be restored for the stepper
+ void *arenaState = stepperArena->SaveState();
+ new(stepperCallContext) dxSingleIslandCallContext(this, stepperArena, arenaState, islandBodiesStart, islandJointsStart);
+
+ // Summary fault flag may be omitted as any failures will automatically propagate to dependent releasee (i.e. to m_groupReleasee)
+ m_world->PostThreadedCallForUnawareReleasee(NULL, NULL, 0, m_groupReleasee, NULL,
+ &dxIslandsProcessingCallContext::ThreadedProcessIslandSearch_Callback, (void *)stepperCallContext, 0, "World Islands Stepping Selection");
+}
+
+int dxIslandsProcessingCallContext::ThreadedProcessIslandSearch_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee)
+{
+ (void)callInstanceIndex; // unused
+ (void)callThisReleasee; // unused
+ dxSingleIslandCallContext *stepperCallContext = static_cast<dxSingleIslandCallContext *>(callContext);
+ stepperCallContext->m_islandsProcessingContext->ThreadedProcessIslandSearch(stepperCallContext);
+ return true;
+}
+
+void dxIslandsProcessingCallContext::ThreadedProcessIslandSearch(dxSingleIslandCallContext *stepperCallContext)
+{
+ bool finalizeJob = false;
+
+ const dxWorldProcessIslandsInfo &islandsInfo = m_islandsInfo;
+ unsigned int const *islandSizes = islandsInfo.GetIslandSizes();
+
+ const sizeint islandsCount = islandsInfo.GetIslandsCount();
+ sizeint islandToProcess = ObtainNextIslandToBeProcessed(islandsCount);
+
+ if (islandToProcess != islandsCount) {
+ // First time, the counts are zeros and on next passes, adding counts will skip island that has just been processed by stepper
+ dxBody *const *islandBodiesStart = stepperCallContext->GetSelectedIslandBodiesEnd();
+ dxJoint *const *islandJointsStart = stepperCallContext->GetSelectedIslandJointsEnd();
+ sizeint islandIndex = stepperCallContext->m_islandIndex;
+
+ for (; ; ++islandIndex) {
+ unsigned int bcount = islandSizes[islandIndex * dxISE__MAX + dxISE_BODIES_COUNT];
+ unsigned int jcount = islandSizes[islandIndex * dxISE__MAX + dxISE_JOINTS_COUNT];
+
+ if (islandIndex == islandToProcess) {
+ // Store selected island details
+ stepperCallContext->AssignIslandSelection(islandBodiesStart, islandJointsStart, bcount, jcount);
+
+ // Store next island index to continue search from
+ ++islandIndex;
+ stepperCallContext->AssignIslandSearchProgress(islandIndex);
+
+ // Restore saved stepper memory arena position
+ stepperCallContext->RestoreSavedMemArenaStateForStepper();
+
+ dCallReleaseeID nextSearchReleasee;
+
+ // Summary fault flag may be omitted as any failures will automatically propagate to dependent releasee (i.e. to m_groupReleasee)
+ m_world->PostThreadedCallForUnawareReleasee(NULL, &nextSearchReleasee, 1, m_groupReleasee, NULL,
+ &dxIslandsProcessingCallContext::ThreadedProcessIslandSearch_Callback, (void *)stepperCallContext, 0, "World Islands Stepping Selection");
+
+ stepperCallContext->AssignStepperCallFinalReleasee(nextSearchReleasee);
+
+ m_world->PostThreadedCall(NULL, NULL, 0, nextSearchReleasee, NULL,
+ &dxIslandsProcessingCallContext::ThreadedProcessIslandStepper_Callback, (void *)stepperCallContext, 0, "Island Stepping Job Start");
+
+ break;
+ }
+
+ islandBodiesStart += bcount;
+ islandJointsStart += jcount;
+ }
+ }
+ else {
+ finalizeJob = true;
+ }
+
+ if (finalizeJob) {
+ dxWorldProcessMemArena *stepperArena = stepperCallContext->m_stepperArena;
+ stepperCallContext->dxSingleIslandCallContext::~dxSingleIslandCallContext();
+
+ dxWorldProcessContext *context = m_world->unsafeGetWorldProcessingContext();
+ context->ReturnStepperMemArena(stepperArena);
+ }
+}
+
+int dxIslandsProcessingCallContext::ThreadedProcessIslandStepper_Callback(void *callContext, dcallindex_t callInstanceIndex, dCallReleaseeID callThisReleasee)
+{
+ (void)callInstanceIndex; // unused
+ (void)callThisReleasee; // unused
+ dxSingleIslandCallContext *stepperCallContext = static_cast<dxSingleIslandCallContext *>(callContext);
+ stepperCallContext->m_islandsProcessingContext->ThreadedProcessIslandStepper(stepperCallContext);
+ return true;
+}
+
+void dxIslandsProcessingCallContext::ThreadedProcessIslandStepper(dxSingleIslandCallContext *stepperCallContext)
+{
+ m_stepper(&stepperCallContext->m_stepperCallContext);
+}
+
+sizeint dxIslandsProcessingCallContext::ObtainNextIslandToBeProcessed(sizeint islandsCount)
+{
+ return ThrsafeIncrementSizeUpToLimit(&m_islandToProcessStorage, islandsCount);
+}
+
+
+//****************************************************************************
+// World processing context management
+
+dxWorldProcessMemArena *dxWorldProcessMemArena::ReallocateMemArena (
+ dxWorldProcessMemArena *oldarena, sizeint memreq,
+ const dxWorldProcessMemoryManager *memmgr, float rsrvfactor, unsigned rsrvminimum)
+{
+ dxWorldProcessMemArena *arena = oldarena;
+ bool allocsuccess = false;
+
+ sizeint nOldArenaSize;
+ void *pOldArenaBuffer;
+
+ do {
+ sizeint oldmemsize = oldarena ? oldarena->GetMemorySize() : 0;
+ if (oldarena == NULL || oldmemsize < memreq) {
+ nOldArenaSize = oldarena ? dxWorldProcessMemArena::MakeArenaSize(oldmemsize) : 0;
+ pOldArenaBuffer = oldarena ? oldarena->m_pArenaBegin : NULL;
+
+ if (!dxWorldProcessMemArena::IsArenaPossible(memreq)) {
+ break;
+ }
+
+ sizeint arenareq = dxWorldProcessMemArena::MakeArenaSize(memreq);
+ sizeint arenareq_with_reserve = AdjustArenaSizeForReserveRequirements(arenareq, rsrvfactor, rsrvminimum);
+ sizeint memreq_with_reserve = memreq + (arenareq_with_reserve - arenareq);
+
+ if (oldarena != NULL) {
+ oldarena->m_pArenaMemMgr->m_fnFree(pOldArenaBuffer, nOldArenaSize);
+ oldarena = NULL;
+
+ // Zero variables to avoid another freeing on exit
+ pOldArenaBuffer = NULL;
+ nOldArenaSize = 0;
+ }
+
+ // Allocate new arena
+ void *pNewArenaBuffer = memmgr->m_fnAlloc(arenareq_with_reserve);
+ if (pNewArenaBuffer == NULL) {
+ break;
+ }
+
+ arena = (dxWorldProcessMemArena *)dEFFICIENT_PTR(pNewArenaBuffer);
+
+ void *blockbegin = dEFFICIENT_PTR(arena + 1);
+ void *blockend = dOFFSET_EFFICIENTLY(blockbegin, memreq_with_reserve);
+
+ arena->m_pAllocBegin = blockbegin;
+ arena->m_pAllocEnd = blockend;
+ arena->m_pArenaBegin = pNewArenaBuffer;
+ arena->m_pAllocCurrentOrNextArena = NULL;
+ arena->m_pArenaMemMgr = memmgr;
+ }
+
+ allocsuccess = true;
+ }
+ while (false);
+
+ if (!allocsuccess) {
+ if (pOldArenaBuffer != NULL) {
+ dIASSERT(oldarena != NULL);
+ oldarena->m_pArenaMemMgr->m_fnFree(pOldArenaBuffer, nOldArenaSize);
+ }
+ arena = NULL;
+ }
+
+ return arena;
+}
+
+void dxWorldProcessMemArena::FreeMemArena (dxWorldProcessMemArena *arena)
+{
+ sizeint memsize = arena->GetMemorySize();
+ sizeint arenasize = dxWorldProcessMemArena::MakeArenaSize(memsize);
+
+ void *pArenaBegin = arena->m_pArenaBegin;
+ arena->m_pArenaMemMgr->m_fnFree(pArenaBegin, arenasize);
+}
+
+
+sizeint dxWorldProcessMemArena::AdjustArenaSizeForReserveRequirements(sizeint arenareq, float rsrvfactor, unsigned rsrvminimum)
+{
+ float scaledarena = arenareq * rsrvfactor;
+ sizeint adjustedarena = (scaledarena < SIZE_MAX) ? (sizeint)scaledarena : SIZE_MAX;
+ sizeint boundedarena = (adjustedarena > rsrvminimum) ? adjustedarena : (sizeint)rsrvminimum;
+ return dEFFICIENT_SIZE(boundedarena);
+}
+
+
+bool dxReallocateWorldProcessContext (dxWorld *world, dxWorldProcessIslandsInfo &islandsInfo,
+ dReal stepSize, dmemestimate_fn_t stepperEstimate)
+{
+ bool result = false;
+
+ do
+ {
+ dxStepWorkingMemory *wmem = AllocateOnDemand(world->wmem);
+ if (wmem == NULL)
+ {
+ break;
+ }
+
+ dxWorldProcessContext *context = wmem->SureGetWorldProcessingContext();
+ if (context == NULL)
+ {
+ break;
+ }
+
+ if (!context->EnsureStepperSyncObjectsAreAllocated(world))
+ {
+ break;
+ }
+
+ const dxWorldProcessMemoryReserveInfo *reserveInfo = wmem->SureGetMemoryReserveInfo();
+ const dxWorldProcessMemoryManager *memmgr = wmem->SureGetMemoryManager();
+
+ sizeint islandsReq = EstimateIslandProcessingMemoryRequirements(world);
+ dIASSERT(islandsReq == dEFFICIENT_SIZE(islandsReq));
+
+ dxWorldProcessMemArena *islandsArena = context->ReallocateIslandsMemArena(islandsReq, memmgr, 1.0f, reserveInfo->m_uiReserveMinimum);
+ if (islandsArena == NULL)
+ {
+ break;
+ }
+ dIASSERT(islandsArena->IsStructureValid());
+
+ sizeint stepperReq = BuildIslandsAndEstimateStepperMemoryRequirements(islandsInfo, islandsArena, world, stepSize, stepperEstimate);
+ dIASSERT(stepperReq == dEFFICIENT_SIZE(stepperReq));
+
+ sizeint stepperReqWithCallContext = stepperReq + dEFFICIENT_SIZE(sizeof(dxSingleIslandCallContext));
+
+ unsigned islandThreadsCount = world->calculateIslandProcessingMaxThreadCount();
+ if (!context->ReallocateStepperMemArenas(world, islandThreadsCount, stepperReqWithCallContext,
+ memmgr, reserveInfo->m_fReserveFactor, reserveInfo->m_uiReserveMinimum))
+ {
+ break;
+ }
+
+ result = true;
+ }
+ while (false);
+
+ return result;
+}
+
+dxWorldProcessMemArena *dxAllocateTemporaryWorldProcessMemArena(
+ sizeint memreq, const dxWorldProcessMemoryManager *memmgr/*=NULL*/, const dxWorldProcessMemoryReserveInfo *reserveinfo/*=NULL*/)
+{
+ const dxWorldProcessMemoryManager *surememmgr = memmgr ? memmgr : &g_WorldProcessMallocMemoryManager;
+ const dxWorldProcessMemoryReserveInfo *surereserveinfo = reserveinfo ? reserveinfo : &g_WorldProcessDefaultReserveInfo;
+ dxWorldProcessMemArena *arena = dxWorldProcessMemArena::ReallocateMemArena(NULL, memreq, surememmgr, surereserveinfo->m_fReserveFactor, surereserveinfo->m_uiReserveMinimum);
+ return arena;
+}
+
+void dxFreeTemporaryWorldProcessMemArena(dxWorldProcessMemArena *arena)
+{
+ dxWorldProcessMemArena::FreeMemArena(arena);
+}
+