TimeRuler.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  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 PerformanceMeasuring.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. #if TRACE
  209. debugManager =
  210. Game.Services.GetService(typeof(DebugManager)) as DebugManager;
  211. if (debugManager == null)
  212. throw new InvalidOperationException("DebugManager is not registered.");
  213. // Add "tr" command if DebugCommandHost is registered.
  214. IDebugCommandHost host =
  215. Game.Services.GetService(typeof(IDebugCommandHost))
  216. as IDebugCommandHost;
  217. if (host != null)
  218. {
  219. host.RegisterCommand("tr", "TimeRuler", this.CommandExecute);
  220. this.Visible = true;
  221. }
  222. // Initialize Parameters.
  223. logs = new FrameLog[2];
  224. for (int i = 0; i < logs.Length; ++i)
  225. logs[i] = new FrameLog();
  226. sampleFrames = TargetSampleFrames = 1;
  227. // Time-Ruler's update method doesn't need to get called.
  228. this.Enabled = false;
  229. #endif
  230. base.Initialize();
  231. }
  232. protected override void LoadContent()
  233. {
  234. Width = (int)(GraphicsDevice.Viewport.Width * 0.8f);
  235. Layout layout = new Layout(GraphicsDevice.Viewport);
  236. position = layout.Place(new Vector2(Width, BarHeight),
  237. 0, 0.01f, Alignment.BottomCenter);
  238. base.LoadContent();
  239. }
  240. #if TRACE
  241. /// <summary>
  242. /// 'tr' command execution.
  243. /// </summary>
  244. void CommandExecute(IDebugCommandHost host, string command,
  245. IList<string> arguments)
  246. {
  247. bool previousVisible = Visible;
  248. if (arguments.Count == 0)
  249. Visible = !Visible;
  250. char[] subArgSeparator = new[] { ':' };
  251. foreach (string orgArg in arguments)
  252. {
  253. string arg = orgArg.ToLower();
  254. string[] subargs = arg.Split(subArgSeparator);
  255. switch (subargs[0])
  256. {
  257. case "on":
  258. Visible = true;
  259. break;
  260. case "off":
  261. Visible = false;
  262. break;
  263. case "reset":
  264. ResetLog();
  265. break;
  266. case "log":
  267. if (subargs.Length > 1)
  268. {
  269. if (String.Compare(subargs[1], "on") == 0)
  270. ShowLog = true;
  271. if (String.Compare(subargs[1], "off") == 0)
  272. ShowLog = false;
  273. }
  274. else
  275. {
  276. ShowLog = !ShowLog;
  277. }
  278. break;
  279. case "frame":
  280. int a = Int32.Parse(subargs[1]);
  281. a = Math.Max(a, 1);
  282. a = Math.Min(a, MaxSampleFrames);
  283. TargetSampleFrames = a;
  284. break;
  285. case "/?":
  286. case "--help":
  287. host.Echo("tr [log|on|off|reset|frame]");
  288. host.Echo("Options:");
  289. host.Echo(" on Display TimeRuler.");
  290. host.Echo(" off Hide TimeRuler.");
  291. host.Echo(" log Show/Hide marker log.");
  292. host.Echo(" reset Reset marker log.");
  293. host.Echo(" frame:sampleFrames");
  294. host.Echo(" Change target sample frame count");
  295. break;
  296. default:
  297. break;
  298. }
  299. }
  300. // Reset update count when Visible state changed.
  301. if (Visible != previousVisible)
  302. {
  303. Interlocked.Exchange(ref updateCount, 0);
  304. }
  305. }
  306. #endif
  307. /// <summary>
  308. /// Start new frame.
  309. /// </summary>
  310. [Conditional("TRACE")]
  311. public void StartFrame()
  312. {
  313. #if TRACE
  314. lock (this)
  315. {
  316. // We skip reset frame when this method gets called multiple times.
  317. int count = Interlocked.Increment(ref updateCount);
  318. if (Visible && (1 < count && count < MaxSampleFrames))
  319. return;
  320. // Update current frame log.
  321. prevLog = logs[frameCount++ & 0x1];
  322. curLog = logs[frameCount & 0x1];
  323. float endFrameTime = (float)stopwatch.Elapsed.TotalMilliseconds;
  324. // Update marker and create a log.
  325. for (int barIdx = 0; barIdx < prevLog.Bars.Length; ++barIdx)
  326. {
  327. MarkerCollection prevBar = prevLog.Bars[barIdx];
  328. MarkerCollection nextBar = curLog.Bars[barIdx];
  329. // Re-open marker that didn't get called EndMark in previous frame.
  330. for (int nest = 0; nest < prevBar.NestCount; ++nest)
  331. {
  332. int markerIdx = prevBar.MarkerNests[nest];
  333. prevBar.Markers[markerIdx].EndTime = endFrameTime;
  334. nextBar.MarkerNests[nest] = nest;
  335. nextBar.Markers[nest].MarkerId =
  336. prevBar.Markers[markerIdx].MarkerId;
  337. nextBar.Markers[nest].BeginTime = 0;
  338. nextBar.Markers[nest].EndTime = -1;
  339. nextBar.Markers[nest].Color = prevBar.Markers[markerIdx].Color;
  340. }
  341. // Update marker log.
  342. for (int markerIdx = 0; markerIdx < prevBar.MarkCount; ++markerIdx)
  343. {
  344. float duration = prevBar.Markers[markerIdx].EndTime -
  345. prevBar.Markers[markerIdx].BeginTime;
  346. int markerId = prevBar.Markers[markerIdx].MarkerId;
  347. MarkerInfo m = markers[markerId];
  348. m.Logs[barIdx].Color = prevBar.Markers[markerIdx].Color;
  349. if (!m.Logs[barIdx].Initialized)
  350. {
  351. // First frame process.
  352. m.Logs[barIdx].Min = duration;
  353. m.Logs[barIdx].Max = duration;
  354. m.Logs[barIdx].Avg = duration;
  355. m.Logs[barIdx].Initialized = true;
  356. }
  357. else
  358. {
  359. // Process after first frame.
  360. m.Logs[barIdx].Min = Math.Min(m.Logs[barIdx].Min, duration);
  361. m.Logs[barIdx].Max = Math.Min(m.Logs[barIdx].Max, duration);
  362. m.Logs[barIdx].Avg += duration;
  363. m.Logs[barIdx].Avg *= 0.5f;
  364. if (m.Logs[barIdx].Samples++ >= LogSnapDuration)
  365. {
  366. m.Logs[barIdx].SnapMin = m.Logs[barIdx].Min;
  367. m.Logs[barIdx].SnapMax = m.Logs[barIdx].Max;
  368. m.Logs[barIdx].SnapAvg = m.Logs[barIdx].Avg;
  369. m.Logs[barIdx].Samples = 0;
  370. }
  371. }
  372. }
  373. nextBar.MarkCount = prevBar.NestCount;
  374. nextBar.NestCount = prevBar.NestCount;
  375. }
  376. // Start measuring.
  377. stopwatch.Reset();
  378. stopwatch.Start();
  379. }
  380. #endif
  381. }
  382. /// <summary>
  383. /// Start measure time.
  384. /// </summary>
  385. /// <param name="markerName">name of marker.</param>
  386. /// <param name="color">color/param>
  387. [Conditional("TRACE")]
  388. public void BeginMark(string markerName, Color color)
  389. {
  390. #if TRACE
  391. BeginMark(0, markerName, color);
  392. #endif
  393. }
  394. /// <summary>
  395. /// Start measure time.
  396. /// </summary>
  397. /// <param name="barIndex">index of bar</param>
  398. /// <param name="markerName">name of marker.</param>
  399. /// <param name="color">color/param>
  400. [Conditional("TRACE")]
  401. public void BeginMark(int barIndex, string markerName, Color color)
  402. {
  403. #if TRACE
  404. lock (this)
  405. {
  406. if (barIndex < 0 || barIndex >= MaxBars)
  407. throw new ArgumentOutOfRangeException("barIndex");
  408. MarkerCollection bar = curLog.Bars[barIndex];
  409. if (bar.MarkCount >= MaxSamples)
  410. {
  411. throw new OverflowException(
  412. "Exceeded sample count.\n" +
  413. "Either set larger number to TimeRuler.MaxSmpale or" +
  414. "lower sample count.");
  415. }
  416. if (bar.NestCount >= MaxNestCall)
  417. {
  418. throw new OverflowException(
  419. "Exceeded nest count.\n" +
  420. "Either set larget number to TimeRuler.MaxNestCall or" +
  421. "lower nest calls.");
  422. }
  423. // Gets registered marker.
  424. int markerId;
  425. if (!markerNameToIdMap.TryGetValue(markerName, out markerId))
  426. {
  427. // Register this if this marker is not registered.
  428. markerId = markers.Count;
  429. markerNameToIdMap.Add(markerName, markerId);
  430. markers.Add(new MarkerInfo(markerName));
  431. }
  432. // Start measuring.
  433. bar.MarkerNests[bar.NestCount++] = bar.MarkCount;
  434. // Fill marker parameters.
  435. bar.Markers[bar.MarkCount].MarkerId = markerId;
  436. bar.Markers[bar.MarkCount].Color = color;
  437. bar.Markers[bar.MarkCount].BeginTime =
  438. (float)stopwatch.Elapsed.TotalMilliseconds;
  439. bar.Markers[bar.MarkCount].EndTime = -1;
  440. bar.MarkCount++;
  441. }
  442. #endif
  443. }
  444. /// <summary>
  445. /// End measuring.
  446. /// </summary>
  447. /// <param name="markerName">Name of marker.</param>
  448. [Conditional("TRACE")]
  449. public void EndMark(string markerName)
  450. {
  451. #if TRACE
  452. EndMark(0, markerName);
  453. #endif
  454. }
  455. /// <summary>
  456. /// End measuring.
  457. /// </summary>
  458. /// <param name="barIndex">Index of bar.</param>
  459. /// <param name="markerName">Name of marker.</param>
  460. [Conditional("TRACE")]
  461. public void EndMark(int barIndex, string markerName)
  462. {
  463. #if TRACE
  464. lock (this)
  465. {
  466. if (barIndex < 0 || barIndex >= MaxBars)
  467. throw new ArgumentOutOfRangeException("barIndex");
  468. MarkerCollection bar = curLog.Bars[barIndex];
  469. if (bar.NestCount <= 0)
  470. {
  471. throw new InvalidOperationException(
  472. "Call BeingMark method before call EndMark method.");
  473. }
  474. int markerId;
  475. if (!markerNameToIdMap.TryGetValue(markerName, out markerId))
  476. {
  477. throw new InvalidOperationException(
  478. String.Format("Maker '{0}' is not registered." +
  479. "Make sure you specifed same name as you used for BeginMark" +
  480. " method.",
  481. markerName));
  482. }
  483. int markerIdx = bar.MarkerNests[--bar.NestCount];
  484. if (bar.Markers[markerIdx].MarkerId != markerId)
  485. {
  486. throw new InvalidOperationException(
  487. "Incorrect call order of BeginMark/EndMark method." +
  488. "You call it like BeginMark(A), BeginMark(B), EndMark(B), EndMark(A)" +
  489. " But you can't call it like " +
  490. "BeginMark(A), BeginMark(B), EndMark(A), EndMark(B).");
  491. }
  492. bar.Markers[markerIdx].EndTime =
  493. (float)stopwatch.Elapsed.TotalMilliseconds;
  494. }
  495. #endif
  496. }
  497. /// <summary>
  498. /// Get average time of given bar index and marker name.
  499. /// </summary>
  500. /// <param name="barIndex">Index of bar</param>
  501. /// <param name="markerName">name of marker</param>
  502. /// <returns>average spending time in ms.</returns>
  503. public float GetAverageTime(int barIndex, string markerName)
  504. {
  505. #if TRACE
  506. if (barIndex < 0 || barIndex >= MaxBars)
  507. throw new ArgumentOutOfRangeException("barIndex");
  508. float result = 0;
  509. int markerId;
  510. if (markerNameToIdMap.TryGetValue(markerName, out markerId))
  511. result = markers[markerId].Logs[barIndex].Avg;
  512. return result;
  513. #else
  514. return 0f;
  515. #endif
  516. }
  517. /// <summary>
  518. /// Reset marker log.
  519. /// </summary>
  520. [Conditional("TRACE")]
  521. public void ResetLog()
  522. {
  523. #if TRACE
  524. lock (this)
  525. {
  526. foreach (MarkerInfo markerInfo in markers)
  527. {
  528. for (int i = 0; i < markerInfo.Logs.Length; ++i)
  529. {
  530. markerInfo.Logs[i].Initialized = false;
  531. markerInfo.Logs[i].SnapMin = 0;
  532. markerInfo.Logs[i].SnapMax = 0;
  533. markerInfo.Logs[i].SnapAvg = 0;
  534. markerInfo.Logs[i].Min = 0;
  535. markerInfo.Logs[i].Max = 0;
  536. markerInfo.Logs[i].Avg = 0;
  537. markerInfo.Logs[i].Samples = 0;
  538. }
  539. }
  540. }
  541. #endif
  542. }
  543. public override void Draw(GameTime gameTime)
  544. {
  545. Draw(position, Width);
  546. base.Draw(gameTime);
  547. }
  548. [Conditional("TRACE")]
  549. public void Draw(Vector2 position, int width)
  550. {
  551. #if TRACE
  552. // Reset update count.
  553. Interlocked.Exchange(ref updateCount, 0);
  554. // Gets SpriteBatch, SpriteFont, and WhiteTexture from DebugManager.
  555. SpriteBatch spriteBatch = debugManager.SpriteBatch;
  556. SpriteFont font = debugManager.DebugFont;
  557. Texture2D texture = debugManager.WhiteTexture;
  558. // Adjust size and position based of number of bars we should draw.
  559. int height = 0;
  560. float maxTime = 0;
  561. foreach (MarkerCollection bar in prevLog.Bars)
  562. {
  563. if (bar.MarkCount > 0)
  564. {
  565. height += BarHeight + BarPadding * 2;
  566. maxTime = Math.Max(maxTime,
  567. bar.Markers[bar.MarkCount - 1].EndTime);
  568. }
  569. }
  570. // Auto display frame adjustment.
  571. // For example, if the entire process of frame doesn't finish in less than 16.6ms
  572. // thin it will adjust display frame duration as 33.3ms.
  573. const float frameSpan = 1.0f / 60.0f * 1000f;
  574. float sampleSpan = (float)sampleFrames * frameSpan;
  575. if (maxTime > sampleSpan)
  576. frameAdjust = Math.Max(0, frameAdjust) + 1;
  577. else
  578. frameAdjust = Math.Min(0, frameAdjust) - 1;
  579. if (Math.Abs(frameAdjust) > AutoAdjustDelay)
  580. {
  581. sampleFrames = Math.Min(MaxSampleFrames, sampleFrames);
  582. sampleFrames =
  583. Math.Max(TargetSampleFrames, (int)(maxTime / frameSpan) + 1);
  584. frameAdjust = 0;
  585. }
  586. // Compute factor that converts from ms to pixel.
  587. float msToPs = (float)width / sampleSpan;
  588. // Draw start position.
  589. int startY = (int)position.Y - (height - BarHeight);
  590. // Current y position.
  591. int y = startY;
  592. spriteBatch.Begin();
  593. // Draw transparency background.
  594. Rectangle rc = new Rectangle((int)position.X, y, width, height);
  595. spriteBatch.Draw(texture, rc, new Color(0, 0, 0, 128));
  596. // Draw markers for each bars.
  597. rc.Height = BarHeight;
  598. foreach (MarkerCollection bar in prevLog.Bars)
  599. {
  600. rc.Y = y + BarPadding;
  601. if (bar.MarkCount > 0)
  602. {
  603. for (int j = 0; j < bar.MarkCount; ++j)
  604. {
  605. float bt = bar.Markers[j].BeginTime;
  606. float et = bar.Markers[j].EndTime;
  607. int sx = (int)(position.X + bt * msToPs);
  608. int ex = (int)(position.X + et * msToPs);
  609. rc.X = sx;
  610. rc.Width = Math.Max(ex - sx, 1);
  611. spriteBatch.Draw(texture, rc, bar.Markers[j].Color);
  612. }
  613. }
  614. y += BarHeight + BarPadding;
  615. }
  616. // Draw grid lines.
  617. // Each grid represents ms.
  618. rc = new Rectangle((int)position.X, (int)startY, 1, height);
  619. for (float t = 1.0f; t < sampleSpan; t += 1.0f)
  620. {
  621. rc.X = (int)(position.X + t * msToPs);
  622. spriteBatch.Draw(texture, rc, Color.Gray);
  623. }
  624. // Draw frame grid.
  625. for (int i = 0; i <= sampleFrames; ++i)
  626. {
  627. rc.X = (int)(position.X + frameSpan * (float)i * msToPs);
  628. spriteBatch.Draw(texture, rc, Color.White);
  629. }
  630. // Draw log.
  631. if (ShowLog)
  632. {
  633. // Generate log string.
  634. y = startY - font.LineSpacing;
  635. logString.Length = 0;
  636. foreach (MarkerInfo markerInfo in markers)
  637. {
  638. for (int i = 0; i < MaxBars; ++i)
  639. {
  640. if (markerInfo.Logs[i].Initialized)
  641. {
  642. if (logString.Length > 0)
  643. logString.Append("\n");
  644. logString.Append(" Bar ");
  645. logString.AppendNumber(i);
  646. logString.Append(" ");
  647. logString.Append(markerInfo.Name);
  648. logString.Append(" Avg.:");
  649. logString.AppendNumber(markerInfo.Logs[i].SnapAvg);
  650. logString.Append("ms ");
  651. y -= font.LineSpacing;
  652. }
  653. }
  654. }
  655. // Compute background size and draw it.
  656. Vector2 size = font.MeasureString(logString);
  657. rc = new Rectangle((int)position.X, (int)y, (int)size.X + 12, (int)size.Y);
  658. spriteBatch.Draw(texture, rc, new Color(0, 0, 0, 128));
  659. // Draw log string.
  660. spriteBatch.DrawString(font, logString,
  661. new Vector2(position.X + 12, y), Color.White);
  662. // Draw log color boxes.
  663. y += (int)((float)font.LineSpacing * 0.3f);
  664. rc = new Rectangle((int)position.X + 4, y, 10, 10);
  665. Rectangle rc2 = new Rectangle((int)position.X + 5, y + 1, 8, 8);
  666. foreach (MarkerInfo markerInfo in markers)
  667. {
  668. for (int i = 0; i < MaxBars; ++i)
  669. {
  670. if (markerInfo.Logs[i].Initialized)
  671. {
  672. rc.Y = y;
  673. rc2.Y = y + 1;
  674. spriteBatch.Draw(texture, rc, Color.White);
  675. spriteBatch.Draw(texture, rc2, markerInfo.Logs[i].Color);
  676. y += font.LineSpacing;
  677. }
  678. }
  679. }
  680. }
  681. spriteBatch.End();
  682. #endif
  683. }
  684. }
  685. }