Skip to content

Commit

Permalink
some experiments with observable tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniil Korostelev committed Jun 19, 2024
1 parent 2de4b52 commit e14fa62
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 2 deletions.
15 changes: 15 additions & 0 deletions TinkStateSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinkState-Unity-Test", "pro
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinkState-Godot", "projects\TinkState-Godot\TinkState-Godot.csproj", "{CD1EE987-C509-4BC5-B996-F7EAAF4BA868}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinkState-Unity-Editor", "projects\TinkState-Unity-Editor\TinkState-Unity-Editor.csproj", "{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}"
EndProject
Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -102,12 +104,25 @@ Global
{CD1EE987-C509-4BC5-B996-F7EAAF4BA868}.Release|x64.Build.0 = Release|Any CPU
{CD1EE987-C509-4BC5-B996-F7EAAF4BA868}.Release|x86.ActiveCfg = Release|Any CPU
{CD1EE987-C509-4BC5-B996-F7EAAF4BA868}.Release|x86.Build.0 = Release|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Debug|x64.ActiveCfg = Debug|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Debug|x64.Build.0 = Debug|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Debug|x86.ActiveCfg = Debug|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Debug|x86.Build.0 = Debug|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Release|Any CPU.Build.0 = Release|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Release|x64.ActiveCfg = Release|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Release|x64.Build.0 = Release|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Release|x86.ActiveCfg = Release|Any CPU
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{6B383783-3B6F-4685-A506-1E502392E83B} = {C6990338-31DB-4CD2-BC63-DA0C25A8F043}
{DA5CD7B7-76F9-4C6D-875D-36219E9D1086} = {C6990338-31DB-4CD2-BC63-DA0C25A8F043}
{FBE6AC15-3025-45FA-A8EA-FBFD05A7B25F} = {C6990338-31DB-4CD2-BC63-DA0C25A8F043}
{7CC48BB6-A797-449D-B681-44A364271C97} = {C6990338-31DB-4CD2-BC63-DA0C25A8F043}
{CD1EE987-C509-4BC5-B996-F7EAAF4BA868} = {C6990338-31DB-4CD2-BC63-DA0C25A8F043}
{A2B35BAB-77D6-4DC7-8DF8-6E7DD9F9FAA4} = {C6990338-31DB-4CD2-BC63-DA0C25A8F043}
EndGlobalSection
EndGlobal
29 changes: 29 additions & 0 deletions projects/TinkState-Unity-Editor/TinkState-Unity-Editor.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<AssemblyName>Nadako.TinkState.Unity.Editor</AssemblyName>
</PropertyGroup>

<PropertyGroup>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>

<ItemGroup>
<Compile Include="../../src/TinkState-Unity/Editor/**/*.cs"/>
</ItemGroup>

<ItemGroup>
<Reference Include="UnityEngine">
<HintPath>../../unity-dlls/UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEditor">
<HintPath>../../unity-dlls/UnityEditor.dll</HintPath>
</Reference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../TinkState/TinkState.csproj"/>
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions src/TinkState-Unity/Editor.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Nadako.TinkState.Unity.Editor",
"references": [
"Nadako.TinkState"
]
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 82 additions & 0 deletions src/TinkState-Unity/Editor/ObservableTrackerWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using TinkState.Internal;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace TinkState.Editor
{
public class ObservableTrackerWindow : EditorWindow
{
readonly List<ObservableTracker.TrackingData> data = new();

ListView listView;

[MenuItem("Window/Observable Tracker")]
public static void Open()
{
var window = GetWindow<ObservableTrackerWindow>();
window.titleContent = new GUIContent("Observable Tracker");
}

void OnEnable()
{
ObservableTracker.EnableTracking = true;
}

void OnDisable()
{
ObservableTracker.EnableTracking = false;
}

public void CreateGUI()
{
const string uxmlGUID = "bc0d80c10c9ac4d5ab4a26571a0e49e3";
var treeAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(AssetDatabase.GUIDToAssetPath(uxmlGUID));
var tree = treeAsset.Instantiate();
tree.style.flexGrow = 1;
rootVisualElement.Add(tree);

var stackTraceView = tree.Q<Label>("stack-trace");

listView = tree.Q<ListView>("observable-list");
listView.itemsSource = data;
listView.selectionType = SelectionType.Single;
listView.makeItem = () => new Label();
listView.bindItem = (element, index) =>
{
var label = (Label)element;
label.text = data[index].Type;
};
listView.onSelectedIndicesChange += indices =>
{
foreach (var index in indices)
{
var trackingData = data[index];
Debug.Log("Choosing " + index);
stackTraceView.text = trackingData.Stack;
return;
}

stackTraceView.text = "";
};
}

private void Update()
{
if (ObservableTracker.CheckDirty())
{
Debug.Log("Updating");
data.Clear();
ObservableTracker.IterateEntries((observer, trackingData) =>
{
Debug.Log("Adding: " + trackingData.Type);
data.Add(trackingData);
});
listView.Rebuild();
}
}
}
}
11 changes: 11 additions & 0 deletions src/TinkState-Unity/Editor/ObservableTrackerWindow.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/TinkState-Unity/Editor/ObservableTrackerWindow.uxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
<ui:TwoPaneSplitView orientation="Vertical">
<ui:ListView focusable="true" name="observable-list" />
<ui:Label name="stack-trace" />
</ui:TwoPaneSplitView>
</ui:UXML>
10 changes: 10 additions & 0 deletions src/TinkState-Unity/Editor/ObservableTrackerWindow.uxml.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions src/TinkState/Runtime/Internal/Dispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ bool AddObserver(Observer observer)
{
if (!observers.Contains(observer))
{
ObservableTracker.TrackSubscription(this, observer);
observers.Add(observer);
return true;
}
Expand All @@ -102,7 +103,13 @@ bool AddObserver(Observer observer)

bool RemoveObserver(Observer observer)
{
return observers.Remove(observer); // we should probably throw on false
if (observers.Remove(observer))
{
ObservableTracker.UntrackSubscription(this, observer);
return true;
}

return false; // we should probably throw on false
}

protected void Dispose()
Expand All @@ -111,4 +118,4 @@ protected void Dispose()
modifications = null;
}
}
}
}
89 changes: 89 additions & 0 deletions src/TinkState/Runtime/Internal/ObservableTracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Nadako.TinkState.Unity.Editor")]

namespace TinkState.Internal
{
static class ObservableTracker
{
public static bool EnableTracking = false;

static bool isDirty;

internal readonly struct TrackingData
{
public readonly string Type;
public readonly string Stack;

public TrackingData(string type, string stack)
{
Type = type;
Stack = stack;
}
}

static readonly Dictionary<WeakReference<Observer>, TrackingData> tracking = new();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void TrackSubscription(Dispatcher dispatcher, Observer observer)
{
if (EnableTracking) DoTrackSubscription(dispatcher, observer);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UntrackSubscription(Dispatcher dispatcher, Observer observer)
{
if (EnableTracking) DoUntrackSubscription(dispatcher, observer);
}

public static bool CheckDirty()
{
var wasDirty = isDirty;
isDirty = false;
return wasDirty;
}

public static void IterateEntries(Action<Observer, TrackingData> callback)
{
foreach (var (weakRef, trackingData) in tracking)
{
if (weakRef.TryGetTarget(out var observer))
{
callback(observer, trackingData);
}
}
}

static void DoTrackSubscription(Dispatcher dispatcher, Observer observer)
{
var weakRef = new WeakReference<Observer>(observer);
tracking[weakRef] = new TrackingData(
observer.GetType().ToString(),
new StackTrace(2, true).ToString()
);
isDirty = true;
}

static void DoUntrackSubscription(Dispatcher dispatcher, Observer observer)
{
WeakReference<Observer> weakRef = null;
foreach (var entry in tracking.Keys)
{
if (entry.TryGetTarget(out var target) && target == observer)
{
weakRef = entry;
break;
}
}

if (weakRef != null)
{
tracking.Remove(weakRef);
isDirty = true;
}
}
}
}
3 changes: 3 additions & 0 deletions src/TinkState/Runtime/Internal/ObservableTracker.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added unity-dlls/UnityEditor.dll
Binary file not shown.

0 comments on commit e14fa62

Please sign in to comment.