mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-10 20:42:21 +02:00
Moved BeefPerf
This commit is contained in:
parent
d28392f8fd
commit
b3487d733b
26 changed files with 6 additions and 6458 deletions
42
BeefTools/BeefPerf/BeefProj.toml
Normal file
42
BeefTools/BeefPerf/BeefProj.toml
Normal file
|
@ -0,0 +1,42 @@
|
|||
FileVersion = 1
|
||||
Dependencies = {corlib = "*", Beefy2D = "*"}
|
||||
|
||||
[Project]
|
||||
Name = "BeefPerf"
|
||||
TargetType = "BeefWindowsApplication"
|
||||
StartupObject = "BeefPerf.Program"
|
||||
|
||||
[Platform.Windows]
|
||||
IconFile = "res/BeefPerf.ico"
|
||||
ManifestFile = "res/BeefPerf.manifest"
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
TargetDirectory = "$(WorkspaceDir)/../../IDE/dist"
|
||||
TargetName = "$(ProjectName)_d"
|
||||
OtherLinkFlags = "$(LinkFlags) BeefySysLib64_d.lib"
|
||||
BeefLibType = "DynamicDebug"
|
||||
|
||||
[Configs.Debug.Win32]
|
||||
CLibType = "Static"
|
||||
BeefLibType = "Static"
|
||||
|
||||
[Configs.Paranoid.Win32]
|
||||
CLibType = "Static"
|
||||
BeefLibType = "Static"
|
||||
|
||||
[Configs.Paranoid.Win64]
|
||||
CLibType = "Static"
|
||||
BeefLibType = "Static"
|
||||
|
||||
[Configs.Test.Win32]
|
||||
CLibType = "Static"
|
||||
BeefLibType = "Static"
|
||||
|
||||
[Configs.Test.Win64]
|
||||
CLibType = "Static"
|
||||
BeefLibType = "Static"
|
||||
|
||||
[Configs.Release.Win64]
|
||||
TargetDirectory = "$(WorkspaceDir)/../../IDE/dist"
|
||||
OtherLinkFlags = "$(LinkFlags) BeefySysLib64.lib"
|
||||
BeefLibType = "Dynamic"
|
6
BeefTools/BeefPerf/BeefSpace.toml
Normal file
6
BeefTools/BeefPerf/BeefSpace.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
FileVersion = 1
|
||||
Projects = {BeefPerf = {Path = "."}, Beefy2D = {Path = "../BeefLibs/Beefy2D"}}
|
||||
Unlocked = ["corlib"]
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "BeefPerf"
|
BIN
BeefTools/BeefPerf/res/BeefPerf.ico
Normal file
BIN
BeefTools/BeefPerf/res/BeefPerf.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 133 KiB |
10
BeefTools/BeefPerf/res/BeefPerf.manifest
Normal file
10
BeefTools/BeefPerf/res/BeefPerf.manifest
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level='asInvoker' uiAccess='false' />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
998
BeefTools/BeefPerf/src/BPApp.bf
Normal file
998
BeefTools/BeefPerf/src/BPApp.bf
Normal file
|
@ -0,0 +1,998 @@
|
|||
using System;
|
||||
using Beefy;
|
||||
using Beefy.widgets;
|
||||
using Beefy.gfx;
|
||||
using Beefy.theme.dark;
|
||||
using Beefy.theme;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Beefy.events;
|
||||
using Beefy.sys;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class BPApp : BFApp
|
||||
{
|
||||
public new static BPApp sApp;
|
||||
public float mTimePerFrame = 1.0f / 60;
|
||||
public bool mWantsFullscreen = false;
|
||||
public int32 mListenPort = 4208;
|
||||
//public int32 mListenPort = 135;
|
||||
|
||||
WidgetWindow mMainWindow;
|
||||
Widget mRootWidget;
|
||||
|
||||
public Widget mMainWidget;
|
||||
public List<String> mLogLines = new List<String>() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
//public PopupMenuManager mPopupMenuManager;
|
||||
//public Trainer mTrainer ~ delete _;
|
||||
|
||||
public ScriptManager mScriptManager = new ScriptManager() ~ delete _;
|
||||
public DarkDockingFrame mDockingFrame;
|
||||
public MainFrame mMainFrame;
|
||||
|
||||
public WorkspacePanel mWorkspacePanel;
|
||||
public Board mBoard;
|
||||
public ProfilePanel mProfilePanel;
|
||||
public FindPanel mFindPanel;
|
||||
public Windows.ProcessInformation mProcessInformation;
|
||||
|
||||
public Socket mListenSocket ~ delete _;
|
||||
public Thread mSocketThread ~ delete _;
|
||||
public List<BpClient> mClients = new List<BpClient>() ~ delete _;
|
||||
public List<BpSession> mSessions = new List<BpSession> ~ DeleteContainerAndItems!(_);
|
||||
public Monitor mClientMonitor = new Monitor() ~ delete _;
|
||||
public WaitEvent mShutdownEvent = new WaitEvent() ~ delete _;
|
||||
public BpSession mCurSession;
|
||||
bool mEnableGCCollect = true;
|
||||
|
||||
public bool mIsAutoOpened;
|
||||
public int32 mStatBytesReceived;
|
||||
public int32 mStatReportTick;
|
||||
public int32 mStatBytesPerSec;
|
||||
public bool mFailed;
|
||||
public int32 mFailIdx;
|
||||
|
||||
public bool Listening
|
||||
{
|
||||
get
|
||||
{
|
||||
return mListenSocket.IsOpen;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
if (!mListenSocket.IsOpen)
|
||||
{
|
||||
if (mListenSocket.Listen(mListenPort) case .Err)
|
||||
{
|
||||
Fail(scope String()..AppendF("Failed to listen on port {0}", mListenPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mListenSocket.IsOpen)
|
||||
{
|
||||
mListenSocket.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
sApp = this;
|
||||
gApp = this;
|
||||
|
||||
//RefreshRate = 120;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if (mSocketThread != null)
|
||||
mSocketThread.Join();
|
||||
|
||||
for (var client in mClients)
|
||||
{
|
||||
if (!client.IsSessionInitialized)
|
||||
delete client;
|
||||
}
|
||||
|
||||
Widget.RemoveAndDelete(mWorkspacePanel);
|
||||
Widget.RemoveAndDelete(mBoard);
|
||||
Widget.RemoveAndDelete(mProfilePanel);
|
||||
Widget.RemoveAndDelete(mFindPanel);
|
||||
|
||||
if (!mLogLines.IsEmpty)
|
||||
{
|
||||
var fs = scope FileStream();
|
||||
if (fs.Open("BeefPerf.txt", .Append, .ReadWrite, .ReadWrite, 4096, .None, null) case .Ok)
|
||||
{
|
||||
var streamWriter = scope StreamWriter(fs, Encoding.UTF8, 4096);
|
||||
for (var line in mLogLines)
|
||||
streamWriter.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32 TimeToUnixTime(DateTime ft)
|
||||
{
|
||||
// takes the last modified date
|
||||
int64 date = ft.ToFileTime();
|
||||
|
||||
// 100-nanoseconds = milliseconds * 10000
|
||||
int64 adjust = 11644473600000L * 10000;
|
||||
|
||||
// removes the diff between 1970 and 1601
|
||||
date -= adjust;
|
||||
|
||||
// converts back from 100-nanoseconds to seconds
|
||||
return (uint32)(date / 10000000L);
|
||||
}
|
||||
|
||||
public void LogLine(String text)
|
||||
{
|
||||
mLogLines.Add(new String(text));
|
||||
}
|
||||
|
||||
public void Fail(String text)
|
||||
{
|
||||
//MessageBeep(MessageBeepType.Error);
|
||||
|
||||
// Always write to STDOUT even if we're running as a GUI, allowing cases like RunAndWait to pass us a stdout handle
|
||||
Console.WriteLine("ERROR: {0}", text);
|
||||
|
||||
mFailIdx++;
|
||||
|
||||
if (gApp.mScriptManager.mRunningCommand)
|
||||
return;
|
||||
|
||||
if (mMainWindow == null)
|
||||
{
|
||||
LogLine(text);
|
||||
|
||||
mFailed = true;
|
||||
if (!mShuttingDown)
|
||||
Shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
Dialog aDialog = ThemeFactory.mDefault.CreateDialog("ERROR", text, DarkTheme.sDarkTheme.mIconError);
|
||||
aDialog.mDefaultButton = aDialog.AddButton("OK");
|
||||
aDialog.mEscButton = aDialog.mDefaultButton;
|
||||
aDialog.PopupWindow(mMainWindow);
|
||||
}
|
||||
|
||||
void StartFakeClient()
|
||||
{
|
||||
return;
|
||||
#unwarn
|
||||
ProcessStartInfo procInfo = scope ProcessStartInfo();
|
||||
procInfo.UseShellExecute = false;
|
||||
procInfo.RedirectStandardError = true;
|
||||
procInfo.SetFileName(@"c:\proj\BeefPerf\x64\Debug\BeefPerf.exe");
|
||||
procInfo.CreateNoWindow = true;
|
||||
|
||||
var spawnedProcess = scope SpawnedProcess();
|
||||
spawnedProcess.Start(procInfo).IgnoreError();
|
||||
|
||||
//Process process = Process.Start(procInfo).GetValueOrDefault();
|
||||
//delete process;
|
||||
}
|
||||
|
||||
public void UpdateTitle()
|
||||
{
|
||||
String title = scope String();
|
||||
if ((mBoard.mPerfView != null) && (mBoard.mPerfView.mSession.mClientName != null))
|
||||
{
|
||||
title.Append(mBoard.mPerfView.mSession.mClientName);
|
||||
title.Append(" - ");
|
||||
}
|
||||
title.Append("BeefPerf");
|
||||
mMainWindow.SetTitle(title);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
/*uint timeNow = GetCurrentUnixTime();
|
||||
|
||||
FitEntry[] fitEntries = new FitEntry[200];
|
||||
for (int i = 0; i < fitEntries.Length; i++)
|
||||
{
|
||||
fitEntries[i].mElapsedTimeMS = i * 500;
|
||||
fitEntries[i].mPower = i;
|
||||
fitEntries[i].mCadence = 90;
|
||||
}
|
||||
|
||||
int[] lapOffsets = scope int[] {100};
|
||||
|
||||
WriteFit("c:\\temp\\test.fit", UnixTimeToFitTime(timeNow), fitEntries.CArray(), fitEntries.Length, lapOffsets.CArray(), lapOffsets.Length);*/
|
||||
|
||||
base.Init();
|
||||
mAutoDirty = false;
|
||||
|
||||
Socket.Init();
|
||||
|
||||
mListenSocket = new Socket();
|
||||
Listening = true;
|
||||
|
||||
mSocketThread = new Thread(new => SocketThreadProc);
|
||||
mSocketThread.Start(false);
|
||||
|
||||
DarkTheme aTheme = new DarkTheme();
|
||||
aTheme.Init();
|
||||
ThemeFactory.mDefault = aTheme;
|
||||
|
||||
mMainFrame = new MainFrame();
|
||||
mDockingFrame = mMainFrame.mDockingFrame;
|
||||
|
||||
BFWindow.Flags windowFlags = BFWindow.Flags.Border | BFWindow.Flags.SysMenu | //| BFWindow.Flags.CaptureMediaKeys |
|
||||
BFWindow.Flags.Caption | BFWindow.Flags.Minimize | BFWindow.Flags.QuitOnClose | BFWindowBase.Flags.Resizable |
|
||||
BFWindow.Flags.Menu | BFWindow.Flags.SysMenu;
|
||||
if (mWantsFullscreen)
|
||||
windowFlags |= BFWindowBase.Flags.Fullscreen;
|
||||
|
||||
//mRootWidget = new Widget();
|
||||
mWorkspacePanel = new WorkspacePanel();
|
||||
mBoard = new Board();
|
||||
mProfilePanel = new ProfilePanel();
|
||||
mFindPanel = new FindPanel();
|
||||
|
||||
//mRootWidget = mBoard;
|
||||
mMainWindow = new WidgetWindow(null, "BeefPerf", 0, 0, 1600, 1200, windowFlags, mMainFrame);
|
||||
mMainWindow.mOnWindowKeyDown.Add(new => SysKeyDown);
|
||||
mMainWindow.SetMinimumSize(480, 360);
|
||||
mMainWindow.mIsMainWindow = true;
|
||||
CreateMenu();
|
||||
|
||||
ShowWorkspacePanel();
|
||||
ShowTimelinePanel();
|
||||
ShowProfilePanel();
|
||||
ShowFindPanel();
|
||||
|
||||
StartFakeClient();
|
||||
|
||||
GC.SetAutoCollectPeriod(mEnableGCCollect ? 20 : -1);
|
||||
GC.SetCollectFreeThreshold(mEnableGCCollect ? 64*1024*1024 : -1);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
base.Stop();
|
||||
mListenSocket.Close();
|
||||
}
|
||||
|
||||
void ShowWorkspacePanel()
|
||||
{
|
||||
if (mWorkspacePanel.mWidgetWindow != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TabbedView tabbedView = CreateTabbedView();
|
||||
SetupTab(tabbedView, "Workspace", mWorkspacePanel);
|
||||
tabbedView.SetRequestedSize(320, 320);
|
||||
if ((mDockingFrame.mSplitType == .Vert) || (mDockingFrame.mDockedWidgets.Count == 1))
|
||||
mDockingFrame.AddDockedWidget(tabbedView, mDockingFrame.mDockedWidgets[0], DockingFrame.WidgetAlign.Left);
|
||||
else
|
||||
mDockingFrame.AddDockedWidget(tabbedView, null, DockingFrame.WidgetAlign.Top);
|
||||
}
|
||||
|
||||
void ShowTimelinePanel()
|
||||
{
|
||||
if (mBoard.mWidgetWindow != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TabbedView tabbedView = CreateTabbedView();
|
||||
SetupTab(tabbedView, "Timeline", mBoard);
|
||||
tabbedView.SetRequestedSize(250, 250);
|
||||
tabbedView.mIsFillWidget = true;
|
||||
if ((mDockingFrame.mSplitType == .Vert) || (mDockingFrame.mDockedWidgets.Count == 1))
|
||||
mDockingFrame.AddDockedWidget(tabbedView, mDockingFrame.mDockedWidgets[0], DockingFrame.WidgetAlign.Right);
|
||||
else
|
||||
mDockingFrame.AddDockedWidget(tabbedView, null, DockingFrame.WidgetAlign.Top);
|
||||
}
|
||||
|
||||
void ShowProfilePanel()
|
||||
{
|
||||
if (mProfilePanel.mWidgetWindow != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tabbedView = CreateTabbedView();
|
||||
SetupTab(tabbedView, "Profile", mProfilePanel);
|
||||
tabbedView.SetRequestedSize(300, 300);
|
||||
|
||||
if ((mDockingFrame.mSplitType == .Vert) && (mDockingFrame.mDockedWidgets.Count > 0))
|
||||
mDockingFrame.AddDockedWidget(tabbedView, mDockingFrame.mDockedWidgets.Back, DockingFrame.WidgetAlign.Left);
|
||||
else
|
||||
mDockingFrame.AddDockedWidget(tabbedView, null, DockingFrame.WidgetAlign.Bottom);
|
||||
}
|
||||
|
||||
void ShowFindPanel()
|
||||
{
|
||||
if (mFindPanel.mWidgetWindow != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tabbedView = CreateTabbedView();
|
||||
SetupTab(tabbedView, "Find", mFindPanel);
|
||||
tabbedView.SetRequestedSize(300, 300);
|
||||
if ((mDockingFrame.mSplitType == .Vert) && (mDockingFrame.mDockedWidgets.Count > 0))
|
||||
mDockingFrame.AddDockedWidget(tabbedView, mDockingFrame.mDockedWidgets.Back, DockingFrame.WidgetAlign.Right);
|
||||
else
|
||||
mDockingFrame.AddDockedWidget(tabbedView, null, DockingFrame.WidgetAlign.Bottom);
|
||||
}
|
||||
|
||||
void SetupTab(TabbedView tabbedView, String label, Widget widget)
|
||||
{
|
||||
var tabButton = tabbedView.AddTab(label, 0, widget, false);
|
||||
tabButton.mCloseClickedEvent.Add(new () =>
|
||||
{
|
||||
var tabbedView = tabButton.mTabbedView;
|
||||
tabbedView.RemoveTab(tabButton);
|
||||
if (tabbedView.mTabs.IsEmpty)
|
||||
{
|
||||
tabbedView.mParentDockingFrame.RemoveDockedWidget(tabbedView);
|
||||
gApp.DeferDelete(tabbedView);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ToggleCheck(IMenu menu, ref bool checkVal)
|
||||
{
|
||||
checkVal = !checkVal;
|
||||
var sysMenu = (SysMenu)menu;
|
||||
sysMenu.Modify(null, null, null, true, checkVal ? 1 : 0);
|
||||
}
|
||||
|
||||
void ShowInfo()
|
||||
{
|
||||
int streamSize = 0;
|
||||
int lodStreamSize = 0;
|
||||
|
||||
for (var client in mClients)
|
||||
{
|
||||
for (var thread in client.mThreads)
|
||||
{
|
||||
for (var streamData in thread.mStreamDataList)
|
||||
{
|
||||
streamSize += streamData.mBuffer.Count;
|
||||
}
|
||||
|
||||
for (var streamLOD in thread.mStreamLODs)
|
||||
{
|
||||
for (var streamData in streamLOD.mStreamDataList)
|
||||
{
|
||||
lodStreamSize += streamData.mBuffer.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine("Stream Size: {0}k", streamSize / 1024);
|
||||
Debug.WriteLine("LOD Stream Size: {0}k", lodStreamSize / 1024);
|
||||
}
|
||||
|
||||
public void CloseSession()
|
||||
{
|
||||
SetSession(null);
|
||||
}
|
||||
|
||||
public void OpenSession()
|
||||
{
|
||||
List<int> iList = scope List<int>();
|
||||
iList.Add(123);
|
||||
|
||||
var fileDialog = scope OpenFileDialog();
|
||||
fileDialog.ShowReadOnly = false;
|
||||
fileDialog.Title = "Open Session";
|
||||
fileDialog.Multiselect = true;
|
||||
fileDialog.ValidateNames = true;
|
||||
fileDialog.DefaultExt = ".bfps";
|
||||
fileDialog.SetFilter("BeefPerf Session (*.bfps)|*.bfps|All files (*.*)|*.*");
|
||||
if (fileDialog.ShowDialog() case .Ok)
|
||||
{
|
||||
for (String origProjFilePath in fileDialog.FileNames)
|
||||
{
|
||||
var session = new BpSession(false);
|
||||
switch (session.Load(origProjFilePath))
|
||||
{
|
||||
case .Ok:
|
||||
mSessions.Add(session);
|
||||
MarkDirty();
|
||||
case .Err(let err):
|
||||
switch (err)
|
||||
{
|
||||
case .FileNotFound, .FileError:
|
||||
Fail(scope String()..AppendF("Failed to open file '{0}'", origProjFilePath));
|
||||
case .InvalidData:
|
||||
Fail(scope String()..AppendF("Invalid data in file '{0}'", origProjFilePath));
|
||||
case .InvalidVersion:
|
||||
Fail(scope String()..AppendF("Unsupported version in file '{0}'", origProjFilePath));
|
||||
}
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveSession()
|
||||
{
|
||||
if (mCurSession == null)
|
||||
return;
|
||||
|
||||
var fileDialog = scope SaveFileDialog();
|
||||
//fileDialog.ShowReadOnly = false;
|
||||
fileDialog.Title = "Save Session";
|
||||
fileDialog.Multiselect = true;
|
||||
fileDialog.ValidateNames = true;
|
||||
fileDialog.DefaultExt = ".bfps";
|
||||
fileDialog.SetFilter("BeefPerf Session (*.bfps)|*.bfps|All files (*.*)|*.*");
|
||||
if (fileDialog.ShowDialog() case .Ok)
|
||||
{
|
||||
for (String origProjFilePath in fileDialog.FileNames)
|
||||
{
|
||||
if (mCurSession.Save(origProjFilePath) case .Err)
|
||||
{
|
||||
Fail(scope String()..AppendF("Failed to save file '{0}'", origProjFilePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveSession()
|
||||
{
|
||||
if (mCurSession == null)
|
||||
return;
|
||||
|
||||
var session = mCurSession;
|
||||
|
||||
SetSession(null);
|
||||
mSessions.Remove(session);
|
||||
|
||||
using (mClientMonitor.Enter())
|
||||
{
|
||||
var client = session as BpClient;
|
||||
if (client != null)
|
||||
mClients.Remove(client);
|
||||
delete session;
|
||||
}
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
public void RemoveAllSessions()
|
||||
{
|
||||
SetSession(null);
|
||||
|
||||
using (mClientMonitor.Enter())
|
||||
{
|
||||
ClearAndDeleteItems(mSessions);
|
||||
mSessions.Clear();
|
||||
mClients.Clear();
|
||||
}
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
public void CreateMenu()
|
||||
{
|
||||
SysMenu root = mMainWindow.mSysMenu;
|
||||
|
||||
SysMenu subMenu = root.AddMenuItem("&File");
|
||||
subMenu.AddMenuItem("&Open Session", "Ctrl+O", new (menu) => { OpenSession(); });
|
||||
subMenu.AddMenuItem("&Save Session", "Ctrl+S", new (menu) => { SaveSession(); });
|
||||
subMenu.AddMenuItem("&Close Session", "Ctrl+W", new (menu) => { CloseSession(); });
|
||||
subMenu.AddMenuItem("&Remove Session", null, new (menu) => { RemoveSession(); });
|
||||
subMenu.AddMenuItem("Remove &All Sessions", null, new (menu) => { RemoveAllSessions(); });
|
||||
subMenu.AddMenuItem("&Info", "Ctrl+I", new (menu) => { ShowInfo(); });
|
||||
subMenu.AddMenuItem("E&xit", null, new (menu) => { mMainWindow.Close(); });
|
||||
|
||||
subMenu = root.AddMenuItem("&View");
|
||||
subMenu.AddMenuItem("&Workspace", "Ctrl+Alt+W", new (menu) => { ShowWorkspacePanel(); });
|
||||
subMenu.AddMenuItem("&Timeline", "Ctrl+Alt+T", new (menu) => { ShowTimelinePanel(); });
|
||||
subMenu.AddMenuItem("&Profile", "Ctrl+Alt+P", new (menu) => { ShowProfilePanel(); });
|
||||
subMenu.AddMenuItem("&Find", "Ctrl+Alt+F", new (menu) => { ShowFindPanel(); });
|
||||
|
||||
subMenu = root.AddMenuItem("&Debug");
|
||||
subMenu.AddMenuItem("GC Collect", null, new (menu) =>
|
||||
{
|
||||
if (Profiler.StartSampling() case .Ok(let id))
|
||||
{
|
||||
GC.Collect();
|
||||
id.Dispose();
|
||||
}
|
||||
});
|
||||
subMenu.AddMenuItem("Enable GC Collect", null, new (menu) => { ToggleCheck(menu, ref mEnableGCCollect); }, null, null, true, mEnableGCCollect ? 1 : 0);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
mShutdownEvent.Set(true);
|
||||
}
|
||||
|
||||
public override bool HandleCommandLineParam(String key, String value)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "-autoOpened":
|
||||
mIsAutoOpened = true;
|
||||
return true;
|
||||
case "-cmd":
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
Socket.Init();
|
||||
|
||||
LogLine(scope String()..AppendF("Cmd: {0}", value));
|
||||
|
||||
bool alreadyRunning = false;
|
||||
|
||||
//
|
||||
{
|
||||
Socket socket = scope Socket();
|
||||
if (socket.Listen(mListenPort) case .Err)
|
||||
alreadyRunning = true;
|
||||
}
|
||||
|
||||
if (!alreadyRunning)
|
||||
{
|
||||
LogLine("Launching BeefPerf process");
|
||||
|
||||
var exePath = scope String();
|
||||
|
||||
var curProcess = scope Process();
|
||||
curProcess.GetProcessById(Process.CurrentId);
|
||||
exePath.Append(curProcess.ProcessName);
|
||||
|
||||
ProcessStartInfo procInfo = scope ProcessStartInfo();
|
||||
procInfo.SetFileName(exePath);
|
||||
procInfo.SetArguments("-autoOpened");
|
||||
SpawnedProcess spawnedProcess = scope SpawnedProcess();
|
||||
spawnedProcess.Start(procInfo);
|
||||
}
|
||||
|
||||
// Try for 10 seconds
|
||||
for (int i < 100)
|
||||
{
|
||||
Socket socket = scope Socket();
|
||||
if (socket.Connect("127.0.0.1", mListenPort) case .Err)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
DynMemStream memStream = scope DynMemStream();
|
||||
memStream.Write((int32)0); // Size placeholder
|
||||
|
||||
memStream.Write((uint8)BpCmd.Cmd);
|
||||
String cmdStr = value;
|
||||
memStream.Write(cmdStr);
|
||||
memStream.Write((uint8)0);
|
||||
|
||||
(*(int32*)memStream.Ptr) = (int32)memStream.Length - 4;
|
||||
|
||||
// Try 10 seconds to send
|
||||
for (int sendItr < 100)
|
||||
{
|
||||
if (memStream.IsEmpty)
|
||||
break;
|
||||
|
||||
switch (socket.Send(memStream.Ptr, memStream.Length))
|
||||
{
|
||||
case .Ok(let sendLen):
|
||||
memStream.RemoveFromStart(sendLen);
|
||||
case .Err:
|
||||
Fail("Failed to send command");
|
||||
}
|
||||
|
||||
if (memStream.IsEmpty)
|
||||
break;
|
||||
Thread.Sleep(100); // Wait until we can send more...
|
||||
}
|
||||
|
||||
// Try 10 seconds for a response
|
||||
bool gotResult = false;
|
||||
RecvLoop: for (int recvStr < 100)
|
||||
{
|
||||
int result = 0;
|
||||
switch (socket.Recv(&result, 1))
|
||||
{
|
||||
case .Ok(let recvLen):
|
||||
if (recvLen > 0)
|
||||
{
|
||||
if (result != 1)
|
||||
Fail("Command failed");
|
||||
gotResult = true;
|
||||
break RecvLoop;
|
||||
}
|
||||
case .Err:
|
||||
break;
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
if (!gotResult)
|
||||
{
|
||||
Fail("Failed to receive command response");
|
||||
}
|
||||
|
||||
if (!mShuttingDown)
|
||||
Shutdown();
|
||||
return true;
|
||||
}
|
||||
Fail("Failed to connect to BeefPerf");
|
||||
case "-fullscreen":
|
||||
mWantsFullscreen = true;
|
||||
case "-windowed":
|
||||
mWantsFullscreen = false;
|
||||
default:
|
||||
return base.HandleCommandLineParam(key, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void UnhandledCommandLine(String key, String value)
|
||||
{
|
||||
Fail(StackStringFormat!("Unhandled command line param: {0}", key));
|
||||
}
|
||||
|
||||
void SetupNewWindow(WidgetWindow window)
|
||||
{
|
||||
window.mOnWindowKeyDown.Add(new => SysKeyDown);
|
||||
//window.mWindowCloseQueryHandler.Add(new => SecondaryAllowClose);
|
||||
}
|
||||
|
||||
DarkTabbedView CreateTabbedView()
|
||||
{
|
||||
var tabbedView = new DarkTabbedView();
|
||||
tabbedView.mSharedData.mOpenNewWindowDelegate.Add(new (fromTabbedView, newWindow) => SetupNewWindow(newWindow));
|
||||
return tabbedView;
|
||||
}
|
||||
|
||||
public void PushMainWidget(Widget widget)
|
||||
{
|
||||
if (mMainWidget != null)
|
||||
{
|
||||
mMainWidget.RemoveSelf();
|
||||
DeferDelete(mMainWidget);
|
||||
}
|
||||
|
||||
mMainWidget = widget;
|
||||
|
||||
mRootWidget.AddWidget(widget);
|
||||
widget.Resize(0, 0, mRootWidget.mWidth, mRootWidget.mHeight);
|
||||
widget.SetFocus();
|
||||
}
|
||||
|
||||
void SocketThreadProc()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (mShutdownEvent.WaitFor(0))
|
||||
return;
|
||||
|
||||
//TODO: Add client sockets to the select set
|
||||
|
||||
var readSet = default(Socket.FDSet);
|
||||
var writeSet = default(Socket.FDSet);
|
||||
var exceptSet = default(Socket.FDSet);
|
||||
|
||||
void Add(Socket socket)
|
||||
{
|
||||
readSet.Add(socket.[Friend]mHandle);
|
||||
exceptSet.Add(socket.[Friend]mHandle);
|
||||
}
|
||||
|
||||
Add(mListenSocket);
|
||||
using (mClientMonitor.Enter())
|
||||
{
|
||||
for (var client in mClients)
|
||||
Add(client.mSocket);
|
||||
}
|
||||
|
||||
#unwarn
|
||||
int selectResult = Socket.Select(&readSet, &writeSet, &exceptSet, 20*1000);
|
||||
|
||||
int clientIdx = 0;
|
||||
while (true)
|
||||
{
|
||||
BpClient client = null;
|
||||
using (mClientMonitor.Enter())
|
||||
{
|
||||
if (clientIdx < mClients.Count)
|
||||
{
|
||||
client = mClients[clientIdx++];
|
||||
}
|
||||
|
||||
if (client == null)
|
||||
break;
|
||||
|
||||
client.TryRecv();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(bool batchStart)
|
||||
{
|
||||
base.Update(batchStart);
|
||||
|
||||
/*if (!mListenSocket.IsConnected)
|
||||
{
|
||||
Listen();
|
||||
}*/
|
||||
|
||||
Socket newConnection = new Socket();
|
||||
if (newConnection.AcceptFrom(mListenSocket) case .Ok)
|
||||
{
|
||||
using (mClientMonitor.Enter())
|
||||
{
|
||||
BpClient client = new BpClient();
|
||||
//client.mProfileId = Profiler.StartSampling();
|
||||
client.mConnectTime = DateTime.Now;
|
||||
client.mSocket = newConnection;
|
||||
mClients.Add(client);
|
||||
}
|
||||
|
||||
/*{
|
||||
// Kill all old clients - remove this when we support switching between clients
|
||||
ClearAndDeleteItems(mClients);
|
||||
|
||||
mFindPanel.Clear();
|
||||
mProfilePanel.Clear();
|
||||
mFindPanel.mNeedsRestartSearch = true;
|
||||
|
||||
BpClient client = new BpClient();
|
||||
client.mSocket = newConnection;
|
||||
mClients.Add(client);
|
||||
|
||||
SetClient(client);
|
||||
}*/
|
||||
}
|
||||
else
|
||||
delete newConnection;
|
||||
|
||||
using (mClientMonitor.Enter())
|
||||
{
|
||||
for (var client in mClients)
|
||||
{
|
||||
client.Update();
|
||||
|
||||
if (client.mSessionOver)
|
||||
{
|
||||
if (!client.IsSessionInitialized)
|
||||
delete client;
|
||||
@client.Remove();
|
||||
|
||||
if (mClients.Count == 0)
|
||||
{
|
||||
gApp.mStatBytesPerSec = 0;
|
||||
}
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
if (mUpdateCnt % 20 == 0)
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
if (mUpdateCnt - mStatReportTick >= 60)
|
||||
{
|
||||
mStatBytesPerSec = mStatBytesReceived;
|
||||
mStatBytesReceived = 0;
|
||||
mStatReportTick = mUpdateCnt;
|
||||
}
|
||||
}
|
||||
|
||||
void SysKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
var window = (WidgetWindow)evt.mSender;
|
||||
|
||||
#unwarn
|
||||
Widget focusWidget = window.mFocusWidget;
|
||||
|
||||
if (evt.mKeyFlags == 0) // No ctrl/shift/alt
|
||||
{
|
||||
switch (evt.mKeyCode)
|
||||
{
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (evt.mKeyFlags == .Ctrl)
|
||||
{
|
||||
switch (evt.mKeyCode)
|
||||
{
|
||||
case (KeyCode)'F':
|
||||
ShowFind();
|
||||
case (KeyCode)'C':
|
||||
if (DarkTooltipManager.sTooltip != null)
|
||||
{
|
||||
SetClipboardText(DarkTooltipManager.sTooltip.mText);
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum ShowTabResult
|
||||
{
|
||||
Existing,
|
||||
OpenedNew
|
||||
}
|
||||
|
||||
public void WithDocumentTabbedViews(Action<DarkTabbedView> func)
|
||||
{
|
||||
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
|
||||
{
|
||||
var window = mWindows[windowIdx];
|
||||
var widgetWindow = window as WidgetWindow;
|
||||
if (widgetWindow != null)
|
||||
{
|
||||
var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame;
|
||||
if (widgetWindow == mMainWindow)
|
||||
darkDockingFrame = mDockingFrame;
|
||||
|
||||
if (darkDockingFrame != null)
|
||||
{
|
||||
darkDockingFrame.WithAllDockedWidgets(scope (dockedWidget) =>
|
||||
{
|
||||
var tabbedView = dockedWidget as DarkTabbedView;
|
||||
if (tabbedView != null)
|
||||
func(tabbedView);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WithTabs(Action<TabbedView.TabButton> func)
|
||||
{
|
||||
WithDocumentTabbedViews(scope (documentTabbedView) =>
|
||||
{
|
||||
documentTabbedView.WithTabs(func);
|
||||
});
|
||||
}
|
||||
|
||||
public TabbedView.TabButton GetTab(Widget content)
|
||||
{
|
||||
TabbedView.TabButton tab = null;
|
||||
WithTabs(scope [&] (checkTab) =>
|
||||
{
|
||||
if (checkTab.mContent == content)
|
||||
tab = checkTab;
|
||||
});
|
||||
return tab;
|
||||
}
|
||||
|
||||
TabbedView FindTabbedView(DockingFrame dockingFrame, int32 xDir, int32 yDir)
|
||||
{
|
||||
bool useFirst = true;
|
||||
if (dockingFrame.mSplitType == DockingFrame.SplitType.Horz)
|
||||
{
|
||||
useFirst = xDir > 0;
|
||||
}
|
||||
else
|
||||
useFirst = yDir > 0;
|
||||
|
||||
for (int32 pass = 0; pass < 2; pass++)
|
||||
{
|
||||
for (int32 i = 0; i < dockingFrame.mDockedWidgets.Count; i++)
|
||||
{
|
||||
if ((useFirst) && (i == 0) && (pass == 0))
|
||||
continue;
|
||||
|
||||
var widget = dockingFrame.mDockedWidgets[i];
|
||||
if (widget is TabbedView)
|
||||
return (TabbedView)widget;
|
||||
DockingFrame childFrame = widget as DockingFrame;
|
||||
if (childFrame != null)
|
||||
{
|
||||
TabbedView tabbedView = FindTabbedView(childFrame, xDir, yDir);
|
||||
if (tabbedView != null)
|
||||
return tabbedView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ShowTabResult ShowTab(Widget tabContent, String name, float width, bool ownsContent)
|
||||
{
|
||||
var result = ShowTabResult.Existing;
|
||||
var tabButton = GetTab(tabContent);
|
||||
if (tabButton == null)
|
||||
{
|
||||
TabbedView tabbedView = FindTabbedView(mDockingFrame, -1, 1);
|
||||
if (tabbedView != null)
|
||||
{
|
||||
tabButton = tabbedView.AddTab(name, width, tabContent, ownsContent);
|
||||
result = ShowTabResult.OpenedNew;
|
||||
}
|
||||
}
|
||||
if (tabButton != null)
|
||||
tabButton.Activate();
|
||||
return result;
|
||||
}
|
||||
|
||||
void ShowPanel(Widget panel, String label)
|
||||
{
|
||||
//RecordHistoryLocation();
|
||||
ShowTab(panel, label, 150, false);
|
||||
//panel.FocusForKeyboard();
|
||||
}
|
||||
|
||||
public void ShowFind()
|
||||
{
|
||||
ShowPanel(mFindPanel, "Find");
|
||||
mFindPanel.mEntryEdit.SetFocus();
|
||||
}
|
||||
|
||||
public void SetSession(BpSession session)
|
||||
{
|
||||
if (session == mCurSession)
|
||||
return;
|
||||
|
||||
mFindPanel.Clear();
|
||||
mProfilePanel.Clear();
|
||||
mFindPanel.mNeedsRestartSearch = true;
|
||||
|
||||
mCurSession = session;
|
||||
mBoard.ShowSession(session);
|
||||
|
||||
mFindPanel.Show(mBoard.mPerfView);
|
||||
mFindPanel.Clear();
|
||||
|
||||
MarkDirty();
|
||||
|
||||
/*if (mBoard.mPerfView != null)
|
||||
{
|
||||
mBoard.mPerfView.SaveSummary("BfCompiler_Compile", @"c:\temp\save.txt");
|
||||
}*/
|
||||
}
|
||||
|
||||
public void ZoneSelected(PerfView perfView, BPSelection selection)
|
||||
{
|
||||
mProfilePanel.Show(perfView, selection);
|
||||
}
|
||||
|
||||
public void ReportBytesReceived(int32 count)
|
||||
{
|
||||
mStatBytesReceived += count;
|
||||
}
|
||||
|
||||
public bool HandleClientCommand(BpClient client, StringView cmd)
|
||||
{
|
||||
int failIdx = mFailIdx;
|
||||
mScriptManager.Exec(cmd);
|
||||
return failIdx == mFailIdx;
|
||||
}
|
||||
|
||||
public void SessionInitialized(BpClient bpClient)
|
||||
{
|
||||
mSessions.Add(bpClient);
|
||||
UpdateTitle();
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
static BeefPerf.BPApp gApp;
|
||||
}
|
1635
BeefTools/BeefPerf/src/BPClient.bf
Normal file
1635
BeefTools/BeefPerf/src/BPClient.bf
Normal file
File diff suppressed because it is too large
Load diff
103
BeefTools/BeefPerf/src/BPUtils.bf
Normal file
103
BeefTools/BeefPerf/src/BPUtils.bf
Normal file
|
@ -0,0 +1,103 @@
|
|||
using Beefy.gfx;
|
||||
using System;
|
||||
using Beefy.theme.dark;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class BPUtils
|
||||
{
|
||||
public static void DrawWait(Graphics g, float x, float y)
|
||||
{
|
||||
for (int segIdx < 8)
|
||||
{
|
||||
int relSegIdx = (segIdx + (gApp.mUpdateCnt / 10)) % 8;
|
||||
|
||||
using (g.PushColor(Color.Get(1.0f - (float)relSegIdx * 0.1f)))
|
||||
using (g.PushRotate((float)Math.PI_f * -(float)segIdx / 4, x + 10, y + 10))
|
||||
g.Draw(DarkTheme.sDarkTheme.GetImage(.WaitBar), x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static Image sScratchHiliteImage ~ delete _;
|
||||
|
||||
static Image GetOutlineHiliteImage(Image hiliteImage, int ofs, int maxWidth)
|
||||
{
|
||||
int xOfs = ofs % 18;
|
||||
int width = Math.Min(maxWidth, 18 - xOfs);
|
||||
if (sScratchHiliteImage == null)
|
||||
{
|
||||
sScratchHiliteImage = hiliteImage.CreateImageSegment(xOfs, 0, width, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
sScratchHiliteImage.Modify(hiliteImage, xOfs, 0, width, 2);
|
||||
}
|
||||
|
||||
return sScratchHiliteImage;
|
||||
}
|
||||
|
||||
static void DrawOutlineSegment(Graphics g, Image hiliteImage, int len, int ofs)
|
||||
{
|
||||
int curOfs = ofs;
|
||||
int lenLeft = len;
|
||||
int xOfs = 0;
|
||||
while (lenLeft > 0)
|
||||
{
|
||||
var image = GetOutlineHiliteImage(hiliteImage, curOfs, lenLeft);
|
||||
g.Draw(image, (float)xOfs, 0);
|
||||
lenLeft -= (int)image.mWidth;
|
||||
xOfs += (int)image.mWidth;
|
||||
curOfs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawOutlineHilite(Graphics g, float x, float y, float width, float height)
|
||||
{
|
||||
var hiliteImage = DarkTheme.sDarkTheme.GetImage((Math.Min(width, height) > 2.5f) ? DarkTheme.ImageIdx.HiliteOutline : DarkTheme.ImageIdx.HiliteOutlineThin);
|
||||
|
||||
Matrix mat;
|
||||
int ofs = 17 - (gApp.mUpdateCnt / 6)%18;
|
||||
|
||||
if (width > 1.5f)
|
||||
{
|
||||
mat = Matrix.IdentityMatrix;
|
||||
mat.Translate(x, y);
|
||||
using (g.PushMatrix(mat))
|
||||
{
|
||||
DrawOutlineSegment(g, hiliteImage, (int)width, ofs);
|
||||
ofs += (int)width;
|
||||
}
|
||||
}
|
||||
|
||||
mat = Matrix.IdentityMatrix;
|
||||
mat.Rotate((float)Math.PI_f * 0.5f);
|
||||
mat.Translate(x + width, y + 1);
|
||||
using (g.PushMatrix(mat))
|
||||
{
|
||||
DrawOutlineSegment(g, hiliteImage, (int)height - 1, ofs);
|
||||
ofs += (int)height - 1;
|
||||
}
|
||||
|
||||
if (width < 1.5f)
|
||||
return;
|
||||
|
||||
mat = Matrix.IdentityMatrix;
|
||||
mat.Rotate((float)Math.PI_f);
|
||||
mat.Translate(x + width - 1, y + height);
|
||||
using (g.PushMatrix(mat))
|
||||
{
|
||||
DrawOutlineSegment(g, hiliteImage, (int)width - 1, ofs);
|
||||
ofs += (int)width - 1;
|
||||
}
|
||||
|
||||
mat = Matrix.IdentityMatrix;
|
||||
mat.Rotate((float)Math.PI_f * 1.5f);
|
||||
mat.Translate(x, y + height - 1);
|
||||
using (g.PushMatrix(mat))
|
||||
{
|
||||
DrawOutlineSegment(g, hiliteImage, (int)height - 2, ofs);
|
||||
ofs += (int)height - 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
BeefTools/BeefPerf/src/Board.bf
Normal file
82
BeefTools/BeefPerf/src/Board.bf
Normal file
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using Beefy;
|
||||
using Beefy.gfx;
|
||||
using Beefy.widgets;
|
||||
using Beefy.geom;
|
||||
using System.Collections.Generic;
|
||||
using Beefy.utils;
|
||||
using System.Diagnostics;
|
||||
using Beefy.theme.dark;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
struct BPEntry
|
||||
{
|
||||
public int32 mZoneNameId;
|
||||
public int32 mParamsReadPos;
|
||||
public int64 mStartTick;
|
||||
}
|
||||
|
||||
struct BPSelection
|
||||
{
|
||||
public int32 mThreadIdx;
|
||||
public int64 mTickStart;
|
||||
public int64 mTickEnd;
|
||||
public int32 mDepth;
|
||||
}
|
||||
|
||||
class Board : Widget
|
||||
{
|
||||
public PerfView mPerfView;
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Resize(float x, float y, float width, float height)
|
||||
{
|
||||
base.Resize(x, y, width, height);
|
||||
ResizeComponents();
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Bkg), 0, 0, mWidth, mHeight);
|
||||
}
|
||||
|
||||
void GetData()
|
||||
{
|
||||
}
|
||||
|
||||
public void ShowSession(BpSession session)
|
||||
{
|
||||
if (mPerfView != null)
|
||||
{
|
||||
mPerfView.RemoveSelf();
|
||||
DeleteAndNullify!(mPerfView);
|
||||
}
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
mPerfView = new PerfView(this, session);
|
||||
mPerfView.mSession = session;
|
||||
AddWidget(mPerfView);
|
||||
|
||||
ResizeComponents();
|
||||
|
||||
if (mWidgetWindow != null)
|
||||
mPerfView.SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeComponents()
|
||||
{
|
||||
if (mPerfView != null)
|
||||
mPerfView.Resize(0, 0, mWidth, mHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
242
BeefTools/BeefPerf/src/BpStateContext.bf
Normal file
242
BeefTools/BeefPerf/src/BpStateContext.bf
Normal file
|
@ -0,0 +1,242 @@
|
|||
using System.Threading;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Beefy;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class BPStateContext
|
||||
{
|
||||
public enum Event
|
||||
{
|
||||
case Enter(int64 startTick, int32 strIdx);
|
||||
case Leave(int64 endTick);
|
||||
case PrevFrameTick(int64 tick);
|
||||
case FrameTick(int64 tick);
|
||||
case LODSmallEntry(int64 startTick, int64 endTick, int32 stackDepth);
|
||||
case Event(int64 tick, char8* name, char8* details);
|
||||
case LODEvent(int64 tick, int32 paramsOfs);
|
||||
|
||||
case EndOfStream;
|
||||
}
|
||||
|
||||
public BpSession mSession;
|
||||
public BpStreamData mStreamData;
|
||||
public uint8* mReadPtr;
|
||||
public uint8* mReadStart;
|
||||
public uint8* mReadEnd;
|
||||
public uint8* mParamsPtr;
|
||||
public int64 mCurTick;
|
||||
public int32 mPendingParamsSize;
|
||||
public int32 mSplitCarryoverCount;
|
||||
public int mDepth;
|
||||
public bool mAutoLeave = true;
|
||||
public bool mTimeInferred;
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public this(BpSession session, BpStreamData streamData)
|
||||
{
|
||||
mSession = session;
|
||||
mStreamData = streamData;
|
||||
mCurTick = streamData.mStartTick;
|
||||
if (mStreamData.mBuffer.Count > 0)
|
||||
mReadPtr = &mStreamData.mBuffer[0];
|
||||
mReadStart = mReadPtr;
|
||||
mReadEnd = mReadPtr + mStreamData.mBuffer.Count;
|
||||
}
|
||||
|
||||
public int32 ReadPos
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int32)(mReadPtr - mReadStart);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mReadPtr = mReadStart + value;
|
||||
}
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public uint8 Read()
|
||||
{
|
||||
return *(mReadPtr++);
|
||||
}
|
||||
|
||||
public void Read(void* ptr, int32 size)
|
||||
{
|
||||
Internal.MemCpy(ptr, mReadPtr, size);
|
||||
mReadPtr += size;
|
||||
}
|
||||
|
||||
public int64 ReadSLEB128()
|
||||
{
|
||||
int64 value = 0;
|
||||
int32 shift = 0;
|
||||
int64 curByte;
|
||||
repeat
|
||||
{
|
||||
curByte = *(mReadPtr++);
|
||||
value |= ((curByte & 0x7f) << shift);
|
||||
shift += 7;
|
||||
|
||||
} while (curByte >= 128);
|
||||
// Sign extend negative numbers.
|
||||
if (((curByte & 0x40) != 0) && (shift < 64))
|
||||
value |= ~0L << shift;
|
||||
return value;
|
||||
}
|
||||
|
||||
void ReadTickDelta()
|
||||
{
|
||||
int64 tickDelta = ReadSLEB128() * BpClient.cBufTickScale;
|
||||
mCurTick += tickDelta;
|
||||
}
|
||||
|
||||
public void MoveToParamData()
|
||||
{
|
||||
if (mPendingParamsSize == -1)
|
||||
mPendingParamsSize = (int32)ReadSLEB128();
|
||||
}
|
||||
|
||||
public Event GetNextEvent()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (mPendingParamsSize == -1)
|
||||
mPendingParamsSize = (int32)ReadSLEB128();
|
||||
if (mPendingParamsSize != 0)
|
||||
{
|
||||
Debug.Assert(mPendingParamsSize >= 0);
|
||||
mReadPtr += mPendingParamsSize;
|
||||
mPendingParamsSize = 0;
|
||||
}
|
||||
|
||||
if (mReadPtr == mReadEnd)
|
||||
{
|
||||
if ((mDepth > 0) && (mAutoLeave))
|
||||
{
|
||||
--mDepth;
|
||||
Debug.Assert(mDepth >= 0); // ??
|
||||
mTimeInferred = true;
|
||||
return .Leave(mSession.mCurTick);
|
||||
}
|
||||
|
||||
return .EndOfStream;
|
||||
}
|
||||
|
||||
BpCmd cmd = (BpCmd)Read();
|
||||
switch (cmd)
|
||||
{
|
||||
case .Enter:
|
||||
mDepth++;
|
||||
ReadTickDelta();
|
||||
int32 zoneNameId = (int32)ReadSLEB128();
|
||||
if (zoneNameId < 0)
|
||||
{
|
||||
mPendingParamsSize = -1;
|
||||
mReadPtr += -zoneNameId;
|
||||
}
|
||||
else
|
||||
{
|
||||
let zoneName = mSession.mZoneNames[zoneNameId];
|
||||
mPendingParamsSize = zoneName.mParamsSize;
|
||||
}
|
||||
return .Enter(mCurTick, zoneNameId);
|
||||
case .Leave:
|
||||
mDepth--;
|
||||
Debug.Assert(mDepth >= 0);
|
||||
ReadTickDelta();
|
||||
return .Leave(mCurTick);
|
||||
case .LODSmallEntry:
|
||||
ReadTickDelta();
|
||||
int64 tickStart = mCurTick;
|
||||
ReadTickDelta();
|
||||
int32 stackDepth = (int32)ReadSLEB128();
|
||||
return .LODSmallEntry(tickStart, mCurTick, stackDepth);
|
||||
case .FrameTick:
|
||||
ReadTickDelta();
|
||||
return .FrameTick(mCurTick);
|
||||
case .PrevFrameTick:
|
||||
ReadTickDelta();
|
||||
return .PrevFrameTick(mCurTick);
|
||||
case .StreamSplitInfo:
|
||||
mSplitCarryoverCount = (int32)ReadSLEB128();
|
||||
case .Event:
|
||||
ReadTickDelta();
|
||||
mParamsPtr = mReadPtr;
|
||||
char8* name = (char8*)mReadPtr;
|
||||
int32 nameLen = String.StrLen(name);
|
||||
mReadPtr += nameLen + 1;
|
||||
char8* details = (char8*)mReadPtr;
|
||||
int32 detailsLen = String.StrLen(details);
|
||||
mReadPtr += detailsLen + 1;
|
||||
return .Event(mCurTick, name, details);
|
||||
case .LODEvent:
|
||||
ReadTickDelta();
|
||||
int32 paramsOfs = (int32)ReadSLEB128();
|
||||
return .LODEvent(mCurTick, paramsOfs);
|
||||
default:
|
||||
Runtime.FatalError("Not handled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FormatStr(int32 paramReadPos, int paramSize, String fmtStr, String outStr)
|
||||
{
|
||||
|
||||
int32 prevReadPos = ReadPos;
|
||||
ReadPos = paramReadPos;
|
||||
defer { ReadPos = prevReadPos; }
|
||||
|
||||
if (paramSize == -1)
|
||||
ReadSLEB128();
|
||||
|
||||
for (int idx = 0; idx < fmtStr.Length; idx++)
|
||||
{
|
||||
let c = fmtStr[idx];
|
||||
if (c == '%')
|
||||
{
|
||||
let cNext = fmtStr[idx + 1];
|
||||
idx++;
|
||||
|
||||
if (cNext == '%')
|
||||
{
|
||||
outStr.Append('%');
|
||||
}
|
||||
else if (cNext == 'd')
|
||||
{
|
||||
int32 val = 0;
|
||||
Read(&val, 4);
|
||||
val.ToString(outStr);
|
||||
}
|
||||
else if (cNext == 'f')
|
||||
{
|
||||
float val = 0;
|
||||
Read(&val, 4);
|
||||
val.ToString(outStr);
|
||||
}
|
||||
else if (cNext == 's')
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
char8 paramC = (char8)Read();
|
||||
if (paramC == 0)
|
||||
break;
|
||||
outStr.Append(paramC);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
outStr.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
278
BeefTools/BeefPerf/src/CircularBuffer.bf
Normal file
278
BeefTools/BeefPerf/src/CircularBuffer.bf
Normal file
|
@ -0,0 +1,278 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class CircularBuffer
|
||||
{
|
||||
public class View
|
||||
{
|
||||
public uint8* mPtr;
|
||||
public int32 mSrcIdx;
|
||||
public int32 mSrcSize;
|
||||
public uint8* mTempBuf ~ delete _;
|
||||
public int32 mTempBufSize;
|
||||
|
||||
public CircularBuffer mCircularBuffer;
|
||||
|
||||
//void Commit(int size = -1);
|
||||
};
|
||||
|
||||
uint8* mBuffer;
|
||||
int32 mTail;
|
||||
int32 mDataSize;
|
||||
int32 mBufSize;
|
||||
|
||||
public this()
|
||||
{
|
||||
mTail = 0;
|
||||
mBufSize = 0;
|
||||
mDataSize = 0;
|
||||
mBuffer = null;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
delete mBuffer;
|
||||
}
|
||||
|
||||
public void Resize(int32 newSize)
|
||||
{
|
||||
uint8* newBuffer = new uint8[newSize]*;
|
||||
Read(newBuffer, 0, mDataSize);
|
||||
|
||||
delete mBuffer;
|
||||
mBuffer = newBuffer;
|
||||
mBufSize = newSize;
|
||||
mTail = 0;
|
||||
}
|
||||
|
||||
public void GrowReserve(int32 addSize)
|
||||
{
|
||||
if (mDataSize + addSize <= mBufSize)
|
||||
return;
|
||||
Debug.WriteLine("GrowReserve");
|
||||
Resize(Math.Max(mDataSize + addSize, mDataSize + mDataSize/2));
|
||||
}
|
||||
|
||||
public void Grow(int32 addSize)
|
||||
{
|
||||
GrowReserve(addSize);
|
||||
mDataSize += addSize;
|
||||
}
|
||||
|
||||
/// This ensures that we have a span that does not wrap around. This ensures we can decode
|
||||
/// an entire block without wrap checks.
|
||||
public void EnsureSpan(int32 spanSize)
|
||||
{
|
||||
//Debug.WriteLine("Span Size:{0} Tail:{1} DataSize:{2} BufSize:{3}", spanSize, mTail, mDataSize, mBufSize);
|
||||
|
||||
if (mDataSize + spanSize > mBufSize)
|
||||
{
|
||||
int32 newSize = (int32)Math.Max(mDataSize + spanSize, (int32)mBufSize * 1.5);
|
||||
//Debug.WriteLine("Resizing {0}", newSize);
|
||||
Resize(newSize);
|
||||
return;
|
||||
}
|
||||
|
||||
int head = mTail + mDataSize;
|
||||
if (head + spanSize > mBufSize)
|
||||
{
|
||||
// If we are sufficiently full then keep the buffer at the same size
|
||||
if (mDataSize + spanSize < mBufSize / 2)
|
||||
{
|
||||
// If our buffer doesn't wrap around then we can just pull back inplace
|
||||
if (mTail + mDataSize <= mBufSize)
|
||||
{
|
||||
if (mTail != 0)
|
||||
{
|
||||
//Debug.WriteLine("MemMove");
|
||||
Internal.MemMove(mBuffer, mBuffer + mTail, mDataSize);
|
||||
mTail = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
//Debug.WriteLine("MakeLinear");
|
||||
MakeLinear();
|
||||
return;
|
||||
}
|
||||
|
||||
int32 newSize = (int32)Math.Max(mDataSize + spanSize, (int32)mBufSize * 1.5);
|
||||
//Debug.WriteLine("Resizing2 {0}", newSize);
|
||||
Resize(newSize);
|
||||
return;
|
||||
}
|
||||
|
||||
/*if (mTail + spanSize > mBufSize)
|
||||
{
|
||||
/*Debug.WriteLine("EnsureSpan {0} {1} {2} {3}", mTail, spanSize, mBufSize, mDataSize);
|
||||
|
||||
// If we are sufficiently full then keep the buffer at the same size
|
||||
if (mDataSize + spanSize < mBufSize / 2)
|
||||
{
|
||||
// If our buffer doesn't wrap around then we can just pull back inplace
|
||||
if (mTail + mDataSize <= mBufSize)
|
||||
{
|
||||
if (mTail != 0)
|
||||
{
|
||||
Internal.MemMove(mBuffer, mBuffer + mTail, mDataSize);
|
||||
mTail = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
MakeLinear();
|
||||
return;
|
||||
}*/
|
||||
|
||||
int32 newBufSize = (int32)((mTail + spanSize) * 1.5);
|
||||
Resize(newBufSize);
|
||||
}*/
|
||||
}
|
||||
|
||||
public void MakeLinear()
|
||||
{
|
||||
if (mDataSize == 0)
|
||||
{
|
||||
mTail = 0;
|
||||
return;
|
||||
}
|
||||
Resize(mBufSize);
|
||||
}
|
||||
|
||||
public void GrowFront(int32 addSize)
|
||||
{
|
||||
if (mDataSize + addSize > mBufSize)
|
||||
{
|
||||
Resize(mDataSize + addSize);
|
||||
}
|
||||
mDataSize += addSize;
|
||||
mTail = (mTail + mBufSize - addSize) % mBufSize;
|
||||
}
|
||||
|
||||
public int32 GetSize()
|
||||
{
|
||||
return mDataSize;
|
||||
}
|
||||
|
||||
public int32 GetBufSize()
|
||||
{
|
||||
return mBufSize;
|
||||
}
|
||||
|
||||
public uint8* GetPtr()
|
||||
{
|
||||
return mBuffer + mTail;
|
||||
}
|
||||
|
||||
public void SetPtr(uint8* ptr)
|
||||
{
|
||||
int32 newTail = (int32)(ptr - mBuffer);
|
||||
mDataSize -= newTail - mTail;
|
||||
Debug.Assert(mDataSize >= 0);
|
||||
if (newTail == mBufSize)
|
||||
newTail = 0;
|
||||
mTail = newTail;
|
||||
Debug.Assert(mTail < mBufSize);
|
||||
}
|
||||
|
||||
public void MapView(int32 idx, int32 len, View view)
|
||||
{
|
||||
view.mCircularBuffer = this;
|
||||
view.mSrcIdx = idx;
|
||||
view.mSrcSize = len;
|
||||
if (mTail + idx + len <= mBufSize)
|
||||
{
|
||||
view.mPtr = mBuffer + mTail + idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (view.mTempBufSize < len)
|
||||
{
|
||||
delete view.mTempBuf;
|
||||
view.mTempBuf = new uint8[len]*;
|
||||
view.mTempBufSize = len;
|
||||
}
|
||||
view.mPtr = view.mTempBuf;
|
||||
Read(view.mTempBuf, idx, len);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(void* ptr, int32 idx, int32 len)
|
||||
{
|
||||
Debug.Assert(len <= mBufSize);
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
int absIdx = (mTail + idx) % mBufSize;
|
||||
if (absIdx + len > mBufSize)
|
||||
{
|
||||
int lowSize = mBufSize - absIdx;
|
||||
Internal.MemCpy(ptr, mBuffer + absIdx, lowSize);
|
||||
Internal.MemCpy((uint8*)ptr + lowSize, mBuffer, len - lowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Internal.MemCpy(ptr, mBuffer + absIdx, len);
|
||||
}
|
||||
}
|
||||
|
||||
public uint8 Read()
|
||||
{
|
||||
uint8 val = mBuffer[mTail];
|
||||
mTail = (mTail + 1) % mBufSize;
|
||||
mDataSize--;
|
||||
return val;
|
||||
}
|
||||
|
||||
public int64 ReadSLEB128()
|
||||
{
|
||||
int64 value = 0;
|
||||
int32 shift = 0;
|
||||
int64 curByte;
|
||||
repeat
|
||||
{
|
||||
curByte = Read();
|
||||
value |= ((curByte & 0x7f) << shift);
|
||||
shift += 7;
|
||||
|
||||
} while (curByte >= 128);
|
||||
// Sign extend negative numbers.
|
||||
if (((curByte & 0x40) != 0) && (shift < 64))
|
||||
value |= ~0L << shift;
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Write(void* ptr, int32 idx, int32 len)
|
||||
{
|
||||
Debug.Assert(len <= mBufSize);
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
int absIdx = (mTail + idx) % mBufSize;
|
||||
if (absIdx + len > mBufSize)
|
||||
{
|
||||
int lowSize = mBufSize - absIdx;
|
||||
Internal.MemCpy(mBuffer + absIdx, ptr, lowSize);
|
||||
Internal.MemCpy(mBuffer, (uint8*)ptr + lowSize, len - lowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Internal.MemCpy(mBuffer + absIdx, ptr, len);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFront(int32 len)
|
||||
{
|
||||
mTail = (mTail + len) % mBufSize;
|
||||
mDataSize -= len;
|
||||
}
|
||||
|
||||
public void RemoveBack(int32 len)
|
||||
{
|
||||
mDataSize -= len;
|
||||
}
|
||||
}
|
||||
}
|
1173
BeefTools/BeefPerf/src/FindPanel.bf
Normal file
1173
BeefTools/BeefPerf/src/FindPanel.bf
Normal file
File diff suppressed because it is too large
Load diff
11
BeefTools/BeefPerf/src/Images.bf
Normal file
11
BeefTools/BeefPerf/src/Images.bf
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Beefy.gfx;
|
||||
|
||||
namespace Mercury
|
||||
{
|
||||
class Images
|
||||
{
|
||||
public static Image sGrass ~ delete _;
|
||||
public static Image[,] sPeople ~ DeleteContainerAndItems!(_);
|
||||
public static Image sStone ~ delete _;
|
||||
}
|
||||
}
|
38
BeefTools/BeefPerf/src/MainFrame.bf
Normal file
38
BeefTools/BeefPerf/src/MainFrame.bf
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Beefy;
|
||||
using Beefy.gfx;
|
||||
using Beefy.widgets;
|
||||
using Beefy.theme;
|
||||
using Beefy.theme.dark;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
public class MainFrame : Widget
|
||||
{
|
||||
public StatusBar mStatusBar;
|
||||
public DarkDockingFrame mDockingFrame;
|
||||
|
||||
public this()
|
||||
{
|
||||
mStatusBar = new StatusBar();
|
||||
AddWidget(mStatusBar);
|
||||
mDockingFrame = (DarkDockingFrame)ThemeFactory.mDefault.CreateDockingFrame();
|
||||
AddWidget(mDockingFrame);
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Resize(float x, float y, float width, float height)
|
||||
{
|
||||
base.Resize(x, y, width, height);
|
||||
int32 statusHeight = 20;
|
||||
mDockingFrame.Resize(0, 0, width, height - statusHeight);
|
||||
mStatusBar.Resize(0, mHeight - statusHeight, width, statusHeight);
|
||||
}
|
||||
}
|
||||
}
|
2507
BeefTools/BeefPerf/src/PerfView.bf
Normal file
2507
BeefTools/BeefPerf/src/PerfView.bf
Normal file
File diff suppressed because it is too large
Load diff
805
BeefTools/BeefPerf/src/ProfilePanel.bf
Normal file
805
BeefTools/BeefPerf/src/ProfilePanel.bf
Normal file
|
@ -0,0 +1,805 @@
|
|||
using Beefy.widgets;
|
||||
using Beefy.theme.dark;
|
||||
using Beefy.gfx;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Beefy.events;
|
||||
using System.Threading;
|
||||
using Beefy;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class ProfilePanel : Widget
|
||||
{
|
||||
public class ProfileListViewItem : DarkVirtualListViewItem
|
||||
{
|
||||
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
||||
{
|
||||
((ProfileListView)mListView).mProfilePanel.ItemClicked(this, btn, btnCount, x, y);
|
||||
}
|
||||
|
||||
public override bool Selected
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
int32 selectedIdx = mVirtualIdx;
|
||||
var profilePanel = ((ProfileListView)mListView).mProfilePanel;
|
||||
var result = profilePanel.mProfileCtx.mResults[selectedIdx];
|
||||
DeleteAndNullify!(profilePanel.mPerfView.mProfileHiliteZone);
|
||||
profilePanel.mPerfView.mProfileHiliteZone = new PerfView.HiliteZone(result.mZoneNameId, result.mUnformattedName);
|
||||
|
||||
//result.mName
|
||||
}
|
||||
|
||||
base.Selected = value;
|
||||
//int32 selectedIdx = item.mVirtualIdx;
|
||||
//var foundEntry = mSearchState.mFoundEntries[selectedIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ProfileListView : DarkVirtualListView
|
||||
{
|
||||
public ProfilePanel mProfilePanel;
|
||||
|
||||
public override void ChangeSort(DarkListView.SortType sortType)
|
||||
{
|
||||
base.ChangeSort(sortType);
|
||||
mSortType = sortType;
|
||||
mProfilePanel.RefreshList();
|
||||
}
|
||||
|
||||
protected override ListViewItem CreateListViewItem()
|
||||
{
|
||||
return new ProfileListViewItem();
|
||||
}
|
||||
|
||||
public override void PopulateVirtualItem(DarkVirtualListViewItem listViewItem)
|
||||
{
|
||||
base.PopulateVirtualItem(listViewItem);
|
||||
|
||||
var client = mProfilePanel.mPerfView.mSession;
|
||||
var perfInfo = mProfilePanel.mProfileCtx.mResults[listViewItem.mVirtualIdx];
|
||||
listViewItem.Label = perfInfo.mName;
|
||||
|
||||
var subItem = listViewItem.CreateSubItem(1);
|
||||
subItem.mLabel = new String();
|
||||
subItem.mLabel.AppendF("{0}", perfInfo.mCount);
|
||||
|
||||
subItem = listViewItem.CreateSubItem(2);
|
||||
subItem.mLabel = new String();
|
||||
client.ElapsedTicksToStr(perfInfo.mTicks, subItem.mLabel);
|
||||
|
||||
subItem = listViewItem.CreateSubItem(3);
|
||||
subItem.mLabel = new String();
|
||||
client.ElapsedTicksToStr(perfInfo.mTicks - perfInfo.mChildTicks, subItem.mLabel);
|
||||
}
|
||||
|
||||
public override void DrawAll(Graphics g)
|
||||
{
|
||||
base.DrawAll(g);
|
||||
|
||||
if (((mProfilePanel.mProfileCtx != null) && (!mProfilePanel.mProfileCtx.mDone)) || (mProfilePanel.mSorting))
|
||||
{
|
||||
using (g.PushColor(0xA0595959))
|
||||
g.FillRect(0, 20, mWidth - 20, mHeight - 20);
|
||||
BPUtils.DrawWait(g, mWidth / 2, mHeight / 2);
|
||||
}
|
||||
}
|
||||
|
||||
public override void KeyDown(KeyCode keyCode, bool isRepeat)
|
||||
{
|
||||
base.KeyDown(keyCode, isRepeat);
|
||||
if (keyCode == .Escape)
|
||||
{
|
||||
mProfilePanel.RemoveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSummaryString(String str)
|
||||
{
|
||||
str.Append("Name____________________________________Count_______Total________Self\n");
|
||||
for (var entry in mProfilePanel.mProfileCtx.mResults)
|
||||
{
|
||||
str.Append(entry.mName);
|
||||
str.Append(' ', Math.Max(38 - entry.mName.Length, 1));
|
||||
|
||||
var entryStr = scope String();
|
||||
entry.mCount.ToString(entryStr);
|
||||
str.Append(' ', Math.Max(7 - entryStr.Length, 1));
|
||||
str.Append(entryStr);
|
||||
|
||||
entryStr.Clear();
|
||||
mProfilePanel.mProfileCtx.mSession.ElapsedTicksToStr(entry.mTicks, entryStr);
|
||||
str.Append(' ', Math.Max(12 - entryStr.Length, 1));
|
||||
str.Append(entryStr);
|
||||
|
||||
entryStr.Clear();
|
||||
mProfilePanel.mProfileCtx.mSession.ElapsedTicksToStr(entry.mTicks - entry.mChildTicks, entryStr);
|
||||
str.Append(' ', Math.Max(12 - entryStr.Length, 1));
|
||||
str.Append(entryStr);
|
||||
str.Append('\n');
|
||||
}
|
||||
|
||||
str.Append("---------------------------------------------------------------------\n");
|
||||
str.Append("Total Time: ");
|
||||
|
||||
mProfilePanel.mProfileCtx.mSession.ElapsedTicksToStr(mProfilePanel.mSelection.mTickEnd - mProfilePanel.mSelection.mTickStart, str);
|
||||
}
|
||||
|
||||
public void AddStaticMenu(Menu menu)
|
||||
{
|
||||
var menuItem = menu.AddItem("Copy to Clipboard");
|
||||
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
||||
{
|
||||
String str = scope String();
|
||||
GetSummaryString(str);
|
||||
gApp.SetClipboardText(str);
|
||||
//Debug.WriteLine(str);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
||||
{
|
||||
base.MouseDown(x, y, btn, btnCount);
|
||||
|
||||
GetRoot().SelectItemExclusively(null);
|
||||
if (btn == 1)
|
||||
{
|
||||
Menu menu = new Menu();
|
||||
AddStaticMenu(menu);
|
||||
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
|
||||
menuWidget.Init(this, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkCheckBox mFormatCheckbox;
|
||||
ProfileListView mListView;
|
||||
DarkButton mGetButton;
|
||||
|
||||
PerfView mPerfView;
|
||||
BPSelection mSelection;
|
||||
bool mSelectionDirty;
|
||||
int64 mActiveLastCurTick;
|
||||
|
||||
ProfileContext mProfileCtx ~ delete _;
|
||||
public WaitEvent mSortDoneHandle = new WaitEvent() ~ delete _;
|
||||
public bool mSorting;
|
||||
|
||||
public this()
|
||||
{
|
||||
mFormatCheckbox = new DarkCheckBox();
|
||||
mFormatCheckbox.Checked = true;
|
||||
mFormatCheckbox.Label = "Format Strings";
|
||||
mFormatCheckbox.mOnMouseClick.Add(new [&] (evt) => { mSelectionDirty = true; } );
|
||||
AddWidget(mFormatCheckbox);
|
||||
|
||||
mListView = new ProfileListView();
|
||||
mListView.mProfilePanel = this;
|
||||
mListView.mOnLostFocus.Add(new (evt) => { RemoveFocus(); });
|
||||
mListView.mSortType.mColumn = 2;
|
||||
mListView.mSortType.mReverse = true;
|
||||
AddWidget(mListView);
|
||||
|
||||
mListView.AddColumn(200, "Name");
|
||||
mListView.AddColumn(100, "Count");
|
||||
mListView.AddColumn(150, "Total");
|
||||
mListView.AddColumn(150, "Self");
|
||||
mListView.InitScrollbars(false, true);
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
FinishSorting();
|
||||
}
|
||||
|
||||
void FinishSorting()
|
||||
{
|
||||
if (mSorting)
|
||||
{
|
||||
mSortDoneHandle.WaitFor();
|
||||
mSorting = false;
|
||||
mSortDoneHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFocus()
|
||||
{
|
||||
mListView.GetRoot().SelectItemExclusively(null);
|
||||
if (mPerfView != null)
|
||||
DeleteAndNullify!(mPerfView.mProfileHiliteZone);
|
||||
}
|
||||
|
||||
public override void Resize(float x, float y, float width, float height)
|
||||
{
|
||||
base.Resize(x, y, width, height);
|
||||
|
||||
mListView.ResizeClamped(0, 20, width, height - 20);
|
||||
mFormatCheckbox.Resize(0, 0, 20, 20);
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
|
||||
if (mProfileCtx != null)
|
||||
{
|
||||
String str = scope String();
|
||||
str.Append("Total Time: ");
|
||||
mProfileCtx.mSession.ElapsedTicksToStr(mSelection.mTickEnd - mSelection.mTickStart, str);
|
||||
g.DrawString(str, 150, 0);
|
||||
}
|
||||
//g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Bkg), 0, 0, mWidth, mHeight);
|
||||
}
|
||||
|
||||
public override void DrawAll(Graphics g)
|
||||
{
|
||||
base.DrawAll(g);
|
||||
}
|
||||
|
||||
public void Show(PerfView perfView, BPSelection selection)
|
||||
{
|
||||
Debug.Assert(perfView != null);
|
||||
mPerfView = perfView;
|
||||
mSelection = selection;
|
||||
mSelectionDirty = true;
|
||||
}
|
||||
|
||||
void GetData()
|
||||
{
|
||||
mListView.GetRoot().Clear();
|
||||
|
||||
}
|
||||
|
||||
struct PerfInfo
|
||||
{
|
||||
public String mName;
|
||||
public int32 mCount;
|
||||
public int64 mTicks;
|
||||
public int64 mChildTicks;
|
||||
public int32 mStackCount; // Number of times this entry appears in entryStack
|
||||
|
||||
public int32 mZoneNameId;
|
||||
public String mUnformattedName;
|
||||
}
|
||||
|
||||
struct BPPerfEntry
|
||||
{
|
||||
public int32 mZoneNameId;
|
||||
public int64 mTickStart;
|
||||
public int64 mChildTicks;
|
||||
public int32 mParamsReadPos;
|
||||
public PerfInfo* mPerfInfo;
|
||||
}
|
||||
|
||||
class ProfileContext
|
||||
{
|
||||
BumpAllocator mAlloc = new BumpAllocator() ~ delete _;
|
||||
public BpSession mSession;
|
||||
public Dictionary<String, PerfInfo*> mPerfDict = new .() ~ delete _;
|
||||
String mTempStr = new String() ~ delete _;
|
||||
String mTempDynStr = new String() ~ delete _;
|
||||
public List<PerfInfo*> mResults = new List<PerfInfo*>() ~ delete _;
|
||||
public List<PerfInfo*> mSortingResults = new List<PerfInfo*>() ~ delete _;
|
||||
public int32 mStreamDataIdx;
|
||||
public bool mDone;
|
||||
public bool mFormatStrings;
|
||||
public bool mHasSelectionEndChanged;
|
||||
|
||||
public PerfInfo* GetPerfInfo(BPPerfEntry entry, BPStateContext stateCtx)
|
||||
{
|
||||
int32 paramsSize;
|
||||
|
||||
String str;
|
||||
if (entry.mZoneNameId < 0)
|
||||
{
|
||||
int32 nameLen = -entry.mZoneNameId;
|
||||
str = mTempDynStr;
|
||||
str.Reference((char8*)stateCtx.mReadStart + entry.mParamsReadPos - nameLen, nameLen, 0);
|
||||
paramsSize = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
let zoneName = mSession.mZoneNames[entry.mZoneNameId];
|
||||
str = zoneName.mName;
|
||||
paramsSize = zoneName.mParamsSize;
|
||||
}
|
||||
|
||||
//bool dbgStr = str == "DeepStack0 %d";
|
||||
|
||||
String unformattedStr = str;
|
||||
|
||||
if ((paramsSize != 0) && (mFormatStrings))
|
||||
{
|
||||
mTempStr.Clear();
|
||||
stateCtx.FormatStr(entry.mParamsReadPos, paramsSize, str, mTempStr);
|
||||
str = mTempStr;
|
||||
}
|
||||
|
||||
String* keyPtr;
|
||||
PerfInfo* perfInfo;
|
||||
PerfInfo** perfInfoPtr;
|
||||
if (mPerfDict.TryAdd(str, out keyPtr, out perfInfoPtr))
|
||||
{
|
||||
perfInfo = new:mAlloc PerfInfo();
|
||||
*perfInfoPtr = perfInfo;
|
||||
|
||||
if (str == (Object)mTempStr)
|
||||
{
|
||||
String heapStr = new:mAlloc String(str);
|
||||
*keyPtr = heapStr;
|
||||
perfInfo.mName = heapStr;
|
||||
}
|
||||
else
|
||||
perfInfo.mName = str;
|
||||
|
||||
if ((unformattedStr == (Object)mTempDynStr) && (unformattedStr != (Object)mTempStr))
|
||||
{
|
||||
String heapStr = new:mAlloc String(unformattedStr);
|
||||
perfInfo.mUnformattedName = heapStr;
|
||||
}
|
||||
else
|
||||
perfInfo.mUnformattedName = unformattedStr;
|
||||
perfInfo.mZoneNameId = entry.mZoneNameId;
|
||||
}
|
||||
else
|
||||
{
|
||||
perfInfo = *perfInfoPtr;
|
||||
if ((entry.mZoneNameId > 0) && (perfInfo.mZoneNameId < 0))
|
||||
{
|
||||
// Set to a valid zoneNameId if the inserting entry was a dynamic string but this one isn't
|
||||
perfInfo.mZoneNameId = entry.mZoneNameId;
|
||||
}
|
||||
}
|
||||
|
||||
return perfInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProfileCtx()
|
||||
{
|
||||
Stopwatch stopwatch = scope Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
var client = mPerfView.mSession;
|
||||
var thread = client.mThreads[mSelection.mThreadIdx];
|
||||
|
||||
bool isRecording = false;
|
||||
bool isFirstDrawn = mProfileCtx.mStreamDataIdx == 0;
|
||||
|
||||
bool isManualSelection = mSelection.mDepth == -1;
|
||||
|
||||
StreamLoop: while (mProfileCtx.mStreamDataIdx < thread.mStreamDataList.Count)
|
||||
{
|
||||
int streamDataListIdx = mProfileCtx.mStreamDataIdx++;
|
||||
var streamData = thread.mStreamDataList[streamDataListIdx];
|
||||
|
||||
if ((streamData.mSplitTick > 0) && (streamData.mSplitTick < mSelection.mTickStart))
|
||||
continue; // All data is to the left
|
||||
|
||||
BPStateContext stateCtx = scope BPStateContext(client, streamData);
|
||||
|
||||
List<BPPerfEntry> entryStack = scope List<BPPerfEntry>();
|
||||
int32 stackDepth = 0;
|
||||
|
||||
CmdLoop: while (true)
|
||||
{
|
||||
switch (stateCtx.GetNextEvent())
|
||||
{
|
||||
case let .Enter(startTick, strIdx):
|
||||
int stackPos = stackDepth++;
|
||||
|
||||
if (((mSelection.mDepth == -1) && (startTick >= mSelection.mTickStart)) ||
|
||||
((startTick == mSelection.mTickStart) && (stackPos == mSelection.mDepth)))
|
||||
{
|
||||
isRecording = true;
|
||||
}
|
||||
|
||||
if (isRecording)
|
||||
{
|
||||
BPPerfEntry entry;
|
||||
entry.mTickStart = startTick;
|
||||
entry.mZoneNameId = strIdx;
|
||||
entry.mChildTicks = 0;
|
||||
//stateCtx.MoveToParamData();
|
||||
entry.mParamsReadPos = stateCtx.ReadPos;
|
||||
entry.mPerfInfo = null;
|
||||
entry.mPerfInfo = mProfileCtx.GetPerfInfo(entry, stateCtx);
|
||||
entry.mPerfInfo.mStackCount++;
|
||||
entryStack.Add(entry);
|
||||
}
|
||||
case let .Leave(endTick):
|
||||
stackDepth--;
|
||||
if (isRecording)
|
||||
{
|
||||
let entry = entryStack.PopBack();
|
||||
entry.mPerfInfo.mStackCount--;
|
||||
|
||||
if ((entry.mTickStart == mSelection.mTickStart) && (stackDepth == mSelection.mDepth))
|
||||
{
|
||||
if (client.mCurTick == endTick)
|
||||
{
|
||||
mProfileCtx.mHasSelectionEndChanged = true;
|
||||
mSelection.mTickEnd = endTick;
|
||||
}
|
||||
}
|
||||
|
||||
int64 ticks = endTick - entry.mTickStart;
|
||||
|
||||
if (isManualSelection)
|
||||
{
|
||||
int64 tickStart = Math.Max(entry.mTickStart, mSelection.mTickStart);
|
||||
int64 tickEnd = Math.Min(endTick, mSelection.mTickEnd);
|
||||
ticks = tickEnd - tickStart;
|
||||
if (ticks <= 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
//PerfInfo* perfInfo = mProfileCtx.GetPerfInfo(entry, stateCtx);
|
||||
PerfInfo* perfInfo = entry.mPerfInfo;
|
||||
|
||||
bool isOld = ((entry.mTickStart <= streamData.mStartTick) && (stackDepth < stateCtx.mSplitCarryoverCount));
|
||||
|
||||
// Is this a duplicate spanned entry? If so, we don't add it's stats but we still
|
||||
// must process it as a parent to keep track of mChildTicks for new children
|
||||
if ((isFirstDrawn) || (!isOld))
|
||||
{
|
||||
perfInfo.mCount++;
|
||||
if (perfInfo.mStackCount != 0)
|
||||
{
|
||||
// Total time is already handled by outer scope
|
||||
}
|
||||
else
|
||||
perfInfo.mTicks += ticks;
|
||||
}
|
||||
|
||||
if (entryStack.Count > 0)
|
||||
{
|
||||
var prevEntry = entryStack[entryStack.Count - 1];
|
||||
|
||||
//bool prevIsOld = ((prevEntry.mTickStart <= streamData.mStartTick) && (stackDepth - 1 < stateCtx.mSplitCarryoverCount));
|
||||
//if ((isFirstDrawn) || (!prevIsOld))
|
||||
if (!isOld)
|
||||
{
|
||||
//PerfInfo* prevPerfInfo = mProfileCtx.GetPerfInfo(prevEntry, stateCtx);
|
||||
|
||||
PerfInfo* prevPerfInfo = prevEntry.mPerfInfo;
|
||||
prevPerfInfo.mChildTicks += ticks;
|
||||
|
||||
if (perfInfo.mStackCount != 0)
|
||||
{
|
||||
// We have an instance of ourselves on an outer scope, so time we thought was child time actually isn't
|
||||
perfInfo.mChildTicks -= ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isManualSelection)
|
||||
{
|
||||
if ((stackDepth == 0) && (endTick >= mSelection.mTickEnd))
|
||||
{
|
||||
if (endTick <= streamData.mSplitTick)
|
||||
break StreamLoop;
|
||||
}
|
||||
//isRecording = false;
|
||||
}
|
||||
|
||||
if (stackDepth <= mSelection.mDepth)
|
||||
{
|
||||
if (endTick <= streamData.mSplitTick)
|
||||
break StreamLoop;
|
||||
isRecording = false;
|
||||
}
|
||||
}
|
||||
case .EndOfStream:
|
||||
break CmdLoop;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
isFirstDrawn = false;
|
||||
|
||||
if (stopwatch.ElapsedMilliseconds > (int)(gApp.mTimePerFrame * 1000))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
mProfileCtx.mDone = true;
|
||||
|
||||
for (var value in mProfileCtx.mPerfDict.Keys)
|
||||
{
|
||||
var perfInfo = @value.Value;
|
||||
Debug.Assert(perfInfo.mName != null);
|
||||
mProfileCtx.mResults.Add(perfInfo);
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
void RefreshData()
|
||||
{
|
||||
mListView.GetRoot().Clear();
|
||||
|
||||
if (mPerfView == null)
|
||||
return;
|
||||
|
||||
var session = mPerfView.mSession;
|
||||
|
||||
delete mProfileCtx;
|
||||
mProfileCtx = new ProfileContext();
|
||||
mProfileCtx.mFormatStrings = mFormatCheckbox.Checked;
|
||||
mProfileCtx.mSession = session;
|
||||
}
|
||||
|
||||
public void ItemClicked(ProfileListViewItem clickedItem, int32 btn, int32 btnCount, float x, float y)
|
||||
{
|
||||
if (clickedItem.mParentItem == null)
|
||||
return;
|
||||
ProfileListViewItem item = (ProfileListViewItem)clickedItem.GetSubItem(0);
|
||||
mListView.GetRoot().SelectItemExclusively(item);
|
||||
mListView.SetFocus();
|
||||
|
||||
if (btn == 1)
|
||||
{
|
||||
Menu menu = new Menu();
|
||||
|
||||
var menuItem = menu.AddItem("Find Instances");
|
||||
menuItem.mOnMenuItemSelected.Add(new (selectedItem) =>
|
||||
{
|
||||
var find = gApp.mFindPanel;
|
||||
var str = scope String();
|
||||
var client = mPerfView.mSession;
|
||||
|
||||
var thread = client.mThreads[mSelection.mThreadIdx];
|
||||
|
||||
str.Clear();
|
||||
var perfInfo = mProfileCtx.mResults[item.mVirtualIdx];
|
||||
str.Append('=');
|
||||
str.Append(perfInfo.mName);
|
||||
if (str.Contains(' '))
|
||||
{
|
||||
str.Insert(1, '\"');
|
||||
str.Append('"');
|
||||
}
|
||||
|
||||
if (mSelection.mDepth > 0)
|
||||
{
|
||||
str.Append(" Depth>");
|
||||
mSelection.mDepth.ToString(str);
|
||||
}
|
||||
find.mEntryEdit.SetText(str);
|
||||
|
||||
str.Clear();
|
||||
str.Append('=');
|
||||
thread.GetName(str);
|
||||
if (str.Contains(' '))
|
||||
{
|
||||
str.Insert(1, '\"');
|
||||
str.Append('"');
|
||||
}
|
||||
find.mTrackEdit.mEditWidget.SetText(str);
|
||||
|
||||
str.Clear();
|
||||
client.TicksToStr(mSelection.mTickStart, str);
|
||||
find.mTimeFromEdit.mEditWidget.SetText(str);
|
||||
|
||||
str.Clear();
|
||||
client.TicksToStr(mSelection.mTickEnd, str);
|
||||
find.mTimeToEdit.mEditWidget.SetText(str);
|
||||
|
||||
find.mFormatCheckbox.Checked = true;
|
||||
find.mZonesCheckbox.Checked = true;
|
||||
find.mEventsCheckbox.Checked = false;
|
||||
|
||||
find.mNeedsRestartSearch = true;
|
||||
});
|
||||
|
||||
menu.AddItem();
|
||||
mListView.AddStaticMenu(menu);
|
||||
|
||||
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
|
||||
menuWidget.Init(clickedItem, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public void ValueClicked(MouseEvent theEvent)
|
||||
{
|
||||
DarkVirtualListViewItem clickedItem = (DarkVirtualListViewItem)theEvent.mSender;
|
||||
DarkVirtualListViewItem item = (DarkVirtualListViewItem)clickedItem.GetSubItem(0);
|
||||
|
||||
mListView.GetRoot().SelectItemExclusively(item);
|
||||
mListView.SetFocus();
|
||||
|
||||
if ((theEvent.mBtn == 0) && (theEvent.mBtnCount > 1))
|
||||
{
|
||||
for (int32 childIdx = 1; childIdx < mListView.GetRoot().GetChildCount(); childIdx++)
|
||||
{
|
||||
var checkListViewItem = mListView.GetRoot().GetChildAtIndex(childIdx);
|
||||
checkListViewItem.IconImage = null;
|
||||
}
|
||||
|
||||
/*int32 selectedIdx = item.mVirtualIdx;
|
||||
var foundEntry = mSearchState.mFoundEntries[selectedIdx];
|
||||
mPerfView.ZoomTo(foundEntry.mStartTick, foundEntry.mEndTick);
|
||||
BPSelection selection;
|
||||
selection.mStartTick = foundEntry.mStartTick;
|
||||
selection.mEndTick = foundEntry.mEndTick;
|
||||
selection.mDepth = foundEntry.mDepth;
|
||||
selection.mThreadIdx = foundEntry.mTrackIdx;
|
||||
|
||||
mPerfView.mSelection = selection;*/
|
||||
}
|
||||
|
||||
if (theEvent.mBtn == 1)
|
||||
{
|
||||
Menu menu = new Menu();
|
||||
|
||||
#unwarn
|
||||
var menuItem = menu.AddItem("Set Track Color ...");
|
||||
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
|
||||
menuWidget.Init(this, theEvent.mX, theEvent.mY);
|
||||
}
|
||||
}
|
||||
|
||||
int EntryCompare(PerfInfo* lhs, PerfInfo* rhs)
|
||||
{
|
||||
int64 result = 0;
|
||||
if (mListView.mSortType.mColumn == 0)
|
||||
{
|
||||
result = String.Compare(lhs.mName, rhs.mName, true);
|
||||
if (result == 0)
|
||||
result = lhs.mTicks - rhs.mTicks;
|
||||
}
|
||||
else if (mListView.mSortType.mColumn == 1)
|
||||
{
|
||||
result = lhs.mCount - rhs.mCount;
|
||||
}
|
||||
else if (mListView.mSortType.mColumn == 2)
|
||||
{
|
||||
result = lhs.mTicks - rhs.mTicks;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (lhs.mTicks - lhs.mChildTicks) - (rhs.mTicks - rhs.mChildTicks);
|
||||
}
|
||||
if (mListView.mSortType.mReverse)
|
||||
result = -result;
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
void SortList()
|
||||
{
|
||||
mProfileCtx.mSortingResults.Sort(scope => EntryCompare);
|
||||
mSortDoneHandle.Set(true);
|
||||
}
|
||||
|
||||
void CheckSorting(int32 waitMS = 0)
|
||||
{
|
||||
if (mSorting)
|
||||
{
|
||||
if (mSortDoneHandle.WaitFor(waitMS))
|
||||
{
|
||||
mSorting = false;
|
||||
mSortDoneHandle.Reset();
|
||||
|
||||
Debug.Assert(mProfileCtx.mResults.Count == mProfileCtx.mSortingResults.Count);
|
||||
Swap!(mProfileCtx.mResults, mProfileCtx.mSortingResults);
|
||||
|
||||
mListView.GetRoot().Clear();
|
||||
|
||||
if (mProfileCtx.mResults.Count > 0)
|
||||
{
|
||||
var listViewItem = (DarkVirtualListViewItem)mListView.GetRoot().CreateChildItem();
|
||||
listViewItem.mVirtualHeadItem = listViewItem;
|
||||
listViewItem.mVirtualCount = (int32)mProfileCtx.mResults.Count;
|
||||
mListView.PopulateVirtualItem(listViewItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshList()
|
||||
{
|
||||
if (mPerfView == null)
|
||||
{
|
||||
mListView.GetRoot().Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
FinishSorting();
|
||||
|
||||
mSorting = true;
|
||||
|
||||
mProfileCtx.mSortingResults.Clear();
|
||||
mProfileCtx.mSortingResults.GrowUnitialized(mProfileCtx.mResults.Count);
|
||||
for (int i < mProfileCtx.mResults.Count)
|
||||
mProfileCtx.mSortingResults[i] = mProfileCtx.mResults[i];
|
||||
|
||||
ThreadPool.QueueUserWorkItem(new => SortList);
|
||||
CheckSorting(20);
|
||||
|
||||
/*for (var strId in iList)
|
||||
{
|
||||
var childItem = mListView.GetRoot().CreateChildItem();
|
||||
childItem.Label = strId;
|
||||
|
||||
var perfInfo = ref perfDict[strId];
|
||||
|
||||
var subItem = childItem.CreateSubItem(1);
|
||||
subItem.mLabel = new String();
|
||||
subItem.mLabel.FormatInto("{0}", perfInfo.mCount);
|
||||
subItem.mMouseDownHandler.Add(new => ValueClicked);
|
||||
|
||||
subItem = childItem.CreateSubItem(2);
|
||||
subItem.mLabel = new String();
|
||||
client.ElapsedTicksToStr(perfInfo.mTicks, subItem.mLabel);
|
||||
subItem.mMouseDownHandler.Add(new => ValueClicked);
|
||||
|
||||
subItem = childItem.CreateSubItem(3);
|
||||
subItem.mLabel = new String();
|
||||
client.ElapsedTicksToStr(perfInfo.mTicks - perfInfo.mChildTicks, subItem.mLabel);
|
||||
subItem.mMouseDownHandler.Add(new => ValueClicked);
|
||||
}*/
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (mPerfView == null)
|
||||
return;
|
||||
|
||||
// Were we awaiting more data to refresh
|
||||
var client = mPerfView.mSession;
|
||||
if ((mActiveLastCurTick != 0) && (mActiveLastCurTick != client.mCurTick))
|
||||
{
|
||||
mActiveLastCurTick = 0;
|
||||
mSelectionDirty = true;
|
||||
}
|
||||
|
||||
if (mSorting)
|
||||
{
|
||||
CheckSorting();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((mSelectionDirty) && (gApp.mIsUpdateBatchStart) &&
|
||||
((mProfileCtx == null) || (mProfileCtx.mDone)))
|
||||
{
|
||||
mSelectionDirty = false;
|
||||
RefreshData();
|
||||
}
|
||||
|
||||
if ((mProfileCtx != null) && (!mProfileCtx.mDone))
|
||||
{
|
||||
if (gApp.mIsUpdateBatchStart)
|
||||
UpdateProfileCtx();
|
||||
|
||||
if (mProfileCtx.mDone)
|
||||
{
|
||||
if (mProfileCtx.mHasSelectionEndChanged)
|
||||
{
|
||||
mActiveLastCurTick = client.mCurTick;
|
||||
}
|
||||
else
|
||||
mActiveLastCurTick = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
mListView.GetRoot().Clear();
|
||||
DeleteAndNullify!(mProfileCtx);
|
||||
mActiveLastCurTick = 0;
|
||||
mPerfView = null;
|
||||
}
|
||||
}
|
||||
}
|
24
BeefTools/BeefPerf/src/Program.bf
Normal file
24
BeefTools/BeefPerf/src/Program.bf
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static int32 Main(String[] args)
|
||||
{
|
||||
BPApp mApp = new BPApp();
|
||||
mApp.ParseCommandLine(args);
|
||||
if (!mApp.mShuttingDown)
|
||||
{
|
||||
mApp.Init();
|
||||
mApp.Run();
|
||||
mApp.Shutdown();
|
||||
}
|
||||
bool failed = mApp.mFailed;
|
||||
delete mApp;
|
||||
return failed ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
543
BeefTools/BeefPerf/src/ScriptManager.bf
Normal file
543
BeefTools/BeefPerf/src/ScriptManager.bf
Normal file
|
@ -0,0 +1,543 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using Beefy;
|
||||
using Beefy.widgets;
|
||||
using Beefy.theme.dark;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
[AttributeUsage(.Method, .ReflectAttribute | .AlwaysIncludeTarget, ReflectUser=.All)]
|
||||
struct BpCommandAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ScriptManager
|
||||
{
|
||||
class Target
|
||||
{
|
||||
public class Cmd
|
||||
{
|
||||
public MethodInfo mMethodInfo;
|
||||
public Object mTargetObject;
|
||||
}
|
||||
|
||||
public Dictionary<String, Target> mTargets = new .() ~ DeleteDictionyAndKeysAndItems!(_);
|
||||
public Dictionary<String, Cmd> mCmds = new .() ~ DeleteDictionyAndKeysAndItems!(_);
|
||||
}
|
||||
|
||||
public class QueuedCmd
|
||||
{
|
||||
public bool mHandled = true;
|
||||
public String mCmd ~ delete _;
|
||||
public String mSrcFile ~ delete _;
|
||||
public int mLineNum = -1;
|
||||
|
||||
public int mIntParam;
|
||||
public bool mNoWait;
|
||||
}
|
||||
|
||||
ScriptHelper mScriptHelper = new ScriptHelper() ~ delete _;
|
||||
Target mRoot = new Target() ~ delete _;
|
||||
Dictionary<String, Variant> mVars = new .() ~
|
||||
{
|
||||
for (var kv in _)
|
||||
{
|
||||
delete kv.key;
|
||||
kv.value.Dispose();
|
||||
}
|
||||
delete _;
|
||||
};
|
||||
List<QueuedCmd> mCmdList = new .() ~ DeleteContainerAndItems!(_);
|
||||
bool mFailed;
|
||||
public QueuedCmd mCurCmd;
|
||||
public Stopwatch mTimeoutStopwatch ~ delete _;
|
||||
public int mTimeoutMS;
|
||||
public String mExpectingError ~ delete _;
|
||||
public bool mHadExpectingError;
|
||||
public int mDoneTicks;
|
||||
public bool mRunningCommand;
|
||||
|
||||
public bool Failed
|
||||
{
|
||||
get
|
||||
{
|
||||
return mFailed;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasQueuedCommands
|
||||
{
|
||||
get
|
||||
{
|
||||
return !mCmdList.IsEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
AddTarget(mScriptHelper);
|
||||
|
||||
//Exec("OutputLine(\"Hey bro!\", 2)");
|
||||
}
|
||||
|
||||
public void MarkNotHandled()
|
||||
{
|
||||
if (mCurCmd != null)
|
||||
mCurCmd.mHandled = false;
|
||||
}
|
||||
|
||||
public void AddTarget(Object targetObject)
|
||||
{
|
||||
var targetType = targetObject.GetType();
|
||||
for (var methodInfo in targetType.GetMethods(.Instance | .Public | .NonPublic))
|
||||
{
|
||||
var methodName = methodInfo.Name;
|
||||
if (methodName.StartsWith("Cmd_"))
|
||||
methodName = .(methodName, 4);
|
||||
|
||||
Target curTarget = mRoot;
|
||||
for (var cmdPart in methodName.Split('_'))
|
||||
{
|
||||
if (@cmdPart.HasMore)
|
||||
{
|
||||
String* keyPtr;
|
||||
Target* targetPtr;
|
||||
if (curTarget.mTargets.TryAdd(scope String(cmdPart), out keyPtr, out targetPtr))
|
||||
{
|
||||
*keyPtr = new String(cmdPart);
|
||||
*targetPtr = new Target();
|
||||
}
|
||||
curTarget = *targetPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
String* keyPtr;
|
||||
Target.Cmd* cmdPtr;
|
||||
if (curTarget.mCmds.TryAdd(scope String(cmdPart), out keyPtr, out cmdPtr))
|
||||
{
|
||||
*keyPtr = new String(cmdPart);
|
||||
*cmdPtr = new .();
|
||||
|
||||
let cmd = *cmdPtr;
|
||||
cmd.mMethodInfo = methodInfo;
|
||||
cmd.mTargetObject = targetObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fail(StringView err)
|
||||
{
|
||||
if (mFailed)
|
||||
return;
|
||||
|
||||
mFailed = true;
|
||||
var errStr = scope String(err);
|
||||
if (mCurCmd != null)
|
||||
{
|
||||
errStr.AppendF(" at line {0} in {1}\n\t{2}", mCurCmd.mLineNum + 1, mCurCmd.mSrcFile, mCurCmd.mCmd);
|
||||
}
|
||||
gApp.Fail(errStr);
|
||||
}
|
||||
|
||||
public void Fail(StringView fmt, params Object[] args)
|
||||
{
|
||||
Fail(scope String()..AppendF(fmt, params args));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
DeleteAndClearItems!(mCmdList);
|
||||
}
|
||||
|
||||
public void QueueCommandFile(StringView filePath)
|
||||
{
|
||||
int lineNum = 0;
|
||||
|
||||
let streamReader = scope StreamReader();
|
||||
if (streamReader.Open(filePath) case .Err)
|
||||
{
|
||||
Fail("Unable to open command file '{0}'", filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var lineResult in streamReader.Lines)
|
||||
{
|
||||
switch (lineResult)
|
||||
{
|
||||
case .Ok(var line):
|
||||
line.Trim();
|
||||
if ((!line.IsEmpty) && (!line.StartsWith("#")))
|
||||
{
|
||||
QueuedCmd queuedCmd = new .();
|
||||
if (line.StartsWith("nowait "))
|
||||
{
|
||||
queuedCmd.mNoWait = true;
|
||||
line.RemoveFromStart("no wait".Length);
|
||||
}
|
||||
|
||||
queuedCmd.mCmd = new String(line);
|
||||
queuedCmd.mSrcFile = new String(filePath);
|
||||
queuedCmd.mLineNum = lineNum;
|
||||
mCmdList.Add(queuedCmd);
|
||||
}
|
||||
lineNum++;
|
||||
case .Err:
|
||||
Fail("Failed reading from file '{0}'", filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTimeoutMS(int timeoutMS)
|
||||
{
|
||||
if (mTimeoutStopwatch == null)
|
||||
{
|
||||
mTimeoutStopwatch = new .();
|
||||
mTimeoutStopwatch.Start();
|
||||
}
|
||||
mTimeoutMS = timeoutMS;
|
||||
}
|
||||
|
||||
public void Exec(StringView cmdLineView)
|
||||
{
|
||||
var cmdLineView;
|
||||
cmdLineView.Trim();
|
||||
|
||||
if ((cmdLineView.StartsWith("#")) || (cmdLineView.IsEmpty))
|
||||
return;
|
||||
|
||||
bool inQuote = false;
|
||||
for (int i < cmdLineView.Length - 1)
|
||||
{
|
||||
char8 c = cmdLineView[i];
|
||||
if (!inQuote)
|
||||
{
|
||||
if (c == '"')
|
||||
inQuote = true;
|
||||
else if (c == ';')
|
||||
{
|
||||
Exec(.(cmdLineView, 0, i));
|
||||
cmdLineView.RemoveFromStart(i + 1);
|
||||
cmdLineView.Trim();
|
||||
i = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '\\')
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (c == '"')
|
||||
{
|
||||
inQuote = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringView varName = .();
|
||||
|
||||
/*int eqPos = cmdLineView.IndexOf('=');
|
||||
if (eqPos != -1)
|
||||
{
|
||||
varName = StringView(cmdLineView, eqPos);
|
||||
varName.Trim();
|
||||
cmdLineView.RemoveFromStart(eqPos + 1);
|
||||
cmdLineView.Clear();
|
||||
}*/
|
||||
|
||||
StringView methodName;
|
||||
List<Object> args = scope .();
|
||||
|
||||
int parenPos = cmdLineView.IndexOf('(');
|
||||
if (parenPos != -1)
|
||||
{
|
||||
methodName = cmdLineView.Substring(0, parenPos);
|
||||
methodName.Trim();
|
||||
|
||||
int endParenPos = cmdLineView.LastIndexOf(')');
|
||||
if (endParenPos == -1)
|
||||
{
|
||||
Fail("Missing argument end ')'");
|
||||
return;
|
||||
}
|
||||
|
||||
var postStr = StringView(cmdLineView, endParenPos + 1);
|
||||
postStr.Trim();
|
||||
if ((!postStr.IsEmpty) && (!postStr.StartsWith("#")))
|
||||
{
|
||||
Fail("Invalid string following command");
|
||||
return;
|
||||
}
|
||||
|
||||
bool isLiteralString = false;
|
||||
bool inQuotes = false;
|
||||
int startIdx = parenPos;
|
||||
for (int idx = parenPos; idx <= endParenPos; idx++)
|
||||
{
|
||||
char8 c = cmdLineView[idx];
|
||||
if ((c == '\\') && (!isLiteralString))
|
||||
{
|
||||
// Skip past slashed strings
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
else if (c == '"')
|
||||
{
|
||||
if (!inQuotes)
|
||||
{
|
||||
isLiteralString = ((idx > 0) && (cmdLineView[idx - 1] == '@'));
|
||||
}
|
||||
inQuotes ^= true;
|
||||
}
|
||||
else if (((c == ',') || (c == ')')) && (!inQuotes))
|
||||
{
|
||||
StringView argView = cmdLineView.Substring(startIdx + 1, idx - startIdx - 1);
|
||||
|
||||
argView.Trim();
|
||||
if (argView.IsEmpty)
|
||||
continue;
|
||||
|
||||
if ((argView.StartsWith("\"")) || (isLiteralString))
|
||||
{
|
||||
var str = scope::String();
|
||||
if (argView.UnQuoteString(str) case .Err)
|
||||
Fail("Failed to unquote string");
|
||||
args.Add(str);
|
||||
isLiteralString = false;
|
||||
}
|
||||
else if (argView.EndsWith('f'))
|
||||
{
|
||||
switch (float.Parse(argView))
|
||||
{
|
||||
case .Ok(let val):
|
||||
args.Add(scope:: box val);
|
||||
case .Err:
|
||||
Fail("Failed to parse float");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (argView.Contains('.'))
|
||||
{
|
||||
switch (double.Parse(argView))
|
||||
{
|
||||
case .Ok(let val):
|
||||
args.Add(scope:: box val);
|
||||
case .Err:
|
||||
Fail("Failed to parse double");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // Integer
|
||||
{
|
||||
switch (int.Parse(argView))
|
||||
{
|
||||
case .Ok(let val):
|
||||
args.Add(scope:: box val);
|
||||
case .Err:
|
||||
Fail("Failed to parse int");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
startIdx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
/*for (var argView in cmdLineView.Substring(parenPos + 1, endParenPos - parenPos - 1).Split(','))
|
||||
{
|
||||
HandleArg(argView);
|
||||
}*/
|
||||
}
|
||||
else
|
||||
{
|
||||
methodName = cmdLineView;
|
||||
}
|
||||
|
||||
Target curTarget = mRoot;
|
||||
for (var cmdPart in methodName.Split('.'))
|
||||
{
|
||||
if (@cmdPart.HasMore)
|
||||
{
|
||||
if (!curTarget.mTargets.TryGetValue(scope String(cmdPart), out curTarget))
|
||||
{
|
||||
Fail("Unable to find target '{0}'", cmdPart);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool prevRunningCommand = mRunningCommand;
|
||||
mRunningCommand = true;
|
||||
defer { mRunningCommand = prevRunningCommand; }
|
||||
|
||||
Target.Cmd cmd;
|
||||
if (!curTarget.mCmds.TryGetValue(scope String(cmdPart), out cmd))
|
||||
{
|
||||
Fail("Unable to find command '{0}'", cmdPart);
|
||||
return;
|
||||
}
|
||||
Object[] argsArr = scope Object[args.Count];
|
||||
args.CopyTo(argsArr);
|
||||
switch (cmd.mMethodInfo.Invoke(cmd.mTargetObject, params argsArr))
|
||||
{
|
||||
case .Err:
|
||||
Fail("Failed to invoke command");
|
||||
return;
|
||||
case .Ok(var result):
|
||||
if (!varName.IsEmpty)
|
||||
{
|
||||
String* keyPtr;
|
||||
Variant* valuePtr;
|
||||
if (mVars.TryAdd(scope String(varName), out keyPtr, out valuePtr))
|
||||
*keyPtr = new String(varName);
|
||||
else
|
||||
valuePtr.Dispose();
|
||||
*valuePtr = result;
|
||||
}
|
||||
else
|
||||
result.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (mFailed)
|
||||
return;
|
||||
|
||||
/*if ((mTimeoutMS > 0) && (gApp.mRunningTestScript))
|
||||
{
|
||||
if (mTimeoutStopwatch.ElapsedMilliseconds >= mTimeoutMS)
|
||||
Fail("Script has timed out: {0}ms", mTimeoutStopwatch.ElapsedMilliseconds);
|
||||
}*/
|
||||
|
||||
while ((!mCmdList.IsEmpty) && (!mFailed))
|
||||
{
|
||||
mCurCmd = mCmdList[0];
|
||||
mCurCmd.mHandled = true;
|
||||
if (!mCurCmd.mNoWait)
|
||||
{
|
||||
if (!mScriptHelper.IsPaused())
|
||||
break;
|
||||
// Only do a wait for the initial execution
|
||||
// This is required for things like AssertEvalEquals that will be handled internally by repeated
|
||||
// calls where 'mHandled = false' is set
|
||||
mCurCmd.mNoWait = true;
|
||||
}
|
||||
|
||||
Exec(mCurCmd.mCmd);
|
||||
|
||||
if (mCmdList.IsEmpty)
|
||||
break;
|
||||
|
||||
if (!mCurCmd.mHandled)
|
||||
break; // Try again next update
|
||||
|
||||
mCmdList.RemoveAt(0);
|
||||
delete mCurCmd;
|
||||
mCurCmd = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptHelper
|
||||
{
|
||||
public EditWidgetContent.LineAndColumn mMarkedPos;
|
||||
|
||||
void FixFilePath(String fileName, String outFilePath)
|
||||
{
|
||||
outFilePath.Append(fileName);
|
||||
if (File.Exists(outFilePath))
|
||||
return;
|
||||
|
||||
outFilePath.Clear();
|
||||
Path.GetAbsolutePath(fileName, gApp.mInstallDir, outFilePath);
|
||||
|
||||
if (!File.Exists(outFilePath))
|
||||
{
|
||||
gApp.mScriptManager.Fail("Unable to locate file '{0}'", outFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPaused()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void Nop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void Close()
|
||||
{
|
||||
gApp.Stop();
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void CloseIfAutoOpened()
|
||||
{
|
||||
if (gApp.mIsAutoOpened)
|
||||
gApp.Stop();
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void SelectLastSession()
|
||||
{
|
||||
if (gApp.mSessions.IsEmpty)
|
||||
{
|
||||
gApp.Fail("No sessions recorded");
|
||||
return;
|
||||
}
|
||||
gApp.SetSession(gApp.mSessions.Back);
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void SaveSession(String outPath)
|
||||
{
|
||||
if (gApp.mCurSession == null)
|
||||
{
|
||||
gApp.Fail("No session selected");
|
||||
return;
|
||||
}
|
||||
if (gApp.mCurSession.Save(outPath) case .Err)
|
||||
gApp.mScriptManager.Fail("Unable to save file '{0}'", outPath);
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void SaveEntrySummary(String entryName, String outPath)
|
||||
{
|
||||
if (gApp.mCurSession == null)
|
||||
{
|
||||
gApp.Fail("No session selected");
|
||||
return;
|
||||
}
|
||||
gApp.mBoard.mPerfView.SaveEntrySummary(entryName, outPath);
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void Stop()
|
||||
{
|
||||
gApp.mScriptManager.Clear();
|
||||
}
|
||||
|
||||
[BpCommand]
|
||||
public void Exit()
|
||||
{
|
||||
gApp.Stop();
|
||||
}
|
||||
}
|
||||
}
|
80
BeefTools/BeefPerf/src/StatusBar.bf
Normal file
80
BeefTools/BeefPerf/src/StatusBar.bf
Normal file
|
@ -0,0 +1,80 @@
|
|||
using Beefy.widgets;
|
||||
using Beefy.theme.dark;
|
||||
using System;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class StatusBar : Widget
|
||||
{
|
||||
public double mShowTime;
|
||||
public double mSelTime;
|
||||
public int64 mSelTick;
|
||||
|
||||
public override void Draw(Beefy.gfx.Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
uint32 bkgColor = 0xFF404040;
|
||||
using (g.PushColor(bkgColor))
|
||||
g.FillRect(0, 0, mWidth, mHeight);
|
||||
|
||||
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
|
||||
g.DrawString(scope String..AppendF("FPS: {0}", gApp.mLastFPS), 4, 0);
|
||||
|
||||
//
|
||||
g.DrawString(scope String..AppendF("{0}", gApp.mUpdateCnt), 0, 0, .Right, mWidth - 8);
|
||||
|
||||
float curY = 64;
|
||||
if (!gApp.mListenSocket.IsConnected)
|
||||
{
|
||||
using (g.PushColor(0xFFFF4040))
|
||||
g.DrawString(scope String..AppendF("Failed to listen on port {0}", gApp.mListenPort), 0, 0, .Right, mWidth - 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.DrawString(scope String..AppendF("Clients: {0}", gApp.mClients.Count), curY, 0);
|
||||
curY += 64;
|
||||
|
||||
float kPerSec = gApp.mStatBytesPerSec / 1024.0f;
|
||||
if ((kPerSec > 0) && (kPerSec < 0.1f))
|
||||
kPerSec = 0.1f;
|
||||
g.DrawString(scope String..AppendF("BPS: {0:0.0}k", kPerSec), curY, 0);
|
||||
curY += 80;
|
||||
}
|
||||
|
||||
if (gApp.mBoard.mPerfView != null)
|
||||
{
|
||||
var client = gApp.mBoard.mPerfView.mSession;
|
||||
g.DrawString(scope String..AppendF("Zones: {0}", (int32)client.mNumZones), curY, 0);
|
||||
curY += 118;
|
||||
|
||||
var str = scope String();
|
||||
BpClient.TimeToStr(client.TicksToUS(client.mCurTick - client.mFirstTick), str);
|
||||
g.DrawString(str, curY, 0);
|
||||
curY += 108;
|
||||
}
|
||||
|
||||
if (mShowTime != 0)
|
||||
{
|
||||
var str = scope String();
|
||||
BpClient.TimeToStr(mShowTime, str);
|
||||
g.DrawString(str, curY, 0);
|
||||
curY += 108;
|
||||
}
|
||||
|
||||
if (mSelTime != 0)
|
||||
{
|
||||
var str = scope String();
|
||||
BpClient.ElapsedTimeToStr(mSelTime, str);
|
||||
g.DrawString(str, curY, 0);
|
||||
curY += 108;
|
||||
}
|
||||
|
||||
/*{
|
||||
var str = scope String();
|
||||
str.FormatInto("{0}", mSelTick);
|
||||
g.DrawString(str, 550, 0);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
234
BeefTools/BeefPerf/src/WorkspacePanel.bf
Normal file
234
BeefTools/BeefPerf/src/WorkspacePanel.bf
Normal file
|
@ -0,0 +1,234 @@
|
|||
using Beefy.widgets;
|
||||
using Beefy.gfx;
|
||||
using Beefy.theme.dark;
|
||||
using System;
|
||||
|
||||
namespace BeefPerf
|
||||
{
|
||||
class WorkspaceWidget : Widget
|
||||
{
|
||||
DarkEditWidget mClientEdit;
|
||||
DarkEditWidget mSessionEdit;
|
||||
DarkCheckBox mEnableCB;
|
||||
|
||||
int EntriesYStart
|
||||
{
|
||||
get
|
||||
{
|
||||
return GS!(110);
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
mEnableCB = new DarkCheckBox();
|
||||
mEnableCB.Label = "Allow Connections";
|
||||
mEnableCB.Checked = gApp.Listening;
|
||||
mEnableCB.mOnValueChanged.Add(new () =>
|
||||
{
|
||||
gApp.Listening = mEnableCB.Checked;
|
||||
mEnableCB.Checked = gApp.Listening;
|
||||
});
|
||||
AddWidget(mEnableCB);
|
||||
|
||||
mClientEdit = new DarkEditWidget();
|
||||
AddWidget(mClientEdit);
|
||||
|
||||
mSessionEdit = new DarkEditWidget();
|
||||
AddWidget(mSessionEdit);
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
g.DrawString("Client Name Filter", mClientEdit.mX, mClientEdit.mY - GS!(20));
|
||||
g.DrawString("Session Name Filter", mSessionEdit.mX, mSessionEdit.mY - GS!(20));
|
||||
|
||||
|
||||
/*using (g.PushColor(0xFFFF0000))
|
||||
g.FillRect(0, 0, mWidth, mHeight);
|
||||
|
||||
using (g.PushColor(0xFFFF00FF))
|
||||
g.FillRect(0, 0, mWidth, 20);*/
|
||||
|
||||
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
|
||||
|
||||
float curY = EntriesYStart;
|
||||
float boxHeight = 60;
|
||||
for (int clientIdx = gApp.mSessions.Count - 1; clientIdx >= 0; clientIdx--)
|
||||
{
|
||||
var session = gApp.mSessions[clientIdx];
|
||||
using (g.PushTranslate(8, curY))
|
||||
{
|
||||
float width = Math.Max(mParent.mWidth - 16, 140);
|
||||
g.DrawBox(DarkTheme.sDarkTheme.GetImage(.Bkg), 0, 0, width, boxHeight);
|
||||
|
||||
if (gApp.mCurSession == session)
|
||||
{
|
||||
using (g.PushColor(DarkTheme.COLOR_SELECTED_OUTLINE))
|
||||
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Outline), 0, 0, width, boxHeight);
|
||||
}
|
||||
|
||||
var sessionName = scope String();
|
||||
if (session.mSessionName != null)
|
||||
sessionName.Append(session.mSessionName);
|
||||
if (sessionName.IsWhiteSpace)
|
||||
sessionName.Append("<Unnamed Session>");
|
||||
|
||||
var clientName = scope String();
|
||||
if (session.mClientName != null)
|
||||
clientName.Append(session.mClientName);
|
||||
if (clientName.IsWhiteSpace)
|
||||
clientName.Append("<Unnamed Client>");
|
||||
|
||||
//clientName.Append("This makes it a long string!");
|
||||
//sessionName.Append("LONG!!!!");
|
||||
float clientNameWidth = Math.Min(g.mFont.GetWidth(clientName), width * 0.5f);
|
||||
|
||||
g.DrawString(sessionName, 4, 2, .Left, width - clientNameWidth - GS!(12), .Ellipsis);
|
||||
g.DrawString(clientName, width - clientNameWidth - 4, 2, .Left, clientNameWidth, .Ellipsis);
|
||||
|
||||
if (!session.mSessionOver)
|
||||
{
|
||||
//using (g.PushColor(((mUpdateCnt / 20) % 2 == 0) ? 0xFFFFFFFF : 0xD0FFFFFF))
|
||||
//g.Draw(DarkTheme.sDarkTheme.GetImage(.RedDot), width - 20, 0);
|
||||
|
||||
using (g.PushColor(((mUpdateCnt / 20) % 2 == 0) ? 0xFFFF0000 : 0xD0FF0000))
|
||||
{
|
||||
//g.Draw(DarkTheme.sDarkTheme.GetImage(.RedDot), width - 20, 0);
|
||||
g.FillRect(-3, 0, 5, boxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
var startTime = scope String();
|
||||
session.mConnectTime.ToString(startTime, "dddd, MMMM d, yyyy @ h:mm:ss tt");
|
||||
g.DrawString(startTime, 4, 20, .Left, width - 8, .Ellipsis);
|
||||
|
||||
String timeStr = scope String();
|
||||
double timeUS = session.TicksToUS(session.mCurTick - session.mFirstTick);
|
||||
BpClient.TimeToStr(timeUS, timeStr, false);
|
||||
g.DrawString(timeStr, 4, 40, .Left, width - 8, .Ellipsis);
|
||||
|
||||
g.DrawString(scope String..AppendF("BPS: {0}k", session.mDispBPS), 4, 40, .Right, width - 8);
|
||||
}
|
||||
|
||||
curY += boxHeight + 4;
|
||||
}
|
||||
}
|
||||
|
||||
BpSession GetSessionIdxAt(float x, float y)
|
||||
{
|
||||
if ((x < 8) || (x > mWidth - 8))
|
||||
return null;
|
||||
|
||||
int32 boxHeight = 60;
|
||||
int32 spacing = boxHeight + 4;
|
||||
int yStart = EntriesYStart;
|
||||
if (y < yStart)
|
||||
return null;
|
||||
|
||||
int idx = (int32)(y - yStart) / spacing;
|
||||
int ofs = (int32)(y - yStart) % spacing;
|
||||
|
||||
if (idx >= gApp.mSessions.Count)
|
||||
return null;
|
||||
if (ofs > boxHeight)
|
||||
return null;
|
||||
return gApp.mSessions[gApp.mSessions.Count - idx - 1];
|
||||
}
|
||||
|
||||
public float GetWantHeight()
|
||||
{
|
||||
int32 boxHeight = 60;
|
||||
int32 spacing = boxHeight + 4;
|
||||
return EntriesYStart + spacing * gApp.mSessions.Count;
|
||||
}
|
||||
|
||||
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
||||
{
|
||||
base.MouseDown(x, y, btn, btnCount);
|
||||
|
||||
var session = GetSessionIdxAt(x, y);
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
gApp.SetSession(session);
|
||||
}
|
||||
|
||||
public override void Resize(float x, float y, float width, float height)
|
||||
{
|
||||
base.Resize(x, y, width, height);
|
||||
ResizeComponents();
|
||||
}
|
||||
|
||||
public void ResizeComponents()
|
||||
{
|
||||
mEnableCB.Resize(4, 0, mWidth - 8, 20);
|
||||
mClientEdit.Resize(4, 40, mWidth - 8, 20);
|
||||
mSessionEdit.Resize(4, 80, mWidth - 8, 20);
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspacePanel : Widget
|
||||
{
|
||||
ScrollableWidget mScrollableWidget;
|
||||
WorkspaceWidget mWorkspaceWidget;
|
||||
|
||||
public this()
|
||||
{
|
||||
mWorkspaceWidget = new WorkspaceWidget();
|
||||
mWorkspaceWidget.Resize(0, 0, 200, 200);
|
||||
|
||||
mScrollableWidget = new ScrollableWidget();
|
||||
mScrollableWidget.InitScrollbars(false, true);
|
||||
mScrollableWidget.mScrollContent = mWorkspaceWidget;
|
||||
mScrollableWidget.mScrollContentContainer.AddWidget(mWorkspaceWidget);
|
||||
AddWidget(mScrollableWidget);
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
//g.DrawBox(DarkTheme.sDarkTheme.GetImage(.Bkg), 0, 0, mWidth, mHeight);
|
||||
}
|
||||
|
||||
public override void Resize(float x, float y, float width, float height)
|
||||
{
|
||||
base.Resize(x, y, width, height);
|
||||
|
||||
mScrollableWidget.Resize(0, 0, width, height - 0);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
mWorkspaceWidget.mWidth = mWorkspaceWidget.mParent.mWidth;
|
||||
mWorkspaceWidget.mHeight = mWorkspaceWidget.GetWantHeight();
|
||||
}
|
||||
|
||||
public bool PassesFilter(BpClient client)
|
||||
{
|
||||
String clientFilter = scope .();
|
||||
mWorkspaceWidget.[Friend]mClientEdit.GetText(clientFilter);
|
||||
clientFilter.Trim();
|
||||
if (!clientFilter.IsEmpty)
|
||||
{
|
||||
if ((client.mClientName == null) || (!client.mClientName.Contains(clientFilter, true)))
|
||||
return false;
|
||||
}
|
||||
|
||||
String sessionFilter = scope .();
|
||||
mWorkspaceWidget.[Friend]mSessionEdit.GetText(sessionFilter);
|
||||
sessionFilter.Trim();
|
||||
if (!sessionFilter.IsEmpty)
|
||||
{
|
||||
if ((client.mSessionName == null) || (!client.mSessionName.Contains(sessionFilter, true)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue