diff --git a/Basis/Packages/com.basis.eventdriver/BasisEventDriver.cs b/Basis/Packages/com.basis.eventdriver/BasisEventDriver.cs index 4a322bf24..15e6af9d0 100644 --- a/Basis/Packages/com.basis.eventdriver/BasisEventDriver.cs +++ b/Basis/Packages/com.basis.eventdriver/BasisEventDriver.cs @@ -230,10 +230,12 @@ public void LateUpdate() /// private void OnBeforeRender() { + // Ensure remote face jobs are completed/applied even before local player is ready. + BasisRemoteFaceManagement.Apply(); //apply blendshapes + if (BasisLocalPlayer.PlayerReady) { BasisLocalPlayer.Instance.SimulateOnRender(); - BasisRemoteFaceManagement.Apply(); //apply blendshapes BasisLocalCameraDriver.Instance.microphoneIconDriver.Simulate(DeltaTime); //update microphone icon } } @@ -275,3 +277,4 @@ public void OnDrawGizmosSelected() #endif } } + diff --git a/Basis/Packages/com.basis.framework/Networking/BasisRemoteFaceManagement.cs b/Basis/Packages/com.basis.framework/Networking/BasisRemoteFaceManagement.cs index cbc2c1285..5c4207a6b 100644 --- a/Basis/Packages/com.basis.framework/Networking/BasisRemoteFaceManagement.cs +++ b/Basis/Packages/com.basis.framework/Networking/BasisRemoteFaceManagement.cs @@ -55,6 +55,10 @@ public static void Simulate(double t,float dt) { snapshot = BasisNetworkPlayers.ReceiversSnapshot; count = BasisNetworkPlayers.ReceiverCount; + + // Keep job lifecycle simple: finish prior frame work before scheduling again. + handle.Complete(); + if (count <= 0) { return; diff --git a/Basis/Packages/com.hecomi.ulipsync/BasisUlipSync.cs b/Basis/Packages/com.hecomi.ulipsync/BasisUlipSync.cs index d866195f4..1d4ece47e 100644 --- a/Basis/Packages/com.hecomi.ulipsync/BasisUlipSync.cs +++ b/Basis/Packages/com.hecomi.ulipsync/BasisUlipSync.cs @@ -52,6 +52,7 @@ public unsafe class BasisUlipSync BasisDctPlan _dctPlan; float[] _lastApplied; + readonly object _bufferSwapLock = new object(); public struct BlendMap { @@ -142,6 +143,13 @@ public void Execute() public void Simulate(float DeltaTime) { + if (HasJob) + { + _jobHandle.Complete(); + _jobHandle = default; + HasJob = false; + } + if (Interlocked.Exchange(ref _isDataReceived, 0) != 1) return; if (!_allocated) return; @@ -149,16 +157,23 @@ public void Simulate(float DeltaTime) if (!BasisUlipSyncDriver.IsInitialized) return; - int oldActive = _activeInputBuffer; - int newActive = oldActive ^ 1; + int oldActive; + int frozenStartIndex; + NativeArray frozenInput; + + lock (_bufferSwapLock) + { + oldActive = _activeInputBuffer; + int newActive = oldActive ^ 1; - Volatile.Write(ref _activeInputBuffer, newActive); + Volatile.Write(ref _activeInputBuffer, newActive); - int frozenStartIndex = oldActive == 0 - ? Volatile.Read(ref _writeIndexA) - : Volatile.Read(ref _writeIndexB); + frozenStartIndex = oldActive == 0 + ? Volatile.Read(ref _writeIndexA) + : Volatile.Read(ref _writeIndexB); - NativeArray frozenInput = oldActive == 0 ? _inputA : _inputB; + frozenInput = oldActive == 0 ? _inputA : _inputB; + } byte normalizeScores = (byte)0; @@ -236,6 +251,7 @@ public void Apply() HasJob = false; _jobHandle.Complete(); + _jobHandle = default; if (_mfccForOther.IsCreated && _mfcc.IsCreated) { @@ -441,24 +457,27 @@ public void OnDataReceived(float[] input, int channels, int length) int ch = math.max(channels, 1); - int buf = Volatile.Read(ref _activeInputBuffer); - NativeArray dstArr = (buf == 0) ? _inputA : _inputB; - - float* dst = (float*)NativeArrayUnsafeUtility.GetUnsafePtr(dstArr); - - fixed (float* src = input) + lock (_bufferSwapLock) { - int w = (buf == 0) ? Volatile.Read(ref _writeIndexA) : Volatile.Read(ref _writeIndexB); + int buf = Volatile.Read(ref _activeInputBuffer); + NativeArray dstArr = (buf == 0) ? _inputA : _inputB; + + float* dst = (float*)NativeArrayUnsafeUtility.GetUnsafePtr(dstArr); - for (int s = 0; s < length; s += ch) + fixed (float* src = input) { - dst[w] = src[s]; - w++; - if (w == cap) w = 0; - } + int w = (buf == 0) ? Volatile.Read(ref _writeIndexA) : Volatile.Read(ref _writeIndexB); + + for (int s = 0; s < length; s += ch) + { + dst[w] = src[s]; + w++; + if (w == cap) w = 0; + } - if (buf == 0) Volatile.Write(ref _writeIndexA, w); - else Volatile.Write(ref _writeIndexB, w); + if (buf == 0) Volatile.Write(ref _writeIndexA, w); + else Volatile.Write(ref _writeIndexB, w); + } } Interlocked.Exchange(ref _isDataReceived, 1); diff --git a/Basis/Packages/com.steam.steamaudio/Runtime/SteamAudioManager.cs b/Basis/Packages/com.steam.steamaudio/Runtime/SteamAudioManager.cs index 30274baff..b65b9ce87 100644 --- a/Basis/Packages/com.steam.steamaudio/Runtime/SteamAudioManager.cs +++ b/Basis/Packages/com.steam.steamaudio/Runtime/SteamAudioManager.cs @@ -549,6 +549,9 @@ private void ScheduleInstance() EnsureSourceCapacity(CurrentArraySource); EnsureListenerCapacity(CurrentArrayListener); + // Keep job lifecycle simple: finish prior frame work before scheduling again. + combined.Complete(); + JobHandle sourcesHandle = default; JobHandle listenersHandle = default; @@ -569,11 +572,15 @@ private void ScheduleInstance() }; listenersHandle = job.Schedule(mListenerTransforms); } + combined = JobHandle.CombineDependencies(sourcesHandle, listenersHandle); } public JobHandle combined; private void ApplyInstance() { + // Always complete pose gather jobs even when later early-returns skip simulation work. + combined.Complete(); + if (mAudioEngineState == null) return; @@ -621,10 +628,6 @@ private void ApplyInstance() mSimulator.SetSharedInputs(SimulationFlags.Direct, sharedInputs); - - // Complete before calling into Steam Audio (main-thread plugin calls) - combined.Complete(); - // --- Direct inputs from cached pose arrays --- for (int i = 0; i < CurrentArraySource; i++) { @@ -1011,7 +1014,8 @@ private void EnsureSourceCapacity(int required) int newCap = (mSourceCapacity <= 0) ? 8 : mSourceCapacity * 2; if (newCap < required) newCap = required; - // Dispose old arrays if created + // Dispose old arrays if created. Ensure no gather jobs are still using them. + combined.Complete(); if (mSourceGathers.IsCreated) mSourceGathers.Dispose(); mSourceGathers = new NativeArray(newCap, Allocator.Persistent); @@ -1027,6 +1031,7 @@ private void EnsureListenerCapacity(int required) int newCap = (mListenerCapacity <= 0) ? 4 : mListenerCapacity * 2; if (newCap < required) newCap = required; + combined.Complete(); if (mListenerGathers.IsCreated) mListenerGathers.Dispose(); mListenerGathers = new NativeArray(newCap, Allocator.Persistent); @@ -1049,8 +1054,9 @@ private void DisposeTransformAndPoseBuffers() if (mSourceTransforms.isCreated) mSourceTransforms.Dispose(); if (mListenerTransforms.isCreated) mListenerTransforms.Dispose(); - if (mSourceGathers.IsCreated) mSourceGathers.Dispose(); + combined.Complete(); + if (mSourceGathers.IsCreated) mSourceGathers.Dispose(); if (mListenerGathers.IsCreated) mListenerGathers.Dispose(); mSourceCapacity = 0;