Made memory arena debugger show the new arenas. Made by gemini
This commit is contained in:
@@ -58,7 +58,8 @@ namespace Juliet
|
|||||||
else if (AllowRealloc && !InternalArena)
|
else if (AllowRealloc && !InternalArena)
|
||||||
{
|
{
|
||||||
DataFirst = Data = static_cast<Type*>(ArenaReallocate(Arena, Data, Capacity * sizeof(Type),
|
DataFirst = Data = static_cast<Type*>(ArenaReallocate(Arena, Data, Capacity * sizeof(Type),
|
||||||
newCapacity * sizeof(Type), AlignOf(Type), true));
|
newCapacity * sizeof(Type), AlignOf(Type), true
|
||||||
|
JULIET_DEBUG_ONLY(, "VectorRealloc")));
|
||||||
DataLast = Data + Count - 1;
|
DataLast = Data + Count - 1;
|
||||||
}
|
}
|
||||||
Capacity = newCapacity;
|
Capacity = newCapacity;
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
#include <Core/Common/NonNullPtr.h>
|
#include <Core/Common/NonNullPtr.h>
|
||||||
#include <Core/Common/String.h>
|
#include <Core/Common/String.h>
|
||||||
#include <Core/Memory/Utils.h>
|
#include <Core/Memory/Utils.h>
|
||||||
|
#include <Core/Memory/Utils.h>
|
||||||
#include <Juliet.h>
|
#include <Juliet.h>
|
||||||
|
#include <typeinfo> // Added for typeid
|
||||||
|
|
||||||
namespace Juliet
|
namespace Juliet
|
||||||
{
|
{
|
||||||
@@ -15,6 +17,10 @@ namespace Juliet
|
|||||||
|
|
||||||
struct ArenaFreeNode;
|
struct ArenaFreeNode;
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
struct ArenaDebugInfo;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Arena
|
struct Arena
|
||||||
{
|
{
|
||||||
Arena* Previous;
|
Arena* Previous;
|
||||||
@@ -36,8 +42,13 @@ namespace Juliet
|
|||||||
|
|
||||||
bool AllowRealloc : 1;
|
bool AllowRealloc : 1;
|
||||||
|
|
||||||
JULIET_DEBUG_ONLY(uint16 LostNodeCount);
|
JULIET_DEBUG_ONLY(uint16 LostNodeCount;)
|
||||||
JULIET_DEBUG_ONLY(bool CanReserveMore : 1;)
|
JULIET_DEBUG_ONLY(bool CanReserveMore : 1;)
|
||||||
|
|
||||||
|
JULIET_DEBUG_ONLY(Arena* GlobalNext;)
|
||||||
|
JULIET_DEBUG_ONLY(Arena* GlobalPrev;)
|
||||||
|
JULIET_DEBUG_ONLY(ArenaDebugInfo* FirstDebugInfo;)
|
||||||
|
JULIET_DEBUG_ONLY(const char* Name;)
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
|
static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
|
||||||
|
|
||||||
@@ -54,14 +65,17 @@ namespace Juliet
|
|||||||
JULIET_DEBUG_ONLY(bool CanReserveMore : 1 = true;)
|
JULIET_DEBUG_ONLY(bool CanReserveMore : 1 = true;)
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] Arena* ArenaAllocate(const ArenaParams& params = {},
|
[[nodiscard]] Arena* ArenaAllocate(const ArenaParams& params = {}
|
||||||
|
JULIET_DEBUG_ONLY(, const char* name = "Unnamed Arena"),
|
||||||
const std::source_location& loc = std::source_location::current());
|
const std::source_location& loc = std::source_location::current());
|
||||||
void ArenaRelease(NonNullPtr<Arena> arena);
|
void ArenaRelease(NonNullPtr<Arena> arena);
|
||||||
|
|
||||||
// Raw Push, can be used but templated helpers exists below
|
// Raw Push, can be used but templated helpers exists below
|
||||||
[[nodiscard]] void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, bool shouldBeZeroed);
|
// Raw Push, can be used but templated helpers exists below
|
||||||
|
[[nodiscard]] void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, bool shouldBeZeroed
|
||||||
|
JULIET_DEBUG_ONLY(, const char* tag));
|
||||||
[[nodiscard]] void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize,
|
[[nodiscard]] void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize,
|
||||||
size_t align, bool shouldBeZeroed);
|
size_t align, bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag));
|
||||||
void ArenaPopTo(NonNullPtr<Arena> arena, size_t position);
|
void ArenaPopTo(NonNullPtr<Arena> arena, size_t position);
|
||||||
void ArenaPop(NonNullPtr<Arena> arena, size_t amount);
|
void ArenaPop(NonNullPtr<Arena> arena, size_t amount);
|
||||||
void ArenaClear(NonNullPtr<Arena> arena);
|
void ArenaClear(NonNullPtr<Arena> arena);
|
||||||
@@ -70,7 +84,7 @@ namespace Juliet
|
|||||||
template <typename Type>
|
template <typename Type>
|
||||||
[[nodiscard]] Type* ArenaPushStruct(NonNullPtr<Arena> arena)
|
[[nodiscard]] Type* ArenaPushStruct(NonNullPtr<Arena> arena)
|
||||||
{
|
{
|
||||||
return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * 1, AlignOf(Type), true));
|
return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * 1, AlignOf(Type), true JULIET_DEBUG_ONLY(, typeid(Type).name())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// #define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0))
|
// #define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0))
|
||||||
@@ -81,17 +95,11 @@ namespace Juliet
|
|||||||
template <typename Type>
|
template <typename Type>
|
||||||
[[nodiscard]] Type* ArenaPushArray(NonNullPtr<Arena> arena, size_t count)
|
[[nodiscard]] Type* ArenaPushArray(NonNullPtr<Arena> arena, size_t count)
|
||||||
{
|
{
|
||||||
return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * count, Max(8ull, AlignOf(Type)), true));
|
return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * count, Max(8ull, AlignOf(Type)), true JULIET_DEBUG_ONLY(, typeid(Type).name())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Paged Memory Architecture ---
|
// --- Paged Memory Architecture ---
|
||||||
struct ArenaAllocation
|
struct ArenaAllocation;
|
||||||
{
|
|
||||||
size_t Offset;
|
|
||||||
size_t Size;
|
|
||||||
String Tag;
|
|
||||||
ArenaAllocation* Next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MemoryBlock
|
struct MemoryBlock
|
||||||
{
|
{
|
||||||
@@ -159,9 +167,15 @@ namespace Juliet
|
|||||||
// Internal engine function to initialize memory arenas.
|
// Internal engine function to initialize memory arenas.
|
||||||
void MemoryArenasInit();
|
void MemoryArenasInit();
|
||||||
|
|
||||||
|
// Internal engine function to shutdown memory arenas.
|
||||||
// Internal engine function to shutdown memory arenas.
|
// Internal engine function to shutdown memory arenas.
|
||||||
void MemoryArenasShutdown();
|
void MemoryArenasShutdown();
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
JULIET_API Arena* GetGlobalArenaListHead();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T* ArenaPushType(MemoryArena* arena, String tag)
|
inline T* ArenaPushType(MemoryArena* arena, String tag)
|
||||||
{
|
{
|
||||||
|
|||||||
50
Juliet/include/Core/Memory/MemoryArenaDebug.h
Normal file
50
Juliet/include/Core/Memory/MemoryArenaDebug.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Juliet.h>
|
||||||
|
#include <Core/Common/CoreTypes.h>
|
||||||
|
#include <Core/Common/String.h>
|
||||||
|
#include <Core/Common/NonNullPtr.h>
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
|
||||||
|
namespace Juliet
|
||||||
|
{
|
||||||
|
struct Arena;
|
||||||
|
struct MemoryBlock;
|
||||||
|
|
||||||
|
// Arena (Struct)
|
||||||
|
struct ArenaDebugInfo
|
||||||
|
{
|
||||||
|
const char* Tag;
|
||||||
|
size_t Offset;
|
||||||
|
size_t Size;
|
||||||
|
ArenaDebugInfo* Next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// MemoryArena (Pool-based)
|
||||||
|
struct ArenaAllocation
|
||||||
|
{
|
||||||
|
size_t Offset;
|
||||||
|
size_t Size;
|
||||||
|
String Tag;
|
||||||
|
ArenaAllocation* Next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Arena (Struct)
|
||||||
|
void DebugRegisterArena(NonNullPtr<Arena> arena);
|
||||||
|
void DebugUnregisterArena(NonNullPtr<Arena> arena);
|
||||||
|
void DebugArenaSetDebugName(NonNullPtr<Arena> arena, const char* name);
|
||||||
|
bool IsDebugInfoArena(const Arena* arena); // To prevent recursion
|
||||||
|
void DebugArenaFreeBlock(Arena* block); // To clear all debug infos in a block
|
||||||
|
void DebugArenaRemoveAllocation(Arena* block, size_t oldOffset);
|
||||||
|
void DebugArenaPopTo(Arena* block, size_t newPosition);
|
||||||
|
void DebugArenaAddDebugInfo(Arena* block, size_t size, size_t offset, const char* tag);
|
||||||
|
|
||||||
|
// MemoryArena (Pool-based)
|
||||||
|
void DebugFreeArenaAllocations(MemoryBlock* blk);
|
||||||
|
void DebugArenaAddAllocation(MemoryBlock* blk, size_t size, size_t offset, String tag);
|
||||||
|
void DebugArenaRemoveLastAllocation(MemoryBlock* blk);
|
||||||
|
|
||||||
|
} // namespace Juliet
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
#define JULIET_DEBUG_ONLY(...) __VA_ARGS__
|
#define JULIET_DEBUG_ONLY(...) __VA_ARGS__
|
||||||
#else
|
#else
|
||||||
#define JULIET_DEBUG 0
|
#define JULIET_DEBUG 0
|
||||||
#define JULIET_DEBUG_ONLY(expr)
|
#define JULIET_DEBUG_ONLY(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Manual override to disable ImGui
|
// Manual override to disable ImGui
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Juliet::ImGuiService
|
|||||||
|
|
||||||
void* ImGuiAllocWrapper(size_t size, void* /*user_data*/)
|
void* ImGuiAllocWrapper(size_t size, void* /*user_data*/)
|
||||||
{
|
{
|
||||||
return ArenaPush(g_ImGuiArena, size, 8, false);
|
return ArenaPush(g_ImGuiArena, size, 8, false JULIET_DEBUG_ONLY(, "ImGuiAlloc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiFreeWrapper(void* /*ptr*/, void* /*user_data*/)
|
void ImGuiFreeWrapper(void* /*ptr*/, void* /*user_data*/)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <Core/Logging/LogManager.h>
|
#include <Core/Logging/LogManager.h>
|
||||||
#include <Core/Memory/Allocator.h>
|
#include <Core/Memory/Allocator.h>
|
||||||
#include <Core/Memory/MemoryArena.h>
|
#include <Core/Memory/MemoryArena.h>
|
||||||
|
#include <Core/Memory/MemoryArenaDebug.h>
|
||||||
#include <Core/Memory/Utils.h>
|
#include <Core/Memory/Utils.h>
|
||||||
|
|
||||||
#include <algorithm> // For std::max
|
#include <algorithm> // For std::max
|
||||||
@@ -33,7 +34,7 @@ namespace Juliet
|
|||||||
|
|
||||||
// https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c
|
// https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c
|
||||||
|
|
||||||
Arena* ArenaAllocate(const ArenaParams& params, const std::source_location& loc)
|
Arena* ArenaAllocate(const ArenaParams& params JULIET_DEBUG_ONLY(, const char* name), const std::source_location& loc)
|
||||||
{
|
{
|
||||||
Log(LogLevel::Message, LogCategory::Core, "Allocating from %s : %ul", loc.file_name(), loc.line());
|
Log(LogLevel::Message, LogCategory::Core, "Allocating from %s : %ul", loc.file_name(), loc.line());
|
||||||
|
|
||||||
@@ -61,22 +62,34 @@ namespace Juliet
|
|||||||
arena->FreeNodes = nullptr;
|
arena->FreeNodes = nullptr;
|
||||||
arena->AllowRealloc = params.AllowRealloc;
|
arena->AllowRealloc = params.AllowRealloc;
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
arena->CanReserveMore = params.CanReserveMore;
|
arena->CanReserveMore = params.CanReserveMore;
|
||||||
|
arena->FirstDebugInfo = nullptr;
|
||||||
|
|
||||||
|
DebugArenaSetDebugName(arena, name);
|
||||||
|
DebugRegisterArena(arena);
|
||||||
|
#endif
|
||||||
|
|
||||||
return arena;
|
return arena;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArenaRelease(NonNullPtr<Arena> arena)
|
void ArenaRelease(NonNullPtr<Arena> arena)
|
||||||
{
|
{
|
||||||
|
JULIET_DEBUG_ONLY(DebugUnregisterArena(arena);)
|
||||||
|
|
||||||
for (Arena *node = arena->Current, *previous = nullptr; node != nullptr; node = previous)
|
for (Arena *node = arena->Current, *previous = nullptr; node != nullptr; node = previous)
|
||||||
{
|
{
|
||||||
previous = node->Previous;
|
previous = node->Previous;
|
||||||
|
|
||||||
|
JULIET_DEBUG_ONLY(DebugArenaFreeBlock(node);)
|
||||||
|
|
||||||
Memory::OS_Release(node, node->Reserved);
|
Memory::OS_Release(node, node->Reserved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, bool shouldBeZeroed)
|
void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag))
|
||||||
{
|
{
|
||||||
|
// Assert(IsPowerOfTwo(align));
|
||||||
Arena* current = arena->Current;
|
Arena* current = arena->Current;
|
||||||
size_t positionPrePush = AlignPow2(current->Position, align);
|
size_t positionPrePush = AlignPow2(current->Position, align);
|
||||||
size_t positionPostPush = positionPrePush + size;
|
size_t positionPostPush = positionPrePush + size;
|
||||||
@@ -104,7 +117,12 @@ namespace Juliet
|
|||||||
next->Previous = previous;
|
next->Previous = previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
JULIET_DEBUG_ONLY(remainingSize > 0 ? ++current->LostNodeCount : current->LostNodeCount);
|
#if JULIET_DEBUG
|
||||||
|
if (remainingSize > 0)
|
||||||
|
{
|
||||||
|
++current->LostNodeCount;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -158,7 +176,8 @@ namespace Juliet
|
|||||||
reserveSize = AlignPow2(size + k_ArenaHeaderSize, align);
|
reserveSize = AlignPow2(size + k_ArenaHeaderSize, align);
|
||||||
commitSize = AlignPow2(size + k_ArenaHeaderSize, align);
|
commitSize = AlignPow2(size + k_ArenaHeaderSize, align);
|
||||||
}
|
}
|
||||||
newBlock = ArenaAllocate({ .ReserveSize = reserveSize, .CommitSize = commitSize });
|
|
||||||
|
newBlock = ArenaAllocate({ .ReserveSize = reserveSize, .CommitSize = commitSize } JULIET_DEBUG_ONLY(, arena->Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
newBlock->BasePosition = current->BasePosition + current->Reserved;
|
newBlock->BasePosition = current->BasePosition + current->Reserved;
|
||||||
@@ -202,18 +221,21 @@ namespace Juliet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If alloc failed, log and assert
|
|
||||||
if (result == nullptr) [[unlikely]]
|
if (result == nullptr) [[unlikely]]
|
||||||
{
|
{
|
||||||
|
Log(LogLevel::Error, LogCategory::Core, "Fatal Allocation Failure - Unexpected allocation failure");
|
||||||
Assert(false, "Fatal Allocation Failure - Unexpected allocation failure");
|
Assert(false, "Fatal Allocation Failure - Unexpected allocation failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JULIET_DEBUG_ONLY(if (!IsDebugInfoArena(arena)) { DebugArenaAddDebugInfo(current, size, positionPrePush, tag); })
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize, size_t align, bool shouldBeZeroed)
|
void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize, size_t align,
|
||||||
|
bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag))
|
||||||
{
|
{
|
||||||
void* result = ArenaPush(arena, newSize, align, shouldBeZeroed);
|
void* result = ArenaPush(arena, newSize, align, shouldBeZeroed JULIET_DEBUG_ONLY(, tag));
|
||||||
|
|
||||||
// Find the correct block to release
|
// Find the correct block to release
|
||||||
Arena* block = nullptr;
|
Arena* block = nullptr;
|
||||||
@@ -249,6 +271,12 @@ namespace Juliet
|
|||||||
block->FreeNodes->Previous = freeNode;
|
block->FreeNodes->Previous = freeNode;
|
||||||
}
|
}
|
||||||
block->FreeNodes = freeNode;
|
block->FreeNodes = freeNode;
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
// Remove the debug info for the old allocation (since it's now free)
|
||||||
|
size_t oldOffset = static_cast<size_t>(static_cast<Byte*>(oldPtr) - reinterpret_cast<Byte*>(block));
|
||||||
|
DebugArenaRemoveAllocation(block, oldOffset);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -262,20 +290,18 @@ namespace Juliet
|
|||||||
{
|
{
|
||||||
previous = current->Previous;
|
previous = current->Previous;
|
||||||
current->Position = k_ArenaHeaderSize;
|
current->Position = k_ArenaHeaderSize;
|
||||||
|
|
||||||
|
JULIET_DEBUG_ONLY(DebugArenaFreeBlock(current);)
|
||||||
|
|
||||||
SingleLinkedListPushPrevious(arena->FreeBlockLast, current);
|
SingleLinkedListPushPrevious(arena->FreeBlockLast, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No freelist :
|
|
||||||
// for (Arena* previous = nullptr; current->BasePosition >= clampedPosition; current = previous)
|
|
||||||
// {
|
|
||||||
// previous = current->Previous;
|
|
||||||
// Memory::OS_Release(current, current->Reserved);
|
|
||||||
// }
|
|
||||||
|
|
||||||
arena->Current = current;
|
arena->Current = current;
|
||||||
size_t newPosition = clampedPosition - current->BasePosition;
|
size_t newPosition = clampedPosition - current->BasePosition;
|
||||||
Assert(newPosition <= current->Position);
|
Assert(newPosition <= current->Position);
|
||||||
current->Position = newPosition;
|
current->Position = newPosition;
|
||||||
|
|
||||||
|
JULIET_DEBUG_ONLY(DebugArenaPopTo(current, newPosition);)
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArenaPop(NonNullPtr<Arena> arena, size_t amount)
|
void ArenaPop(NonNullPtr<Arena> arena, size_t amount)
|
||||||
@@ -308,23 +334,6 @@ namespace Juliet
|
|||||||
|
|
||||||
// --- MemoryPool Implementation ---
|
// --- MemoryPool Implementation ---
|
||||||
|
|
||||||
#if JULIET_DEBUG
|
|
||||||
static void FreeDebugAllocations(MemoryBlock* blk)
|
|
||||||
{
|
|
||||||
if (!blk)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ArenaAllocation* curr = blk->FirstAllocation;
|
|
||||||
while (curr)
|
|
||||||
{
|
|
||||||
ArenaAllocation* next = curr->Next;
|
|
||||||
SafeFree(curr);
|
|
||||||
curr = next;
|
|
||||||
}
|
|
||||||
blk->FirstAllocation = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Simple First-Fit Allocator
|
// Simple First-Fit Allocator
|
||||||
MemoryBlock* MemoryPool::AllocateBlock(size_t minCapacity)
|
MemoryBlock* MemoryPool::AllocateBlock(size_t minCapacity)
|
||||||
@@ -401,7 +410,7 @@ namespace Juliet
|
|||||||
|
|
||||||
// Poison Header and Data in Debug
|
// Poison Header and Data in Debug
|
||||||
#if JULIET_DEBUG
|
#if JULIET_DEBUG
|
||||||
FreeDebugAllocations(block);
|
DebugFreeArenaAllocations(block);
|
||||||
// 0xDD = Dead Data
|
// 0xDD = Dead Data
|
||||||
MemSet(block->GetData(), 0xDD, block->TotalSize - sizeof(MemoryBlock));
|
MemSet(block->GetData(), 0xDD, block->TotalSize - sizeof(MemoryBlock));
|
||||||
block->Magic = 0xDEADBEEF;
|
block->Magic = 0xDEADBEEF;
|
||||||
@@ -485,23 +494,7 @@ namespace Juliet
|
|||||||
blk->Used += alignmentOffset;
|
blk->Used += alignmentOffset;
|
||||||
void* ptr = blk->GetData() + blk->Used;
|
void* ptr = blk->GetData() + blk->Used;
|
||||||
|
|
||||||
#if JULIET_DEBUG
|
JULIET_DEBUG_ONLY(DebugArenaAddAllocation(blk, size, blk->Used, tag);)
|
||||||
ArenaAllocation* node = (ArenaAllocation*)Malloc(sizeof(ArenaAllocation));
|
|
||||||
node->Offset = blk->Used;
|
|
||||||
node->Size = size;
|
|
||||||
node->Tag = tag;
|
|
||||||
node->Next = nullptr;
|
|
||||||
|
|
||||||
if (!blk->FirstAllocation)
|
|
||||||
blk->FirstAllocation = node;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ArenaAllocation* t = blk->FirstAllocation;
|
|
||||||
while (t->Next)
|
|
||||||
t = t->Next;
|
|
||||||
t->Next = node;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
blk->Used += size;
|
blk->Used += size;
|
||||||
|
|
||||||
@@ -565,29 +558,7 @@ namespace Juliet
|
|||||||
{
|
{
|
||||||
// Yes, we can just rewind the Used pointer
|
// Yes, we can just rewind the Used pointer
|
||||||
blk->Used -= size;
|
blk->Used -= size;
|
||||||
#if JULIET_DEBUG
|
JULIET_DEBUG_ONLY(DebugArenaRemoveLastAllocation(blk);)
|
||||||
{
|
|
||||||
ArenaAllocation* t = blk->FirstAllocation;
|
|
||||||
ArenaAllocation* prev = nullptr;
|
|
||||||
while (t && t->Next)
|
|
||||||
{
|
|
||||||
prev = t;
|
|
||||||
t = t->Next;
|
|
||||||
}
|
|
||||||
if (t)
|
|
||||||
{
|
|
||||||
SafeFree(t);
|
|
||||||
if (prev)
|
|
||||||
{
|
|
||||||
prev->Next = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
blk->FirstAllocation = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,7 +585,7 @@ namespace Juliet
|
|||||||
|
|
||||||
#if JULIET_DEBUG
|
#if JULIET_DEBUG
|
||||||
// Poison First Block
|
// Poison First Block
|
||||||
FreeDebugAllocations(arena->FirstBlock);
|
DebugFreeArenaAllocations(arena->FirstBlock);
|
||||||
MemSet(arena->FirstBlock->GetData(), 0xCD, arena->FirstBlock->TotalSize - sizeof(MemoryBlock));
|
MemSet(arena->FirstBlock->GetData(), 0xCD, arena->FirstBlock->TotalSize - sizeof(MemoryBlock));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
242
Juliet/src/Core/Memory/MemoryArenaDebug.cpp
Normal file
242
Juliet/src/Core/Memory/MemoryArenaDebug.cpp
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
#include <Core/Memory/MemoryArenaDebug.h>
|
||||||
|
#include <Core/Memory/MemoryArena.h> // For Arena definition
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
|
||||||
|
namespace Juliet
|
||||||
|
{
|
||||||
|
Arena* g_ArenaGlobalHead = nullptr;
|
||||||
|
Arena* g_DebugInfoArena = nullptr;
|
||||||
|
ArenaDebugInfo* g_DebugInfoFreeList = nullptr;
|
||||||
|
ArenaAllocation* g_ArenaAllocationFreeList = nullptr;
|
||||||
|
|
||||||
|
// --- Internal Helpers ---
|
||||||
|
static ArenaDebugInfo* AllocDebugInfo()
|
||||||
|
{
|
||||||
|
if (g_DebugInfoFreeList)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo* info = g_DebugInfoFreeList;
|
||||||
|
g_DebugInfoFreeList = info->Next;
|
||||||
|
info->Next = nullptr;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_DebugInfoArena)
|
||||||
|
{
|
||||||
|
// Create a dedicated arena for debug info
|
||||||
|
g_DebugInfoArena = ArenaAllocate({ .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64) } JULIET_DEBUG_ONLY(, "Debug Info Arena"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArenaPushStruct<ArenaDebugInfo>(g_DebugInfoArena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeDebugInfo(ArenaDebugInfo* info)
|
||||||
|
{
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
info->Next = g_DebugInfoFreeList;
|
||||||
|
g_DebugInfoFreeList = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ArenaAllocation* AllocArenaAllocation()
|
||||||
|
{
|
||||||
|
if (g_ArenaAllocationFreeList)
|
||||||
|
{
|
||||||
|
ArenaAllocation* info = g_ArenaAllocationFreeList;
|
||||||
|
g_ArenaAllocationFreeList = info->Next;
|
||||||
|
info->Next = nullptr;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_DebugInfoArena)
|
||||||
|
{
|
||||||
|
g_DebugInfoArena = ArenaAllocate({ .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64) } JULIET_DEBUG_ONLY(, "Debug Info Arena"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArenaPushStruct<ArenaAllocation>(g_DebugInfoArena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeArenaAllocation(ArenaAllocation* info)
|
||||||
|
{
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
info->Next = g_ArenaAllocationFreeList;
|
||||||
|
g_ArenaAllocationFreeList = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ------------------------
|
||||||
|
|
||||||
|
void DebugRegisterArena(NonNullPtr<Arena> arena)
|
||||||
|
{
|
||||||
|
// Add to Global List
|
||||||
|
// Note: Not thread safe, assuming single thread for now as per context
|
||||||
|
if (g_ArenaGlobalHead)
|
||||||
|
{
|
||||||
|
g_ArenaGlobalHead->GlobalPrev = arena;
|
||||||
|
}
|
||||||
|
arena->GlobalNext = g_ArenaGlobalHead;
|
||||||
|
arena->GlobalPrev = nullptr;
|
||||||
|
g_ArenaGlobalHead = arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugUnregisterArena(NonNullPtr<Arena> arena)
|
||||||
|
{
|
||||||
|
// Remove from Global List
|
||||||
|
if (arena->GlobalPrev)
|
||||||
|
{
|
||||||
|
arena->GlobalPrev->GlobalNext = arena->GlobalNext;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_ArenaGlobalHead = arena->GlobalNext;
|
||||||
|
}
|
||||||
|
if (arena->GlobalNext)
|
||||||
|
{
|
||||||
|
arena->GlobalNext->GlobalPrev = arena->GlobalPrev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaSetDebugName(NonNullPtr<Arena> arena, const char* name)
|
||||||
|
{
|
||||||
|
arena->Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDebugInfoArena(const Arena* arena)
|
||||||
|
{
|
||||||
|
return arena == g_DebugInfoArena;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaFreeBlock(Arena* block)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo* info = block->FirstDebugInfo;
|
||||||
|
while (info)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo* nextInfo = info->Next;
|
||||||
|
FreeDebugInfo(info);
|
||||||
|
info = nextInfo;
|
||||||
|
}
|
||||||
|
block->FirstDebugInfo = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaRemoveAllocation(Arena* block, size_t oldOffset)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo** prevInfo = &block->FirstDebugInfo;
|
||||||
|
ArenaDebugInfo* info = block->FirstDebugInfo;
|
||||||
|
|
||||||
|
while (info)
|
||||||
|
{
|
||||||
|
if (info->Offset == oldOffset) // Found it
|
||||||
|
{
|
||||||
|
*prevInfo = info->Next;
|
||||||
|
FreeDebugInfo(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prevInfo = &info->Next;
|
||||||
|
info = info->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaPopTo(Arena* block, size_t newPosition)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo** prevInfo = &block->FirstDebugInfo;
|
||||||
|
ArenaDebugInfo* info = block->FirstDebugInfo;
|
||||||
|
while (info)
|
||||||
|
{
|
||||||
|
if (info->Offset >= newPosition)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo* toFree = info;
|
||||||
|
*prevInfo = info->Next; // Unlink
|
||||||
|
info = info->Next; // Advance
|
||||||
|
FreeDebugInfo(toFree);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prevInfo = &info->Next;
|
||||||
|
info = info->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaAddDebugInfo(Arena* block, size_t size, size_t offset, const char* tag)
|
||||||
|
{
|
||||||
|
ArenaDebugInfo* info = AllocDebugInfo();
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
info->Tag = tag ? tag : "Untagged";
|
||||||
|
info->Size = size;
|
||||||
|
info->Offset = offset;
|
||||||
|
info->Next = block->FirstDebugInfo;
|
||||||
|
block->FirstDebugInfo = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moved to top
|
||||||
|
|
||||||
|
void DebugFreeArenaAllocations(MemoryBlock* blk)
|
||||||
|
{
|
||||||
|
if (!blk)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArenaAllocation* curr = blk->FirstAllocation;
|
||||||
|
while (curr)
|
||||||
|
{
|
||||||
|
ArenaAllocation* next = curr->Next;
|
||||||
|
FreeArenaAllocation(curr);
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
blk->FirstAllocation = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaAddAllocation(MemoryBlock* blk, size_t size, size_t offset, String tag)
|
||||||
|
{
|
||||||
|
ArenaAllocation* node = AllocArenaAllocation();
|
||||||
|
node->Offset = offset;
|
||||||
|
node->Size = size;
|
||||||
|
node->Tag = tag;
|
||||||
|
node->Next = nullptr;
|
||||||
|
|
||||||
|
if (!blk->FirstAllocation)
|
||||||
|
blk->FirstAllocation = node;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ArenaAllocation* t = blk->FirstAllocation;
|
||||||
|
while (t->Next)
|
||||||
|
t = t->Next;
|
||||||
|
t->Next = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugArenaRemoveLastAllocation(MemoryBlock* blk)
|
||||||
|
{
|
||||||
|
ArenaAllocation* t = blk->FirstAllocation;
|
||||||
|
ArenaAllocation* prev = nullptr;
|
||||||
|
while (t && t->Next)
|
||||||
|
{
|
||||||
|
prev = t;
|
||||||
|
t = t->Next;
|
||||||
|
}
|
||||||
|
if (t)
|
||||||
|
{
|
||||||
|
FreeArenaAllocation(t);
|
||||||
|
if (prev)
|
||||||
|
{
|
||||||
|
prev->Next = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blk->FirstAllocation = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessor for global list head (if needed by other files)
|
||||||
|
Arena* GetGlobalArenaListHead()
|
||||||
|
{
|
||||||
|
return g_ArenaGlobalHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Juliet
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -124,7 +124,7 @@ namespace Juliet::UnitTest
|
|||||||
char* secondCharArray = ArenaPushArray<char>(arena, 128);
|
char* secondCharArray = ArenaPushArray<char>(arena, 128);
|
||||||
char* thirdCharArray = ArenaPushArray<char>(arena, 128);
|
char* thirdCharArray = ArenaPushArray<char>(arena, 128);
|
||||||
|
|
||||||
secondCharArray = static_cast<char*>(ArenaReallocate(arena, secondCharArray, 128, 256, alignof(char), true));
|
secondCharArray = static_cast<char*>(ArenaReallocate(arena, secondCharArray, 128, 256, alignof(char), true JULIET_DEBUG_ONLY(, "ReallocChar")));
|
||||||
|
|
||||||
char* fourthCharArray = ArenaPushArray<char>(arena, 128);
|
char* fourthCharArray = ArenaPushArray<char>(arena, 128);
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ namespace Juliet::UnitTest
|
|||||||
MemSet(large, 'A', largeSize);
|
MemSet(large, 'A', largeSize);
|
||||||
|
|
||||||
size_t smallSize = 50;
|
size_t smallSize = 50;
|
||||||
char* smallData = static_cast<char*>(ArenaReallocate(arena, large, largeSize, smallSize, alignof(char), true));
|
char* smallData = static_cast<char*>(ArenaReallocate(arena, large, largeSize, smallSize, alignof(char), true JULIET_DEBUG_ONLY(, "ResizeSmall")));
|
||||||
|
|
||||||
for (size_t i = 0; i < smallSize; ++i)
|
for (size_t i = 0; i < smallSize; ++i)
|
||||||
{
|
{
|
||||||
@@ -154,7 +154,7 @@ namespace Juliet::UnitTest
|
|||||||
|
|
||||||
// Allocate next block (should be immediately after 'small')
|
// Allocate next block (should be immediately after 'small')
|
||||||
// Don't zero it, if overflow happened it will contain 'A's
|
// Don't zero it, if overflow happened it will contain 'A's
|
||||||
char* next = static_cast<char*>(ArenaPush(arena, 50, alignof(char), false));
|
char* next = static_cast<char*>(ArenaPush(arena, 50, alignof(char), false JULIET_DEBUG_ONLY(, "OverflowCheck")));
|
||||||
|
|
||||||
bool corrupted = false;
|
bool corrupted = false;
|
||||||
for(size_t i = 0; i < 50; ++i)
|
for(size_t i = 0; i < 50; ++i)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#include <Core/Common/String.h>
|
#include <Core/Common/String.h>
|
||||||
#include <Engine/Debug/MemoryDebugger.h>
|
#include <Engine/Debug/MemoryDebugger.h>
|
||||||
#include <imgui.h>
|
#include <Core/Common/String.h>
|
||||||
|
#include <Core/Memory/MemoryArena.h> // Added for Arena definition
|
||||||
|
#include <Engine/Debug/MemoryDebugger.h>
|
||||||
|
#include <Core/Container/Vector.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace Juliet::Debug
|
namespace Juliet::Debug
|
||||||
{
|
{
|
||||||
@@ -16,6 +18,49 @@ namespace Juliet::Debug
|
|||||||
bool ScrollListToSelected = false;
|
bool ScrollListToSelected = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
struct PagedArenaDebugState
|
||||||
|
{
|
||||||
|
float Zoom = 1.0f;
|
||||||
|
ArenaDebugInfo* SelectedAlloc = nullptr;
|
||||||
|
bool ScrollVisualToSelected = false;
|
||||||
|
bool ScrollListToSelected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PagedArenaStateEntry
|
||||||
|
{
|
||||||
|
const Arena* Arena;
|
||||||
|
PagedArenaDebugState State;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Static Vector for lookup
|
||||||
|
VectorArena<PagedArenaStateEntry> s_PagedArenaStates;
|
||||||
|
|
||||||
|
PagedArenaDebugState& GetPagedArenaState(const Arena* arena)
|
||||||
|
{
|
||||||
|
if (s_PagedArenaStates.Arena == nullptr)
|
||||||
|
{
|
||||||
|
s_PagedArenaStates.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& entry : s_PagedArenaStates)
|
||||||
|
{
|
||||||
|
if (entry.Arena == arena)
|
||||||
|
{
|
||||||
|
return entry.State;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PagedArenaStateEntry newEntry;
|
||||||
|
newEntry.Arena = arena;
|
||||||
|
// Default construct state
|
||||||
|
newEntry.State = PagedArenaDebugState();
|
||||||
|
|
||||||
|
s_PagedArenaStates.PushBack(newEntry);
|
||||||
|
return s_PagedArenaStates.Last()->State;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ArenaDebugState& GetState(const String& name)
|
ArenaDebugState& GetState(const String& name)
|
||||||
{
|
{
|
||||||
// Simple static map-like storage using standard map would be better but we minimize deps.
|
// Simple static map-like storage using standard map would be better but we minimize deps.
|
||||||
@@ -403,6 +448,164 @@ namespace Juliet::Debug
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
void DrawPagedArena(const Arena* arena)
|
||||||
|
{
|
||||||
|
ImGui::PushID(arena);
|
||||||
|
const char* name = (arena->Name && arena->Name[0] != '\0') ? arena->Name : "Unnamed Arena";
|
||||||
|
PagedArenaDebugState& state = GetPagedArenaState(arena);
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader(name, ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
|
{
|
||||||
|
// Collect Blocks (Pages) in order (Oldest -> Newest)
|
||||||
|
// Arena->Previous chain goes Newest -> Oldest.
|
||||||
|
// Collect Blocks (Pages) in order (Oldest -> Newest)
|
||||||
|
// Arena->Previous chain goes Newest -> Oldest.
|
||||||
|
static VectorArena<const Arena*> blocks;
|
||||||
|
if (blocks.Arena == nullptr)
|
||||||
|
{
|
||||||
|
blocks.Create();
|
||||||
|
}
|
||||||
|
blocks.Clear();
|
||||||
|
|
||||||
|
for (const Arena* curr = arena->Current; curr != nullptr; curr = curr->Previous)
|
||||||
|
{
|
||||||
|
blocks.PushBack(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse manually
|
||||||
|
size_t count = blocks.Count;
|
||||||
|
for (size_t i = 0; i < count / 2; ++i)
|
||||||
|
{
|
||||||
|
const Arena* temp = blocks[i];
|
||||||
|
blocks[i] = blocks[count - 1 - i];
|
||||||
|
blocks[count - 1 - i] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate Stats
|
||||||
|
size_t totalCapacity = 0;
|
||||||
|
size_t totalUsed = 0;
|
||||||
|
|
||||||
|
for (const Arena* blk : blocks)
|
||||||
|
{
|
||||||
|
totalCapacity += blk->Reserved;
|
||||||
|
totalUsed += blk->Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Used: %zu / %zu bytes (%zu pages)", totalUsed, totalCapacity, blocks.Size());
|
||||||
|
ImGui::SliderFloat("Zoom", &state.Zoom, 0.1f, 1000.0f, "%.2f");
|
||||||
|
|
||||||
|
// --- Visual View ---
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Visual Map");
|
||||||
|
|
||||||
|
float blockHeight = 24.0f;
|
||||||
|
float blockSpacing = 4.0f;
|
||||||
|
float requiredVisHeight = (float)blocks.Size() * (blockHeight + blockSpacing) + 30.0f;
|
||||||
|
// constrains
|
||||||
|
if (requiredVisHeight > 300.0f) requiredVisHeight = 300.0f;
|
||||||
|
if (requiredVisHeight < 50.0f) requiredVisHeight = 50.0f;
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("VisualMap", ImVec2(0, requiredVisHeight), true,
|
||||||
|
ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar))
|
||||||
|
{
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
float availWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
ImVec2 startPos = ImGui::GetCursorScreenPos();
|
||||||
|
|
||||||
|
float virtualWidth = availWidth * state.Zoom;
|
||||||
|
if (virtualWidth < availWidth) virtualWidth = availWidth;
|
||||||
|
ImGui::Dummy(ImVec2(virtualWidth, requiredVisHeight + 1.0f));
|
||||||
|
|
||||||
|
// Input Handling (Simple Pan/Zoom)
|
||||||
|
bool isMapHovered = ImGui::IsWindowHovered();
|
||||||
|
if (isMapHovered && ImGui::GetIO().MouseWheel != 0.0f)
|
||||||
|
{
|
||||||
|
state.Zoom += ImGui::GetIO().MouseWheel * 0.2f * state.Zoom;
|
||||||
|
if (state.Zoom < 0.1f) state.Zoom = 0.1f;
|
||||||
|
if (state.Zoom > 1000.0f) state.Zoom = 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 pos = startPos;
|
||||||
|
for (const Arena* blk : blocks)
|
||||||
|
{
|
||||||
|
ImGui::PushID(blk);
|
||||||
|
ImVec2 rectMin = pos;
|
||||||
|
ImVec2 rectMax = ImVec2(pos.x + virtualWidth, pos.y + blockHeight);
|
||||||
|
|
||||||
|
dl->AddRect(rectMin, rectMax, IM_COL32(100, 100, 100, 255)); // Block Border
|
||||||
|
|
||||||
|
size_t dataSize = blk->Reserved - k_ArenaHeaderSize; // Approximate
|
||||||
|
if (dataSize > 0)
|
||||||
|
{
|
||||||
|
double scale = (double)virtualWidth / (double)blk->Reserved;
|
||||||
|
|
||||||
|
// Draw Allocations
|
||||||
|
ArenaDebugInfo* info = blk->FirstDebugInfo;
|
||||||
|
while (info)
|
||||||
|
{
|
||||||
|
ImGui::PushID(info);
|
||||||
|
float xStart = pos.x + (float)((double)info->Offset * scale);
|
||||||
|
float width = (float)((double)info->Size * scale);
|
||||||
|
width = std::max(width, 1.0f);
|
||||||
|
|
||||||
|
ImVec2 aMin(xStart, pos.y + 1);
|
||||||
|
ImVec2 aMax(xStart + width, pos.y + blockHeight - 1);
|
||||||
|
|
||||||
|
ImU32 color = GetColorForTag(ConstString(info->Tag));
|
||||||
|
if (state.SelectedAlloc == info)
|
||||||
|
dl->AddRectFilled(aMin, aMax, IM_COL32_WHITE);
|
||||||
|
else
|
||||||
|
dl->AddRectFilled(aMin, aMax, color);
|
||||||
|
|
||||||
|
if (ImGui::IsMouseHoveringRect(aMin, aMax) && ImGui::IsWindowHovered())
|
||||||
|
{
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("%s", info->Tag);
|
||||||
|
ImGui::Text("Size: %zu", info->Size);
|
||||||
|
ImGui::Text("Offset: %zu", info->Offset);
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
|
||||||
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
|
||||||
|
{
|
||||||
|
state.SelectedAlloc = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info = info->Next;
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw Free Nodes (Holes) if Realloc is enabled
|
||||||
|
if (arena->AllowRealloc && blk->FreeNodes)
|
||||||
|
{
|
||||||
|
ArenaFreeNode* freeNode = blk->FreeNodes;
|
||||||
|
while(freeNode)
|
||||||
|
{
|
||||||
|
float xStart = pos.x + (float)((double)freeNode->Position * scale);
|
||||||
|
float width = (float)((double)freeNode->Size * scale);
|
||||||
|
|
||||||
|
ImVec2 fMin(xStart, pos.y + 1);
|
||||||
|
ImVec2 fMax(xStart + width, pos.y + blockHeight - 1);
|
||||||
|
|
||||||
|
dl->AddRectFilled(fMin, fMax, IM_COL32(50, 50, 50, 200));
|
||||||
|
|
||||||
|
freeNode = freeNode->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.y += blockHeight + blockSpacing;
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void DebugDrawMemoryArena()
|
void DebugDrawMemoryArena()
|
||||||
@@ -417,8 +620,35 @@ namespace Juliet::Debug
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ImGui::Begin("Memory Debugger"))
|
if (ImGui::Begin("Memory Debugger"))
|
||||||
|
{
|
||||||
|
if (ImGui::BeginTabBar("ArenaTabs"))
|
||||||
|
{
|
||||||
|
if (ImGui::BeginTabItem("Legacy Arenas"))
|
||||||
{
|
{
|
||||||
DrawMemoryArena(ConstString("Game Arena"), *GetGameArena(), s_ConfirmedHovered, frameHovered);
|
DrawMemoryArena(ConstString("Game Arena"), *GetGameArena(), s_ConfirmedHovered, frameHovered);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if JULIET_DEBUG
|
||||||
|
if (ImGui::BeginTabItem("Paged Arenas"))
|
||||||
|
{
|
||||||
|
Arena* head = GetGlobalArenaListHead();
|
||||||
|
if (!head)
|
||||||
|
{
|
||||||
|
ImGui::Text("No active paged arenas.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (Arena* a = head; a != nullptr; a = a->GlobalNext)
|
||||||
|
{
|
||||||
|
DrawPagedArena(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user