From 3bd627379cb5956a1605a551cc3cddd7f1a02fd6 Mon Sep 17 00:00:00 2001 From: bd_ Date: Sat, 19 Oct 2024 18:26:57 -0700 Subject: [PATCH] fix: preview system fails to recover from the loss of the primary proxy object --- .../Rendering/ProxyObjectCache.cs | 38 +++++++++++++++---- .../Rendering/ProxyObjectController.cs | 10 ++++- .../PreviewSystem/Rendering/ProxyPipeline.cs | 6 +++ Runtime/ProxyTagComponent.cs | 20 ++++++++++ Runtime/ProxyTagComponent.cs.meta | 3 ++ 5 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 Runtime/ProxyTagComponent.cs create mode 100644 Runtime/ProxyTagComponent.cs.meta diff --git a/Editor/PreviewSystem/Rendering/ProxyObjectCache.cs b/Editor/PreviewSystem/Rendering/ProxyObjectCache.cs index 9317eea..929dbad 100644 --- a/Editor/PreviewSystem/Rendering/ProxyObjectCache.cs +++ b/Editor/PreviewSystem/Rendering/ProxyObjectCache.cs @@ -31,6 +31,7 @@ private class ProxyHandleImpl : IProxyHandle private readonly Renderer _key; private readonly Func _createFunc; private readonly RendererState _state; + private bool _disposed; public ProxyHandleImpl(ProxyObjectCache cache, Renderer key, Func createFunc, RendererState state) { @@ -70,6 +71,14 @@ public void ReturnSetupProxy(Renderer proxy) public void Dispose() { + if (_disposed) + { + Debug.LogWarning("Proxy handle was disposed twice!"); + throw new ObjectDisposedException(nameof(ProxyHandleImpl)); + } + + _disposed = true; + if (--_state.ActivePrimaryCount == 0) { _cache.MaybeDisposeProxy(_key); @@ -118,23 +127,32 @@ private void OnPlayModeStateChanged(PlayModeStateChange obj) public IProxyHandle GetHandle(Renderer original, Func create) { IsRegistered = true; + + Func createShimmed = () => + { + var newProxy = create(); + newProxy.gameObject.AddComponent(); + _proxyObjectInstanceIds.Add(newProxy.gameObject.GetInstanceID()); + + return newProxy; + }; if (!_renderers.TryGetValue(original, out var state)) { state = new RendererState(); - state.PrimaryProxy = create(); + state.PrimaryProxy = createShimmed(); _renderers.Add(original, state); } - state.ActivePrimaryCount++; - - return new ProxyHandleImpl(this, original, () => + if (state.PrimaryProxy == null) { - var newProxy = create(); - _proxyObjectInstanceIds.Add(newProxy.gameObject.GetInstanceID()); + // Recover from loss of the primary proxy + state.PrimaryProxy = createShimmed(); + } - return newProxy; - }, state); + state.ActivePrimaryCount++; + + return new ProxyHandleImpl(this, original, createShimmed, state); } private static void DestroyProxy(Renderer proxy) @@ -142,6 +160,10 @@ private static void DestroyProxy(Renderer proxy) if (proxy == null) return; var gameObject = proxy.gameObject; + if (gameObject.TryGetComponent(out var tag)) + { + tag.Armed = false; + } _proxyObjectInstanceIds.Remove(gameObject.GetInstanceID()); Object.DestroyImmediate(gameObject); } diff --git a/Editor/PreviewSystem/Rendering/ProxyObjectController.cs b/Editor/PreviewSystem/Rendering/ProxyObjectController.cs index 0bb02a1..2bfc20c 100644 --- a/Editor/PreviewSystem/Rendering/ProxyObjectController.cs +++ b/Editor/PreviewSystem/Rendering/ProxyObjectController.cs @@ -1,11 +1,13 @@ #region using System; +using System.Diagnostics; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.SceneManagement; +using Debug = UnityEngine.Debug; using Object = UnityEngine.Object; #endregion @@ -33,6 +35,8 @@ internal class ProxyObjectController : IDisposable private bool _visibilityOffOriginal; private bool _pickingOffOriginal, _pickingOffReplacement; private long _lastVisibilityCheck = long.MinValue; + + private readonly Stopwatch _lastWarning = new(); private static CustomSampler _onPreFrameSampler = CustomSampler.Create("ProxyObjectController.OnPreFrame"); @@ -137,7 +141,11 @@ internal bool OnPreFrame() { if (Renderer == null) { - Debug.LogWarning("Proxy object was destroyed improperly! Resetting pipeline..."); + if (!_lastWarning.IsRunning || _lastWarning.ElapsedMilliseconds > 1000) + { + Debug.LogWarning("Proxy object was destroyed improperly! Resetting pipeline..."); + _lastWarning.Restart(); + } } return false; } diff --git a/Editor/PreviewSystem/Rendering/ProxyPipeline.cs b/Editor/PreviewSystem/Rendering/ProxyPipeline.cs index ddcbca3..46b3680 100644 --- a/Editor/PreviewSystem/Rendering/ProxyPipeline.cs +++ b/Editor/PreviewSystem/Rendering/ProxyPipeline.cs @@ -303,6 +303,12 @@ await Task.WhenAll(_stages.SelectMany(s => s.NodeTasks)) { proxy.FinishSetup(); + if (proxy.Renderer == null) + { + Invalidate(); + continue; + } + OriginalToProxyRenderer = OriginalToProxyRenderer.Add(r, proxy.Renderer); OriginalToProxyObject = OriginalToProxyObject.Add(r.gameObject, proxy.Renderer.gameObject); ProxyToOriginalObject = ProxyToOriginalObject.Add(proxy.Renderer.gameObject, r.gameObject); diff --git a/Runtime/ProxyTagComponent.cs b/Runtime/ProxyTagComponent.cs new file mode 100644 index 0000000..0152c8d --- /dev/null +++ b/Runtime/ProxyTagComponent.cs @@ -0,0 +1,20 @@ +using UnityEngine; + +namespace nadena.dev.ndmf.preview +{ + [AddComponentMenu("/")] + internal class ProxyTagComponent : MonoBehaviour + { + internal bool Armed = true; + + private void OnDestroy() + { +#if NDMF_DEBUG + if (Armed) + { + Debug.LogWarning("Proxy object was destroyed improperly here!"); + } +#endif + } + } +} \ No newline at end of file diff --git a/Runtime/ProxyTagComponent.cs.meta b/Runtime/ProxyTagComponent.cs.meta new file mode 100644 index 0000000..2df8b54 --- /dev/null +++ b/Runtime/ProxyTagComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5fcc33a40bec42a4a7c117b74e5b061f +timeCreated: 1729374758 \ No newline at end of file