1873 lines
60 KiB
C++
1873 lines
60 KiB
C++
|
/* ========================================================================
|
||
|
$File: $
|
||
|
$Date: $
|
||
|
$Revision: $
|
||
|
$Creator: Casey Muratori $
|
||
|
$Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $
|
||
|
======================================================================== */
|
||
|
|
||
|
// TODO(casey): Stop using stdio!
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include "handmade_debug.h"
|
||
|
|
||
|
internal void RestartCollation(debug_state *DebugState, u32 InvalidEventArrayIndex);
|
||
|
|
||
|
inline debug_id
|
||
|
DebugIDFromLink(debug_tree *Tree, debug_variable_link *Link)
|
||
|
{
|
||
|
debug_id Result = {};
|
||
|
|
||
|
Result.Value[0] = Tree;
|
||
|
Result.Value[1] = Link;
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
inline debug_state *
|
||
|
DEBUGGetState(game_memory *Memory)
|
||
|
{
|
||
|
debug_state *DebugState = 0;
|
||
|
if(Memory)
|
||
|
{
|
||
|
DebugState = (debug_state *)Memory->DebugStorage;
|
||
|
if(!DebugState->Initialized)
|
||
|
{
|
||
|
DebugState = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(DebugState);
|
||
|
}
|
||
|
|
||
|
inline debug_state *
|
||
|
DEBUGGetState(void)
|
||
|
{
|
||
|
debug_state *Result = DEBUGGetState(DebugGlobalMemory);
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal debug_tree *
|
||
|
AddTree(debug_state *DebugState, debug_variable_group *Group, v2 AtP)
|
||
|
{
|
||
|
debug_tree *Tree = PushStruct(&DebugState->DebugArena, debug_tree);
|
||
|
|
||
|
Tree->UIP = AtP;
|
||
|
Tree->Group = Group;
|
||
|
|
||
|
DLIST_INSERT(&DebugState->TreeSentinel, Tree);
|
||
|
|
||
|
return(Tree);
|
||
|
}
|
||
|
|
||
|
inline b32
|
||
|
IsHex(char Char)
|
||
|
{
|
||
|
b32 Result = (((Char >= '0') && (Char <= '9')) ||
|
||
|
((Char >= 'A') && (Char <= 'F')));
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
inline u32
|
||
|
GetHex(char Char)
|
||
|
{
|
||
|
u32 Result = 0;
|
||
|
|
||
|
if((Char >= '0') && (Char <= '9'))
|
||
|
{
|
||
|
Result = Char - '0';
|
||
|
}
|
||
|
else if((Char >= 'A') && (Char <= 'F'))
|
||
|
{
|
||
|
Result = 0xA + (Char - 'A');
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal rectangle2
|
||
|
DEBUGTextOp(debug_state *DebugState, debug_text_op Op, v2 P, char *String, v4 Color = V4(1, 1, 1, 1))
|
||
|
{
|
||
|
rectangle2 Result = InvertedInfinityRectangle2();
|
||
|
if(DebugState && DebugState->DebugFont)
|
||
|
{
|
||
|
render_group *RenderGroup = DebugState->RenderGroup;
|
||
|
loaded_font *Font = DebugState->DebugFont;
|
||
|
hha_font *Info = DebugState->DebugFontInfo;
|
||
|
|
||
|
u32 PrevCodePoint = 0;
|
||
|
r32 CharScale = DebugState->FontScale;
|
||
|
r32 AtY = P.y;
|
||
|
r32 AtX = P.x;
|
||
|
for(char *At = String;
|
||
|
*At;
|
||
|
)
|
||
|
{
|
||
|
if((At[0] == '\\') &&
|
||
|
(At[1] == '#') &&
|
||
|
(At[2] != 0) &&
|
||
|
(At[3] != 0) &&
|
||
|
(At[4] != 0))
|
||
|
{
|
||
|
r32 CScale = 1.0f / 9.0f;
|
||
|
Color = V4(Clamp01(CScale*(r32)(At[2] - '0')),
|
||
|
Clamp01(CScale*(r32)(At[3] - '0')),
|
||
|
Clamp01(CScale*(r32)(At[4] - '0')),
|
||
|
1.0f);
|
||
|
At += 5;
|
||
|
}
|
||
|
else if((At[0] == '\\') &&
|
||
|
(At[1] == '^') &&
|
||
|
(At[2] != 0))
|
||
|
{
|
||
|
r32 CScale = 1.0f / 9.0f;
|
||
|
CharScale = DebugState->FontScale*Clamp01(CScale*(r32)(At[2] - '0'));
|
||
|
At += 3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
u32 CodePoint = *At;
|
||
|
if((At[0] == '\\') &&
|
||
|
(IsHex(At[1])) &&
|
||
|
(IsHex(At[2])) &&
|
||
|
(IsHex(At[3])) &&
|
||
|
(IsHex(At[4])))
|
||
|
{
|
||
|
CodePoint = ((GetHex(At[1]) << 12) |
|
||
|
(GetHex(At[2]) << 8) |
|
||
|
(GetHex(At[3]) << 4) |
|
||
|
(GetHex(At[4]) << 0));
|
||
|
At += 4;
|
||
|
}
|
||
|
|
||
|
r32 AdvanceX = CharScale*GetHorizontalAdvanceForPair(Info, Font, PrevCodePoint, CodePoint);
|
||
|
AtX += AdvanceX;
|
||
|
|
||
|
if(CodePoint != ' ')
|
||
|
{
|
||
|
bitmap_id BitmapID = GetBitmapForGlyph(RenderGroup->Assets, Info, Font, CodePoint);
|
||
|
hha_bitmap *Info = GetBitmapInfo(RenderGroup->Assets, BitmapID);
|
||
|
|
||
|
r32 BitmapScale = CharScale*(r32)Info->Dim[1];
|
||
|
v3 BitmapOffset = V3(AtX, AtY, 0);
|
||
|
if(Op == DEBUGTextOp_DrawText)
|
||
|
{
|
||
|
PushBitmap(RenderGroup, BitmapID, BitmapScale, BitmapOffset, Color);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert(Op == DEBUGTextOp_SizeText);
|
||
|
|
||
|
loaded_bitmap *Bitmap = GetBitmap(RenderGroup->Assets, BitmapID, RenderGroup->GenerationID);
|
||
|
if(Bitmap)
|
||
|
{
|
||
|
used_bitmap_dim Dim = GetBitmapDim(RenderGroup, Bitmap, BitmapScale, BitmapOffset, 1.0f);
|
||
|
rectangle2 GlyphDim = RectMinDim(Dim.P.xy, Dim.Size);
|
||
|
Result = Union(Result, GlyphDim);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PrevCodePoint = CodePoint;
|
||
|
|
||
|
++At;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGTextOutAt(v2 P, char *String, v4 Color = V4(1, 1, 1, 1))
|
||
|
{
|
||
|
debug_state *DebugState = DEBUGGetState();
|
||
|
if(DebugState)
|
||
|
{
|
||
|
render_group *RenderGroup = DebugState->RenderGroup;
|
||
|
DEBUGTextOp(DebugState, DEBUGTextOp_DrawText, P, String, Color);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal rectangle2
|
||
|
DEBUGGetTextSize(debug_state *DebugState, char *String)
|
||
|
{
|
||
|
rectangle2 Result = DEBUGTextOp(DebugState, DEBUGTextOp_SizeText, V2(0, 0), String);
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
|
||
|
internal void
|
||
|
DEBUGTextLine(char *String)
|
||
|
{
|
||
|
debug_state *DebugState = DEBUGGetState();
|
||
|
if(DebugState)
|
||
|
{
|
||
|
render_group *RenderGroup = DebugState->RenderGroup;
|
||
|
|
||
|
loaded_font *Font = PushFont(RenderGroup, DebugState->FontID);
|
||
|
if(Font)
|
||
|
{
|
||
|
hha_font *Info = GetFontInfo(RenderGroup->Assets, DebugState->FontID);
|
||
|
|
||
|
DEBUGTextOutAt(V2(DebugState->LeftEdge,
|
||
|
DebugState->AtY - DebugState->FontScale*GetStartingBaselineY(DebugState->DebugFontInfo)), String);
|
||
|
|
||
|
DebugState->AtY -= GetLineAdvanceFor(Info)*DebugState->FontScale;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct debug_statistic
|
||
|
{
|
||
|
r64 Min;
|
||
|
r64 Max;
|
||
|
r64 Avg;
|
||
|
u32 Count;
|
||
|
};
|
||
|
inline void
|
||
|
BeginDebugStatistic(debug_statistic *Stat)
|
||
|
{
|
||
|
Stat->Min = Real32Maximum;
|
||
|
Stat->Max = -Real32Maximum;
|
||
|
Stat->Avg = 0.0f;
|
||
|
Stat->Count = 0;
|
||
|
}
|
||
|
|
||
|
inline void
|
||
|
AccumDebugStatistic(debug_statistic *Stat, r64 Value)
|
||
|
{
|
||
|
++Stat->Count;
|
||
|
|
||
|
if(Stat->Min > Value)
|
||
|
{
|
||
|
Stat->Min = Value;
|
||
|
}
|
||
|
|
||
|
if(Stat->Max < Value)
|
||
|
{
|
||
|
Stat->Max = Value;
|
||
|
}
|
||
|
|
||
|
Stat->Avg += Value;
|
||
|
}
|
||
|
|
||
|
inline void
|
||
|
EndDebugStatistic(debug_statistic *Stat)
|
||
|
{
|
||
|
if(Stat->Count)
|
||
|
{
|
||
|
Stat->Avg /= (r64)Stat->Count;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Stat->Min = 0.0f;
|
||
|
Stat->Max = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal memory_index
|
||
|
DEBUGEventToText(char *Buffer, char *End, debug_event *Event, u32 Flags)
|
||
|
{
|
||
|
char *At = Buffer;
|
||
|
char *Name = Event->BlockName;
|
||
|
|
||
|
if(Flags & DEBUGVarToText_AddDebugUI)
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"#define DEBUGUI_");
|
||
|
}
|
||
|
|
||
|
if(Flags & DEBUGVarToText_AddName)
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"%s%s ", Name, (Flags & DEBUGVarToText_Colon) ? ":" : "");
|
||
|
}
|
||
|
|
||
|
switch(Event->Type)
|
||
|
{
|
||
|
case DebugType_r32:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"%f", Event->Value_r32);
|
||
|
if(Flags & DEBUGVarToText_FloatSuffix)
|
||
|
{
|
||
|
*At++ = 'f';
|
||
|
}
|
||
|
} break;
|
||
|
|
||
|
case DebugType_b32:
|
||
|
{
|
||
|
if(Flags & DEBUGVarToText_PrettyBools)
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"%s",
|
||
|
Event->Value_b32 ? "true" : "false");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"%d", Event->Value_b32);
|
||
|
}
|
||
|
} break;
|
||
|
|
||
|
case DebugType_s32:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"%d", Event->Value_s32);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_u32:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"%u", Event->Value_u32);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_v2:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"V2(%f, %f)",
|
||
|
Event->Value_v2.x, Event->Value_v2.y);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_v3:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"V3(%f, %f, %f)",
|
||
|
Event->Value_v3.x, Event->Value_v3.y, Event->Value_v3.z);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_v4:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"V4(%f, %f, %f, %f)",
|
||
|
Event->Value_v4.x, Event->Value_v4.y,
|
||
|
Event->Value_v4.z, Event->Value_v4.w);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_rectangle2:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"Rect2(%f, %f -> %f, %f)",
|
||
|
Event->Value_rectangle2.Min.x,
|
||
|
Event->Value_rectangle2.Min.y,
|
||
|
Event->Value_rectangle2.Max.x,
|
||
|
Event->Value_rectangle2.Max.y);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_rectangle3:
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"Rect2(%f, %f, %f -> %f, %f, %f)",
|
||
|
Event->Value_rectangle3.Min.x,
|
||
|
Event->Value_rectangle3.Min.y,
|
||
|
Event->Value_rectangle3.Min.z,
|
||
|
Event->Value_rectangle3.Max.x,
|
||
|
Event->Value_rectangle3.Max.y,
|
||
|
Event->Value_rectangle3.Max.z);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_CounterThreadList:
|
||
|
case DebugType_bitmap_id:
|
||
|
case DebugType_OpenDataBlock:
|
||
|
{
|
||
|
} break;
|
||
|
|
||
|
InvalidDefaultCase;
|
||
|
}
|
||
|
|
||
|
if(Flags & DEBUGVarToText_LineFeedEnd)
|
||
|
{
|
||
|
*At++ = '\n';
|
||
|
}
|
||
|
|
||
|
if(Flags & DEBUGVarToText_NullTerminator)
|
||
|
{
|
||
|
*At++ = 0;
|
||
|
}
|
||
|
|
||
|
return(At - Buffer);
|
||
|
}
|
||
|
|
||
|
struct debug_variable_iterator
|
||
|
{
|
||
|
debug_variable_link *Link;
|
||
|
debug_variable_link *Sentinel;
|
||
|
};
|
||
|
|
||
|
internal void
|
||
|
WriteHandmadeConfig(debug_state *DebugState)
|
||
|
{
|
||
|
#if 0
|
||
|
// TODO(casey): Need a giant buffer here!
|
||
|
char Temp[4096];
|
||
|
char *At = Temp;
|
||
|
char *End = Temp + sizeof(Temp);
|
||
|
|
||
|
int Depth = 0;
|
||
|
debug_variable_iterator Stack[DEBUG_MAX_VARIABLE_STACK_DEPTH];
|
||
|
|
||
|
Stack[Depth].Link = DebugState->RootGroup->VarGroup.Next;
|
||
|
Stack[Depth].Sentinel = &DebugState->RootGroup->VarGroup;
|
||
|
++Depth;
|
||
|
while(Depth > 0)
|
||
|
{
|
||
|
debug_variable_iterator *Iter = Stack + (Depth - 1);
|
||
|
if(Iter->Link == Iter->Sentinel)
|
||
|
{
|
||
|
--Depth;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
debug_variable *Var = Iter->Link->Var;
|
||
|
Iter->Link = Iter->Link->Next;
|
||
|
|
||
|
if(DEBUGShouldBeWritten(Var->Type))
|
||
|
{
|
||
|
// TODO(casey): Other variable types!
|
||
|
for(int Indent = 0;
|
||
|
Indent < Depth;
|
||
|
++Indent)
|
||
|
{
|
||
|
*At++ = ' ';
|
||
|
*At++ = ' ';
|
||
|
*At++ = ' ';
|
||
|
*At++ = ' ';
|
||
|
}
|
||
|
|
||
|
if(Var->Type == DebugVariableType_VarGroup)
|
||
|
{
|
||
|
At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At),
|
||
|
"// ");
|
||
|
}
|
||
|
At += DEBUGVariableToText(At, End, Var,
|
||
|
DEBUGVarToText_AddDebugUI|
|
||
|
DEBUGVarToText_AddName|
|
||
|
DEBUGVarToText_LineFeedEnd|
|
||
|
DEBUGVarToText_FloatSuffix);
|
||
|
}
|
||
|
|
||
|
if(Var->Type == DebugVariableType_VarGroup)
|
||
|
{
|
||
|
Iter = Stack + Depth;
|
||
|
Iter->Link = Var->VarGroup.Next;
|
||
|
Iter->Sentinel = &Var->VarGroup;
|
||
|
++Depth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
Platform.DEBUGWriteEntireFile("../code/handmade_config.h", (u32)(At - Temp), Temp);
|
||
|
|
||
|
if(!DebugState->Compiling)
|
||
|
{
|
||
|
DebugState->Compiling = true;
|
||
|
DebugState->Compiler = Platform.DEBUGExecuteSystemCommand("..\\code", "c:\\windows\\system32\\cmd.exe", "/C build.bat");
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DrawProfileIn(debug_state *DebugState, rectangle2 ProfileRect, v2 MouseP)
|
||
|
{
|
||
|
PushRect(DebugState->RenderGroup, ProfileRect, 0.0f, V4(0, 0, 0, 0.25f));
|
||
|
|
||
|
r32 BarSpacing = 4.0f;
|
||
|
r32 LaneHeight = 0.0f;
|
||
|
u32 LaneCount = DebugState->FrameBarLaneCount;
|
||
|
r32 FrameBarScale = FLT_MAX;
|
||
|
for(debug_frame *Frame = DebugState->OldestFrame;
|
||
|
Frame;
|
||
|
Frame = Frame->Next)
|
||
|
{
|
||
|
if(FrameBarScale < Frame->FrameBarScale)
|
||
|
{
|
||
|
FrameBarScale = Frame->FrameBarScale;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
u32 MaxFrame = DebugState->FrameCount;
|
||
|
if(MaxFrame > 10)
|
||
|
{
|
||
|
MaxFrame = 10;
|
||
|
}
|
||
|
|
||
|
if((LaneCount > 0) && (MaxFrame > 0))
|
||
|
{
|
||
|
r32 PixelsPerFramePlusSpacing = GetDim(ProfileRect).y / (r32)MaxFrame;
|
||
|
r32 PixelsPerFrame = PixelsPerFramePlusSpacing - BarSpacing;
|
||
|
LaneHeight = PixelsPerFrame / (r32)LaneCount;
|
||
|
}
|
||
|
|
||
|
r32 BarHeight = LaneHeight*LaneCount;
|
||
|
r32 BarsPlusSpacing = BarHeight + BarSpacing;
|
||
|
r32 ChartLeft = ProfileRect.Min.x;
|
||
|
r32 ChartHeight = BarsPlusSpacing*(r32)MaxFrame;
|
||
|
r32 ChartWidth = GetDim(ProfileRect).x;
|
||
|
r32 ChartTop = ProfileRect.Max.y;
|
||
|
r32 Scale = ChartWidth*FrameBarScale;
|
||
|
|
||
|
v3 Colors[] =
|
||
|
{
|
||
|
{1, 0, 0},
|
||
|
{0, 1, 0},
|
||
|
{0, 0, 1},
|
||
|
{1, 1, 0},
|
||
|
{0, 1, 1},
|
||
|
{1, 0, 1},
|
||
|
{1, 0.5f, 0},
|
||
|
{1, 0, 0.5f},
|
||
|
{0.5f, 1, 0},
|
||
|
{0, 1, 0.5f},
|
||
|
{0.5f, 0, 1},
|
||
|
{0, 0.5f, 1},
|
||
|
};
|
||
|
|
||
|
u32 FrameIndex = 0;
|
||
|
for(debug_frame *Frame = DebugState->OldestFrame;
|
||
|
Frame;
|
||
|
Frame = Frame->Next, ++FrameIndex)
|
||
|
{
|
||
|
r32 StackX = ChartLeft;
|
||
|
r32 StackY = ChartTop - BarsPlusSpacing*(r32)FrameIndex;
|
||
|
for(u32 RegionIndex = 0;
|
||
|
RegionIndex < Frame->RegionCount;
|
||
|
++RegionIndex)
|
||
|
{
|
||
|
debug_frame_region *Region = Frame->Regions + RegionIndex;
|
||
|
|
||
|
// v3 Color = Colors[RegionIndex%ArrayCount(Colors)];
|
||
|
v3 Color = Colors[Region->ColorIndex%ArrayCount(Colors)];
|
||
|
r32 ThisMinX = StackX + Scale*Region->MinT;
|
||
|
r32 ThisMaxX = StackX + Scale*Region->MaxT;
|
||
|
|
||
|
rectangle2 RegionRect = RectMinMax(
|
||
|
V2(ThisMinX, StackY - LaneHeight*(Region->LaneIndex + 1)),
|
||
|
V2(ThisMaxX, StackY - LaneHeight*Region->LaneIndex));
|
||
|
|
||
|
PushRect(DebugState->RenderGroup, RegionRect, 0.0f, V4(Color, 1));
|
||
|
|
||
|
if(IsInRectangle(RegionRect, MouseP))
|
||
|
{
|
||
|
debug_event *Record = Region->Event;
|
||
|
char TextBuffer[256];
|
||
|
_snprintf_s(TextBuffer, sizeof(TextBuffer),
|
||
|
"%s: %10ucy [%s(%d)]",
|
||
|
Record->BlockName,
|
||
|
Region->CycleCount,
|
||
|
Record->FileName,
|
||
|
Record->LineNumber);
|
||
|
DEBUGTextOutAt(MouseP + V2(0.0f, 10.0f), TextBuffer);
|
||
|
|
||
|
// HotRecord = Record;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#if 0
|
||
|
PushRect(RenderGroup, V3(ChartLeft + 0.5f*ChartWidth, ChartMinY + ChartHeight, 0.0f),
|
||
|
V2(ChartWidth, 1.0f), V4(1, 1, 1, 1));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline b32
|
||
|
InteractionsAreEqual(debug_interaction A, debug_interaction B)
|
||
|
{
|
||
|
b32 Result = ((A.Type == B.Type) &&
|
||
|
(A.Generic == B.Generic));
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
inline b32
|
||
|
InteractionIsHot(debug_state *DebugState, debug_interaction B)
|
||
|
{
|
||
|
b32 Result = InteractionsAreEqual(DebugState->HotInteraction, B);
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
struct layout
|
||
|
{
|
||
|
debug_state *DebugState;
|
||
|
v2 MouseP;
|
||
|
v2 At;
|
||
|
int Depth;
|
||
|
real32 LineAdvance;
|
||
|
r32 SpacingY;
|
||
|
};
|
||
|
|
||
|
inline rectangle2
|
||
|
PlaceRectangle(layout *Layout, v2 Dim)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
struct layout_element
|
||
|
{
|
||
|
// NOTE(casey): Storage;
|
||
|
layout *Layout;
|
||
|
v2 *Dim;
|
||
|
v2 *Size;
|
||
|
debug_interaction Interaction;
|
||
|
|
||
|
// NOTE(casey): Out
|
||
|
rectangle2 Bounds;
|
||
|
};
|
||
|
|
||
|
inline layout_element
|
||
|
BeginElementRectangle(layout *Layout, v2 *Dim)
|
||
|
{
|
||
|
layout_element Element = {};
|
||
|
|
||
|
Element.Layout = Layout;
|
||
|
Element.Dim = Dim;
|
||
|
|
||
|
return(Element);
|
||
|
}
|
||
|
|
||
|
inline void
|
||
|
MakeElementSizable(layout_element *Element)
|
||
|
{
|
||
|
Element->Size = Element->Dim;
|
||
|
}
|
||
|
|
||
|
inline void
|
||
|
DefaultInteraction(layout_element *Element, debug_interaction Interaction)
|
||
|
{
|
||
|
Element->Interaction = Interaction;
|
||
|
}
|
||
|
|
||
|
inline void
|
||
|
EndElement(layout_element *Element)
|
||
|
{
|
||
|
layout *Layout = Element->Layout;
|
||
|
debug_state *DebugState = Layout->DebugState;
|
||
|
|
||
|
r32 SizeHandlePixels = 4.0f;
|
||
|
|
||
|
v2 Frame = {0, 0};
|
||
|
if(Element->Size)
|
||
|
{
|
||
|
Frame.x = SizeHandlePixels;
|
||
|
Frame.y = SizeHandlePixels;
|
||
|
}
|
||
|
|
||
|
v2 TotalDim = *Element->Dim + 2.0f*Frame;
|
||
|
|
||
|
v2 TotalMinCorner = V2(Layout->At.x + Layout->Depth*2.0f*Layout->LineAdvance,
|
||
|
Layout->At.y - TotalDim.y);
|
||
|
v2 TotalMaxCorner = TotalMinCorner + TotalDim;
|
||
|
|
||
|
v2 InteriorMinCorner = TotalMinCorner + Frame;
|
||
|
v2 InteriorMaxCorner = InteriorMinCorner + *Element->Dim;
|
||
|
|
||
|
rectangle2 TotalBounds = RectMinMax(TotalMinCorner, TotalMaxCorner);
|
||
|
Element->Bounds = RectMinMax(InteriorMinCorner, InteriorMaxCorner);
|
||
|
|
||
|
if(Element->Interaction.Type && IsInRectangle(Element->Bounds, Layout->MouseP))
|
||
|
{
|
||
|
DebugState->NextHotInteraction = Element->Interaction;
|
||
|
}
|
||
|
|
||
|
if(Element->Size)
|
||
|
{
|
||
|
PushRect(DebugState->RenderGroup, RectMinMax(V2(TotalMinCorner.x, InteriorMinCorner.y),
|
||
|
V2(InteriorMinCorner.x, InteriorMaxCorner.y)), 0.0f,
|
||
|
V4(0, 0, 0, 1));
|
||
|
PushRect(DebugState->RenderGroup, RectMinMax(V2(InteriorMaxCorner.x, InteriorMinCorner.y),
|
||
|
V2(TotalMaxCorner.x, InteriorMaxCorner.y)), 0.0f,
|
||
|
V4(0, 0, 0, 1));
|
||
|
PushRect(DebugState->RenderGroup, RectMinMax(V2(InteriorMinCorner.x, TotalMinCorner.y),
|
||
|
V2(InteriorMaxCorner.x, InteriorMinCorner.y)), 0.0f,
|
||
|
V4(0, 0, 0, 1));
|
||
|
PushRect(DebugState->RenderGroup, RectMinMax(V2(InteriorMinCorner.x, InteriorMaxCorner.y),
|
||
|
V2(InteriorMaxCorner.x, TotalMaxCorner.y)), 0.0f,
|
||
|
V4(0, 0, 0, 1));
|
||
|
|
||
|
debug_interaction SizeInteraction = {};
|
||
|
SizeInteraction.Type = DebugInteraction_Resize;
|
||
|
SizeInteraction.P = Element->Size;
|
||
|
|
||
|
rectangle2 SizeBox = RectMinMax(V2(InteriorMaxCorner.x, TotalMinCorner.y),
|
||
|
V2(TotalMaxCorner.x, InteriorMinCorner.y));
|
||
|
PushRect(DebugState->RenderGroup, SizeBox, 0.0f,
|
||
|
(InteractionIsHot(DebugState, SizeInteraction) ? V4(1, 1, 0, 1) : V4(1, 1, 1, 1)));
|
||
|
if(IsInRectangle(SizeBox, Layout->MouseP))
|
||
|
{
|
||
|
DebugState->NextHotInteraction = SizeInteraction;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
r32 SpacingY = Layout->SpacingY;
|
||
|
if(0)
|
||
|
{
|
||
|
SpacingY = 0.0f;
|
||
|
}
|
||
|
Layout->At.y = GetMinCorner(TotalBounds).y - SpacingY;
|
||
|
}
|
||
|
|
||
|
inline b32
|
||
|
DebugIDsAreEqual(debug_id A, debug_id B)
|
||
|
{
|
||
|
b32 Result = ((A.Value[0] == B.Value[0]) &&
|
||
|
(A.Value[1] == B.Value[1]));
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal debug_view *
|
||
|
GetOrCreateDebugViewFor(debug_state *DebugState, debug_id ID)
|
||
|
{
|
||
|
// TODO(casey): Better hash function
|
||
|
u32 HashIndex = (((u32)ID.Value[0] >> 2) + ((u32)ID.Value[1] >> 2)) % ArrayCount(DebugState->ViewHash);
|
||
|
debug_view **HashSlot = DebugState->ViewHash + HashIndex;
|
||
|
|
||
|
debug_view *Result = 0;
|
||
|
for(debug_view *Search = *HashSlot;
|
||
|
Search;
|
||
|
Search = Search->NextInHash)
|
||
|
{
|
||
|
if(DebugIDsAreEqual(Search->ID, ID))
|
||
|
{
|
||
|
Result = Search;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!Result)
|
||
|
{
|
||
|
Result = PushStruct(&DebugState->DebugArena, debug_view);
|
||
|
Result->ID = ID;
|
||
|
Result->Type = DebugViewType_Unknown;
|
||
|
Result->NextInHash = *HashSlot;
|
||
|
*HashSlot = Result;
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
inline debug_interaction
|
||
|
VarLinkInteraction(debug_interaction_type Type, debug_tree *Tree, debug_variable_link *Link)
|
||
|
{
|
||
|
debug_interaction ItemInteraction = {};
|
||
|
ItemInteraction.ID = DebugIDFromLink(Tree, Link);
|
||
|
ItemInteraction.Type = Type;
|
||
|
ItemInteraction.Event = Link->Event;
|
||
|
|
||
|
return(ItemInteraction);
|
||
|
}
|
||
|
|
||
|
inline debug_interaction
|
||
|
DebugIDInteraction(debug_interaction_type Type, debug_id ID)
|
||
|
{
|
||
|
debug_interaction ItemInteraction = {};
|
||
|
ItemInteraction.ID = ID;
|
||
|
ItemInteraction.Type = Type;
|
||
|
|
||
|
return(ItemInteraction);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
IsSelected(debug_state *DebugState, debug_id ID)
|
||
|
{
|
||
|
b32 Result = false;
|
||
|
|
||
|
for(u32 Index = 0;
|
||
|
Index < DebugState->SelectedIDCount;
|
||
|
++Index)
|
||
|
{
|
||
|
if(DebugIDsAreEqual(ID, DebugState->SelectedID[Index]))
|
||
|
{
|
||
|
Result = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
ClearSelection(debug_state *DebugState)
|
||
|
{
|
||
|
DebugState->SelectedIDCount = 0;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
AddToSelection(debug_state *DebugState, debug_id ID)
|
||
|
{
|
||
|
if((DebugState->SelectedIDCount < ArrayCount(DebugState->SelectedID)) &&
|
||
|
!IsSelected(DebugState, ID))
|
||
|
{
|
||
|
DebugState->SelectedID[DebugState->SelectedIDCount++] = ID;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUG_HIT(debug_id ID, r32 ZValue)
|
||
|
{
|
||
|
debug_state *DebugState = DEBUGGetState();
|
||
|
if(DebugState)
|
||
|
{
|
||
|
DebugState->NextHotInteraction = DebugIDInteraction(DebugInteraction_Select, ID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
DEBUG_HIGHLIGHTED(debug_id ID, v4 *Color)
|
||
|
{
|
||
|
b32 Result = false;
|
||
|
|
||
|
debug_state *DebugState = DEBUGGetState();
|
||
|
if(DebugState)
|
||
|
{
|
||
|
if(IsSelected(DebugState, ID))
|
||
|
{
|
||
|
*Color = V4(0, 1, 1, 1);
|
||
|
Result = true;
|
||
|
}
|
||
|
|
||
|
if(DebugIDsAreEqual(DebugState->HotInteraction.ID, ID))
|
||
|
{
|
||
|
*Color = V4(1, 1, 0, 1);
|
||
|
Result = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
DEBUG_REQUESTED(debug_id ID)
|
||
|
{
|
||
|
b32 Result = false;
|
||
|
|
||
|
debug_state *DebugState = DEBUGGetState();
|
||
|
if(DebugState)
|
||
|
{
|
||
|
Result = IsSelected(DebugState, ID)
|
||
|
|| DebugIDsAreEqual(DebugState->HotInteraction.ID, ID);
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGDrawMainMenu(debug_state *DebugState, render_group *RenderGroup, v2 MouseP)
|
||
|
{
|
||
|
for(debug_tree *Tree = DebugState->TreeSentinel.Next;
|
||
|
Tree != &DebugState->TreeSentinel;
|
||
|
Tree = Tree->Next)
|
||
|
{
|
||
|
layout Layout = {};
|
||
|
Layout.DebugState = DebugState;
|
||
|
Layout.MouseP = MouseP;
|
||
|
Layout.At = Tree->UIP;
|
||
|
Layout.LineAdvance = DebugState->FontScale*GetLineAdvanceFor(DebugState->DebugFontInfo);
|
||
|
Layout.SpacingY = 4.0f;
|
||
|
|
||
|
int Depth = 0;
|
||
|
debug_variable_iterator Stack[DEBUG_MAX_VARIABLE_STACK_DEPTH];
|
||
|
|
||
|
debug_variable_group *Group = Tree->Group;
|
||
|
if(DebugState->FrameCount > 0)
|
||
|
{
|
||
|
// debug_variable_group *HackyGroup = DebugState->Frames[0].RootGroup;
|
||
|
debug_variable_group *HackyGroup = DebugState->ValuesGroup;
|
||
|
if(HackyGroup)
|
||
|
{
|
||
|
Group = HackyGroup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(Group)
|
||
|
{
|
||
|
Stack[Depth].Link = Group->Sentinel.Next;
|
||
|
Stack[Depth].Sentinel = &Group->Sentinel;
|
||
|
++Depth;
|
||
|
while(Depth > 0)
|
||
|
{
|
||
|
debug_variable_iterator *Iter = Stack + (Depth - 1);
|
||
|
if(Iter->Link == Iter->Sentinel)
|
||
|
{
|
||
|
--Depth;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Layout.Depth = Depth;
|
||
|
|
||
|
debug_variable_link *Link = Iter->Link;
|
||
|
debug_event *Event = Iter->Link->Event;
|
||
|
Iter->Link = Iter->Link->Next;
|
||
|
|
||
|
debug_interaction ItemInteraction =
|
||
|
VarLinkInteraction(DebugInteraction_AutoModifyVariable, Tree, Link);
|
||
|
|
||
|
b32 IsHot = InteractionIsHot(DebugState, ItemInteraction);
|
||
|
v4 ItemColor = IsHot ? V4(1, 1, 0, 1) : V4(1, 1, 1, 1);
|
||
|
|
||
|
debug_view *View = GetOrCreateDebugViewFor(DebugState, DebugIDFromLink(Tree, Link));
|
||
|
switch(Event->Type)
|
||
|
{
|
||
|
case DebugType_CounterThreadList:
|
||
|
{
|
||
|
layout_element Element = BeginElementRectangle(
|
||
|
&Layout, &View->InlineBlock.Dim);
|
||
|
MakeElementSizable(&Element);
|
||
|
DefaultInteraction(&Element, ItemInteraction);
|
||
|
EndElement(&Element);
|
||
|
|
||
|
DrawProfileIn(DebugState, Element.Bounds, MouseP);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_bitmap_id:
|
||
|
{
|
||
|
loaded_bitmap *Bitmap = GetBitmap(RenderGroup->Assets, Event->Value_bitmap_id, RenderGroup->GenerationID);
|
||
|
r32 BitmapScale = View->InlineBlock.Dim.y;
|
||
|
if(Bitmap)
|
||
|
{
|
||
|
used_bitmap_dim Dim = GetBitmapDim(RenderGroup, Bitmap, BitmapScale, V3(0.0f, 0.0f, 0.0f), 1.0f);
|
||
|
View->InlineBlock.Dim.x = Dim.Size.x;
|
||
|
}
|
||
|
|
||
|
debug_interaction TearInteraction = VarLinkInteraction(DebugInteraction_TearValue, Tree, Link);
|
||
|
|
||
|
layout_element Element = BeginElementRectangle(&Layout, &View->InlineBlock.Dim);
|
||
|
MakeElementSizable(&Element);
|
||
|
DefaultInteraction(&Element, TearInteraction);
|
||
|
EndElement(&Element);
|
||
|
|
||
|
PushRect(DebugState->RenderGroup, Element.Bounds, 0.0f, V4(0, 0, 0, 1.0f));
|
||
|
PushBitmap(DebugState->RenderGroup, Event->Value_bitmap_id, BitmapScale,
|
||
|
V3(GetMinCorner(Element.Bounds), 0.0f), V4(1, 1, 1, 1), 0.0f);
|
||
|
} break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
char Text[256];
|
||
|
DEBUGEventToText(Text, Text + sizeof(Text), Event,
|
||
|
DEBUGVarToText_AddName|
|
||
|
DEBUGVarToText_NullTerminator|
|
||
|
DEBUGVarToText_Colon|
|
||
|
DEBUGVarToText_PrettyBools);
|
||
|
|
||
|
rectangle2 TextBounds = DEBUGGetTextSize(DebugState, Text);
|
||
|
v2 Dim = {GetDim(TextBounds).x, Layout.LineAdvance};
|
||
|
|
||
|
layout_element Element = BeginElementRectangle(&Layout, &Dim);
|
||
|
DefaultInteraction(&Element, ItemInteraction);
|
||
|
EndElement(&Element);
|
||
|
|
||
|
DEBUGTextOutAt(V2(GetMinCorner(Element.Bounds).x,
|
||
|
GetMaxCorner(Element.Bounds).y - DebugState->FontScale*GetStartingBaselineY(DebugState->DebugFontInfo)),
|
||
|
Text, ItemColor);
|
||
|
} break;
|
||
|
}
|
||
|
|
||
|
if(Link->Children
|
||
|
// && View->Collapsible.ExpandedAlways
|
||
|
)
|
||
|
{
|
||
|
Iter = Stack + Depth;
|
||
|
Iter->Link = Link->Children->Sentinel.Next;
|
||
|
Iter->Sentinel = &Link->Children->Sentinel;
|
||
|
++Depth;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugState->AtY = Layout.At.y;
|
||
|
|
||
|
if(1)
|
||
|
{
|
||
|
debug_interaction MoveInteraction = {};
|
||
|
MoveInteraction.Type = DebugInteraction_Move;
|
||
|
MoveInteraction.P = &Tree->UIP;
|
||
|
|
||
|
rectangle2 MoveBox = RectCenterHalfDim(Tree->UIP - V2(4.0f, 4.0f), V2(4.0f, 4.0f));
|
||
|
PushRect(DebugState->RenderGroup, MoveBox, 0.0f,
|
||
|
InteractionIsHot(DebugState, MoveInteraction) ? V4(1, 1, 0, 1) : V4(1, 1, 1, 1));
|
||
|
|
||
|
if(IsInRectangle(MoveBox, MouseP))
|
||
|
{
|
||
|
DebugState->NextHotInteraction = MoveInteraction;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
u32 NewHotMenuIndex = ArrayCount(DebugVariableList);
|
||
|
r32 BestDistanceSq = Real32Maximum;
|
||
|
|
||
|
r32 MenuRadius = 400.0f;
|
||
|
r32 AngleStep = Tau32 / (r32)ArrayCount(DebugVariableList);
|
||
|
for(u32 MenuItemIndex = 0;
|
||
|
MenuItemIndex < ArrayCount(DebugVariableList);
|
||
|
++MenuItemIndex)
|
||
|
{
|
||
|
debug_variable *Var = DebugVariableList + MenuItemIndex;
|
||
|
char *Text = Var->Name;
|
||
|
|
||
|
v4 ItemColor = Var->Value ? V4(1, 1, 1, 1) : V4(0.5f, 0.5f, 0.5f, 1);
|
||
|
if(MenuItemIndex == DebugState->HotMenuIndex)
|
||
|
{
|
||
|
ItemColor = V4(1, 1, 0, 1);
|
||
|
}
|
||
|
|
||
|
r32 Angle = (r32)MenuItemIndex*AngleStep;
|
||
|
v2 TextP = DebugState->MenuP + MenuRadius*Arm2(Angle);
|
||
|
|
||
|
r32 ThisDistanceSq = LengthSq(TextP - MouseP);
|
||
|
if(BestDistanceSq > ThisDistanceSq)
|
||
|
{
|
||
|
NewHotMenuIndex = MenuItemIndex;
|
||
|
BestDistanceSq = ThisDistanceSq;
|
||
|
}
|
||
|
|
||
|
rectangle2 TextBounds = DEBUGGetTextSize(DebugState, Text);
|
||
|
DEBUGTextOutAt(TextP - 0.5f*GetDim(TextBounds), Text, ItemColor);
|
||
|
}
|
||
|
|
||
|
if(LengthSq(MouseP - DebugState->MenuP) > Square(MenuRadius))
|
||
|
{
|
||
|
DebugState->HotMenuIndex = NewHotMenuIndex;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugState->HotMenuIndex = ArrayCount(DebugVariableList);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGBeginInteract(debug_state *DebugState, game_input *Input, v2 MouseP, b32 AltUI)
|
||
|
{
|
||
|
if(DebugState->HotInteraction.Type)
|
||
|
{
|
||
|
if(DebugState->HotInteraction.Type == DebugInteraction_AutoModifyVariable)
|
||
|
{
|
||
|
switch(DebugState->HotInteraction.Event->Type)
|
||
|
{
|
||
|
case DebugType_b32:
|
||
|
{
|
||
|
DebugState->HotInteraction.Type = DebugInteraction_ToggleValue;
|
||
|
} break;
|
||
|
|
||
|
case DebugType_r32:
|
||
|
{
|
||
|
DebugState->HotInteraction.Type = DebugInteraction_DragValue;
|
||
|
} break;
|
||
|
|
||
|
case DebugType_OpenDataBlock:
|
||
|
{
|
||
|
DebugState->HotInteraction.Type = DebugInteraction_ToggleValue;
|
||
|
} break;
|
||
|
}
|
||
|
|
||
|
if(AltUI)
|
||
|
{
|
||
|
DebugState->HotInteraction.Type = DebugInteraction_TearValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(DebugState->HotInteraction.Type)
|
||
|
{
|
||
|
case DebugInteraction_TearValue:
|
||
|
{
|
||
|
#if 0
|
||
|
debug_variable *RootGroup = DEBUGAddRootGroup(DebugState, "NewUserGroup");
|
||
|
DEBUGAddVariableToGroup(DebugState, RootGroup, DebugState->HotInteraction.Var);
|
||
|
debug_tree *Tree = AddTree(DebugState, RootGroup, V2(0, 0));
|
||
|
Tree->UIP = MouseP;
|
||
|
DebugState->HotInteraction.Type = DebugInteraction_Move;
|
||
|
DebugState->HotInteraction.P = &Tree->UIP;
|
||
|
#endif
|
||
|
} break;
|
||
|
|
||
|
case DebugInteraction_Select:
|
||
|
{
|
||
|
if(!Input->ShiftDown)
|
||
|
{
|
||
|
ClearSelection(DebugState);
|
||
|
}
|
||
|
AddToSelection(DebugState, DebugState->HotInteraction.ID);
|
||
|
} break;
|
||
|
}
|
||
|
|
||
|
DebugState->Interaction = DebugState->HotInteraction;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugState->Interaction.Type = DebugInteraction_NOP;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGEndInteract(debug_state *DebugState, game_input *Input, v2 MouseP)
|
||
|
{
|
||
|
switch(DebugState->Interaction.Type)
|
||
|
{
|
||
|
case DebugInteraction_ToggleValue:
|
||
|
{
|
||
|
debug_event *Event = DebugState->Interaction.Event;
|
||
|
Assert(Event);
|
||
|
switch(Event->Type)
|
||
|
{
|
||
|
case DebugType_b32:
|
||
|
{
|
||
|
Event->Value_b32 = !Event->Value_b32;
|
||
|
} break;
|
||
|
|
||
|
case DebugType_OpenDataBlock:
|
||
|
{
|
||
|
debug_view *View = GetOrCreateDebugViewFor(DebugState, DebugState->Interaction.ID);
|
||
|
View->Collapsible.ExpandedAlways = !View->Collapsible.ExpandedAlways;
|
||
|
} break;
|
||
|
}
|
||
|
} break;
|
||
|
}
|
||
|
|
||
|
WriteHandmadeConfig(DebugState);
|
||
|
|
||
|
DebugState->Interaction.Type = DebugInteraction_None;
|
||
|
DebugState->Interaction.Generic = 0;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGInteract(debug_state *DebugState, game_input *Input, v2 MouseP)
|
||
|
{
|
||
|
v2 dMouseP = MouseP - DebugState->LastMouseP;
|
||
|
|
||
|
/*
|
||
|
if(Input->MouseButtons[PlatformMouseButton_Right].EndedDown)
|
||
|
{
|
||
|
if(Input->MouseButtons[PlatformMouseButton_Right].HalfTransitionCount > 0)
|
||
|
{
|
||
|
DebugState->MenuP = MouseP;
|
||
|
}
|
||
|
DrawDebugMainMenu(DebugState, RenderGroup, MouseP);
|
||
|
}
|
||
|
else if(Input->MouseButtons[PlatformMouseButton_Right].HalfTransitionCount > 0)
|
||
|
*/
|
||
|
if(DebugState->Interaction.Type)
|
||
|
{
|
||
|
debug_event *Event = DebugState->Interaction.Event;
|
||
|
debug_tree *Tree = DebugState->Interaction.Tree;
|
||
|
v2 *P = DebugState->Interaction.P;
|
||
|
|
||
|
// NOTE(casey): Mouse move interaction
|
||
|
switch(DebugState->Interaction.Type)
|
||
|
{
|
||
|
case DebugInteraction_DragValue:
|
||
|
{
|
||
|
switch(Event->Type)
|
||
|
{
|
||
|
case DebugType_r32:
|
||
|
{
|
||
|
Event->Value_r32 += 0.1f*dMouseP.y;
|
||
|
} break;
|
||
|
}
|
||
|
} break;
|
||
|
|
||
|
case DebugInteraction_Resize:
|
||
|
{
|
||
|
*P += V2(dMouseP.x, -dMouseP.y);
|
||
|
P->x = Maximum(P->x, 10.0f);
|
||
|
P->y = Maximum(P->y, 10.0f);
|
||
|
} break;
|
||
|
|
||
|
case DebugInteraction_Move:
|
||
|
{
|
||
|
*P += V2(dMouseP.x, dMouseP.y);
|
||
|
} break;
|
||
|
}
|
||
|
b32 AltUI = Input->MouseButtons[PlatformMouseButton_Right].EndedDown;
|
||
|
|
||
|
// NOTE(casey): Click interaction
|
||
|
for(u32 TransitionIndex = Input->MouseButtons[PlatformMouseButton_Left].HalfTransitionCount;
|
||
|
TransitionIndex > 1;
|
||
|
--TransitionIndex)
|
||
|
{
|
||
|
DEBUGEndInteract(DebugState, Input, MouseP);
|
||
|
DEBUGBeginInteract(DebugState, Input, MouseP, AltUI);
|
||
|
}
|
||
|
|
||
|
if(!Input->MouseButtons[PlatformMouseButton_Left].EndedDown)
|
||
|
{
|
||
|
DEBUGEndInteract(DebugState, Input, MouseP);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugState->HotInteraction = DebugState->NextHotInteraction;
|
||
|
|
||
|
b32 AltUI = Input->MouseButtons[PlatformMouseButton_Right].EndedDown;
|
||
|
for(u32 TransitionIndex = Input->MouseButtons[PlatformMouseButton_Left].HalfTransitionCount;
|
||
|
TransitionIndex > 1;
|
||
|
--TransitionIndex)
|
||
|
{
|
||
|
DEBUGBeginInteract(DebugState, Input, MouseP, AltUI);
|
||
|
DEBUGEndInteract(DebugState, Input, MouseP);
|
||
|
}
|
||
|
|
||
|
if(Input->MouseButtons[PlatformMouseButton_Left].EndedDown)
|
||
|
{
|
||
|
DEBUGBeginInteract(DebugState, Input, MouseP, AltUI);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugState->LastMouseP = MouseP;
|
||
|
}
|
||
|
|
||
|
global_variable debug_table GlobalDebugTable_;
|
||
|
debug_table *GlobalDebugTable = &GlobalDebugTable_;
|
||
|
|
||
|
inline u32
|
||
|
GetLaneFromThreadIndex(debug_state *DebugState, u32 ThreadIndex)
|
||
|
{
|
||
|
u32 Result = 0;
|
||
|
|
||
|
// TODO(casey): Implement thread ID lookup.
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal debug_thread *
|
||
|
GetDebugThread(debug_state *DebugState, u32 ThreadID)
|
||
|
{
|
||
|
debug_thread *Result = 0;
|
||
|
for(debug_thread *Thread = DebugState->FirstThread;
|
||
|
Thread;
|
||
|
Thread = Thread->Next)
|
||
|
{
|
||
|
if(Thread->ID == ThreadID)
|
||
|
{
|
||
|
Result = Thread;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!Result)
|
||
|
{
|
||
|
FREELIST_ALLOCATE(debug_thread, Result, DebugState->FirstFreeThread, &DebugState->DebugArena);
|
||
|
|
||
|
Result->ID = ThreadID;
|
||
|
Result->LaneIndex = DebugState->FrameBarLaneCount++;
|
||
|
Result->FirstOpenCodeBlock = 0;
|
||
|
Result->FirstOpenDataBlock = 0;
|
||
|
Result->Next = DebugState->FirstThread;
|
||
|
DebugState->FirstThread = Result;
|
||
|
}
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
debug_frame_region *
|
||
|
AddRegion(debug_state *DebugState, debug_frame *CurrentFrame)
|
||
|
{
|
||
|
Assert(CurrentFrame->RegionCount < MAX_REGIONS_PER_FRAME);
|
||
|
debug_frame_region *Result = CurrentFrame->Regions + CurrentFrame->RegionCount++;
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
inline open_debug_block *
|
||
|
AllocateOpenDebugBlock(debug_state *DebugState, u32 FrameIndex, debug_event *Event,
|
||
|
open_debug_block **FirstOpenBlock)
|
||
|
{
|
||
|
open_debug_block *Result = 0;
|
||
|
FREELIST_ALLOCATE(open_debug_block, Result, DebugState->FirstFreeBlock, &DebugState->DebugArena);
|
||
|
|
||
|
Result->StartingFrameIndex = FrameIndex;
|
||
|
Result->OpeningEvent = Event;
|
||
|
Result->NextFree = 0;
|
||
|
|
||
|
Result->Parent = *FirstOpenBlock;
|
||
|
*FirstOpenBlock = Result;
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
inline void
|
||
|
DeallocateOpenDebugBlock(debug_state *DebugState, open_debug_block **FirstOpenBlock)
|
||
|
{
|
||
|
open_debug_block *FreeBlock = *FirstOpenBlock;
|
||
|
*FirstOpenBlock = FreeBlock->Parent;
|
||
|
|
||
|
FreeBlock->NextFree = DebugState->FirstFreeBlock;
|
||
|
DebugState->FirstFreeBlock = FreeBlock;
|
||
|
}
|
||
|
|
||
|
inline b32
|
||
|
EventsMatch(debug_event A, debug_event B)
|
||
|
{
|
||
|
// TODO(casey): Have counters for blocks?
|
||
|
b32 Result = (A.ThreadID == B.ThreadID);
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal debug_event *
|
||
|
CreateVariable(debug_state *State, debug_type Type, char *Name)
|
||
|
{
|
||
|
debug_event *Var = PushStruct(&State->DebugArena, debug_event);
|
||
|
ZeroStruct(*Var);
|
||
|
Var->Type = (u8)Type;
|
||
|
Var->BlockName = (char *)PushCopy(&State->DebugArena, StringLength(Name) + 1, Name);
|
||
|
|
||
|
return(Var);
|
||
|
}
|
||
|
|
||
|
internal debug_variable_link *
|
||
|
AddVariableToGroup(debug_state *DebugState, debug_variable_group *Group, debug_event *Add)
|
||
|
{
|
||
|
debug_variable_link *Link = PushStruct(&DebugState->DebugArena, debug_variable_link);
|
||
|
|
||
|
DLIST_INSERT(&Group->Sentinel, Link);
|
||
|
Link->Children = 0;
|
||
|
Link->Event = Add;
|
||
|
|
||
|
Assert(Link->Event->Type != DebugType_BeginBlock);
|
||
|
return(Link);
|
||
|
}
|
||
|
|
||
|
internal debug_variable_group *
|
||
|
CreateVariableGroup(debug_state *DebugState)
|
||
|
{
|
||
|
debug_variable_group *Group = PushStruct(&DebugState->DebugArena, debug_variable_group);
|
||
|
DLIST_INIT(&Group->Sentinel);
|
||
|
|
||
|
return(Group);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
FreeVariableGroup(debug_state *DebugState, debug_variable_group *Group)
|
||
|
{
|
||
|
// TODO(casey): Also remember to trigger freeing frames during arena pushes...
|
||
|
// TODO(casey): Remember to copy out the debug events into the debug variable links.
|
||
|
Assert(!"Not implemented");
|
||
|
}
|
||
|
|
||
|
internal debug_variable_group *
|
||
|
GetGroupForHierarchicalName(debug_state *DebugState, char *Name)
|
||
|
{
|
||
|
debug_variable_group *Result = DebugState->ValuesGroup;
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal debug_frame *
|
||
|
NewFrame(debug_state *DebugState, u64 BeginClock)
|
||
|
{
|
||
|
// TODO(casey): Simplify this once regions are more reasonable!
|
||
|
debug_frame *Result = DebugState->FirstFreeFrame;
|
||
|
if(Result)
|
||
|
{
|
||
|
DebugState->FirstFreeFrame = Result->NextFree;
|
||
|
debug_frame_region *Regions = Result->Regions;
|
||
|
ZeroStruct(*Result);
|
||
|
Result->Regions = Regions;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Result = PushStruct(&DebugState->DebugArena, debug_frame);
|
||
|
ZeroStruct(*Result);
|
||
|
Result->Regions = PushArray(&DebugState->DebugArena, MAX_REGIONS_PER_FRAME, debug_frame_region);
|
||
|
}
|
||
|
|
||
|
Result->FrameBarScale = 1.0f;
|
||
|
Result->RootGroup = CreateVariableGroup(DebugState);
|
||
|
|
||
|
Result->BeginClock = BeginClock;
|
||
|
|
||
|
return(Result);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
FreeFrame(debug_state *DebugState, debug_frame *Frame)
|
||
|
{
|
||
|
FreeVariableGroup(DebugState, Frame->RootGroup);
|
||
|
FREELIST_DEALLOCATE(Frame, DebugState->FirstFreeFrame);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
CollateDebugRecords(debug_state *DebugState, u32 EventCount, debug_event *EventArray)
|
||
|
{
|
||
|
for(u32 EventIndex = 0;
|
||
|
EventIndex < EventCount;
|
||
|
++EventIndex)
|
||
|
{
|
||
|
debug_event *Event = EventArray + EventIndex;
|
||
|
|
||
|
if(!DebugState->CollationFrame)
|
||
|
{
|
||
|
DebugState->CollationFrame = NewFrame(DebugState, Event->Clock);
|
||
|
}
|
||
|
|
||
|
if(Event->Type == DebugType_MarkDebugValue)
|
||
|
{
|
||
|
AddVariableToGroup(DebugState,
|
||
|
GetGroupForHierarchicalName(DebugState, Event->Value_debug_event->BlockName),
|
||
|
Event->Value_debug_event);
|
||
|
}
|
||
|
else if(Event->Type == DebugType_FrameMarker)
|
||
|
{
|
||
|
Assert(DebugState->CollationFrame);
|
||
|
|
||
|
DebugState->CollationFrame->EndClock = Event->Clock;
|
||
|
DebugState->CollationFrame->WallSecondsElapsed = Event->Value_r32;
|
||
|
|
||
|
r32 ClockRange = (r32)(DebugState->CollationFrame->EndClock - DebugState->CollationFrame->BeginClock);
|
||
|
// TODO(casey): Can we reenable this now??
|
||
|
#if 0
|
||
|
if(ClockRange > 0.0f)
|
||
|
{
|
||
|
r32 FrameBarScale = 1.0f / ClockRange;
|
||
|
if(DebugState->FrameBarScale > FrameBarScale)
|
||
|
{
|
||
|
DebugState->FrameBarScale = FrameBarScale;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(DebugState->Paused)
|
||
|
{
|
||
|
FreeFrame(DebugState, DebugState->CollationFrame);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(DebugState->MostRecentFrame)
|
||
|
{
|
||
|
DebugState->MostRecentFrame->Next = DebugState->CollationFrame;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugState->OldestFrame = DebugState->MostRecentFrame = DebugState->CollationFrame;
|
||
|
}
|
||
|
++DebugState->FrameCount;
|
||
|
}
|
||
|
|
||
|
DebugState->CollationFrame = NewFrame(DebugState, Event->Clock);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert(DebugState->CollationFrame);
|
||
|
|
||
|
u32 FrameIndex = DebugState->FrameCount - 1;
|
||
|
debug_thread *Thread = GetDebugThread(DebugState, Event->ThreadID);
|
||
|
u64 RelativeClock = Event->Clock - DebugState->CollationFrame->BeginClock;
|
||
|
|
||
|
switch(Event->Type)
|
||
|
{
|
||
|
case DebugType_BeginBlock:
|
||
|
{
|
||
|
open_debug_block *DebugBlock = AllocateOpenDebugBlock(
|
||
|
DebugState, FrameIndex, Event, &Thread->FirstOpenCodeBlock);
|
||
|
} break;
|
||
|
|
||
|
case DebugType_EndBlock:
|
||
|
{
|
||
|
if(Thread->FirstOpenCodeBlock)
|
||
|
{
|
||
|
open_debug_block *MatchingBlock = Thread->FirstOpenCodeBlock;
|
||
|
debug_event *OpeningEvent = MatchingBlock->OpeningEvent;
|
||
|
if(EventsMatch(*OpeningEvent, *Event))
|
||
|
{
|
||
|
if(MatchingBlock->StartingFrameIndex == FrameIndex)
|
||
|
{
|
||
|
char *MatchName =
|
||
|
MatchingBlock->Parent ? MatchingBlock->Parent->OpeningEvent->BlockName : 0;
|
||
|
if(MatchName == DebugState->ScopeToRecord)
|
||
|
{
|
||
|
r32 MinT = (r32)(OpeningEvent->Clock - DebugState->CollationFrame->BeginClock);
|
||
|
r32 MaxT = (r32)(Event->Clock - DebugState->CollationFrame->BeginClock);
|
||
|
r32 ThresholdT = 0.01f;
|
||
|
if((MaxT - MinT) > ThresholdT)
|
||
|
{
|
||
|
debug_frame_region *Region = AddRegion(DebugState, DebugState->CollationFrame);
|
||
|
Region->Event = OpeningEvent;
|
||
|
Region->CycleCount = (Event->Clock - OpeningEvent->Clock);
|
||
|
Region->LaneIndex = (u16)Thread->LaneIndex;
|
||
|
Region->MinT = MinT;
|
||
|
Region->MaxT = MaxT;
|
||
|
Region->ColorIndex = (u16)OpeningEvent->BlockName;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// TODO(casey): Record all frames in between and begin/end spans!
|
||
|
}
|
||
|
|
||
|
DeallocateOpenDebugBlock(DebugState, &Thread->FirstOpenCodeBlock);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// TODO(casey): Record span that goes to the beginning of the frame series?
|
||
|
}
|
||
|
}
|
||
|
} break;
|
||
|
|
||
|
case DebugType_OpenDataBlock:
|
||
|
{
|
||
|
open_debug_block *DebugBlock = AllocateOpenDebugBlock(
|
||
|
DebugState, FrameIndex, Event, &Thread->FirstOpenDataBlock);
|
||
|
|
||
|
DebugBlock->Group = CreateVariableGroup(DebugState);
|
||
|
debug_variable_link *Link =
|
||
|
AddVariableToGroup(DebugState,
|
||
|
DebugBlock->Parent ? DebugBlock->Parent->Group : DebugState->CollationFrame->RootGroup,
|
||
|
Event);
|
||
|
Link->Children = DebugBlock->Group;
|
||
|
} break;
|
||
|
|
||
|
case DebugType_CloseDataBlock:
|
||
|
{
|
||
|
if(Thread->FirstOpenDataBlock)
|
||
|
{
|
||
|
open_debug_block *MatchingBlock = Thread->FirstOpenDataBlock;
|
||
|
debug_event *OpeningEvent = MatchingBlock->OpeningEvent;
|
||
|
if(EventsMatch(*OpeningEvent, *Event))
|
||
|
{
|
||
|
DeallocateOpenDebugBlock(DebugState, &Thread->FirstOpenDataBlock);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// TODO(casey): Record span that goes to the beginning of the frame series?
|
||
|
}
|
||
|
}
|
||
|
} break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
AddVariableToGroup(DebugState, Thread->FirstOpenDataBlock->Group, Event);
|
||
|
} break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGStart(debug_state *DebugState, game_assets *Assets, u32 Width, u32 Height)
|
||
|
{
|
||
|
TIMED_FUNCTION();
|
||
|
|
||
|
if(!DebugState->Initialized)
|
||
|
{
|
||
|
DebugState->FrameBarLaneCount = 0;
|
||
|
DebugState->FirstThread = 0;
|
||
|
DebugState->FirstFreeThread = 0;
|
||
|
DebugState->FirstFreeBlock = 0;
|
||
|
|
||
|
DebugState->FrameCount = 0;
|
||
|
|
||
|
DebugState->OldestFrame = DebugState->MostRecentFrame = DebugState->FirstFreeFrame = 0;
|
||
|
DebugState->CollationFrame = 0;
|
||
|
|
||
|
DebugState->HighPriorityQueue = DebugGlobalMemory->HighPriorityQueue;
|
||
|
DebugState->TreeSentinel.Next = &DebugState->TreeSentinel;
|
||
|
DebugState->TreeSentinel.Prev = &DebugState->TreeSentinel;
|
||
|
DebugState->TreeSentinel.Group = 0;
|
||
|
|
||
|
InitializeArena(&DebugState->DebugArena, DebugGlobalMemory->DebugStorageSize - sizeof(debug_state), DebugState + 1);
|
||
|
|
||
|
#if 0
|
||
|
debug_variable_definition_context Context = {};
|
||
|
Context.State = DebugState;
|
||
|
Context.Arena = &DebugState->DebugArena;
|
||
|
Context.GroupStack[0] = 0;
|
||
|
|
||
|
DebugState->RootGroup = DEBUGBeginVariableGroup(&Context, "Root");
|
||
|
DEBUGBeginVariableGroup(&Context, "Debugging");
|
||
|
|
||
|
DEBUGCreateVariables(&Context);
|
||
|
DEBUGBeginVariableGroup(&Context, "Profile");
|
||
|
DEBUGBeginVariableGroup(&Context, "By Thread");
|
||
|
DEBUGAddVariable(&Context, DebugType_CounterThreadList, "");
|
||
|
DEBUGEndVariableGroup(&Context);
|
||
|
DEBUGBeginVariableGroup(&Context, "By Function");
|
||
|
DEBUGAddVariable(&Context, DebugType_CounterThreadList, "");
|
||
|
DEBUGEndVariableGroup(&Context);
|
||
|
DEBUGEndVariableGroup(&Context);
|
||
|
|
||
|
asset_vector MatchVector = {};
|
||
|
MatchVector.E[Tag_FacingDirection] = 0.0f;
|
||
|
asset_vector WeightVector = {};
|
||
|
WeightVector.E[Tag_FacingDirection] = 1.0f;
|
||
|
bitmap_id ID = GetBestMatchBitmapFrom(Assets, Asset_Head, &MatchVector, &WeightVector);
|
||
|
|
||
|
DEBUGAddVariable(&Context, "Test Bitmap", ID);
|
||
|
|
||
|
DEBUGEndVariableGroup(&Context);
|
||
|
DEBUGEndVariableGroup(&Context);
|
||
|
Assert(Context.GroupDepth == 0);
|
||
|
#endif
|
||
|
|
||
|
DebugState->RenderGroup = AllocateRenderGroup(Assets, &DebugState->DebugArena, Megabytes(16), false);
|
||
|
|
||
|
DebugState->Paused = false;
|
||
|
DebugState->ScopeToRecord = 0;
|
||
|
|
||
|
DebugState->Initialized = true;
|
||
|
DebugState->ValuesGroup = CreateVariableGroup(DebugState);
|
||
|
|
||
|
AddTree(DebugState, DebugState->RootGroup, V2(-0.5f*Width, 0.5f*Height));
|
||
|
}
|
||
|
|
||
|
BeginRender(DebugState->RenderGroup);
|
||
|
DebugState->DebugFont = PushFont(DebugState->RenderGroup, DebugState->FontID);
|
||
|
DebugState->DebugFontInfo = GetFontInfo(DebugState->RenderGroup->Assets, DebugState->FontID);
|
||
|
|
||
|
DebugState->GlobalWidth = (r32)Width;
|
||
|
DebugState->GlobalHeight = (r32)Height;
|
||
|
|
||
|
asset_vector MatchVector = {};
|
||
|
asset_vector WeightVector = {};
|
||
|
MatchVector.E[Tag_FontType] = (r32)FontType_Debug;
|
||
|
WeightVector.E[Tag_FontType] = 1.0f;
|
||
|
DebugState->FontID = GetBestMatchFontFrom(Assets, Asset_Font, &MatchVector, &WeightVector);
|
||
|
|
||
|
DebugState->FontScale = 1.0f;
|
||
|
Orthographic(DebugState->RenderGroup, Width, Height, 1.0f);
|
||
|
DebugState->LeftEdge = -0.5f*Width;
|
||
|
DebugState->RightEdge = 0.5f*Width;
|
||
|
|
||
|
DebugState->AtY = 0.5f*Height;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGDumpStruct(u32 MemberCount, member_definition *MemberDefs, void *StructPtr, u32 IndentLevel = 0)
|
||
|
{
|
||
|
for(u32 MemberIndex = 0;
|
||
|
MemberIndex < MemberCount;
|
||
|
++MemberIndex)
|
||
|
{
|
||
|
char TextBufferBase[256];
|
||
|
char *TextBuffer = TextBufferBase;
|
||
|
for(u32 Indent = 0;
|
||
|
Indent < IndentLevel;
|
||
|
++Indent)
|
||
|
{
|
||
|
*TextBuffer++ = ' ';
|
||
|
*TextBuffer++ = ' ';
|
||
|
*TextBuffer++ = ' ';
|
||
|
*TextBuffer++ = ' ';
|
||
|
}
|
||
|
TextBuffer[0] = 0;
|
||
|
size_t TextBufferLeft = (TextBufferBase + sizeof(TextBufferBase)) - TextBuffer;
|
||
|
|
||
|
|
||
|
member_definition *Member = MemberDefs + MemberIndex;
|
||
|
|
||
|
void *MemberPtr = (((u8 *)StructPtr) + Member->Offset);
|
||
|
if(Member->Flags & MetaMemberFlag_IsPointer)
|
||
|
{
|
||
|
MemberPtr = *(void **)MemberPtr;
|
||
|
}
|
||
|
|
||
|
if(MemberPtr)
|
||
|
{
|
||
|
switch(Member->Type)
|
||
|
{
|
||
|
case MetaType_u32:
|
||
|
{
|
||
|
_snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %u", Member->Name, *(u32 *)MemberPtr);
|
||
|
} break;
|
||
|
|
||
|
case MetaType_b32:
|
||
|
{
|
||
|
_snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %u", Member->Name, *(b32 *)MemberPtr);
|
||
|
} break;
|
||
|
|
||
|
case MetaType_s32:
|
||
|
{
|
||
|
_snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %d", Member->Name, *(s32 *)MemberPtr);
|
||
|
} break;
|
||
|
|
||
|
case MetaType_r32:
|
||
|
{
|
||
|
_snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %f", Member->Name, *(r32 *)MemberPtr);
|
||
|
} break;
|
||
|
|
||
|
case MetaType_v2:
|
||
|
{
|
||
|
_snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: {%f,%f}",
|
||
|
Member->Name,
|
||
|
((v2 *)MemberPtr)->x,
|
||
|
((v2 *)MemberPtr)->y);
|
||
|
} break;
|
||
|
|
||
|
case MetaType_v3:
|
||
|
{
|
||
|
_snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: {%f,%f,%f}",
|
||
|
Member->Name,
|
||
|
((v3 *)MemberPtr)->x,
|
||
|
((v3 *)MemberPtr)->y,
|
||
|
((v3 *)MemberPtr)->z);
|
||
|
} break;
|
||
|
|
||
|
META_HANDLE_TYPE_DUMP(MemberPtr, IndentLevel + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(TextBuffer[0])
|
||
|
{
|
||
|
DEBUGTextLine(TextBufferBase);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
DEBUGEnd(debug_state *DebugState, game_input *Input, loaded_bitmap *DrawBuffer)
|
||
|
{
|
||
|
TIMED_FUNCTION();
|
||
|
|
||
|
render_group *RenderGroup = DebugState->RenderGroup;
|
||
|
|
||
|
debug_event *HotEvent = 0;
|
||
|
|
||
|
v2 MouseP = Unproject(DebugState->RenderGroup, V2(Input->MouseX, Input->MouseY)).xy;
|
||
|
DEBUGDrawMainMenu(DebugState, RenderGroup, MouseP);
|
||
|
DEBUGInteract(DebugState, Input, MouseP);
|
||
|
|
||
|
if(DebugState->Compiling)
|
||
|
{
|
||
|
debug_process_state State = Platform.DEBUGGetProcessState(DebugState->Compiler);
|
||
|
if(State.IsRunning)
|
||
|
{
|
||
|
DEBUGTextLine("COMPILING");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugState->Compiling = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
loaded_font *Font = DebugState->DebugFont;
|
||
|
hha_font *Info = DebugState->DebugFontInfo;
|
||
|
if(Font)
|
||
|
{
|
||
|
#if 0
|
||
|
for(u32 CounterIndex = 0;
|
||
|
CounterIndex < DebugState->CounterCount;
|
||
|
++CounterIndex)
|
||
|
{
|
||
|
debug_counter_state *Counter = DebugState->CounterStates + CounterIndex;
|
||
|
|
||
|
|
||
|
debug_statistic HitCount, CycleCount, CycleOverHit;
|
||
|
BeginDebugStatistic(&HitCount);
|
||
|
BeginDebugStatistic(&CycleCount);
|
||
|
BeginDebugStatistic(&CycleOverHit);
|
||
|
for(u32 SnapshotIndex = 0;
|
||
|
SnapshotIndex < DEBUG_SNAPSHOT_COUNT;
|
||
|
++SnapshotIndex)
|
||
|
{
|
||
|
AccumDebugStatistic(&HitCount, Counter->Snapshots[SnapshotIndex].HitCount);
|
||
|
AccumDebugStatistic(&CycleCount, (u32)Counter->Snapshots[SnapshotIndex].CycleCount);
|
||
|
|
||
|
r64 HOC = 0.0f;
|
||
|
if(Counter->Snapshots[SnapshotIndex].HitCount)
|
||
|
{
|
||
|
HOC = ((r64)Counter->Snapshots[SnapshotIndex].CycleCount /
|
||
|
(r64)Counter->Snapshots[SnapshotIndex].HitCount);
|
||
|
}
|
||
|
AccumDebugStatistic(&CycleOverHit, HOC);
|
||
|
}
|
||
|
EndDebugStatistic(&HitCount);
|
||
|
EndDebugStatistic(&CycleCount);
|
||
|
EndDebugStatistic(&CycleOverHit);
|
||
|
|
||
|
if(Counter->BlockName)
|
||
|
{
|
||
|
if(CycleCount.Max > 0.0f)
|
||
|
{
|
||
|
r32 BarWidth = 4.0f;
|
||
|
r32 ChartLeft = 0.0f;
|
||
|
r32 ChartMinY = AtY;
|
||
|
r32 ChartHeight = Info->AscenderHeight*FontScale;
|
||
|
r32 Scale = 1.0f / (r32)CycleCount.Max;
|
||
|
for(u32 SnapshotIndex = 0;
|
||
|
SnapshotIndex < DEBUG_SNAPSHOT_COUNT;
|
||
|
++SnapshotIndex)
|
||
|
{
|
||
|
r32 ThisProportion = Scale*(r32)Counter->Snapshots[SnapshotIndex].CycleCount;
|
||
|
r32 ThisHeight = ChartHeight*ThisProportion;
|
||
|
PushRect(RenderGroup, V3(ChartLeft + BarWidth*(r32)SnapshotIndex + 0.5f*BarWidth, ChartMinY + 0.5f*ThisHeight, 0.0f), V2(BarWidth, ThisHeight), V4(ThisProportion, 1, 0.0f, 1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 1
|
||
|
char TextBuffer[256];
|
||
|
_snprintf_s(TextBuffer, sizeof(TextBuffer),
|
||
|
"%32s(%4d): %10ucy %8uh %10ucy/h",
|
||
|
Counter->BlockName,
|
||
|
Counter->LineNumber,
|
||
|
(u32)CycleCount.Avg,
|
||
|
(u32)HitCount.Avg,
|
||
|
(u32)CycleOverHit.Avg);
|
||
|
DEBUGTextLine(TextBuffer);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(DebugState->MostRecentFrame)
|
||
|
{
|
||
|
char TextBuffer[256];
|
||
|
_snprintf_s(TextBuffer, sizeof(TextBuffer),
|
||
|
"Last frame time: %.02fms",
|
||
|
DebugState->MostRecentFrame->WallSecondsElapsed * 1000.0f);
|
||
|
DEBUGTextLine(TextBuffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(WasPressed(Input->MouseButtons[PlatformMouseButton_Left]))
|
||
|
{
|
||
|
if(HotEvent)
|
||
|
{
|
||
|
DebugState->ScopeToRecord = HotEvent->BlockName;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugState->ScopeToRecord = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TiledRenderGroupToOutput(DebugState->HighPriorityQueue, DebugState->RenderGroup, DrawBuffer);
|
||
|
EndRender(DebugState->RenderGroup);
|
||
|
|
||
|
// NOTE(casey): Clear the UI state for the next frame
|
||
|
ZeroStruct(DebugState->NextHotInteraction);
|
||
|
}
|
||
|
|
||
|
extern "C" DEBUG_GAME_FRAME_END(DEBUGGameFrameEnd)
|
||
|
{
|
||
|
GlobalDebugTable->CurrentEventArrayIndex = !GlobalDebugTable->CurrentEventArrayIndex;
|
||
|
u64 ArrayIndex_EventIndex = AtomicExchangeU64(&GlobalDebugTable->EventArrayIndex_EventIndex,
|
||
|
(u64)GlobalDebugTable->CurrentEventArrayIndex << 32);
|
||
|
|
||
|
u32 EventArrayIndex = ArrayIndex_EventIndex >> 32;
|
||
|
Assert(EventArrayIndex <= 1);
|
||
|
u32 EventCount = ArrayIndex_EventIndex & 0xFFFFFFFF;
|
||
|
|
||
|
debug_state *DebugState = (debug_state *)Memory->DebugStorage;
|
||
|
if(DebugState)
|
||
|
{
|
||
|
game_assets *Assets = DEBUGGetGameAssets(Memory);
|
||
|
|
||
|
DEBUGStart(DebugState, Assets, Buffer->Width, Buffer->Height);
|
||
|
CollateDebugRecords(DebugState, EventCount, GlobalDebugTable->Events[EventArrayIndex]);
|
||
|
|
||
|
loaded_bitmap DrawBuffer = {};
|
||
|
DrawBuffer.Width = Buffer->Width;
|
||
|
DrawBuffer.Height = Buffer->Height;
|
||
|
DrawBuffer.Pitch = Buffer->Pitch;
|
||
|
DrawBuffer.Memory = Buffer->Memory;
|
||
|
DEBUGEnd(DebugState, Input, &DrawBuffer);
|
||
|
}
|
||
|
|
||
|
return(GlobalDebugTable);
|
||
|
}
|