TimeRuler.cs 27 KB

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