Skip to content

Commit fea6dca

Browse files
committed
little math changes to go a little faster
1 parent 2ea0df4 commit fea6dca

5 files changed

Lines changed: 125 additions & 46 deletions

File tree

Basis/Packages/com.basis.eeriemovement/Spine/BasisEerieMovement.Spine.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,20 @@ namespace Basis.IK
99
/// </summary>
1010
public partial struct BasisEerieMovement
1111
{
12+
static readonly Unity.Profiling.ProfilerMarker sMarkerSpineHips = new Unity.Profiling.ProfilerMarker("BasisEerie.Spine.HipsPlacement");
13+
static readonly Unity.Profiling.ProfilerMarker sMarkerSpineChainPrep = new Unity.Profiling.ProfilerMarker("BasisEerie.Spine.ChainPrep");
14+
static readonly Unity.Profiling.ProfilerMarker sMarkerSpineSequential = new Unity.Profiling.ProfilerMarker("BasisEerie.Spine.SequentialIK");
15+
static readonly Unity.Profiling.ProfilerMarker sMarkerSpineLordosis = new Unity.Profiling.ProfilerMarker("BasisEerie.Spine.Lordosis");
16+
1217
// Hips + the chest/neck/head chain, then the anatomy modifiers that act on the spine after it.
1318
void SolveSpinePass(BasisPoseStream stream)
1419
{
1520
SolveSpine(stream);
1621
if (anatCervicalLordosis)
1722
{
23+
sMarkerSpineLordosis.Begin();
1824
ApplyCervicalLordosis(stream);
25+
sMarkerSpineLordosis.End();
1926
}
2027
}
2128

@@ -25,6 +32,7 @@ public void SolveSpine(BasisPoseStream stream)
2532
{
2633
return;
2734
}
35+
sMarkerSpineHips.Begin();
2836
// ---- Read targets ----
2937
Vector3 headTargetPos = targetPositionHead;
3038
Vector3 hipsTargetPos = targetPositionHips;
@@ -103,8 +111,10 @@ public void SolveSpine(BasisPoseStream stream)
103111
handleHips.SetPosition(stream, hipsTargetPos);
104112
handleHips.SetRotation(stream, hipDesired);
105113
}
114+
sMarkerSpineHips.End();
106115
if (hasChestTracker && handleChest.IsValid(stream))
107116
{
117+
sMarkerSpineChainPrep.Begin();
108118
// Neck rotation produced by your spine IK pass – we keep this
109119
Quaternion neckRot = handleNeck.IsValid(stream) ? handleNeck.GetRotation(stream) : Quaternion.identity;
110120

@@ -124,17 +134,24 @@ public void SolveSpine(BasisPoseStream stream)
124134
DistributeSpineBend(stream, headPos);
125135
BiasSpineTowardChest(stream);
126136
GuardSpineChain(stream);
137+
sMarkerSpineChainPrep.End();
138+
sMarkerSpineSequential.Begin();
127139
SolveSequentialSpineIK(stream, headPos, headRot);
140+
sMarkerSpineSequential.End();
128141
}
129142
else if (handleChest.IsValid(stream) && handleNeck.IsValid(stream) && handleHead.IsValid(stream))
130143
{
131144
Vector3 headPos = targetPositionHead;
132145
Quaternion headRot = targetRotationHead;
133146

147+
sMarkerSpineChainPrep.Begin();
134148
DistributeSpineBend(stream, headPos);
135149
ApplyArmSwingChestFollow(stream);
136150
GuardSpineChain(stream);
151+
sMarkerSpineChainPrep.End();
152+
sMarkerSpineSequential.Begin();
137153
SolveSequentialSpineIK(stream, headPos, headRot);
154+
sMarkerSpineSequential.End();
138155
}
139156
}
140157
public void SolveSequentialSpineIK(BasisPoseStream stream, Vector3 headTargetPos, Quaternion headTargetRot)
@@ -219,7 +236,7 @@ public void SolveSequentialSpineIK(BasisPoseStream stream, Vector3 headTargetPos
219236
// the chest target is off (weight 0). See SolveChestTarget.
220237
// ==========================================================================================
221238
SolveChestTarget(stream, headTargetPos, firstJoint, lastJoint, chainLen, jointSpan,
222-
cervicalTwistKeep, lumbarTwistKeep, ccdUp, ccdRelax, neckCone, chestCone);
239+
cervicalTwistKeep, lumbarTwistKeep, ccdUp, ccdRelax, neckCone, chestCone, tolSqr);
223240

224241
chainHeadToSpine[tipIdx].SetRotation(stream, finalHeadRot);
225242
}
@@ -262,7 +279,7 @@ void ReachHeadJoint(BasisPoseStream stream, int i, Vector3 headTargetPos, int fi
262279
}
263280
void SolveChestTarget(BasisPoseStream stream, Vector3 headTargetPos, int firstJoint, int lastJoint,
264281
int chainLen, float jointSpan, float cervicalTwistKeep, float lumbarTwistKeep, Vector3 ccdUp,
265-
float ccdRelax, float neckCone, float chestCone)
282+
float ccdRelax, float neckCone, float chestCone, float tolSqr)
266283
{
267284
// Off (toggle false -> weight 0): return before touching a single bone, so the head-only solve
268285
// above is the whole story, bit for bit. This is the "same usability" guarantee.
@@ -293,7 +310,19 @@ void SolveChestTarget(BasisPoseStream stream, Vector3 headTargetPos, int firstJo
293310
{
294311
// 1) rotate the Spine so the Chest bone slides toward its target.
295312
Vector3 spinePos = chainHeadToSpine[lastJoint].GetPosition(stream);
296-
Vector3 cCur = chainHeadToSpine[chestBoneIdx].GetPosition(stream) - spinePos;
313+
Vector3 chestNow = chainHeadToSpine[chestBoneIdx].GetPosition(stream);
314+
315+
// Phase A already breaks on this exact criterion. Phase B spent its whole iteration
316+
// budget regardless, re-solving a chest and a head that were both already inside the
317+
// solver's own tolerance. A zero spineTolerance makes this unreachable, which is the
318+
// old behaviour exactly.
319+
if ((chestTargetPos - chestNow).sqrMagnitude < tolSqr
320+
&& (headTargetPos - chainHeadToSpine[0].GetPosition(stream)).sqrMagnitude < tolSqr)
321+
{
322+
break;
323+
}
324+
325+
Vector3 cCur = chestNow - spinePos;
297326
Vector3 cTgt = chestTargetPos - spinePos;
298327
if (cCur.sqrMagnitude > k_SqrEpsilon && cTgt.sqrMagnitude > k_SqrEpsilon)
299328
{

Basis/Packages/com.basis.framework/BasisUI/BasisHorizontalLayout.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,37 @@ namespace Basis.BasisUI
1111
{
1212
public class BasisHorizontalLayout : HorizontalLayoutGroup
1313
{
14+
#if UNITY_EDITOR || DEVELOPMENT_BUILD
15+
/// <summary>
16+
/// A resize that lands WHILE the canvas is rebuilding layout makes uGUI's LayoutGroup.SetDirty
17+
/// take its coroutine branch (DelayedSetDirty), which marks this layout dirty again next frame —
18+
/// which resizes it again. That loop never settles, and it is invisible in a profile unless you
19+
/// know that a steady DelayedSetDirty call count IS the symptom. Name the object once so the
20+
/// oscillation can be found, then get out of the way.
21+
/// </summary>
22+
protected override void OnRectTransformDimensionsChange()
23+
{
24+
if (Application.isPlaying && IsActive() && CanvasUpdateRegistry.IsRebuildingLayout())
25+
{
26+
string path = HierarchyPath(transform);
27+
BasisDebug.LogWarningOnce($"layout-rebuild-loop-{path}",
28+
$"BasisHorizontalLayout '{path}' was resized DURING a layout rebuild, so uGUI queued a DelayedSetDirty coroutine that will mark it dirty again next frame — a self-sustaining rebuild loop. Look for a ContentSizeFitter fighting a LayoutGroup on this object or an ancestor.",
29+
BasisDebug.LogTag.System);
30+
}
31+
base.OnRectTransformDimensionsChange();
32+
}
33+
34+
static string HierarchyPath(Transform leaf)
35+
{
36+
var sb = new System.Text.StringBuilder(128);
37+
for (Transform walk = leaf; walk != null; walk = walk.parent)
38+
{
39+
sb.Insert(0, walk.name).Insert(0, '/');
40+
}
41+
return sb.ToString();
42+
}
43+
#endif
44+
1445
[ContextMenu("Set Alignment Left")]
1546
public void SetAlignmentLeft() => SetAlignment(TextAlignment.Left);
1647
[ContextMenu("Set Alignment Center")]

Basis/Packages/com.basis.framework/Drivers/Local/LocomotionPose/BasisLocomotionPoseSystem.cs

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ public static bool IsStockController(Animator animator)
6666
JobHandle _handle;
6767
bool _scheduled;
6868

69+
static readonly Unity.Profiling.ProfilerMarker sMarkerLocoGate = new Unity.Profiling.ProfilerMarker("BasisDriver.LocoPose.Gate");
70+
static readonly Unity.Profiling.ProfilerMarker sMarkerLocoGraphStep = new Unity.Profiling.ProfilerMarker("BasisDriver.LocoPose.GraphStep");
71+
static readonly Unity.Profiling.ProfilerMarker sMarkerLocoDispatch = new Unity.Profiling.ProfilerMarker("BasisDriver.LocoPose.Dispatch");
72+
6973
public void NotifyLanding()
7074
{
7175
_landingLatch = true;
@@ -94,52 +98,57 @@ public void OnRigBuilt()
9498
/// </summary>
9599
public void Schedule(BasisLocalRigDriver rig, Animator animator, in BasisLocoParams frameParams, float deltaTime)
96100
{
97-
bool rigReady = rig != null && rig.IKDataReady && rig.IKJobCreated && rig.RigLayerActive && rig.PoseSkeleton.IsCreated;
98-
bool graphValid = rigReady && rig.PlayableGraph.IsValid();
99-
bool tposeLike = BasisLocalAvatarDriver.CurrentlyTposing || BasisLocalAvatarDriver.SavedruntimeAnimatorController != null;
100-
bool stock = IsStockController(animator);
101-
102-
// Lazy bake kickoff: a Start failure leaves the failed baker in place, which blocks retries
103-
// until the next rig build.
104-
if (JobDrivenLocomotionPose && stock && rigReady && _bake == null && _baker == null)
101+
bool frozen;
102+
bool fastActive;
103+
using (sMarkerLocoGate.Auto())
105104
{
106-
_baker = new BasisLocomotionPoseBaker();
107-
_baker.Start(animator, sStockController, rig.PoseSkeleton.DebugNodes, rig.basisTransformMapping.Hips);
108-
}
105+
bool rigReady = rig != null && rig.IKDataReady && rig.IKJobCreated && rig.RigLayerActive && rig.PoseSkeleton.IsCreated;
106+
bool graphValid = rigReady && rig.PlayableGraph.IsValid();
107+
bool tposeLike = BasisLocalAvatarDriver.CurrentlyTposing || BasisLocalAvatarDriver.SavedruntimeAnimatorController != null;
108+
bool stock = IsStockController(animator);
109109

110-
if (_baker != null && !_baker.Failed)
111-
{
112-
if (!_baker.Tick() && _baker.Done)
110+
// Lazy bake kickoff: a Start failure leaves the failed baker in place, which blocks retries
111+
// until the next rig build.
112+
if (JobDrivenLocomotionPose && stock && rigReady && _bake == null && _baker == null)
113113
{
114-
_bake = _baker.TakeBake();
115-
EnsureRuntimeArrays();
116-
_baker.Dispose();
117-
_baker = null;
114+
_baker = new BasisLocomotionPoseBaker();
115+
_baker.Start(animator, sStockController, rig.PoseSkeleton.DebugNodes, rig.basisTransformMapping.Hips);
118116
}
119-
}
120-
121-
bool fbtConditions = FreezeAnimatorInFullFBT && stock && !tposeLike
122-
&& BasisAvatarIKStageCalibration.HasLegFBIKTrackers
123-
&& Basis.BasisUI.BasisSettingsDefaults.DisableAnimationsInFBT.RawValue;
124-
_freezeArmTimer = fbtConditions ? _freezeArmTimer + deltaTime : 0f;
125-
bool frozen = fbtConditions && _freezeArmTimer >= FreezeArmSeconds;
126117

127-
bool fastActive = JobDrivenLocomotionPose && stock && !tposeLike && rigReady && graphValid
128-
&& _bake != null && _bake.Ready;
129-
130-
bool suppress = graphValid && (fastActive || (stock && frozen));
131-
EngineAnimatorSuppressed = suppress;
132-
if (graphValid && suppress != _graphStopped)
133-
{
134-
if (suppress)
118+
if (_baker != null && !_baker.Failed)
135119
{
136-
rig.PlayableGraph.Stop();
120+
if (!_baker.Tick() && _baker.Done)
121+
{
122+
_bake = _baker.TakeBake();
123+
EnsureRuntimeArrays();
124+
_baker.Dispose();
125+
_baker = null;
126+
}
137127
}
138-
else
128+
129+
bool fbtConditions = FreezeAnimatorInFullFBT && stock && !tposeLike
130+
&& BasisAvatarIKStageCalibration.HasLegFBIKTrackers
131+
&& Basis.BasisUI.BasisSettingsDefaults.DisableAnimationsInFBT.RawValue;
132+
_freezeArmTimer = fbtConditions ? _freezeArmTimer + deltaTime : 0f;
133+
frozen = fbtConditions && _freezeArmTimer >= FreezeArmSeconds;
134+
135+
fastActive = JobDrivenLocomotionPose && stock && !tposeLike && rigReady && graphValid
136+
&& _bake != null && _bake.Ready;
137+
138+
bool suppress = graphValid && (fastActive || (stock && frozen));
139+
EngineAnimatorSuppressed = suppress;
140+
if (graphValid && suppress != _graphStopped)
139141
{
140-
rig.PlayableGraph.Play();
142+
if (suppress)
143+
{
144+
rig.PlayableGraph.Stop();
145+
}
146+
else
147+
{
148+
rig.PlayableGraph.Play();
149+
}
150+
_graphStopped = suppress;
141151
}
142-
_graphStopped = suppress;
143152
}
144153

145154
if (!fastActive)
@@ -158,6 +167,7 @@ public void Schedule(BasisLocalRigDriver rig, Animator animator, in BasisLocoPar
158167

159168
if (!frozen || _contributionCount == 0)
160169
{
170+
using var _ = sMarkerLocoGraphStep.Auto();
161171
BasisLocoParams stepParams = frameParams;
162172
stepParams.LandingTrigger = _landingLatch;
163173
_contributionCount = BasisLocomotionGraph.Step(
@@ -175,6 +185,7 @@ public void Schedule(BasisLocalRigDriver rig, Animator animator, in BasisLocoPar
175185
return;
176186
}
177187

188+
using var dispatch = sMarkerLocoDispatch.Auto();
178189
rig.PoseSkeleton.CopyRestPositionsInto(_restPositions);
179190
var job = new BasisLocomotionPoseJob
180191
{

Basis/Packages/com.basis.framework/Eerie Movement Integration/Foot/BasisLocalFootDriver.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -944,10 +944,12 @@ public unsafe void ScheduleSimulate(float dt)
944944
var headData = BasisLocalBoneDriver.HeadControl.OutgoingWorldData;
945945
var hipsData = BasisLocalBoneDriver.HipsControl.OutgoingWorldData;
946946
var chestCtrl = BasisLocalBoneDriver.ChestControl;
947-
bool groundHit = GroundCast(hips.position, -cachedPlayerUp, rayCastRange, 0f, Vector3.Dot(hips.position, cachedPlayerUp), out RaycastHit ch);
947+
Vector3 hipsPosition = hips.position;
948+
float hipsUpComponent = Vector3.Dot(hipsPosition, cachedPlayerUp);
949+
bool groundHit = GroundCast(hipsPosition, -cachedPlayerUp, rayCastRange, 0f, hipsUpComponent, out RaycastHit ch);
948950
LastGroundHit = groundHit;
949951
LastGroundUp = groundHit ? Vector3.Dot(ch.point, cachedPlayerUp) : float.NaN;
950-
HipsUp = Vector3.Dot(hips.position, cachedPlayerUp);
952+
HipsUp = hipsUpComponent;
951953

952954
// ── 1b. Surface conformance probes (the Burst sim job cannot raycast) ──
953955
// Consumes the batch scheduled at the END of last frame, so the rays themselves cost the main thread
@@ -959,17 +961,18 @@ public unsafe void ScheduleSimulate(float dt)
959961
}
960962

961963
// ── 2. Pack input (write in place; no job is in flight here) ──
964+
Quaternion avatarRotation = avatarTransform.rotation;
962965
ref BasisFootSimInput inputSlot = ref UnsafeUtility.ArrayElementAsRef<BasisFootSimInput>(_nativeInput.GetUnsafePtr(), 0);
963966
inputSlot = new BasisFootSimInput
964967
{
965968
dt = dt,
966969
headPos = headData.position,
967-
hipsPos = hips.position,
970+
hipsPos = hipsPosition,
968971
hipsRot = hipsData.rotation,
969972
chestRot = chestCtrl.OutgoingWorldData.rotation,
970973
headRot = headData.rotation,
971-
avatarForward = avatarTransform.forward,
972-
avatarRight = avatarTransform.right,
974+
avatarForward = avatarRotation * Vector3.forward,
975+
avatarRight = avatarRotation * Vector3.right,
973976
hasChest = chestCtrl != null,
974977
groundHit = groundHit,
975978
groundPoint = groundHit ? (float3)ch.point : float3.zero,
@@ -1226,6 +1229,7 @@ public unsafe void ScheduleSurfaceProbes()
12261229

12271230
_probeHandle = RaycastCommand.ScheduleBatch(_probeCommands, _probeResults, k_ProbeRays, k_ProbeMaxHits);
12281231
_probePending = true;
1232+
JobHandle.ScheduleBatchedJobs();
12291233
}
12301234

12311235
private unsafe void ApplySurfaceProbes(float dt)

Basis/Packages/com.basis.framework/Players/Local/BasisLocalPlayer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ public void DriveAudioToViseme()
561561
LocalVisemeDriver.ProcessAudioSamples(BasisLocalMicrophoneDriver.processBufferArray,1,BasisLocalMicrophoneDriver.processBufferArray.Length);
562562
#endif
563563
}
564+
static readonly ProfilerMarker sMarkerLocoPoseSchedule = new ProfilerMarker("BasisDriver.LocalPlayer.LocoPoseSchedule");
564565
static readonly ProfilerMarker sMarkerMovement = new ProfilerMarker("BasisDriver.LocalPlayer.Movement");
565566
static readonly ProfilerMarker sMarkerPlayspaceMover = new ProfilerMarker("BasisDriver.LocalPlayer.PlayspaceMover");
566567
static readonly ProfilerMarker sMarkerVirtualData = new ProfilerMarker("BasisDriver.LocalPlayer.VirtualData");
@@ -575,7 +576,10 @@ public void Simulate(float DeltaTime)
575576
{
576577
// Kick the locomotion pose job first: when active it fills the IK stream on a worker
577578
// while everything below runs, and is joined inside SimulateIKDestinations.
578-
LocalRigDriver.ScheduleLocomotionPose(this, DeltaTime);
579+
using (sMarkerLocoPoseSchedule.Auto())
580+
{
581+
LocalRigDriver.ScheduleLocomotionPose(this, DeltaTime);
582+
}
579583

580584
// now lets move the local player position.
581585
using (sMarkerMovement.Auto())

0 commit comments

Comments
 (0)