diff --git a/engine/MUNGA_L4/L4VIDEO.cpp b/engine/MUNGA_L4/L4VIDEO.cpp index 81eb4cb..bbc9ef3 100644 --- a/engine/MUNGA_L4/L4VIDEO.cpp +++ b/engine/MUNGA_L4/L4VIDEO.cpp @@ -8577,6 +8577,37 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte const double presentMs = (double)(_rt2.QuadPart - _rt1.QuadPart) * 1000.0 / (double)_rf.QuadPart; static double sAcc = 0.0, sMaxD = 0.0, sMaxP = 0.0; static int sFrames = 0; sAcc += drawMs + presentMs; ++sFrames; + // RENDER-frame heading probe (BT_RENDHDG): count render frames that redraw + // the SAME peer heading (stale between sim updates) vs frames where it moved. + // sameHeading >> moved => the peer sim updates slower than the render draws, + // so the rotation stutters regardless of how smooth each sim step is. + if (getenv("BT_RENDHDG")) + { + extern volatile float gBTReplRenderYaw; + static float sPrevY = -999.0f, sMaxStep = 0.0f, sSumStep = 0.0f; + static int sN = 0, sBack = 0; static double sHAcc = 0.0; + const float y = gBTReplRenderYaw; + if (sPrevY > -900.0f) + { + float d = y - sPrevY; // per-RENDER-frame heading change + if (d > 3.14159f) d -= 6.28319f; // unwrap + if (d < -3.14159f) d += 6.28319f; + const float ad = (d < 0.0f) ? -d : d; + sSumStep += ad; if (ad > sMaxStep) sMaxStep = ad; + if (d > 0.0f) ++sBack; // spin is -yaw here; +step == backward + ++sN; + } + sPrevY = y; sHAcc += drawMs + presentMs; + if (sHAcc >= 1000.0 && sN > 0) + { + const float avg = sSumStep / sN; + DEBUG_STREAM << "[rendhdg] avgStep=" << avg << " maxStep=" << sMaxStep + << " max/avg=" << (avg > 1e-6f ? sMaxStep / avg : 0.0f) + << " backwardFrames=" << sBack << "/" << sN + << " (smooth: max/avg~1, backward~0)\n" << std::flush; + sMaxStep = 0.0f; sSumStep = 0.0f; sN = 0; sBack = 0; sHAcc = 0.0; + } + } if (drawMs > sMaxD) sMaxD = drawMs; if (presentMs > sMaxP) sMaxP = presentMs; if (drawMs + presentMs > 150.0) diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 0061591..0d8f15f 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -1863,6 +1863,9 @@ void } } +// Peer heading published each sim update, sampled by the render loop (BT_RENDHDG). +volatile float gBTReplRenderYaw = -999.0f; + void Mech::PerformAndWatch(const Time& till, MemoryStream *update_stream) { @@ -1930,6 +1933,18 @@ void DeadReckon(dt); // engine: reckoner + lerp localToWorld = localOrigin; + // RENDER-vs-SIM decoupling probe: publish the heading the RENDER will + // actually draw (localToWorld == localOrigin here). The render loop + // (L4VIDEO.cpp) logs this PER RENDERED FRAME; if it repeats the same + // value across render frames, the peer sim updates slower than the + // render draws -> the rotation stutters even though each sim update is + // smooth (which the per-update [replhdg] metric could not see). + { + extern volatile float gBTReplRenderYaw; + YawPitchRoll _rry; _rry = localOrigin.angularPosition; + gBTReplRenderYaw = (float)_rry.yaw; + } + // Lightweight dead-reckon smoothness probe (one flush/sec, cheap). // PATH length (sum of |per-frame step|) vs NET displacement reveals // jitter: a smooth walk has path==net; oscillation inflates path while @@ -3587,6 +3602,11 @@ void angStepM.Multiply(updateVelocity.angularMotion, dt); projectedOrigin.angularPosition.Add( projectedOrigin.angularPosition, angStepM); + // Adding a scaled angular-velocity VECTOR to a quaternion denormalizes + // it (the reckoner MOVER.cpp:463 does the same); a non-unit quaternion's + // extracted yaw is garbage (~pi), which spiked angDrift and FLOODED angle + // resyncs in bursts -> peer horizon reset -> uneven (max/avg ~2.8x) render. + projectedOrigin.angularPosition.Normalize(); } Scalar angDb = (updateTurnAngleDeadband > 0.0f) ? updateTurnAngleDeadband : -1.0f; Scalar velDb = (updateTurnVelocityDeadband > 0.0f) ? updateTurnVelocityDeadband : -1.0f;