Skip to content

Commit 769375a

Browse files
authored
Merge pull request #1 from PlaytikaOSS/igor/add_profiler
Add Controllers Profiler
2 parents 0f32046 + 7c75093 commit 769375a

28 files changed

+1085
-7
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#if UNITY_CONTROLLERS_PROFILER
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Threading;
6+
using Unity.Collections;
7+
using UnityEngine.Profiling;
8+
9+
namespace Playtika.Controllers
10+
{
11+
public partial class ControllerBase
12+
{
13+
protected static int _totalCount;
14+
protected static int _activeCount;
15+
protected static int _createdThisFrameCount;
16+
17+
internal int _instanceId;
18+
internal long _startTimeMSec;
19+
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
20+
21+
internal List<IController> ChildControllers => _childControllers;
22+
internal string ScopeName => _controllerFactory?.ToString();
23+
24+
internal long GetLifetime() => _stopwatch.ElapsedMilliseconds;
25+
private HashSet<int> _savedNames = new HashSet<int>();
26+
27+
partial void ProfileOnCreated()
28+
{
29+
++_totalCount;
30+
++_activeCount;
31+
++_createdThisFrameCount;
32+
}
33+
34+
partial void ProfileOnStart()
35+
{
36+
_startTimeMSec = _stopwatch.ElapsedMilliseconds;
37+
PushCreateControllerToProfilerStream(Name, _startTimeMSec);
38+
_stopwatch.Restart();
39+
}
40+
41+
partial void ProfileOnStop()
42+
{
43+
--_activeCount;
44+
var elapsedMilliseconds = _stopwatch.ElapsedMilliseconds;
45+
PushStopControllerToProfilerStream(Name, elapsedMilliseconds);
46+
}
47+
48+
private void PushCreateControllerToProfilerStream(
49+
string name,
50+
long startTimeMSec)
51+
{
52+
if (!Profiler.enabled || string.IsNullOrEmpty(name))
53+
{
54+
return;
55+
}
56+
57+
var nameHash = name.GetHashCode();
58+
var data = new NativeArray<CreateControllerFrameData>(1, Allocator.Persistent);
59+
data[0] = new CreateControllerFrameData()
60+
{
61+
NameHash = nameHash,
62+
StartTimeMSec = startTimeMSec
63+
};
64+
65+
Profiler.EmitFrameMetaData(Helper.ControllerProfilerGuid, Helper.ControllerStartTag, data);
66+
data.Dispose();
67+
68+
if (_savedNames.Contains(nameHash))
69+
{
70+
return;
71+
}
72+
73+
var nameData = new NativeArray<NameSessionData>(1, Allocator.Persistent);
74+
nameData[0] = new NameSessionData()
75+
{
76+
NameHash = nameHash,
77+
Name = name
78+
};
79+
Profiler.EmitSessionMetaData(Helper.ControllerProfilerGuid, Helper.ControllerNameTag, nameData);
80+
nameData.Dispose();
81+
_savedNames.Add(nameHash);
82+
}
83+
84+
private static void PushStopControllerToProfilerStream(string name, long elapsedMilliseconds)
85+
{
86+
if (!Profiler.enabled || string.IsNullOrEmpty(name))
87+
{
88+
return;
89+
}
90+
91+
var data = new NativeArray<StopControllerFrameData>(1, Allocator.Persistent);
92+
data[0] = new StopControllerFrameData()
93+
{
94+
NameHash = name.GetHashCode(),
95+
ElapsedMilliseconds = elapsedMilliseconds
96+
};
97+
98+
Profiler.EmitFrameMetaData(Helper.ControllerProfilerGuid, Helper.ControllerStopTag, data);
99+
data.Dispose();
100+
}
101+
}
102+
}
103+
#endif

src/ControllersTree/Core/Controllers/ControllerBase.Profiler.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ControllersTree/Core/Controllers/ControllerBase.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ public abstract partial class ControllerBase : IController, IDisposable
2020

2121
CancellationToken IController.CancellationToken => _lifetimeToken;
2222

23+
partial void ProfileOnCreated();
24+
partial void ProfileOnStart();
25+
partial void ProfileOnStop();
26+
2327
protected ControllerBase(IControllerFactory controllerFactory)
2428
{
2529
_controllerFactory = controllerFactory;
@@ -58,6 +62,7 @@ void IController.Initialize(
5862
_lifetimeToken = _lifetimeTokenSource.Token;
5963
_lifetimeToken.ThrowIfCancellationRequested();
6064
_state = ControllerState.Initialized;
65+
ProfileOnCreated();
6166
break;
6267
}
6368
default:
@@ -72,8 +77,8 @@ void IController.Start()
7277
case ControllerState.Initialized:
7378
{
7479
_state = ControllerState.Running;
75-
7680
OnStart();
81+
ProfileOnStart();
7782
break;
7883
}
7984
default:
@@ -101,6 +106,7 @@ void IController.Stop()
101106
case ControllerState.Initialized:
102107
case ControllerState.Running:
103108
StopChildrenAndSelf();
109+
ProfileOnStop();
104110
break;
105111
case ControllerState.Stopped:
106112
case ControllerState.Disposed:
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#if UNITY_CONTROLLERS_PROFILER
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Threading;
5+
using Cysharp.Threading.Tasks;
6+
using Unity.Collections;
7+
using Unity.Profiling;
8+
using UnityEngine.Profiling;
9+
10+
namespace Playtika.Controllers
11+
{
12+
public partial class RootController
13+
{
14+
private static readonly ProfilerCategory ControllerProfilerCategory = ProfilerCategory.Scripts;
15+
16+
private static readonly ProfilerCounterValue<int> TotalControllersCount =
17+
new ProfilerCounterValue<int>(
18+
ControllerProfilerCategory, Helper.TotalControllersCount,
19+
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);
20+
21+
private static readonly ProfilerCounterValue<int> ActiveControllersCount =
22+
new ProfilerCounterValue<int>(
23+
ControllerProfilerCategory, Helper.ActiveControllersCount,
24+
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);
25+
26+
private static readonly ProfilerCounterValue<int> CreateThisFrameControllersCount =
27+
new ProfilerCounterValue<int>(
28+
ControllerProfilerCategory, Helper.CreateThisFrameControllersCount,
29+
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);
30+
private HashSet<int> _savedNames = new HashSet<int>();
31+
32+
partial void ProfileOnStart()
33+
{
34+
MadeSnapshot(CancellationToken).Forget();
35+
}
36+
37+
private async UniTaskVoid MadeSnapshot(CancellationToken cancellationToken)
38+
{
39+
while (!cancellationToken.IsCancellationRequested)
40+
{
41+
await UniTask.Yield(PlayerLoopTiming.LastUpdate);
42+
PushToProfilerStream();
43+
}
44+
}
45+
46+
private void PushToProfilerStream()
47+
{
48+
if (!Profiler.enabled)
49+
{
50+
return;
51+
}
52+
53+
TotalControllersCount.Value = _totalCount;
54+
ActiveControllersCount.Value = _activeCount;
55+
CreateThisFrameControllersCount.Value = _createdThisFrameCount;
56+
_createdThisFrameCount = 0;
57+
58+
var count = _activeCount;
59+
var frameData = new NativeArray<RunControllerFrameData>(count, Allocator.Persistent);
60+
var frameDataIndex = 0;
61+
ProfilerDumpTree((ControllerBase) this, frameData, ref frameDataIndex, 0);
62+
Profiler.EmitFrameMetaData(Helper.ControllerProfilerGuid, Helper.ControllerRunTag, frameData);
63+
frameData.Dispose();
64+
}
65+
66+
private void ProfilerDumpTree(
67+
ControllerBase controllerBase,
68+
NativeArray<RunControllerFrameData> frameData,
69+
ref int frameDataIndex,
70+
int level)
71+
{
72+
var scopeName = controllerBase.ScopeName;
73+
frameData[frameDataIndex] = new RunControllerFrameData()
74+
{
75+
NameHash = controllerBase.Name.GetHashCode(),
76+
ScopeHash = scopeName.GetHashCode(),
77+
InstanceId = controllerBase._instanceId,
78+
Level = level,
79+
StartTimeMSec = controllerBase._startTimeMSec,
80+
ElapsedMilliseconds = controllerBase.GetLifetime(),
81+
};
82+
83+
++frameDataIndex;
84+
85+
var nameHash = scopeName.GetHashCode();
86+
if (!_savedNames.Contains(nameHash))
87+
{
88+
PushScopeNameToStream(scopeName, nameHash);
89+
_savedNames.Add(nameHash);
90+
}
91+
92+
foreach (var child in controllerBase.ChildControllers)
93+
{
94+
ProfilerDumpTree((ControllerBase) child, frameData, ref frameDataIndex, level + 2);
95+
}
96+
}
97+
98+
private static void PushScopeNameToStream(string name, int nameHash)
99+
{
100+
var nameData = new NativeArray<NameSessionData>(1, Allocator.Persistent);
101+
nameData[0] = new NameSessionData()
102+
{
103+
NameHash = nameHash,
104+
Name = name
105+
};
106+
Profiler.EmitSessionMetaData(Helper.ControllerProfilerGuid, Helper.ScopeNameTag, nameData);
107+
nameData.Dispose();
108+
}
109+
}
110+
}
111+
#endif

src/ControllersTree/Core/Controllers/RootController.Profiler.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ControllersTree/Core/Controllers/RootController.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44

55
namespace Playtika.Controllers
66
{
7-
public abstract class RootController : ControllerBase
7+
public abstract partial class RootController : ControllerBase
88
{
9-
protected RootController(IControllerFactory controllerFactory) : base(controllerFactory)
9+
protected RootController(IControllerFactory controllerFactory)
10+
: base(controllerFactory)
1011
{
1112
}
1213

14+
partial void ProfileOnStart();
15+
1316
/// <summary>
1417
/// Launches the execution of the controller tree.
1518
/// This method initializes the root controller with the provided cancellation token, starts it, and registers a callback to stop the root controller when the cancellation token is cancelled.
@@ -31,6 +34,7 @@ private void StopRootController()
3134
protected override void OnStart()
3235
{
3336
SetRootController(this);
37+
ProfileOnStart();
3438
}
3539

3640
protected override void OnStop()
@@ -55,4 +59,4 @@ private static void SetRootController(ControllerBase controller)
5559
public static ControllerBase Instance { get; private set; }
5660
#endif
5761
}
58-
}
62+
}

src/ControllersTree/Core/Playtika.Controllers.asmdef

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
"name": "Playtika.Controllers",
33
"rootNamespace": "",
44
"references": [
5-
"UniTask"
5+
"UniTask",
6+
"Unity.Profiling.Core"
67
],
78
"includePlatforms": [],
89
"excludePlatforms": [],
9-
"allowUnsafeCode": false,
10+
"allowUnsafeCode": true,
1011
"overrideReferences": false,
1112
"precompiledReferences": [],
1213
"autoReferenced": true,

src/ControllersTree/Core/Profiler.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#if UNITY_CONTROLLERS_PROFILER
2+
using System;
3+
using System.Runtime.InteropServices;
4+
5+
namespace Playtika.Controllers
6+
{
7+
[StructLayout(LayoutKind.Sequential)]
8+
public struct FixedString
9+
{
10+
public unsafe fixed byte Data[128];
11+
12+
public override unsafe string ToString()
13+
{
14+
fixed (byte* dataPtr = Data)
15+
{
16+
return GetStringFromFixedBytes(dataPtr);
17+
}
18+
}
19+
20+
private unsafe void Set(string value)
21+
{
22+
fixed (byte* dataPtr = Data)
23+
{
24+
CopyToFixedBytes(value, dataPtr, 128);
25+
}
26+
}
27+
28+
private static unsafe void CopyToFixedBytes(string source, byte* destination, int maxLength)
29+
{
30+
if (source == null)
31+
{
32+
destination[0] = 0;
33+
return;
34+
}
35+
36+
var bytes = System.Text.Encoding.UTF8.GetBytes(source);
37+
var bytesToCopy = Math.Min(bytes.Length, maxLength - 1);
38+
39+
for (int i = 0; i < bytesToCopy; i++)
40+
{
41+
destination[i] = bytes[i];
42+
}
43+
44+
destination[bytesToCopy] = 0;
45+
}
46+
47+
private static unsafe string GetStringFromFixedBytes(byte* source)
48+
{
49+
var byteLength = 0;
50+
while (source[byteLength] != 0)
51+
{
52+
byteLength++;
53+
}
54+
55+
return System.Text.Encoding.UTF8.GetString(source, byteLength);
56+
}
57+
58+
public static implicit operator FixedString(string value)
59+
{
60+
var result = new FixedString();
61+
result.Set(value);
62+
return result;
63+
}
64+
65+
public static implicit operator string(FixedString value)
66+
{
67+
return value.ToString();
68+
}
69+
}
70+
}
71+
#endif

src/ControllersTree/Core/Profiler/FixedString.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)