xenia-canary/third_party/microprofile/microprofile.h

3848 lines
110 KiB
C++

#pragma once
// This is free and unencumbered software released into the public domain.
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// For more information, please refer to <http://unlicense.org/>
//
// ***********************************************************************
//
//
//
//
// Howto:
// Call these functions from your code:
// MicroProfileOnThreadCreate
// MicroProfileMouseButton
// MicroProfileMousePosition
// MicroProfileModKey
// MicroProfileFlip <-- Call this once per frame
// MicroProfileDraw <-- Call this once per frame
// MicroProfileToggleDisplayMode <-- Bind to a key to toggle profiling
// MicroProfileTogglePause <-- Bind to a key to toggle pause
//
// Use these macros in your code in blocks you want to time:
//
// MICROPROFILE_DECLARE
// MICROPROFILE_DEFINE
// MICROPROFILE_DECLARE_GPU
// MICROPROFILE_DEFINE_GPU
// MICROPROFILE_SCOPE
// MICROPROFILE_SCOPEI
// MICROPROFILE_SCOPEGPU
// MICROPROFILE_SCOPEGPUI
// MICROPROFILE_META
//
//
// Usage:
//
// {
// MICROPROFILE_SCOPEI("GroupName", "TimerName", nColorRgb):
// ..Code to be timed..
// }
//
// MICROPROFILE_DECLARE / MICROPROFILE_DEFINE allows defining groups in a shared place, to ensure sorting of the timers
//
// (in global scope)
// MICROPROFILE_DEFINE(g_ProfileFisk, "Fisk", "Skalle", nSomeColorRgb);
//
// (in some other file)
// MICROPROFILE_DECLARE(g_ProfileFisk);
//
// void foo(){
// MICROPROFILE_SCOPE(g_ProfileFisk);
// }
//
// Once code is instrumented the gui is activeted by calling MicroProfileToggleDisplayMode or by clicking in the upper left corner of
// the screen
//
// The following functions must be implemented before the profiler is usable
// debug render:
// void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, uint32_t nNumCharacters);
// void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType = MicroProfileBoxTypeFlat);
// void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, uint32_t nColor);
// Gpu time stamps:
// uint32_t MicroProfileGpuInsertTimeStamp();
// uint64_t MicroProfileGpuGetTimeStamp(uint32_t nKey);
// uint64_t MicroProfileTicksPerSecondGpu();
// threading:
// const char* MicroProfileGetThreadName(); Threadnames in detailed view
#ifndef MICROPROFILE_ENABLED
#define MICROPROFILE_ENABLED 1
#endif
#include <stdint.h>
typedef uint64_t MicroProfileToken;
typedef uint16_t MicroProfileGroupId;
#if 0 == MICROPROFILE_ENABLED
#define MICROPROFILE_DECLARE(var)
#define MICROPROFILE_DEFINE(var, group, name, color)
#define MICROPROFILE_DECLARE_GPU(var)
#define MICROPROFILE_DEFINE_GPU(var, group, name, color)
#define MICROPROFILE_SCOPE(var) do{}while(0)
#define MICROPROFILE_SCOPEI(group, name, color) do{}while(0)
#define MICROPROFILE_SCOPEGPU(var) do{}while(0)
#define MICROPROFILE_SCOPEGPUI(group, name, color) do{}while(0)
#define MICROPROFILE_META_CPU(name, count)
#define MICROPROFILE_META_GPU(name, count)
#define MICROPROFILE_FORCEENABLECPUGROUP(s) do{} while(0)
#define MICROPROFILE_FORCEDISABLECPUGROUP(s) do{} while(0)
#define MICROPROFILE_FORCEENABLEGPUGROUP(s) do{} while(0)
#define MICROPROFILE_FORCEDISABLEGPUGROUP(s) do{} while(0)
#define MICROPROFILE_SCOPE_TOKEN(token)
#define MicroProfileGetTime(group, name) 0.f
#define MicroProfileOnThreadCreate(foo) do{}while(0)
#define MicroProfileFlip() do{}while(0)
#define MicroProfileSetAggregateFrames(a) do{}while(0)
#define MicroProfileGetAggregateFrames() 0
#define MicroProfileGetCurrentAggregateFrames() 0
#define MicroProfileTogglePause() do{}while(0)
#define MicroProfileToggleAllGroups() do{} while(0)
#define MicroProfileDumpTimers() do{}while(0)
#define MicroProfileShutdown() do{}while(0)
#define MicroProfileSetForceEnable(a) do{} while(0)
#define MicroProfileGetForceEnable() false
#define MicroProfileSetEnableAllGroups(a) do{} while(0)
#define MicroProfileGetEnableAllGroups() false
#define MicroProfileSetForceMetaCounters(a)
#define MicroProfileGetForceMetaCounters() 0
#define MicroProfileDumpHtml(c) do{} while(0)
#define MicroProfileWebServerPort() ((uint32_t)-1)
#else
#include <stdint.h>
#include <string.h>
#include <thread>
#include <mutex>
#include <atomic>
#ifndef MICROPROFILE_API
#define MICROPROFILE_API
#endif
MICROPROFILE_API int64_t MicroProfileTicksPerSecondCpu();
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <unistd.h>
#include <libkern/OSAtomic.h>
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define MICROPROFILE_IOS
#endif
#define MP_TICK() mach_absolute_time()
inline int64_t MicroProfileTicksPerSecondCpu()
{
static int64_t nTicksPerSecond = 0;
if(nTicksPerSecond == 0)
{
mach_timebase_info_data_t sTimebaseInfo;
mach_timebase_info(&sTimebaseInfo);
nTicksPerSecond = 1000000000ll * sTimebaseInfo.denom / sTimebaseInfo.numer;
}
return nTicksPerSecond;
}
#define MP_BREAK() __builtin_trap()
#define MP_THREAD_LOCAL __thread
#define MP_STRCASECMP strcasecmp
#define MP_GETCURRENTTHREADID() (uint64_t)pthread_self()
typedef uint64_t ThreadIdType;
#elif defined(_WIN32)
int64_t MicroProfileGetTick();
#define MP_TICK() MicroProfileGetTick()
#define MP_BREAK() __debugbreak()
#define MP_THREAD_LOCAL __declspec(thread)
#define MP_STRCASECMP _stricmp
#define MP_GETCURRENTTHREADID() GetCurrentThreadId()
typedef uint32_t ThreadIdType;
#elif defined(__linux__)
#include <unistd.h>
#include <time.h>
inline int64_t MicroProfileTicksPerSecondCpu()
{
return 1000000000ll;
}
inline int64_t MicroProfileGetTick()
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return 1000000000ll * ts.tv_sec + ts.tv_nsec;
}
#define MP_TICK() MicroProfileGetTick()
#define MP_BREAK() __builtin_trap()
#define MP_THREAD_LOCAL __thread
#define MP_STRCASECMP strcasecmp
#define MP_GETCURRENTTHREADID() (uint64_t)pthread_self()
typedef uint64_t ThreadIdType;
#endif
#ifndef MP_GETCURRENTTHREADID
#define MP_GETCURRENTTHREADID() 0
typedef uint32_t ThreadIdType;
#endif
#define MP_ASSERT(a) do{if(!(a)){MP_BREAK();} }while(0)
#define MICROPROFILE_DECLARE(var) extern MicroProfileToken g_mp_##var
#define MICROPROFILE_DEFINE(var, group, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu)
#define MICROPROFILE_DECLARE_GPU(var) extern MicroProfileToken g_mp_##var
#define MICROPROFILE_DEFINE_GPU(var, group, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeGpu)
#define MICROPROFILE_TOKEN_PASTE0(a, b) a ## b
#define MICROPROFILE_TOKEN_PASTE(a, b) MICROPROFILE_TOKEN_PASTE0(a,b)
#define MICROPROFILE_SCOPE(var) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(g_mp_##var)
#define MICROPROFILE_SCOPE_TOKEN(token) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(token)
#define MICROPROFILE_SCOPEI(group, name, color) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu); MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo,__LINE__)( MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__))
#define MICROPROFILE_SCOPEGPU(var) MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(g_mp_##var)
#define MICROPROFILE_SCOPEGPUI(group, name, color) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeGpu); MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(foo,__LINE__)( MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__))
#define MICROPROFILE_META_CPU(name, count) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_meta,__LINE__) = MicroProfileGetMetaToken(name); MicroProfileMetaUpdate(MICROPROFILE_TOKEN_PASTE(g_mp_meta,__LINE__), count, MicroProfileTokenTypeCpu)
#define MICROPROFILE_META_GPU(name, count) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_meta,__LINE__) = MicroProfileGetMetaToken(name); MicroProfileMetaUpdate(MICROPROFILE_TOKEN_PASTE(g_mp_meta,__LINE__), count, MicroProfileTokenTypeGpu)
#ifndef MICROPROFILE_USE_THREAD_NAME_CALLBACK
#define MICROPROFILE_USE_THREAD_NAME_CALLBACK 0
#endif
#ifndef MICROPROFILE_GPU_FRAME_DELAY
#define MICROPROFILE_GPU_FRAME_DELAY 3 //must be > 0
#endif
#ifndef MICROPROFILE_PER_THREAD_BUFFER_SIZE
#define MICROPROFILE_PER_THREAD_BUFFER_SIZE (2048<<10)
#endif
#ifndef MICROPROFILE_MAX_FRAME_HISTORY
#define MICROPROFILE_MAX_FRAME_HISTORY 512
#endif
#ifndef MICROPROFILE_PRINTF
#define MICROPROFILE_PRINTF printf
#endif
#ifndef MICROPROFILE_META_MAX
#define MICROPROFILE_META_MAX 8
#endif
#ifndef MICROPROFILE_WEBSERVER_PORT
#define MICROPROFILE_WEBSERVER_PORT 1338
#endif
#ifndef MICROPROFILE_WEBSERVER
#define MICROPROFILE_WEBSERVER 1
#endif
#ifndef MICROPROFILE_WEBSERVER_MAXFRAMES
#define MICROPROFILE_WEBSERVER_MAXFRAMES 30
#endif
#ifndef MICROPROFILE_GPU_TIMERS
#define MICROPROFILE_GPU_TIMERS 1
#endif
#ifndef MICROPROFILE_NAME_MAX_LEN
#define MICROPROFILE_NAME_MAX_LEN 64
#endif
#define MICROPROFILE_FORCEENABLECPUGROUP(s) MicroProfileForceEnableGroup(s, MicroProfileTokenTypeCpu)
#define MICROPROFILE_FORCEDISABLECPUGROUP(s) MicroProfileForceDisableGroup(s, MicroProfileTokenTypeCpu)
#define MICROPROFILE_FORCEENABLEGPUGROUP(s) MicroProfileForceEnableGroup(s, MicroProfileTokenTypeGpu)
#define MICROPROFILE_FORCEDISABLEGPUGROUP(s) MicroProfileForceDisableGroup(s, MicroProfileTokenTypeGpu)
#define MICROPROFILE_INVALID_TICK ((uint64_t)-1)
#define MICROPROFILE_GROUP_MASK_ALL 0xffffffffffff
#define MICROPROFILE_INVALID_TOKEN (uint64_t)-1
enum MicroProfileTokenType
{
MicroProfileTokenTypeCpu,
MicroProfileTokenTypeGpu,
};
enum MicroProfileBoxType
{
MicroProfileBoxTypeBar,
MicroProfileBoxTypeFlat,
};
// struct MicroProfileState
// {
// uint32_t nDisplay;
// uint32_t nAllGroupsWanted;
// uint64_t nActiveGroupWanted;
// uint32_t nAllThreadsWanted;
// uint32_t nAggregateFlip;
// uint32_t nBars;
// float fReferenceTime;
// };
struct MicroProfile;
MICROPROFILE_API void MicroProfileInit();
MICROPROFILE_API void MicroProfileShutdown();
MICROPROFILE_API MicroProfileToken MicroProfileFindToken(const char* sGroup, const char* sName);
MICROPROFILE_API MicroProfileToken MicroProfileGetToken(const char* sGroup, const char* sName, uint32_t nColor, MicroProfileTokenType Token = MicroProfileTokenTypeCpu);
MICROPROFILE_API MicroProfileToken MicroProfileGetMetaToken(const char* pName);
MICROPROFILE_API void MicroProfileMetaUpdate(MicroProfileToken, int nCount, MicroProfileTokenType eTokenType);
MICROPROFILE_API uint64_t MicroProfileEnter(MicroProfileToken nToken);
MICROPROFILE_API void MicroProfileLeave(MicroProfileToken nToken, uint64_t nTick);
MICROPROFILE_API uint64_t MicroProfileGpuEnter(MicroProfileToken nToken);
MICROPROFILE_API void MicroProfileGpuLeave(MicroProfileToken nToken, uint64_t nTick);
inline uint16_t MicroProfileGetTimerIndex(MicroProfileToken t){ return (t&0xffff); }
inline uint64_t MicroProfileGetGroupMask(MicroProfileToken t){ return ((t>>16)&MICROPROFILE_GROUP_MASK_ALL);}
inline MicroProfileToken MicroProfileMakeToken(uint64_t nGroupMask, uint16_t nTimer){ return (nGroupMask<<16) | nTimer;}
MICROPROFILE_API void MicroProfileFlip(); //! called once per frame.
MICROPROFILE_API void MicroProfileTogglePause();
// MICROPROFILE_API void MicroProfileGetState(MicroProfileState* pStateOut);
// MICROPROFILE_API void MicroProfileSetState(MicroProfileState* pStateIn);
MICROPROFILE_API void MicroProfileForceEnableGroup(const char* pGroup, MicroProfileTokenType Type);
MICROPROFILE_API void MicroProfileForceDisableGroup(const char* pGroup, MicroProfileTokenType Type);
MICROPROFILE_API float MicroProfileGetTime(const char* pGroup, const char* pName);
MICROPROFILE_API void MicroProfileOnThreadCreate(const char* pThreadName); //should be called from newly created threads
MICROPROFILE_API void MicroProfileOnThreadExit(); //call on exit to reuse log
MICROPROFILE_API void MicroProfileInitThreadLog();
MICROPROFILE_API void MicroProfileSetForceEnable(bool bForceEnable);
MICROPROFILE_API bool MicroProfileGetForceEnable();
MICROPROFILE_API void MicroProfileSetEnableAllGroups(bool bEnable);
MICROPROFILE_API bool MicroProfileGetEnableAllGroups();
MICROPROFILE_API void MicroProfileSetForceMetaCounters(bool bEnable);
MICROPROFILE_API bool MicroProfileGetForceMetaCounters();
MICROPROFILE_API void MicroProfileSetAggregateFrames(int frames);
MICROPROFILE_API int MicroProfileGetAggregateFrames();
MICROPROFILE_API int MicroProfileGetCurrentAggregateFrames();
MICROPROFILE_API MicroProfile* MicroProfileGet();
MICROPROFILE_API void MicroProfileGetRange(uint32_t nPut, uint32_t nGet, uint32_t nRange[2][2]);
MICROPROFILE_API std::recursive_mutex& MicroProfileGetMutex();
MICROPROFILE_API void MicroProfileStartContextSwitchTrace();
MICROPROFILE_API void MicroProfileStopContextSwitchTrace();
MICROPROFILE_API bool MicroProfileIsLocalThread(uint32_t nThreadId);
#if MICROPROFILE_WEBSERVER
MICROPROFILE_API void MicroProfileDumpHtml(const char* pFile);
MICROPROFILE_API uint32_t MicroProfileWebServerPort();
#else
#define MicroProfileDumpHtml(c) do{} while(0)
#define MicroProfileWebServerPort() ((uint32_t)-1)
#endif
#if MICROPROFILE_GPU_TIMERS
MICROPROFILE_API uint32_t MicroProfileGpuInsertTimeStamp();
MICROPROFILE_API uint64_t MicroProfileGpuGetTimeStamp(uint32_t nKey);
MICROPROFILE_API uint64_t MicroProfileTicksPerSecondGpu();
#else
#define MicroProfileGpuInsertTimeStamp() 1
#define MicroProfileGpuGetTimeStamp(a) 0
#define MicroProfileTicksPerSecondGpu() 1
#endif
#if MICROPROFILE_USE_THREAD_NAME_CALLBACK
MICROPROFILE_API const char* MicroProfileGetThreadName();
#else
#define MicroProfileGetThreadName() "<implement MicroProfileGetThreadName to get threadnames>"
#endif
struct MicroProfileScopeHandler
{
MicroProfileToken nToken;
uint64_t nTick;
MicroProfileScopeHandler(MicroProfileToken Token):nToken(Token)
{
nTick = MicroProfileEnter(nToken);
}
~MicroProfileScopeHandler()
{
MicroProfileLeave(nToken, nTick);
}
};
struct MicroProfileScopeGpuHandler
{
MicroProfileToken nToken;
uint64_t nTick;
MicroProfileScopeGpuHandler(MicroProfileToken Token):nToken(Token)
{
nTick = MicroProfileGpuEnter(nToken);
}
~MicroProfileScopeGpuHandler()
{
MicroProfileGpuLeave(nToken, nTick);
}
};
#define MICROPROFILE_MAX_TIMERS 1024
#define MICROPROFILE_MAX_GROUPS 48 //dont bump! no. of bits used it bitmask
#define MICROPROFILE_MAX_GRAPHS 5
#define MICROPROFILE_GRAPH_HISTORY 128
#define MICROPROFILE_BUFFER_SIZE ((MICROPROFILE_PER_THREAD_BUFFER_SIZE)/sizeof(MicroProfileLogEntry))
#define MICROPROFILE_MAX_CONTEXT_SWITCH_THREADS 256
#define MICROPROFILE_STACK_MAX 32
//#define MICROPROFILE_MAX_PRESETS 5
#define MICROPROFILE_ANIM_DELAY_PRC 0.5f
#define MICROPROFILE_GAP_TIME 50 //extra ms to fetch to close timers from earlier frames
#ifndef MICROPROFILE_MAX_THREADS
#define MICROPROFILE_MAX_THREADS 32
#endif
#ifndef MICROPROFILE_UNPACK_RED
#define MICROPROFILE_UNPACK_RED(c) ((c)>>16)
#endif
#ifndef MICROPROFILE_UNPACK_GREEN
#define MICROPROFILE_UNPACK_GREEN(c) ((c)>>8)
#endif
#ifndef MICROPROFILE_UNPACK_BLUE
#define MICROPROFILE_UNPACK_BLUE(c) ((c))
#endif
#ifndef MICROPROFILE_DEFAULT_PRESET
#define MICROPROFILE_DEFAULT_PRESET "Default"
#endif
#ifndef MICROPROFILE_CONTEXT_SWITCH_TRACE
#ifdef _WIN32
#define MICROPROFILE_CONTEXT_SWITCH_TRACE 1
#else
#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0
#endif
#endif
#if MICROPROFILE_CONTEXT_SWITCH_TRACE
#define MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE (128*1024) //2mb with 16 byte entry size
#else
#define MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE (1)
#endif
#ifdef _WIN32
#include <basetsd.h>
typedef UINT_PTR MpSocket;
#else
typedef int MpSocket;
#endif
enum MicroProfileDrawMask
{
MP_DRAW_OFF = 0x0,
MP_DRAW_BARS = 0x1,
MP_DRAW_DETAILED = 0x2,
MP_DRAW_HIDDEN = 0x3,
};
enum MicroProfileDrawBarsMask
{
MP_DRAW_TIMERS = 0x1,
MP_DRAW_AVERAGE = 0x2,
MP_DRAW_MAX = 0x4,
MP_DRAW_CALL_COUNT = 0x8,
MP_DRAW_TIMERS_EXCLUSIVE = 0x10,
MP_DRAW_AVERAGE_EXCLUSIVE = 0x20,
MP_DRAW_MAX_EXCLUSIVE = 0x40,
MP_DRAW_META_FIRST = 0x80,
MP_DRAW_ALL = 0xffffffff,
};
typedef uint64_t MicroProfileLogEntry;
struct MicroProfileTimer
{
uint64_t nTicks;
uint32_t nCount;
};
struct MicroProfileGroupInfo
{
char pName[MICROPROFILE_NAME_MAX_LEN];
uint32_t nNameLen;
uint32_t nGroupIndex;
uint32_t nNumTimers;
uint32_t nMaxTimerNameLen;
MicroProfileTokenType Type;
};
struct MicroProfileTimerInfo
{
MicroProfileToken nToken;
uint32_t nTimerIndex;
uint32_t nGroupIndex;
char pName[MICROPROFILE_NAME_MAX_LEN];
uint32_t nNameLen;
uint32_t nColor;
bool bGraph;
};
struct MicroProfileGraphState
{
int64_t nHistory[MICROPROFILE_GRAPH_HISTORY];
MicroProfileToken nToken;
int32_t nKey;
};
struct MicroProfileContextSwitch
{
ThreadIdType nThreadOut;
ThreadIdType nThreadIn;
int64_t nCpu : 8;
int64_t nTicks : 56;
};
struct MicroProfileFrameState
{
int64_t nFrameStartCpu;
int64_t nFrameStartGpu;
uint32_t nLogStart[MICROPROFILE_MAX_THREADS];
};
struct MicroProfileThreadLog
{
MicroProfileLogEntry Log[MICROPROFILE_BUFFER_SIZE];
std::atomic<uint32_t> nPut;
std::atomic<uint32_t> nGet;
uint32_t nActive;
uint32_t nGpu;
ThreadIdType nThreadId;
uint32_t nStack[MICROPROFILE_STACK_MAX];
int64_t nChildTickStack[MICROPROFILE_STACK_MAX];
uint32_t nStackPos;
enum
{
THREAD_MAX_LEN = 64,
};
char ThreadName[64];
int nFreeListNext;
};
struct MicroProfile
{
uint32_t nTotalTimers;
uint32_t nGroupCount;
uint32_t nAggregateClear;
uint32_t nAggregateFlip;
uint32_t nAggregateFlipCount;
uint32_t nAggregateFrames;
uint64_t nAggregateFlipTick;
uint32_t nDisplay;
uint32_t nBars;
uint64_t nActiveGroup;
uint32_t nActiveBars;
uint64_t nForceGroup;
uint32_t nForceEnable;
uint32_t nForceMetaCounters;
uint64_t nActiveGroupWanted;
uint32_t nAllGroupsWanted;
uint32_t nAllThreadsWanted;
uint32_t nOverflow;
uint64_t nGroupMask;
uint32_t nRunning;
uint32_t nToggleRunning;
uint32_t nMaxGroupSize;
uint32_t nDumpHtmlNextFrame;
char HtmlDumpPath[512];
int64_t nPauseTicks;
float fReferenceTime;
float fRcpReferenceTime;
MicroProfileGroupInfo GroupInfo[MICROPROFILE_MAX_GROUPS];
MicroProfileTimerInfo TimerInfo[MICROPROFILE_MAX_TIMERS];
MicroProfileTimer AggregateTimers[MICROPROFILE_MAX_TIMERS];
uint64_t MaxTimers[MICROPROFILE_MAX_TIMERS];
uint64_t AggregateTimersExclusive[MICROPROFILE_MAX_TIMERS];
uint64_t MaxTimersExclusive[MICROPROFILE_MAX_TIMERS];
MicroProfileTimer Frame[MICROPROFILE_MAX_TIMERS];
uint64_t FrameExclusive[MICROPROFILE_MAX_TIMERS];
MicroProfileTimer Aggregate[MICROPROFILE_MAX_TIMERS];
uint64_t AggregateMax[MICROPROFILE_MAX_TIMERS];
uint64_t AggregateExclusive[MICROPROFILE_MAX_TIMERS];
uint64_t AggregateMaxExclusive[MICROPROFILE_MAX_TIMERS];
struct
{
uint64_t nCounters[MICROPROFILE_MAX_TIMERS];
const char* pName;
} MetaCounters[MICROPROFILE_META_MAX];
MicroProfileGraphState Graph[MICROPROFILE_MAX_GRAPHS];
uint32_t nGraphPut;
uint32_t nThreadActive[MICROPROFILE_MAX_THREADS];
MicroProfileThreadLog* Pool[MICROPROFILE_MAX_THREADS];
uint32_t nNumLogs;
uint32_t nMemUsage;
int nFreeListHead;
uint32_t nFrameCurrent;
uint32_t nFrameCurrentIndex;
uint32_t nFramePut;
uint64_t nFramePutIndex;
MicroProfileFrameState Frames[MICROPROFILE_MAX_FRAME_HISTORY];
uint64_t nFlipTicks;
uint64_t nFlipAggregate;
uint64_t nFlipMax;
uint64_t nFlipAggregateDisplay;
uint64_t nFlipMaxDisplay;
std::thread* pContextSwitchThread;
bool bContextSwitchRunning;
bool bContextSwitchStop;
bool bContextSwitchAllThreads;
bool bContextSwitchNoBars;
uint32_t nContextSwitchUsage;
uint32_t nContextSwitchLastPut;
int64_t nContextSwitchHoverTickIn;
int64_t nContextSwitchHoverTickOut;
uint32_t nContextSwitchHoverThread;
uint32_t nContextSwitchHoverThreadBefore;
uint32_t nContextSwitchHoverThreadAfter;
uint8_t nContextSwitchHoverCpu;
uint8_t nContextSwitchHoverCpuNext;
uint32_t nContextSwitchPut;
MicroProfileContextSwitch ContextSwitch[MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE];
MpSocket ListenerSocket;
uint32_t nWebServerPort;
};
#define MP_LOG_TICK_MASK 0x0000ffffffffffff
#define MP_LOG_INDEX_MASK 0x3fff000000000000
#define MP_LOG_BEGIN_MASK 0xc000000000000000
#define MP_LOG_META 0x2
#define MP_LOG_ENTER 0x1
#define MP_LOG_LEAVE 0x0
inline int MicroProfileLogType(MicroProfileLogEntry Index)
{
return ((MP_LOG_BEGIN_MASK & Index)>>62) & 0x3;
}
inline uint64_t MicroProfileLogTimerIndex(MicroProfileLogEntry Index)
{
return (0x3fff&(Index>>48));
}
inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfileToken nToken, int64_t nTick)
{
MicroProfileLogEntry Entry = (nBegin<<62) | ((0x3fff&nToken)<<48) | (MP_LOG_TICK_MASK&nTick);
int t = MicroProfileLogType(Entry);
uint64_t nTimerIndex = MicroProfileLogTimerIndex(Entry);
MP_ASSERT(t == nBegin);
MP_ASSERT(nTimerIndex == (nToken&0x3fff));
return Entry;
}
inline int64_t MicroProfileLogTickDifference(MicroProfileLogEntry Start, MicroProfileLogEntry End)
{
uint64_t nStart = Start;
uint64_t nEnd = End;
int64_t nDifference = ((nEnd<<16) - (nStart<<16));
return nDifference >> 16;
}
inline int64_t MicroProfileLogGetTick(MicroProfileLogEntry e)
{
return MP_LOG_TICK_MASK & e;
}
inline int64_t MicroProfileLogSetTick(MicroProfileLogEntry e, int64_t nTick)
{
return (MP_LOG_TICK_MASK & nTick) | (e & ~MP_LOG_TICK_MASK);
}
template<typename T>
T MicroProfileMin(T a, T b)
{ return a < b ? a : b; }
template<typename T>
T MicroProfileMax(T a, T b)
{ return a > b ? a : b; }
inline int64_t MicroProfileMsToTick(float fMs, int64_t nTicksPerSecond)
{
return (int64_t)(fMs*0.001f*nTicksPerSecond);
}
inline float MicroProfileTickToMsMultiplier(int64_t nTicksPerSecond)
{
return 1000.f / nTicksPerSecond;
}
inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
{
return (uint16_t)MicroProfileGet()->TimerInfo[MicroProfileGetTimerIndex(t)].nGroupIndex;
}
#ifdef MICROPROFILE_IMPL
#ifdef _WIN32
#include <windows.h>
#define snprintf _snprintf
#pragma warning(push)
#pragma warning(disable: 4244)
int64_t MicroProfileTicksPerSecondCpu()
{
static int64_t nTicksPerSecond = 0;
if(nTicksPerSecond == 0)
{
QueryPerformanceFrequency((LARGE_INTEGER*)&nTicksPerSecond);
}
return nTicksPerSecond;
}
int64_t MicroProfileGetTick()
{
int64_t ticks;
QueryPerformanceCounter((LARGE_INTEGER*)&ticks);
return ticks;
}
#endif
#if MICROPROFILE_WEBSERVER
#ifdef _WIN32
#define MP_INVALID_SOCKET(f) (f == INVALID_SOCKET)
#endif
#if defined(__APPLE__)
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#define MP_INVALID_SOCKET(f) (f < 0)
#endif
void MicroProfileWebServerStart();
void MicroProfileWebServerStop();
bool MicroProfileWebServerUpdate();
void MicroProfileDumpHtmlToFile();
#else
#define MicroProfileWebServerStart() do{}while(0)
#define MicroProfileWebServerStop() do{}while(0)
#define MicroProfileWebServerUpdate() false
#define MicroProfileDumpHtmlToFile() do{} while(0)
#endif
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#ifndef MICROPROFILE_DEBUG
#define MICROPROFILE_DEBUG 0
#endif
#define S g_MicroProfile
MicroProfile g_MicroProfile;
MicroProfileThreadLog* g_MicroProfileGpuLog = 0;
#ifdef MICROPROFILE_IOS
// iOS doesn't support __thread
static pthread_key_t g_MicroProfileThreadLogKey;
static pthread_once_t g_MicroProfileThreadLogKeyOnce = PTHREAD_ONCE_INIT;
static void MicroProfileCreateThreadLogKey()
{
pthread_key_create(&g_MicroProfileThreadLogKey, NULL);
}
#else
MP_THREAD_LOCAL MicroProfileThreadLog* g_MicroProfileThreadLog = 0;
#endif
static bool g_bUseLock = false; /// This is used because windows does not support using mutexes under dll init(which is where global initialization is handled)
MICROPROFILE_DEFINE(g_MicroProfileFlip, "MicroProfile", "MicroProfileFlip", 0x3355ee);
MICROPROFILE_DEFINE(g_MicroProfileThreadLoop, "MicroProfile", "ThreadLoop", 0x3355ee);
MICROPROFILE_DEFINE(g_MicroProfileClear, "MicroProfile", "Clear", 0x3355ee);
MICROPROFILE_DEFINE(g_MicroProfileAccumulate, "MicroProfile", "Accumulate", 0x3355ee);
inline std::recursive_mutex& MicroProfileMutex()
{
static std::recursive_mutex Mutex;
return Mutex;
}
std::recursive_mutex& MicroProfileGetMutex()
{
return MicroProfileMutex();
}
MICROPROFILE_API MicroProfile* MicroProfileGet()
{
return &g_MicroProfile;
}
MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName);
void MicroProfileInit()
{
std::recursive_mutex& mutex = MicroProfileMutex();
bool bUseLock = g_bUseLock;
if(bUseLock)
mutex.lock();
static bool bOnce = true;
if(bOnce)
{
S.nMemUsage += sizeof(S);
bOnce = false;
memset(&S, 0, sizeof(S));
for(int i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
{
S.GroupInfo[i].pName[0] = '\0';
}
for(int i = 0; i < MICROPROFILE_MAX_TIMERS; ++i)
{
S.TimerInfo[i].pName[0] = '\0';
}
S.nGroupCount = 0;
S.nAggregateFlipTick = MP_TICK();
S.nActiveGroup = 0;
S.nActiveBars = 0;
S.nForceGroup = 0;
S.nAllGroupsWanted = 0;
S.nActiveGroupWanted = 0;
S.nAllThreadsWanted = 1;
S.nAggregateFlip = 0;
S.nTotalTimers = 0;
for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
{
S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN;
}
S.nRunning = 1;
S.fReferenceTime = 33.33f;
S.fRcpReferenceTime = 1.f / S.fReferenceTime;
S.nFreeListHead = -1;
int64_t nTick = MP_TICK();
for(int i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY; ++i)
{
S.Frames[i].nFrameStartCpu = nTick;
S.Frames[i].nFrameStartGpu = -1;
}
MicroProfileThreadLog* pGpu = MicroProfileCreateThreadLog("GPU");
g_MicroProfileGpuLog = pGpu;
MP_ASSERT(S.Pool[0] == pGpu);
pGpu->nGpu = 1;
pGpu->nThreadId = 0;
MicroProfileWebServerStart();
}
if(bUseLock)
mutex.unlock();
}
void MicroProfileShutdown()
{
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
MicroProfileWebServerStop();
#if MICROPROFILE_CONTEXT_SWITCH_TRACE
if(S.pContextSwitchThread)
{
if(S.pContextSwitchThread->joinable())
{
S.bContextSwitchStop = true;
S.pContextSwitchThread->join();
}
delete S.pContextSwitchThread;
}
#endif
}
#ifdef MICROPROFILE_IOS
inline MicroProfileThreadLog* MicroProfileGetThreadLog()
{
pthread_once(&g_MicroProfileThreadLogKeyOnce, MicroProfileCreateThreadLogKey);
return (MicroProfileThreadLog*)pthread_getspecific(g_MicroProfileThreadLogKey);
}
inline void MicroProfileSetThreadLog(MicroProfileThreadLog* pLog)
{
pthread_once(&g_MicroProfileThreadLogKeyOnce, MicroProfileCreateThreadLogKey);
pthread_setspecific(g_MicroProfileThreadLogKey, pLog);
}
#else
MicroProfileThreadLog* MicroProfileGetThreadLog()
{
return g_MicroProfileThreadLog;
}
inline void MicroProfileSetThreadLog(MicroProfileThreadLog* pLog)
{
g_MicroProfileThreadLog = pLog;
}
#endif
MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName)
{
MicroProfileThreadLog* pLog = 0;
if(S.nFreeListHead != -1)
{
pLog = S.Pool[S.nFreeListHead];
MP_ASSERT(pLog->nPut.load() == 0);
MP_ASSERT(pLog->nGet.load() == 0);
S.nFreeListHead = S.Pool[S.nFreeListHead]->nFreeListNext;
}
else
{
pLog = new MicroProfileThreadLog;
S.nMemUsage += sizeof(MicroProfileThreadLog);
S.Pool[S.nNumLogs++] = pLog;
}
memset(pLog, 0, sizeof(*pLog));
int len = (int)strlen(pName);
int maxlen = sizeof(pLog->ThreadName)-1;
len = len < maxlen ? len : maxlen;
memcpy(&pLog->ThreadName[0], pName, len);
pLog->ThreadName[len] = '\0';
pLog->nThreadId = MP_GETCURRENTTHREADID();
pLog->nFreeListNext = -1;
pLog->nActive = 1;
return pLog;
}
void MicroProfileOnThreadCreate(const char* pThreadName)
{
g_bUseLock = true;
MicroProfileInit();
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
MP_ASSERT(MicroProfileGetThreadLog() == 0);
MicroProfileThreadLog* pLog = MicroProfileCreateThreadLog(pThreadName ? pThreadName : MicroProfileGetThreadName());
MP_ASSERT(pLog);
MicroProfileSetThreadLog(pLog);
}
void MicroProfileOnThreadExit()
{
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
MicroProfileThreadLog* pLog = MicroProfileGetThreadLog();
if(pLog)
{
int32_t nLogIndex = -1;
for(int i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
{
if(pLog == S.Pool[i])
{
nLogIndex = i;
break;
}
}
MP_ASSERT(nLogIndex < MICROPROFILE_MAX_THREADS && nLogIndex > 0);
pLog->nFreeListNext = S.nFreeListHead;
pLog->nActive = 0;
pLog->nPut.store(0);
pLog->nGet.store(0);
S.nFreeListHead = nLogIndex;
for(int i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY; ++i)
{
S.Frames[i].nLogStart[nLogIndex] = 0;
}
}
}
void MicroProfileInitThreadLog()
{
MicroProfileOnThreadCreate(nullptr);
}
struct MicroProfileScopeLock
{
bool bUseLock;
std::recursive_mutex& m;
MicroProfileScopeLock(std::recursive_mutex& m) : bUseLock(g_bUseLock), m(m)
{
if(bUseLock)
m.lock();
}
~MicroProfileScopeLock()
{
if(bUseLock)
m.unlock();
}
};
MicroProfileToken MicroProfileFindToken(const char* pGroup, const char* pName)
{
MicroProfileInit();
MicroProfileScopeLock L(MicroProfileMutex());
for(uint32_t i = 0; i < S.nTotalTimers; ++i)
{
if(!MP_STRCASECMP(pName, S.TimerInfo[i].pName) && !MP_STRCASECMP(pGroup, S.GroupInfo[S.TimerInfo[i].nGroupIndex].pName))
{
return S.TimerInfo[i].nToken;
}
}
return MICROPROFILE_INVALID_TOKEN;
}
uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type)
{
for(uint32_t i = 0; i < S.nGroupCount; ++i)
{
if(!MP_STRCASECMP(pGroup, S.GroupInfo[i].pName))
{
return i;
}
}
uint16_t nGroupIndex = 0xffff;
uint32_t nLen = (uint32_t)strlen(pGroup);
if(nLen > MICROPROFILE_NAME_MAX_LEN-1)
nLen = MICROPROFILE_NAME_MAX_LEN-1;
memcpy(&S.GroupInfo[S.nGroupCount].pName[0], pGroup, nLen);
S.GroupInfo[S.nGroupCount].pName[nLen] = '\0';
S.GroupInfo[S.nGroupCount].nNameLen = nLen;
S.GroupInfo[S.nGroupCount].nGroupIndex = S.nGroupCount;
S.GroupInfo[S.nGroupCount].nNumTimers = 0;
S.GroupInfo[S.nGroupCount].Type = Type;
S.GroupInfo[S.nGroupCount].nMaxTimerNameLen = 0;
nGroupIndex = S.nGroupCount++;
S.nGroupMask = (S.nGroupMask<<1)|1;
MP_ASSERT(nGroupIndex < MICROPROFILE_MAX_GROUPS);
return nGroupIndex;
}
MicroProfileToken MicroProfileGetToken(const char* pGroup, const char* pName, uint32_t nColor, MicroProfileTokenType Type)
{
MicroProfileInit();
MicroProfileScopeLock L(MicroProfileMutex());
MicroProfileToken ret = MicroProfileFindToken(pGroup, pName);
if(ret != MICROPROFILE_INVALID_TOKEN)
return ret;
uint16_t nGroupIndex = MicroProfileGetGroup(pGroup, Type);
uint16_t nTimerIndex = (uint16_t)(S.nTotalTimers++);
uint64_t nGroupMask = 1ll << nGroupIndex;
MicroProfileToken nToken = MicroProfileMakeToken(nGroupMask, nTimerIndex);
S.GroupInfo[nGroupIndex].nNumTimers++;
S.GroupInfo[nGroupIndex].nMaxTimerNameLen = MicroProfileMax(S.GroupInfo[nGroupIndex].nMaxTimerNameLen, (uint32_t)strlen(pName));
MP_ASSERT(S.GroupInfo[nGroupIndex].Type == Type); //dont mix cpu & gpu timers in the same group
S.nMaxGroupSize = MicroProfileMax(S.nMaxGroupSize, S.GroupInfo[nGroupIndex].nNumTimers);
S.TimerInfo[nTimerIndex].nToken = nToken;
uint32_t nLen = (uint32_t)strlen(pName);
if(nLen > MICROPROFILE_NAME_MAX_LEN-1)
nLen = MICROPROFILE_NAME_MAX_LEN-1;
memcpy(&S.TimerInfo[nTimerIndex].pName, pName, nLen);
S.TimerInfo[nTimerIndex].pName[nLen] = '\0';
S.TimerInfo[nTimerIndex].nNameLen = nLen;
S.TimerInfo[nTimerIndex].nColor = nColor&0xffffff;
S.TimerInfo[nTimerIndex].nGroupIndex = nGroupIndex;
S.TimerInfo[nTimerIndex].nTimerIndex = nTimerIndex;
return nToken;
}
MicroProfileToken MicroProfileGetMetaToken(const char* pName)
{
MicroProfileInit();
MicroProfileScopeLock L(MicroProfileMutex());
for(uint32_t i = 0; i < MICROPROFILE_META_MAX; ++i)
{
if(!S.MetaCounters[i].pName)
{
S.MetaCounters[i].pName = pName;
return i;
}
else if(!MP_STRCASECMP(pName, S.MetaCounters[i].pName))
{
return i;
}
}
MP_ASSERT(0);//out of slots, increase MICROPROFILE_META_MAX
return (MicroProfileToken)-1;
}
inline void MicroProfileLogPut(MicroProfileToken nToken_, uint64_t nTick, uint64_t nBegin, MicroProfileThreadLog* pLog)
{
MP_ASSERT(pLog != 0); //this assert is hit if MicroProfileOnCreateThread is not called
MP_ASSERT(pLog->nActive);
uint32_t nPos = pLog->nPut.load(std::memory_order_relaxed);
uint32_t nNextPos = (nPos+1) % MICROPROFILE_BUFFER_SIZE;
if(nNextPos == pLog->nGet.load(std::memory_order_relaxed))
{
S.nOverflow = 100;
}
else
{
int64_t test = MicroProfileMakeLogIndex(nBegin, nToken_, nTick);;
MP_ASSERT(MicroProfileLogType(test) == nBegin);
MP_ASSERT(MicroProfileLogTimerIndex(test) == MicroProfileGetTimerIndex(nToken_));
pLog->Log[nPos] = MicroProfileMakeLogIndex(nBegin, nToken_, nTick);
pLog->nPut.store(nNextPos, std::memory_order_release);
}
}
uint64_t MicroProfileEnter(MicroProfileToken nToken_)
{
if(MicroProfileGetGroupMask(nToken_) & S.nActiveGroup)
{
if(!MicroProfileGetThreadLog())
{
MicroProfileInitThreadLog();
}
uint64_t nTick = MP_TICK();
MicroProfileLogPut(nToken_, nTick, MP_LOG_ENTER, MicroProfileGetThreadLog());
return nTick;
}
return MICROPROFILE_INVALID_TICK;
}
void MicroProfileMetaUpdate(MicroProfileToken nToken, int nCount, MicroProfileTokenType eTokenType)
{
if((MP_DRAW_META_FIRST<<nToken) & S.nActiveBars)
{
MicroProfileThreadLog* pLog = MicroProfileTokenTypeCpu == eTokenType ? MicroProfileGetThreadLog() : g_MicroProfileGpuLog;
if(pLog)
{
MP_ASSERT(nToken < MICROPROFILE_META_MAX);
MicroProfileLogPut(nToken, nCount, MP_LOG_META, pLog);
}
}
}
void MicroProfileLeave(MicroProfileToken nToken_, uint64_t nTickStart)
{
if(MICROPROFILE_INVALID_TICK != nTickStart)
{
if(!MicroProfileGetThreadLog())
{
MicroProfileInitThreadLog();
}
uint64_t nTick = MP_TICK();
MicroProfileThreadLog* pLog = MicroProfileGetThreadLog();
MicroProfileLogPut(nToken_, nTick, MP_LOG_LEAVE, pLog);
}
}
uint64_t MicroProfileGpuEnter(MicroProfileToken nToken_)
{
if(MicroProfileGetGroupMask(nToken_) & S.nActiveGroup)
{
uint64_t nTimer = MicroProfileGpuInsertTimeStamp();
MicroProfileLogPut(nToken_, nTimer, MP_LOG_ENTER, g_MicroProfileGpuLog);
return 1;
}
return 0;
}
void MicroProfileGpuLeave(MicroProfileToken nToken_, uint64_t nTickStart)
{
if(nTickStart)
{
uint64_t nTimer = MicroProfileGpuInsertTimeStamp();
MicroProfileLogPut(nToken_, nTimer, MP_LOG_LEAVE, g_MicroProfileGpuLog);
}
}
void MicroProfileContextSwitchPut(MicroProfileContextSwitch* pContextSwitch)
{
if(S.nRunning || pContextSwitch->nTicks <= S.nPauseTicks)
{
uint32_t nPut = S.nContextSwitchPut;
S.ContextSwitch[nPut] = *pContextSwitch;
S.nContextSwitchPut = (S.nContextSwitchPut+1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE;
}
}
void MicroProfileGetRange(uint32_t nPut, uint32_t nGet, uint32_t nRange[2][2])
{
if(nPut > nGet)
{
nRange[0][0] = nGet;
nRange[0][1] = nPut;
nRange[1][0] = nRange[1][1] = 0;
}
else if(nPut != nGet)
{
MP_ASSERT(nGet != MICROPROFILE_BUFFER_SIZE);
uint32_t nCountEnd = MICROPROFILE_BUFFER_SIZE - nGet;
nRange[0][0] = nGet;
nRange[0][1] = nGet + nCountEnd;
nRange[1][0] = 0;
nRange[1][1] = nPut;
}
}
void MicroProfileFlip()
{
#if 0
//verify LogEntry wraps correctly
MicroProfileLogEntry c = MP_LOG_TICK_MASK-5000;
for(int i = 0; i < 10000; ++i, c += 1)
{
MicroProfileLogEntry l2 = (c+2500) & MP_LOG_TICK_MASK;
MP_ASSERT(2500 == MicroProfileLogTickDifference(c, l2));
}
#endif
MICROPROFILE_SCOPE(g_MicroProfileFlip);
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
if(S.nToggleRunning)
{
S.nRunning = !S.nRunning;
if(!S.nRunning)
S.nPauseTicks = MP_TICK();
S.nToggleRunning = 0;
for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
{
MicroProfileThreadLog* pLog = S.Pool[i];
if(pLog)
{
pLog->nStackPos = 0;
}
}
}
uint32_t nAggregateClear = S.nAggregateClear, nAggregateFlip = 0;
if(S.nDumpHtmlNextFrame)
{
S.nDumpHtmlNextFrame = 0;
MicroProfileDumpHtmlToFile();
}
if(MicroProfileWebServerUpdate())
{
nAggregateClear = 1;
nAggregateFlip = 1;
}
if(S.nRunning || S.nForceEnable)
{
S.nFramePutIndex++;
S.nFramePut = (S.nFramePut+1) % MICROPROFILE_MAX_FRAME_HISTORY;
MP_ASSERT((S.nFramePutIndex % MICROPROFILE_MAX_FRAME_HISTORY) == S.nFramePut);
S.nFrameCurrent = (S.nFramePut + MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY - 1) % MICROPROFILE_MAX_FRAME_HISTORY;
S.nFrameCurrentIndex++;
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
uint32_t nContextSwitchPut = S.nContextSwitchPut;
if(S.nContextSwitchLastPut < nContextSwitchPut)
{
S.nContextSwitchUsage = (nContextSwitchPut - S.nContextSwitchLastPut);
}
else
{
S.nContextSwitchUsage = MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE - S.nContextSwitchLastPut + nContextSwitchPut;
}
S.nContextSwitchLastPut = nContextSwitchPut;
MicroProfileFrameState* pFramePut = &S.Frames[S.nFramePut];
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
MicroProfileFrameState* pFrameNext = &S.Frames[nFrameNext];
pFramePut->nFrameStartCpu = MP_TICK();
pFramePut->nFrameStartGpu = (uint32_t)MicroProfileGpuInsertTimeStamp();
if(pFrameNext->nFrameStartGpu != (uint64_t)-1)
pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu);
if(pFrameCurrent->nFrameStartGpu == (uint64_t)-1)
pFrameCurrent->nFrameStartGpu = pFrameNext->nFrameStartGpu + 1;
uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
uint64_t nFrameEndCpu = pFrameNext->nFrameStartCpu;
{
uint64_t nTick = nFrameEndCpu - nFrameStartCpu;
S.nFlipTicks = nTick;
S.nFlipAggregate += nTick;
S.nFlipMax = MicroProfileMax(S.nFlipMax, nTick);
}
for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
{
MicroProfileThreadLog* pLog = S.Pool[i];
if(!pLog)
{
pFramePut->nLogStart[i] = 0;
}
else
{
uint32_t nPut = pLog->nPut.load(std::memory_order_acquire);
pFramePut->nLogStart[i] = nPut;
MP_ASSERT(nPut< MICROPROFILE_BUFFER_SIZE);
//need to keep last frame around to close timers. timers more than 1 frame old is ditched.
pLog->nGet.store(nPut, std::memory_order_relaxed);
}
}
if(S.nRunning)
{
{
MICROPROFILE_SCOPE(g_MicroProfileClear);
for(uint32_t i = 0; i < S.nTotalTimers; ++i)
{
S.Frame[i].nTicks = 0;
S.Frame[i].nCount = 0;
S.FrameExclusive[i] = 0;
}
for(uint32_t j = 0; j < MICROPROFILE_META_MAX; ++j)
{
if(S.MetaCounters[j].pName)
{
for(uint32_t i = 0; i < S.nTotalTimers; ++i)
{
S.MetaCounters[j].nCounters[i] = 0;
}
}
}
}
{
MICROPROFILE_SCOPE(g_MicroProfileThreadLoop);
for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i)
{
MicroProfileThreadLog* pLog = S.Pool[i];
if(!pLog)
continue;
uint32_t nPut = pFrameNext->nLogStart[i];
uint32_t nGet = pFrameCurrent->nLogStart[i];
uint32_t nRange[2][2] = { {0, 0}, {0, 0}, };
MicroProfileGetRange(nPut, nGet, nRange);
//fetch gpu results.
if(pLog->nGpu)
{
for(uint32_t j = 0; j < 2; ++j)
{
uint32_t nStart = nRange[j][0];
uint32_t nEnd = nRange[j][1];
for(uint32_t k = nStart; k < nEnd; ++k)
{
MicroProfileLogEntry L = pLog->Log[k];
pLog->Log[k] = MicroProfileLogSetTick(L, MicroProfileGpuGetTimeStamp((uint32_t)MicroProfileLogGetTick(L)));
}
}
}
uint32_t* pStack = &pLog->nStack[0];
int64_t* pChildTickStack = &pLog->nChildTickStack[0];
uint32_t nStackPos = pLog->nStackPos;
for(uint32_t j = 0; j < 2; ++j)
{
uint32_t nStart = nRange[j][0];
uint32_t nEnd = nRange[j][1];
for(uint32_t k = nStart; k < nEnd; ++k)
{
MicroProfileLogEntry LE = pLog->Log[k];
int nType = MicroProfileLogType(LE);
if(MP_LOG_ENTER == nType)
{
MP_ASSERT(nStackPos < MICROPROFILE_STACK_MAX);
pStack[nStackPos++] = k;
pChildTickStack[nStackPos] = 0;
}
else if(MP_LOG_META == nType)
{
if(nStackPos)
{
int64_t nMetaIndex = MicroProfileLogTimerIndex(LE);
int64_t nMetaCount = MicroProfileLogGetTick(LE);
MP_ASSERT(nMetaIndex < MICROPROFILE_META_MAX);
int64_t nCounter = MicroProfileLogTimerIndex(pLog->Log[pStack[nStackPos-1]]);
S.MetaCounters[nMetaIndex].nCounters[nCounter] += nMetaCount;
}
}
else
{
MP_ASSERT(nType == MP_LOG_LEAVE);
if(nStackPos)
{
int64_t nTickStart = pLog->Log[pStack[nStackPos-1]];
int64_t nTicks = MicroProfileLogTickDifference(nTickStart, LE);
int64_t nChildTicks = pChildTickStack[nStackPos];
nStackPos--;
pChildTickStack[nStackPos] += nTicks;
uint32_t nTimerIndex = MicroProfileLogTimerIndex(LE);
S.Frame[nTimerIndex].nTicks += nTicks;
S.FrameExclusive[nTimerIndex] += (nTicks-nChildTicks);
S.Frame[nTimerIndex].nCount += 1;
}
}
}
}
pLog->nStackPos = nStackPos;
}
}
{
MICROPROFILE_SCOPE(g_MicroProfileAccumulate);
for(uint32_t i = 0; i < S.nTotalTimers; ++i)
{
S.AggregateTimers[i].nTicks += S.Frame[i].nTicks;
S.AggregateTimers[i].nCount += S.Frame[i].nCount;
S.MaxTimers[i] = MicroProfileMax(S.MaxTimers[i], S.Frame[i].nTicks);
S.AggregateTimersExclusive[i] += S.FrameExclusive[i];
S.MaxTimersExclusive[i] = MicroProfileMax(S.MaxTimersExclusive[i], S.FrameExclusive[i]);
}
}
for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
{
if(S.Graph[i].nToken != MICROPROFILE_INVALID_TOKEN)
{
MicroProfileToken nToken = S.Graph[i].nToken;
S.Graph[i].nHistory[S.nGraphPut] = S.Frame[MicroProfileGetTimerIndex(nToken)].nTicks;
}
}
S.nGraphPut = (S.nGraphPut+1) % MICROPROFILE_GRAPH_HISTORY;
}
if(S.nRunning && S.nAggregateFlip <= ++S.nAggregateFlipCount)
{
nAggregateFlip = 1;
if(S.nAggregateFlip) // if 0 accumulate indefinitely
{
nAggregateClear = 1;
}
}
}
if(nAggregateFlip)
{
memcpy(&S.Aggregate[0], &S.AggregateTimers[0], sizeof(S.Aggregate[0]) * S.nTotalTimers);
memcpy(&S.AggregateMax[0], &S.MaxTimers[0], sizeof(S.AggregateMax[0]) * S.nTotalTimers);
memcpy(&S.AggregateExclusive[0], &S.AggregateTimersExclusive[0], sizeof(S.AggregateExclusive[0]) * S.nTotalTimers);
memcpy(&S.AggregateMaxExclusive[0], &S.MaxTimersExclusive[0], sizeof(S.AggregateMaxExclusive[0]) * S.nTotalTimers);
S.nAggregateFrames = S.nAggregateFlipCount;
S.nFlipAggregateDisplay = S.nFlipAggregate;
S.nFlipMaxDisplay = S.nFlipMax;
if(nAggregateClear)
{
memset(&S.AggregateTimers[0], 0, sizeof(S.Aggregate[0]) * S.nTotalTimers);
memset(&S.MaxTimers[0], 0, sizeof(S.MaxTimers[0]) * S.nTotalTimers);
memset(&S.AggregateTimersExclusive[0], 0, sizeof(S.AggregateExclusive[0]) * S.nTotalTimers);
memset(&S.MaxTimersExclusive[0], 0, sizeof(S.MaxTimersExclusive[0]) * S.nTotalTimers);
S.nAggregateFlipCount = 0;
S.nFlipAggregate = 0;
S.nFlipMax = 0;
S.nAggregateFlipTick = MP_TICK();
}
}
S.nAggregateClear = 0;
uint64_t nNewActiveGroup = 0;
if(S.nForceEnable || (S.nDisplay && S.nRunning))
nNewActiveGroup = S.nAllGroupsWanted ? S.nGroupMask : S.nActiveGroupWanted;
nNewActiveGroup |= S.nForceGroup;
if(S.nActiveGroup != nNewActiveGroup)
S.nActiveGroup = nNewActiveGroup;
uint32_t nNewActiveBars = 0;
if(S.nDisplay && S.nRunning)
nNewActiveBars = S.nBars;
if(S.nForceMetaCounters)
{
for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
{
if(S.MetaCounters[i].pName)
{
nNewActiveBars |= (MP_DRAW_META_FIRST<<i);
}
}
}
if(nNewActiveBars != S.nActiveBars)
S.nActiveBars = nNewActiveBars;
}
void MicroProfileSetForceEnable(bool bEnable)
{
S.nForceEnable = bEnable ? 1 : 0;
}
bool MicroProfileGetForceEnable()
{
return S.nForceEnable != 0;
}
void MicroProfileSetEnableAllGroups(bool bEnableAllGroups)
{
S.nAllGroupsWanted = bEnableAllGroups ? 1 : 0;
}
bool MicroProfileGetEnableAllGroups()
{
return 0 != S.nAllGroupsWanted;
}
void MicroProfileSetForceMetaCounters(bool bForce)
{
S.nForceMetaCounters = bForce ? 1 : 0;
}
bool MicroProfileGetForceMetaCounters()
{
return 0 != S.nForceMetaCounters;
}
void MicroProfileSetAggregateFrames(int nFrames)
{
S.nAggregateFlip = (uint32_t)nFrames;
if(0 == nFrames)
{
S.nAggregateClear = 1;
}
}
int MicroProfileGetAggregateFrames()
{
return S.nAggregateFlip;
}
int MicroProfileGetCurrentAggregateFrames()
{
return int(S.nAggregateFlip ? S.nAggregateFlip : S.nAggregateFlipCount);
}
void MicroProfileForceEnableGroup(const char* pGroup, MicroProfileTokenType Type)
{
MicroProfileInit();
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
uint16_t nGroup = MicroProfileGetGroup(pGroup, Type);
S.nForceGroup |= (1ll << nGroup);
}
void MicroProfileForceDisableGroup(const char* pGroup, MicroProfileTokenType Type)
{
MicroProfileInit();
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
uint16_t nGroup = MicroProfileGetGroup(pGroup, Type);
S.nForceGroup &= ~(1ll << nGroup);
}
void MicroProfileCalcAllTimers(float* pTimers, float* pAverage, float* pMax, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, uint32_t nSize)
{
for(uint32_t i = 0; i < S.nTotalTimers && i < nSize; ++i)
{
const uint32_t nGroupId = S.TimerInfo[i].nGroupIndex;
const float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[nGroupId].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
uint32_t nTimer = i;
uint32_t nIdx = i * 2;
uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1;
uint32_t nAggregateCount = S.Aggregate[nTimer].nCount ? S.Aggregate[nTimer].nCount : 1;
float fToPrc = S.fRcpReferenceTime;
float fMs = fToMs * (S.Frame[nTimer].nTicks);
float fPrc = MicroProfileMin(fMs * fToPrc, 1.f);
float fAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateFrames);
float fAveragePrc = MicroProfileMin(fAverageMs * fToPrc, 1.f);
float fMaxMs = fToMs * (S.AggregateMax[nTimer]);
float fMaxPrc = MicroProfileMin(fMaxMs * fToPrc, 1.f);
float fCallAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateCount);
float fCallAveragePrc = MicroProfileMin(fCallAverageMs * fToPrc, 1.f);
float fMsExclusive = fToMs * (S.FrameExclusive[nTimer]);
float fPrcExclusive = MicroProfileMin(fMsExclusive * fToPrc, 1.f);
float fAverageMsExclusive = fToMs * (S.AggregateExclusive[nTimer] / nAggregateFrames);
float fAveragePrcExclusive = MicroProfileMin(fAverageMsExclusive * fToPrc, 1.f);
float fMaxMsExclusive = fToMs * (S.AggregateMaxExclusive[nTimer]);
float fMaxPrcExclusive = MicroProfileMin(fMaxMsExclusive * fToPrc, 1.f);
pTimers[nIdx] = fMs;
pTimers[nIdx+1] = fPrc;
pAverage[nIdx] = fAverageMs;
pAverage[nIdx+1] = fAveragePrc;
pMax[nIdx] = fMaxMs;
pMax[nIdx+1] = fMaxPrc;
pCallAverage[nIdx] = fCallAverageMs;
pCallAverage[nIdx+1] = fCallAveragePrc;
pExclusive[nIdx] = fMsExclusive;
pExclusive[nIdx+1] = fPrcExclusive;
pAverageExclusive[nIdx] = fAverageMsExclusive;
pAverageExclusive[nIdx+1] = fAveragePrcExclusive;
pMaxExclusive[nIdx] = fMaxMsExclusive;
pMaxExclusive[nIdx+1] = fMaxPrcExclusive;
}
}
void MicroProfileTogglePause()
{
S.nToggleRunning = 1;
}
float MicroProfileGetTime(const char* pGroup, const char* pName)
{
MicroProfileToken nToken = MicroProfileFindToken(pGroup, pName);
if(nToken == MICROPROFILE_INVALID_TOKEN)
{
return 0.f;
}
uint32_t nTimerIndex = MicroProfileGetTimerIndex(nToken);
uint32_t nGroupIndex = MicroProfileGetGroupIndex(nToken);
float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[nGroupIndex].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
return S.Frame[nTimerIndex].nTicks * fToMs;
}
#if MICROPROFILE_WEBSERVER
#define MICROPROFILE_EMBED_HTML
extern const char g_MicroProfileHtml_begin[];
extern const char g_MicroProfileHtml_end[];
extern const size_t g_MicroProfileHtml_begin_size;
extern const size_t g_MicroProfileHtml_end_size;
typedef void MicroProfileWriteCallback(void* Handle, size_t size, const char* pData);
uint32_t MicroProfileWebServerPort()
{
return S.nWebServerPort;
}
void MicroProfileDumpHtml(const char* pFile)
{
uint32_t nLen = strlen(pFile);
if(nLen > sizeof(S.HtmlDumpPath)-1)
{
return;
}
memcpy(S.HtmlDumpPath, pFile, nLen+1);
S.nDumpHtmlNextFrame = 1;
}
void MicroProfilePrintf(MicroProfileWriteCallback CB, void* Handle, const char* pFmt, ...)
{
char buffer[32*1024];
va_list args;
va_start (args, pFmt);
#ifdef _WIN32
size_t size = vsprintf_s(buffer, pFmt, args);
#else
size_t size = vsnprintf(buffer, sizeof(buffer)-1, pFmt, args);
#endif
CB(Handle, size, &buffer[0]);
va_end (args);
}
void MicroProfileDumpHtml(MicroProfileWriteCallback CB, void* Handle, int nMaxFrames)
{
CB(Handle, g_MicroProfileHtml_begin_size-1, &g_MicroProfileHtml_begin[0]);
//dump info
uint64_t nTicks = MP_TICK();
float fAggregateMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * (nTicks - S.nAggregateFlipTick);
MicroProfilePrintf(CB, Handle, "var AggregateInfo = {'Frames':%d, 'Time':%f};\n", S.nAggregateFrames, fAggregateMs);
//groups
MicroProfilePrintf(CB, Handle, "var GroupInfo = Array(%d);\n\n",S.nGroupCount);
for(uint32_t i = 0; i < S.nGroupCount; ++i)
{
MP_ASSERT(i == S.GroupInfo[i].nGroupIndex);
MicroProfilePrintf(CB, Handle, "GroupInfo[%d] = MakeGroup(%d, \"%s\", %d, %d);\n", S.GroupInfo[i].nGroupIndex, S.GroupInfo[i].nGroupIndex, S.GroupInfo[i].pName, S.GroupInfo[i].nNumTimers, S.GroupInfo[i].Type == MicroProfileTokenTypeGpu?1:0);
}
//timers
uint32_t nNumTimers = S.nTotalTimers;
uint32_t nBlockSize = 2 * nNumTimers;
float* pTimers = (float*)alloca(nBlockSize * 7 * sizeof(float));
float* pAverage = pTimers + nBlockSize;
float* pMax = pTimers + 2 * nBlockSize;
float* pCallAverage = pTimers + 3 * nBlockSize;
float* pTimersExclusive = pTimers + 4 * nBlockSize;
float* pAverageExclusive = pTimers + 5 * nBlockSize;
float* pMaxExclusive = pTimers + 6 * nBlockSize;
MicroProfileCalcAllTimers(pTimers, pAverage, pMax, pCallAverage, pTimersExclusive, pAverageExclusive, pMaxExclusive, nNumTimers);
MicroProfilePrintf(CB, Handle, "\nvar TimerInfo = Array(%d);\n\n", S.nTotalTimers);
for(uint32_t i = 0; i < S.nTotalTimers; ++i)
{
uint32_t nIdx = i * 2;
MP_ASSERT(i == S.TimerInfo[i].nTimerIndex);
MicroProfilePrintf(CB, Handle, "var Meta%d = [", i);
bool bOnce = true;
for(int j = 0; j < MICROPROFILE_META_MAX; ++j)
{
if(S.MetaCounters[j].pName)
{
uint32_t lala = S.MetaCounters[j].nCounters[i];
MicroProfilePrintf(CB, Handle, bOnce ? "%d" : ",%d", lala);
bOnce = false;
}
}
MicroProfilePrintf(CB, Handle, "];\n");
MicroProfilePrintf(CB, Handle, "TimerInfo[%d] = MakeTimer(%d, \"%s\", %d, '#%02x%02x%02x', %f, %f, %f, %f, %f, %d, Meta%d);\n", S.TimerInfo[i].nTimerIndex, S.TimerInfo[i].nTimerIndex, S.TimerInfo[i].pName, S.TimerInfo[i].nGroupIndex,
MICROPROFILE_UNPACK_RED(S.TimerInfo[i].nColor) & 0xff,
MICROPROFILE_UNPACK_GREEN(S.TimerInfo[i].nColor) & 0xff,
MICROPROFILE_UNPACK_BLUE(S.TimerInfo[i].nColor) & 0xff,
pAverage[nIdx],
pMax[nIdx],
pAverageExclusive[nIdx],
pMaxExclusive[nIdx],
pCallAverage[nIdx],
S.Aggregate[i].nCount,
i
);
}
MicroProfilePrintf(CB, Handle, "\nvar ThreadNames = [");
for(uint32_t i = 0; i < S.nNumLogs; ++i)
{
if(S.Pool[i])
{
MicroProfilePrintf(CB, Handle, "'%s',", S.Pool[i]->ThreadName);
}
else
{
MicroProfilePrintf(CB, Handle, "'Thread %d',", i);
}
}
MicroProfilePrintf(CB, Handle, "];\n\n");
MicroProfilePrintf(CB, Handle, "\nvar MetaNames = [");
for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
{
if(S.MetaCounters[i].pName)
{
MicroProfilePrintf(CB, Handle, "'%s',", S.MetaCounters[i].pName);
}
}
MicroProfilePrintf(CB, Handle, "];\n\n");
uint32_t nNumFrames = (MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY - 1);
if(S.nFrameCurrentIndex < nNumFrames)
nNumFrames = S.nFrameCurrentIndex;
if((int)nNumFrames > nMaxFrames)
{
nNumFrames = nMaxFrames;
}
#if MICROPROFILE_DEBUG
printf("dumping %d frames\n", nNumFrames);
#endif
uint32_t nFirstFrame = (S.nFrameCurrent + MICROPROFILE_MAX_FRAME_HISTORY - nNumFrames) % MICROPROFILE_MAX_FRAME_HISTORY;
uint32_t nFirstFrameIndex = S.nFrameCurrentIndex - nNumFrames;
int64_t nTickStart = S.Frames[nFirstFrame].nFrameStartCpu;
int64_t nTickStartGpu = S.Frames[nFirstFrame].nFrameStartGpu;
MicroProfilePrintf(CB, Handle, "var Frames = Array(%d);\n", nNumFrames);
for(uint32_t i = 0; i < nNumFrames; ++i)
{
uint32_t nFrameIndex = (nFirstFrame + i) % MICROPROFILE_MAX_FRAME_HISTORY;
uint32_t nFrameIndexNext = (nFrameIndex + 1) % MICROPROFILE_MAX_FRAME_HISTORY;
for(uint32_t j = 0; j < S.nNumLogs; ++j)
{
MicroProfileThreadLog* pLog = S.Pool[j];
int64_t nStartTick = pLog->nGpu ? nTickStartGpu : nTickStart;
uint32_t nLogStart = S.Frames[nFrameIndex].nLogStart[j];
uint32_t nLogEnd = S.Frames[nFrameIndexNext].nLogStart[j];
float fToMs = MicroProfileTickToMsMultiplier(pLog->nGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu());
MicroProfilePrintf(CB, Handle, "var ts_%d_%d = [", i, j);
if(nLogStart != nLogEnd)
{
uint32_t k = nLogStart;
uint32_t nLogType = MicroProfileLogType(pLog->Log[k]);
float fTime = nLogType == MP_LOG_META ? 0.f : MicroProfileLogTickDifference(nStartTick, pLog->Log[k]) * fToMs;
MicroProfilePrintf(CB, Handle, "%f", fTime);
for(k = (k+1) % MICROPROFILE_BUFFER_SIZE; k != nLogEnd; k = (k+1) % MICROPROFILE_BUFFER_SIZE)
{
uint32_t nLogType = MicroProfileLogType(pLog->Log[k]);
float fTime = nLogType == MP_LOG_META ? 0.f : MicroProfileLogTickDifference(nStartTick, pLog->Log[k]) * fToMs;
MicroProfilePrintf(CB, Handle, ",%f", fTime);
}
}
MicroProfilePrintf(CB, Handle, "];\n");
MicroProfilePrintf(CB, Handle, "var tt_%d_%d = [", i, j);
if(nLogStart != nLogEnd)
{
uint32_t k = nLogStart;
MicroProfilePrintf(CB, Handle, "%d", MicroProfileLogType(pLog->Log[k]));
for(k = (k+1) % MICROPROFILE_BUFFER_SIZE; k != nLogEnd; k = (k+1) % MICROPROFILE_BUFFER_SIZE)
{
uint32_t nLogType = MicroProfileLogType(pLog->Log[k]);
if(nLogType == MP_LOG_META)
{
//for meta, store the count + 2, which is the tick part
nLogType = 2 + MicroProfileLogGetTick(pLog->Log[k]);
}
MicroProfilePrintf(CB, Handle, ",%d", nLogType);
}
}
MicroProfilePrintf(CB, Handle, "];\n");
MicroProfilePrintf(CB, Handle, "var ti_%d_%d = [", i, j);
if(nLogStart != nLogEnd)
{
uint32_t k = nLogStart;
MicroProfilePrintf(CB, Handle, "%d", (uint32_t)MicroProfileLogTimerIndex(pLog->Log[k]));
for(k = (k+1) % MICROPROFILE_BUFFER_SIZE; k != nLogEnd; k = (k+1) % MICROPROFILE_BUFFER_SIZE)
{
MicroProfilePrintf(CB, Handle, ",%d", (uint32_t)MicroProfileLogTimerIndex(pLog->Log[k]));
}
}
MicroProfilePrintf(CB, Handle, "];\n");
}
MicroProfilePrintf(CB, Handle, "var ts%d = [", i);
for(uint32_t j = 0; j < S.nNumLogs; ++j)
{
MicroProfilePrintf(CB, Handle, "ts_%d_%d,", i, j);
}
MicroProfilePrintf(CB, Handle, "];\n");
MicroProfilePrintf(CB, Handle, "var tt%d = [", i);
for(uint32_t j = 0; j < S.nNumLogs; ++j)
{
MicroProfilePrintf(CB, Handle, "tt_%d_%d,", i, j);
}
MicroProfilePrintf(CB, Handle, "];\n");
MicroProfilePrintf(CB, Handle, "var ti%d = [", i);
for(uint32_t j = 0; j < S.nNumLogs; ++j)
{
MicroProfilePrintf(CB, Handle, "ti_%d_%d,", i, j);
}
MicroProfilePrintf(CB, Handle, "];\n");
int64_t nFrameStart = S.Frames[nFrameIndex].nFrameStartCpu;
int64_t nFrameEnd = S.Frames[nFrameIndexNext].nFrameStartCpu;
float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
float fFrameMs = MicroProfileLogTickDifference(nTickStart, nFrameStart) * fToMs;
float fFrameEndMs = MicroProfileLogTickDifference(nTickStart, nFrameEnd) * fToMs;
MicroProfilePrintf(CB, Handle, "Frames[%d] = MakeFrame(%d, %f, %f, ts%d, tt%d, ti%d);\n", i, nFirstFrameIndex, fFrameMs,fFrameEndMs, i, i, i);
}
CB(Handle, g_MicroProfileHtml_end_size-1, &g_MicroProfileHtml_end[0]);
}
void MicroProfileWriteFile(void* Handle, size_t nSize, const char* pData)
{
fwrite(pData, nSize, 1, (FILE*)Handle);
}
void MicroProfileDumpHtmlToFile()
{
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
FILE* F = fopen(S.HtmlDumpPath, "w");
if(F)
{
MicroProfileDumpHtml(MicroProfileWriteFile, F, MICROPROFILE_WEBSERVER_MAXFRAMES);
fclose(F);
}
}
static uint64_t g_nMicroProfileDataSent = 0;
void MicroProfileWriteSocket(void* Handle, size_t nSize, const char* pData)
{
g_nMicroProfileDataSent += nSize;
send(*(MpSocket*)Handle, pData, nSize, 0);
}
#ifndef MicroProfileSetNonBlocking //fcntl doesnt work on a some unix like platforms..
void MicroProfileSetNonBlocking(MpSocket Socket, int NonBlocking)
{
#ifdef _WIN32
u_long nonBlocking = NonBlocking ? 1 : 0;
ioctlsocket(Socket, FIONBIO, &nonBlocking);
#else
int Options = fcntl(Socket, F_GETFL);
if(NonBlocking)
{
fcntl(Socket, F_SETFL, Options|O_NONBLOCK);
}
else
{
fcntl(Socket, F_SETFL, Options&(~O_NONBLOCK));
}
#endif
}
#endif
void MicroProfileWebServerStart()
{
#ifdef _WIN32
WSADATA wsa;
if(WSAStartup(MAKEWORD(2, 2), &wsa))
{
S.ListenerSocket = -1;
return;
}
#endif
S.ListenerSocket = socket(PF_INET, SOCK_STREAM, 6);
MP_ASSERT(!MP_INVALID_SOCKET(S.ListenerSocket));
MicroProfileSetNonBlocking(S.ListenerSocket, 1);
S.nWebServerPort = (uint32_t)-1;
struct sockaddr_in Addr;
Addr.sin_family = AF_INET;
Addr.sin_addr.s_addr = INADDR_ANY;
for(int i = 0; i < 20; ++i)
{
Addr.sin_port = htons(MICROPROFILE_WEBSERVER_PORT+i);
if(0 == bind(S.ListenerSocket, (sockaddr*)&Addr, sizeof(Addr)))
{
S.nWebServerPort = MICROPROFILE_WEBSERVER_PORT+i;
break;
}
}
listen(S.ListenerSocket, 8);
}
void MicroProfileWebServerStop()
{
#ifdef _WIN32
closesocket(S.ListenerSocket);
WSACleanup();
#else
close(S.ListenerSocket);
#endif
}
bool MicroProfileWebServerUpdate()
{
MICROPROFILE_SCOPEI("MicroProfile", "Webserver-update", -1);
MpSocket Connection = accept(S.ListenerSocket, 0, 0);
bool bServed = false;
if(!MP_INVALID_SOCKET(Connection))
{
int timeout = 100;
setsockopt(Connection, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<char*>(&timeout), sizeof(timeout));
std::lock_guard<std::recursive_mutex> Lock(MicroProfileMutex());
char Req[8192];
MicroProfileSetNonBlocking(Connection, 0);
int nReceived = recv(Connection, Req, sizeof(Req)-1, 0);
if(nReceived > 0)
{
Req[nReceived] = '\0';
#if MICROPROFILE_DEBUG
printf("got request \n%s\n", Req);
#endif
#define MICROPROFILE_HTML_HEADER "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
char* pHttp = strstr(Req, "HTTP/");
char* pGet = strstr(Req, "GET / ");
char* pGetParam = strstr(Req, "GET /?");
if(pHttp && (pGet || pGetParam))
{
int nMaxFrames = MICROPROFILE_WEBSERVER_MAXFRAMES;
if(pGetParam)
{
*pHttp = '\0';
pGetParam += sizeof("GET /?")-1;
while(pGetParam) //split url pairs foo=bar&lala=lele etc
{
char* pSplit = strstr(pGetParam, "&");
if(pSplit)
{
*pSplit++ = '\0';
}
char* pKey = pGetParam;
char* pValue = strstr(pGetParam, "=");
if(pValue)
{
*pValue++ = '\0';
}
if(0 == MP_STRCASECMP(pKey, "frames"))
{
if(pValue)
{
nMaxFrames = atoi(pValue);
}
}
pGetParam = pSplit;
}
}
uint64_t nTickStart = MP_TICK();
send(Connection, MICROPROFILE_HTML_HEADER, sizeof(MICROPROFILE_HTML_HEADER)-1, 0);
uint64_t nDataStart = g_nMicroProfileDataSent;
MicroProfileDumpHtml(MicroProfileWriteSocket, &Connection, nMaxFrames);
uint64_t nDataEnd = g_nMicroProfileDataSent;
uint64_t nTickEnd = MP_TICK();
float fMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * (nTickEnd - nTickStart);
MicroProfilePrintf(MicroProfileWriteSocket, &Connection, "\n<!-- Sent %dkb in %6.2fms-->\n\n",((nDataEnd-nDataStart)>>10) + 1, fMs);
#if MICROPROFILE_DEBUG
printf("\nSent %lldkb, in %6.3fms\n\n", ((nDataEnd-nDataStart)>>10) + 1, fMs);
#endif
bServed = true;
}
}
#ifdef _WIN32
closesocket(Connection);
#else
close(Connection);
#endif
}
return bServed;
}
#endif
#if MICROPROFILE_CONTEXT_SWITCH_TRACE
#ifdef _WIN32
#define INITGUID
#include <evntrace.h>
#include <evntcons.h>
#include <strsafe.h>
static GUID g_MicroProfileThreadClassGuid = { 0x3d6fa8d1, 0xfe05, 0x11d0, 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c };
struct MicroProfileSCSwitch
{
uint32_t NewThreadId;
uint32_t OldThreadId;
int8_t NewThreadPriority;
int8_t OldThreadPriority;
uint8_t PreviousCState;
int8_t SpareByte;
int8_t OldThreadWaitReason;
int8_t OldThreadWaitMode;
int8_t OldThreadState;
int8_t OldThreadWaitIdealProcessor;
uint32_t NewThreadWaitTime;
uint32_t Reserved;
};
VOID WINAPI MicroProfileContextSwitchCallback(PEVENT_TRACE pEvent)
{
if (pEvent->Header.Guid == g_MicroProfileThreadClassGuid)
{
if (pEvent->Header.Class.Type == 36)
{
MicroProfileSCSwitch* pCSwitch = (MicroProfileSCSwitch*) pEvent->MofData;
if ((pCSwitch->NewThreadId != 0) || (pCSwitch->OldThreadId != 0))
{
MicroProfileContextSwitch Switch;
Switch.nThreadOut = pCSwitch->OldThreadId;
Switch.nThreadIn = pCSwitch->NewThreadId;
Switch.nCpu = pEvent->BufferContext.ProcessorNumber;
Switch.nTicks = pEvent->Header.TimeStamp.QuadPart;
MicroProfileContextSwitchPut(&Switch);
}
}
}
}
ULONG WINAPI MicroProfileBufferCallback(PEVENT_TRACE_LOGFILE Buffer)
{
return (S.bContextSwitchStop || !S.bContextSwitchRunning) ? FALSE : TRUE;
}
struct MicroProfileKernelTraceProperties : public EVENT_TRACE_PROPERTIES
{
char dummy[sizeof(KERNEL_LOGGER_NAME)];
};
void MicroProfileContextSwitchStopTrace()
{
TRACEHANDLE SessionHandle = 0;
MicroProfileKernelTraceProperties sessionProperties;
ZeroMemory(&sessionProperties, sizeof(sessionProperties));
sessionProperties.Wnode.BufferSize = sizeof(sessionProperties);
sessionProperties.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
sessionProperties.Wnode.ClientContext = 1; //QPC clock resolution
sessionProperties.Wnode.Guid = SystemTraceControlGuid;
sessionProperties.BufferSize = 1;
sessionProperties.NumberOfBuffers = 128;
sessionProperties.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
sessionProperties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
sessionProperties.MaximumFileSize = 0;
sessionProperties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
sessionProperties.LogFileNameOffset = 0;
EVENT_TRACE_LOGFILE log;
ZeroMemory(&log, sizeof(log));
log.LoggerName = KERNEL_LOGGER_NAME;
log.ProcessTraceMode = 0;
TRACEHANDLE hLog = OpenTrace(&log);
if (hLog)
{
ControlTrace(SessionHandle, KERNEL_LOGGER_NAME, &sessionProperties, EVENT_TRACE_CONTROL_STOP);
}
CloseTrace(hLog);
}
void MicroProfileTraceThread(int unused)
{
MicroProfileContextSwitchStopTrace();
ULONG status = ERROR_SUCCESS;
TRACEHANDLE SessionHandle = 0;
MicroProfileKernelTraceProperties sessionProperties;
ZeroMemory(&sessionProperties, sizeof(sessionProperties));
sessionProperties.Wnode.BufferSize = sizeof(sessionProperties);
sessionProperties.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
sessionProperties.Wnode.ClientContext = 1; //QPC clock resolution
sessionProperties.Wnode.Guid = SystemTraceControlGuid;
sessionProperties.BufferSize = 1;
sessionProperties.NumberOfBuffers = 128;
sessionProperties.EnableFlags = EVENT_TRACE_FLAG_CSWITCH|EVENT_TRACE_FLAG_PROCESS;
sessionProperties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
sessionProperties.MaximumFileSize = 0;
sessionProperties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
sessionProperties.LogFileNameOffset = 0;
status = StartTrace((PTRACEHANDLE) &SessionHandle, KERNEL_LOGGER_NAME, &sessionProperties);
if (ERROR_SUCCESS != status)
{
S.bContextSwitchRunning = false;
return;
}
EVENT_TRACE_LOGFILE log;
ZeroMemory(&log, sizeof(log));
log.LoggerName = KERNEL_LOGGER_NAME;
log.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
log.EventCallback = MicroProfileContextSwitchCallback;
log.BufferCallback = MicroProfileBufferCallback;
TRACEHANDLE hLog = OpenTrace(&log);
ProcessTrace(&hLog, 1, 0, 0);
CloseTrace(hLog);
MicroProfileContextSwitchStopTrace();
S.bContextSwitchRunning = false;
}
void MicroProfileStartContextSwitchTrace()
{
if(!S.bContextSwitchRunning)
{
if(!S.pContextSwitchThread)
S.pContextSwitchThread = new std::thread();
if(S.pContextSwitchThread->joinable())
{
S.bContextSwitchStop = true;
S.pContextSwitchThread->join();
}
S.bContextSwitchRunning = true;
S.bContextSwitchStop = false;
*S.pContextSwitchThread = std::thread(&MicroProfileTraceThread, 0);
}
}
void MicroProfileStopContextSwitchTrace()
{
if(S.bContextSwitchRunning && S.pContextSwitchThread)
{
S.bContextSwitchStop = true;
S.pContextSwitchThread->join();
}
}
bool MicroProfileIsLocalThread(uint32_t nThreadId)
{
HANDLE h = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, nThreadId);
if(h == NULL)
return false;
DWORD hProcess = GetProcessIdOfThread(h);
CloseHandle(h);
return GetCurrentProcessId() == hProcess;
}
#else
#error "context switch trace not supported/implemented on platform"
#endif
#else
bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;}
void MicroProfileStopContextSwitchTrace(){}
void MicroProfileStartContextSwitchTrace(){}
#endif
#undef S
#ifdef _WIN32
#pragma warning(pop)
#endif
#endif
#endif
///start embedded file from microprofile.html
#ifdef MICROPROFILE_EMBED_HTML
const char g_MicroProfileHtml_begin[] =
"<!DOCTYPE HTML>\n"
"<html>\n"
"<head>\n"
"<title>MicroProfile Capture</title>\n"
"<style>\n"
"/* about css: http://bit.ly/1eMQ42U */\n"
"body {margin: 0px;padding: 0px; font: 12px Courier New;background-color:#474747; color:white;overflow:hidden;}\n"
"ul {list-style-type: none;margin: 0;padding: 0;}\n"
"li{display: inline; float:left;border:5px; position:relative;text-align:center;}\n"
"a {\n"
" float:left;\n"
" text-decoration:none;\n"
" display: inline;\n"
" text-align: center;\n"
" padding:5px;\n"
" padding-bottom:0px;\n"
" padding-top:0px;\n"
" color: #FFFFFF;\n"
" background-color: #474747;\n"
"}\n"
"a:hover, a:active{\n"
" background-color: #000000;\n"
"}\n"
"\n"
"ul ul {\n"
" position:absolute;\n"
" left:0;\n"
" top:100%;\n"
" margin-left:-999em;\n"
"}\n"
"li:hover ul {\n"
" margin-left:0;\n"
" margin-right:0;\n"
"}\n"
"ul li ul{ display:block;float:none;width:100%;}\n"
"ul li ul li{ display:block;float:none;width:100%;}\n"
"li li a{ display:block;float:none;width:100%;text-align:left;}\n"
"#nav li:hover div {margin-left:0;}\n"
".help {position:absolute;z-index:5;text-align:left;padding:2px;margin-left:-999em;background-color: #313131;}\n"
".root {z-index:1;position:absolute;top:0px;left:0px;}\n"
"</style>\n"
"</head>\n"
"<body style=\"\">\n"
"<canvas id=\"History\" height=\"70\" style=\"background-color:#474747;margin:0px;padding:0px;\"></canvas><canvas id=\"DetailedView\" height=\"200\" style=\"background-color:#474747;margin:0px;padding:0px;\"></canvas>\n"
"<div id=\"root\" class=\"root\">\n"
"<ul id=\"nav\">\n"
"<li><a href=\"#\">?</a>\n"
"<div class=\"help\" style=\"left:20px;top:20px;width:220px;\">\n"
"Use Cursor to Inspect<br>\n"
"Shift-Drag to Pan view<br>\n"
"Ctrl-Drag to Zoom view<br>\n"
"Click to Zoom to selected range<br>\n"
"</div>\n"
"\n"
"<div class=\"help\" id=\"divFrameInfo\" style=\"left:20px;top:300px;width:auto;\">\n"
"</div>\n"
"\n"
"</li>\n"
"<li><a href=\"#\" onclick=\"SetMode(\'timers\');\" id=\"buttonTimers\">Timers</a> \n"
"<li><a href=\"#\" onclick=\"SetMode(\'detailed\');\" id=\"buttonDetailed\">Detailed</a> \n"
"</li>\n"
"<li><a href=\"#\">Reference</a>\n"
" <ul id=\'ReferenceSubMenu\'>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'5ms\');\">5ms</a></li>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'10ms\');\">10ms</a></li>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'15ms\');\">15ms</a></li>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'20ms\');\">20ms</a></li>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'33ms\');\">33ms</a></li>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'50ms\');\">50ms</a></li>\n"
" <li><a href=\"#\" onclick=\"SetReferenceTime(\'100ms\');\">100ms</a></li>\n"
" </ul>\n"
"</li>\n"
"<li id=\"ilThreads\"><a href=\"#\">Threads</a>\n"
" <ul id=\"ThreadSubMenu\">\n"
" <li><a href=\"#\" onclick=\"ToggleThread();\">All</a></li>\n"
" <li><a href=\"#\">---</a></li>\n"
" </ul>\n"
"</li>\n"
"<li id=\"ilGroups\"><a href=\"#\">Groups</a>\n"
" <ul id=\"GroupSubMenu\">\n"
" <li><a href=\"#\" onclick=\"ToggleGroup();\">All</a></li>\n"
" <li><a href=\"#\">---</a></li>\n"
" </ul>\n"
"</li>\n"
"<li id=\"ilWidth\"><a href=\"#\">Width</a>\n"
" <ul id=\'WidthMenu\'>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'1\');\">1</a></li>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'0.5\');\">0.5</a></li>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'0.25\');\">0.25</a></li>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'0.1\');\">0.1</a></li>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'0.05\');\">0.05</a></li>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'0.025\');\">0.025</a></li>\n"
" <li><a href=\"#\" onclick=\"SetMinWidth(\'0.01\');\">0.01</a></li>\n"
" </ul>\n"
"</li>\n"
"</ul>\n"
"\n"
"</div>\n"
"\n"
"<script>\n"
"function InvertColor(hexTripletColor) {\n"
" var color = hexTripletColor;\n"
" color = color.substring(1); // remove #\n"
" color = parseInt(color, 16); // convert to integer\n"
" var R = ((color >> 16) % 256)/255.0;\n"
" var G = ((color >> 8) % 256)/255.0;\n"
" var B = ((color >> 0) % 256)/255.0;\n"
" var lum = (0.2126*R + 0.7152*G + 0.0722*B);\n"
" if(lum < 0.7)\n"
" {\n"
" return \'#ffffff\';\n"
" }\n"
" else\n"
" {\n"
" return \'#333333\';\n"
" }\n"
"}\n"
"function MakeGroup(id, name, numtimers, isgpu)\n"
"{\n"
" var group = {\"id\":id, \"name\":name, \"numtimers\":numtimers, \"isgpu\":isgpu};\n"
" return group;\n"
"}\n"
"\n"
"function MakeTimer(id, name, group, color, average, max, exclaverage, exclmax, callaverage, callcount, meta)\n"
"{\n"
" var timer = {\"id\":id, \"name\":name, \"len\":name.length, \"color\":color, \"textcolor\":InvertColor(color), \"group\":group, \"average\":average, \"max\":max, \"exclaverage\":exclaverage, \"exclmax\":exclmax, \"callaverage\":callaverage, \"callcount\":callcount, \"meta\":meta};\n"
" return timer;\n"
"}\n"
"function MakeFrame(id, framestart, frameend, ts, tt, ti)\n"
"{\n"
" var frame = {\"id\":id, \"framestart\":framestart, \"frameend\":frameend, \"ts\":ts, \"tt\":tt, \"ti\":ti};\n"
" return frame;\n"
"}\n"
"\n"
"";
const size_t g_MicroProfileHtml_begin_size = sizeof(g_MicroProfileHtml_begin);
const char g_MicroProfileHtml_end[] =
"\n"
"\n"
"\n"
"var CanvasDetailedView = document.getElementById(\'DetailedView\');\n"
"var CanvasHistory = document.getElementById(\'History\');\n"
"\n"
"var fDetailedOffset = Frames[0].framestart;\n"
"var fDetailedRange = Frames[0].frameend - fDetailedOffset;\n"
"var nWidth = CanvasDetailedView.width;\n"
"var nHeight = CanvasDetailedView.height;\n"
"var ReferenceTime = 33;\n"
"var nHistoryHeight = 70;\n"
"var nOffsetY = 0;\n"
"var nOffsetBarsY = 0;\n"
"var nBarsWidth = 80;\n"
"var MouseButtonState = [0,0,0,0,0,0,0,0];\n"
"var MouseDrag = 0;\n"
"var MouseZoom = 0;\n"
"var DetailedViewMouseX = 0;\n"
"var DetailedViewMouseY = 0;\n"
"var HistoryViewMouseX = -1;\n"
"var HistoryViewMouseY = -1;\n"
"var MouseHistory = 0;\n"
"var MouseDetailed = 0;\n"
"var FontHeight = 10;\n"
"var FontWidth = 1;\n"
"var FontAscent = 3; //Set manually\n"
"var Font = \'Bold \' + FontHeight + \'px Courier New\';\n"
"var BoxHeight = FontHeight + 2;\n"
"var ThreadsActive = new Object();\n"
"var ThreadsAllActive = 1;\n"
"var GroupsActive = new Object();\n"
"var GroupsAllActive = 1;\n"
"var nMinWidth = 0.5;\n"
"\n"
"var nModDown = 0;\n"
"var g_MSG = \'no\';\n"
"var nDrawCount = 0;\n"
"var nBackColors = [\'#474747\', \'#313131\' ];\n"
"var nBackColorOffset = \'#606060\';\n"
"var FRAME_HISTORY_COLOR_CPU = \'#ff7f27\';\n"
"var FRAME_HISTORY_COLOR_GPU = \'#ffffff\';\n"
"var ZOOM_TIME = 0.5;\n"
"var AnimationActive = false;\n"
"var nHoverToken = -1;\n"
"var nHoverFrame = -1;\n"
"var nHoverTokenIndex = -1;\n"
"var nHoverTokenLogIndex = -1;\n"
"var nHoverCounter = 0;\n"
"var nHoverCounterDelta = 8;\n"
"\n"
"var fFrameScale = 33.33;\n"
"var fRangeBegin = 0;\n"
"var fRangeEnd = -1;\n"
"\n"
"var ModeDetailed = 0;\n"
"var ModeTimers = 1;\n"
"var Mode = ModeDetailed;\n"
"\n"
"var DebugDrawQuadCount = 0;\n"
"var DebugDrawTextCount = 0;\n"
"\n"
"\n"
"function InitFrameInfo()\n"
"{\n"
" var DetailedTotal = 0;\n"
" for(var i = 0; i < Frames.length; i++)\n"
" {\n"
" var frfr = Frames[i];\n"
" DetailedTotal += frfr.frameend - frfr.framestart;\n"
" }\n"
"\n"
" var div = document.getElementById(\'divFrameInfo\');\n"
" var txt = \'\';\n"
" txt = txt + \'Timers View\' + \'<br>\';\n"
" txt = txt + \'Frames:\' + AggregateInfo.Frames +\'<br>\';\n"
" txt = txt + \'Time:\' + AggregateInfo.Time.toFixed(2) +\'ms<br>\';\n"
" txt = txt + \'<hr>\';\n"
" txt = txt + \'Detailed View\' + \'<br>\';\n"
" txt = txt + \'Frames:\' + Frames.length +\'<br>\';\n"
" txt = txt + \'Time:\' + DetailedTotal.toFixed(2) +\'ms<br>\';\n"
" div.innerHTML = txt;\n"
"}\n"
"function InitGroups()\n"
"{\n"
" for(groupid in GroupInfo)\n"
" {\n"
" var TimerArray = Array();\n"
" for(timerid in TimerInfo)\n"
" {\n"
" if(TimerInfo[timerid].group == groupid)\n"
" {\n"
" TimerArray.push(timerid);\n"
" }\n"
" }\n"
" GroupInfo[groupid].TimerArray = TimerArray;\n"
" }\n"
"}\n"
"\n"
"function InitThreadMenu()\n"
"{\n"
" var ulThreadMenu = document.getElementById(\'ThreadSubMenu\');\n"
" var MaxLen = 7;\n"
" for(var idx in ThreadNames)\n"
" {\n"
" var name = ThreadNames[idx];\n"
" var li = document.createElement(\'li\');\n"
" if(name.length > MaxLen)\n"
" {\n"
" MaxLen = name.length;\n"
" }\n"
" li.innerText = name;\n"
" var asText = li.innerHTML;\n"
" var html = \'<a href=\"#\" onclick=\"ToggleThread(\\'\' + name + \'\\');\">\' + asText + \'</a>\';\n"
" li.innerHTML = html;\n"
" ulThreadMenu.appendChild(li);\n"
" }\n"
" var LenStr = (5+(1+MaxLen) * FontWidth) + \'px\';\n"
" var Lis = ulThreadMenu.getElementsByTagName(\'li\');\n"
" for(var i = 0; i < Lis.length; ++i)\n"
" {\n"
" Lis[i].style[\'width\'] = LenStr;\n"
" }\n"
"}\n"
"\n"
"function UpdateThreadMenu()\n"
"{\n"
" var ulThreadMenu = document.getElementById(\'ThreadSubMenu\');\n"
" var as = ulThreadMenu.getElementsByTagName(\'a\');\n"
" for(var i = 0; i < as.length; ++i)\n"
" {\n"
" var elem = as[i];\n"
" var inner = elem.innerText;\n"
" var bActive = false;\n"
" if(i < 2)\n"
" {\n"
" if(inner == \'All\')\n"
" {\n"
" bActive = ThreadsAllActive;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" bActive = ThreadsActive[inner];\n"
" }\n"
" if(bActive)\n"
" {\n"
" elem.style[\'text-decoration\'] = \'underline\';\n"
" }\n"
" else\n"
" {\n"
" elem.style[\'text-decoration\'] = \'none\';\n"
" }\n"
" }\n"
"}\n"
"\n"
"function ToggleThread(ThreadName)\n"
"{\n"
" if(ThreadName)\n"
" {\n"
" if(ThreadsActive[ThreadName])\n"
" {\n"
" ThreadsActive[ThreadName] = false;\n"
" }\n"
" else\n"
" {\n"
" ThreadsActive[ThreadName] = true;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" if(ThreadsAllActive)\n"
" {\n"
" ThreadsAllActive = 0;\n"
" }\n"
" else\n"
" {\n"
" ThreadsAllActive = 1;\n"
" }\n"
" }\n"
" UpdateThreadMenu();\n"
" WriteCookie();\n"
" Draw();\n"
"}\n"
"\n"
"\n"
"function InitGroupMenu()\n"
"{\n"
" var ulGroupMenu = document.getElementById(\'GroupSubMenu\');\n"
" var MaxLen = 7;\n"
" for(var idx in GroupInfo)\n"
" {\n"
" var name = GroupInfo[idx].name;\n"
" var li = document.createElement(\'li\');\n"
" if(name.length > MaxLen)\n"
" {\n"
" MaxLen = name.length;\n"
" }\n"
" li.innerText = name;\n"
" var asText = li.innerHTML;\n"
" var html = \'<a href=\"#\" onclick=\"ToggleGroup(\\'\' + name + \'\\');\">\' + asText + \'</a>\';\n"
" li.innerHTML = html;\n"
" ulGroupMenu.appendChild(li);\n"
" }\n"
" var LenStr = (5+(1+MaxLen) * FontWidth) + \'px\';\n"
" var Lis = ulGroupMenu.getElementsByTagName(\'li\');\n"
" for(var i = 0; i < Lis.length; ++i)\n"
" {\n"
" Lis[i].style[\'width\'] = LenStr;\n"
" }\n"
" UpdateGroupMenu();\n"
"}\n"
"\n"
"function UpdateGroupMenu()\n"
"{\n"
" var ulThreadMenu = document.getElementById(\'GroupSubMenu\');\n"
" var as = ulThreadMenu.getElementsByTagName(\'a\');\n"
" for(var i = 0; i < as.length; ++i)\n"
" {\n"
" var elem = as[i];\n"
" var inner = elem.innerText;\n"
" var bActive = false;\n"
" if(i < 2)\n"
" {\n"
" if(inner == \'All\')\n"
" {\n"
" bActive = GroupsAllActive;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" bActive = GroupsActive[inner];\n"
" }\n"
" if(bActive)\n"
" {\n"
" elem.style[\'text-decoration\'] = \'underline\';\n"
" }\n"
" else\n"
" {\n"
" elem.style[\'text-decoration\'] = \'none\';\n"
" }\n"
" }\n"
"}\n"
"\n"
"\n"
"function ToggleGroup(GroupName)\n"
"{\n"
" if(GroupName)\n"
" {\n"
" if(GroupsActive[GroupName])\n"
" {\n"
" GroupsActive[GroupName] = false;\n"
" }\n"
" else\n"
" {\n"
" GroupsActive[GroupName] = true;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" if(GroupsAllActive)\n"
" {\n"
" GroupsAllActive = 0;\n"
" }\n"
" else\n"
" {\n"
" GroupsAllActive = 1;\n"
" }\n"
" }\n"
" UpdateGroupMenu();\n"
" WriteCookie();\n"
" Draw();\n"
"}\n"
"\n"
"\n"
"\n"
"function SetMode(NewMode)\n"
"{\n"
" console.log(\'setmodemodemode \' + NewMode);\n"
" console.log(\'!!!mode is \' + NewMode);\n"
" var buttonTimers = document.getElementById(\'buttonTimers\');\n"
" var buttonDetailed = document.getElementById(\'buttonDetailed\');\n"
" var ilWidth = document.getElementById(\'ilWidth\');\n"
" var ilThreads = document.getElementById(\'ilThreads\');\n"
" var ilGroups = document.getElementById(\'ilGroups\');\n"
" if(NewMode == \'timers\' || NewMode == ModeTimers)\n"
" {\n"
" buttonTimers.style[\'text-decoration\'] = \'underline\';\n"
" buttonDetailed.style[\'text-decoration\'] = \'none\';\n"
" ilWidth.style[\'display\'] = \'none\';\n"
" ilThreads.style[\'display\'] = \'none\';\n"
" ilGroups.style[\'display\'] = \'block\';\n"
" Mode = ModeTimers;\n"
" }\n"
" else if(NewMode == \'detailed\' || NewMode == ModeDetailed)\n"
" {\n"
" buttonTimers.style[\'text-decoration\'] = \'none\';\n"
" buttonDetailed.style[\'text-decoration\'] = \'underline\';\n"
" ilWidth.style[\'display\'] = \'block\';\n"
" ilThreads.style[\'display\'] = \'block\';\n"
" ilGroups.style[\'display\'] = \'none\';\n"
" Mode = ModeDetailed;\n"
" }\n"
" WriteCookie();\n"
" Draw();\n"
"}\n"
"\n"
"function SetReferenceTime(TimeString)\n"
"{\n"
" ReferenceTime = parseInt(TimeString);\n"
" var ReferenceMenu = document.getElementById(\'ReferenceSubMenu\');\n"
" var Links = ReferenceMenu.getElementsByTagName(\'a\');\n"
" for(var i = 0; i < Links.length; ++i)\n"
" {\n"
" if(Links[i].innerHTML.match(\'^\' + TimeString))\n"
" {\n"
" Links[i].style[\'text-decoration\'] = \'underline\';\n"
" }\n"
" else\n"
" {\n"
" Links[i].style[\'text-decoration\'] = \'none\';\n"
" }\n"
" }\n"
" WriteCookie();\n"
" Draw();\n"
"}\n"
"\n"
"function SetMinWidth(TimeString)\n"
"{\n"
" nMinWidth = parseFloat(TimeString);\n"
" var ReferenceMenu = document.getElementById(\'WidthMenu\');\n"
" var Links = ReferenceMenu.getElementsByTagName(\'a\');\n"
" for(var i = 0; i < Links.length; ++i)\n"
" {\n"
" if(Links[i].innerHTML.match(\'^\' + TimeString))\n"
" {\n"
" Links[i].style[\'text-decoration\'] = \'underline\';\n"
" }\n"
" else\n"
" {\n"
" Links[i].style[\'text-decoration\'] = \'none\';\n"
" }\n"
" }\n"
" WriteCookie();\n"
" Draw();\n"
"}\n"
"\n"
"function GatherHoverMetaCounters(TimerIndex, StartIndex, nLog, nFrameLast)\n"
"{\n"
" var HoverInfo = new Object();\n"
" var StackPos = 1;\n"
" //search backwards, count meta counters \n"
" for(var i = nFrameLast; i >= 0; i--)\n"
" {\n"
" var fr = Frames[i];\n"
" var ts = fr.ts[nLog];\n"
" var ti = fr.ti[nLog];\n"
" var tt = fr.tt[nLog];\n"
" var start = i == nFrameLast ? StartIndex-1 : ts.length-1;\n"
"\n"
" for(var j = start; j >= 0; j--)\n"
" {\n"
" var type = tt[j];\n"
" var index = ti[j];\n"
" var time = ts[j];\n"
" if(type == 1)\n"
" {\n"
" StackPos--;\n"
" if(StackPos == 0 && index == TimerIndex)\n"
" {\n"
" return HoverInfo;\n"
" }\n"
" }\n"
" else if(type == 0)\n"
" {\n"
" StackPos++;\n"
" }\n"
" else\n"
" {\n"
" var nMetaCount = type - 2;\n"
" var nMetaIndex = MetaNames[index];\n"
" if(nMetaIndex in HoverInfo)\n"
" {\n"
" HoverInfo[nMetaIndex] += nMetaCount;\n"
" }\n"
" else\n"
" {\n"
" HoverInfo[nMetaIndex] = nMetaCount;\n"
" }\n"
" }\n"
" }\n"
" }\n"
"\n"
"}\n"
"function CalculateTimers(Result, TimerIndex, nFrameFirst, nFrameLast)\n"
"{\n"
" //var Result = new Object();\n"
" if(!nFrameFirst || nFrameFirst < 0)\n"
" nFrameFirst = 0;\n"
" if(!nFrameLast || nFrameLast > Frames.length)\n"
" nFrameLast = Frames.length;\n"
" var FrameCount = nFrameLast - nFrameFirst;\n"
" if(0 == FrameCount)\n"
" return;\n"
" var CallCount = 0;\n"
" var Sum = 0;\n"
" var Max = 0;\n"
" var FrameMax = 0;\n"
"\n"
" var nNumLogs = Frames[0].ts.length;\n"
" var StackPosArray = Array(nNumLogs);\n"
" var StackArray = Array(nNumLogs);\n"
" for(var i = 0; i < nNumLogs; ++i)\n"
" {\n"
" StackPosArray[i] = 0;\n"
" StackArray[i] = Array(20);\n"
" }\n"
"\n"
" for(var i = nFrameFirst; i < nFrameLast; i++)\n"
" {\n"
" var FrameSum = 0;\n"
" var fr = Frames[i];\n"
" for(nLog = 0; nLog < nNumLogs; nLog++)\n"
" {\n"
" var StackPos = StackPosArray[nLog];\n"
" var Stack = StackArray[nLog];\n"
" var ts = fr.ts[nLog];\n"
" var ti = fr.ti[nLog];\n"
" var tt = fr.tt[nLog];\n"
" var count = ts.length;\n"
" for(j = 0; j < count; j++)\n"
" {\n"
" var type = tt[j];\n"
" var index = ti[j];\n"
" var time = ts[j];\n"
" if(type == 1) //enter\n"
" {\n"
" //push\n"
" Stack[StackPos] = time;\n"
" if(StackArray[nLog][StackPos] != time)\n"
" {\n"
" console.log(\'fail fail fail\');\n"
" }\n"
" StackPos++;\n"
" }\n"
" else if(type == 0) // leave\n"
" {\n"
" var timestart;\n"
" var timeend = time;\n"
" if(StackPos>0)\n"
" {\n"
" StackPos--;\n"
" timestart = Stack[StackPos];\n"
" }\n"
" else\n"
" {\n"
" timestart = Frames[nFrameFirst].framestart;\n"
" }\n"
" if(index == TimerIndex)\n"
" {\n"
" var TimeDelta = timeend - timestart;\n"
" CallCount++;\n"
" FrameSum += TimeDelta;\n"
" Sum += TimeDelta;\n"
" if(TimeDelta > Max)\n"
" Max = TimeDelta;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" //meta\n"
" }\n"
" }\n"
" if(FrameSum > FrameMax)\n"
" {\n"
" FrameMax = FrameSum;\n"
" }\n"
" StackPosArray[nLog] = StackPos;\n"
" }\n"
" }\n"
"\n"
" Result.CallCount = CallCount;\n"
" Result.Sum = Sum.toFixed(3);\n"
" Result.Max = Max.toFixed(3);\n"
" Result.Average = (Sum / CallCount).toFixed(3);\n"
" Result.FrameAverage = (Sum / FrameCount).toFixed(3);\n"
" Result.FrameCallAverage = (CallCount / FrameCount).toFixed(3);\n"
" Result.FrameMax = FrameMax.toFixed(3);\n"
" return Result;\n"
"\n"
"}\n"
"\n"
"function DrawDetailedFrameHistory()\n"
"{\n"
" var x = HistoryViewMouseX;\n"
"\n"
" var context = CanvasHistory.getContext(\'2d\');\n"
" context.clearRect(0, 0, CanvasHistory.width, CanvasHistory.height);\n"
"\n"
" var fHeight = nHistoryHeight;\n"
" var fWidth = nWidth / Frames.length;\n"
" var fHeightScale = fHeight / ReferenceTime;\n"
" var fX = 0;\n"
" var FrameIndex = -1;\n"
"\n"
" for(i = 0; i < Frames.length; i++)\n"
" {\n"
" var fMs = Frames[i].frameend - Frames[i].framestart;\n"
" var fH = fHeightScale * fMs;\n"
" var bMouse = x > fX && x < fX + fWidth;\n"
" if(bMouse)\n"
" {\n"
" context.fillStyle = FRAME_HISTORY_COLOR_GPU;\n"
" fRangeBegin = Frames[i].framestart;\n"
" fRangeEnd = Frames[i].frameend;\n"
" FrameIndex = i;\n"
" }\n"
" else\n"
" {\n"
" context.fillStyle = FRAME_HISTORY_COLOR_CPU;\n"
" }\n"
" context.fillRect(fX, fHeight - fH, fWidth-1, fH);\n"
" fX += fWidth;\n"
" }\n"
"\n"
" if(FrameIndex>=0)\n"
" {\n"
" var StringArray = [];\n"
" StringArray.push(\"Frame\");\n"
" StringArray.push(\"\" + FrameIndex);\n"
" StringArray.push(\"Time\");\n"
" StringArray.push(\"\" + (Frames[FrameIndex].frameend - Frames[FrameIndex].framestart).toFixed(3));\n"
"\n"
" DrawToolTip(StringArray, CanvasHistory, HistoryViewMouseX, HistoryViewMouseY+20);\n"
"\n"
" }\n"
"\n"
"}\n"
"\n"
"function DrawDetailedBackground()\n"
"{\n"
" var context = CanvasDetailedView.getContext(\'2d\');\n"
" var fMs = fDetailedRange;\n"
" var fMsEnd = fMs + fDetailedOffset;\n"
" var fMsToScreen = nWidth / fMs;\n"
" var fRate = Math.floor(2*((Math.log(fMs)/Math.log(10))-1))/2;\n"
" var fStep = Math.pow(10, fRate);\n"
" var fRcpStep = 1.0 / fStep;\n"
" var nColorIndex = Math.floor(fDetailedOffset * fRcpStep) % 2;\n"
" if(nColorIndex < 0)\n"
" nColorIndex = -nColorIndex;\n"
" var fStart = Math.floor(fDetailedOffset * fRcpStep) * fStep;\n"
" var fHeight = CanvasDetailedView.height;\n"
" var fScaleX = nWidth / fDetailedRange; \n"
" for(f = fStart; f < fMsEnd; )\n"
" {\n"
" var fNext = f + fStep;\n"
" var X = (f - fDetailedOffset) * fScaleX;\n"
" var W = (fNext-f)*fScaleX;\n"
" context.fillStyle = nBackColors[nColorIndex];\n"
" context.fillRect(X, 0, W+2, fHeight);\n"
" nColorIndex = 1 - nColorIndex;\n"
" f = fNext;\n"
" }\n"
" var fScaleX = nWidth / fDetailedRange; \n"
" context.globalAlpha = 0.5;\n"
" context.strokeStyle = \'#bbbbbb\';\n"
" context.beginPath();\n"
" for(var i = 0; i < Frames.length; i++)\n"
" {\n"
" var frfr = Frames[i];\n"
" if(frfr.frameend < fDetailedOffset || frfr.framestart > fDetailedOffset + fDetailedRange)\n"
" {\n"
" continue;\n"
" }\n"
" var X = (frfr.framestart - fDetailedOffset) * fScaleX;\n"
" if(X >= 0 && X < nWidth)\n"
" {\n"
" context.moveTo(X, 0);\n"
" context.lineTo(X, nHeight);\n"
" }\n"
" }\n"
" context.stroke();\n"
" context.globalAlpha = 1;\n"
"\n"
"}\n"
"function DrawToolTip(StringArray, Canvas, x, y)\n"
"{\n"
" var context = Canvas.getContext(\'2d\');\n"
" context.font = Font;\n"
" var WidthArray = Array(StringArray.length);\n"
" var nMaxWidth = 0;\n"
" var nHeight = 0;\n"
" for(i = 0; i < StringArray.length; i += 2)\n"
" {\n"
" var nWidth0 = context.measureText(StringArray[i]).width;\n"
" var nWidth1 = context.measureText(StringArray[i+1]).width;\n"
" var nSum = nWidth0 + nWidth1;\n"
" WidthArray[i] = nWidth0;\n"
" WidthArray[i+1] = nWidth1;\n"
" if(nSum > nMaxWidth)\n"
" {\n"
" nMaxWidth = nSum;\n"
" }\n"
" nHeight += BoxHeight;\n"
" }\n"
" nMaxWidth += 15;\n"
" //bounds check.\n"
" var CanvasRect = Canvas.getBoundingClientRect();\n"
" if(y + nHeight > CanvasRect.height)\n"
" {\n"
" y = CanvasRect.height - nHeight;\n"
" x += 20;\n"
" }\n"
" if(x + nMaxWidth > CanvasRect.width)\n"
" {\n"
" x = CanvasRect.width - nMaxWidth;\n"
" }\n"
"\n"
" context.fillStyle = \'black\';\n"
" context.fillRect(x-1, y, nMaxWidth+2, nHeight);\n"
" context.fillStyle = \'white\';\n"
"\n"
" var XPos = x;\n"
" var XPosRight = x + nMaxWidth;\n"
" var YPos = y + BoxHeight-2;\n"
" for(i = 0; i < StringArray.length; i += 2)\n"
" {\n"
" context.fillText(StringArray[i], XPos, YPos);\n"
" context.fillText(StringArray[i+1], XPosRight - WidthArray[i+1], YPos);\n"
" YPos += BoxHeight;\n"
" }\n"
"}\n"
"function DrawHoverToolTip()\n"
"{\n"
" if(nHoverToken != -1)\n"
" {\n"
" var StringArray = [];\n"
" var groupid = TimerInfo[nHoverToken].group;\n"
" StringArray.push(GroupInfo[groupid].name);\n"
" StringArray.push(TimerInfo[nHoverToken].name);\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"Time\");\n"
" StringArray.push((fRangeEnd-fRangeBegin).toFixed(3));\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"Total\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].Sum);\n"
" StringArray.push(\"Max\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].Max);\n"
" StringArray.push(\"Average\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].Average);\n"
" StringArray.push(\"Count\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].CallCount);\n"
"\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"\");\n"
"\n"
" StringArray.push(\"Max/Frame\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].FrameMax);\n"
"\n"
" StringArray.push(\"Average Time/Frame\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].FrameAverage);\n"
"\n"
" StringArray.push(\"Average Count/Frame\");\n"
" StringArray.push(\"\" + TimerInfo[nHoverToken].FrameCallAverage);\n"
"\n"
" \n"
" if(nHoverFrame != -1)\n"
" {\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"Frame \" + nHoverFrame);\n"
" StringArray.push(\"\");\n"
"\n"
" var FrameTime = new Object();\n"
" CalculateTimers(FrameTime, nHoverToken, nHoverFrame, nHoverFrame+1);\n"
" StringArray.push(\"Total\");\n"
" StringArray.push(\"\" + FrameTime.Sum);\n"
" StringArray.push(\"Count\");\n"
" StringArray.push(\"\" + FrameTime.CallCount);\n"
" StringArray.push(\"Average\");\n"
" StringArray.push(\"\" + FrameTime.Average);\n"
" StringArray.push(\"Max\");\n"
" StringArray.push(\"\" + FrameTime.Max);\n"
" }\n"
"\n"
" var HoverInfo = GatherHoverMetaCounters(nHoverToken, nHoverTokenIndex, nHoverTokenLogIndex, nHoverFrame);\n"
" var Header = 0;\n"
" for(index in HoverInfo)\n"
" {\n"
" if(0 == Header)\n"
" {\n"
" Header = 1;\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"\");\n"
" StringArray.push(\"Meta\");\n"
" StringArray.push(\"\");\n"
"\n"
" }\n"
" StringArray.push(\"\"+index);\n"
" StringArray.push(\"\"+HoverInfo[index]);\n"
" }\n"
" DrawToolTip(StringArray, CanvasDetailedView, DetailedViewMouseX, DetailedViewMouseY+20);\n"
"\n"
" }\n"
"}\n"
"\n"
"\n"
"function DrawBarView()\n"
"{\n"
" console.log(\'bar view view\');\n"
" nHoverToken = -1;\n"
" nHoverFrame = -1;\n"
" var context = CanvasDetailedView.getContext(\'2d\');\n"
" context.clearRect(0, 0, nWidth, nHeight);\n"
"\n"
" var Height = BoxHeight;\n"
" var Width = nWidth;\n"
"\n"
" //clamp offset to prevent scrolling into the void\n"
" var nTotalRows = 0;\n"
" for(var groupid in GroupInfo)\n"
" {\n"
" if(GroupsAllActive || GroupsActive[GroupInfo[groupid].name])\n"
" {\n"
" nTotalRows += GroupInfo[groupid].TimerArray.length + 1;\n"
" }\n"
" }\n"
" var nTotalRowPixels = nTotalRows * Height;\n"
" var nFrameRows = nHeight - BoxHeight;\n"
" if(nOffsetBarsY + nFrameRows > nTotalRowPixels && nTotalRowPixels > nFrameRows)\n"
" {\n"
" nOffsetBarsY = nTotalRowPixels - nFrameRows;\n"
" }\n"
"\n"
"\n"
" var Y = -nOffsetBarsY + BoxHeight;\n"
" var nColorIndex = 0;\n"
"\n"
" context.fillStyle = \'white\';\n"
" context.font = Font;\n"
" var bMouseIn = 0;\n"
" var RcpReferenceTime = 1.0 / ReferenceTime;\n"
" var CountWidth = 8 * FontWidth;\n"
" var nMetaLen = TimerInfo[0].meta.length;\n"
" var nMetaCharacters = 10;\n"
" for(var i = 0; i < nMetaLen; ++i)\n"
" {\n"
" if(nMetaCharacters < MetaNames[i].length)\n"
" nMetaCharacters = MetaNames[i].length;\n"
" }\n"
" var nWidthMeta = nMetaCharacters * FontWidth + 6;\n"
"\n"
"\n"
"\n"
" for(var groupid in GroupInfo)\n"
" {\n"
" var Group = GroupInfo[groupid];\n"
" if(GroupsAllActive || GroupsActive[Group.name])\n"
" {\n"
" //write header\n"
" nColorIndex = 1-nColorIndex;\n"
" bMouseIn = DetailedViewMouseY >= Y && DetailedViewMouseY < Y + BoxHeight;\n"
" context.fillStyle = bMouseIn ? nBackColorOffset : nBackColors[nColorIndex];\n"
" context.fillRect(0, Y, Width, Height);\n"
" context.fillStyle = \'white\';\n"
" context.fillText(Group.name, 1, Y+Height-FontAscent);\n"
"\n"
"\n"
" Y += Height;\n"
" var TimerArray = Group.TimerArray;\n"
" var InnerBoxHeight = BoxHeight-2;\n"
" var TimerLen = 6;\n"
" var TimerWidth = TimerLen * FontWidth;\n"
" var nWidthBars = nBarsWidth+2;\n"
" var nWidthMs = TimerWidth + 2 + 10;\n"
"\n"
"\n"
" for(var timerindex in TimerArray)\n"
" {\n"
" var timerid = TimerArray[timerindex];\n"
" var X = 0;\n"
" nColorIndex = 1-nColorIndex;\n"
" bMouseIn = DetailedViewMouseY >= Y && DetailedViewMouseY < Y + BoxHeight;\n"
" if(bMouseIn)\n"
" {\n"
" nHoverToken = timerid;\n"
" }\n"
" context.fillStyle = bMouseIn ? nBackColorOffset : nBackColors[nColorIndex];\n"
" context.fillRect(X, Y, Width, Height);\n"
"\n"
" var Timer = TimerInfo[timerid];\n"
"\n"
" var Average = Timer.average;\n"
" var Max = Timer.max;\n"
" var ExclusiveMax = Timer.exclmax;\n"
" var ExclusiveAverage = Timer.exclaverage;\n"
" var CallAverage = Timer.callaverage;\n"
" var CallCount = Timer.callcount;\n"
" var YText = Y+Height-FontAscent;\n"
" function DrawTimer(Value)\n"
" {\n"
" var Prc = Value * RcpReferenceTime;\n"
" if(Prc > 1)\n"
" {\n"
" Prc = 1;\n"
" }\n"
" context.fillStyle = Timer.color;\n"
" context.fillRect(X+1, Y+1, Prc * nBarsWidth, InnerBoxHeight);\n"
" X += nWidthBars;\n"
" context.fillStyle = \'white\';\n"
" context.fillText((\" \" + Value.toFixed(2)).slice(-TimerLen), X, YText);\n"
" X += nWidthMs;\n"
" }\n"
" function DrawMeta(Value)\n"
" {\n"
" if(!Value)\n"
" {\n"
" Value = \"0\";\n"
" }\n"
" else\n"
" {\n"
" Value = \'\' + Value;\n"
" }\n"
" context.fillText(Value, X + nWidthMeta - 6 - Value.length * FontWidth, YText);\n"
" X += nWidthMeta;\n"
" }\n"
"\n"
" DrawTimer(Average);\n"
" DrawTimer(Max);\n"
" DrawTimer(CallAverage);\n"
" context.fillStyle = \'white\';\n"
" context.fillText(CallCount, X, YText);\n"
" X += CountWidth;\n"
" DrawTimer(ExclusiveAverage);\n"
" DrawTimer(ExclusiveMax);\n"
"\n"
" context.fillStyle = \'white\';\n"
" for(var j = 0; j < nMetaLen; ++j)\n"
" {\n"
" DrawMeta(Timer.meta[j]);\n"
" }\n"
"\n"
"\n"
" context.fillStyle = Timer.color;\n"
" context.fillText(Timer.name, X+1, YText);\n"
" Y += Height;\n"
"\n"
" }\n"
" }\n"
" }\n"
" X = 0;\n"
" context.fillStyle = nBackColorOffset;\n"
" context.fillRect(0, 0, Width, Height);\n"
" context.fillStyle = \'white\';\n"
"\n"
"\n"
" function DrawHeaderSplit(Header)\n"
" {\n"
" context.fillStyle = \'white\';\n"
" context.fillText(Header, X, Height-FontAscent);\n"
" X += nWidthBars;\n"
" context.fillStyle = nBackColorOffset;\n"
" X += nWidthMs;\n"
" context.fillRect(X-3, 0, 1, nHeight);\n"
" }\n"
" function DrawHeaderSplitSingle(Header, Width)\n"
" {\n"
" context.fillStyle = \'white\';\n"
" context.fillText(Header, X, Height-FontAscent);\n"
" X += Width;\n"
" context.fillStyle = nBackColorOffset;\n"
" context.fillRect(X-3, 0, 1, nHeight);\n"
" }\n"
"\n"
"\n"
" DrawHeaderSplit(\'Average\');\n"
" DrawHeaderSplit(\'Max\');\n"
" DrawHeaderSplit(\'Call Average\');\n"
" DrawHeaderSplitSingle(\'Count\', CountWidth);\n"
" DrawHeaderSplit(\'Excl Average\');\n"
" DrawHeaderSplit(\'Excl Max\');\n"
" for(var i = 0; i < nMetaLen; ++i)\n"
" {\n"
" DrawHeaderSplitSingle(MetaNames[i], nWidthMeta);\n"
" }\n"
" DrawHeaderSplit(\'Name\');\n"
"\n"
"}\n"
"\n"
"function DrawDetailed(Animation)\n"
"{\n"
" if(AnimationActive != Animation)\n"
" {\n"
" return;\n"
" }\n"
" DebugDrawQuadCount = 0;\n"
" DebugDrawTextCount = 0;\n"
"\n"
"\n"
" var start = new Date();\n"
" nDrawCount++;\n"
"\n"
" var context = CanvasDetailedView.getContext(\'2d\');\n"
" context.clearRect(0, 0, CanvasDetailedView.width, CanvasDetailedView.height);\n"
"\n"
" DrawDetailedBackground();\n"
"\n"
" var colors = [ \'#ff0000\', \'#ff00ff\', \'#ffff00\'];\n"
"\n"
" var fScaleX = nWidth / fDetailedRange; \n"
" var fOffsetY = -nOffsetY + BoxHeight;\n"
" var nHoverTokenNext = -1;\n"
" var nHoverTokenLogIndexNext = -1;\n"
" var nHoverTokenIndexNext = -1;\n"
" nHoverCounter += nHoverCounterDelta;\n"
" if(nHoverCounter >= 255) \n"
" {\n"
" nHoverCounter = 255;\n"
" nHoverCounterDelta = -nHoverCounterDelta;\n"
" }\n"
" if(nHoverCounter < 128) \n"
" {\n"
" nHoverCounter = 128;\n"
" nHoverCounterDelta = -nHoverCounterDelta;\n"
" }\n"
" var nHoverHigh = nHoverCounter.toString(16);\n"
" var nHoverLow = (127+255-nHoverCounter).toString(16);\n"
" var nHoverColor = \'#\' + nHoverHigh + nHoverHigh + nHoverHigh;\n"
"\n"
" context.fillStyle = \'black\';\n"
" context.font = Font;\n"
" var nNumLogs = Frames[0].ts.length;\n"
" for(nLog = 0; nLog < nNumLogs; nLog++)\n"
" {\n"
" var ThreadName = ThreadNames[nLog];\n"
" if(ThreadsAllActive || ThreadsActive[ThreadName])\n"
" {\n"
" context.fillStyle = \'white\';\n"
" context.fillText(ThreadName, 0, fOffsetY);\n"
" fOffsetY += BoxHeight;\n"
"\n"
" var MaxDepth = 1;\n"
" var StackPos = 0;\n"
" var Stack = Array(20);\n"
"\n"
" for(var i = 0; i < Frames.length; i++)\n"
" {\n"
" var frfr = Frames[i];\n"
" if(0 == StackPos && frfr.framestart > fDetailedOffset + fDetailedRange) \n"
" {\n"
" continue;\n"
" }\n"
" var ts = frfr.ts[nLog];\n"
" var ti = frfr.ti[nLog];\n"
" var tt = frfr.tt[nLog];\n"
" var count = ts.length;\n"
"\n"
" for(j = 0; j < count; j++)\n"
" {\n"
" var type = tt[j];\n"
" var index = ti[j];\n"
" var time = ts[j];\n"
" if(type == 1)\n"
" {\n"
" //push\n"
" Stack[StackPos] = time;\n"
" StackPos++;\n"
" if(StackPos > MaxDepth)\n"
" {\n"
" MaxDepth = StackPos;\n"
" }\n"
" }\n"
" else if(type == 0)\n"
" {\n"
" if(StackPos>0)\n"
" {\n"
" StackPos--;\n"
" var timestart = Stack[StackPos];\n"
" var timeend = time;\n"
" var X = (timestart - fDetailedOffset) * fScaleX;\n"
" var Y = fOffsetY + StackPos * BoxHeight;\n"
" var W = (timeend-timestart)*fScaleX;\n"
"\n"
" if(W > nMinWidth && X < nWidth && X+W > 0)\n"
" {\n"
" if(index == nHoverToken)\n"
" {\n"
" context.fillStyle = nHoverColor;\n"
" context.fillRect(X, Y, W, BoxHeight-1);\n"
" }\n"
" else\n"
" {\n"
" context.fillStyle = TimerInfo[index].color;\n"
" context.fillRect(X, Y, W, BoxHeight-1);\n"
" }\n"
" DebugDrawQuadCount++;\n"
"\n"
" var XText = X < 0 ? 0 : X;\n"
" var WText = W - (XText-X);\n"
" var name = TimerInfo[index].name;\n"
" var len = TimerInfo[index].len;\n"
" var sublen = Math.floor((WText-2)/FontWidth);\n"
" if(sublen >= 2)\n"
" {\n"
" if(sublen < len)\n"
" name = name.substr(0, sublen);\n"
" context.fillStyle = InvertColor(TimerInfo[index].color);\n"
" context.fillText(name, XText+1, Y+BoxHeight-FontAscent);\n"
" DebugDrawTextCount++;\n"
" }\n"
" if(DetailedViewMouseX >= X && DetailedViewMouseX <= X+W && DetailedViewMouseY < Y+BoxHeight && DetailedViewMouseY >= Y)\n"
" {\n"
" fRangeBegin = timestart;\n"
" fRangeEnd = timeend;\n"
" nHoverTokenNext = index;\n"
" nHoverTokenIndexNext = j;\n"
" nHoverTokenLogIndexNext = nLog;\n"
" nHoverFrame = i;\n"
" }\n"
" }\n"
" }\n"
" }\n"
" }\n"
" }\n"
" fOffsetY += (1+MaxDepth) * BoxHeight;\n"
" }\n"
" }\n"
" if(MouseDrag || MouseZoom)\n"
" {\n"
" nHoverToken = -1;\n"
" nHoverTokenIndex = -1;\n"
" nHoverTokenLogIndex = -1;\n"
"\n"
" fRangeBegin = fRangeEnd = -1;\n"
" }\n"
" else\n"
" {\n"
" nHoverToken = nHoverTokenNext;\n"
" nHoverTokenIndex = nHoverTokenIndexNext;\n"
" nHoverTokenLogIndex = nHoverTokenLogIndexNext;\n"
"\n"
" }\n"
"\n"
"\n"
"\n"
" if(fRangeBegin < fRangeEnd)\n"
" {\n"
" var X = (fRangeBegin - fDetailedOffset) * fScaleX;\n"
" var Y = 0;\n"
" var W = (fRangeEnd - fRangeBegin) * fScaleX;\n"
" context.globalAlpha = 0.1;\n"
" context.fillStyle = \'#009900\';\n"
" context.fillRect(X, Y, W, nHeight);\n"
" context.globalAlpha = 1;\n"
" context.strokeStyle = \'#00ff00\';\n"
" context.beginPath();\n"
" context.moveTo(X, Y);\n"
" context.lineTo(X, Y+nHeight);\n"
" context.moveTo(X+W, Y);\n"
" context.lineTo(X+W, Y+nHeight);\n"
" context.stroke();\n"
" var tRangeBeginWidth = context.measureText(\'\' + fRangeBegin).width;\n"
"\n"
" context.fillStyle = \'white\';\n"
" context.fillText(\'\' + fRangeBegin, X - tRangeBeginWidth-2, 9);\n"
" context.fillText(\'\' + fRangeEnd, X + W, 9);\n"
" }\n"
"\n"
"\n"
"\n"
"\n"
"\n"
" var end = new Date();\n"
" var time = end - start;\n"
" var timeTaken = \'TIME \' + time + \'ms \';\n"
" if(false)\n"
" {\n"
" context.fillStyle = \'white\';\n"
" context.font = \'italic 12pt Calibri\';\n"
" context.fillText(timeTaken+\" \" + nDrawCount + \"...\" + g_MSG + \" Q:\" + DebugDrawQuadCount + \" T:\" + DebugDrawTextCount, 20, 20);\n"
" g_MSG = \'\';\n"
" }\n"
"}\n"
"\n"
"function ZoomTo(fZoomBegin, fZoomEnd)\n"
"{\n"
" if(fZoomBegin < fZoomEnd)\n"
" {\n"
" AnimationActive = true;\n"
" var fDetailedOffsetOriginal = fDetailedOffset;\n"
" var fDetailedRangeOriginal = fDetailedRange;\n"
" var fDetailedOffsetTarget = fZoomBegin;\n"
" var fDetailedRangeTarget = fZoomEnd - fZoomBegin;\n"
" var TimestampStart = new Date();\n"
" var count = 0;\n"
" function ZoomFunc(Timestamp)\n"
" {\n"
" var fPrc = (new Date() - TimestampStart) / (ZOOM_TIME * 1000.0);\n"
" if(fPrc > 1.0)\n"
" {\n"
" fPrc = 1.0;\n"
" }\n"
" fPrc = Math.pow(fPrc, 0.3);\n"
" fDetailedOffset = fDetailedOffsetOriginal + (fDetailedOffsetTarget - fDetailedOffsetOriginal) * fPrc;\n"
" fDetailedRange = fDetailedRangeOriginal + (fDetailedRangeTarget - fDetailedRangeOriginal) * fPrc;\n"
" DrawDetailed(true);\n"
" if(fPrc >= 1.0)\n"
" {\n"
" AnimationActive = false;\n"
" fDetailedOffset = fDetailedOffsetTarget;\n"
" fDetailedRange = fDetailedRangeTarget;\n"
" }\n"
" else\n"
" {\n"
" requestAnimationFrame(ZoomFunc);\n"
" }\n"
" }\n"
" requestAnimationFrame(ZoomFunc);\n"
" }\n"
"}\n"
"function Draw()\n"
"{\n"
"\n"
" console.log(\'drawing \' + Mode);\n"
" if(Mode == ModeTimers)\n"
" {\n"
" DrawBarView();\n"
" }\n"
" else if(Mode == ModeDetailed)\n"
" {\n"
" DrawDetailed(false);\n"
" DrawHoverToolTip();\n"
" }\n"
" DrawDetailedFrameHistory();\n"
"}\n"
"function AutoRedraw(Timestamp)\n"
"{\n"
" if(Mode == ModeDetailed)\n"
" {\n"
" if(nHoverToken != -1 && !MouseZoom && !MouseDrag)\n"
" {\n"
" DrawDetailed(false);\n"
" DrawHoverToolTip();\n"
" }\n"
" }\n"
" requestAnimationFrame(AutoRedraw);\n"
"}\n"
"\n"
"\n"
"function ZoomGraph(nZoom)\n"
"{\n"
" var fOldRange = fDetailedRange;\n"
" if(nZoom>0)\n"
" {\n"
" fDetailedRange *= Math.pow(nModDown ? 1.40 : 1.03, nZoom);\n"
" }\n"
" else\n"
" {\n"
" var fNewDetailedRange = fDetailedRange / Math.pow((nModDown ? 1.40 : 1.03), -nZoom);\n"
" if(fNewDetailedRange < 0.0001) //100ns\n"
" fNewDetailedRange = 0.0001;\n"
" fDetailedRange = fNewDetailedRange;\n"
" }\n"
"\n"
" var fDiff = fOldRange - fDetailedRange;\n"
" var fMousePrc = DetailedViewMouseX / nWidth;\n"
" if(fMousePrc < 0)\n"
" {\n"
" fMousePrc = 0;\n"
" }\n"
" fDetailedOffset += fDiff * fMousePrc;\n"
"\n"
"}\n"
"\n"
"function MeasureFont()\n"
"{\n"
" var context = CanvasDetailedView.getContext(\'2d\');\n"
" context.font = Font;\n"
" FontWidth = context.measureText(\'W\').width;\n"
"\n"
"}\n"
"function ResizeCanvas() \n"
"{\n"
" nWidth = window.innerWidth;\n"
" nHeight = window.innerHeight - CanvasHistory.height-2;\n"
" var DPR = window.devicePixelRatio;\n"
" if(DPR)\n"
" {\n"
" CanvasDetailedView.style.width = nWidth + \'px\'; \n"
" CanvasDetailedView.style.height = nHeight + \'px\';\n"
" CanvasDetailedView.width = nWidth * DPR;\n"
" CanvasDetailedView.height = nHeight * DPR;\n"
" CanvasHistory.style.width = window.innerWidth + \'px\';\n"
" CanvasHistory.style.height = 70 + \'px\';\n"
" CanvasHistory.width = window.innerWidth * DPR;\n"
" CanvasHistory.height = 70 * DPR;\n"
" CanvasHistory.getContext(\'2d\').scale(DPR,DPR);\n"
" CanvasDetailedView.getContext(\'2d\').scale(DPR,DPR);\n"
"\n"
"\n"
" }\n"
" else\n"
" {\n"
" CanvasDetailedView.width = nWidth;\n"
" CanvasDetailedView.height = nHeight;\n"
" CanvasHistory.width = window.innerWidth;\n"
" }\n"
" Draw();\n"
"}\n"
"\n"
"function MouseMove(evt)\n"
"{\n"
" MouseHistory = 0;\n"
" MouseDetailed = 0;\n"
" HistoryViewMouseX = HistoryViewMouseY = -1;\n"
" if(evt.target == CanvasDetailedView)\n"
" {\n"
" fRangeBegin = fRangeEnd = -1;\n"
" var rect = CanvasDetailedView.getBoundingClientRect();\n"
" var x = evt.clientX - rect.left;\n"
" var y = evt.clientY - rect.top;\n"
" if(Mode == ModeDetailed)\n"
" {\n"
" if(MouseDrag)\n"
" {\n"
" var X = x - DetailedViewMouseX;\n"
" var Y = y - DetailedViewMouseY;\n"
" if(X)\n"
" {\n"
" fDetailedOffset += -X * fDetailedRange / nWidth;\n"
" }\n"
" nOffsetY -= Y;\n"
" if(nOffsetY < 0)\n"
" {\n"
" nOffsetY = 0;\n"
" }\n"
" }\n"
" if(MouseZoom)\n"
" {\n"
" if(y != DetailedViewMouseY)\n"
" {\n"
" ZoomGraph(y - DetailedViewMouseY);\n"
" }\n"
" }\n"
" }\n"
" else if(Mode == ModeTimers)\n"
" {\n"
" if(MouseDrag)\n"
" {\n"
" var X = x - DetailedViewMouseX;\n"
" var Y = y - DetailedViewMouseY;\n"
" nOffsetBarsY -= Y;\n"
" if(nOffsetBarsY < 0)\n"
" {\n"
" nOffsetBarsY = 0;\n"
" }\n"
" }\n"
" }\n"
" DetailedViewMouseX = x;\n"
" DetailedViewMouseY = y;\n"
" }\n"
" else if(evt.target = CanvasHistory)\n"
" {\n"
" var Rect = CanvasHistory.getBoundingClientRect();\n"
" HistoryViewMouseX = evt.clientX - Rect.left;\n"
" HistoryViewMouseY = evt.clientY - Rect.top;\n"
" }\n"
" Draw();\n"
"}\n"
"function MouseButton(bPressed, evt)\n"
"{\n"
" if(evt.target == CanvasHistory)\n"
" {\n"
" if(!bPressed)\n"
" {\n"
" if(evt.button == 0)\n"
" {\n"
" ZoomTo(fRangeBegin, fRangeEnd);\n"
" }\n"
" }\n"
" }\n"
" else if(evt.target == CanvasDetailedView)\n"
" {\n"
" var rect = CanvasDetailedView.getBoundingClientRect();\n"
" var x = evt.clientX - rect.left;\n"
" var y = evt.clientY - rect.top;\n"
" if(bPressed)\n"
" {\n"
" MouseButtonState[evt.button]=1;\n"
" if(evt.button == 0)\n"
" {\n"
" }\n"
" }\n"
" else\n"
" {\n"
" MouseButtonState[evt.button]=1;\n"
" if(evt.button == 0)\n"
" {\n"
" ZoomTo(fRangeBegin, fRangeEnd);\n"
" }\n"
" }\n"
" }\n"
"}\n"
"function MouseOut(evt)\n"
"{\n"
" MouseZoom = 0;\n"
" MouseDrag = 0;\n"
" nHoverToken = -1;\n"
" fRangeBegin = fRangeEnd = -1;\n"
"}\n"
"\n"
"function KeyUp(evt)\n"
"{\n"
" if(evt.keyCode == 17)\n"
" {\n"
" MouseZoom = 0;\n"
" }\n"
" else if(evt.keyCode == 16)\n"
" {\n"
" MouseDrag = 0;\n"
" }\n"
"}\n"
"\n"
"function KeyDown(evt)\n"
"{\n"
" g_MSG = \' keycode\' + evt.keyCode;\n"
" if(evt.keyCode == 17)\n"
" {\n"
" MouseDrag = 0;\n"
" MouseZoom = 1;\n"
" }\n"
" else if(evt.keyCode == 16)\n"
" {\n"
" MouseDrag = 1;\n"
" }\n"
"}\n"
"\n"
"function ReadCookie()\n"
"{\n"
" var result = document.cookie.match(/fisk=([^;]+)/);\n"
" var NewMode = ModeDetailed;\n"
" var ReferenceTimeString = \'33ms\';\n"
" var nMinWidth = 0.1;\n"
" if(result && result.length > 0)\n"
" {\n"
" var Obj = JSON.parse(result[1]);\n"
" if(Obj.Mode)\n"
" {\n"
" NewMode = Obj.Mode;\n"
" }\n"
" if(Obj.ReferenceTime)\n"
" {\n"
" ReferenceTimeString = Obj.ReferenceTime;\n"
" }\n"
" if(Obj.ThreadsAllActive || Obj.ThreadsAllActive == 0 || Obj.ThreadsAllActive == false)\n"
" {\n"
" ThreadsAllActive = Obj.ThreadsAllActive;\n"
" }\n"
" else\n"
" {\n"
" ThreadsAllActive = 1;\n"
" }\n"
" if(Obj.ThreadsActive)\n"
" {\n"
" ThreadsActive = Obj.ThreadsActive;\n"
" }\n"
" if(Obj.nMinWidth)\n"
" {\n"
" nMinWidth = Obj.nMinWidth;\n"
" }\n"
" if(Obj.GroupsAllActive || Obj.GroupsAllActive == 0 || Obj.GroupsAllActive)\n"
" {\n"
" GroupsAllActive = Obj.GroupsAllActive;\n"
" }\n"
" else\n"
" {\n"
" GroupsAllActive = 1;\n"
" }\n"
" if(Obj.GroupsActive)\n"
" {\n"
" GroupsActive = Obj.GroupsActive;\n"
" }\n"
" }\n"
" SetMode(NewMode);\n"
" SetReferenceTime(ReferenceTimeString);\n"
" SetMinWidth(\'\' + nMinWidth);\n"
"\n"
"}\n"
"function WriteCookie()\n"
"{\n"
" var Obj = new Object();\n"
" Obj.Mode = Mode;\n"
" Obj.ReferenceTime = ReferenceTime + \'ms\';\n"
" Obj.ThreadsActive = ThreadsActive;\n"
" Obj.ThreadsAllActive = ThreadsAllActive;\n"
" Obj.GroupsActive = GroupsActive;\n"
" Obj.GroupsAllActive = GroupsAllActive;\n"
" Obj.nMinWidth = nMinWidth;\n"
" var date = new Date();\n"
" date.setFullYear(2099);\n"
" var cookie = \'fisk=\' + JSON.stringify(Obj) + \';expires=\' + date;\n"
" document.cookie = cookie;\n"
"}\n"
"\n"
"CanvasDetailedView.addEventListener(\'mousemove\', MouseMove, false);\n"
"CanvasDetailedView.addEventListener(\'mousedown\', function(evt) { MouseButton(true, evt); });\n"
"CanvasDetailedView.addEventListener(\'mouseup\', function(evt) { MouseButton(false, evt); } );\n"
"CanvasDetailedView.addEventListener(\'mouseout\', MouseOut);\n"
"CanvasHistory.addEventListener(\'mousemove\', MouseMove);\n"
"CanvasHistory.addEventListener(\'mousedown\', function(evt) { MouseButton(true, evt); });\n"
"CanvasHistory.addEventListener(\'mouseup\', function(evt) { MouseButton(false, evt); } );\n"
"CanvasHistory.addEventListener(\'mouseout\', MouseOut);\n"
"window.addEventListener(\'keydown\', KeyDown);\n"
"window.addEventListener(\'keyup\', KeyUp);\n"
"window.addEventListener(\'resize\', ResizeCanvas, false);\n"
"\n"
"\n"
"var start = new Date();\n"
"for(var i = 0; i < TimerInfo.length; i++)\n"
"{\n"
" var v = CalculateTimers(TimerInfo[i], i);\n"
"\n"
"}\n"
"var end = new Date();\n"
"var time = end - start;\n"
"console.log(\'setup :: \' + time + \'ms \');\n"
"\n"
"InitGroups();\n"
"ReadCookie();\n"
"MeasureFont()\n"
"InitThreadMenu();\n"
"InitGroupMenu();\n"
"InitFrameInfo();\n"
"UpdateThreadMenu();\n"
"ResizeCanvas();\n"
"Draw();\n"
"AutoRedraw();\n"
"\n"
"</script>\n"
"</body>\n"
"</html> ";
const size_t g_MicroProfileHtml_end_size = sizeof(g_MicroProfileHtml_end);
#endif //MICROPROFILE_EMBED_HTML
///end embedded file from microprofile.html