#define SCOPE_NAME_LENGTH 256 struct scope_time_record { char ScopeName_[SCOPE_NAME_LENGTH]; string ScopeName; u32 Duration_Cycles; }; struct debug_interface { b32 ShowCameraMouse; b32 ShowTrackedScopes; b32 RenderSculpture; b32 SendSACNData; }; typedef s64 debug_timing_proc(); #define HISTOGRAM_DEPTH 10 struct debug_histogram_entry { char ScopeName_[SCOPE_NAME_LENGTH]; string ScopeName; u32 PerFrame_Cycles[HISTOGRAM_DEPTH]; u32 PerFrame_CallCount[HISTOGRAM_DEPTH]; s32 CurrentFrame; // NOTE(Peter): Cached Values, recalculated ever frame u32 Average_Cycles; u32 Average_CallCount; u32 Total_Cycles; u32 Total_CallCount; }; #define SCOPE_HISTOGRAM_SIZE 512 struct debug_services { s32 TrackedScopesCount; s32 TrackedScopesMax; scope_time_record* TrackedScopes; s64 PerformanceCountFrequency; memory_arena DebugStorage; debug_interface Interface; debug_timing_proc* GetWallClock; debug_histogram_entry* ScopeHistogramUnsorted; debug_histogram_entry* ScopeHistogramSorted; s32 ScopeHistogramUsed; }; internal void InitDebugServices (debug_services* Services, u8* Memory, s32 MemorySize, s32 TrackedScopesMax, s64 PerformanceCountFrequency) { InitMemoryArena(&Services->DebugStorage, Memory, MemorySize, 0); Services->TrackedScopesCount = 0; Services->TrackedScopesMax = TrackedScopesMax; Services->TrackedScopes = PushArray(&Services->DebugStorage, scope_time_record, TrackedScopesMax); Services->Interface.RenderSculpture = true; Services->PerformanceCountFrequency = PerformanceCountFrequency; Services->Interface.ShowCameraMouse = false; Services->Interface.ShowTrackedScopes = false; Services->Interface.RenderSculpture = true; Services->Interface.SendSACNData = false; Services->ScopeHistogramUnsorted = PushArray(&Services->DebugStorage, debug_histogram_entry, SCOPE_HISTOGRAM_SIZE); Services->ScopeHistogramSorted = PushArray(&Services->DebugStorage, debug_histogram_entry, SCOPE_HISTOGRAM_SIZE); Services->ScopeHistogramUsed = 0; } internal s32 DEBUGFindScopeHistogram (debug_services* Services, string Name) { s32 Result = -1; for (s32 i = 0; i < SCOPE_HISTOGRAM_SIZE; i++) { if (StringsEqual(Services->ScopeHistogramUnsorted[i].ScopeName, Name)) { Result = i; break; } } return Result; } internal s32 DEBUGAddScopeHistogram (debug_services* Services, scope_time_record Record) { Assert(Services->ScopeHistogramUsed < SCOPE_HISTOGRAM_SIZE); s32 Result = Services->ScopeHistogramUsed++; debug_histogram_entry* Entry = Services->ScopeHistogramUnsorted + Result; Entry->ScopeName = MakeString(Entry->ScopeName_, 256); Entry->CurrentFrame = 0; Entry->Average_Cycles = 0; Entry->Average_CallCount = 0; Entry->Total_Cycles = 0; Entry->Total_CallCount = 0; CopyStringTo(Record.ScopeName, &Entry->ScopeName); return Result; } internal void DEBUGRecordScopeInHistogram (debug_services* Services, s32 Index, scope_time_record Record) { debug_histogram_entry* Entry = Services->ScopeHistogramUnsorted + Index; s32 FrameIndex = Entry->CurrentFrame; if (FrameIndex >= 0 && FrameIndex < HISTOGRAM_DEPTH) { Entry->PerFrame_Cycles[FrameIndex] += Record.Duration_Cycles; Entry->PerFrame_CallCount[FrameIndex]++; } } internal void DEBUGCacheScopeHistogramValues (debug_histogram_entry* Histogram) { Histogram->Total_Cycles = 0; Histogram->Total_CallCount = 0; // TODO(Peter): This doesn't account for the first frames when the histogram isn't full for (s32 i = 0; i < HISTOGRAM_DEPTH; i++) { Histogram->Total_Cycles += Histogram->PerFrame_Cycles[i]; Histogram->Total_CallCount += Histogram->PerFrame_CallCount[i]; } Histogram->Average_Cycles = (Histogram->Total_Cycles / HISTOGRAM_DEPTH); Histogram->Average_CallCount = (Histogram->Total_CallCount / HISTOGRAM_DEPTH); } internal void DEBUGSortedHistogramInsert(debug_histogram_entry Source, debug_histogram_entry* DestList, s32 DestCount, s32 Max) { s32 CurrentFrame0 = Source.CurrentFrame; s32 V0 = Source.Average_Cycles; //PerFrame_Cycles[CurrentFrame0]; if (DestCount > 0) { for (s32 i = DestCount - 1; i >= 0; i--) { s32 CurrentFrame1 = DestList[i].CurrentFrame; s32 V1 = DestList[i].Average_Cycles; //PerFrame_Cycles[CurrentFrame1]; if (V0 < V1) { DestList[i + 1] = Source; break; } else { DestList[i + 1] = DestList[i]; } } } else { DestList[0] = Source; } } internal void DEBUGSortHistogram(debug_histogram_entry* Source, debug_histogram_entry* Dest, s32 Count, s32 Max) { for (s32 i = 0; i < Count; i++) { DEBUGSortedHistogramInsert(Source[i], Dest, i, Max); } } internal void DEBUGCollateScopeRecords (debug_services* Services) { for (s32 i = 0; i < Services->TrackedScopesCount; i++) { scope_time_record Record = Services->TrackedScopes[i]; s32 Index = DEBUGFindScopeHistogram(Services, Record.ScopeName); if (Index < 0) { Index = DEBUGAddScopeHistogram(Services, Record); } DEBUGRecordScopeInHistogram(Services, Index, Record); } for (s32 h = 0; h < Services->ScopeHistogramUsed; h++) { DEBUGCacheScopeHistogramValues(Services->ScopeHistogramUnsorted + h); } DEBUGSortHistogram(Services->ScopeHistogramUnsorted, Services->ScopeHistogramSorted, Services->ScopeHistogramUsed, SCOPE_HISTOGRAM_SIZE); } internal void EndDebugFrame (debug_services* Services) { DEBUGCollateScopeRecords(Services); GSZeroMemory((u8*)Services->TrackedScopes, sizeof(scope_time_record) * Services->TrackedScopesMax); Services->TrackedScopesCount = 0; for (s32 i = 0; i < Services->ScopeHistogramUsed; i++) { s32 NewFrame = Services->ScopeHistogramUnsorted[i].CurrentFrame + 1; if (NewFrame >= HISTOGRAM_DEPTH) { NewFrame = 0; } Services->ScopeHistogramUnsorted[i].CurrentFrame = NewFrame; Services->ScopeHistogramUnsorted[i].PerFrame_Cycles[NewFrame] = 0; Services->ScopeHistogramUnsorted[i].PerFrame_CallCount[NewFrame] = 0; } } internal scope_time_record* PushScopeRecord(string ScopeName, debug_services* Services) { scope_time_record* Result = 0; s32 OnePastIndex = InterlockedIncrement((long*)&Services->TrackedScopesCount); Assert(OnePastIndex <= Services->TrackedScopesMax); Result = Services->TrackedScopes + OnePastIndex - 1; Result->ScopeName = MakeString(Result->ScopeName_, SCOPE_NAME_LENGTH); CopyStringTo(ScopeName, &Result->ScopeName); return Result; } internal void LogScopeTime (debug_services* Services, string ScopeName, u64 CyclesElapsed) { scope_time_record* Record = PushScopeRecord(ScopeName, Services); Record->Duration_Cycles = CyclesElapsed; } internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFrequency) { r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency); return Result; } #if 1 #define DEBUG_TRACK_FUNCTION scope_tracker ScopeTracker (__FUNCTION__, GlobalDebugServices) #define DEBUG_TRACK_SCOPE(name) scope_tracker ScopeTracker_##name (#name, GlobalDebugServices) #else #define DEBUG_TRACK_FUNCTION #define DEBUG_TRACK_SCOPE(name) #endif struct scope_tracker { s64 ScopeStart; char ScopeName_[SCOPE_NAME_LENGTH]; string ScopeName; debug_services* DebugServices; scope_tracker(char* ScopeName, debug_services* DebugServices) { this->ScopeName = MakeString(this->ScopeName_, SCOPE_NAME_LENGTH); CopyCharArrayToString(ScopeName, &this->ScopeName); this->ScopeStart = DebugServices->GetWallClock(); this->DebugServices = DebugServices; } ~scope_tracker() { s64 ScopeEnd = DebugServices->GetWallClock(); u32 CyclesElapsed = (u32)(ScopeEnd - this->ScopeStart); #if 0 r32 SecondsElapsed = DEBUGGetSecondsElapsed(this->ScopeStart, ScopeEnd, DebugServices->PerformanceCountFrequency); #endif LogScopeTime(DebugServices, ScopeName, CyclesElapsed); } };