From e455a71c2ed3eb6215c2ab5e59e712dd2a49a56b Mon Sep 17 00:00:00 2001 From: John Johns Date: Mon, 31 May 2021 15:01:46 -0700 Subject: [PATCH 1/4] Hoikas' framerate unlock https://github.com/H-uru/Plasma/pull/143 --- Sources/Plasma/Apps/plClient/plClient.cpp | 31 +++++------ .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 54 +++++++------------ .../PubUtilLib/plPhysX/plSimulationMgr.h | 17 ------ 3 files changed, 32 insertions(+), 70 deletions(-) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index c1013bf1..eb5ab0ef 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -1287,7 +1287,20 @@ void plClient::IProgressMgrCallbackProc(plOperationProgress * progress) return; fInstance->fMessagePumpProc(); - fInstance->IDraw(); + + // HACK HACK HACK HACK! + // Yes, this is the ORIGINAL, EVIL famerate limit from plClient::IDraw (except I bumped it to 60fps) + // As it so happens, this callback is happening in the main resource loading thread + // Without this NASTY ASS HACK, we draw after loading every KO, which starves the loader. + // At some point, a better solution should be found... Like running the loader in a separate thread. + static float lastDrawTime; + static const float kMaxFrameRate = 1.f/60.f; + float currTime = (float) hsTimer::GetSeconds(); + if ((currTime - lastDrawTime) > kMaxFrameRate) + { + fInstance->IDraw(); + lastDrawTime = currTime; + } } //============================================================================ @@ -1847,22 +1860,6 @@ hsBool plClient::IDrawProgress() { hsBool plClient::IDraw() { - // Limit framerate - static float lastDrawTime; - static const float kMaxFrameRate = 1.f/30.f; - float currTime = (float) hsTimer::GetSeconds(); - if (!fPipeline->IsDebugFlagSet(plPipeDbg::kFlagNVPerfHUD)) - { - // If we're using NVPerfHUD to step through draw calls, - // We're going to have a frame delta of zero. In that - // case we need to draw no matter what, and we don't - // care as much about starving other threads because - // we're presumably just debugging a graphics glitch. - if ((currTime - lastDrawTime) < kMaxFrameRate) - return true; - } - lastDrawTime = currTime; - // If we're shutting down, don't attempt to draw. Doing so // tends to cause a device reload each frame. if (fDone) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index 5603c566..b1b8b0bc 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -347,8 +347,6 @@ plSimulationMgr* plSimulationMgr::GetInstance() plSimulationMgr::plSimulationMgr() : fSuspended(true) - , fMaxDelta(kDefaultMaxDelta) - , fStepSize(kDefaultStepSize) , fAccumulator(0.0f) , fStepCount(0) , fLOSDispatch(TRACKED_NEW plLOSDispatch()) @@ -422,16 +420,20 @@ NxScene* plSimulationMgr::GetScene(plKey world) if (!scene) { - UInt32 maxSteps = (UInt32)hsCeil(fMaxDelta / fStepSize); - NxSceneDesc sceneDesc; sceneDesc.gravity.set(0, 0, -32.174049f); sceneDesc.userTriggerReport = &gSensorReport; sceneDesc.userContactReport = &gContactReport; - sceneDesc.maxTimestep = fStepSize; - sceneDesc.maxIter = maxSteps; scene = fSDK->createScene(sceneDesc); + // See "Advancing The Simulation State" in the PhysX SDK Documentation + // This will cause PhysX to only update for our step size. If we call simulate + // faster than that, PhysX will return immediately. If we call it slower than that, + // PhysX will do some extra steps for us (isn't that nice?). + // Anyway, this should be a good way to make us independent of the framerate. + // If not, I blame the usual suspects + scene->setTiming(kDefaultStepSize); + // Most physicals use the default friction and restitution values, so we // make them the default. NxMaterial* mat = scene->getMaterialFromIndex(0); @@ -605,25 +607,25 @@ void plSimulationMgr::Advance(float delSecs) return; fAccumulator += delSecs; - if (fAccumulator < fStepSize) + if (fAccumulator < kDefaultStepSize) { // Not enough time has passed to perform a substep. - plPXPhysicalControllerCore::UpdateNonPhysical(fAccumulator / fStepSize); + plPXPhysicalControllerCore::UpdateNonPhysical(fAccumulator / kDefaultStepSize); return; } - else if (fAccumulator > fMaxDelta) + else if (fAccumulator > kDefaultMaxDelta) { if (fExtraProfile) - Log("Step clamped from %f to limit of %f", fAccumulator, fMaxDelta); - fAccumulator = fMaxDelta; + Log("Step clamped from %f to limit of %f", fAccumulator, kDefaultMaxDelta); + fAccumulator = kDefaultMaxDelta; } ++fStepCount; - // Perform as many whole substeps as possible saving the remainder in our accumulator. - int numSubSteps = (int)(fAccumulator / fStepSize + 0.000001f); - float delta = numSubSteps * fStepSize; - fAccumulator -= delta; + // Perform as many whole substeps as possible saving the remainder in our accumulator. + int numSubSteps = (int)(fAccumulator / kDefaultStepSize + 0.000001f); + float delta = numSubSteps * kDefaultStepSize; + fAccumulator -= delta; plProfile_IncCount(StepLen, (int)(delta*1000)); plProfile_BeginTiming(Step); @@ -649,7 +651,7 @@ void plSimulationMgr::Advance(float delSecs) } } - plPXPhysicalControllerCore::Update(numSubSteps, fAccumulator / fStepSize); + plPXPhysicalControllerCore::Update(numSubSteps, fAccumulator / kDefaultStepSize); //sending off and clearing the Collision Messages generated by scene->simulate IDispatchCollisionMessages(); @@ -773,26 +775,6 @@ void plSimulationMgr::ISendUpdates() // ///////////////////////////////////////////////////////////////// -void plSimulationMgr::SetMaxDelta(float maxDelta) -{ - fMaxDelta = maxDelta; -} - -float plSimulationMgr::GetMaxDelta() const -{ - return fMaxDelta; -} - -void plSimulationMgr::SetStepsPerSecond(int stepsPerSecond) -{ - fStepSize = 1.0f / (float)stepsPerSecond; -} - -int plSimulationMgr::GetStepsPerSecond() -{ - return (int)((1.0 / fStepSize) + 0.5f); // round to nearest int -} - int plSimulationMgr::GetMaterialIdx(NxScene* scene, hsScalar friction, hsScalar restitution) { if (friction == 0.5f && restitution == 0.5f) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h index 7f85cd1c..34975a23 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -115,20 +115,6 @@ protected: plSimulationMgr(); virtual ~plSimulationMgr(); - // Set the maximum amount of time (in seconds) that the physics will advance - // between frames. If a frame-to-frame delta is bigger than this, we'll - // clamp it to this value. - // WARNING: animation doesn't do this, so if we clamp the time animated - // physicals and the avatar may move at a faster rate than usual. - void SetMaxDelta(float maxDelta); - float GetMaxDelta() const; - - // Set the number of steps per second that physics will advance. - // The more steps per second, the less fallthough and more accurate - // simulation response. - void SetStepsPerSecond(int stepsPerSecond); - int GetStepsPerSecond(); - // Walk through the synchronization requests and send them as appropriate. void IProcessSynchs(); @@ -157,10 +143,7 @@ protected: // but nothing will move. bool fSuspended; - float fMaxDelta; - float fStepSize; float fAccumulator; - UInt32 fStepCount; // A utility class to keep track of a request for a physical synchronization. From d0f1e6f3e7f8c083e60249e6c1fa30f124bfadb2 Mon Sep 17 00:00:00 2001 From: John Johns Date: Thu, 24 Jun 2021 22:05:24 -0700 Subject: [PATCH 2/4] Remove the PhysX changes orig. bundled with unlock --- .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 44 ++++++++++++------- .../PubUtilLib/plPhysX/plSimulationMgr.h | 18 ++++++++ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index b1b8b0bc..0715523e 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -347,6 +347,8 @@ plSimulationMgr* plSimulationMgr::GetInstance() plSimulationMgr::plSimulationMgr() : fSuspended(true) + , fMaxDelta(kDefaultMaxDelta) + , fStepSize(kDefaultStepSize) , fAccumulator(0.0f) , fStepCount(0) , fLOSDispatch(TRACKED_NEW plLOSDispatch()) @@ -420,20 +422,16 @@ NxScene* plSimulationMgr::GetScene(plKey world) if (!scene) { + UInt32 maxSteps = (UInt32)hsCeil(fMaxDelta / fStepSize); + NxSceneDesc sceneDesc; sceneDesc.gravity.set(0, 0, -32.174049f); sceneDesc.userTriggerReport = &gSensorReport; sceneDesc.userContactReport = &gContactReport; + sceneDesc.maxTimestep = fStepSize; + sceneDesc.maxIter = maxSteps; scene = fSDK->createScene(sceneDesc); - // See "Advancing The Simulation State" in the PhysX SDK Documentation - // This will cause PhysX to only update for our step size. If we call simulate - // faster than that, PhysX will return immediately. If we call it slower than that, - // PhysX will do some extra steps for us (isn't that nice?). - // Anyway, this should be a good way to make us independent of the framerate. - // If not, I blame the usual suspects - scene->setTiming(kDefaultStepSize); - // Most physicals use the default friction and restitution values, so we // make them the default. NxMaterial* mat = scene->getMaterialFromIndex(0); @@ -607,24 +605,24 @@ void plSimulationMgr::Advance(float delSecs) return; fAccumulator += delSecs; - if (fAccumulator < kDefaultStepSize) + if (fAccumulator < fStepSize) { // Not enough time has passed to perform a substep. - plPXPhysicalControllerCore::UpdateNonPhysical(fAccumulator / kDefaultStepSize); + plPXPhysicalControllerCore::UpdateNonPhysical(fAccumulator / fStepSize); return; } - else if (fAccumulator > kDefaultMaxDelta) + else if (fAccumulator > fMaxDelta) { if (fExtraProfile) - Log("Step clamped from %f to limit of %f", fAccumulator, kDefaultMaxDelta); + Log("Step clamped from %f to limit of %f", fAccumulator, fMaxDelta); fAccumulator = kDefaultMaxDelta; } ++fStepCount; // Perform as many whole substeps as possible saving the remainder in our accumulator. - int numSubSteps = (int)(fAccumulator / kDefaultStepSize + 0.000001f); - float delta = numSubSteps * kDefaultStepSize; + int numSubSteps = (int)(fAccumulator / fStepSize + 0.000001f); + float delta = numSubSteps * fStepSize; fAccumulator -= delta; plProfile_IncCount(StepLen, (int)(delta*1000)); @@ -651,7 +649,7 @@ void plSimulationMgr::Advance(float delSecs) } } - plPXPhysicalControllerCore::Update(numSubSteps, fAccumulator / kDefaultStepSize); + plPXPhysicalControllerCore::Update(numSubSteps, fAccumulator / fStepSize); //sending off and clearing the Collision Messages generated by scene->simulate IDispatchCollisionMessages(); @@ -774,6 +772,22 @@ void plSimulationMgr::ISendUpdates() // RESOLUTION & TIMEOUT PARAMETERS // ///////////////////////////////////////////////////////////////// +void plSimulationMgr::SetMaxDelta(float maxDelta) +{ + fMaxDelta = maxDelta; +} +float plSimulationMgr::GetMaxDelta() const +{ + return fMaxDelta; +} +void plSimulationMgr::SetStepsPerSecond(int stepsPerSecond) +{ + fStepSize = 1.0f / (float)stepsPerSecond; +} +int plSimulationMgr::GetStepsPerSecond() +{ + return (int)((1.0 / fStepSize) + 0.5f); // round to nearest int +} int plSimulationMgr::GetMaterialIdx(NxScene* scene, hsScalar friction, hsScalar restitution) { diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h index 34975a23..5720669a 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -115,6 +115,20 @@ protected: plSimulationMgr(); virtual ~plSimulationMgr(); + // Set the maximum amount of time (in seconds) that the physics will advance + // between frames. If a frame-to-frame delta is bigger than this, we'll + // clamp it to this value. + // WARNING: animation doesn't do this, so if we clamp the time animated + // physicals and the avatar may move at a faster rate than usual. + void SetMaxDelta(float maxDelta); + float GetMaxDelta() const; + + // Set the number of steps per second that physics will advance. + // The more steps per second, the less fallthough and more accurate + // simulation response. + void SetStepsPerSecond(int stepsPerSecond); + int GetStepsPerSecond(); + // Walk through the synchronization requests and send them as appropriate. void IProcessSynchs(); @@ -143,7 +157,11 @@ protected: // but nothing will move. bool fSuspended; + float fMaxDelta; + float fStepSize; + float fAccumulator; + UInt32 fStepCount; // A utility class to keep track of a request for a physical synchronization. From 4550451ae58d7ab06b2f572dee1811552a77cc61 Mon Sep 17 00:00:00 2001 From: John Johns Date: Fri, 2 Jul 2021 17:57:23 -0700 Subject: [PATCH 3/4] Fix things that framerate unlock will expose Hoikas pointed these out to me https://github.com/H-uru/Plasma/pull/503 https://github.com/H-uru/Plasma/pull/505 --- .../Plasma/PubUtilLib/plInterp/hsInterp.cpp | 2 +- .../PubUtilLib/plInterp/plAnimTimeConvert.cpp | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp b/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp index 092e7496..8495f710 100644 --- a/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp +++ b/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp @@ -404,7 +404,7 @@ void hsInterp::GetBoundaryKeyFrames(hsScalar time, UInt32 numKeys, void *keys, U { hsAssert(numKeys>1, "Must have more than 1 keyframe"); int k1, k2; - UInt16 frame = (UInt16)(time * MAX_FRAMES_PER_SEC); + float frame = (time * MAX_FRAMES_PER_SEC); // boundary case, past end if (frame > GetKey(numKeys-1, keys, size)->fFrame) diff --git a/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp b/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp index d0f5e4e7..31970667 100644 --- a/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp +++ b/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp @@ -557,8 +557,13 @@ hsScalar plAnimTimeConvert::WorldToAnimTime(double wSecs) { if (secs > fLoopEnd) { - secs = fmodf(secs - fLoopBegin, fLoopEnd - fLoopBegin) + fLoopBegin; - wrapped = true; + float result = fmodf(secs - fLoopBegin, fLoopEnd - fLoopBegin) + fLoopBegin; + // if fLoopBegin == fLoopEnd == 0, result will not be a number + if (!isnan(result)) + { + secs = result; + wrapped = true; + } } } } @@ -576,8 +581,13 @@ hsScalar plAnimTimeConvert::WorldToAnimTime(double wSecs) { if (secs < fLoopBegin) { - secs = fLoopEnd - fmodf(fLoopEnd - secs, fLoopEnd - fLoopBegin); - wrapped = true; + float result = fLoopEnd - fmodf(fLoopEnd - secs, fLoopEnd - fLoopBegin); + // if fLoopBegin == fLoopEnd == 0, result will not be a number + if (!isnan(result)) + { + secs = result; + wrapped = true; + } } } } From 8ca8eb29d3d70c1a9433e26555e3080178e40b58 Mon Sep 17 00:00:00 2001 From: rarified Date: Tue, 6 Jul 2021 14:37:41 -0600 Subject: [PATCH 4/4] Replace isnan() reference with _isnan(). VS2010 didn't have it in std::cmath. --- Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp b/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp index 31970667..48b0667c 100644 --- a/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp +++ b/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp @@ -559,7 +559,7 @@ hsScalar plAnimTimeConvert::WorldToAnimTime(double wSecs) { float result = fmodf(secs - fLoopBegin, fLoopEnd - fLoopBegin) + fLoopBegin; // if fLoopBegin == fLoopEnd == 0, result will not be a number - if (!isnan(result)) + if (!_isnan(result)) { secs = result; wrapped = true; @@ -583,7 +583,7 @@ hsScalar plAnimTimeConvert::WorldToAnimTime(double wSecs) { float result = fLoopEnd - fmodf(fLoopEnd - secs, fLoopEnd - fLoopBegin); // if fLoopBegin == fLoopEnd == 0, result will not be a number - if (!isnan(result)) + if (!_isnan(result)) { secs = result; wrapped = true;