2
0

TimeRuler.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // TimeRuler.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.Threading;
  14. using System.Text;
  15. using Microsoft.Xna.Framework;
  16. using Microsoft.Xna.Framework.Graphics;
  17. #endregion
  18. namespace HoneycombRush.GameDebugTools
  19. {
  20. /// <summary>
  21. /// Realtime CPU measuring tool
  22. /// </summary>
  23. /// <remarks>
  24. /// You can visually find bottle neck, and know how much you can put more CPU jobs
  25. /// by using this tool.
  26. /// Because of this is real time profile, you can find glitches in the game too.
  27. ///
  28. /// TimeRuler provide the following features:
  29. /// * Up to 8 bars (Configurable)
  30. /// * Change colors for each markers
  31. /// * Marker logging.
  32. /// * It won't even generate BeginMark/EndMark method calls when you got rid of the
  33. /// TRACE constant.
  34. /// * It supports up to 32 (Configurable) nested BeginMark method calls.
  35. /// * Multithreaded safe
  36. /// * Automatically changes display frames based on frame duration.
  37. ///
  38. /// How to use:
  39. /// Added TimerRuler instance to Game.Components and call timerRuler.StartFrame in
  40. /// top of the Game.Update method.
  41. ///
  42. /// Then, surround the code that you want measure by BeginMark and EndMark.
  43. ///
  44. /// timeRuler.BeginMark( "Update", Color.Blue );
  45. /// // process that you want to measure.
  46. /// timerRuler.EndMark( "Update" );
  47. ///
  48. /// Also, you can specify bar index of marker (default value is 0)
  49. ///
  50. /// timeRuler.BeginMark( 1, "Update", Color.Blue );
  51. ///
  52. /// All profiling methods has CondionalAttribute with "TRACE".
  53. /// If you not specified "TRACE" constant, it doesn't even generate
  54. /// method calls for BeginMark/EndMark.
  55. /// So, don't forget remove "TRACE" constant when you release your game.
  56. ///
  57. /// </remarks>
  58. public class TimeRuler : DrawableGameComponent
  59. {
  60. #region Constants
  61. /// <summary>
  62. /// Max bar count.
  63. /// </summary>
  64. const int MaxBars = 8;
  65. /// <summary>
  66. /// Maximum sample number for each bar.
  67. /// </summary>
  68. const int MaxSamples = 256;
  69. /// <summary>
  70. /// Maximum nest calls for each bar.
  71. /// </summary>
  72. const int MaxNestCall = 32;
  73. /// <summary>
  74. /// Maximum display frames.
  75. /// </summary>
  76. const int MaxSampleFrames = 4;
  77. /// <summary>
  78. /// Duration (in frame count) for take snap shot of log.
  79. /// </summary>
  80. const int LogSnapDuration = 120;
  81. /// <summary>
  82. /// Height(in pixels) of bar.
  83. /// </summary>
  84. const int BarHeight = 8;
  85. /// <summary>
  86. /// Padding(in pixels) of bar.
  87. /// </summary>
  88. const int BarPadding = 2;
  89. /// <summary>
  90. /// Delay frame count for auto display frame adjustment.
  91. /// </summary>
  92. const int AutoAdjustDelay = 30;
  93. #endregion
  94. #region Properties
  95. /// <summary>
  96. /// Gets/Set log display or no.
  97. /// </summary>
  98. public bool ShowLog { get; set; }
  99. /// <summary>
  100. /// Gets/Sets target sample frames.
  101. /// </summary>
  102. public int TargetSampleFrames { get; set; }
  103. /// <summary>
  104. /// Gets/Sets TimeRuler rendering position.
  105. /// </summary>
  106. public Vector2 Position { get { return position; } set { position = value; } }
  107. /// <summary>
  108. /// Gets/Sets timer ruler width.
  109. /// </summary>
  110. public int Width { get; set; }
  111. #endregion
  112. #region Fields
  113. #if TRACE
  114. /// <summary>
  115. /// Marker structure.
  116. /// </summary>
  117. private struct Marker
  118. {
  119. public int MarkerId;
  120. public float BeginTime;
  121. public float EndTime;
  122. public Color Color;
  123. }
  124. /// <summary>
  125. /// Collection of markers.
  126. /// </summary>
  127. private class MarkerCollection
  128. {
  129. // Marker collection.
  130. public Marker[] Markers = new Marker[MaxSamples];
  131. public int MarkCount;
  132. // Marker nest information.
  133. public int[] MarkerNests = new int[MaxNestCall];
  134. public int NestCount;
  135. }
  136. /// <summary>
  137. /// Frame logging information.
  138. /// </summary>
  139. private class FrameLog
  140. {
  141. public MarkerCollection[] Bars;
  142. public FrameLog()
  143. {
  144. // Initialize markers.
  145. Bars = new MarkerCollection[MaxBars];
  146. for (int i = 0; i < MaxBars; ++i)
  147. Bars[i] = new MarkerCollection();
  148. }
  149. }
  150. /// <summary>
  151. /// Marker information
  152. /// </summary>
  153. private class MarkerInfo
  154. {
  155. // Name of marker.
  156. public string Name;
  157. // Marker log.
  158. public MarkerLog[] Logs = new MarkerLog[MaxBars];
  159. public MarkerInfo(string name)
  160. {
  161. Name = name;
  162. }
  163. }
  164. /// <summary>
  165. /// Marker log information.
  166. /// </summary>
  167. private struct MarkerLog
  168. {
  169. public float SnapMin;
  170. public float SnapMax;
  171. public float SnapAvg;
  172. public float Min;
  173. public float Max;
  174. public float Avg;
  175. public int Samples;
  176. public Color Color;
  177. public bool Initialized;
  178. }
  179. // Reference of debug manager.
  180. DebugManager debugManager;
  181. // Logs for each frames.
  182. FrameLog[] logs;
  183. // Previous frame log.
  184. FrameLog prevLog;
  185. // Current log.
  186. FrameLog curLog;
  187. // Current frame count.
  188. int frameCount;
  189. // Stopwatch for measure the time.
  190. Stopwatch stopwatch = new Stopwatch();
  191. // Marker information array.
  192. List<MarkerInfo> markers = new List<MarkerInfo>();
  193. // Dictionary that maps from marker name to marker id.
  194. Dictionary<string, int> markerNameToIdMap = new Dictionary<string, int>();
  195. // Display frame adjust counter.
  196. int frameAdjust;
  197. // Current display frame count.
  198. int sampleFrames;
  199. // Marker log string.
  200. StringBuilder logString = new StringBuilder(512);
  201. // You want to call StartFrame at beginning of Game.Update method.
  202. // But Game.Update gets calls multiple time when game runs slow in fixed time step mode.
  203. // In this case, we should ignore StartFrame call.
  204. // To do this, we just keep tracking of number of StartFrame calls until Draw gets called.
  205. int updateCount;
  206. #endif
  207. // TimerRuler draw position.
  208. Vector2 position;
  209. #endregion
  210. #region Initialization
  211. public TimeRuler(Game game)
  212. : base(game)
  213. {
  214. // Add this as a service.
  215. Game.Services.AddService(typeof(TimeRuler), this);
  216. }
  217. public override void Initialize()
  218. {
  219. DrawOrder = int.MaxValue - 2;
  220. #if TRACE
  221. debugManager =
  222. Game.Services.GetService(typeof(DebugManager)) as DebugManager;
  223. if (debugManager == null)
  224. throw new InvalidOperationException("DebugManager is not registered.");
  225. // Add "tr" command if DebugCommandHost is registered.
  226. IDebugCommandHost host =
  227. Game.Services.GetService(typeof(IDebugCommandHost))
  228. as IDebugCommandHost;
  229. if (host != null)
  230. {
  231. host.RegisterCommand("tr", "TimeRuler", this.CommandExecute);
  232. this.Visible = false;
  233. }
  234. // Initialize Parameters.
  235. logs = new FrameLog[2];
  236. for (int i = 0; i < logs.Length; ++i)
  237. logs[i] = new FrameLog();
  238. sampleFrames = TargetSampleFrames = 1;
  239. // Time-Ruler's update method doesn't need to get called.
  240. this.Enabled = false;
  241. #endif
  242. base.Initialize();
  243. }
  244. protected override void LoadContent()
  245. {
  246. Width = (int)(GraphicsDevice.Viewport.Width * 0.8f);
  247. Layout layout = new Layout(GraphicsDevice.Viewport);
  248. position = layout.Place(new Vector2(Width, BarHeight),
  249. 0, 0.01f, Alignment.BottomCenter);
  250. base.LoadContent();
  251. }
  252. #if TRACE
  253. /// <summary>
  254. /// 'tr' command execution.
  255. /// </summary>
  256. void CommandExecute(IDebugCommandHost host, string command,
  257. IList<string> arguments)
  258. {
  259. bool previousVisible = Visible;
  260. if (arguments.Count == 0)
  261. Visible = !Visible;
  262. char[] subArgSeparator = new[] { ':' };
  263. foreach (string orgArg in arguments)
  264. {
  265. string arg = orgArg.ToLower();
  266. string[] subargs = arg.Split(subArgSeparator);
  267. switch (subargs[0])
  268. {
  269. case "on":
  270. Visible = true;
  271. break;
  272. case "off":
  273. Visible = false;
  274. break;
  275. case "reset":
  276. ResetLog();
  277. break;
  278. case "log":
  279. if (subargs.Length > 1)
  280. {
  281. if (String.Compare(subargs[1], "on") == 0)
  282. ShowLog = true;
  283. if (String.Compare(subargs[1], "off") == 0)
  284. ShowLog = false;
  285. }
  286. else
  287. {
  288. ShowLog = !ShowLog;
  289. }
  290. break;
  291. case "frame":
  292. int a = Int32.Parse(subargs[1]);
  293. a = Math.Max(a, 1);
  294. a = Math.Min(a, MaxSampleFrames);
  295. TargetSampleFrames = a;
  296. break;
  297. case "/?":
  298. case "--help":
  299. host.Echo("tr [log|on|off|reset|frame]");
  300. host.Echo("Options:");
  301. host.Echo(" on Display TimeRuler.");
  302. host.Echo(" off Hide TimeRuler.");
  303. host.Echo(" log Show/Hide marker log.");
  304. host.Echo(" reset Reset marker log.");
  305. host.Echo(" frame:sampleFrames");
  306. host.Echo(" Change target sample frame count");
  307. break;
  308. default:
  309. break;
  310. }
  311. }
  312. // Reset update count when Visible state changed.
  313. if (Visible != previousVisible)
  314. {
  315. Interlocked.Exchange(ref updateCount, 0);
  316. }
  317. }
  318. #endif
  319. #endregion
  320. #region Measuring methods
  321. /// <summary>
  322. /// Start new frame.
  323. /// </summary>
  324. [Conditional("TRACE")]
  325. public void StartFrame()
  326. {
  327. #if TRACE
  328. lock (this)
  329. {
  330. // We skip reset frame when this method gets called multiple times.
  331. int count = Interlocked.Increment(ref updateCount);
  332. if (Visible && (1 < count && count < MaxSampleFrames))
  333. return;
  334. // Update current frame log.
  335. prevLog = logs[frameCount++ & 0x1];
  336. curLog = logs[frameCount & 0x1];
  337. float endFrameTime = (float)stopwatch.Elapsed.TotalMilliseconds;
  338. // Update marker and create a log.
  339. for (int barIdx = 0; barIdx < prevLog.Bars.Length; ++barIdx)
  340. {
  341. MarkerCollection prevBar = prevLog.Bars[barIdx];
  342. MarkerCollection nextBar = curLog.Bars[barIdx];
  343. // Re-open marker that didn't get called EndMark in previous frame.
  344. for (int nest = 0; nest < prevBar.NestCount; ++nest)
  345. {
  346. int markerIdx = prevBar.MarkerNests[nest];
  347. prevBar.Markers[markerIdx].EndTime = endFrameTime;
  348. nextBar.MarkerNests[nest] = nest;
  349. nextBar.Markers[nest].MarkerId =
  350. prevBar.Markers[markerIdx].MarkerId;
  351. nextBar.Markers[nest].BeginTime = 0;
  352. nextBar.Markers[nest].EndTime = -1;
  353. nextBar.Markers[nest].Color = prevBar.Markers[markerIdx].Color;
  354. }
  355. // Update marker log.
  356. for (int markerIdx = 0; markerIdx < prevBar.MarkCount; ++markerIdx)
  357. {
  358. float duration = prevBar.Markers[markerIdx].EndTime -
  359. prevBar.Markers[markerIdx].BeginTime;
  360. int markerId = prevBar.Markers[markerIdx].MarkerId;
  361. MarkerInfo m = markers[markerId];
  362. m.Logs[barIdx].Color = prevBar.Markers[markerIdx].Color;
  363. if (!m.Logs[barIdx].Initialized)
  364. {
  365. // First frame process.
  366. m.Logs[barIdx].Min = duration;
  367. m.Logs[barIdx].Max = duration;
  368. m.Logs[barIdx].Avg = duration;
  369. m.Logs[barIdx].Initialized = true;
  370. }
  371. else
  372. {
  373. // Process after first frame.
  374. m.Logs[barIdx].Min = Math.Min(m.Logs[barIdx].Min, duration);
  375. m.Logs[barIdx].Max = Math.Min(m.Logs[barIdx].Max, duration);
  376. m.Logs[barIdx].Avg += duration;
  377. m.Logs[barIdx].Avg *= 0.5f;
  378. if (m.Logs[barIdx].Samples++ >= LogSnapDuration)
  379. {
  380. m.Logs[barIdx].SnapMin = m.Logs[barIdx].Min;
  381. m.Logs[barIdx].SnapMax = m.Logs[barIdx].Max;
  382. m.Logs[barIdx].SnapAvg = m.Logs[barIdx].Avg;
  383. m.Logs[barIdx].Samples = 0;
  384. }
  385. }
  386. }
  387. nextBar.MarkCount = prevBar.NestCount;
  388. nextBar.NestCount = prevBar.NestCount;
  389. }
  390. // Start measuring.
  391. stopwatch.Reset();
  392. stopwatch.Start();
  393. }
  394. #endif
  395. }
  396. /// <summary>
  397. /// Start measure time.
  398. /// </summary>
  399. /// <param name="markerName">name of marker.</param>
  400. /// <param name="color">color/param>
  401. [Conditional("TRACE")]
  402. public void BeginMark(string markerName, Color color)
  403. {
  404. #if TRACE
  405. BeginMark(0, markerName, color);
  406. #endif
  407. }
  408. /// <summary>
  409. /// Start measure time.
  410. /// </summary>
  411. /// <param name="barIndex">index of bar</param>
  412. /// <param name="markerName">name of marker.</param>
  413. /// <param name="color">color/param>
  414. [Conditional("TRACE")]
  415. public void BeginMark(int barIndex, string markerName, Color color)
  416. {
  417. #if TRACE
  418. lock (this)
  419. {
  420. if (barIndex < 0 || barIndex >= MaxBars)
  421. throw new ArgumentOutOfRangeException("barIndex");
  422. MarkerCollection bar = curLog.Bars[barIndex];
  423. if (bar.MarkCount >= MaxSamples)
  424. {
  425. throw new OverflowException(
  426. "Exceeded sample count.\n" +
  427. "Either set larger number to TimeRuler.MaxSmpale or" +
  428. "lower sample count.");
  429. }
  430. if (bar.NestCount >= MaxNestCall)
  431. {
  432. throw new OverflowException(
  433. "Exceeded nest count.\n" +
  434. "Either set larget number to TimeRuler.MaxNestCall or" +
  435. "lower nest calls.");
  436. }
  437. // Gets registered marker.
  438. int markerId;
  439. if (!markerNameToIdMap.TryGetValue(markerName, out markerId))
  440. {
  441. // Register this if this marker is not registered.
  442. markerId = markers.Count;
  443. markerNameToIdMap.Add(markerName, markerId);
  444. markers.Add(new MarkerInfo(markerName));
  445. }
  446. // Start measuring.
  447. bar.MarkerNests[bar.NestCount++] = bar.MarkCount;
  448. // Fill marker parameters.
  449. bar.Markers[bar.MarkCount].MarkerId = markerId;
  450. bar.Markers[bar.MarkCount].Color = color;
  451. bar.Markers[bar.MarkCount].BeginTime =
  452. (float)stopwatch.Elapsed.TotalMilliseconds;
  453. bar.Markers[bar.MarkCount].EndTime = -1;
  454. bar.MarkCount++;
  455. }
  456. #endif
  457. }
  458. /// <summary>
  459. /// End measuring.
  460. /// </summary>
  461. /// <param name="markerName">Name of marker.</param>
  462. [Conditional("TRACE")]
  463. public void EndMark(string markerName)
  464. {
  465. #if TRACE
  466. EndMark(0, markerName);
  467. #endif
  468. }
  469. /// <summary>
  470. /// End measuring.
  471. /// </summary>
  472. /// <param name="barIndex">Index of bar.</param>
  473. /// <param name="markerName">Name of marker.</param>
  474. [Conditional("TRACE")]
  475. public void EndMark(int barIndex, string markerName)
  476. {
  477. #if TRACE
  478. lock (this)
  479. {
  480. if (barIndex < 0 || barIndex >= MaxBars)
  481. throw new ArgumentOutOfRangeException("barIndex");
  482. MarkerCollection bar = curLog.Bars[barIndex];
  483. if (bar.NestCount <= 0)
  484. {
  485. throw new InvalidOperationException(
  486. "Call BeingMark method before call EndMark method.");
  487. }
  488. int markerId;
  489. if (!markerNameToIdMap.TryGetValue(markerName, out markerId))
  490. {
  491. throw new InvalidOperationException(
  492. String.Format("Maker '{0}' is not registered." +
  493. "Make sure you specifed same name as you used for BeginMark" +
  494. " method.",
  495. markerName));
  496. }
  497. int markerIdx = bar.MarkerNests[--bar.NestCount];
  498. if (bar.Markers[markerIdx].MarkerId != markerId)
  499. {
  500. throw new InvalidOperationException(
  501. "Incorrect call order of BeginMark/EndMark method." +
  502. "You call it like BeginMark(A), BeginMark(B), EndMark(B), EndMark(A)" +
  503. " But you can't call it like " +
  504. "BeginMark(A), BeginMark(B), EndMark(A), EndMark(B).");
  505. }
  506. bar.Markers[markerIdx].EndTime =
  507. (float)stopwatch.Elapsed.TotalMilliseconds;
  508. }
  509. #endif
  510. }
  511. /// <summary>
  512. /// Get average time of given bar index and marker name.
  513. /// </summary>
  514. /// <param name="barIndex">Index of bar</param>
  515. /// <param name="markerName">name of marker</param>
  516. /// <returns>average spending time in ms.</returns>
  517. public float GetAverageTime(int barIndex, string markerName)
  518. {
  519. #if TRACE
  520. if (barIndex < 0 || barIndex >= MaxBars)
  521. throw new ArgumentOutOfRangeException("barIndex");
  522. float result = 0;
  523. int markerId;
  524. if (markerNameToIdMap.TryGetValue(markerName, out markerId))
  525. result = markers[markerId].Logs[barIndex].Avg;
  526. return result;
  527. #else
  528. return 0;
  529. #endif
  530. }
  531. /// <summary>
  532. /// Reset marker log.
  533. /// </summary>
  534. [Conditional("TRACE")]
  535. public void ResetLog()
  536. {
  537. #if TRACE
  538. lock (this)
  539. {
  540. foreach (MarkerInfo markerInfo in markers)
  541. {
  542. for (int i = 0; i < markerInfo.Logs.Length; ++i)
  543. {
  544. markerInfo.Logs[i].Initialized = false;
  545. markerInfo.Logs[i].SnapMin = 0;
  546. markerInfo.Logs[i].SnapMax = 0;
  547. markerInfo.Logs[i].SnapAvg = 0;
  548. markerInfo.Logs[i].Min = 0;
  549. markerInfo.Logs[i].Max = 0;
  550. markerInfo.Logs[i].Avg = 0;
  551. markerInfo.Logs[i].Samples = 0;
  552. }
  553. }
  554. }
  555. #endif
  556. }
  557. #endregion
  558. #region Draw
  559. public override void Draw(GameTime gameTime)
  560. {
  561. Draw(position, Width);
  562. base.Draw(gameTime);
  563. }
  564. [Conditional("TRACE")]
  565. public void Draw(Vector2 position, int width)
  566. {
  567. #if TRACE
  568. // Reset update count.
  569. Interlocked.Exchange(ref updateCount, 0);
  570. // Gets SpriteBatch, SpriteFont, and WhiteTexture from DebugManager.
  571. SpriteBatch spriteBatch = debugManager.SpriteBatch;
  572. SpriteFont font = debugManager.DebugFont;
  573. Texture2D texture = debugManager.WhiteTexture;
  574. // Adjust size and position based of number of bars we should draw.
  575. int height = 0;
  576. float maxTime = 0;
  577. foreach (MarkerCollection bar in prevLog.Bars)
  578. {
  579. if (bar.MarkCount > 0)
  580. {
  581. height += BarHeight + BarPadding * 2;
  582. maxTime = Math.Max(maxTime,
  583. bar.Markers[bar.MarkCount - 1].EndTime);
  584. }
  585. }
  586. // Auto display frame adjustment.
  587. // For example, if the entire process of frame doesn't finish in less than 16.6ms
  588. // thin it will adjust display frame duration as 33.3ms.
  589. const float frameSpan = 1.0f / 60.0f * 1000f;
  590. float sampleSpan = (float)sampleFrames * frameSpan;
  591. if (maxTime > sampleSpan)
  592. frameAdjust = Math.Max(0, frameAdjust) + 1;
  593. else
  594. frameAdjust = Math.Min(0, frameAdjust) - 1;
  595. if (Math.Abs(frameAdjust) > AutoAdjustDelay)
  596. {
  597. sampleFrames = Math.Min(MaxSampleFrames, sampleFrames);
  598. sampleFrames =
  599. Math.Max(TargetSampleFrames, (int)(maxTime / frameSpan) + 1);
  600. frameAdjust = 0;
  601. }
  602. // Compute factor that converts from ms to pixel.
  603. float msToPs = (float)width / sampleSpan;
  604. // Draw start position.
  605. int startY = (int)position.Y - (height - BarHeight);
  606. // Current y position.
  607. int y = startY;
  608. spriteBatch.Begin();
  609. // Draw transparency background.
  610. Rectangle rc = new Rectangle((int)position.X, y, width, height);
  611. spriteBatch.Draw(texture, rc, new Color(0, 0, 0, 128));
  612. // Draw markers for each bars.
  613. rc.Height = BarHeight;
  614. foreach (MarkerCollection bar in prevLog.Bars)
  615. {
  616. rc.Y = y + BarPadding;
  617. if (bar.MarkCount > 0)
  618. {
  619. for (int j = 0; j < bar.MarkCount; ++j)
  620. {
  621. float bt = bar.Markers[j].BeginTime;
  622. float et = bar.Markers[j].EndTime;
  623. int sx = (int)(position.X + bt * msToPs);
  624. int ex = (int)(position.X + et * msToPs);
  625. rc.X = sx;
  626. rc.Width = Math.Max(ex - sx, 1);
  627. spriteBatch.Draw(texture, rc, bar.Markers[j].Color);
  628. }
  629. }
  630. y += BarHeight + BarPadding;
  631. }
  632. // Draw grid lines.
  633. // Each grid represents ms.
  634. rc = new Rectangle((int)position.X, (int)startY, 1, height);
  635. for (float t = 1.0f; t < sampleSpan; t += 1.0f)
  636. {
  637. rc.X = (int)(position.X + t * msToPs);
  638. spriteBatch.Draw(texture, rc, Color.Gray);
  639. }
  640. // Draw frame grid.
  641. for (int i = 0; i <= sampleFrames; ++i)
  642. {
  643. rc.X = (int)(position.X + frameSpan * (float)i * msToPs);
  644. spriteBatch.Draw(texture, rc, Color.White);
  645. }
  646. // Draw log.
  647. if (ShowLog)
  648. {
  649. // Generate log string.
  650. y = startY - font.LineSpacing;
  651. logString.Length = 0;
  652. foreach (MarkerInfo markerInfo in markers)
  653. {
  654. for (int i = 0; i < MaxBars; ++i)
  655. {
  656. if (markerInfo.Logs[i].Initialized)
  657. {
  658. if (logString.Length > 0)
  659. logString.Append("\n");
  660. logString.Append(" Bar ");
  661. logString.AppendNumber(i);
  662. logString.Append(" ");
  663. logString.Append(markerInfo.Name);
  664. logString.Append(" Avg.:");
  665. logString.AppendNumber(markerInfo.Logs[i].SnapAvg);
  666. logString.Append("ms ");
  667. y -= font.LineSpacing;
  668. }
  669. }
  670. }
  671. // Compute background size and draw it.
  672. Vector2 size = font.MeasureString(logString);
  673. rc = new Rectangle((int)position.X, (int)y, (int)size.X + 12, (int)size.Y);
  674. spriteBatch.Draw(texture, rc, new Color(0, 0, 0, 128));
  675. // Draw log string.
  676. spriteBatch.DrawString(font, logString,
  677. new Vector2(position.X + 12, y), Color.White);
  678. // Draw log color boxes.
  679. y += (int)((float)font.LineSpacing * 0.3f);
  680. rc = new Rectangle((int)position.X + 4, y, 10, 10);
  681. Rectangle rc2 = new Rectangle((int)position.X + 5, y + 1, 8, 8);
  682. foreach (MarkerInfo markerInfo in markers)
  683. {
  684. for (int i = 0; i < MaxBars; ++i)
  685. {
  686. if (markerInfo.Logs[i].Initialized)
  687. {
  688. rc.Y = y;
  689. rc2.Y = y + 1;
  690. spriteBatch.Draw(texture, rc, Color.White);
  691. spriteBatch.Draw(texture, rc2, markerInfo.Logs[i].Color);
  692. y += font.LineSpacing;
  693. }
  694. }
  695. }
  696. }
  697. spriteBatch.End();
  698. #endif
  699. }
  700. #endregion
  701. }
  702. }