diff --git a/Juliet/include/Core/Common/NonNullPtr.h b/Juliet/include/Core/Common/NonNullPtr.h index c701c75..bc6a7b6 100644 --- a/Juliet/include/Core/Common/NonNullPtr.h +++ b/Juliet/include/Core/Common/NonNullPtr.h @@ -18,7 +18,7 @@ namespace Juliet NonNullPtr(Type* 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 @@ -26,13 +26,13 @@ namespace Juliet NonNullPtr(const NonNullPtr& otherPtr) : 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 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; return *this; } @@ -48,25 +48,25 @@ namespace Juliet // Accessors operator Type*() const { - Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); + Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null"); return InternalPtr; } Type* Get() const { - Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); + Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null"); return InternalPtr; } Type& operator*() const { - Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); + Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null"); return *InternalPtr; } Type* operator->() const { - Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null"); + Assert(InternalPtr, "NonNullPtr: Internal Pointer is Null"); return InternalPtr; } diff --git a/Juliet/include/Core/Common/String.h b/Juliet/include/Core/Common/String.h index 3914566..ec18cef 100644 --- a/Juliet/include/Core/Common/String.h +++ b/Juliet/include/Core/Common/String.h @@ -1,10 +1,13 @@ #pragma once +#include #include #include namespace Juliet { + struct Arena; + #define ConstString(str) { const_cast((str)), sizeof(str) - 1 } #define CStr(str) ((str).Data) #define InplaceString(name, size) \ @@ -38,6 +41,20 @@ namespace Juliet 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; 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(String from, String to, String src, StringBuffer& dst, bool nullTerminate); + JULIET_API String StringCopy(NonNullPtr arena, String str); } // namespace Juliet #ifdef UNIT_TEST diff --git a/Juliet/include/Core/Logging/LogManager.h b/Juliet/include/Core/Logging/LogManager.h index edb2ee0..d965ab8 100644 --- a/Juliet/include/Core/Logging/LogManager.h +++ b/Juliet/include/Core/Logging/LogManager.h @@ -1,54 +1,26 @@ #pragma once #include +#include +#include #include // TODO : Juliet strings -#include // TODO Juliet Containers + Allocators... // TODO: Juliet chrono, because it prevents me from doing #define global static -#ifdef global -#undef global -#endif -#include -#define global static - -#include namespace Juliet { enum class LogLevel : 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 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 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 LogMessage(LogCategory category, const char* fmt, ...); extern void JULIET_API LogWarning(LogCategory category, const char* fmt, ...); diff --git a/Juliet/include/Core/Memory/MemoryArena.h b/Juliet/include/Core/Memory/MemoryArena.h index f89c5c8..533e0ae 100644 --- a/Juliet/include/Core/Memory/MemoryArena.h +++ b/Juliet/include/Core/Memory/MemoryArena.h @@ -55,6 +55,17 @@ namespace Juliet return static_cast(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 + [[nodiscard]] Type* ArenaPushArray(NonNullPtr arena, size_t count) + { + return static_cast(ArenaPush(arena, sizeof(Type) * count, Max(8ull, AlignOf(Type)), true)); + } + // --- Paged Memory Architecture --- struct ArenaAllocation { diff --git a/Juliet/include/Core/Memory/Utils.h b/Juliet/include/Core/Memory/Utils.h index 01f438d..98f7677 100644 --- a/Juliet/include/Core/Memory/Utils.h +++ b/Juliet/include/Core/Memory/Utils.h @@ -19,7 +19,63 @@ namespace Juliet 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 + struct SingleLinkedListNode + { + SingleLinkedListType* Next; + }; + + // Double linked list + template + 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 + 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 MemCopy memcpy diff --git a/Juliet/src/Core/Common/String.cpp b/Juliet/src/Core/Common/String.cpp index 7b55149..1df1983 100644 --- a/Juliet/src/Core/Common/String.cpp +++ b/Juliet/src/Core/Common/String.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include namespace Juliet @@ -222,14 +223,14 @@ namespace Juliet while (true) { { - uint32 leftFolded[3]; - [[maybe_unused]] int8 num_folded = CaseFoldUnicode(StepUTF8(str1), leftFolded); + uint32 leftFolded[3]; + [[maybe_unused]] int8 num_folded = CaseFoldUnicode(StepUTF8(str1), leftFolded); Assert(num_folded == 1); // Only one uint32 codepoint supported for now (low ascii) left = leftFolded[0]; } { - uint32 rightFolded[3]; - [[maybe_unused]] int8 num_folded = CaseFoldUnicode(StepUTF8(str2), rightFolded); + uint32 rightFolded[3]; + [[maybe_unused]] int8 num_folded = CaseFoldUnicode(StepUTF8(str2), rightFolded); Assert(num_folded == 1); // Only one uint32 codepoint supported for now (low ascii) right = rightFolded[0]; } @@ -487,4 +488,15 @@ namespace Juliet return ConvertString(sourceFormat, destFormat, src, dst, nullTerminate); } + + String StringCopy(NonNullPtr arena, String str) + { + String result; + result.Size = str.Size; + result.Data = ArenaPushArray(arena, str.Size + 1); + MemCopy(result.Data, str.Data, str.Size); + result.Data[result.Size] = 0; + return result; + } + } // namespace Juliet diff --git a/Juliet/src/Core/HAL/Display/Display.cpp b/Juliet/src/Core/HAL/Display/Display.cpp index 406e0fd..35c3bd7 100644 --- a/Juliet/src/Core/HAL/Display/Display.cpp +++ b/Juliet/src/Core/HAL/Display/Display.cpp @@ -4,7 +4,6 @@ #include #include #include -#include namespace Juliet { diff --git a/Juliet/src/Core/HAL/Event/SystemEvent.cpp b/Juliet/src/Core/HAL/Event/SystemEvent.cpp index 771ceca..b0aa16c 100644 --- a/Juliet/src/Core/HAL/Event/SystemEvent.cpp +++ b/Juliet/src/Core/HAL/Event/SystemEvent.cpp @@ -1,9 +1,13 @@ -#include -#include - #include #include +#pragma push_macro("global") +#undef global +#include +#pragma pop_macro("global") + +#include + namespace Juliet { namespace diff --git a/Juliet/src/Core/Logging/LogManager.cpp b/Juliet/src/Core/Logging/LogManager.cpp index 56c311e..ff48b2a 100644 --- a/Juliet/src/Core/Logging/LogManager.cpp +++ b/Juliet/src/Core/Logging/LogManager.cpp @@ -2,10 +2,10 @@ #include #include -#include // Begin Todo JULIET debug output #include +#include #ifdef JULIET_WIN32 #include @@ -13,67 +13,84 @@ #endif // End Todo +// TODO Juliet chrono + namespace Juliet { namespace { - LogManager LogManagerSingleton; - 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 - Time = std::chrono::system_clock::now(); - } - - LogManager::LogManager() - : IsInitialized(false) - { - } - - void LogManager::Init() - { - 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) + struct LogsEntry : public QueueNode { - Entries.pop_front(); + String Value; + 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(arena); + logs->Arena = arena; + return logs; } - Entries.push_back(std::move(entry)); - } + void LogRelease(NonNullPtr logs) + { + ArenaRelease(logs->Arena); + } - void LogManager::OutputLog(Entry& entry) - { - // TODO Juliet Output io for each platform + void OutputLog(LogsEntry& entry) + { + // TODO Juliet Output io for each platform - // {:%F} is YYYY-MM-DD, {:%T} is HH:MM:SS.ffffff - std::string timestamp = std::format("[{:%F %T}] ", entry.Time); - std::string fullMessage = timestamp + entry.Value + "\n"; + // {:%F} is YYYY-MM-DD, {:%T} is HH:MM:SS.ffffff + std::string timestamp = std::format("[{:%F %T}] ", entry.Time); + std::string fullMessage = timestamp + CStr(entry.Value) + "\n"; #ifdef JULIET_WIN32 - OutputDebugStringA(fullMessage.c_str()); + OutputDebugStringA(fullMessage.c_str()); #endif - printf("%s", fullMessage.c_str()); - } + printf("%s", fullMessage.c_str()); + } + + void PushLogEntry(NonNullPtr logs, String value, LogLevel level, LogCategory category) + { + Arena* arena = logs->Arena; + String str = StringCopy(arena, value); + + // Push onto the current scope + LogsEntry* newEntry = ArenaPushStruct(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() { @@ -82,12 +99,36 @@ namespace Juliet SetConsoleCP(CP_UTF8); #endif - LogManagerSingleton.Init(); + ActiveLog = LogAllocate(); + LogScopeBegin(); } void ShutdownLogManager() { - LogManagerSingleton.Shutdown(); + LogScopeEnd(); + LogRelease(ActiveLog); + } + + void LogScopeBegin() + { + Assert(ActiveLog != nullptr); + + size_t position = ArenaPos(ActiveLog->Arena); + LogScope* scope = ArenaPushStruct(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) @@ -104,12 +145,12 @@ namespace Juliet while (std::getline(ss, individualLine, '\n')) { - LogManager::Entry entry(individualLine, level, category); - LogManagerSingleton.OutputLog(entry); - - if (LogManagerSingleton.GetIsInitialized()) + // Logs can happen before the active log is set, in that case, we can write to the console but not register the 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) { - LogManagerSingleton.AddEntry(std::move(entry)); + String julietstr = WrapString(individualLine.c_str()); + PushLogEntry(ActiveLog, julietstr, level, category); } } } diff --git a/Juliet/src/Core/Memory/MemoryArena.cpp b/Juliet/src/Core/Memory/MemoryArena.cpp index 12a0707..c370df1 100644 --- a/Juliet/src/Core/Memory/MemoryArena.cpp +++ b/Juliet/src/Core/Memory/MemoryArena.cpp @@ -140,18 +140,18 @@ namespace Juliet void ArenaPopTo(NonNullPtr arena, size_t position) { - size_t bbbPosition = ClampBottom(k_ArenaHeaderSize, position); - Arena* current = arena->Current; + size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position); + Arena* current = arena->Current; // TODO : Free list - for (Arena* previous = nullptr; current->BasePosition >= bbbPosition; current = previous) + for (Arena* previous = nullptr; current->BasePosition >= clampedPosition; current = previous) { previous = current->Previous; Memory::OS_Release(current, current->Reserved); } arena->Current = current; - size_t newPosition = bbbPosition - current->BasePosition; + size_t newPosition = clampedPosition - current->BasePosition; Assert(newPosition <= current->Position); current->Position = newPosition; } diff --git a/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp b/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp index 05fa2d2..05921ab 100644 --- a/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12DescriptorHeap.cpp @@ -58,8 +58,9 @@ namespace Juliet::D3D12::Internal { heap->Handle->Release(); } - SafeFree(heap->FreeIndices); - Free(heap.Get()); + // TODO: should not free because its on an arena, but need to use arena properly + // SafeFree(heap->FreeIndices); + // Free(heap.Get()); } bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor)