Skip to content

Commit

Permalink
feat: add a debugging trace feature for the preview system
Browse files Browse the repository at this point in the history
  • Loading branch information
bdunderscore committed Sep 29, 2024
1 parent f0a6980 commit 38a5f04
Show file tree
Hide file tree
Showing 25 changed files with 919 additions and 168 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- [#424] Added tracing system for the preview/invalidation system

### Fixed

Expand Down
9 changes: 9 additions & 0 deletions Editor/ChangeStream/ChangeStreamMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using nadena.dev.ndmf.preview;
using nadena.dev.ndmf.preview.trace;
using UnityEditor;
using UnityEngine.Profiling;
using Debug = UnityEngine.Debug;
Expand Down Expand Up @@ -53,6 +54,14 @@ private static void OnChange(ref ObjectChangeEventStream stream)

private static void HandleEvent(ObjectChangeEventStream stream, int i)
{
var trace = TraceBuffer.RecordTraceEvent(
"ChangeStreamMonitor.HandleEvent",
(ev) => $"Handling event {ev.Arg0}",
stream.GetEventType(i),
level: TraceEventLevel.Trace
);

using (trace.Scope())
switch (stream.GetEventType(i))
{
case ObjectChangeKind.None: break;
Expand Down
36 changes: 32 additions & 4 deletions Editor/ChangeStream/ListenerSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using nadena.dev.ndmf.preview;
using nadena.dev.ndmf.preview.trace;
using UnityEngine;

#endregion
Expand Down Expand Up @@ -53,13 +54,33 @@ public bool TryFire(T ev)
{
if (_targetRef.TryGetTarget(out var target))
{
if (TargetIsExpended(target)) return true;
if (TargetIsExpended(target))
{
TraceBuffer.RecordTraceEvent(
eventType: "ListenerSet.Expired",
formatEvent: e => $"Listener for {e.Arg0} expired",
arg0: target
);
return true;
}

try
{
if (!_filter(ev)) return false;

_receiver(target);
if (!_filter(ev))
{
return false;
}

var tev = TraceBuffer.RecordTraceEvent(
eventType: "ListenerSet.Fire",
formatEvent: e => $"Listener for {e.Arg0} fired with {e.Arg1}",
arg0: target,
arg1: ev
);
using (tev.Scope())
{
_receiver(target);
}

RepaintTrigger.RequestRepaint();
}
Expand All @@ -71,6 +92,13 @@ public bool TryFire(T ev)

return true;
}
else
{
TraceBuffer.RecordTraceEvent(
eventType: "ListenerSet.GC",
formatEvent: e => "Listener expired"
);
}

return true;
}
Expand Down
34 changes: 31 additions & 3 deletions Editor/ChangeStream/ObjectWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using JetBrains.Annotations;
using nadena.dev.ndmf.preview;
using nadena.dev.ndmf.preview.trace;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
Expand Down Expand Up @@ -156,7 +158,7 @@ private void BindCancel(ComputeContext ctx, IDisposable cancel)
}

public R MonitorObjectProps<T, R>(T obj, ComputeContext ctx, Func<T, R> extract, Func<R, R, bool> compare,
bool usePropMonitor)
bool usePropMonitor, [CanBeNull] string file, int? line)
where T : UnityObject
{
var curVal = extract(obj);
Expand All @@ -172,7 +174,19 @@ public R MonitorObjectProps<T, R>(T obj, ComputeContext ctx, Func<T, R> extract,
{
case HierarchyEvent.ObjectDirty:
case HierarchyEvent.ForceInvalidate:
return obj == null || !compare(curVal, extract(obj));
if (obj != null && compare(curVal, extract(obj)))
{
TraceBuffer.RecordTraceEvent(
"ObjectWatcher.MonitorObjectProps",
ev => $"[{ev.FilePath}:{ev.Line}] Object {ev.Arg0} unchanged",
go.name,
filename: file ?? "???",
line: line ?? 0
);
return false;
}
return true;
default:
return false;
}
Expand All @@ -182,13 +196,27 @@ public R MonitorObjectProps<T, R>(T obj, ComputeContext ctx, Func<T, R> extract,
}
else
{
object objName = (obj is Component c_) ? c_.gameObject.name : obj;

var cancel = Hierarchy.RegisterObjectListener(obj, e =>
{
switch (e)
{
case HierarchyEvent.ObjectDirty:
case HierarchyEvent.ForceInvalidate:
return obj == null || !compare(curVal, extract(obj));
if (obj != null && compare(curVal, extract(obj)))
{
TraceBuffer.RecordTraceEvent(
"ObjectWatcher.MonitorObjectProps",
ev => $"[{ev.FilePath}:{ev.Line}] Object {ev.Arg0} unchanged",
objName,
filename: file ?? "???",
line: line ?? 0
);
return false;
}
return true;
default:
return false;
}
Expand Down
43 changes: 34 additions & 9 deletions Editor/ChangeStream/PropertyMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,42 @@ internal class PropertyMonitor
private Task _activeRefreshTask = Task.CompletedTask;
private Task _pendingRefreshTask = Task.CompletedTask;

internal void MaybeStartRefreshTimer()
private bool _isEnabled;
internal bool IsEnabled
{
using (var scope = NDMFSyncContext.Scope())
get => _isEnabled;
set
{
_activeRefreshTask = Task.Factory.StartNew(
CheckAllObjectsLoop,
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
);
if (_isEnabled == value) return;

_isEnabled = value;

if (_isEnabled)
{
using (var scope = NDMFSyncContext.Scope())
{

var curRefreshTask = _activeRefreshTask;
_activeRefreshTask = Task.Factory.StartNew(
async () =>
{
await curRefreshTask;
await CheckAllObjectsLoop();
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
).Unwrap();
}
}
}
}

internal void MaybeStartRefreshTimer()
{
IsEnabled = true;
}

public enum PropertyMonitorEvent
{
PropsUpdated
Expand Down Expand Up @@ -62,7 +85,7 @@ public ListenerSet<PropertyMonitorEvent> MonitorObjectProps(Object obj)

private async Task CheckAllObjectsLoop()
{
while (true)
while (_isEnabled)
{
await CheckAllObjects();
await NextFrame();
Expand All @@ -81,6 +104,8 @@ public async Task CheckAllObjects()

foreach (var pair in _registeredObjects.ToList())
{
if (!_isEnabled) break;

var (instanceId, reg) = pair;

// Wake up all listeners to see if their monitored value has changed
Expand Down
60 changes: 45 additions & 15 deletions Editor/PreviewSystem/ComputeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using nadena.dev.ndmf.cs;
using nadena.dev.ndmf.preview.trace;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools.Constraints;

#endregion

Expand Down Expand Up @@ -59,23 +61,39 @@ public static void FlushInvalidates()
ctx._onInvalidateListeners.FireAll();
ctx._onInvalidateTask.TrySetResult(true);
}

lock (_pendingInvalidatesLock)
{
if (_pendingInvalidates.Count > 0)
{
list = _pendingInvalidates;
_pendingInvalidates = new();
}
else
{
list.Clear();
}
}
}
}

static ComputeContext()
{
NullContext = new ComputeContext("null", null);
}

#if NDMF_TRACE


~ComputeContext()
{
if (!IsInvalidated)
Debug.LogError("ComputeContext " + Description + " was GCed without being invalidated!");
{
TraceBuffer.RecordTraceEvent(
"ComputeContext.Leak",
(ev) => "Leaked context: " + ((ComputeContext)ev.Arg0).Description,
arg0: this
);
}
}

#endif

internal string Description { get; }

Expand All @@ -102,14 +120,29 @@ internal Task OnInvalidate
public bool IsInvalidated => _invalidatePending || _invalidater.Task.IsCompleted;
private bool _invalidatePending;

private long? _invalidateTriggerEvent;

public ComputeContext(string description)
{
if (string.IsNullOrEmpty(description))
{
Debug.LogWarning("ComputeContext created with empty description");
}

Invalidate = () =>
{
#if NDMF_TRACE
Debug.Log("Invalidating " + Description);
#endif
if (_invalidatePending || IsInvalidated) return;
_invalidateTriggerEvent = TraceBuffer.RecordTraceEvent(
"ComputeContext.Invalidate",
(ev) => "Invalidate: " + ev.Arg0,
arg0: this
).EventId;
TaskUtil.OnMainThread(this, DoInvalidate);
};
Description = description;
Expand All @@ -128,11 +161,6 @@ private static void DoInvalidate(ComputeContext ctx)
}
}

private static void InvalidateInternal(ComputeContext ctx)
{
ctx._invalidater.TrySetResult(null);
}

private ComputeContext(string description, object nullToken)
{
Invalidate = () => { };
Expand All @@ -148,11 +176,13 @@ private ComputeContext(string description, object nullToken)
public void Invalidates(ComputeContext other)
{
if (other.IsInvalidated) return;

_invalidater.Task.ContinueWith(_ =>
{
InvalidateInternal(other);
});

InvokeOnInvalidate(other, ForwardInvalidation);
}

private void ForwardInvalidation(ComputeContext obj)
{
obj.Invalidate();
}

/// <summary>
Expand Down
Loading

0 comments on commit 38a5f04

Please sign in to comment.