1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-10 20:42:21 +02:00

Moved BeefPerf

This commit is contained in:
Brian Fiete 2019-09-04 11:39:56 -07:00
parent d28392f8fd
commit b3487d733b
26 changed files with 6 additions and 6458 deletions

View 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"

View file

@ -0,0 +1,6 @@
FileVersion = 1
Projects = {BeefPerf = {Path = "."}, Beefy2D = {Path = "../BeefLibs/Beefy2D"}}
Unlocked = ["corlib"]
[Workspace]
StartupProject = "BeefPerf"

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View 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>

View 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;
}

File diff suppressed because it is too large Load diff

View 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;
}
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}

File diff suppressed because it is too large Load diff

View 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 _;
}
}

View 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);
}
}
}

File diff suppressed because it is too large Load diff

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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);
}*/
}
}
}

View 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;
}
}
}