Fix thread-safety crash in LipSync anim node: TMap race condition
GetCurrentBlendshapes() was copying CurrentBlendshapes on the anim worker thread while the game thread mutated it (TSet::UnhashElements crash). Use a snapshot pattern: game thread copies to ThreadSafeBlendshapes under FCriticalSection at end of TickComponent, anim node reads the snapshot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8d4065944c
commit
ca10689bb6
@ -1109,6 +1109,12 @@ void UPS_AI_ConvAgent_LipSyncComponent::TickComponent(float DeltaTime, ELevelTic
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot for thread-safe anim node read (GetCurrentBlendshapes)
|
||||
{
|
||||
FScopeLock Lock(&BlendshapeLock);
|
||||
ThreadSafeBlendshapes = CurrentBlendshapes;
|
||||
}
|
||||
|
||||
// Auto-apply morph targets if a target mesh is set
|
||||
if (TargetMesh)
|
||||
{
|
||||
@ -1201,6 +1207,10 @@ void UPS_AI_ConvAgent_LipSyncComponent::ResetToNeutral()
|
||||
|
||||
// Clear blendshapes so the mouth returns to fully neutral
|
||||
CurrentBlendshapes.Reset();
|
||||
{
|
||||
FScopeLock Lock(&BlendshapeLock);
|
||||
ThreadSafeBlendshapes.Reset();
|
||||
}
|
||||
PreviousBlendshapes.Reset();
|
||||
LastConsumedVisemes.Reset();
|
||||
}
|
||||
|
||||
@ -170,9 +170,14 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "PS AI ConvAgent|LipSync")
|
||||
TMap<FName, float> GetCurrentVisemes() const { return SmoothedVisemes; }
|
||||
|
||||
/** Get current ARKit blendshape weights (MetaHuman compatible: jawOpen, mouthFunnel, mouthClose, etc.). */
|
||||
/** Get current ARKit blendshape weights (MetaHuman compatible: jawOpen, mouthFunnel, mouthClose, etc.).
|
||||
* Thread-safe: returns a snapshot updated each tick. Safe to call from anim worker threads. */
|
||||
UFUNCTION(BlueprintCallable, Category = "PS AI ConvAgent|LipSync")
|
||||
TMap<FName, float> GetCurrentBlendshapes() const { return CurrentBlendshapes; }
|
||||
TMap<FName, float> GetCurrentBlendshapes() const
|
||||
{
|
||||
FScopeLock Lock(&BlendshapeLock);
|
||||
return ThreadSafeBlendshapes;
|
||||
}
|
||||
|
||||
/** True when the agent is currently producing speech audio.
|
||||
* When false, lip sync releases mouth curves to let emotion curves through. */
|
||||
@ -268,9 +273,14 @@ private:
|
||||
// Smoothed viseme weights (interpolated each tick, exposed via GetCurrentVisemes)
|
||||
TMap<FName, float> SmoothedVisemes;
|
||||
|
||||
// ARKit blendshape weights derived from SmoothedVisemes (exposed via GetCurrentBlendshapes)
|
||||
// ARKit blendshape weights derived from SmoothedVisemes (game-thread working copy)
|
||||
TMap<FName, float> CurrentBlendshapes;
|
||||
|
||||
// Thread-safe snapshot of CurrentBlendshapes, updated each tick under BlendshapeLock.
|
||||
// Read by the anim worker thread via GetCurrentBlendshapes().
|
||||
TMap<FName, float> ThreadSafeBlendshapes;
|
||||
mutable FCriticalSection BlendshapeLock;
|
||||
|
||||
// Previous frame's blendshape values for additional output smoothing
|
||||
TMap<FName, float> PreviousBlendshapes;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user