/ externals / microprofile / microprofileui.h
microprofileui.h
   1  #pragma once
   2  // This is free and unencumbered software released into the public domain.
   3  // Anyone is free to copy, modify, publish, use, compile, sell, or
   4  // distribute this software, either in source code form or as a compiled
   5  // binary, for any purpose, commercial or non-commercial, and by any
   6  // means.
   7  // In jurisdictions that recognize copyright laws, the author or authors
   8  // of this software dedicate any and all copyright interest in the
   9  // software to the public domain. We make this dedication for the benefit
  10  // of the public at large and to the detriment of our heirs and
  11  // successors. We intend this dedication to be an overt act of
  12  // relinquishment in perpetuity of all present and future rights to this
  13  // software under copyright law.
  14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  17  // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18  // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19  // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20  // OTHER DEALINGS IN THE SOFTWARE.
  21  // For more information, please refer to <http://unlicense.org/>
  22  //
  23  // ***********************************************************************
  24  //
  25  //
  26  //
  27  
  28  
  29  #ifndef MICROPROFILE_ENABLED
  30  #error "microprofile.h must be included before including microprofileui.h"
  31  #endif
  32  
  33  #ifndef MICROPROFILEUI_ENABLED
  34  #define MICROPROFILEUI_ENABLED MICROPROFILE_ENABLED
  35  #endif
  36  
  37  #ifndef MICROPROFILEUI_API
  38  #define MICROPROFILEUI_API
  39  #endif
  40  
  41  
  42  #if 0 == MICROPROFILEUI_ENABLED
  43  #define MicroProfileMouseButton(foo, bar) do{}while(0)
  44  #define MicroProfileMousePosition(foo, bar, z) do{}while(0)
  45  #define MicroProfileModKey(key) do{}while(0)
  46  #define MicroProfileDraw(foo, bar) do{}while(0)
  47  #define MicroProfileIsDrawing() 0
  48  #define MicroProfileToggleDisplayMode() do{}while(0)
  49  #define MicroProfileSetDisplayMode(f) do{}while(0)
  50  #else
  51  
  52  #ifndef MICROPROFILE_DRAWCURSOR
  53  #define MICROPROFILE_DRAWCURSOR 0
  54  #endif
  55  
  56  #ifndef MICROPROFILE_DETAILED_BAR_NAMES
  57  #define MICROPROFILE_DETAILED_BAR_NAMES 1
  58  #endif
  59  
  60  #ifndef MICROPROFILE_TEXT_WIDTH
  61  #define MICROPROFILE_TEXT_WIDTH 5
  62  #endif
  63  
  64  #ifndef MICROPROFILE_TEXT_HEIGHT
  65  #define MICROPROFILE_TEXT_HEIGHT 8
  66  #endif
  67  
  68  #ifndef MICROPROFILE_DETAILED_BAR_HEIGHT
  69  #define MICROPROFILE_DETAILED_BAR_HEIGHT 12
  70  #endif
  71  
  72  #ifndef MICROPROFILE_DETAILED_CONTEXT_SWITCH_HEIGHT
  73  #define MICROPROFILE_DETAILED_CONTEXT_SWITCH_HEIGHT 7
  74  #endif
  75  
  76  #ifndef MICROPROFILE_GRAPH_WIDTH
  77  #define MICROPROFILE_GRAPH_WIDTH 256
  78  #endif
  79  
  80  #ifndef MICROPROFILE_GRAPH_HEIGHT
  81  #define MICROPROFILE_GRAPH_HEIGHT 256
  82  #endif
  83  
  84  #ifndef MICROPROFILE_BORDER_SIZE
  85  #define MICROPROFILE_BORDER_SIZE 1
  86  #endif
  87  
  88  #ifndef MICROPROFILE_HELP_LEFT
  89  #define MICROPROFILE_HELP_LEFT "Left-Click"
  90  #endif
  91  
  92  #ifndef MICROPROFILE_HELP_ALT
  93  #define MICROPROFILE_HELP_ALT "Alt-Click"
  94  #endif
  95  
  96  #ifndef MICROPROFILE_HELP_MOD
  97  #define MICROPROFILE_HELP_MOD "Mod"
  98  #endif
  99  
 100  #ifndef MICROPROFILE_BAR_WIDTH
 101  #define MICROPROFILE_BAR_WIDTH 100
 102  #endif
 103  
 104  #ifndef MICROPROFILE_CUSTOM_MAX
 105  #define MICROPROFILE_CUSTOM_MAX 8
 106  #endif
 107  
 108  #ifndef MICROPROFILE_CUSTOM_MAX_TIMERS
 109  #define MICROPROFILE_CUSTOM_MAX_TIMERS 64
 110  #endif
 111  
 112  #ifndef MICROPROFILE_CUSTOM_PADDING
 113  #define MICROPROFILE_CUSTOM_PADDING 12
 114  #endif
 115  
 116  
 117  #define MICROPROFILE_FRAME_HISTORY_HEIGHT 50
 118  #define MICROPROFILE_FRAME_HISTORY_WIDTH 7
 119  #define MICROPROFILE_FRAME_HISTORY_COLOR_CPU 0xffff7f27 //255 127 39
 120  #define MICROPROFILE_FRAME_HISTORY_COLOR_GPU 0xff37a0ee //55 160 238
 121  #define MICROPROFILE_FRAME_HISTORY_COLOR_HIGHTLIGHT 0x7733bb44
 122  #define MICROPROFILE_FRAME_COLOR_HIGHTLIGHT 0x20009900
 123  #define MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU 0x20996600
 124  #define MICROPROFILE_NUM_FRAMES (MICROPROFILE_MAX_FRAME_HISTORY - (MICROPROFILE_GPU_FRAME_DELAY+1))
 125  
 126  #define MICROPROFILE_TOOLTIP_MAX_STRINGS (32 + MICROPROFILE_MAX_GROUPS*2)
 127  #define MICROPROFILE_TOOLTIP_STRING_BUFFER_SIZE (4*1024)
 128  #define MICROPROFILE_TOOLTIP_MAX_LOCKED 3
 129  
 130  
 131  enum
 132  {
 133      MICROPROFILE_CUSTOM_BARS = 0x1,
 134      MICROPROFILE_CUSTOM_BAR_SOURCE_MAX = 0x2,
 135      MICROPROFILE_CUSTOM_BAR_SOURCE_AVG = 0,
 136      MICROPROFILE_CUSTOM_STACK = 0x4,
 137      MICROPROFILE_CUSTOM_STACK_SOURCE_MAX = 0x8,
 138      MICROPROFILE_CUSTOM_STACK_SOURCE_AVG = 0,
 139  };
 140  
 141  
 142  MICROPROFILEUI_API void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight); //! call if drawing microprofilers
 143  MICROPROFILEUI_API bool MicroProfileIsDrawing();
 144  MICROPROFILEUI_API void MicroProfileToggleGraph(MicroProfileToken nToken);
 145  MICROPROFILEUI_API bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight);
 146  MICROPROFILEUI_API void MicroProfileToggleDisplayMode(); //switch between off, bars, detailed
 147  MICROPROFILEUI_API void MicroProfileSetDisplayMode(int); //switch between off, bars, detailed
 148  MICROPROFILEUI_API void MicroProfileClearGraph();
 149  MICROPROFILEUI_API void MicroProfileMousePosition(uint32_t nX, uint32_t nY, int nWheelDelta);
 150  MICROPROFILEUI_API void MicroProfileModKey(uint32_t nKeyState);
 151  MICROPROFILEUI_API void MicroProfileMouseButton(uint32_t nLeft, uint32_t nRight);
 152  MICROPROFILEUI_API void MicroProfileDrawLineVertical(int nX, int nTop, int nBottom, uint32_t nColor);
 153  MICROPROFILEUI_API void MicroProfileDrawLineHorizontal(int nLeft, int nRight, int nY, uint32_t nColor);
 154  MICROPROFILEUI_API void MicroProfileLoadPreset(const char* pSuffix);
 155  MICROPROFILEUI_API void MicroProfileSavePreset(const char* pSuffix);
 156  
 157  MICROPROFILEUI_API void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, uint32_t nNumCharacters);
 158  MICROPROFILEUI_API void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType = MicroProfileBoxTypeFlat);
 159  MICROPROFILEUI_API void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, uint32_t nColor);
 160  MICROPROFILEUI_API void MicroProfileDumpTimers();
 161  
 162  MICROPROFILEUI_API void MicroProfileInitUI();
 163  
 164  MICROPROFILEUI_API void MicroProfileCustomGroupToggle(const char* pCustomName);
 165  MICROPROFILEUI_API void MicroProfileCustomGroupEnable(const char* pCustomName);
 166  MICROPROFILEUI_API void MicroProfileCustomGroupEnable(uint32_t nIndex);
 167  MICROPROFILEUI_API void MicroProfileCustomGroupDisable();
 168  MICROPROFILEUI_API void MicroProfileCustomGroup(const char* pCustomName, uint32_t nMaxTimers, uint32_t nAggregateFlip, float fReferenceTime, uint32_t nFlags);
 169  MICROPROFILEUI_API void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer);
 170  
 171  #ifdef MICROPROFILEUI_IMPL
 172  #include <inttypes.h>
 173  #include <stdio.h>
 174  #include <stdlib.h>
 175  #include <stdarg.h>
 176  #include <math.h>
 177  #include <algorithm>
 178  #include <array>
 179  
 180  MICROPROFILE_DEFINE(g_MicroProfileDetailed, "MicroProfile", "Detailed View", 0x8888000);
 181  MICROPROFILE_DEFINE(g_MicroProfileDrawGraph, "MicroProfile", "Draw Graph", 0xff44ee00);
 182  MICROPROFILE_DEFINE(g_MicroProfileDrawBarView, "MicroProfile", "DrawBarView", 0x00dd77);
 183  MICROPROFILE_DEFINE(g_MicroProfileDraw,"MicroProfile", "Draw", 0x737373);
 184  
 185  
 186  struct MicroProfileStringArray
 187  {
 188      const char* ppStrings[MICROPROFILE_TOOLTIP_MAX_STRINGS];
 189      char Buffer[MICROPROFILE_TOOLTIP_STRING_BUFFER_SIZE];
 190      char* pBufferPos;
 191      uint32_t nNumStrings;
 192  };
 193  
 194  struct MicroProfileGroupMenuItem
 195  {
 196      uint32_t nIsCategory;
 197      uint32_t nCategoryIndex;
 198      uint32_t nIndex;
 199      const char* pName;
 200  };
 201  
 202  struct MicroProfileCustom
 203  {
 204      char pName[MICROPROFILE_NAME_MAX_LEN];
 205      uint32_t nFlags;
 206      uint32_t nAggregateFlip;
 207      uint32_t nNumTimers;
 208      uint32_t nMaxTimers;
 209      uint64_t nGroupMask;
 210      float fReference;
 211      uint64_t* pTimers;
 212  };
 213  
 214  struct SOptionDesc
 215  {
 216      SOptionDesc(){}
 217      SOptionDesc(uint8_t nSubType, uint8_t nIndex, const char* fmt, ...):nSubType(nSubType), nIndex(nIndex)
 218      {
 219          va_list args;
 220          va_start (args, fmt);
 221          vsprintf(Text, fmt, args);
 222          va_end(args);
 223      }
 224      char Text[32];
 225      uint8_t nSubType;
 226      uint8_t nIndex;
 227      bool bSelected;
 228  };
 229  static const std::array<uint32_t, 6> g_MicroProfileAggregatePresets{0, 10, 20, 30, 60, 120};
 230  static const std::array<float, 10> g_MicroProfileReferenceTimePresets{5.f, 10.f, 15.f,20.f, 33.33f, 66.66f, 100.f, 250.f, 500.f, 1000.f};
 231  static const std::array<uint32_t, 4> g_MicroProfileOpacityPresets{0x40, 0x80, 0xc0, 0xff};
 232  static const std::array<const char*, 7> g_MicroProfilePresetNames
 233  {
 234      MICROPROFILE_DEFAULT_PRESET,
 235      "Render",
 236      "GPU",
 237      "Lighting",
 238      "AI",
 239      "Visibility",
 240      "Sound",
 241  };
 242  
 243  enum
 244  {
 245      MICROPROFILE_NUM_REFERENCE_PRESETS = g_MicroProfileReferenceTimePresets.size(),
 246      MICROPROFILE_NUM_OPACITY_PRESETS = g_MicroProfileOpacityPresets.size(),
 247  #if MICROPROFILE_CONTEXT_SWITCH_TRACE
 248      MICROPROFILE_OPTION_SIZE = MICROPROFILE_NUM_REFERENCE_PRESETS + MICROPROFILE_NUM_OPACITY_PRESETS * 2 + 2 + 7,
 249  #else
 250      MICROPROFILE_OPTION_SIZE = MICROPROFILE_NUM_REFERENCE_PRESETS + MICROPROFILE_NUM_OPACITY_PRESETS * 2 + 2 + 3,
 251  #endif
 252  };
 253  
 254  struct MicroProfileUI
 255  {
 256      //menu/mouse over stuff
 257      uint64_t nHoverToken;
 258      int64_t  nHoverTime;
 259      int      nHoverFrame;
 260  #if MICROPROFILE_DEBUG
 261      uint64_t nHoverAddressEnter;
 262      uint64_t nHoverAddressLeave;
 263  #endif
 264  
 265      uint32_t nWidth;
 266      uint32_t nHeight;
 267  
 268  
 269      int nOffsetX;
 270      int nOffsetY;
 271      float fDetailedOffset; //display offset relative to start of latest displayable frame.
 272      float fDetailedRange; //no. of ms to display
 273      float fDetailedOffsetTarget;
 274      float fDetailedRangeTarget;
 275      uint32_t nOpacityBackground;
 276      uint32_t nOpacityForeground;
 277      bool bShowSpikes;
 278  
 279  
 280  
 281      uint32_t                nMouseX;
 282      uint32_t                nMouseY;
 283      uint32_t                nMouseDownX;
 284      uint32_t                nMouseDownY;
 285      int                     nMouseWheelDelta;
 286      uint32_t                nMouseDownLeft;
 287      uint32_t                nMouseDownRight;
 288      uint32_t                nMouseLeft;
 289      uint32_t                nMouseRight;
 290      uint32_t                nMouseLeftMod;
 291      uint32_t                nMouseRightMod;
 292      uint32_t                nModDown;
 293      uint32_t                nActiveMenu;
 294  
 295      MicroProfileLogEntry* pDisplayMouseOver;
 296  
 297      int64_t                 nRangeBegin;
 298      int64_t                 nRangeEnd;
 299      int64_t                 nRangeBeginGpu;
 300      int64_t                 nRangeEndGpu;
 301      uint32_t                nRangeBeginIndex;
 302      uint32_t                nRangeEndIndex;
 303      MicroProfileThreadLog*  pRangeLog;
 304      uint32_t                nHoverColor;
 305      uint32_t                nHoverColorShared;
 306  
 307      MicroProfileStringArray LockedToolTips[MICROPROFILE_TOOLTIP_MAX_LOCKED];
 308      uint32_t                nLockedToolTipColor[MICROPROFILE_TOOLTIP_MAX_LOCKED];
 309      int                     LockedToolTipFront;
 310  
 311      MicroProfileGroupMenuItem   GroupMenu[MICROPROFILE_MAX_GROUPS + MICROPROFILE_MAX_CATEGORIES];
 312      uint32_t                    GroupMenuCount;
 313  
 314  
 315      uint32_t                    nCustomActive;
 316      uint32_t                    nCustomTimerCount;
 317      uint32_t                    nCustomCount;
 318      MicroProfileCustom          Custom[MICROPROFILE_CUSTOM_MAX];
 319      uint64_t                    CustomTimer[MICROPROFILE_CUSTOM_MAX_TIMERS];
 320  
 321      SOptionDesc Options[MICROPROFILE_OPTION_SIZE];
 322  
 323  
 324  };
 325  
 326  MicroProfileUI g_MicroProfileUI;
 327  #define UI g_MicroProfileUI
 328  static const std::array<uint32_t, 2> g_nMicroProfileBackColors{  0x474747, 0x313131 };
 329  #define MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS 16
 330  static const std::array<uint32_t, MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS> g_nMicroProfileContextSwitchThreadColors //palette generated by http://tools.medialab.sciences-po.fr/iwanthue/index.php
 331  {
 332      0x63607B,
 333      0x755E2B,
 334      0x326A55,
 335      0x523135,
 336      0x904F42,
 337      0x87536B,
 338      0x346875,
 339      0x5E6046,
 340      0x35404C,
 341      0x224038,
 342      0x413D1E,
 343      0x5E3A26,
 344      0x5D6161,
 345      0x4C6234,
 346      0x7D564F,
 347      0x5C4352,
 348  };
 349  
 350  
 351  void MicroProfileInitUI()
 352  {
 353      static bool bInitialized = false;
 354      if(!bInitialized)
 355      {
 356          bInitialized = true;
 357          g_MicroProfileUI = {};
 358          UI.nActiveMenu = UINT32_MAX;
 359          UI.fDetailedOffsetTarget = UI.fDetailedOffset = 0.f;
 360          UI.fDetailedRangeTarget = UI.fDetailedRange = 50.f;
 361  
 362          UI.nOpacityBackground = 0xff<<24;
 363          UI.nOpacityForeground = 0xff<<24;
 364  
 365          UI.bShowSpikes = false;
 366  
 367          UI.nWidth = 100;
 368          UI.nHeight = 100;
 369  
 370          UI.nCustomActive = UINT32_MAX;
 371          UI.nCustomTimerCount = 0;
 372          UI.nCustomCount = 0;
 373  
 374          int nIndex = 0;
 375          UI.Options[nIndex++] = SOptionDesc(0xff, 0, "%s", "Reference");
 376          for(int i = 0; i < MICROPROFILE_NUM_REFERENCE_PRESETS; ++i)
 377          {
 378              UI.Options[nIndex++] = SOptionDesc(0, i, "  %6.2fms", g_MicroProfileReferenceTimePresets[i]);
 379          }
 380          UI.Options[nIndex++] = SOptionDesc(0xff, 0, "%s", "BG Opacity");
 381          for(int i = 0; i < MICROPROFILE_NUM_OPACITY_PRESETS; ++i)
 382          {
 383              UI.Options[nIndex++] = SOptionDesc(1, i, "  %7d%%", (i+1)*25);
 384          }
 385          UI.Options[nIndex++] = SOptionDesc(0xff, 0, "%s", "FG Opacity");
 386          for(int i = 0; i < MICROPROFILE_NUM_OPACITY_PRESETS; ++i)
 387          {
 388              UI.Options[nIndex++] = SOptionDesc(2, i, "  %7d%%", (i+1)*25);
 389          }
 390          UI.Options[nIndex++] = SOptionDesc(0xff, 0, "%s", "Spike Display");
 391          UI.Options[nIndex++] = SOptionDesc(3, 0, "%s", "  Enable");
 392  
 393  #if MICROPROFILE_CONTEXT_SWITCH_TRACE
 394          UI.Options[nIndex++] = SOptionDesc(0xff, 0, "%s", "CSwitch Trace");
 395          UI.Options[nIndex++] = SOptionDesc(4, 0, "%s", "  Enable");
 396          UI.Options[nIndex++] = SOptionDesc(4, 1, "%s", "  All Threads");
 397          UI.Options[nIndex++] = SOptionDesc(4, 2, "%s", "  No Bars");
 398  #endif
 399          MP_ASSERT(nIndex == MICROPROFILE_OPTION_SIZE);
 400      }
 401  }
 402  
 403  void MicroProfileSetDisplayMode(int nValue)
 404  {
 405      MicroProfile& S = *MicroProfileGet();
 406      nValue = nValue >= 0 && nValue < 4 ? nValue : S.nDisplay;
 407      S.nDisplay = nValue;
 408      UI.nOffsetY = 0;
 409  }
 410  
 411  void MicroProfileToggleDisplayMode()
 412  {
 413      MicroProfile& S = *MicroProfileGet();
 414      S.nDisplay = (S.nDisplay + 1) % 4;
 415      UI.nOffsetY = 0;
 416  }
 417  
 418  
 419  void MicroProfileStringArrayClear(MicroProfileStringArray* pArray)
 420  {
 421      pArray->nNumStrings = 0;
 422      pArray->pBufferPos = &pArray->Buffer[0];
 423  }
 424  
 425  void MicroProfileStringArrayAddLiteral(MicroProfileStringArray* pArray, const char* pLiteral)
 426  {
 427      MP_ASSERT(pArray->nNumStrings < MICROPROFILE_TOOLTIP_MAX_STRINGS);
 428      pArray->ppStrings[pArray->nNumStrings++] = pLiteral;
 429  }
 430  
 431  void MicroProfileStringArrayFormat(MicroProfileStringArray* pArray, const char* fmt, ...)
 432  {
 433      MP_ASSERT(pArray->nNumStrings < MICROPROFILE_TOOLTIP_MAX_STRINGS);
 434      pArray->ppStrings[pArray->nNumStrings++] = pArray->pBufferPos;
 435      va_list args;
 436      va_start (args, fmt);
 437      pArray->pBufferPos += 1 + vsprintf(pArray->pBufferPos, fmt, args);
 438      va_end(args);
 439      MP_ASSERT(pArray->pBufferPos < pArray->Buffer + MICROPROFILE_TOOLTIP_STRING_BUFFER_SIZE);
 440  }
 441  void MicroProfileStringArrayCopy(MicroProfileStringArray* pDest, MicroProfileStringArray* pSrc)
 442  {
 443      memcpy(&pDest->ppStrings[0], &pSrc->ppStrings[0], sizeof(pDest->ppStrings));
 444      memcpy(&pDest->Buffer[0], &pSrc->Buffer[0], sizeof(pDest->Buffer));
 445      for(uint32_t i = 0; i < MICROPROFILE_TOOLTIP_MAX_STRINGS; ++i)
 446      {
 447          if(i < pSrc->nNumStrings)
 448          {
 449              if(pSrc->ppStrings[i] >= &pSrc->Buffer[0] && pSrc->ppStrings[i] < &pSrc->Buffer[0] + MICROPROFILE_TOOLTIP_STRING_BUFFER_SIZE)
 450              {
 451                  pDest->ppStrings[i] += &pDest->Buffer[0] - &pSrc->Buffer[0];
 452              }
 453          }
 454      }
 455      pDest->nNumStrings = pSrc->nNumStrings;
 456  }
 457  
 458  void MicroProfileFloatWindowSize(const char** ppStrings, uint32_t nNumStrings, uint32_t* pColors, uint32_t& nWidth, uint32_t& nHeight, uint32_t* pStringLengths = 0)
 459  {
 460      uint32_t* nStringLengths = pStringLengths ? pStringLengths : (uint32_t*)alloca(nNumStrings * sizeof(uint32_t));
 461      uint32_t nTextCount = nNumStrings/2;
 462      for(uint32_t i = 0; i < nTextCount; ++i)
 463      {
 464          uint32_t i0 = i * 2;
 465          uint32_t s0, s1;
 466          nStringLengths[i0] = s0 = (uint32_t)strlen(ppStrings[i0]);
 467          nStringLengths[i0+1] = s1 = (uint32_t)strlen(ppStrings[i0+1]);
 468          nWidth = MicroProfileMax(s0+s1, nWidth);
 469      }
 470      nWidth = (MICROPROFILE_TEXT_WIDTH+1) * (2+nWidth) + 2 * MICROPROFILE_BORDER_SIZE;
 471      if(pColors)
 472          nWidth += MICROPROFILE_TEXT_WIDTH + 1;
 473      nHeight = (MICROPROFILE_TEXT_HEIGHT+1) * nTextCount + 2 * MICROPROFILE_BORDER_SIZE;
 474  }
 475  
 476  void MicroProfileDrawFloatWindow(uint32_t nX, uint32_t nY, const char** ppStrings, uint32_t nNumStrings, uint32_t nColor, uint32_t* pColors = 0)
 477  {
 478      uint32_t nWidth = 0, nHeight = 0;
 479      uint32_t* nStringLengths = (uint32_t*)alloca(nNumStrings * sizeof(uint32_t));
 480      MicroProfileFloatWindowSize(ppStrings, nNumStrings, pColors, nWidth, nHeight, nStringLengths);
 481      uint32_t nTextCount = nNumStrings/2;
 482      if(nX + nWidth > UI.nWidth)
 483          nX = UI.nWidth - nWidth;
 484      if(nY + nHeight > UI.nHeight)
 485          nY = UI.nHeight - nHeight;
 486      MicroProfileDrawBox(nX-1, nY-1, nX + nWidth+1, nY + nHeight+1, 0xff000000|nColor);
 487      MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000);
 488      if(pColors)
 489      {
 490          nX += MICROPROFILE_TEXT_WIDTH+1;
 491          nWidth -= MICROPROFILE_TEXT_WIDTH+1;
 492      }
 493      for(uint32_t i = 0; i < nTextCount; ++i)
 494      {
 495          int i0 = i * 2;
 496          if(pColors)
 497          {
 498              MicroProfileDrawBox(nX-MICROPROFILE_TEXT_WIDTH, nY, nX, nY + MICROPROFILE_TEXT_WIDTH, pColors[i]|0xff000000);
 499          }
 500          MicroProfileDrawText(nX + 1, nY + 1, UINT32_MAX, ppStrings[i0], (uint32_t)strlen(ppStrings[i0]));
 501          MicroProfileDrawText(nX + nWidth - nStringLengths[i0+1] * (MICROPROFILE_TEXT_WIDTH+1), nY + 1, UINT32_MAX, ppStrings[i0+1], (uint32_t)strlen(ppStrings[i0+1]));
 502          nY += (MICROPROFILE_TEXT_HEIGHT+1);
 503      }
 504  }
 505  void MicroProfileDrawTextBox(uint32_t nX, uint32_t nY, const char** ppStrings, uint32_t nNumStrings, uint32_t nColor, uint32_t* pColors = 0)
 506  {
 507      uint32_t nWidth = 0, nHeight = 0;
 508      uint32_t* nStringLengths = (uint32_t*)alloca(nNumStrings * sizeof(uint32_t));
 509      for(uint32_t i = 0; i < nNumStrings; ++i)
 510      {
 511          nStringLengths[i] = (uint32_t)strlen(ppStrings[i]);
 512          nWidth = MicroProfileMax(nWidth, nStringLengths[i]);
 513          nHeight++;
 514      }
 515      nWidth = (MICROPROFILE_TEXT_WIDTH+1) * (2+nWidth) + 2 * MICROPROFILE_BORDER_SIZE;
 516      nHeight = (MICROPROFILE_TEXT_HEIGHT+1) * nHeight + 2 * MICROPROFILE_BORDER_SIZE;
 517      if(nX + nWidth > UI.nWidth)
 518          nX = UI.nWidth - nWidth;
 519      if(nY + nHeight > UI.nHeight)
 520          nY = UI.nHeight - nHeight;
 521      MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000);
 522      for(uint32_t i = 0; i < nNumStrings; ++i)
 523      {
 524          MicroProfileDrawText(nX + 1, nY + 1, UINT32_MAX, ppStrings[i], (uint32_t)strlen(ppStrings[i]));
 525          nY += (MICROPROFILE_TEXT_HEIGHT+1);
 526      }
 527  }
 528  
 529  
 530  
 531  void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
 532  {
 533      MicroProfile& S = *MicroProfileGet();
 534      if(UI.nRangeBeginIndex != UI.nRangeEndIndex && UI.pRangeLog)
 535      {
 536          uint64_t nMetaSum[MICROPROFILE_META_MAX] = {0};
 537          uint64_t nMetaSumInclusive[MICROPROFILE_META_MAX] = {0};
 538          int nStackDepth = 0;
 539          uint32_t nRange[2][2];
 540          MicroProfileThreadLog* pLog = UI.pRangeLog;
 541  
 542  
 543          MicroProfileGetRange(UI.nRangeEndIndex, UI.nRangeBeginIndex, nRange);
 544          for(uint32_t i = 0; i < 2; ++i)
 545          {
 546              uint32_t nStart = nRange[i][0];
 547              uint32_t nEnd = nRange[i][1];
 548              for(uint32_t j = nStart; j < nEnd; ++j)
 549              {
 550                  MicroProfileLogEntry LE = pLog->Log[j];
 551                  int nType = MicroProfileLogType(LE);
 552                  switch(nType)
 553                  {
 554                  case MP_LOG_META:
 555                      {
 556                          int64_t nMetaIndex = MicroProfileLogTimerIndex(LE);
 557                          int64_t nMetaCount = MicroProfileLogGetTick(LE);
 558                          MP_ASSERT(nMetaIndex < MICROPROFILE_META_MAX);
 559                          if(nStackDepth>1)
 560                          {
 561                              nMetaSumInclusive[nMetaIndex] += nMetaCount;
 562                          }
 563                          else
 564                          {
 565                              nMetaSum[nMetaIndex] += nMetaCount;
 566                          }
 567                      }
 568                      break;
 569                  case MP_LOG_LEAVE:
 570                      if(nStackDepth)
 571                      {
 572                          nStackDepth--;
 573                      }
 574                      else
 575                      {
 576                          for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
 577                          {
 578                              nMetaSumInclusive[i] += nMetaSum[i];
 579                              nMetaSum[i] = 0;
 580                          }
 581                      }
 582                      break;
 583                  case MP_LOG_ENTER:
 584                      nStackDepth++;
 585                      break;
 586                  }
 587  
 588              }
 589          }
 590          bool bSpaced = false;
 591          for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
 592          {
 593              if(S.MetaCounters[i].pName && (nMetaSum[i]||nMetaSumInclusive[i]))
 594              {
 595                  if(!bSpaced)
 596                  {
 597                      bSpaced = true;
 598                      MicroProfileStringArrayAddLiteral(pToolTip, "");
 599                      MicroProfileStringArrayAddLiteral(pToolTip, "");
 600                  }
 601                  MicroProfileStringArrayFormat(pToolTip, "%s excl", S.MetaCounters[i].pName);
 602                  MicroProfileStringArrayFormat(pToolTip, "%5d", nMetaSum[i]);
 603                  MicroProfileStringArrayFormat(pToolTip, "%s incl", S.MetaCounters[i].pName);
 604                  MicroProfileStringArrayFormat(pToolTip, "%5d", nMetaSum[i] + nMetaSumInclusive[i]);
 605              }
 606          }
 607      }
 608  }
 609  
 610  void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nToken, uint64_t nTime)
 611  {
 612      MicroProfile& S = *MicroProfileGet();
 613  
 614      uint32_t nIndex = MicroProfileGetTimerIndex(nToken);
 615      uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1;
 616      uint32_t nAggregateCount = S.Aggregate[nIndex].nCount ? S.Aggregate[nIndex].nCount : 1;
 617  
 618      uint32_t nGroupId = MicroProfileGetGroupIndex(nToken);
 619      uint32_t nTimerId = MicroProfileGetTimerIndex(nToken);
 620      bool bGpu = S.GroupInfo[nGroupId].Type == MicroProfileTokenTypeGpu;
 621  
 622      float fToMs = MicroProfileTickToMsMultiplier(bGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
 623  
 624      float fMs = fToMs * (nTime);
 625      float fFrameMs = fToMs * (S.Frame[nIndex].nTicks);
 626      float fAverage = fToMs * (S.Aggregate[nIndex].nTicks/nAggregateFrames);
 627      float fCallAverage = fToMs * (S.Aggregate[nIndex].nTicks / nAggregateCount);
 628      float fMax = fToMs * (S.AggregateMax[nIndex]);
 629  
 630      float fFrameMsExclusive = fToMs * (S.FrameExclusive[nIndex]);
 631      float fAverageExclusive = fToMs * (S.AggregateExclusive[nIndex]/nAggregateFrames);
 632      float fMaxExclusive = fToMs * (S.AggregateMaxExclusive[nIndex]);
 633  
 634      float fGroupAverage = fToMs * (S.AggregateGroup[nGroupId] / nAggregateFrames);
 635      float fGroupMax = fToMs * (S.AggregateGroupMax[nGroupId]);
 636      float fGroup = fToMs * (S.FrameGroup[nGroupId]);
 637  
 638  
 639      MicroProfileStringArray ToolTip;
 640      MicroProfileStringArrayClear(&ToolTip);
 641      const char* pGroupName = S.GroupInfo[nGroupId].pName;
 642      const char* pTimerName = S.TimerInfo[nTimerId].pName;
 643      MicroProfileStringArrayAddLiteral(&ToolTip, "Timer:");
 644      MicroProfileStringArrayFormat(&ToolTip, "%s", pTimerName);
 645  
 646  #if MICROPROFILE_DEBUG
 647      MicroProfileStringArrayFormat(&ToolTip,"0x%p", UI.nHoverAddressEnter);
 648      MicroProfileStringArrayFormat(&ToolTip,"0x%p", UI.nHoverAddressLeave);
 649  #endif
 650  
 651      if(nTime != (uint64_t)0)
 652      {
 653          MicroProfileStringArrayAddLiteral(&ToolTip, "Time:");
 654          MicroProfileStringArrayFormat(&ToolTip,"%6.3fms",  fMs);
 655          MicroProfileStringArrayAddLiteral(&ToolTip, "");
 656          MicroProfileStringArrayAddLiteral(&ToolTip, "");
 657      }
 658  
 659      MicroProfileStringArrayAddLiteral(&ToolTip, "Frame Time:");
 660      MicroProfileStringArrayFormat(&ToolTip,"%6.3fms",  fFrameMs);
 661  
 662      MicroProfileStringArrayAddLiteral(&ToolTip, "Average:");
 663      MicroProfileStringArrayFormat(&ToolTip,"%6.3fms",  fAverage);
 664  
 665      MicroProfileStringArrayAddLiteral(&ToolTip, "Max:");
 666      MicroProfileStringArrayFormat(&ToolTip,"%6.3fms",  fMax);
 667  
 668      MicroProfileStringArrayAddLiteral(&ToolTip, "");
 669      MicroProfileStringArrayAddLiteral(&ToolTip, "");
 670  
 671      MicroProfileStringArrayAddLiteral(&ToolTip, "Frame Call Average:");
 672      MicroProfileStringArrayFormat(&ToolTip,"%6.3fms",  fCallAverage);
 673  
 674      MicroProfileStringArrayAddLiteral(&ToolTip, "Frame Call Count:");
 675      MicroProfileStringArrayFormat(&ToolTip, "%6d",  nAggregateCount / nAggregateFrames);
 676  
 677      MicroProfileStringArrayAddLiteral(&ToolTip, "");
 678      MicroProfileStringArrayAddLiteral(&ToolTip, "");
 679  
 680      MicroProfileStringArrayAddLiteral(&ToolTip, "Exclusive Frame Time:");
 681      MicroProfileStringArrayFormat(&ToolTip, "%6.3fms",  fFrameMsExclusive);
 682  
 683      MicroProfileStringArrayAddLiteral(&ToolTip, "Exclusive Average:");
 684      MicroProfileStringArrayFormat(&ToolTip, "%6.3fms",  fAverageExclusive);
 685  
 686      MicroProfileStringArrayAddLiteral(&ToolTip, "Exclusive Max:");
 687      MicroProfileStringArrayFormat(&ToolTip, "%6.3fms",  fMaxExclusive);
 688  
 689      MicroProfileStringArrayAddLiteral(&ToolTip, "");
 690      MicroProfileStringArrayAddLiteral(&ToolTip, "");
 691  
 692      MicroProfileStringArrayAddLiteral(&ToolTip, "Group:");
 693      MicroProfileStringArrayFormat(&ToolTip, "%s", pGroupName);
 694      MicroProfileStringArrayAddLiteral(&ToolTip, "Frame Time:");
 695      MicroProfileStringArrayFormat(&ToolTip, "%6.3f", fGroup);
 696      MicroProfileStringArrayAddLiteral(&ToolTip, "Frame Average:");
 697      MicroProfileStringArrayFormat(&ToolTip, "%6.3f", fGroupAverage);
 698      MicroProfileStringArrayAddLiteral(&ToolTip, "Frame Max:");
 699      MicroProfileStringArrayFormat(&ToolTip, "%6.3f", fGroupMax);
 700  
 701  
 702  
 703  
 704      MicroProfileToolTipMeta(&ToolTip);
 705  
 706  
 707      MicroProfileDrawFloatWindow(nX, nY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, S.TimerInfo[nTimerId].nColor);
 708  
 709      if(UI.nMouseLeftMod)
 710      {
 711          int nIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
 712          g_MicroProfileUI.nLockedToolTipColor[nIndex] = S.TimerInfo[nTimerId].nColor;
 713          MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nIndex], &ToolTip);
 714          g_MicroProfileUI.LockedToolTipFront = nIndex;
 715  
 716      }
 717  }
 718  
 719  
 720  void MicroProfileZoomTo(int64_t nTickStart, int64_t nTickEnd)
 721  {
 722      MicroProfile& S = *MicroProfileGet();
 723  
 724      int64_t nStart = S.Frames[S.nFrameCurrent].nFrameStartCpu;
 725      float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
 726      UI.fDetailedOffsetTarget = MicroProfileLogTickDifference(nStart, nTickStart) * fToMs;
 727      UI.fDetailedRangeTarget = MicroProfileLogTickDifference(nTickStart, nTickEnd) * fToMs;
 728  }
 729  
 730  void MicroProfileCenter(int64_t nTickCenter)
 731  {
 732      MicroProfile& S = *MicroProfileGet();
 733      int64_t nStart = S.Frames[S.nFrameCurrent].nFrameStartCpu;
 734      float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
 735      float fCenter = MicroProfileLogTickDifference(nStart, nTickCenter) * fToMs;
 736      UI.fDetailedOffsetTarget = UI.fDetailedOffset = fCenter - 0.5f * UI.fDetailedRange;
 737  }
 738  #ifdef MICROPROFILE_DEBUG
 739  uint64_t* g_pMicroProfileDumpStart = 0;
 740  uint64_t* g_pMicroProfileDumpEnd = 0;
 741  void MicroProfileDebugDumpRange()
 742  {
 743      MicroProfile& S = *MicroProfileGet();
 744      if(g_pMicroProfileDumpStart != g_pMicroProfileDumpEnd)
 745      {
 746          uint64_t* pStart = g_pMicroProfileDumpStart;
 747          uint64_t* pEnd = g_pMicroProfileDumpEnd;
 748          while(pStart != pEnd)
 749          {
 750              uint64_t nTick = MicroProfileLogGetTick(*pStart);
 751              uint64_t nToken = MicroProfileLogTimerIndex(*pStart);
 752              uint32_t nTimerId = MicroProfileGetTimerIndex(nToken);
 753  
 754              const char* pTimerName = S.TimerInfo[nTimerId].pName;
 755              char buffer[256];
 756              int type = MicroProfileLogType(*pStart);
 757  
 758              const char* pBegin = type == MP_LOG_LEAVE ? "END" :
 759                  (type == MP_LOG_ENTER ? "BEGIN" : "META");
 760              snprintf(buffer, 255, "DUMP 0x%p: %s :: %llx: %s\n", pStart, pBegin,  nTick, pTimerName);
 761  #ifdef _WIN32
 762              OutputDebugString(buffer);
 763  #else
 764              printf("%s", buffer);
 765  #endif
 766              pStart++;
 767          }
 768  
 769          g_pMicroProfileDumpStart = g_pMicroProfileDumpEnd;
 770      }
 771  }
 772  #define MP_DEBUG_DUMP_RANGE() MicroProfileDebugDumpRange();
 773  #else
 774  #define MP_DEBUG_DUMP_RANGE() do{} while(0)
 775  #endif
 776  
 777  #define MICROPROFILE_HOVER_DIST 0.5f
 778  
 779  void MicroProfileDrawDetailedContextSwitchBars(uint32_t nY, uint32_t nThreadId, uint32_t nContextSwitchStart, uint32_t nContextSwitchEnd, int64_t nBaseTicks, uint32_t nBaseY)
 780  {
 781      MicroProfile& S = *MicroProfileGet();
 782      int64_t nTickIn = -1;
 783      uint32_t nThreadBefore = UINT32_MAX;
 784      float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
 785      float fMsToScreen = UI.nWidth / UI.fDetailedRange;
 786      float fMouseX = (float)UI.nMouseX;
 787      float fMouseY = (float)UI.nMouseY;
 788  
 789  
 790      for(uint32_t j = nContextSwitchStart; j != nContextSwitchEnd; j = (j+1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE)
 791      {
 792          MP_ASSERT(j < MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE);
 793          MicroProfileContextSwitch CS = S.ContextSwitch[j];
 794  
 795          if(nTickIn == -1)
 796          {
 797              if(CS.nThreadIn == nThreadId)
 798              {
 799                  nTickIn = CS.nTicks;
 800                  nThreadBefore = CS.nThreadOut;
 801              }
 802          }
 803          else
 804          {
 805              if(CS.nThreadOut == nThreadId)
 806              {
 807                  int64_t nTickOut = CS.nTicks;
 808                  float fMsStart = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickIn);
 809                  float fMsEnd = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickOut);
 810                  if(fMsStart <= fMsEnd)
 811                  {
 812                      float fXStart = fMsStart * fMsToScreen;
 813                      float fXEnd = fMsEnd * fMsToScreen;
 814                      float fYStart = (float)nY;
 815                      float fYEnd = fYStart + (MICROPROFILE_DETAILED_CONTEXT_SWITCH_HEIGHT);
 816                      uint32_t nColor = g_nMicroProfileContextSwitchThreadColors[CS.nCpu%MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS];
 817                      float fXDist = MicroProfileMax(fXStart - fMouseX, fMouseX - fXEnd);
 818                      bool bHover = fXDist < MICROPROFILE_HOVER_DIST && fYStart <= fMouseY && fMouseY <= fYEnd && nBaseY < fMouseY;
 819                      if(bHover)
 820                      {
 821                          UI.nRangeBegin = nTickIn;
 822                          UI.nRangeEnd = nTickOut;
 823                          S.nContextSwitchHoverTickIn = nTickIn;
 824                          S.nContextSwitchHoverTickOut = nTickOut;
 825                          S.nContextSwitchHoverThread = CS.nThreadOut;
 826                          S.nContextSwitchHoverThreadBefore = nThreadBefore;
 827                          S.nContextSwitchHoverThreadAfter = CS.nThreadIn;
 828                          S.nContextSwitchHoverCpuNext = CS.nCpu;
 829                          nColor = UI.nHoverColor;
 830                      }
 831                      if(CS.nCpu == S.nContextSwitchHoverCpu)
 832                      {
 833                          nColor = UI.nHoverColorShared;
 834                      }
 835                      MicroProfileDrawBox(fXStart, fYStart, fXEnd, fYEnd, nColor|UI.nOpacityForeground, MicroProfileBoxTypeFlat);
 836                  }
 837                  nTickIn = -1;
 838              }
 839          }
 840      }
 841  }
 842  
 843  void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY, int nSelectedFrame)
 844  {
 845      MicroProfile& S = *MicroProfileGet();
 846      MP_DEBUG_DUMP_RANGE();
 847      int nY = nBaseY - UI.nOffsetY;
 848      [[maybe_unused]] int64_t nNumBoxes = 0;
 849      [[maybe_unused]] int64_t nNumLines = 0;
 850  
 851      uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
 852      MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
 853      MicroProfileFrameState* pFrameNext = &S.Frames[nFrameNext];
 854  
 855      UI.nRangeBegin = 0;
 856      UI.nRangeEnd = 0;
 857      UI.nRangeBeginGpu = 0;
 858      UI.nRangeEndGpu = 0;
 859      UI.nRangeBeginIndex = UI.nRangeEndIndex = 0;
 860      UI.pRangeLog = 0;
 861      int64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
 862      int64_t nFrameStartGpu = pFrameCurrent->nFrameStartGpu;
 863      int64_t nTicksPerSecondCpu = MicroProfileTicksPerSecondCpu();
 864      int64_t nTicksPerSecondGpu = MicroProfileTicksPerSecondGpu();
 865      float fToMsCpu = MicroProfileTickToMsMultiplier(nTicksPerSecondCpu);
 866      float fToMsGpu = MicroProfileTickToMsMultiplier(nTicksPerSecondGpu);
 867  
 868      float fDetailedOffset = UI.fDetailedOffset;
 869      float fDetailedRange = UI.fDetailedRange;
 870  
 871  
 872      int64_t nDetailedOffsetTicksCpu = MicroProfileMsToTick(fDetailedOffset, MicroProfileTicksPerSecondCpu());
 873      int64_t nDetailedOffsetTicksGpu = MicroProfileMsToTick(fDetailedOffset, MicroProfileTicksPerSecondGpu());
 874      int64_t nBaseTicksCpu = nDetailedOffsetTicksCpu + nFrameStartCpu;
 875      int64_t nBaseTicksGpu = nDetailedOffsetTicksGpu + nFrameStartGpu;
 876      int64_t nBaseTicksEndCpu = nBaseTicksCpu + MicroProfileMsToTick(fDetailedRange, MicroProfileTicksPerSecondCpu());
 877  
 878      int64_t nTickReferenceCpu = 0, nTickReferenceGpu = 0;
 879      static int64_t nRefCpu = 0, nRefGpu = 0;
 880      if(MicroProfileGetGpuTickReference(&nTickReferenceCpu, &nTickReferenceGpu))
 881      {
 882          if(0 == nRefCpu || std::abs(nRefCpu-nBaseTicksCpu) > std::abs(nTickReferenceCpu-nBaseTicksCpu))
 883          {
 884              nRefCpu = nTickReferenceCpu;
 885              nRefGpu = nTickReferenceGpu;
 886          }
 887          else
 888          {
 889              nTickReferenceCpu = nRefCpu;
 890              nTickReferenceGpu = nRefGpu;
 891          }
 892          nBaseTicksGpu = (nBaseTicksCpu - nTickReferenceCpu) * nTicksPerSecondGpu / nTicksPerSecondCpu + nTickReferenceGpu;
 893      }
 894      int64_t nBaseTicksEndGpu = nBaseTicksCpu + MicroProfileMsToTick(fDetailedRange, MicroProfileTicksPerSecondCpu());
 895  
 896      MicroProfileFrameState* pFrameFirst = pFrameCurrent;
 897      int64_t nGapTime = MicroProfileTicksPerSecondCpu() * MICROPROFILE_GAP_TIME / 1000;
 898      for(uint32_t i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY; ++i)
 899      {
 900          uint32_t nNextIndex = (S.nFrameCurrent + MICROPROFILE_MAX_FRAME_HISTORY - i) % MICROPROFILE_MAX_FRAME_HISTORY;
 901          pFrameFirst = &S.Frames[nNextIndex];
 902          if(pFrameFirst->nFrameStartCpu <= nBaseTicksCpu-nGapTime)
 903              break;
 904      }
 905  
 906      float fMsBase = fToMsCpu * nDetailedOffsetTicksCpu;
 907      float fMs = fDetailedRange;
 908      float fMsEnd = fMs + fMsBase;
 909      float fWidth = (float)nWidth;
 910      float fMsToScreen = fWidth / fMs;
 911  
 912      {
 913          float fRate = floor(2*(log10(fMs)-1))/2;
 914          float fStep = powf(10.f, fRate);
 915          float fRcpStep = 1.f / fStep;
 916          int nColorIndex = (int)(floor(fMsBase*fRcpStep));
 917          float fStart = floor(fMsBase*fRcpStep) * fStep;
 918          for(float f = fStart; f < fMsEnd; )
 919          {
 920              float fStart = f;
 921              float fNext = f + fStep;
 922              MicroProfileDrawBox(((fStart-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
 923              f = fNext;
 924          }
 925      }
 926  
 927      nY += MICROPROFILE_TEXT_HEIGHT+1;
 928      MicroProfileLogEntry* pMouseOver = UI.pDisplayMouseOver;
 929      MicroProfileLogEntry* pMouseOverNext = 0;
 930      uint64_t nMouseOverToken = pMouseOver ? MicroProfileLogTimerIndex(*pMouseOver) : MICROPROFILE_INVALID_TOKEN;
 931      float fMouseX = (float)UI.nMouseX;
 932      float fMouseY = (float)UI.nMouseY;
 933      uint64_t nHoverToken = MICROPROFILE_INVALID_TOKEN;
 934      int64_t nHoverTime = 0;
 935  
 936      static int nHoverCounter = 155;
 937      static int nHoverCounterDelta = 10;
 938      nHoverCounter += nHoverCounterDelta;
 939      if(nHoverCounter >= 245)
 940          nHoverCounterDelta = -10;
 941      else if(nHoverCounter < 100)
 942          nHoverCounterDelta = 10;
 943      UI.nHoverColor = (nHoverCounter<<24)|(nHoverCounter<<16)|(nHoverCounter<<8)|nHoverCounter;
 944      uint32_t nHoverCounterShared = nHoverCounter>>2;
 945      UI.nHoverColorShared = (nHoverCounterShared<<24)|(nHoverCounterShared<<16)|(nHoverCounterShared<<8)|nHoverCounterShared;
 946  
 947      uint32_t nLinesDrawn[MICROPROFILE_STACK_MAX]={0};
 948  
 949      uint32_t nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadAfter;
 950      uint32_t nContextSwitchHoverThreadBefore = S.nContextSwitchHoverThreadBefore;
 951      S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = UINT32_MAX;
 952  
 953      uint32_t nContextSwitchStart = UINT32_MAX;
 954      uint32_t nContextSwitchEnd = UINT32_MAX;
 955      S.nContextSwitchHoverCpuNext = 0xff;
 956      S.nContextSwitchHoverTickIn = -1;
 957      S.nContextSwitchHoverTickOut = -1;
 958      if(S.bContextSwitchRunning)
 959      {
 960          MicroProfileContextSwitchSearch(&nContextSwitchStart, &nContextSwitchEnd, nBaseTicksCpu, nBaseTicksEndCpu);
 961      }
 962  
 963      bool bSkipBarView = S.bContextSwitchRunning && S.bContextSwitchNoBars;
 964  
 965      if(!bSkipBarView)
 966      {
 967          for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
 968          {
 969              MicroProfileThreadLog* pLog = S.Pool[i];
 970              if(!pLog)
 971                  continue;
 972  
 973              uint32_t nPut = pFrameNext->nLogStart[i];
 974              ///note: this may display new samples as old data, but this will only happen when
 975              //       unpaused, where the detailed view is hardly perceptible
 976              uint32_t nFront = S.Pool[i]->nPut.load(std::memory_order_relaxed);
 977              MicroProfileFrameState* pFrameLogFirst = pFrameCurrent;
 978              MicroProfileFrameState* pFrameLogLast = pFrameNext;
 979              uint32_t nGet = pFrameLogFirst->nLogStart[i];
 980              do
 981              {
 982                  MP_ASSERT(pFrameLogFirst >= &S.Frames[0] && pFrameLogFirst < &S.Frames[MICROPROFILE_MAX_FRAME_HISTORY]);
 983                  uint32_t nNewGet = pFrameLogFirst->nLogStart[i];
 984                  bool bIsValid = false;
 985                  if(nPut < nFront)
 986                  {
 987                      bIsValid = nNewGet <= nPut || nNewGet >= nFront;
 988                  }
 989                  else
 990                  {
 991                      bIsValid = nNewGet <= nPut && nNewGet >= nFront;
 992                  }
 993                  if(bIsValid)
 994                  {
 995                      nGet = nNewGet;
 996                      pFrameLogFirst--;
 997                      if(pFrameLogFirst < &S.Frames[0])
 998                          pFrameLogFirst = &S.Frames[MICROPROFILE_MAX_FRAME_HISTORY-1];
 999                  }
1000                  else
1001                  {
1002                      break;
1003                  }
1004              }while(pFrameLogFirst != pFrameFirst);
1005  
1006  
1007              if (nGet == UINT32_MAX) {
1008                  continue;
1009              }
1010              MP_ASSERT(nGet != UINT32_MAX);
1011  
1012              nPut = pFrameLogLast->nLogStart[i];
1013  
1014              uint32_t nRange[2][2] = { {0, 0}, {0, 0}, };
1015  
1016              MicroProfileGetRange(nPut, nGet, nRange);
1017              if(nPut == nGet)
1018                  continue;
1019              uint32_t nMaxStackDepth = 0;
1020  
1021              bool bGpu = pLog->nGpu != 0;
1022              float fToMs = bGpu ? fToMsGpu : fToMsCpu;
1023              int64_t nBaseTicks = bGpu ? nBaseTicksGpu : nBaseTicksCpu;
1024              char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16];
1025              uint64_t nThreadId = pLog->nThreadId;
1026              snprintf(ThreadName, sizeof(ThreadName)-1, "%04" PRIx64 ": %s", nThreadId, &pLog->ThreadName[0] );
1027              nY += 3;
1028              uint32_t nThreadColor = UINT32_MAX;
1029              if(pLog->nThreadId == nContextSwitchHoverThreadAfter || pLog->nThreadId == nContextSwitchHoverThreadBefore)
1030                  nThreadColor = UI.nHoverColorShared|0x906060;
1031              MicroProfileDrawText(0, nY, nThreadColor, &ThreadName[0], (uint32_t)strlen(&ThreadName[0]));
1032              nY += 3;
1033              nY += MICROPROFILE_TEXT_HEIGHT + 1;
1034  
1035              if(S.bContextSwitchRunning)
1036              {
1037                  MicroProfileDrawDetailedContextSwitchBars(nY, pLog->nThreadId, nContextSwitchStart, nContextSwitchEnd, nBaseTicks, nBaseY);
1038                  nY -= MICROPROFILE_DETAILED_BAR_HEIGHT;
1039                  nY += MICROPROFILE_DETAILED_CONTEXT_SWITCH_HEIGHT+1;
1040              }
1041  
1042              uint32_t nYDelta = MICROPROFILE_DETAILED_BAR_HEIGHT;
1043              uint32_t nStack[MICROPROFILE_STACK_MAX];
1044              uint32_t nStackPos = 0;
1045              for(uint32_t j = 0; j < 2; ++j)
1046              {
1047                  uint32_t nStart = nRange[j][0];
1048                  uint32_t nEnd = nRange[j][1];
1049                  for(uint32_t k = nStart; k < nEnd; ++k)
1050                  {
1051                      MicroProfileLogEntry* pEntry = &pLog->Log[k];
1052                      int nType = MicroProfileLogType(*pEntry);
1053                      if(MP_LOG_ENTER == nType)
1054                      {
1055                          MP_ASSERT(nStackPos < MICROPROFILE_STACK_MAX);
1056                          nStack[nStackPos++] = k;
1057                      }
1058                      else if(MP_LOG_META == nType)
1059                      {
1060  
1061                      }
1062                      else if(MP_LOG_LEAVE == nType)
1063                      {
1064                          if(0 == nStackPos)
1065                          {
1066                              continue;
1067                          }
1068  
1069                          MicroProfileLogEntry* pEntryEnter = &pLog->Log[nStack[nStackPos-1]];
1070                          if(MicroProfileLogTimerIndex(*pEntryEnter) != MicroProfileLogTimerIndex(*pEntry))
1071                          {
1072                              //uprintf("mismatch %llx %llx\n", pEntryEnter->nToken, pEntry->nToken);
1073                              continue;
1074                          }
1075                          int64_t nTickStart = MicroProfileLogGetTick(*pEntryEnter);
1076                          int64_t nTickEnd = MicroProfileLogGetTick(*pEntry);
1077                          uint64_t nTimerIndex = MicroProfileLogTimerIndex(*pEntry);
1078                          uint32_t nColor = S.TimerInfo[nTimerIndex].nColor;
1079                          if(nMouseOverToken == nTimerIndex)
1080                          {
1081                              if(pEntry == pMouseOver)
1082                              {
1083                                  nColor = UI.nHoverColor;
1084                                  if(bGpu)
1085                                  {
1086                                      UI.nRangeBeginGpu = *pEntryEnter;
1087                                      UI.nRangeEndGpu = *pEntry;
1088                                      uint32_t nCpuBegin = (nStack[nStackPos-1] + 1) % MICROPROFILE_BUFFER_SIZE;
1089                                      uint32_t nCpuEnd = (k + 1) % MICROPROFILE_BUFFER_SIZE;
1090                                      MicroProfileLogEntry LogCpuBegin = pLog->Log[nCpuBegin];
1091                                      MicroProfileLogEntry LogCpuEnd = pLog->Log[nCpuEnd];
1092                                      if(MicroProfileLogType(LogCpuBegin)==3 && MicroProfileLogType(LogCpuEnd) == 3)
1093                                      {
1094                                          UI.nRangeBegin = LogCpuBegin;
1095                                          UI.nRangeEnd = LogCpuEnd;
1096                                      }
1097                                      UI.nRangeBeginIndex = nStack[nStackPos-1];
1098                                      UI.nRangeEndIndex = k;
1099                                      UI.pRangeLog = pLog;
1100                                  }
1101                                  else
1102                                  {
1103                                      UI.nRangeBegin = *pEntryEnter;
1104                                      UI.nRangeEnd = *pEntry;
1105                                      UI.nRangeBeginIndex = nStack[nStackPos-1];
1106                                      UI.nRangeEndIndex = k;
1107                                      UI.pRangeLog = pLog;
1108  
1109                                  }
1110                              }
1111                              else
1112                              {
1113                                  nColor = UI.nHoverColorShared;
1114                              }
1115                          }
1116  
1117                          nMaxStackDepth = MicroProfileMax(nMaxStackDepth, nStackPos);
1118                          float fMsStart = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickStart);
1119                          float fMsEnd = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
1120                          float fXStart = fMsStart * fMsToScreen;
1121                          float fXEnd = fMsEnd * fMsToScreen;
1122                          float fYStart = (float)(nY + nStackPos * nYDelta);
1123                          float fYEnd = fYStart + (MICROPROFILE_DETAILED_BAR_HEIGHT);
1124                          float fXDist = MicroProfileMax(fXStart - fMouseX, fMouseX - fXEnd);
1125                          bool bHover = fXDist < MICROPROFILE_HOVER_DIST && fYStart <= fMouseY && fMouseY <= fYEnd && nBaseY < fMouseY;
1126                          uint32_t nIntegerWidth = (uint32_t)(fXEnd - fXStart);
1127                          if(nIntegerWidth)
1128                          {
1129                              if(bHover && UI.nActiveMenu == UINT32_MAX)
1130                              {
1131                                  nHoverToken = MicroProfileLogTimerIndex(*pEntry);
1132      #if MICROPROFILE_DEBUG
1133                                  UI.nHoverAddressEnter = (uint64_t)pEntryEnter;
1134                                  UI.nHoverAddressLeave = (uint64_t)pEntry;
1135      #endif
1136                                  nHoverTime = MicroProfileLogTickDifference(nTickStart, nTickEnd);
1137                                  pMouseOverNext = pEntry;
1138                              }
1139  
1140                              MicroProfileDrawBox(fXStart, fYStart, fXEnd, fYEnd, nColor|UI.nOpacityForeground, MicroProfileBoxTypeBar);
1141  #if MICROPROFILE_DETAILED_BAR_NAMES
1142                              if(nIntegerWidth>3*MICROPROFILE_TEXT_WIDTH)
1143                              {
1144                                  float fXStartText = MicroProfileMax(fXStart, 0.f);
1145                                  int nTextWidth = (int)(fXEnd - fXStartText);
1146                                  int nCharacters = (nTextWidth - 2*MICROPROFILE_TEXT_WIDTH) / MICROPROFILE_TEXT_WIDTH;
1147                                  if(nCharacters>0)
1148                                  {
1149                                      MicroProfileDrawText(fXStartText + 1, fYStart + 1, UINT32_MAX, S.TimerInfo[nTimerIndex].pName, MicroProfileMin<uint32_t>(S.TimerInfo[nTimerIndex].nNameLen, nCharacters));
1150                                  }
1151                              }
1152  #endif
1153                              ++nNumBoxes;
1154                          }
1155                          else
1156                          {
1157                              float fXAvg = 0.5f * (fXStart + fXEnd);
1158                              int nLineX = (int)floor(fXAvg+0.5f);
1159                              if(nLineX != (int)nLinesDrawn[nStackPos])
1160                              {
1161                                  if(bHover && UI.nActiveMenu == UINT32_MAX)
1162                                  {
1163                                      nHoverToken = (uint32_t)MicroProfileLogTimerIndex(*pEntry);
1164                                      nHoverTime = MicroProfileLogTickDifference(nTickStart, nTickEnd);
1165                                      pMouseOverNext = pEntry;
1166                                  }
1167                                  nLinesDrawn[nStackPos] = nLineX;
1168                                  MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground);
1169                                  ++nNumLines;
1170                              }
1171                          }
1172                          nStackPos--;
1173                          if(0 == nStackPos)
1174                          {
1175                              if(bGpu ? (nTickStart > nBaseTicksEndGpu) : (nTickStart > nBaseTicksEndCpu))
1176                              {
1177                                  break;
1178                              }
1179                          }
1180                      }
1181                  }
1182              }
1183              nY += nMaxStackDepth * nYDelta + MICROPROFILE_DETAILED_BAR_HEIGHT+1;
1184          }
1185      }
1186      if(S.bContextSwitchRunning && (S.bContextSwitchAllThreads||S.bContextSwitchNoBars))
1187      {
1188          uint32_t nNumThreads = 0;
1189          uint32_t nThreads[MICROPROFILE_MAX_CONTEXT_SWITCH_THREADS];
1190          for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS && S.Pool[i]; ++i)
1191              nThreads[nNumThreads++] = S.Pool[i]->nThreadId;
1192          uint32_t nNumThreadsBase = nNumThreads;
1193          if(S.bContextSwitchAllThreads)
1194          {
1195              for(uint32_t i = nContextSwitchStart; i != nContextSwitchEnd; i = (i+1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE)
1196              {
1197                  MicroProfileContextSwitch CS = S.ContextSwitch[i];
1198                  ThreadIdType nThreadId = CS.nThreadIn;
1199                  if(nThreadId)
1200                  {
1201                      bool bSeen = false;
1202                      for(uint32_t j = 0; j < nNumThreads; ++j)
1203                      {
1204                          if(nThreads[j] == nThreadId)
1205                          {
1206                              bSeen = true;
1207                              break;
1208                          }
1209                      }
1210                      if(!bSeen)
1211                      {
1212                          nThreads[nNumThreads++] = nThreadId;
1213                      }
1214                  }
1215                  if(nNumThreads == MICROPROFILE_MAX_CONTEXT_SWITCH_THREADS)
1216                  {
1217                      S.nOverflow = 10;
1218                      break;
1219                  }
1220              }
1221              std::sort(&nThreads[nNumThreadsBase], &nThreads[nNumThreads]);
1222          }
1223          uint32_t nStart = nNumThreadsBase;
1224          if(S.bContextSwitchNoBars)
1225              nStart = 0;
1226          for(uint32_t i = nStart; i < nNumThreads; ++i)
1227          {
1228              ThreadIdType nThreadId = nThreads[i];
1229              if(nThreadId)
1230              {
1231                  char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16];
1232                  const char* cLocal = MicroProfileIsLocalThread(nThreadId) ? "*": " ";
1233  
1234  #if defined(_WIN32)
1235                  // nThreadId is 32-bit on Windows
1236                  int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04x: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
1237  #else
1238                  int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04" PRIx64 ": %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
1239  #endif
1240                  uint32_t nThreadColor = UINT32_MAX;
1241                  if(nThreadId == nContextSwitchHoverThreadAfter || nThreadId == nContextSwitchHoverThreadBefore)
1242                      nThreadColor = UI.nHoverColorShared|0x906060;
1243                  MicroProfileDrawDetailedContextSwitchBars(nY+2, nThreadId, nContextSwitchStart, nContextSwitchEnd, nBaseTicksCpu, nBaseY);
1244                  MicroProfileDrawText(0, nY, nThreadColor, &ThreadName[0], nStrLen);
1245                  nY += MICROPROFILE_TEXT_HEIGHT+1;
1246              }
1247          }
1248      }
1249  
1250      S.nContextSwitchHoverCpu = S.nContextSwitchHoverCpuNext;
1251  
1252      UI.pDisplayMouseOver = pMouseOverNext;
1253  
1254      if(!S.nRunning)
1255      {
1256          if(nHoverToken != MICROPROFILE_INVALID_TOKEN && nHoverTime)
1257          {
1258              UI.nHoverToken = nHoverToken;
1259              UI.nHoverTime = nHoverTime;
1260          }
1261  
1262          if(nSelectedFrame != -1)
1263          {
1264              UI.nRangeBegin = S.Frames[nSelectedFrame].nFrameStartCpu;
1265              UI.nRangeEnd = S.Frames[(nSelectedFrame+1)%MICROPROFILE_MAX_FRAME_HISTORY].nFrameStartCpu;
1266              UI.nRangeBeginGpu = S.Frames[nSelectedFrame].nFrameStartGpu;
1267              UI.nRangeEndGpu = S.Frames[(nSelectedFrame+1)%MICROPROFILE_MAX_FRAME_HISTORY].nFrameStartGpu;
1268          }
1269          if(UI.nRangeBegin != UI.nRangeEnd)
1270          {
1271              float fMsStart = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeBegin);
1272              float fMsEnd = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
1273              float fXStart = fMsStart * fMsToScreen;
1274              float fXEnd = fMsEnd * fMsToScreen;
1275              MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
1276              MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
1277              MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
1278  
1279              fMsStart += fDetailedOffset;
1280              fMsEnd += fDetailedOffset;
1281              char sBuffer[32];
1282              uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
1283              float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
1284              float fStartTextX = fXStart - fStartTextWidth - 2;
1285              MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1286              MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
1287              uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
1288              MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1289              MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
1290  
1291              if(UI.nMouseRight)
1292              {
1293                  MicroProfileZoomTo(UI.nRangeBegin, UI.nRangeEnd);
1294              }
1295          }
1296  
1297          if(UI.nRangeBeginGpu != UI.nRangeEndGpu)
1298          {
1299              float fMsStart = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeBeginGpu);
1300              float fMsEnd = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
1301              float fXStart = fMsStart * fMsToScreen;
1302              float fXEnd = fMsEnd * fMsToScreen;
1303              MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU, MicroProfileBoxTypeFlat);
1304              MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
1305              MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
1306  
1307              nBaseY += MICROPROFILE_TEXT_HEIGHT+1;
1308  
1309              fMsStart += fDetailedOffset;
1310              fMsEnd += fDetailedOffset;
1311              char sBuffer[32];
1312              uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
1313              float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
1314              float fStartTextX = fXStart - fStartTextWidth - 2;
1315              MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1316              MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
1317              uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
1318              MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
1319              MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
1320          }
1321      }
1322  }
1323  
1324  
1325  void MicroProfileDrawDetailedFrameHistory(uint32_t nWidth, uint32_t nHeight, uint32_t nBaseY, uint32_t nSelectedFrame)
1326  {
1327      MicroProfile& S = *MicroProfileGet();
1328  
1329      const uint32_t nBarHeight = MICROPROFILE_FRAME_HISTORY_HEIGHT;
1330      float fBaseX = (float)nWidth;
1331      float fDx = fBaseX / MICROPROFILE_NUM_FRAMES;
1332  
1333      uint32_t nLastIndex =  (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
1334      MicroProfileDrawBox(0, nBaseY, nWidth, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, 0xff000000 | g_nMicroProfileBackColors[0], MicroProfileBoxTypeFlat);
1335      float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * S.fRcpReferenceTime;
1336      float fToMsGpu = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()) * S.fRcpReferenceTime;
1337  
1338  
1339      MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
1340      uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
1341      int64_t nDetailedOffsetTicksCpu = MicroProfileMsToTick(UI.fDetailedOffset, MicroProfileTicksPerSecondCpu());
1342      int64_t nCpuStart = nDetailedOffsetTicksCpu + nFrameStartCpu;
1343      int64_t nCpuEnd = nCpuStart + MicroProfileMsToTick(UI.fDetailedRange, MicroProfileTicksPerSecondCpu());;
1344  
1345  
1346      float fSelectionStart = (float)nWidth;
1347      float fSelectionEnd = 0.f;
1348      for(uint32_t i = 0; i < MICROPROFILE_NUM_FRAMES; ++i)
1349      {
1350          uint32_t nIndex = (S.nFrameCurrent + MICROPROFILE_MAX_FRAME_HISTORY - i) % MICROPROFILE_MAX_FRAME_HISTORY;
1351          MicroProfileFrameState* pCurrent = &S.Frames[nIndex];
1352          MicroProfileFrameState* pNext = &S.Frames[nLastIndex];
1353  
1354          int64_t nTicks = pNext->nFrameStartCpu - pCurrent->nFrameStartCpu;
1355          int64_t nTicksGpu = pNext->nFrameStartGpu - pCurrent->nFrameStartGpu;
1356          float fScale = fToMs * nTicks;
1357          float fScaleGpu = fToMsGpu * nTicksGpu;
1358          fScale = fScale > 1.f ? 0.f : 1.f - fScale;
1359          fScaleGpu = fScaleGpu > 1.f ? 0.f : 1.f - fScaleGpu;
1360          float fXEnd = fBaseX;
1361          float fXStart = fBaseX - fDx;
1362          fBaseX = fXStart;
1363          uint32_t nColor = MICROPROFILE_FRAME_HISTORY_COLOR_CPU;
1364          if(nIndex == nSelectedFrame)
1365              nColor = UINT32_MAX;
1366          MicroProfileDrawBox(fXStart, nBaseY + fScale * nBarHeight, fXEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, nColor, MicroProfileBoxTypeBar);
1367          if(pNext->nFrameStartCpu > nCpuStart)
1368          {
1369              fSelectionStart = fXStart;
1370          }
1371          if(pCurrent->nFrameStartCpu < nCpuEnd && fSelectionEnd == 0.f)
1372          {
1373              fSelectionEnd = fXEnd;
1374          }
1375          nLastIndex = nIndex;
1376      }
1377      MicroProfileDrawBox(fSelectionStart, nBaseY, fSelectionEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, MICROPROFILE_FRAME_HISTORY_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
1378  }
1379  void MicroProfileDrawDetailedView(uint32_t nWidth, uint32_t nHeight)
1380  {
1381      MicroProfile& S = *MicroProfileGet();
1382  
1383      MICROPROFILE_SCOPE(g_MicroProfileDetailed);
1384      uint32_t nBaseY = MICROPROFILE_TEXT_HEIGHT + 1;
1385  
1386      int nSelectedFrame = -1;
1387      if(UI.nMouseY > nBaseY && UI.nMouseY <= nBaseY + MICROPROFILE_FRAME_HISTORY_HEIGHT && UI.nActiveMenu == UINT32_MAX)
1388      {
1389  
1390          nSelectedFrame = ((MICROPROFILE_NUM_FRAMES) * (UI.nWidth-UI.nMouseX) / UI.nWidth);
1391          nSelectedFrame = (S.nFrameCurrent + MICROPROFILE_MAX_FRAME_HISTORY - nSelectedFrame) % MICROPROFILE_MAX_FRAME_HISTORY;
1392          UI.nHoverFrame = nSelectedFrame;
1393          if(UI.nMouseRight)
1394          {
1395              int64_t nRangeBegin = S.Frames[nSelectedFrame].nFrameStartCpu;
1396              int64_t nRangeEnd = S.Frames[(nSelectedFrame+1)%MICROPROFILE_MAX_FRAME_HISTORY].nFrameStartCpu;
1397              MicroProfileZoomTo(nRangeBegin, nRangeEnd);
1398          }
1399          if(UI.nMouseDownLeft)
1400          {
1401              uint64_t nFrac = (1024 * (MICROPROFILE_NUM_FRAMES) * (UI.nMouseX) / UI.nWidth) % 1024;
1402              int64_t nRangeBegin = S.Frames[nSelectedFrame].nFrameStartCpu;
1403              int64_t nRangeEnd = S.Frames[(nSelectedFrame+1)%MICROPROFILE_MAX_FRAME_HISTORY].nFrameStartCpu;
1404              MicroProfileCenter(nRangeBegin + (nRangeEnd-nRangeBegin) * nFrac / 1024);
1405          }
1406      }
1407      else
1408      {
1409          UI.nHoverFrame = -1;
1410      }
1411  
1412      MicroProfileDrawDetailedBars(nWidth, nHeight, nBaseY + MICROPROFILE_FRAME_HISTORY_HEIGHT, nSelectedFrame);
1413      MicroProfileDrawDetailedFrameHistory(nWidth, nHeight, nBaseY, nSelectedFrame);
1414  }
1415  
1416  void MicroProfileDrawTextRight(uint32_t nX, uint32_t nY, uint32_t nColor, const char* pStr, uint32_t nStrLen)
1417  {
1418      MicroProfileDrawText(nX - nStrLen * (MICROPROFILE_TEXT_WIDTH+1), nY, nColor, pStr, nStrLen);
1419  }
1420  void MicroProfileDrawHeader(int32_t nX, uint32_t nWidth, const char* pName)
1421  {
1422      if(pName)
1423      {
1424          MicroProfileDrawBox(nX-8, MICROPROFILE_TEXT_HEIGHT + 2, nX + nWidth+5, MICROPROFILE_TEXT_HEIGHT + 2 + (MICROPROFILE_TEXT_HEIGHT+1), 0xff000000|g_nMicroProfileBackColors[1]);
1425          MicroProfileDrawText(nX, MICROPROFILE_TEXT_HEIGHT + 2, UINT32_MAX, pName, (uint32_t)strlen(pName));
1426      }
1427  }
1428  
1429  
1430  typedef void (*MicroProfileLoopGroupCallback)(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pData);
1431  
1432  void MicroProfileLoopActiveGroupsDraw(int32_t nX, int32_t nY, const char* pName, MicroProfileLoopGroupCallback CB, void* pData)
1433  {
1434      MicroProfile& S = *MicroProfileGet();
1435      nY += MICROPROFILE_TEXT_HEIGHT + 2;
1436      uint64_t nGroup = S.nAllGroupsWanted ? S.nGroupMask : S.nActiveGroupWanted;
1437      uint32_t nCount = 0;
1438      for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1439      {
1440          uint64_t nMask = 1ULL << j;
1441          if(nMask & nGroup)
1442          {
1443              nY += MICROPROFILE_TEXT_HEIGHT + 1;
1444              for(uint32_t i = 0; i < S.nTotalTimers;++i)
1445              {
1446                  uint64_t nTokenMask = MicroProfileGetGroupMask(S.TimerInfo[i].nToken);
1447                  if(nTokenMask & nMask)
1448                  {
1449                      if(nY >= 0)
1450                          CB(i, nCount, nMask, nX, nY, pData);
1451  
1452                      nCount += 2;
1453                      nY += MICROPROFILE_TEXT_HEIGHT + 1;
1454  
1455                      if(nY > (int)UI.nHeight)
1456                          return;
1457                  }
1458              }
1459  
1460          }
1461      }
1462  }
1463  
1464  
1465  void MicroProfileCalcTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, uint64_t nGroup, uint32_t nSize)
1466  {
1467      MicroProfile& S = *MicroProfileGet();
1468  
1469      uint32_t nCount = 0;
1470      uint64_t nMask = 1;
1471  
1472      for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1473      {
1474          if(nMask & nGroup)
1475          {
1476              const float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[j].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
1477              for(uint32_t i = 0; i < S.nTotalTimers;++i)
1478              {
1479                  uint64_t nTokenMask = MicroProfileGetGroupMask(S.TimerInfo[i].nToken);
1480                  if(nTokenMask & nMask)
1481                  {
1482                      {
1483                          uint32_t nTimer = i;
1484                          uint32_t nIdx = nCount;
1485                          uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1;
1486                          uint32_t nAggregateCount = S.Aggregate[nTimer].nCount ? S.Aggregate[nTimer].nCount : 1;
1487                          float fToPrc = S.fRcpReferenceTime;
1488                          float fMs = fToMs * (S.Frame[nTimer].nTicks);
1489                          float fPrc = MicroProfileMin(fMs * fToPrc, 1.f);
1490                          float fAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateFrames);
1491                          float fAveragePrc = MicroProfileMin(fAverageMs * fToPrc, 1.f);
1492                          float fMaxMs = fToMs * (S.AggregateMax[nTimer]);
1493                          float fMaxPrc = MicroProfileMin(fMaxMs * fToPrc, 1.f);
1494                          float fCallAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateCount);
1495                          float fCallAveragePrc = MicroProfileMin(fCallAverageMs * fToPrc, 1.f);
1496                          float fMsExclusive = fToMs * (S.FrameExclusive[nTimer]);
1497                          float fPrcExclusive = MicroProfileMin(fMsExclusive * fToPrc, 1.f);
1498                          float fAverageMsExclusive = fToMs * (S.AggregateExclusive[nTimer] / nAggregateFrames);
1499                          float fAveragePrcExclusive = MicroProfileMin(fAverageMsExclusive * fToPrc, 1.f);
1500                          float fMaxMsExclusive = fToMs * (S.AggregateMaxExclusive[nTimer]);
1501                          float fMaxPrcExclusive = MicroProfileMin(fMaxMsExclusive * fToPrc, 1.f);
1502                          pTimers[nIdx] = fMs;
1503                          pTimers[nIdx+1] = fPrc;
1504                          pAverage[nIdx] = fAverageMs;
1505                          pAverage[nIdx+1] = fAveragePrc;
1506                          pMax[nIdx] = fMaxMs;
1507                          pMax[nIdx+1] = fMaxPrc;
1508                          pCallAverage[nIdx] = fCallAverageMs;
1509                          pCallAverage[nIdx+1] = fCallAveragePrc;
1510                          pExclusive[nIdx] = fMsExclusive;
1511                          pExclusive[nIdx+1] = fPrcExclusive;
1512                          pAverageExclusive[nIdx] = fAverageMsExclusive;
1513                          pAverageExclusive[nIdx+1] = fAveragePrcExclusive;
1514                          pMaxExclusive[nIdx] = fMaxMsExclusive;
1515                          pMaxExclusive[nIdx+1] = fMaxPrcExclusive;
1516                      }
1517                      nCount += 2;
1518                  }
1519              }
1520          }
1521          nMask <<= 1;
1522      }
1523  }
1524  
1525  #define SBUF_MAX 32
1526  
1527  void MicroProfileDrawBarArrayCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1528  {
1529      const uint32_t nHeight = MICROPROFILE_TEXT_HEIGHT;
1530      const uint32_t nTextWidth = 6 * (1+MICROPROFILE_TEXT_WIDTH);
1531      const float fWidth = (float)MICROPROFILE_BAR_WIDTH;
1532  
1533      float* pTimers = ((float**)pExtra)[0];
1534      float* pTimers2 = ((float**)pExtra)[1];
1535      MicroProfile& S = *MicroProfileGet();
1536      char sBuffer[SBUF_MAX];
1537      if (pTimers2 && pTimers2[nIdx] > 0.1f)
1538          snprintf(sBuffer, SBUF_MAX-1, "%5.2f %3.1fx", pTimers[nIdx], pTimers[nIdx] / pTimers2[nIdx]);
1539      else
1540          snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pTimers[nIdx]);
1541      if (!pTimers2)
1542          MicroProfileDrawBox(nX + nTextWidth, nY, nX + nTextWidth + fWidth * pTimers[nIdx+1], nY + nHeight, UI.nOpacityForeground|S.TimerInfo[nTimer].nColor, MicroProfileBoxTypeBar);
1543      MicroProfileDrawText(nX, nY, UINT32_MAX, sBuffer, (uint32_t)strlen(sBuffer));
1544  }
1545  
1546  
1547  uint32_t MicroProfileDrawBarArray(int32_t nX, int32_t nY, float* pTimers, const char* pName, uint32_t nTotalHeight, float* pTimers2 = NULL)
1548  {
1549      const uint32_t nTextWidth = 6 * (1+MICROPROFILE_TEXT_WIDTH);
1550      const uint32_t nWidth = MICROPROFILE_BAR_WIDTH;
1551  
1552      MicroProfileDrawLineVertical(nX-5, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1553      float* pTimersArray[2] = {pTimers, pTimers2};
1554      MicroProfileLoopActiveGroupsDraw(nX, nY, pName, MicroProfileDrawBarArrayCallback, pTimersArray);
1555      MicroProfileDrawHeader(nX, nTextWidth + nWidth, pName);
1556      return nWidth + 5 + nTextWidth;
1557  
1558  }
1559  void MicroProfileDrawBarCallCountCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1560  {
1561      MicroProfile& S = *MicroProfileGet();
1562      char sBuffer[SBUF_MAX];
1563      int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5d", S.Frame[nTimer].nCount);//fix
1564      MicroProfileDrawText(nX, nY, UINT32_MAX, sBuffer, nLen);
1565  }
1566  
1567  uint32_t MicroProfileDrawBarCallCount(int32_t nX, int32_t nY, const char* pName)
1568  {
1569      MicroProfileLoopActiveGroupsDraw(nX, nY, pName, MicroProfileDrawBarCallCountCallback, 0);
1570      const uint32_t nTextWidth = 6 * MICROPROFILE_TEXT_WIDTH;
1571      MicroProfileDrawHeader(nX, 5 + nTextWidth, pName);
1572      return 5 + nTextWidth;
1573  }
1574  
1575  struct MicroProfileMetaAverageArgs
1576  {
1577      uint64_t* pCounters;
1578      float fRcpFrames;
1579  };
1580  
1581  void MicroProfileDrawBarMetaAverageCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1582  {
1583      MicroProfileMetaAverageArgs* pArgs = (MicroProfileMetaAverageArgs*)pExtra;
1584      uint64_t* pCounters = pArgs->pCounters;
1585      float fRcpFrames = pArgs->fRcpFrames;
1586      char sBuffer[SBUF_MAX];
1587      int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pCounters[nTimer] * fRcpFrames);
1588      MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, UINT32_MAX, sBuffer, nLen);
1589  }
1590  
1591  uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
1592  {
1593      if(!pName)
1594          return 0;
1595      MicroProfileDrawLineVertical(nX-5, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1596      uint32_t nTextWidth = (1+MICROPROFILE_TEXT_WIDTH) * MicroProfileMax<uint32_t>(6, (uint32_t)strlen(pName));
1597      float fRcpFrames = 1.f / (MicroProfileGet()->nAggregateFrames ? MicroProfileGet()->nAggregateFrames : 1);
1598      MicroProfileMetaAverageArgs Args = {pCounters, fRcpFrames};
1599      MicroProfileLoopActiveGroupsDraw(nX + nTextWidth, nY, pName, MicroProfileDrawBarMetaAverageCallback, &Args);
1600      MicroProfileDrawHeader(nX, 5 + nTextWidth, pName);
1601      return 5 + nTextWidth;
1602  }
1603  
1604  
1605  void MicroProfileDrawBarMetaCountCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1606  {
1607      uint64_t* pCounters = (uint64_t*)pExtra;
1608      char sBuffer[SBUF_MAX];
1609      int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5" PRIu64, pCounters[nTimer]);
1610      MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, UINT32_MAX, sBuffer, nLen);
1611  }
1612  
1613  uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
1614  {
1615      if(!pName)
1616          return 0;
1617  
1618      MicroProfileDrawLineVertical(nX-5, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1619      uint32_t nTextWidth = (1+MICROPROFILE_TEXT_WIDTH) * MicroProfileMax<uint32_t>(6, (uint32_t)strlen(pName));
1620      MicroProfileLoopActiveGroupsDraw(nX + nTextWidth, nY, pName, MicroProfileDrawBarMetaCountCallback, pCounters);
1621      MicroProfileDrawHeader(nX, 5 + nTextWidth, pName);
1622      return 5 + nTextWidth;
1623  }
1624  
1625  void MicroProfileDrawBarLegendCallback(uint32_t nTimer, uint32_t nIdx, uint64_t nGroupMask, uint32_t nX, uint32_t nY, void* pExtra)
1626  {
1627      MicroProfile& S = *MicroProfileGet();
1628      if (S.TimerInfo[nTimer].bGraph)
1629      {
1630          MicroProfileDrawText(nX, nY, S.TimerInfo[nTimer].nColor, ">", 1);
1631      }
1632      MicroProfileDrawTextRight(nX, nY, S.TimerInfo[nTimer].nColor, S.TimerInfo[nTimer].pName, (uint32_t)strlen(S.TimerInfo[nTimer].pName));
1633      if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT+1)
1634      {
1635          UI.nHoverToken = nTimer;
1636          UI.nHoverTime = 0;
1637      }
1638  }
1639  
1640  uint32_t MicroProfileDrawBarLegend(int32_t nX, int32_t nY, uint32_t nTotalHeight, uint32_t nMaxWidth)
1641  {
1642      MicroProfileDrawLineVertical(nX-5, nY, nTotalHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1643      MicroProfileLoopActiveGroupsDraw(nMaxWidth, nY, 0, MicroProfileDrawBarLegendCallback, 0);
1644      return nX;
1645  }
1646  
1647  bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
1648  {
1649      MicroProfile& S = *MicroProfileGet();
1650  
1651      MICROPROFILE_SCOPE(g_MicroProfileDrawGraph);
1652      bool bEnabled = false;
1653      for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
1654          if(S.Graph[i].nToken != MICROPROFILE_INVALID_TOKEN)
1655              bEnabled = true;
1656      if(!bEnabled)
1657          return false;
1658  
1659      uint32_t nX = nScreenWidth - MICROPROFILE_GRAPH_WIDTH;
1660      uint32_t nY = nScreenHeight - MICROPROFILE_GRAPH_HEIGHT;
1661      MicroProfileDrawBox(nX, nY, nX + MICROPROFILE_GRAPH_WIDTH, nY + MICROPROFILE_GRAPH_HEIGHT, 0x88000000 | g_nMicroProfileBackColors[0]);
1662      bool bMouseOver = UI.nMouseX >= nX && UI.nMouseY >= nY;
1663      float fMouseXPrc =(float(UI.nMouseX - nX)) / MICROPROFILE_GRAPH_WIDTH;
1664      if(bMouseOver)
1665      {
1666          float fXAvg = fMouseXPrc * MICROPROFILE_GRAPH_WIDTH + nX;
1667          MicroProfileDrawLineVertical(fXAvg, nY, nY + MICROPROFILE_GRAPH_HEIGHT, UINT32_MAX);
1668      }
1669  
1670  
1671      float fY = (float)nScreenHeight;
1672      float fDX = MICROPROFILE_GRAPH_WIDTH * 1.f / MICROPROFILE_GRAPH_HISTORY;
1673      float fDY = MICROPROFILE_GRAPH_HEIGHT;
1674      uint32_t nPut = S.nGraphPut;
1675      float* pGraphData = (float*)alloca(sizeof(float)* MICROPROFILE_GRAPH_HISTORY*2);
1676      for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
1677      {
1678          if(S.Graph[i].nToken != MICROPROFILE_INVALID_TOKEN)
1679          {
1680              uint32_t nGroupId = MicroProfileGetGroupIndex(S.Graph[i].nToken);
1681              bool bGpu = S.GroupInfo[nGroupId].Type == MicroProfileTokenTypeGpu;
1682              float fToMs = MicroProfileTickToMsMultiplier(bGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
1683              float fToPrc = fToMs * S.fRcpReferenceTime * 3 / 4;
1684  
1685              float fX = (float)nX;
1686              for(uint32_t j = 0; j < MICROPROFILE_GRAPH_HISTORY; ++j)
1687              {
1688                  float fWeigth = MicroProfileMin(fToPrc * (S.Graph[i].nHistory[(j+nPut)%MICROPROFILE_GRAPH_HISTORY]), 1.f);
1689                  pGraphData[(j*2)] = fX;
1690                  pGraphData[(j*2)+1] = fY - fDY * fWeigth;
1691                  fX += fDX;
1692              }
1693              MicroProfileDrawLine2D(MICROPROFILE_GRAPH_HISTORY, pGraphData, S.TimerInfo[MicroProfileGetTimerIndex(S.Graph[i].nToken)].nColor);
1694          }
1695      }
1696      {
1697          float fY1 = 0.25f * MICROPROFILE_GRAPH_HEIGHT + nY;
1698          float fY2 = 0.50f * MICROPROFILE_GRAPH_HEIGHT + nY;
1699          float fY3 = 0.75f * MICROPROFILE_GRAPH_HEIGHT + nY;
1700          MicroProfileDrawLineHorizontal(nX, nX + MICROPROFILE_GRAPH_WIDTH, fY1, 0xffdd4444);
1701          MicroProfileDrawLineHorizontal(nX, nX + MICROPROFILE_GRAPH_WIDTH, fY2, 0xff000000| g_nMicroProfileBackColors[0]);
1702          MicroProfileDrawLineHorizontal(nX, nX + MICROPROFILE_GRAPH_WIDTH, fY3, 0xff000000|g_nMicroProfileBackColors[0]);
1703  
1704          char buf[32];
1705          int nLen = snprintf(buf, sizeof(buf)-1, "%5.2fms", S.fReferenceTime);
1706          MicroProfileDrawText(nX+1, fY1 - (2+MICROPROFILE_TEXT_HEIGHT), UINT32_MAX, buf, nLen);
1707      }
1708  
1709  
1710  
1711      if(bMouseOver)
1712      {
1713          uint32_t pColors[MICROPROFILE_MAX_GRAPHS];
1714          MicroProfileStringArray Strings;
1715          MicroProfileStringArrayClear(&Strings);
1716          uint32_t nTextCount = 0;
1717          uint32_t nGraphIndex = (S.nGraphPut + MICROPROFILE_GRAPH_HISTORY - int(MICROPROFILE_GRAPH_HISTORY*(1.f - fMouseXPrc))) % MICROPROFILE_GRAPH_HISTORY;
1718  
1719          uint32_t nX = UI.nMouseX;
1720          uint32_t nY = UI.nMouseY + 20;
1721  
1722          for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
1723          {
1724              if(S.Graph[i].nToken != MICROPROFILE_INVALID_TOKEN)
1725              {
1726                  uint32_t nGroupId = MicroProfileGetGroupIndex(S.Graph[i].nToken);
1727                  bool bGpu = S.GroupInfo[nGroupId].Type == MicroProfileTokenTypeGpu;
1728                  float fToMs = MicroProfileTickToMsMultiplier(bGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
1729                  uint32_t nIndex = MicroProfileGetTimerIndex(S.Graph[i].nToken);
1730                  uint32_t nColor = S.TimerInfo[nIndex].nColor;
1731                  const char* pName = S.TimerInfo[nIndex].pName;
1732                  pColors[nTextCount++] = nColor;
1733                  MicroProfileStringArrayAddLiteral(&Strings, pName);
1734                  MicroProfileStringArrayFormat(&Strings, "%5.2fms", fToMs * (S.Graph[i].nHistory[nGraphIndex]));
1735              }
1736          }
1737          if(nTextCount)
1738          {
1739              MicroProfileDrawFloatWindow(nX, nY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
1740          }
1741  
1742          if(UI.nMouseRight)
1743          {
1744              for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
1745              {
1746                  S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN;
1747              }
1748          }
1749      }
1750  
1751      return bMouseOver;
1752  }
1753  
1754  void MicroProfileDumpTimers()
1755  {
1756      MicroProfile& S = *MicroProfileGet();
1757  
1758      uint64_t nActiveGroup = S.nGroupMask;
1759  
1760      uint32_t nNumTimers = S.nTotalTimers;
1761      uint32_t nBlockSize = 2 * nNumTimers;
1762      float* pTimers = (float*)alloca(nBlockSize * 7 * sizeof(float));
1763      float* pAverage = pTimers + nBlockSize;
1764      float* pMax = pTimers + 2 * nBlockSize;
1765      float* pCallAverage = pTimers + 3 * nBlockSize;
1766      float* pTimersExclusive = pTimers + 4 * nBlockSize;
1767      float* pAverageExclusive = pTimers + 5 * nBlockSize;
1768      float* pMaxExclusive = pTimers + 6 * nBlockSize;
1769      MicroProfileCalcTimers(pTimers, pAverage, pMax, pCallAverage, pTimersExclusive, pAverageExclusive, pMaxExclusive, nActiveGroup, nNumTimers);
1770  
1771      MICROPROFILE_PRINTF("%11s, ", "Time");
1772      MICROPROFILE_PRINTF("%11s, ", "Average");
1773      MICROPROFILE_PRINTF("%11s, ", "Max");
1774      MICROPROFILE_PRINTF("%11s, ", "Call Avg");
1775      MICROPROFILE_PRINTF("%9s, ", "Count");
1776      MICROPROFILE_PRINTF("%11s, ", "Excl");
1777      MICROPROFILE_PRINTF("%11s, ", "Avg Excl");
1778      MICROPROFILE_PRINTF("%11s, \n", "Max Excl");
1779  
1780      for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1781      {
1782          uint64_t nMask = 1ULL << j;
1783          if(nMask & nActiveGroup)
1784          {
1785              MICROPROFILE_PRINTF("%s\n", S.GroupInfo[j].pName);
1786              for(uint32_t i = 0; i < S.nTotalTimers;++i)
1787              {
1788                  uint64_t nTokenMask = MicroProfileGetGroupMask(S.TimerInfo[i].nToken);
1789                  if(nTokenMask & nMask)
1790                  {
1791                      uint32_t nIdx = i * 2;
1792                      MICROPROFILE_PRINTF("%9.2fms, ", pTimers[nIdx]);
1793                      MICROPROFILE_PRINTF("%9.2fms, ", pAverage[nIdx]);
1794                      MICROPROFILE_PRINTF("%9.2fms, ", pMax[nIdx]);
1795                      MICROPROFILE_PRINTF("%9.2fms, ", pCallAverage[nIdx]);
1796                      MICROPROFILE_PRINTF("%9d, ", S.Frame[i].nCount);
1797                      MICROPROFILE_PRINTF("%9.2fms, ", pTimersExclusive[nIdx]);
1798                      MICROPROFILE_PRINTF("%9.2fms, ", pAverageExclusive[nIdx]);
1799                      MICROPROFILE_PRINTF("%9.2fms, ", pMaxExclusive[nIdx]);
1800                      MICROPROFILE_PRINTF("%s\n", S.TimerInfo[i].pName);
1801                  }
1802              }
1803          }
1804      }
1805  }
1806  
1807  void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeight)
1808  {
1809      MicroProfile& S = *MicroProfileGet();
1810  
1811      uint64_t nActiveGroup = S.nAllGroupsWanted ? S.nGroupMask : S.nActiveGroupWanted;
1812      if(!nActiveGroup)
1813          return;
1814      MICROPROFILE_SCOPE(g_MicroProfileDrawBarView);
1815  
1816      const uint32_t nHeight = MICROPROFILE_TEXT_HEIGHT;
1817      int nColorIndex = 0;
1818      uint32_t nMaxTimerNameLen = 1;
1819      uint32_t nNumTimers = 0;
1820      uint32_t nNumGroups = 0;
1821      for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1822      {
1823          if(nActiveGroup & (1ULL << j))
1824          {
1825              nNumTimers += S.GroupInfo[j].nNumTimers;
1826              nNumGroups += 1;
1827              nMaxTimerNameLen = MicroProfileMax(nMaxTimerNameLen, S.GroupInfo[j].nMaxTimerNameLen);
1828          }
1829      }
1830      uint32_t nTimerWidth = 2+(4+nMaxTimerNameLen) * (MICROPROFILE_TEXT_WIDTH+1);
1831      uint32_t nX = nTimerWidth + UI.nOffsetX;
1832      uint32_t nY = nHeight + 3 - UI.nOffsetY;
1833      uint32_t nBlockSize = 2 * nNumTimers;
1834      float* pTimers = (float*)alloca(nBlockSize * 7 * sizeof(float));
1835      float* pAverage = pTimers + nBlockSize;
1836      float* pMax = pTimers + 2 * nBlockSize;
1837      float* pCallAverage = pTimers + 3 * nBlockSize;
1838      float* pTimersExclusive = pTimers + 4 * nBlockSize;
1839      float* pAverageExclusive = pTimers + 5 * nBlockSize;
1840      float* pMaxExclusive = pTimers + 6 * nBlockSize;
1841      MicroProfileCalcTimers(pTimers, pAverage, pMax, pCallAverage, pTimersExclusive, pAverageExclusive, pMaxExclusive, nActiveGroup, nNumTimers);
1842      uint32_t nWidth = 0;
1843      {
1844          uint32_t nMetaIndex = 0;
1845          for(uint32_t i = 1; i ; i <<= 1)
1846          {
1847              if(S.nBars & i)
1848              {
1849                  if(i >= MP_DRAW_META_FIRST)
1850                  {
1851                      if(nMetaIndex < MICROPROFILE_META_MAX && S.MetaCounters[nMetaIndex].pName)
1852                      {
1853                          uint32_t nStrWidth = strlen(S.MetaCounters[nMetaIndex].pName);
1854                          if(S.nBars & MP_DRAW_TIMERS)
1855                              nWidth += 6 + (1+MICROPROFILE_TEXT_WIDTH) * (nStrWidth);
1856                          if(S.nBars & MP_DRAW_AVERAGE)
1857                              nWidth += 6 + (1+MICROPROFILE_TEXT_WIDTH) * (nStrWidth + 4);
1858                          if(S.nBars & MP_DRAW_MAX)
1859                              nWidth += 6 + (1+MICROPROFILE_TEXT_WIDTH) * (nStrWidth + 4);
1860                      }
1861                  }
1862                  else
1863                  {
1864                      nWidth += MICROPROFILE_BAR_WIDTH + 6 + 6 * (1+MICROPROFILE_TEXT_WIDTH);
1865                      if(i & MP_DRAW_CALL_COUNT)
1866                          nWidth += 6 + 6 * MICROPROFILE_TEXT_WIDTH;
1867                  }
1868              }
1869              if(i >= MP_DRAW_META_FIRST)
1870              {
1871                  ++nMetaIndex;
1872              }
1873          }
1874          nWidth += (1+nMaxTimerNameLen) * (MICROPROFILE_TEXT_WIDTH+1);
1875          for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i)
1876          {
1877              uint32_t nY0 = nY + i * (nHeight + 1);
1878              bool bInside = (UI.nActiveMenu == UINT32_MAX) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
1879              MicroProfileDrawBox(nX, nY0, nWidth+nX, nY0 + (nHeight+1)+1, UI.nOpacityBackground | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0)));
1880          }
1881          nX += 10;
1882      }
1883      int nTotalHeight = (nNumTimers+nNumGroups+1) * (nHeight+1);
1884      uint32_t nLegendOffset = 1;
1885      if(S.nBars & MP_DRAW_TIMERS)
1886          nX += MicroProfileDrawBarArray(nX, nY, pTimers, "Time", nTotalHeight) + 1;
1887      if(S.nBars & MP_DRAW_AVERAGE)
1888          nX += MicroProfileDrawBarArray(nX, nY, pAverage, "Average", nTotalHeight) + 1;
1889      if(S.nBars & MP_DRAW_MAX)
1890          nX += MicroProfileDrawBarArray(nX, nY, pMax, (!UI.bShowSpikes) ? "Max Time" : "Max Time, Spike", nTotalHeight, UI.bShowSpikes ? pAverage : NULL) + 1;
1891      if(S.nBars & MP_DRAW_CALL_COUNT)
1892      {
1893          nX += MicroProfileDrawBarArray(nX, nY, pCallAverage, "Call Average", nTotalHeight) + 1;
1894          nX += MicroProfileDrawBarCallCount(nX, nY, "Count") + 1;
1895      }
1896      if(S.nBars & MP_DRAW_TIMERS_EXCLUSIVE)
1897          nX += MicroProfileDrawBarArray(nX, nY, pTimersExclusive, "Exclusive Time", nTotalHeight) + 1;
1898      if(S.nBars & MP_DRAW_AVERAGE_EXCLUSIVE)
1899          nX += MicroProfileDrawBarArray(nX, nY, pAverageExclusive, "Exclusive Average", nTotalHeight) + 1;
1900      if(S.nBars & MP_DRAW_MAX_EXCLUSIVE)
1901          nX += MicroProfileDrawBarArray(nX, nY, pMaxExclusive, (!UI.bShowSpikes) ? "Exclusive Max Time" :"Excl Max Time, Spike", nTotalHeight, UI.bShowSpikes ? pAverageExclusive : NULL) + 1;
1902  
1903      for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
1904      {
1905          if(0 != (S.nBars & (MP_DRAW_META_FIRST<<i)) && S.MetaCounters[i].pName)
1906          {
1907              uint32_t nBufferSize = strlen(S.MetaCounters[i].pName) + 32;
1908              char* buffer = (char*)alloca(nBufferSize);
1909              if(S.nBars & MP_DRAW_TIMERS)
1910                  nX += MicroProfileDrawBarMetaCount(nX, nY, &S.MetaCounters[i].nCounters[0], S.MetaCounters[i].pName, nTotalHeight) + 1;
1911              if(S.nBars & MP_DRAW_AVERAGE)
1912              {
1913                  snprintf(buffer, nBufferSize-1, "%s Avg", S.MetaCounters[i].pName);
1914                  nX += MicroProfileDrawBarMetaAverage(nX, nY, &S.MetaCounters[i].nAggregate[0], buffer, nTotalHeight) + 1;
1915              }
1916              if(S.nBars & MP_DRAW_MAX)
1917              {
1918                  snprintf(buffer, nBufferSize-1, "%s Max", S.MetaCounters[i].pName);
1919                  nX += MicroProfileDrawBarMetaCount(nX, nY, &S.MetaCounters[i].nAggregateMax[0], buffer, nTotalHeight) + 1;
1920              }
1921          }
1922      }
1923      nX = 0;
1924      nY = nHeight + 3 - UI.nOffsetY;
1925      for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i)
1926      {
1927          const uint32_t nY0 = nY + i * (nHeight + 1);
1928          const bool bInside = (UI.nActiveMenu == UINT32_MAX) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
1929          MicroProfileDrawBox(nX, nY0, nTimerWidth, nY0 + (nHeight+1)+1, 0xff0000000 | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0)));
1930      }
1931      nX += MicroProfileDrawBarLegend(nX, nY, nTotalHeight, nTimerWidth-5) + 1;
1932  
1933      for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
1934      {
1935          if(nActiveGroup & (1ULL << j))
1936          {
1937              MicroProfileDrawText(nX, nY + (1+nHeight) * nLegendOffset, UINT32_MAX, S.GroupInfo[j].pName, S.GroupInfo[j].nNameLen);
1938              nLegendOffset += S.GroupInfo[j].nNumTimers+1;
1939          }
1940      }
1941      MicroProfileDrawHeader(nX, nTimerWidth-5, "Group");
1942      MicroProfileDrawTextRight(nTimerWidth-3, MICROPROFILE_TEXT_HEIGHT + 2, UINT32_MAX, "Timer", 5);
1943      MicroProfileDrawLineVertical(nTimerWidth, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1944      MicroProfileDrawLineHorizontal(0, nWidth, 2*MICROPROFILE_TEXT_HEIGHT + 3, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
1945  }
1946  
1947  typedef const char* (*MicroProfileSubmenuCallback)(int, bool* bSelected);
1948  typedef void (*MicroProfileClickCallback)(int);
1949  
1950  
1951  const char* MicroProfileUIMenuMode(int nIndex, bool* bSelected)
1952  {
1953      MicroProfile& S = *MicroProfileGet();
1954      switch(nIndex)
1955      {
1956          case 0:
1957              *bSelected = S.nDisplay == MP_DRAW_DETAILED;
1958              return "Detailed";
1959          case 1:
1960              *bSelected = S.nDisplay == MP_DRAW_BARS;
1961              return "Timers";
1962          case 2:
1963              *bSelected = S.nDisplay == MP_DRAW_HIDDEN;
1964              return "Hidden";
1965          case 3:
1966              *bSelected = true;
1967              return "Off";
1968          case 4:
1969              *bSelected = true;
1970              return "------";
1971          case 5:
1972              *bSelected = S.nForceEnable != 0;
1973              return "Force Enable";
1974  
1975          default: return 0;
1976      }
1977  }
1978  
1979  const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
1980  {
1981      MicroProfile& S = *MicroProfileGet();
1982      *bSelected = false;
1983      if(nIndex == 0)
1984      {
1985          *bSelected = S.nAllGroupsWanted != 0;
1986          return "[ALL]";
1987      }
1988      else
1989      {
1990          nIndex = nIndex-1;
1991          if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
1992          {
1993              MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
1994              static char buffer[MICROPROFILE_NAME_MAX_LEN+32];
1995              if(Item.nIsCategory)
1996              {
1997                  uint64_t nGroupMask = S.CategoryInfo[Item.nIndex].nGroupMask;
1998                  *bSelected = nGroupMask == (nGroupMask & S.nActiveGroupWanted);
1999                  snprintf(buffer, sizeof(buffer)-1, "[%s]", Item.pName);
2000              }
2001              else
2002              {
2003                  *bSelected = 0 != (S.nActiveGroupWanted & (1ULL << Item.nIndex));
2004                  snprintf(buffer, sizeof(buffer)-1, "   %s", Item.pName);
2005              }
2006              return buffer;
2007          }
2008          return 0;
2009      }
2010  }
2011  
2012  const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected)
2013  {
2014      MicroProfile& S = *MicroProfileGet();
2015      if(static_cast<uint32_t>(nIndex) < g_MicroProfileAggregatePresets.size())
2016      {
2017          uint32_t val = g_MicroProfileAggregatePresets[nIndex];
2018          *bSelected = S.nAggregateFlip == val;
2019          if (0 == val)
2020          {
2021              return "Infinite";
2022          }
2023          else
2024          {
2025              static char buf[128];
2026              snprintf(buf, sizeof(buf)-1, "%7u", val);
2027              return buf;
2028          }
2029      }
2030      return 0;
2031  
2032  }
2033  
2034  const char* MicroProfileUIMenuTimers(int nIndex, bool* bSelected)
2035  {
2036      MicroProfile& S = *MicroProfileGet();
2037      *bSelected = 0 != (S.nBars & (1 << nIndex));
2038      switch(nIndex)
2039      {
2040          case 0: return "Time";
2041          case 1: return "Average";
2042          case 2: return "Max";
2043          case 3: return "Call Count";
2044          case 4: return "Exclusive Timers";
2045          case 5: return "Exclusive Average";
2046          case 6: return "Exclusive Max";
2047      }
2048      int nMetaIndex = nIndex - 7;
2049      if(nMetaIndex < MICROPROFILE_META_MAX)
2050      {
2051          return S.MetaCounters[nMetaIndex].pName;
2052      }
2053      return 0;
2054  }
2055  
2056  const char* MicroProfileUIMenuOptions(int nIndex, bool* bSelected)
2057  {
2058      MicroProfile& S = *MicroProfileGet();
2059      if(nIndex >= MICROPROFILE_OPTION_SIZE) return 0;
2060      switch(UI.Options[nIndex].nSubType)
2061      {
2062      case 0:
2063          *bSelected = S.fReferenceTime == g_MicroProfileReferenceTimePresets[UI.Options[nIndex].nIndex];
2064          break;
2065      case 1:
2066          *bSelected = UI.nOpacityBackground>>24 == g_MicroProfileOpacityPresets[UI.Options[nIndex].nIndex];
2067          break;
2068      case 2:
2069          *bSelected = UI.nOpacityForeground>>24 == g_MicroProfileOpacityPresets[UI.Options[nIndex].nIndex];
2070          break;
2071      case 3:
2072          *bSelected = UI.bShowSpikes;
2073          break;
2074  #if MICROPROFILE_CONTEXT_SWITCH_TRACE
2075      case 4:
2076          {
2077              switch(UI.Options[nIndex].nIndex)
2078              {
2079              case 0:
2080                  *bSelected = S.bContextSwitchRunning;
2081                  break;
2082              case 1:
2083                  *bSelected = S.bContextSwitchAllThreads;
2084                  break;
2085              case 2:
2086                  *bSelected = S.bContextSwitchNoBars;
2087                  break;
2088              }
2089          }
2090          break;
2091  #endif
2092      }
2093      return UI.Options[nIndex].Text;
2094  }
2095  
2096  const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
2097  {
2098      static char buf[128];
2099      *bSelected = false;
2100      int nNumPresets = static_cast<int>(g_MicroProfilePresetNames.size());
2101      int nIndexSave = nIndex - nNumPresets - 1;
2102      if (nIndex == nNumPresets)
2103      {
2104          return "--";
2105      }
2106      else if(nIndexSave >=0 && nIndexSave < nNumPresets)
2107      {
2108          snprintf(buf, sizeof(buf)-1, "Save '%s'", g_MicroProfilePresetNames[nIndexSave]);
2109          return buf;
2110      }
2111      else if(nIndex < nNumPresets)
2112      {
2113          snprintf(buf, sizeof(buf)-1, "Load '%s'", g_MicroProfilePresetNames[nIndex]);
2114          return buf;
2115      }
2116      else
2117      {
2118          return 0;
2119      }
2120  }
2121  
2122  const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
2123  {
2124      if(UINT32_MAX == UI.nCustomActive)
2125      {
2126          *bSelected = nIndex == 0;
2127      }
2128      else
2129      {
2130          *bSelected = nIndex-2 == static_cast<int>(UI.nCustomActive);
2131      }
2132      switch(nIndex)
2133      {
2134      case 0: return "Disable";
2135      case 1: return "--";
2136      default:
2137          nIndex -= 2;
2138          if(static_cast<uint32_t>(nIndex) < UI.nCustomCount)
2139          {
2140              return UI.Custom[nIndex].pName;
2141          }
2142          else
2143          {
2144              return 0;
2145          }
2146      }
2147  }
2148  
2149  const char* MicroProfileUIMenuEmpty(int nIndex, bool* bSelected)
2150  {
2151      return 0;
2152  }
2153  
2154  
2155  void MicroProfileUIClickMode(int nIndex)
2156  {
2157      MicroProfile& S = *MicroProfileGet();
2158      switch(nIndex)
2159      {
2160          case 0:
2161              S.nDisplay = MP_DRAW_DETAILED;
2162              break;
2163          case 1:
2164              S.nDisplay = MP_DRAW_BARS;
2165              break;
2166          case 2:
2167              S.nDisplay = MP_DRAW_HIDDEN;
2168              break;
2169          case 3:
2170              S.nDisplay = 0;
2171              break;
2172          case 4:
2173              break;
2174          case 5:
2175              S.nForceEnable = !S.nForceEnable;
2176              break;
2177      }
2178  }
2179  
2180  void MicroProfileUIClickGroups(int nIndex)
2181  {
2182      MicroProfile& S = *MicroProfileGet();
2183      if(nIndex == 0)
2184          S.nAllGroupsWanted = 1-S.nAllGroupsWanted;
2185      else
2186      {
2187          nIndex -= 1;
2188          if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
2189          {
2190              MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
2191              if(Item.nIsCategory)
2192              {
2193                  uint64_t nGroupMask = S.CategoryInfo[Item.nIndex].nGroupMask;
2194                  if(nGroupMask != (nGroupMask & S.nActiveGroupWanted))
2195                  {
2196                      S.nActiveGroupWanted |= nGroupMask;
2197                  }
2198                  else
2199                  {
2200                      S.nActiveGroupWanted &= ~nGroupMask;
2201                  }
2202              }
2203              else
2204              {
2205                  MP_ASSERT(Item.nIndex < S.nGroupCount);
2206                  S.nActiveGroupWanted ^= (1ULL << Item.nIndex);
2207              }
2208          }
2209      }
2210  }
2211  
2212  void MicroProfileUIClickAggregate(int nIndex)
2213  {
2214      MicroProfile& S = *MicroProfileGet();
2215      S.nAggregateFlip = g_MicroProfileAggregatePresets[nIndex];
2216      if(0 == S.nAggregateFlip)
2217      {
2218          S.nAggregateClear = 1;
2219      }
2220  }
2221  
2222  void MicroProfileUIClickTimers(int nIndex)
2223  {
2224      MicroProfile& S = *MicroProfileGet();
2225      S.nBars ^= (1 << nIndex);
2226  }
2227  
2228  void MicroProfileUIClickOptions(int nIndex)
2229  {
2230      MicroProfile& S = *MicroProfileGet();
2231      switch(UI.Options[nIndex].nSubType)
2232      {
2233      case 0:
2234          S.fReferenceTime = g_MicroProfileReferenceTimePresets[UI.Options[nIndex].nIndex];
2235          S.fRcpReferenceTime = 1.f / S.fReferenceTime;
2236          break;
2237      case 1:
2238          UI.nOpacityBackground = g_MicroProfileOpacityPresets[UI.Options[nIndex].nIndex]<<24;
2239          break;
2240      case 2:
2241          UI.nOpacityForeground = g_MicroProfileOpacityPresets[UI.Options[nIndex].nIndex]<<24;
2242          break;
2243      case 3:
2244          UI.bShowSpikes = !UI.bShowSpikes;
2245          break;
2246  #if MICROPROFILE_CONTEXT_SWITCH_TRACE
2247      case 4:
2248          {
2249              switch(UI.Options[nIndex].nIndex)
2250              {
2251              case 0:
2252                  if(S.bContextSwitchRunning)
2253                  {
2254                      MicroProfileStopContextSwitchTrace();
2255                  }
2256                  else
2257                  {
2258                      MicroProfileStartContextSwitchTrace();
2259                  }
2260                  break;
2261              case 1:
2262                  S.bContextSwitchAllThreads = !S.bContextSwitchAllThreads;
2263                  break;
2264              case 2:
2265                  S.bContextSwitchNoBars= !S.bContextSwitchNoBars;
2266                  break;
2267  
2268              }
2269          }
2270          break;
2271  #endif
2272      }
2273  }
2274  
2275  void MicroProfileUIClickPreset(int nIndex)
2276  {
2277      int nNumPresets = static_cast<int>(g_MicroProfilePresetNames.size());
2278      int nIndexSave = nIndex - nNumPresets - 1;
2279      if(nIndexSave >= 0 && nIndexSave < nNumPresets)
2280      {
2281          MicroProfileSavePreset(g_MicroProfilePresetNames[nIndexSave]);
2282      }
2283      else if(nIndex >= 0 && nIndex < nNumPresets)
2284      {
2285          MicroProfileLoadPreset(g_MicroProfilePresetNames[nIndex]);
2286      }
2287  }
2288  
2289  void MicroProfileUIClickCustom(int nIndex)
2290  {
2291      if(nIndex == 0)
2292      {
2293          MicroProfileCustomGroupDisable();
2294      }
2295      else
2296      {
2297          MicroProfileCustomGroupEnable(nIndex-2);
2298      }
2299  
2300  }
2301  
2302  void MicroProfileUIClickEmpty(int nIndex)
2303  {
2304  
2305  }
2306  
2307  
2308  void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
2309  {
2310      MicroProfile& S = *MicroProfileGet();
2311  
2312      uint32_t nX = 0;
2313      uint32_t nY = 0;
2314  
2315  #define SBUF_SIZE 256
2316      char buffer[256];
2317      MicroProfileDrawBox(nX, nY, nX + nWidth, nY + (MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff000000|g_nMicroProfileBackColors[1]);
2318  
2319  #define MICROPROFILE_MENU_MAX 16
2320      const char* pMenuText[MICROPROFILE_MENU_MAX] = {0};
2321      uint32_t    nMenuX[MICROPROFILE_MENU_MAX] = {0};
2322      uint32_t nNumMenuItems = 0;
2323  
2324      int nLen = snprintf(buffer, 127, "MicroProfile");
2325      MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
2326      nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
2327      pMenuText[nNumMenuItems++] = "Mode";
2328      pMenuText[nNumMenuItems++] = "Groups";
2329      char AggregateText[64];
2330      snprintf(AggregateText, sizeof(AggregateText)-1, "Aggregate[%d]", S.nAggregateFlip ? S.nAggregateFlip : S.nAggregateFlipCount);
2331      pMenuText[nNumMenuItems++] = &AggregateText[0];
2332      pMenuText[nNumMenuItems++] = "Timers";
2333      pMenuText[nNumMenuItems++] = "Options";
2334      pMenuText[nNumMenuItems++] = "Preset";
2335      pMenuText[nNumMenuItems++] = "Custom";
2336      const int nPauseIndex = nNumMenuItems;
2337      pMenuText[nNumMenuItems++] = S.nRunning ? "Pause" : "Unpause";
2338      pMenuText[nNumMenuItems++] = "Help";
2339  
2340      if(S.nOverflow)
2341      {
2342          pMenuText[nNumMenuItems++] = "!BUFFERSFULL!";
2343      }
2344  
2345  
2346      if(UI.GroupMenuCount != S.nGroupCount + S.nCategoryCount)
2347      {
2348          UI.GroupMenuCount = S.nGroupCount + S.nCategoryCount;
2349          for(uint32_t i = 0; i < S.nCategoryCount; ++i)
2350          {
2351              UI.GroupMenu[i].nIsCategory = 1;
2352              UI.GroupMenu[i].nCategoryIndex = i;
2353              UI.GroupMenu[i].nIndex = i;
2354              UI.GroupMenu[i].pName = S.CategoryInfo[i].pName;
2355          }
2356          for(uint32_t i = 0; i < S.nGroupCount; ++i)
2357          {
2358              uint32_t idx = i + S.nCategoryCount;
2359              UI.GroupMenu[idx].nIsCategory = 0;
2360              UI.GroupMenu[idx].nCategoryIndex = S.GroupInfo[i].nCategory;
2361              UI.GroupMenu[idx].nIndex = i;
2362              UI.GroupMenu[idx].pName = S.GroupInfo[i].pName;
2363          }
2364          std::sort(&UI.GroupMenu[0], &UI.GroupMenu[UI.GroupMenuCount],
2365              [] (const MicroProfileGroupMenuItem& l, const MicroProfileGroupMenuItem& r) -> bool
2366              {
2367                  if(l.nCategoryIndex < r.nCategoryIndex)
2368                  {
2369                      return true;
2370                  }
2371                  else if(r.nCategoryIndex < l.nCategoryIndex)
2372                  {
2373                      return false;
2374                  }
2375                  if(r.nIsCategory || l.nIsCategory)
2376                  {
2377                      return l.nIsCategory > r.nIsCategory;
2378                  }
2379                  return MP_STRCASECMP(l.pName, r.pName)<0;
2380              }
2381          );
2382      }
2383  
2384      MicroProfileSubmenuCallback GroupCallback[MICROPROFILE_MENU_MAX] =
2385      {
2386          MicroProfileUIMenuMode,
2387          MicroProfileUIMenuGroups,
2388          MicroProfileUIMenuAggregate,
2389          MicroProfileUIMenuTimers,
2390          MicroProfileUIMenuOptions,
2391          MicroProfileUIMenuPreset,
2392          MicroProfileUIMenuCustom,
2393          MicroProfileUIMenuEmpty,
2394          MicroProfileUIMenuEmpty,
2395          MicroProfileUIMenuEmpty,
2396      };
2397  
2398      MicroProfileClickCallback CBClick[MICROPROFILE_MENU_MAX] =
2399      {
2400          MicroProfileUIClickMode,
2401          MicroProfileUIClickGroups,
2402          MicroProfileUIClickAggregate,
2403          MicroProfileUIClickTimers,
2404          MicroProfileUIClickOptions,
2405          MicroProfileUIClickPreset,
2406          MicroProfileUIClickCustom,
2407          MicroProfileUIClickEmpty,
2408          MicroProfileUIClickEmpty,
2409          MicroProfileUIClickEmpty,
2410      };
2411  
2412  
2413      uint32_t nSelectMenu = UINT32_MAX;
2414      for(uint32_t i = 0; i < nNumMenuItems; ++i)
2415      {
2416          nMenuX[i] = nX;
2417          uint32_t nLen = (uint32_t)strlen(pMenuText[i]);
2418          uint32_t nEnd = nX + nLen * (MICROPROFILE_TEXT_WIDTH+1);
2419          if(UI.nMouseY <= MICROPROFILE_TEXT_HEIGHT && UI.nMouseX <= nEnd && UI.nMouseX >= nX)
2420          {
2421              MicroProfileDrawBox(nX-1, nY, nX + nLen * (MICROPROFILE_TEXT_WIDTH+1), nY +(MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff888888);
2422              nSelectMenu = i;
2423              if((UI.nMouseLeft || UI.nMouseRight) && i == (uint32_t)nPauseIndex)
2424              {
2425                  S.nToggleRunning = 1;
2426              }
2427          }
2428          MicroProfileDrawText(nX, nY, UINT32_MAX, pMenuText[i], (uint32_t)strlen(pMenuText[i]));
2429          nX += (nLen+1) * (MICROPROFILE_TEXT_WIDTH+1);
2430      }
2431      uint32_t nMenu = nSelectMenu != UINT32_MAX ? nSelectMenu : UI.nActiveMenu;
2432      UI.nActiveMenu = nMenu;
2433      if(UINT32_MAX != nMenu)
2434      {
2435          nX = nMenuX[nMenu];
2436          nY += MICROPROFILE_TEXT_HEIGHT+1;
2437          MicroProfileSubmenuCallback CB = GroupCallback[nMenu];
2438          int nNumLines = 0;
2439          bool bSelected = false;
2440          const char* pString = CB(nNumLines, &bSelected);
2441          uint32_t nWidth = 0, nHeight = 0;
2442          while(pString)
2443          {
2444              nWidth = MicroProfileMax<int>(nWidth, (int)strlen(pString));
2445              nNumLines++;
2446              pString = CB(nNumLines, &bSelected);
2447          }
2448          nWidth = (2+nWidth) * (MICROPROFILE_TEXT_WIDTH+1);
2449          nHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
2450          if(UI.nMouseY <= nY + nHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nWidth + 0 && UI.nMouseX >= nX - 0)
2451          {
2452              UI.nActiveMenu = nMenu;
2453          }
2454          else if(nSelectMenu == UINT32_MAX)
2455          {
2456              UI.nActiveMenu = UINT32_MAX;
2457          }
2458          MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
2459          for(int i = 0; i < nNumLines; ++i)
2460          {
2461              bool bSelected = false;
2462              const char* pString = CB(i, &bSelected);
2463              if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
2464              {
2465                  if(UI.nMouseLeft || UI.nMouseRight)
2466                  {
2467                      CBClick[nMenu](i);
2468                  }
2469                  MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
2470              }
2471              int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
2472              MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
2473              nY += MICROPROFILE_TEXT_HEIGHT+1;
2474          }
2475      }
2476  
2477  
2478      {
2479          static char FrameTimeMessage[64];
2480          float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
2481          uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1;
2482          float fMs = fToMs * (S.nFlipTicks);
2483          float fAverageMs = fToMs * (S.nFlipAggregateDisplay / nAggregateFrames);
2484          float fMaxMs = fToMs * S.nFlipMaxDisplay;
2485          int nLen = snprintf(FrameTimeMessage, sizeof(FrameTimeMessage)-1, "Time[%6.2f] Avg[%6.2f] Max[%6.2f]", fMs, fAverageMs, fMaxMs);
2486          pMenuText[nNumMenuItems++] = &FrameTimeMessage[0];
2487          MicroProfileDrawText(nWidth - nLen * (MICROPROFILE_TEXT_WIDTH+1), 0, UINT32_MAX, FrameTimeMessage, nLen);
2488      }
2489  }
2490  
2491  
2492  void MicroProfileMoveGraph()
2493  {
2494  
2495      int nZoom = UI.nMouseWheelDelta;
2496      int nPanX = 0;
2497      int nPanY = 0;
2498      static int X = 0, Y = 0;
2499      if(UI.nMouseDownLeft && !UI.nModDown)
2500      {
2501          nPanX = UI.nMouseX - X;
2502          nPanY = UI.nMouseY - Y;
2503      }
2504      X = UI.nMouseX;
2505      Y = UI.nMouseY;
2506  
2507      if(nZoom)
2508      {
2509          float fOldRange = UI.fDetailedRange;
2510          if(nZoom>0)
2511          {
2512              UI.fDetailedRangeTarget = UI.fDetailedRange *= UI.nModDown ? 1.40f : 1.05f;
2513          }
2514          else
2515          {
2516              float fNewDetailedRange = UI.fDetailedRange / (UI.nModDown ? 1.40f : 1.05f);
2517              if(fNewDetailedRange < 1e-4f) //100ns
2518                  fNewDetailedRange = 1e-4f;
2519              UI.fDetailedRangeTarget = UI.fDetailedRange = fNewDetailedRange;
2520          }
2521  
2522          float fDiff = fOldRange - UI.fDetailedRange;
2523          float fMousePrc = MicroProfileMax((float)UI.nMouseX / UI.nWidth ,0.f);
2524          UI.fDetailedOffsetTarget = UI.fDetailedOffset += fDiff * fMousePrc;
2525  
2526      }
2527      if(nPanX)
2528      {
2529          UI.fDetailedOffsetTarget = UI.fDetailedOffset += -nPanX * UI.fDetailedRange / UI.nWidth;
2530      }
2531      UI.nOffsetY -= nPanY;
2532      UI.nOffsetX += nPanX;
2533      if(UI.nOffsetX > 0)
2534          UI.nOffsetX = 0;
2535      if(UI.nOffsetY<0)
2536          UI.nOffsetY = 0;
2537  }
2538  
2539  void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
2540  {
2541      if(UINT32_MAX != UI.nCustomActive)
2542      {
2543          MicroProfile& S = *MicroProfileGet();
2544          MP_ASSERT(UI.nCustomActive < MICROPROFILE_CUSTOM_MAX);
2545          MicroProfileCustom* pCustom = &UI.Custom[UI.nCustomActive];
2546          uint32_t nCount = pCustom->nNumTimers;
2547          uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1;
2548          uint32_t nExtraOffset = 1 + ((pCustom->nFlags & MICROPROFILE_CUSTOM_STACK) != 0 ? 3 : 0);
2549          uint32_t nOffsetYBase = nHeight - (nExtraOffset+nCount)* (1+MICROPROFILE_TEXT_HEIGHT) - MICROPROFILE_CUSTOM_PADDING;
2550          uint32_t nOffsetY = nOffsetYBase;
2551          float fReference = pCustom->fReference;
2552          float fRcpReference = 1.f / fReference;
2553          uint32_t nReducedWidth = UI.nWidth - 2*MICROPROFILE_CUSTOM_PADDING - MICROPROFILE_GRAPH_WIDTH;
2554  
2555          char Buffer[MICROPROFILE_NAME_MAX_LEN*2+1];
2556          float* pTime = (float*)alloca(sizeof(float)*nCount);
2557          float* pTimeAvg = (float*)alloca(sizeof(float)*nCount);
2558          float* pTimeMax = (float*)alloca(sizeof(float)*nCount);
2559          uint32_t* pColors = (uint32_t*)alloca(sizeof(uint32_t)*nCount);
2560          uint32_t nMaxOffsetX = 0;
2561          MicroProfileDrawBox(MICROPROFILE_CUSTOM_PADDING-1, nOffsetY-1, MICROPROFILE_CUSTOM_PADDING+nReducedWidth+1, UI.nHeight - MICROPROFILE_CUSTOM_PADDING+1, 0x88000000|g_nMicroProfileBackColors[0]);
2562  
2563          for(uint32_t i = 0; i < nCount; ++i)
2564          {
2565              uint16_t nTimerIndex = MicroProfileGetTimerIndex(pCustom->pTimers[i]);
2566              uint16_t nGroupIndex = MicroProfileGetGroupIndex(pCustom->pTimers[i]);
2567              float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[nGroupIndex].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
2568              pTime[i] = S.Frame[nTimerIndex].nTicks * fToMs;
2569              pTimeAvg[i] = fToMs * (S.Aggregate[nTimerIndex].nTicks / nAggregateFrames);
2570              pTimeMax[i] = fToMs * (S.AggregateMax[nTimerIndex]);
2571              pColors[i] = S.TimerInfo[nTimerIndex].nColor;
2572          }
2573  
2574          MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 3*MICROPROFILE_TEXT_WIDTH, nOffsetY, UINT32_MAX, "Avg", sizeof("Avg")-1);
2575          MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 13*MICROPROFILE_TEXT_WIDTH, nOffsetY, UINT32_MAX, "Max", sizeof("Max")-1);
2576          for(uint32_t i = 0; i < nCount; ++i)
2577          {
2578              nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
2579              uint16_t nTimerIndex = MicroProfileGetTimerIndex(pCustom->pTimers[i]);
2580              uint16_t nGroupIndex = MicroProfileGetGroupIndex(pCustom->pTimers[i]);
2581              MicroProfileTimerInfo* pTimerInfo = &S.TimerInfo[nTimerIndex];
2582              int nSize;
2583              uint32_t nOffsetX = MICROPROFILE_CUSTOM_PADDING;
2584              nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeAvg[i]);
2585              MicroProfileDrawText(nOffsetX, nOffsetY, UINT32_MAX, Buffer, nSize);
2586              nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
2587              nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeMax[i]);
2588              MicroProfileDrawText(nOffsetX, nOffsetY, UINT32_MAX, Buffer, nSize);
2589              nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
2590              nSize = snprintf(Buffer, sizeof(Buffer)-1, "%s:%s", S.GroupInfo[nGroupIndex].pName, pTimerInfo->pName);
2591              MicroProfileDrawText(nOffsetX, nOffsetY, pTimerInfo->nColor, Buffer, nSize);
2592              nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
2593              nMaxOffsetX = MicroProfileMax(nMaxOffsetX, nOffsetX);
2594          }
2595          uint32_t nMaxWidth = nReducedWidth- nMaxOffsetX;
2596  
2597          if(pCustom->nFlags & MICROPROFILE_CUSTOM_BARS)
2598          {
2599              nOffsetY = nOffsetYBase;
2600              float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? pTimeMax : pTimeAvg;
2601              const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? "Max" : "Avg";
2602              MicroProfileDrawText(nMaxOffsetX, nOffsetY, UINT32_MAX, pString, static_cast<uint32_t>(strlen(pString)));
2603              int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
2604              MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, UINT32_MAX, Buffer, nSize);
2605              for(uint32_t i = 0; i < nCount; ++i)
2606              {
2607                  nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
2608                  uint32_t nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
2609                  MicroProfileDrawBox(nMaxOffsetX, nOffsetY, nMaxOffsetX+nWidth, nOffsetY+MICROPROFILE_TEXT_HEIGHT, pColors[i]|0xff000000);
2610              }
2611          }
2612          if(pCustom->nFlags & MICROPROFILE_CUSTOM_STACK)
2613          {
2614              nOffsetY += 2*(1+MICROPROFILE_TEXT_HEIGHT);
2615              const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? "Max" : "Avg";
2616              MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, UINT32_MAX, pString, static_cast<uint32_t>(strlen(pString)));
2617              int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
2618              MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, UINT32_MAX, Buffer, nSize);
2619              nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
2620              float fPosX = MICROPROFILE_CUSTOM_PADDING;
2621              float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? pTimeMax : pTimeAvg;
2622              for(uint32_t i = 0; i < nCount; ++i)
2623              {
2624                  float fWidth = pMs[i] * fRcpReference * nReducedWidth;
2625                  uint32_t nX = fPosX;
2626                  fPosX += fWidth;
2627                  uint32_t nXEnd = fPosX;
2628                  if(nX < nXEnd)
2629                  {
2630                      MicroProfileDrawBox(nX, nOffsetY, nXEnd, nOffsetY+MICROPROFILE_TEXT_HEIGHT, pColors[i]|0xff000000);
2631                  }
2632              }
2633          }
2634      }
2635  }
2636  void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
2637  {
2638      MICROPROFILE_SCOPE(g_MicroProfileDraw);
2639      MicroProfile& S = *MicroProfileGet();
2640  
2641      {
2642          static int once = 0;
2643          if(0 == once)
2644          {
2645              std::recursive_mutex& m = MicroProfileGetMutex();
2646              m.lock();
2647              MicroProfileInitUI();
2648  
2649  
2650  
2651              uint32_t nDisplay = S.nDisplay;
2652              MicroProfileLoadPreset(MICROPROFILE_DEFAULT_PRESET);
2653              once++;
2654              S.nDisplay = nDisplay;// dont load display, just state
2655              m.unlock();
2656  
2657          }
2658      }
2659  
2660  
2661      if(S.nDisplay)
2662      {
2663          std::recursive_mutex& m = MicroProfileGetMutex();
2664          m.lock();
2665          UI.nWidth = nWidth;
2666          UI.nHeight = nHeight;
2667          UI.nHoverToken = MICROPROFILE_INVALID_TOKEN;
2668          UI.nHoverTime = 0;
2669          UI.nHoverFrame = -1;
2670          if(S.nDisplay != MP_DRAW_DETAILED)
2671              S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = UINT32_MAX;
2672          MicroProfileMoveGraph();
2673  
2674  
2675          if(S.nDisplay == MP_DRAW_DETAILED)
2676          {
2677              MicroProfileDrawDetailedView(nWidth, nHeight);
2678          }
2679          else if(S.nDisplay == MP_DRAW_BARS && S.nBars)
2680          {
2681              MicroProfileDrawBarView(nWidth, nHeight);
2682          }
2683  
2684          MicroProfileDrawMenu(nWidth, nHeight);
2685          bool bMouseOverGraph = MicroProfileDrawGraph(nWidth, nHeight);
2686          MicroProfileDrawCustom(nWidth, nHeight);
2687          bool bHidden = S.nDisplay == MP_DRAW_HIDDEN;
2688          if(!bHidden)
2689          {
2690              uint32_t nLockedToolTipX = 3;
2691              bool bDeleted = false;
2692              for(int i = 0; i < MICROPROFILE_TOOLTIP_MAX_LOCKED; ++i)
2693              {
2694                  int nIndex = (g_MicroProfileUI.LockedToolTipFront + i) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
2695                  if(g_MicroProfileUI.LockedToolTips[nIndex].ppStrings[0])
2696                  {
2697                      uint32_t nToolTipWidth = 0, nToolTipHeight = 0;
2698                      MicroProfileFloatWindowSize(g_MicroProfileUI.LockedToolTips[nIndex].ppStrings, g_MicroProfileUI.LockedToolTips[nIndex].nNumStrings, 0, nToolTipWidth, nToolTipHeight, 0);
2699                      uint32_t nStartY = nHeight - nToolTipHeight - 2;
2700                      if(!bDeleted && UI.nMouseY > nStartY && UI.nMouseX > nLockedToolTipX && UI.nMouseX <= nLockedToolTipX + nToolTipWidth && (UI.nMouseLeft || UI.nMouseRight) )
2701                      {
2702                          bDeleted = true;
2703                          int j = i;
2704                          for(; j < MICROPROFILE_TOOLTIP_MAX_LOCKED-1; ++j)
2705                          {
2706                              int nIndex0 = (g_MicroProfileUI.LockedToolTipFront + j) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
2707                              int nIndex1 = (g_MicroProfileUI.LockedToolTipFront + j+1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
2708                              MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nIndex0], &g_MicroProfileUI.LockedToolTips[nIndex1]);
2709                          }
2710                          MicroProfileStringArrayClear(&g_MicroProfileUI.LockedToolTips[(g_MicroProfileUI.LockedToolTipFront + j) % MICROPROFILE_TOOLTIP_MAX_LOCKED]);
2711                      }
2712                      else
2713                      {
2714                          MicroProfileDrawFloatWindow(nLockedToolTipX, nHeight-nToolTipHeight-2, &g_MicroProfileUI.LockedToolTips[nIndex].ppStrings[0], g_MicroProfileUI.LockedToolTips[nIndex].nNumStrings, g_MicroProfileUI.nLockedToolTipColor[nIndex]);
2715                          nLockedToolTipX += nToolTipWidth + 4;
2716                      }
2717                  }
2718              }
2719  
2720              if(UI.nActiveMenu == 8)
2721              {
2722                  if(S.nDisplay & MP_DRAW_DETAILED)
2723                  {
2724                      MicroProfileStringArray DetailedHelp;
2725                      MicroProfileStringArrayClear(&DetailedHelp);
2726                      MicroProfileStringArrayFormat(&DetailedHelp, "%s", MICROPROFILE_HELP_LEFT);
2727                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Toggle Graph");
2728                      MicroProfileStringArrayFormat(&DetailedHelp, "%s", MICROPROFILE_HELP_ALT);
2729                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Zoom");
2730                      MicroProfileStringArrayFormat(&DetailedHelp, "%s + %s", MICROPROFILE_HELP_MOD, MICROPROFILE_HELP_LEFT);
2731                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Lock Tooltip");
2732                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Drag");
2733                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Pan View");
2734                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Mouse Wheel");
2735                      MicroProfileStringArrayAddLiteral(&DetailedHelp, "Zoom");
2736                      MicroProfileDrawFloatWindow(nWidth, MICROPROFILE_FRAME_HISTORY_HEIGHT+20, DetailedHelp.ppStrings, DetailedHelp.nNumStrings, 0xff777777);
2737  
2738                      MicroProfileStringArray DetailedHistoryHelp;
2739                      MicroProfileStringArrayClear(&DetailedHistoryHelp);
2740                      MicroProfileStringArrayFormat(&DetailedHistoryHelp, "%s", MICROPROFILE_HELP_LEFT);
2741                      MicroProfileStringArrayAddLiteral(&DetailedHistoryHelp, "Center View");
2742                      MicroProfileStringArrayFormat(&DetailedHistoryHelp, "%s", MICROPROFILE_HELP_ALT);
2743                      MicroProfileStringArrayAddLiteral(&DetailedHistoryHelp, "Zoom to frame");
2744                      MicroProfileDrawFloatWindow(nWidth, 20, DetailedHistoryHelp.ppStrings, DetailedHistoryHelp.nNumStrings, 0xff777777);
2745  
2746  
2747  
2748                  }
2749                  else if(0 != (S.nDisplay & MP_DRAW_BARS) && S.nBars)
2750                  {
2751                      MicroProfileStringArray BarHelp;
2752                      MicroProfileStringArrayClear(&BarHelp);
2753                      MicroProfileStringArrayFormat(&BarHelp, "%s", MICROPROFILE_HELP_LEFT);
2754                      MicroProfileStringArrayAddLiteral(&BarHelp, "Toggle Graph");
2755                      MicroProfileStringArrayFormat(&BarHelp, "%s + %s", MICROPROFILE_HELP_MOD, MICROPROFILE_HELP_LEFT);
2756                      MicroProfileStringArrayAddLiteral(&BarHelp, "Lock Tooltip");
2757                      MicroProfileStringArrayAddLiteral(&BarHelp, "Drag");
2758                      MicroProfileStringArrayAddLiteral(&BarHelp, "Pan View");
2759                      MicroProfileDrawFloatWindow(nWidth, MICROPROFILE_FRAME_HISTORY_HEIGHT+20, BarHelp.ppStrings, BarHelp.nNumStrings, 0xff777777);
2760  
2761                  }
2762                  MicroProfileStringArray Debug;
2763                  MicroProfileStringArrayClear(&Debug);
2764                  MicroProfileStringArrayAddLiteral(&Debug, "Memory Usage");
2765                  MicroProfileStringArrayFormat(&Debug, "%4.2fmb", S.nMemUsage / (1024.f * 1024.f));
2766                  MicroProfileStringArrayAddLiteral(&Debug, "Web Server Port");
2767                  MicroProfileStringArrayFormat(&Debug, "%d", MicroProfileWebServerPort());
2768                  uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
2769                  MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
2770                  MicroProfileFrameState* pFrameNext = &S.Frames[nFrameNext];
2771  
2772  
2773                  MicroProfileStringArrayAddLiteral(&Debug, "");
2774                  MicroProfileStringArrayAddLiteral(&Debug, "");
2775                  MicroProfileStringArrayAddLiteral(&Debug, "Usage");
2776                  MicroProfileStringArrayAddLiteral(&Debug, "markers [frames] ");
2777  
2778  #if MICROPROFILE_CONTEXT_SWITCH_TRACE
2779                  MicroProfileStringArrayAddLiteral(&Debug, "Context Switch");
2780                  MicroProfileStringArrayFormat(&Debug, "%9d [%7d]", S.nContextSwitchUsage, MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE / S.nContextSwitchUsage );
2781  #endif
2782  
2783                  for(int i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
2784                  {
2785                      if(pFrameCurrent->nLogStart[i] && S.Pool[i])
2786                      {
2787                          uint32_t nEnd = pFrameNext->nLogStart[i];
2788                          uint32_t nStart = pFrameCurrent->nLogStart[i];
2789                          uint32_t nUsage = nStart < nEnd ? (nEnd - nStart) : (nEnd + MICROPROFILE_BUFFER_SIZE - nStart);
2790                          uint32_t nFrameSupport = MICROPROFILE_BUFFER_SIZE / nUsage;
2791                          MicroProfileStringArrayFormat(&Debug, "%s", &S.Pool[i]->ThreadName[0]);
2792                          MicroProfileStringArrayFormat(&Debug, "%9d [%7d]", nUsage, nFrameSupport);
2793                      }
2794                  }
2795  
2796                  MicroProfileDrawFloatWindow(0, nHeight-10, Debug.ppStrings, Debug.nNumStrings, 0xff777777);
2797              }
2798  
2799  
2800  
2801              if(UI.nActiveMenu == UINT32_MAX && !bMouseOverGraph)
2802              {
2803                  if(UI.nHoverToken != MICROPROFILE_INVALID_TOKEN)
2804                  {
2805                      MicroProfileDrawFloatTooltip(UI.nMouseX, UI.nMouseY, UI.nHoverToken, UI.nHoverTime);
2806                  }
2807                  else if(S.nContextSwitchHoverThreadAfter != UINT32_MAX && S.nContextSwitchHoverThreadBefore != UINT32_MAX)
2808                  {
2809                      float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
2810                      MicroProfileStringArray ToolTip;
2811                      MicroProfileStringArrayClear(&ToolTip);
2812                      MicroProfileStringArrayAddLiteral(&ToolTip, "Context Switch");
2813                      MicroProfileStringArrayFormat(&ToolTip, "%04x", S.nContextSwitchHoverThread);
2814                      MicroProfileStringArrayAddLiteral(&ToolTip, "Before");
2815                      MicroProfileStringArrayFormat(&ToolTip, "%04x", S.nContextSwitchHoverThreadBefore);
2816                      MicroProfileStringArrayAddLiteral(&ToolTip, "After");
2817                      MicroProfileStringArrayFormat(&ToolTip, "%04x", S.nContextSwitchHoverThreadAfter);
2818                      MicroProfileStringArrayAddLiteral(&ToolTip, "Duration");
2819                      int64_t nDifference = MicroProfileLogTickDifference(S.nContextSwitchHoverTickIn, S.nContextSwitchHoverTickOut);
2820                      MicroProfileStringArrayFormat(&ToolTip, "%6.2fms", fToMs * nDifference );
2821                      MicroProfileStringArrayAddLiteral(&ToolTip, "CPU");
2822                      MicroProfileStringArrayFormat(&ToolTip, "%d", S.nContextSwitchHoverCpu);
2823                      MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, UINT32_MAX);
2824  
2825  
2826                  }
2827                  else if(UI.nHoverFrame != -1)
2828                  {
2829                      uint32_t nNextFrame = (UI.nHoverFrame+1)%MICROPROFILE_MAX_FRAME_HISTORY;
2830                      int64_t nTick = S.Frames[UI.nHoverFrame].nFrameStartCpu;
2831                      int64_t nTickNext = S.Frames[nNextFrame].nFrameStartCpu;
2832                      int64_t nTickGpu = S.Frames[UI.nHoverFrame].nFrameStartGpu;
2833                      int64_t nTickNextGpu = S.Frames[nNextFrame].nFrameStartGpu;
2834  
2835                      float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
2836                      float fToMsGpu = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu());
2837                      float fMs = fToMs * (nTickNext - nTick);
2838                      float fMsGpu = fToMsGpu * (nTickNextGpu - nTickGpu);
2839                      MicroProfileStringArray ToolTip;
2840                      MicroProfileStringArrayClear(&ToolTip);
2841                      MicroProfileStringArrayFormat(&ToolTip, "Frame %d", UI.nHoverFrame);
2842      #if MICROPROFILE_DEBUG
2843                      MicroProfileStringArrayFormat(&ToolTip, "%p", &S.Frames[UI.nHoverFrame]);
2844      #else
2845                      MicroProfileStringArrayAddLiteral(&ToolTip, "");
2846      #endif
2847                      MicroProfileStringArrayAddLiteral(&ToolTip, "CPU Time");
2848                      MicroProfileStringArrayFormat(&ToolTip, "%6.2fms", fMs);
2849                      MicroProfileStringArrayAddLiteral(&ToolTip, "GPU Time");
2850                      MicroProfileStringArrayFormat(&ToolTip, "%6.2fms", fMsGpu);
2851                      #if MICROPROFILE_DEBUG
2852                      for(int i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
2853                      {
2854                          if(S.Frames[UI.nHoverFrame].nLogStart[i])
2855                          {
2856                              MicroProfileStringArrayFormat(&ToolTip, "%d", i);
2857                              MicroProfileStringArrayFormat(&ToolTip, "%d", S.Frames[UI.nHoverFrame].nLogStart[i]);
2858                          }
2859                      }
2860                      #endif
2861                      MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, UINT32_MAX);
2862                  }
2863                  if(UI.nMouseLeft)
2864                  {
2865                      if(UI.nHoverToken != MICROPROFILE_INVALID_TOKEN)
2866                          MicroProfileToggleGraph(UI.nHoverToken);
2867                  }
2868              }
2869          }
2870  
2871  #if MICROPROFILE_DRAWCURSOR
2872          {
2873              float fCursor[8] =
2874              {
2875                  MicroProfileMax(0, (int)UI.nMouseX-3), UI.nMouseY,
2876                  MicroProfileMin(nWidth, UI.nMouseX+3), UI.nMouseY,
2877                  UI.nMouseX, MicroProfileMax((int)UI.nMouseY-3, 0),
2878                  UI.nMouseX, MicroProfileMin(nHeight, UI.nMouseY+3),
2879              };
2880              MicroProfileDrawLine2D(2, &fCursor[0], 0xff00ff00);
2881              MicroProfileDrawLine2D(2, &fCursor[4], 0xff00ff00);
2882          }
2883  #endif
2884          m.unlock();
2885      }
2886      else if(UI.nCustomActive != UINT32_MAX)
2887      {
2888          std::recursive_mutex& m = MicroProfileGetMutex();
2889          m.lock();
2890          MicroProfileDrawGraph(nWidth, nHeight);
2891          MicroProfileDrawCustom(nWidth, nHeight);
2892          m.unlock();
2893  
2894      }
2895      UI.nMouseLeft = UI.nMouseRight = 0;
2896      UI.nMouseLeftMod = UI.nMouseRightMod = 0;
2897      UI.nMouseWheelDelta = 0;
2898      if(S.nOverflow)
2899          S.nOverflow--;
2900  
2901      UI.fDetailedOffset = UI.fDetailedOffset + (UI.fDetailedOffsetTarget - UI.fDetailedOffset) * MICROPROFILE_ANIM_DELAY_PRC;
2902      UI.fDetailedRange = UI.fDetailedRange + (UI.fDetailedRangeTarget - UI.fDetailedRange) * MICROPROFILE_ANIM_DELAY_PRC;
2903  
2904  
2905  }
2906  
2907  bool MicroProfileIsDrawing()
2908  {
2909      MicroProfile& S = *MicroProfileGet();
2910      return S.nDisplay != 0;
2911  }
2912  
2913  void MicroProfileToggleGraph(MicroProfileToken nToken)
2914  {
2915      MicroProfile& S = *MicroProfileGet();
2916      uint32_t nTimerId = MicroProfileGetTimerIndex(nToken);
2917      nToken &= 0xffff;
2918      int32_t nMinSort = 0x7fffffff;
2919      int32_t nFreeIndex = -1;
2920      int32_t nMinIndex = 0;
2921      int32_t nMaxSort = 0x80000000;
2922      for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
2923      {
2924          if(S.Graph[i].nToken == MICROPROFILE_INVALID_TOKEN)
2925              nFreeIndex = i;
2926          if(S.Graph[i].nToken == nToken)
2927          {
2928              S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN;
2929              S.TimerInfo[nTimerId].bGraph = false;
2930              return;
2931          }
2932          if(S.Graph[i].nKey < nMinSort)
2933          {
2934              nMinSort = S.Graph[i].nKey;
2935              nMinIndex = i;
2936          }
2937          if(S.Graph[i].nKey > nMaxSort)
2938          {
2939              nMaxSort = S.Graph[i].nKey;
2940          }
2941      }
2942      int nIndex = nFreeIndex > -1 ? nFreeIndex : nMinIndex;
2943      if (nFreeIndex == -1)
2944      {
2945          uint32_t idx = MicroProfileGetTimerIndex(S.Graph[nIndex].nToken);
2946          S.TimerInfo[idx].bGraph = false;
2947      }
2948      S.Graph[nIndex].nToken = nToken;
2949      S.Graph[nIndex].nKey = nMaxSort+1;
2950      memset(&S.Graph[nIndex].nHistory[0], 0, sizeof(S.Graph[nIndex].nHistory));
2951      S.TimerInfo[nTimerId].bGraph = true;
2952  }
2953  
2954  
2955  void MicroProfileMousePosition(uint32_t nX, uint32_t nY, int nWheelDelta)
2956  {
2957      UI.nMouseX = nX;
2958      UI.nMouseY = nY;
2959      UI.nMouseWheelDelta = nWheelDelta;
2960  }
2961  
2962  void MicroProfileModKey(uint32_t nKeyState)
2963  {
2964      UI.nModDown = nKeyState ? 1 : 0;
2965  }
2966  
2967  void MicroProfileClearGraph()
2968  {
2969      MicroProfile& S = *MicroProfileGet();
2970      for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
2971      {
2972          if(S.Graph[i].nToken != 0)
2973          {
2974              S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN;
2975          }
2976      }
2977  }
2978  
2979  void MicroProfileMouseButton(uint32_t nLeft, uint32_t nRight)
2980  {
2981      bool bCanRelease = abs((int)(UI.nMouseDownX - UI.nMouseX)) + abs((int)(UI.nMouseDownY - UI.nMouseY)) < 3;
2982  
2983      if(0 == nLeft && UI.nMouseDownLeft && bCanRelease)
2984      {
2985          if(UI.nModDown)
2986              UI.nMouseLeftMod = 1;
2987          else
2988              UI.nMouseLeft = 1;
2989      }
2990  
2991      if(0 == nRight && UI.nMouseDownRight && bCanRelease)
2992      {
2993          if(UI.nModDown)
2994              UI.nMouseRightMod = 1;
2995          else
2996              UI.nMouseRight = 1;
2997      }
2998      if((nLeft || nRight) && !(UI.nMouseDownLeft || UI.nMouseDownRight))
2999      {
3000          UI.nMouseDownX = UI.nMouseX;
3001          UI.nMouseDownY = UI.nMouseY;
3002      }
3003  
3004      UI.nMouseDownLeft = nLeft;
3005      UI.nMouseDownRight = nRight;
3006  
3007  }
3008  
3009  void MicroProfileDrawLineVertical(int nX, int nTop, int nBottom, uint32_t nColor)
3010  {
3011      MicroProfileDrawBox(nX, nTop, nX + 1, nBottom, nColor);
3012  }
3013  
3014  void MicroProfileDrawLineHorizontal(int nLeft, int nRight, int nY, uint32_t nColor)
3015  {
3016      MicroProfileDrawBox(nLeft, nY, nRight, nY + 1, nColor);
3017  }
3018  
3019  
3020  
3021  #include <stdio.h>
3022  
3023  #define MICROPROFILE_PRESET_HEADER_MAGIC 0x28586813
3024  #define MICROPROFILE_PRESET_HEADER_VERSION 0x00000102
3025  struct MicroProfilePresetHeader
3026  {
3027      uint32_t nMagic;
3028      uint32_t nVersion;
3029      //groups, threads, aggregate, reference frame, graphs timers
3030      uint32_t nGroups[MICROPROFILE_MAX_GROUPS];
3031      uint32_t nThreads[MICROPROFILE_MAX_THREADS];
3032      uint32_t nGraphName[MICROPROFILE_MAX_GRAPHS];
3033      uint32_t nGraphGroupName[MICROPROFILE_MAX_GRAPHS];
3034      uint32_t nAllGroupsWanted;
3035      uint32_t nAllThreadsWanted;
3036      uint32_t nAggregateFlip;
3037      float fReferenceTime;
3038      uint32_t nBars;
3039      uint32_t nDisplay;
3040      uint32_t nOpacityBackground;
3041      uint32_t nOpacityForeground;
3042      uint32_t nShowSpikes;
3043  };
3044  
3045  #ifndef MICROPROFILE_PRESET_FILENAME_FUNC
3046  #define MICROPROFILE_PRESET_FILENAME_FUNC MicroProfilePresetFilename
3047  static const char* MicroProfilePresetFilename(const char* pSuffix)
3048  {
3049      static char filename[512];
3050      snprintf(filename, sizeof(filename)-1, ".microprofilepreset.%s", pSuffix);
3051      return filename;
3052  }
3053  #endif
3054  
3055  void MicroProfileSavePreset(const char* pPresetName)
3056  {
3057      std::lock_guard<std::recursive_mutex> Lock(MicroProfileGetMutex());
3058      FILE* F = fopen(MICROPROFILE_PRESET_FILENAME_FUNC(pPresetName), "wb");
3059      if(!F) return;
3060  
3061      MicroProfile& S = *MicroProfileGet();
3062  
3063      MicroProfilePresetHeader Header;
3064      memset(&Header, 0, sizeof(Header));
3065      Header.nAggregateFlip = S.nAggregateFlip;
3066      Header.nBars = S.nBars;
3067      Header.fReferenceTime = S.fReferenceTime;
3068      Header.nAllGroupsWanted = S.nAllGroupsWanted;
3069      Header.nAllThreadsWanted = S.nAllThreadsWanted;
3070      Header.nMagic = MICROPROFILE_PRESET_HEADER_MAGIC;
3071      Header.nVersion = MICROPROFILE_PRESET_HEADER_VERSION;
3072      Header.nDisplay = S.nDisplay;
3073      Header.nOpacityBackground = UI.nOpacityBackground;
3074      Header.nOpacityForeground = UI.nOpacityForeground;
3075      Header.nShowSpikes = UI.bShowSpikes ? 1 : 0;
3076      fwrite(&Header, sizeof(Header), 1, F);
3077      uint64_t nMask = 1;
3078      for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
3079      {
3080          if(S.nActiveGroupWanted & nMask)
3081          {
3082              uint32_t offset = ftell(F);
3083              const char* pName = S.GroupInfo[i].pName;
3084              int nLen = (int)strlen(pName)+1;
3085              fwrite(pName, nLen, 1, F);
3086              Header.nGroups[i] = offset;
3087          }
3088          nMask <<= 1;
3089      }
3090      for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
3091      {
3092          MicroProfileThreadLog* pLog = S.Pool[i];
3093          if(pLog && S.nThreadActive[i])
3094          {
3095              uint32_t nOffset = ftell(F);
3096              const char* pName = &pLog->ThreadName[0];
3097              int nLen = (int)strlen(pName)+1;
3098              fwrite(pName, nLen, 1, F);
3099              Header.nThreads[i] = nOffset;
3100          }
3101      }
3102      for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
3103      {
3104          MicroProfileToken nToken = S.Graph[i].nToken;
3105          if(nToken != MICROPROFILE_INVALID_TOKEN)
3106          {
3107              uint32_t nGroupIndex = MicroProfileGetGroupIndex(nToken);
3108              uint32_t nTimerIndex = MicroProfileGetTimerIndex(nToken);
3109              const char* pGroupName = S.GroupInfo[nGroupIndex].pName;
3110              const char* pTimerName = S.TimerInfo[nTimerIndex].pName;
3111              MP_ASSERT(pGroupName);
3112              MP_ASSERT(pTimerName);
3113              int nGroupLen = (int)strlen(pGroupName)+1;
3114              int nTimerLen = (int)strlen(pTimerName)+1;
3115  
3116              uint32_t nOffsetGroup = ftell(F);
3117              fwrite(pGroupName, nGroupLen, 1, F);
3118              uint32_t nOffsetTimer = ftell(F);
3119              fwrite(pTimerName, nTimerLen, 1, F);
3120              Header.nGraphName[i] = nOffsetTimer;
3121              Header.nGraphGroupName[i] = nOffsetGroup;
3122          }
3123      }
3124      fseek(F, 0, SEEK_SET);
3125      fwrite(&Header, sizeof(Header), 1, F);
3126  
3127      fclose(F);
3128  
3129  }
3130  
3131  
3132  
3133  void MicroProfileLoadPreset(const char* pSuffix)
3134  {
3135      std::lock_guard<std::recursive_mutex> Lock(MicroProfileGetMutex());
3136      FILE* F = fopen(MICROPROFILE_PRESET_FILENAME_FUNC(pSuffix), "rb");
3137      if(!F)
3138      {
3139          return;
3140      }
3141      fseek(F, 0, SEEK_END);
3142      int nSize = ftell(F);
3143      char* const pBuffer = (char*)alloca(nSize);
3144      fseek(F, 0, SEEK_SET);
3145      int nRead = (int)fread(pBuffer, nSize, 1, F);
3146      fclose(F);
3147      if(1 != nRead)
3148          return;
3149  
3150      MicroProfile& S = *MicroProfileGet();
3151  
3152      MicroProfilePresetHeader& Header = *(MicroProfilePresetHeader*)pBuffer;
3153  
3154      if(Header.nMagic != MICROPROFILE_PRESET_HEADER_MAGIC || Header.nVersion != MICROPROFILE_PRESET_HEADER_VERSION)
3155      {
3156          return;
3157      }
3158  
3159      S.nAggregateFlip = Header.nAggregateFlip;
3160      S.nBars = Header.nBars;
3161      S.fReferenceTime = Header.fReferenceTime;
3162      S.fRcpReferenceTime = 1.f / Header.fReferenceTime;
3163      S.nAllGroupsWanted = Header.nAllGroupsWanted;
3164      S.nAllThreadsWanted = Header.nAllThreadsWanted;
3165      S.nDisplay = Header.nDisplay;
3166      S.nActiveGroupWanted = 0;
3167      UI.nOpacityBackground = Header.nOpacityBackground;
3168      UI.nOpacityForeground = Header.nOpacityForeground;
3169      UI.bShowSpikes = Header.nShowSpikes == 1;
3170  
3171      memset(&S.nThreadActive[0], 0, sizeof(S.nThreadActive));
3172  
3173      for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
3174      {
3175          if(Header.nGroups[i])
3176          {
3177              const char* pGroupName = pBuffer + Header.nGroups[i];
3178              for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
3179              {
3180                  if(0 == MP_STRCASECMP(pGroupName, S.GroupInfo[j].pName))
3181                  {
3182                      S.nActiveGroupWanted |= (1ULL << j);
3183                  }
3184              }
3185          }
3186      }
3187      for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
3188      {
3189          if(Header.nThreads[i])
3190          {
3191              const char* pThreadName = pBuffer + Header.nThreads[i];
3192              for(uint32_t j = 0; j < MICROPROFILE_MAX_THREADS; ++j)
3193              {
3194                  MicroProfileThreadLog* pLog = S.Pool[j];
3195                  if(pLog && 0 == MP_STRCASECMP(pThreadName, &pLog->ThreadName[0]))
3196                  {
3197                      S.nThreadActive[j] = 1;
3198                  }
3199              }
3200          }
3201      }
3202      for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
3203      {
3204          MicroProfileToken nPrevToken = S.Graph[i].nToken;
3205          S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN;
3206          if(Header.nGraphName[i] && Header.nGraphGroupName[i])
3207          {
3208              const char* pGraphName = pBuffer + Header.nGraphName[i];
3209              const char* pGraphGroupName = pBuffer + Header.nGraphGroupName[i];
3210              for(uint32_t j = 0; j < S.nTotalTimers; ++j)
3211              {
3212                  uint64_t nGroupIndex = S.TimerInfo[j].nGroupIndex;
3213                  if(0 == MP_STRCASECMP(pGraphName, S.TimerInfo[j].pName) && 0 == MP_STRCASECMP(pGraphGroupName, S.GroupInfo[nGroupIndex].pName))
3214                  {
3215                      MicroProfileToken nToken = MicroProfileMakeToken(1ULL << nGroupIndex, (uint16_t)j);
3216                      S.Graph[i].nToken = nToken;         // note: group index is stored here but is checked without in MicroProfileToggleGraph()!
3217                      S.TimerInfo[j].bGraph = true;
3218                      if(nToken != nPrevToken)
3219                      {
3220                          memset(&S.Graph[i].nHistory, 0, sizeof(S.Graph[i].nHistory));
3221                      }
3222                      break;
3223                  }
3224              }
3225          }
3226      }
3227  }
3228  
3229  uint32_t MicroProfileCustomGroupFind(const char* pCustomName)
3230  {
3231      for(uint32_t i = 0; i < UI.nCustomCount; ++i)
3232      {
3233          if(!MP_STRCASECMP(pCustomName, UI.Custom[i].pName))
3234          {
3235              return i;
3236          }
3237      }
3238      return UINT32_MAX;
3239  }
3240  
3241  uint32_t MicroProfileCustomGroup(const char* pCustomName)
3242  {
3243      for(uint32_t i = 0; i < UI.nCustomCount; ++i)
3244      {
3245          if(!MP_STRCASECMP(pCustomName, UI.Custom[i].pName))
3246          {
3247              return i;
3248          }
3249      }
3250      MP_ASSERT(UI.nCustomCount < MICROPROFILE_CUSTOM_MAX);
3251      uint32_t nIndex = UI.nCustomCount;
3252      UI.nCustomCount++;
3253      memset(&UI.Custom[nIndex], 0, sizeof(UI.Custom[nIndex]));
3254      size_t nLen = strlen(pCustomName);
3255      if(nLen > MICROPROFILE_NAME_MAX_LEN-1)
3256          nLen = MICROPROFILE_NAME_MAX_LEN-1;
3257      memcpy(&UI.Custom[nIndex].pName[0], pCustomName, nLen);
3258      UI.Custom[nIndex].pName[nLen] = '\0';
3259      return nIndex;
3260  }
3261  void MicroProfileCustomGroup(const char* pCustomName, uint32_t nMaxTimers, uint32_t nAggregateFlip, float fReferenceTime, uint32_t nFlags)
3262  {
3263      uint32_t nIndex = MicroProfileCustomGroup(pCustomName);
3264      MP_ASSERT(UI.Custom[nIndex].pTimers == 0);//only call once!
3265      UI.Custom[nIndex].pTimers = &UI.CustomTimer[UI.nCustomTimerCount];
3266      UI.Custom[nIndex].nMaxTimers = nMaxTimers;
3267      UI.Custom[nIndex].fReference = fReferenceTime;
3268      UI.nCustomTimerCount += nMaxTimers;
3269      MP_ASSERT(UI.nCustomTimerCount <= MICROPROFILE_CUSTOM_MAX_TIMERS); //bump MICROPROFILE_CUSTOM_MAX_TIMERS
3270      UI.Custom[nIndex].nFlags = nFlags;
3271      UI.Custom[nIndex].nAggregateFlip = nAggregateFlip;
3272  }
3273  
3274  void MicroProfileCustomGroupEnable(uint32_t nIndex)
3275  {
3276      if(nIndex < UI.nCustomCount)
3277      {
3278          MicroProfile& S = *MicroProfileGet();
3279          S.nForceGroupUI = UI.Custom[nIndex].nGroupMask;
3280          MicroProfileSetAggregateFrames(UI.Custom[nIndex].nAggregateFlip);
3281          S.fReferenceTime = UI.Custom[nIndex].fReference;
3282          S.fRcpReferenceTime = 1.f / UI.Custom[nIndex].fReference;
3283          UI.nCustomActive = nIndex;
3284  
3285          for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
3286          {
3287              if(S.Graph[i].nToken != MICROPROFILE_INVALID_TOKEN)
3288              {
3289                  uint32_t nTimerId = MicroProfileGetTimerIndex(S.Graph[i].nToken);
3290                  S.TimerInfo[nTimerId].bGraph = false;
3291                  S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN;
3292              }
3293          }
3294  
3295          for(uint32_t i = 0; i < UI.Custom[nIndex].nNumTimers; ++i)
3296          {
3297              if(i == MICROPROFILE_MAX_GRAPHS)
3298              {
3299                  break;
3300              }
3301              S.Graph[i].nToken = UI.Custom[nIndex].pTimers[i];
3302              S.Graph[i].nKey = i;
3303              uint32_t nTimerId = MicroProfileGetTimerIndex(S.Graph[i].nToken);
3304              S.TimerInfo[nTimerId].bGraph = true;
3305          }
3306      }
3307  }
3308  
3309  void MicroProfileCustomGroupToggle(const char* pCustomName)
3310  {
3311      uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
3312      if(nIndex == UINT32_MAX || nIndex == UI.nCustomActive)
3313      {
3314          MicroProfileCustomGroupDisable();
3315      }
3316      else
3317      {
3318          MicroProfileCustomGroupEnable(nIndex);
3319      }
3320  }
3321  
3322  void MicroProfileCustomGroupEnable(const char* pCustomName)
3323  {
3324      uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
3325      MicroProfileCustomGroupEnable(nIndex);
3326  }
3327  void MicroProfileCustomGroupDisable()
3328  {
3329      MicroProfile& S = *MicroProfileGet();
3330      S.nForceGroupUI = 0;
3331      UI.nCustomActive = UINT32_MAX;
3332  }
3333  
3334  void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer)
3335  {
3336      uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
3337      if(UINT32_MAX == nIndex)
3338      {
3339          return;
3340      }
3341      uint32_t nTimerIndex = UI.Custom[nIndex].nNumTimers;
3342      MP_ASSERT(nTimerIndex < UI.Custom[nIndex].nMaxTimers);
3343      uint64_t nToken = MicroProfileFindToken(pGroup, pTimer);
3344      MP_ASSERT(nToken != MICROPROFILE_INVALID_TOKEN); //Timer must be registered first.
3345      UI.Custom[nIndex].pTimers[nTimerIndex] = nToken;
3346      uint16_t nGroup = MicroProfileGetGroupIndex(nToken);
3347      UI.Custom[nIndex].nGroupMask |= (1ULL << nGroup);
3348      UI.Custom[nIndex].nNumTimers++;
3349  }
3350  
3351  #undef UI
3352  
3353  #endif
3354  #endif