Fix: OptiTrack 플러그인 런타임 안전성 강화
- m_dataDescs null 체크 추가: SkipDataDescriptions=true 시 NatNet 스레드 크래시 방지 - m_dataDescs를 NatNet 콜백에서 로컬 변수로 캡처: UpdateDefinitions() 중 참조 교체 레이스 방지 - m_assetIdToNameCache 클리어를 락으로 보호: NatNet 스레드와의 동시 접근 방지 - _EnterFrameDataUpdateLock/_ExitFrameDataUpdateLock을 internal+Obsolete로 변경: 데드락 위험 차단 - OptitrackRawDataReceiver를 FillBoneSnapshot 패턴으로 변경: torn read 방지 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5aa805e16a
commit
f6a6034387
@ -10,6 +10,10 @@ public class OptitrackRawDataReceiver : MonoBehaviour
|
||||
private OptitrackSkeletonDefinition m_skeletonDef;
|
||||
private Dictionary<int, OptitrackPose> m_lastBonePoses = new Dictionary<int, OptitrackPose>();
|
||||
|
||||
// FillBoneSnapshot 패턴: 락 안에서 데이터를 복사하여 torn read 방지
|
||||
private Dictionary<int, Vector3> m_snapshotPositions = new Dictionary<int, Vector3>();
|
||||
private Dictionary<int, Quaternion> m_snapshotOrientations = new Dictionary<int, Quaternion>();
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (StreamingClient == null)
|
||||
@ -37,16 +41,24 @@ public class OptitrackRawDataReceiver : MonoBehaviour
|
||||
if (m_skeletonDef == null) return;
|
||||
}
|
||||
|
||||
// 최신 스켈레톤 상태 가져오기
|
||||
OptitrackSkeletonState skelState = StreamingClient.GetLatestSkeletonState(m_skeletonDef.Id);
|
||||
if (skelState == null) return;
|
||||
// FillBoneSnapshot으로 락 보호 하에 스냅샷 복사 (torn read 방지)
|
||||
OptitrackHiResTimer.Timestamp ts;
|
||||
if (!StreamingClient.FillBoneSnapshot(m_skeletonDef.Id, m_snapshotPositions, m_snapshotOrientations, out ts))
|
||||
return;
|
||||
|
||||
// 각 본의 원본 데이터 저장
|
||||
// 스냅샷에서 OptitrackPose로 변환하여 저장
|
||||
foreach (var bone in m_skeletonDef.Bones)
|
||||
{
|
||||
if (skelState.LocalBonePoses.TryGetValue(bone.Id, out OptitrackPose bonePose))
|
||||
if (m_snapshotPositions.TryGetValue(bone.Id, out Vector3 pos) &&
|
||||
m_snapshotOrientations.TryGetValue(bone.Id, out Quaternion ori))
|
||||
{
|
||||
m_lastBonePoses[bone.Id] = bonePose;
|
||||
if (!m_lastBonePoses.TryGetValue(bone.Id, out OptitrackPose existingPose))
|
||||
{
|
||||
existingPose = new OptitrackPose();
|
||||
m_lastBonePoses[bone.Id] = existingPose;
|
||||
}
|
||||
existingPose.Position = pos;
|
||||
existingPose.Orientation = ori;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1269,7 +1269,12 @@ public class OptitrackStreamingClient : MonoBehaviour
|
||||
m_skeletonDefinitions.Clear();
|
||||
m_tmarkersetDefinitions.Clear();
|
||||
m_mirrorBoneIdMaps.Clear(); // 스켈레톤 정의 변경 시 mirror map 캐시 무효화
|
||||
m_assetIdToNameCache.Clear(); // assetID→이름 캐시 무효화
|
||||
|
||||
// NatNet 스레드가 접근하는 캐시는 락으로 보호하여 레이스 방지
|
||||
lock (m_frameDataUpdateLock)
|
||||
{
|
||||
m_assetIdToNameCache.Clear();
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// - Translate Rigid Body Definitions
|
||||
@ -1880,19 +1885,19 @@ public class OptitrackStreamingClient : MonoBehaviour
|
||||
// -----------------------------------------------------
|
||||
// - Update trained markerset // trained markerset added
|
||||
// ----------------------------------------------------
|
||||
//Int32 frameTMarkersetCount = m_dataDescs.AssetDescriptions.Count;
|
||||
/*result = NaturalPoint.NatNetLib.NativeMethods.NatNet_Frame_GetTMarkersetCount(pFrame, out frameTMarkersetCount);
|
||||
NatNetException.ThrowIfNotOK(result, "NatNet_Frame_GetTMarkersetCount failed.");*/
|
||||
|
||||
for (int tmarkIdx = 0; tmarkIdx < m_dataDescs.AssetDescriptions.Count; ++tmarkIdx)
|
||||
// m_dataDescs를 로컬 변수로 캡처: UpdateDefinitions()(메인 스레드)가 참조를 교체해도 안전
|
||||
// null 체크: SkipDataDescriptions=true 또는 UpdateDefinitions() 미완료/실패 시 크래시 방지
|
||||
var dataDescsSnapshot = m_dataDescs;
|
||||
if (dataDescsSnapshot != null && dataDescsSnapshot.AssetDescriptions != null)
|
||||
for (int tmarkIdx = 0; tmarkIdx < dataDescsSnapshot.AssetDescriptions.Count; ++tmarkIdx)
|
||||
{
|
||||
Int32 tmarkersetId = m_dataDescs.AssetDescriptions[tmarkIdx].AssetID;
|
||||
Int32 tmarkersetId = dataDescsSnapshot.AssetDescriptions[tmarkIdx].AssetID;
|
||||
|
||||
// Ensure we have a state corresponding to this tmarkerset ID.
|
||||
OptitrackTMarkersetState tmarkState = GetOrCreateTMarkersetState(tmarkersetId);
|
||||
|
||||
// TMarkerset 정의 검색을 본 루프 밖에서 1회만 수행 (기존: 매 본마다 선형 탐색)
|
||||
Int32 tmarkRbCount = m_dataDescs.AssetDescriptions[tmarkIdx].RigidBodyCount;
|
||||
Int32 tmarkRbCount = dataDescsSnapshot.AssetDescriptions[tmarkIdx].RigidBodyCount;
|
||||
OptitrackTMarkersetDefinition tmarkDef = GetTMarkersetDefinitionById(tmarkersetId);
|
||||
if (tmarkDef == null)
|
||||
{
|
||||
@ -1940,7 +1945,7 @@ public class OptitrackStreamingClient : MonoBehaviour
|
||||
// --------------------------------------------
|
||||
// - Update trained markerset markers
|
||||
// --------------------------------------------
|
||||
Int32 tmarkMarkerCount = m_dataDescs.AssetDescriptions[tmarkIdx].MarkerCount;
|
||||
Int32 tmarkMarkerCount = dataDescsSnapshot.AssetDescriptions[tmarkIdx].MarkerCount;
|
||||
/*result = NaturalPoint.NatNetLib.NativeMethods.NatNet_Frame_TMarkerset_GetMarkerCount(pFrame, tmarkIdx, out tmarkMarkerCount);
|
||||
NatNetException.ThrowIfNotOK(result, "NatNet_Frame_TMarkerset_GetMarkerCount failed.");*/
|
||||
//Debug.Log("tmark marker count: " + tmarkMarkerCount); // working finally
|
||||
@ -2398,13 +2403,21 @@ public class OptitrackStreamingClient : MonoBehaviour
|
||||
}
|
||||
|
||||
|
||||
public void _EnterFrameDataUpdateLock()
|
||||
/// <summary>
|
||||
/// 내부 프레임 데이터 락 진입. 반드시 try-finally 패턴으로 _ExitFrameDataUpdateLock()과 쌍으로 사용하세요.
|
||||
/// Exit 없이 호출하면 NatNet 스레드가 영구 데드락됩니다.
|
||||
/// </summary>
|
||||
[System.Obsolete("직접 락 조작 대신 FillBoneSnapshot() 등 스레드 안전 API를 사용하세요.")]
|
||||
internal void _EnterFrameDataUpdateLock()
|
||||
{
|
||||
Monitor.Enter( m_frameDataUpdateLock );
|
||||
}
|
||||
|
||||
|
||||
public void _ExitFrameDataUpdateLock()
|
||||
/// <summary>
|
||||
/// 내부 프레임 데이터 락 해제. 반드시 _EnterFrameDataUpdateLock()과 쌍으로 사용하세요.
|
||||
/// </summary>
|
||||
[System.Obsolete("직접 락 조작 대신 FillBoneSnapshot() 등 스레드 안전 API를 사용하세요.")]
|
||||
internal void _ExitFrameDataUpdateLock()
|
||||
{
|
||||
Monitor.Exit( m_frameDataUpdateLock );
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user