Handmade Hero » Forums » Game » Understanding the debug system
r2d2
31 posts
#13338 Understanding the debug system
3 weeks, 1 day ago

Me again, still trying to figure out some bugs that came up after I have updated my local code base to day 242.

For some reason, introduction of the DEBUG_IF(Renderer_UseSoftware) macro call to the platform layer makes the debug system go bananas. During the cutscene mode the GroundChunks submenu (with the RecomputeOnEXEChange variable) never gets drawn on screen. However, all is well when I remove the macro call at platform level.

It doesn't happen at Day 239 and earlier. After 240-241 the renderer was moved into the third tier and the actual DisplayBufferInWindow call (where Renderer_UseSoftware is used to determine which render path to take) happens after calling DEBUGGameFrameEnd and collating the debug records. So my reasoning is something weird happens to the debug event table during this.

From what I understand, the DEBUGGameFrameEnd function swaps current event array index on the debug event table:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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;

    // ... collate stuff

   return GlobalDebugTable;
}


However, this is how the call to the function looks in the platform layer:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        BEGIN_BLOCK(DebugCollation);

        if (Game.DEBUGFrameEnd)
        {
            GlobalDebugTable = Game.DEBUGFrameEnd(&GameMemory, &Input, &RenderCommands);
        }

        GlobalDebugTable_.EventArrayIndex_EventIndex = 0;

        END_BLOCK(DebugCollation);


Why does the EventArrayIndex_EventIndex gets reset when CurrentEventArrayIndex doesn't? GlobalDebugTable_.EventArrayIndex_EventIndex >> 32 would return zero but CurrentEventArrayIndex might be set to one. What am I missing?

The only other reference to EventArrayIndex_EventIndex that I've found in the code base is the AtomicAddU64 in the RecordDebugEvent macro. Code listed below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#define RecordDebugEvent(EventType, Block)           \
        u64 ArrayIndex_EventIndex = AtomicAddU64(&GlobalDebugTable->EventArrayIndex_EventIndex, 1); \
        u32 EventIndex = ArrayIndex_EventIndex & 0xFFFFFFFF;            \
        Assert(EventIndex < ArrayCount(GlobalDebugTable->Events[0]));   \
        debug_event *Event = GlobalDebugTable->Events[ArrayIndex_EventIndex >> 32] + EventIndex; \
        Event->Clock = __rdtsc();                       \
        Event->Type = (u8)EventType;                                    \
        Event->CoreIndex = 0;                                           \
        Event->ThreadID = (u16)GetThreadID();                         \
        Event->GUID = UniqueFileCounterString(); \
        Event->BlockName = Block;                              \


I feel like calling RecordDebugEvent on the platform layer messes something up. Why do we even bother with the GlobalDebugTable->CurrentEventArrayIndex if it never gets encoded into EventArrayIndex_EventIndex?

Hopefully, someone familiar with HH's codebase might give me some hints.
r2d2
31 posts
#13339 Understanding the debug system
3 weeks, 1 day ago

Well, this is fun...

During my debugging I've made a completely identical copy of DEBUGInitializeValue function with a different name and used it in the platform layer where I've written out the complete function call instead of DEBUG_IF macro. Everything works now as intended.

To summarise, this is used in the game lib:
1
2
3
#define DEBUG_IF__(Path)  \
    local_persist debug_event DebugValue##Path = DEBUGInitializeValue((DebugValue##Path.Value_b32 = GlobalConstants_##Path, DebugType_b32), &DebugValue##Path, UniqueFileCounterString(), #Path); \
    if (DebugValue##Path.Value_b32)


This is what I've written in the platform layer code:
1
2
3
4
5
6
7
    // DEBUG_IF(Renderer_UseSoftware)
    // if(0)
    local_persist debug_event DebugValueRenderer_UseSoftware =
        DEBUGTestInitValue((DebugValueRenderer_UseSoftware.Value_b32 = GlobalConstants_Renderer_UseSoftware, DebugType_b32),
        &DebugValueRenderer_UseSoftware, UniqueFileCounterString(), "Renderer_UseSoftware");

    if (DebugValueRenderer_UseSoftware.Value_b32)


DEBUGInitializeValue and DEBUGTestInitValue are identical. What's up with that?
r2d2
31 posts
#13340 Understanding the debug system
3 weeks ago

Sorry for multi-posting...

I've compiled the code under -O2 instead of -O0 and everything works perfectly. Then I've had a terrible sense of deja vu, added static to DEBUGInitializeValue function's definition and lo and behold: all is right in the world again.

We've discussed a similar issue with Mārtiņš Možeiko in this thread: https://hero.handmade.network/for...ay_152:_whats_up_with_the_linking

What's curious, since then I've migrated my platform layer code to be compiled as Obj-C++ as suggested by Mārtiņš here:
3) rename file to main.mm This will make compiler to treat it as Objective-C++ source

But I guess the problem is slightly different here and I have no clue how exactly.