Converted log manager to use arenas

This commit is contained in:
2026-02-02 22:13:50 -05:00
parent 45ef1134f7
commit 4d66261c9f
11 changed files with 228 additions and 114 deletions

View File

@@ -18,7 +18,7 @@ namespace Juliet
NonNullPtr(Type* ptr) NonNullPtr(Type* ptr)
: InternalPtr(ptr) : InternalPtr(ptr)
{ {
Assert(ptr && "Tried to initialize a NonNullPtr with a null pointer"); Assert(ptr, "Tried to initialize a NonNullPtr with a null pointer");
} }
template <typename OtherType> template <typename OtherType>
@@ -26,13 +26,13 @@ namespace Juliet
NonNullPtr(const NonNullPtr<OtherType>& otherPtr) NonNullPtr(const NonNullPtr<OtherType>& otherPtr)
: InternalPtr(otherPtr.Get()) : InternalPtr(otherPtr.Get())
{ {
Assert(InternalPtr && "Fatal Error: Assigned a non null ptr using another NonNullPtr but its was null."); Assert(InternalPtr, "Fatal Error: Assigned a non null ptr using another NonNullPtr but its was null.");
} }
// Assignment // Assignment
NonNullPtr& operator=(Type* ptr) NonNullPtr& operator=(Type* ptr)
{ {
Assert(ptr && "Tried to assign a null pointer to a NonNullPtr!"); Assert(ptr, "Tried to assign a null pointer to a NonNullPtr!");
InternalPtr = ptr; InternalPtr = ptr;
return *this; return *this;
} }
@@ -48,25 +48,25 @@ namespace Juliet
// Accessors // Accessors
operator Type*() const operator Type*() const
{ {
Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null");
return InternalPtr; return InternalPtr;
} }
Type* Get() const Type* Get() const
{ {
Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null");
return InternalPtr; return InternalPtr;
} }
Type& operator*() const Type& operator*() const
{ {
Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null");
return *InternalPtr; return *InternalPtr;
} }
Type* operator->() const Type* operator->() const
{ {
Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null");
return InternalPtr; return InternalPtr;
} }

View File

@@ -1,10 +1,13 @@
#pragma once #pragma once
#include <Core/Common/NonNullPtr.h>
#include <Core/Math/MathUtils.h> #include <Core/Math/MathUtils.h>
#include <Core/Memory/Utils.h> #include <Core/Memory/Utils.h>
namespace Juliet namespace Juliet
{ {
struct Arena;
#define ConstString(str) { const_cast<char*>((str)), sizeof(str) - 1 } #define ConstString(str) { const_cast<char*>((str)), sizeof(str) - 1 }
#define CStr(str) ((str).Data) #define CStr(str) ((str).Data)
#define InplaceString(name, size) \ #define InplaceString(name, size) \
@@ -38,6 +41,20 @@ namespace Juliet
size_t Capacity; size_t Capacity;
}; };
struct StringListNode
{
StringListNode* Next;
String Content;
};
struct StringList
{
StringListNode* First;
StringListNode* Last;
size_t NodeCount;
size_t Size;
};
constexpr uint32 kInvalidUTF8 = 0xFFFD; constexpr uint32 kInvalidUTF8 = 0xFFFD;
inline size_t StringLength(String str) inline size_t StringLength(String str)
@@ -140,6 +157,7 @@ namespace Juliet
extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst, bool nullTerminate); extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst, bool nullTerminate);
extern JULIET_API bool ConvertString(String from, String to, String src, StringBuffer& dst, bool nullTerminate); extern JULIET_API bool ConvertString(String from, String to, String src, StringBuffer& dst, bool nullTerminate);
JULIET_API String StringCopy(NonNullPtr<Arena> arena, String str);
} // namespace Juliet } // namespace Juliet
#ifdef UNIT_TEST #ifdef UNIT_TEST

View File

@@ -1,54 +1,26 @@
#pragma once #pragma once
#include <Core/Common/CoreTypes.h> #include <Core/Common/CoreTypes.h>
#include <Core/Common/NonNullPtr.h>
#include <Core/Memory/MemoryArena.h>
#include <Juliet.h> #include <Juliet.h>
// TODO : Juliet strings // TODO : Juliet strings
#include <string>
// TODO Juliet Containers + Allocators... // TODO Juliet Containers + Allocators...
// TODO: Juliet chrono, because it prevents me from doing #define global static // TODO: Juliet chrono, because it prevents me from doing #define global static
#ifdef global
#undef global
#endif
#include <chrono>
#define global static
#include <deque>
namespace Juliet namespace Juliet
{ {
enum class LogLevel : uint8; enum class LogLevel : uint8;
enum class LogCategory : uint8; enum class LogCategory : uint8;
class LogManager final
{
public:
LogManager();
void Init();
void Shutdown();
bool GetIsInitialized() const { return IsInitialized; }
private:
struct Entry
{
std::string Value;
std::chrono::system_clock::time_point Time;
LogLevel Level;
LogCategory Category;
Entry(std::string& value, LogLevel level, LogCategory category);
};
std::deque<Entry> Entries;
bool IsInitialized : 1;
friend void Log(LogLevel level, LogCategory category, const char* fmt, va_list args);
void AddEntry(Entry&& entry);
static void OutputLog(Entry& entry);
};
extern void JULIET_API InitializeLogManager(); extern void JULIET_API InitializeLogManager();
extern void JULIET_API ShutdownLogManager(); extern void JULIET_API ShutdownLogManager();
extern void JULIET_API LogScopeBegin();
// TODO everything that happened in there to export them to file or something
extern void JULIET_API LogScopeEnd();
extern void JULIET_API Log(LogLevel level, LogCategory category, const char* fmt, ...); extern void JULIET_API Log(LogLevel level, LogCategory category, const char* fmt, ...);
extern void JULIET_API LogMessage(LogCategory category, const char* fmt, ...); extern void JULIET_API LogMessage(LogCategory category, const char* fmt, ...);
extern void JULIET_API LogWarning(LogCategory category, const char* fmt, ...); extern void JULIET_API LogWarning(LogCategory category, const char* fmt, ...);

View File

@@ -55,6 +55,17 @@ namespace Juliet
return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * 1, AlignOf(Type), true)); return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * 1, AlignOf(Type), true));
} }
// #define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0))
// #define push_array_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (1))
// #define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T)))
// #define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T)))
template <typename Type>
[[nodiscard]] Type* ArenaPushArray(NonNullPtr<Arena> arena, size_t count)
{
return static_cast<Type*>(ArenaPush(arena, sizeof(Type) * count, Max(8ull, AlignOf(Type)), true));
}
// --- Paged Memory Architecture --- // --- Paged Memory Architecture ---
struct ArenaAllocation struct ArenaAllocation
{ {

View File

@@ -19,7 +19,63 @@ namespace Juliet
return size ? *left - *right : 0; return size ? *left - *right : 0;
} }
// TODO: homemade versions // Single linked list
void SingleLinkedListPush(auto*& stackTop, auto* node)
{
node->Next = stackTop;
stackTop = node;
}
void SingleLinkedListPop(auto*& stackTop)
{
stackTop = stackTop->Next;
}
template <typename SingleLinkedListType>
struct SingleLinkedListNode
{
SingleLinkedListType* Next;
};
// Double linked list
template <typename QueueType, typename QueueTypeNode>
void Enqueue(QueueType& queue, QueueTypeNode* node)
{
if (queue.First == nullptr)
{
queue.First = queue.Last = node;
node->Next = nullptr;
}
else
{
queue.Last->Next = node, queue.Last = node;
node->Next = nullptr;
}
queue.Nodecount += 1;
}
template <typename QueueType>
struct QueueNode
{
QueueType* Next;
};
#define DECLARE_QUEUE(type) \
struct type##Queue \
{ \
type* First; \
type* Last; \
size_t Nodecount; \
size_t Size; \
};
#define SLLQueuePush(f, l, n) SLLQueuePush_NZ(0, f, l, n, next)
#define SLLQueuePush_NZ(nil, f, l, n, next) \
(CheckNil(nil, f) ? ((f) = (l) = (n), SetNil(nil, (n)->next)) : ((l)->next = (n), (l) = (n), SetNil(nil, (n)->next)))
// TODO: homemade versions
#define MemSet memset #define MemSet memset
#define MemCopy memcpy #define MemCopy memcpy

View File

@@ -2,6 +2,7 @@
#include <Core/Common/String.h> #include <Core/Common/String.h>
#include <Core/Logging/LogManager.h> #include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h> #include <Core/Logging/LogTypes.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Memory/Utils.h> #include <Core/Memory/Utils.h>
namespace Juliet namespace Juliet
@@ -487,4 +488,15 @@ namespace Juliet
return ConvertString(sourceFormat, destFormat, src, dst, nullTerminate); return ConvertString(sourceFormat, destFormat, src, dst, nullTerminate);
} }
String StringCopy(NonNullPtr<Arena> arena, String str)
{
String result;
result.Size = str.Size;
result.Data = ArenaPushArray<char>(arena, str.Size + 1);
MemCopy(result.Data, str.Data, str.Size);
result.Data[result.Size] = 0;
return result;
}
} // namespace Juliet } // namespace Juliet

View File

@@ -4,7 +4,6 @@
#include <Core/Memory/Allocator.h> #include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h> #include <Core/Memory/EngineArena.h>
#include <Core/Memory/MemoryArena.h> #include <Core/Memory/MemoryArena.h>
#include <format>
namespace Juliet namespace Juliet
{ {

View File

@@ -1,9 +1,13 @@
#include <chrono>
#include <queue>
#include <Core/HAL/Display/DisplayDevice.h> #include <Core/HAL/Display/DisplayDevice.h>
#include <Core/HAL/Event/SystemEvent.h> #include <Core/HAL/Event/SystemEvent.h>
#pragma push_macro("global")
#undef global
#include <chrono>
#pragma pop_macro("global")
#include <queue>
namespace Juliet namespace Juliet
{ {
namespace namespace

View File

@@ -2,10 +2,10 @@
#include <Core/Logging/LogTypes.h> #include <Core/Logging/LogTypes.h>
#include <chrono> #include <chrono>
#include <cstdarg>
// Begin Todo JULIET debug output // Begin Todo JULIET debug output
#include <cinttypes> #include <cinttypes>
#include <Core/Memory/MemoryArena.h>
#ifdef JULIET_WIN32 #ifdef JULIET_WIN32
#include <Core/HAL/Win32.h> #include <Core/HAL/Win32.h>
@@ -13,61 +13,59 @@
#endif #endif
// End Todo // End Todo
// TODO Juliet chrono
namespace Juliet namespace Juliet
{ {
namespace namespace
{ {
LogManager LogManagerSingleton; struct LogsEntry : public QueueNode<LogsEntry>
constexpr size_t kLogBufferSize = 1024;
} // namespace
LogManager::Entry::Entry(std::string& value, LogLevel level, LogCategory category)
: Value(value)
, Level(level)
, Category(category)
{ {
// TODO Juliet clock String Value;
Time = std::chrono::system_clock::now(); std::chrono::system_clock::time_point Time;
LogLevel Level;
LogCategory Category;
};
DECLARE_QUEUE(LogsEntry);
// A log scope accumulates log until end of scope.
// Can be used to accumulate then write in a log file each frame.
struct LogScope
{
LogScope* Next;
size_t Position;
LogsEntryQueue Entries;
};
// Logs contains the current scopes and the arena
struct Logs
{
Arena* Arena = nullptr;
LogScope* TopScope = nullptr;
};
Logs* ActiveLog = nullptr;
Logs* LogAllocate()
{
Arena* arena = ArenaAllocate();
Logs* logs = ArenaPushStruct<Logs>(arena);
logs->Arena = arena;
return logs;
} }
LogManager::LogManager() void LogRelease(NonNullPtr<Logs> logs)
: IsInitialized(false)
{ {
ArenaRelease(logs->Arena);
} }
void LogManager::Init() void OutputLog(LogsEntry& entry)
{
IsInitialized = true;
Log(LogLevel::Message, LogCategory::Engine, "Initializing Log Manager");
}
void LogManager::Shutdown()
{
Log(LogLevel::Message, LogCategory::Engine, "Shutting down Log Manager");
Entries.clear();
IsInitialized = false;
}
void LogManager::AddEntry(Entry&& entry)
{
if (Entries.size() >= kLogBufferSize)
{
Entries.pop_front();
}
Entries.push_back(std::move(entry));
}
void LogManager::OutputLog(Entry& entry)
{ {
// TODO Juliet Output io for each platform // TODO Juliet Output io for each platform
// {:%F} is YYYY-MM-DD, {:%T} is HH:MM:SS.ffffff // {:%F} is YYYY-MM-DD, {:%T} is HH:MM:SS.ffffff
std::string timestamp = std::format("[{:%F %T}] ", entry.Time); std::string timestamp = std::format("[{:%F %T}] ", entry.Time);
std::string fullMessage = timestamp + entry.Value + "\n"; std::string fullMessage = timestamp + CStr(entry.Value) + "\n";
#ifdef JULIET_WIN32 #ifdef JULIET_WIN32
OutputDebugStringA(fullMessage.c_str()); OutputDebugStringA(fullMessage.c_str());
@@ -75,6 +73,25 @@ namespace Juliet
printf("%s", fullMessage.c_str()); printf("%s", fullMessage.c_str());
} }
void PushLogEntry(NonNullPtr<Logs> logs, String value, LogLevel level, LogCategory category)
{
Arena* arena = logs->Arena;
String str = StringCopy(arena, value);
// Push onto the current scope
LogsEntry* newEntry = ArenaPushStruct<LogsEntry>(arena);
newEntry->Time = std::chrono::system_clock::now();
newEntry->Level = level;
newEntry->Category = category;
newEntry->Value = str;
Enqueue(logs->TopScope->Entries, newEntry);
logs->TopScope->Entries.Size += sizeof(LogsEntry) + str.Size;
OutputLog(*newEntry);
}
} // namespace
void InitializeLogManager() void InitializeLogManager()
{ {
#ifdef JULIET_WIN32 #ifdef JULIET_WIN32
@@ -82,12 +99,36 @@ namespace Juliet
SetConsoleCP(CP_UTF8); SetConsoleCP(CP_UTF8);
#endif #endif
LogManagerSingleton.Init(); ActiveLog = LogAllocate();
LogScopeBegin();
} }
void ShutdownLogManager() void ShutdownLogManager()
{ {
LogManagerSingleton.Shutdown(); LogScopeEnd();
LogRelease(ActiveLog);
}
void LogScopeBegin()
{
Assert(ActiveLog != nullptr);
size_t position = ArenaPos(ActiveLog->Arena);
LogScope* scope = ArenaPushStruct<LogScope>(ActiveLog->Arena);
scope->Position = position;
SingleLinkedListPush(ActiveLog->TopScope, scope);
}
void LogScopeEnd()
{
Assert(ActiveLog != nullptr);
LogScope* scope = ActiveLog->TopScope;
Assert(scope != nullptr);
SingleLinkedListPop(ActiveLog->TopScope);
ArenaPopTo(ActiveLog->Arena, scope->Position);
} }
void Log(LogLevel level, LogCategory category, const char* fmt, va_list args) void Log(LogLevel level, LogCategory category, const char* fmt, va_list args)
@@ -104,12 +145,12 @@ namespace Juliet
while (std::getline(ss, individualLine, '\n')) while (std::getline(ss, individualLine, '\n'))
{ {
LogManager::Entry entry(individualLine, level, category); // Logs can happen before the active log is set, in that case, we can write to the console but not register the entry.
LogManagerSingleton.OutputLog(entry); // One case is the Arena Allocate that calls logs and will happen before the logs are properly set because logs need to allocate an arena.
if (ActiveLog != nullptr)
if (LogManagerSingleton.GetIsInitialized())
{ {
LogManagerSingleton.AddEntry(std::move(entry)); String julietstr = WrapString(individualLine.c_str());
PushLogEntry(ActiveLog, julietstr, level, category);
} }
} }
} }

View File

@@ -140,18 +140,18 @@ namespace Juliet
void ArenaPopTo(NonNullPtr<Arena> arena, size_t position) void ArenaPopTo(NonNullPtr<Arena> arena, size_t position)
{ {
size_t bbbPosition = ClampBottom(k_ArenaHeaderSize, position); size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position);
Arena* current = arena->Current; Arena* current = arena->Current;
// TODO : Free list // TODO : Free list
for (Arena* previous = nullptr; current->BasePosition >= bbbPosition; current = previous) for (Arena* previous = nullptr; current->BasePosition >= clampedPosition; current = previous)
{ {
previous = current->Previous; previous = current->Previous;
Memory::OS_Release(current, current->Reserved); Memory::OS_Release(current, current->Reserved);
} }
arena->Current = current; arena->Current = current;
size_t newPosition = bbbPosition - current->BasePosition; size_t newPosition = clampedPosition - current->BasePosition;
Assert(newPosition <= current->Position); Assert(newPosition <= current->Position);
current->Position = newPosition; current->Position = newPosition;
} }

View File

@@ -58,8 +58,9 @@ namespace Juliet::D3D12::Internal
{ {
heap->Handle->Release(); heap->Handle->Release();
} }
SafeFree(heap->FreeIndices); // TODO: should not free because its on an arena, but need to use arena properly
Free(heap.Get()); // SafeFree(heap->FreeIndices);
// Free(heap.Get());
} }
bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor) bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor)