diff --git a/Juliet/include/Core/Common/CoreUtils.h b/Juliet/include/Core/Common/CoreUtils.h index 4da2dba..4aa84e2 100644 --- a/Juliet/include/Core/Common/CoreUtils.h +++ b/Juliet/include/Core/Common/CoreUtils.h @@ -162,11 +162,12 @@ namespace Juliet return (x + alignment - 1) & ~(alignment - 1); } - inline void Swap(auto* Restrict a, auto* Restrict b) + template + inline void Swap(T* Restrict a, T* Restrict b) { - auto temp = *a; - *a = *b; - *b = temp; + T temp = std::move(*a); + *a = std::move(*b); + *b = std::move(temp); } // Move to another file dedicated to those diff --git a/Juliet/include/Core/Container/Vector.h b/Juliet/include/Core/Container/Vector.h index d065ad6..82b89fb 100644 --- a/Juliet/include/Core/Container/Vector.h +++ b/Juliet/include/Core/Container/Vector.h @@ -3,75 +3,80 @@ #include #include #include -#include namespace Juliet { template struct VectorArena { - public: - VectorArena() - : First(nullptr) - , Last(nullptr) - , Count(0) - , Stride(sizeof(Type)) + void Create() { - Arena = ArenaAllocate(); + Assert(!Arena); + + DataFirst = DataLast = nullptr; + Count = 0; + Stride = sizeof(Type); + Arena = ArenaAllocate(); + } + void Destroy() + { + ArenaRelease(Arena); + DataFirst = DataLast = nullptr; + Count = 0; + Stride = 0; + Arena = nullptr; } - ~VectorArena() { ArenaRelease(Arena); } + void PushBack(const Type& value) + { + Assert(Arena); - VectorArena(const VectorArena&) - : Arena(nullptr) - , First(nullptr) - , Last(nullptr) - , Count(0) - , Stride(sizeof(Type)) - { - Assert(false, "Copying VectorArena is not allowed"); + Type* entry = ArenaPushStruct(Arena); + *entry = value; + + if (Count == 0) + { + DataFirst = entry; + } + DataLast = entry; + ++Count; } - VectorArena(VectorArena&&) noexcept - : Arena(nullptr) - , First(nullptr) - , Last(nullptr) - , Count(0) - , Stride(sizeof(Type)) - { - Assert(false, "Moving VectorArena is not allowed"); - } - void operator=(const VectorArena&) { Assert(false, "Copying VectorArena is not allowed"); } - void operator=(VectorArena&&) noexcept { Assert(false, "Moving VectorArena is not allowed"); } void PushBack(Type&& value) { + Assert(Arena); + Type* entry = ArenaPushStruct(Arena); *entry = std::move(value); if (Count == 0) { - First = entry; + DataFirst = entry; } - Last = entry; - + DataLast = entry; ++Count; } void RemoveAtFast(index_t index) { + Assert(Arena); Assert(index < Count); Assert(Count > 0); - Type* elementAdr = First + index; + Type* elementAdr = DataFirst + index; - // Swap Last and element - Swap(Last, elementAdr); - --Last; + // Swap DataLast and element + if (DataLast != elementAdr) + { + Swap(DataLast, elementAdr); + } + + --DataLast; --Count; if (Count == 0) { - First = Last = nullptr; + DataFirst = DataLast = nullptr; } ArenaPop(Arena, Stride); @@ -79,34 +84,36 @@ namespace Juliet void Clear() { + Assert(Arena); + ArenaClear(Arena); - First = Last = nullptr; - Count = 0; + DataFirst = DataLast = nullptr; + Count = 0; } // C++ Accessors for loop supports and Index based access - Type& operator[](size_t index) { return First[index]; } - const Type& operator[](size_t index) const { return First[index]; } + Type& operator[](size_t index) { return DataFirst[index]; } + const Type& operator[](size_t index) const { return DataFirst[index]; } - Type* begin() { return First; } - Type* end() { return First + Count; } + Type* begin() { return DataFirst; } + Type* end() { return DataFirst + Count; } - const Type* begin() const { return First; } - const Type* end() const { return First + Count; } + const Type* begin() const { return DataFirst; } + const Type* end() const { return DataFirst + Count; } + + Type* First() { return DataFirst; } + Type* Last() { return DataLast; } size_t Size() const { return Count; } - private: Arena* Arena; - Type* First; - Type* Last; + Type* DataFirst; + Type* DataLast; size_t Count; size_t Stride; }; - - // TODO : Create my own Vector class based on https://github.com/niklas-ourmachinery/bitsquid-foundation/blob/master/collection_types.h - template - class Vector : public std::vector - { - }; + static_assert(std::is_standard_layout_v>, + "VectorArena must have a standard layout to remain POD-like."); + static_assert(std::is_trivially_copyable_v>, + "VectorArena must be trivially copyable (no custom destructors/assignment)."); } // namespace Juliet diff --git a/Juliet/include/Core/Networking/NetworkPacket.h b/Juliet/include/Core/Networking/NetworkPacket.h index e3d91f9..8d9f292 100644 --- a/Juliet/include/Core/Networking/NetworkPacket.h +++ b/Juliet/include/Core/Networking/NetworkPacket.h @@ -5,6 +5,12 @@ namespace Juliet { + // TODO Use VectorArena + template + class Vector : public std::vector + { + }; + class NetworkPacket { public: diff --git a/Juliet/src/Core/HAL/Display/Display.cpp b/Juliet/src/Core/HAL/Display/Display.cpp index 3e661db..1d8745e 100644 --- a/Juliet/src/Core/HAL/Display/Display.cpp +++ b/Juliet/src/Core/HAL/Display/Display.cpp @@ -10,7 +10,9 @@ namespace Juliet namespace { DisplayDevice* g_CurrentDisplayDevice = nullptr; - } + + void DestroyPlatformWindow(index_t windowIndex); + } // namespace namespace Internal::Display { @@ -60,9 +62,9 @@ namespace Juliet } // Destroy all Windows that are still alive - if (g_CurrentDisplayDevice->MainWindow) + for (index_t idx = g_CurrentDisplayDevice->Windows.Size(); idx-- > 0;) { - DestroyPlatformWindow(g_CurrentDisplayDevice->MainWindow); + DestroyPlatformWindow(idx); } g_CurrentDisplayDevice->Shutdown(g_CurrentDisplayDevice); @@ -78,44 +80,59 @@ namespace Juliet { Assert(g_CurrentDisplayDevice->CreatePlatformWindow); - auto* arena = g_CurrentDisplayDevice->Arena; - Assert(arena); + Window window = {}; + window.Arena = ArenaAllocate(); + window.Width = width; + window.Height = height; - auto* window = ArenaPushStruct(arena); - Assert(window); + window.Title = StringCopy(window.Arena, WrapString(title)); - window->Width = width; - window->Height = height; + g_CurrentDisplayDevice->Windows.PushBack(window); - window->Title = StringCopy(arena, WrapString(title)); - - g_CurrentDisplayDevice->MainWindow = window; - if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, window)) + auto* pWindow = g_CurrentDisplayDevice->Windows.Last(); + if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, pWindow)) { - // Note: We don't "free" from arena easily, but since this is catastrophic - // and persistent, we just leak the small amount of arena space or handle it if we had a marker. + ArenaRelease(window.Arena); return nullptr; } // TODO : make SHOW optional on creation with a flag - g_CurrentDisplayDevice->ShowWindow(g_CurrentDisplayDevice, window); + g_CurrentDisplayDevice->ShowWindow(g_CurrentDisplayDevice, pWindow); - return window; + return pWindow; } + namespace + { + void DestroyPlatformWindow(index_t windowIndex) + { + VectorArena& windows = g_CurrentDisplayDevice->Windows; + Window* window = &windows[windowIndex]; + + HideWindow(window); + + g_CurrentDisplayDevice->DestroyPlatformWindow(g_CurrentDisplayDevice, window); + + ArenaClear(window->Arena); + ArenaRelease(window->Arena); + + windows.RemoveAtFast(windowIndex); + } + } // namespace + void DestroyPlatformWindow(NonNullPtr window) { - Assert(g_CurrentDisplayDevice->MainWindow == window.Get()); - - HideWindow(window); - - // TODO: Pop from arena. - window->Title.Data = nullptr; - window->Title.Size = 0; - - g_CurrentDisplayDevice->DestroyPlatformWindow(g_CurrentDisplayDevice, window); - - g_CurrentDisplayDevice->MainWindow = nullptr; + // Find and destroy + VectorArena& windows = g_CurrentDisplayDevice->Windows; + for (index_t idx = windows.Size() - 1; idx != 0; --idx) + { + Window& windowRef = windows[idx]; + if (windowRef.ID == window->ID) + { + DestroyPlatformWindow(idx); + break; + } + } } void ShowWindow(NonNullPtr window) diff --git a/Juliet/src/Core/HAL/Display/DisplayDevice.h b/Juliet/src/Core/HAL/Display/DisplayDevice.h index 29323bd..7d9cd63 100644 --- a/Juliet/src/Core/HAL/Display/DisplayDevice.h +++ b/Juliet/src/Core/HAL/Display/DisplayDevice.h @@ -29,9 +29,7 @@ namespace Juliet // Events void (*PumpEvents)(NonNullPtr self); - // TODO : Use vector - VectorArena Windows; - Window* MainWindow = nullptr; + VectorArena Windows; }; struct DisplayDeviceFactory diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp index 295287f..f700464 100644 --- a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp +++ b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp @@ -1,9 +1,7 @@ #include #include #include -#include #include -#include namespace Juliet::Win32 { @@ -14,7 +12,10 @@ namespace Juliet::Win32 return true; } void Shutdown(NonNullPtr /*self*/) {} - void Free(NonNullPtr /*self*/) {} + void Free(NonNullPtr device) + { + device->Windows.Destroy(); + } DisplayDevice* CreateDevice(Arena* arena) { @@ -36,6 +37,8 @@ namespace Juliet::Win32 device->PumpEvents = PumpEvents; + device->Windows.Create(); + return device; } } // namespace diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp index 5b3be09..34fe64d 100644 --- a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp +++ b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp @@ -14,10 +14,9 @@ // For GET_X_LPARAM, GET_Y_LPARAM. #include -#include // Need For IMGUI_IMPL_API +#include // Need For IMGUI_IMPL_API extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - namespace Juliet::Win32 { namespace @@ -29,17 +28,12 @@ namespace Juliet::Win32 { if (auto* displayDevice = GetDisplayDevice()) { - auto* window = displayDevice->MainWindow; - // TODO : use a vector - // for (Window* window : displayDevice->MainWindow) + for (auto& window : displayDevice->Windows) { - if (window) + auto state = static_cast(window.State); + if (state && state->Handle == handle) { - auto state = static_cast(window->State); - if (state && state->Handle == handle) - { - return state; - } + return state; } } } @@ -158,7 +152,6 @@ namespace Juliet::Win32 LRESULT returnCode = -1; - // Wait until the window state is created before doing anything auto* windowState = GetWindowStateFromHandle(handle); if (!windowState) diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp index 2669002..aa3f7db 100644 --- a/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp +++ b/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp @@ -14,7 +14,7 @@ namespace Juliet::Win32 bool SetupWindowState(NonNullPtr /*self*/, NonNullPtr window, HWND handle) { - auto state = ArenaPushType(GetEngineArena(), ConstString("Window32State")); + auto* state = ArenaPushStruct(window->Arena); window->State = state; state->Handle = handle; @@ -23,18 +23,15 @@ namespace Juliet::Win32 state->IsMouseTracked = false; - // TODO Use SetProp to associate data to the window handle. Could be used to fetch it from the WinProc - return true; } void CleanUpWindowState(NonNullPtr /*self*/, NonNullPtr window) { - if (auto* state = static_cast(window->State)) - { - ReleaseDC(state->Handle, state->HDC); - DestroyWindow(state->Handle); - } + auto* state = static_cast(window->State); + Assert(state); + ReleaseDC(state->Handle, state->HDC); + DestroyWindow(state->Handle); window->State = nullptr; } } // namespace diff --git a/Juliet/src/Core/HAL/Display/Window.h b/Juliet/src/Core/HAL/Display/Window.h index bd844d6..32faa1e 100644 --- a/Juliet/src/Core/HAL/Display/Window.h +++ b/Juliet/src/Core/HAL/Display/Window.h @@ -15,9 +15,10 @@ namespace Juliet { WindowID ID; WindowState* State; + Arena* Arena; - int32 Width; - int32 Height; + int32 Width; + int32 Height; String Title; }; } // namespace Juliet diff --git a/Juliet/src/UnitTest/Container/VectorUnitTest.cpp b/Juliet/src/UnitTest/Container/VectorUnitTest.cpp index 3bc44aa..4cabdaf 100644 --- a/Juliet/src/UnitTest/Container/VectorUnitTest.cpp +++ b/Juliet/src/UnitTest/Container/VectorUnitTest.cpp @@ -31,7 +31,8 @@ namespace Juliet { // Test 1: Integer VectorArena { - VectorArena vec; + VectorArena vec = {}; + vec.Create(); VerifyVectorState(vec, 0); vec.PushBack(10); @@ -58,11 +59,14 @@ namespace Juliet sum += val; } Assert(sum == 60); + vec.Destroy(); } // Test 2: RemoveAtFast { - VectorArena vec; + VectorArena vec = {}; + vec.Create(); + vec.PushBack(10); vec.PushBack(20); vec.PushBack(30); @@ -84,11 +88,15 @@ namespace Juliet // Remove remaining element vec.RemoveAtFast(0); VerifyVectorState(vec, 0); + + vec.Destroy(); } // Test 3: Clear and Reuse { - VectorArena vec; + VectorArena vec = {}; + vec.Create(); + vec.PushBack(100); vec.PushBack(200); VerifyVectorState(vec, 2); @@ -100,6 +108,8 @@ namespace Juliet VerifyVectorState(vec, 1); Assert(vec[0] == 300); Assert(*(vec.end() - 1) == 300); + + vec.Destroy(); } // Test 4: Struct VectorArena @@ -116,7 +126,9 @@ namespace Juliet } }; - VectorArena vec; + VectorArena vec = {}; + vec.Create(); + VerifyVectorState(vec, 0); vec.PushBack({ 1, 1.0f }); @@ -129,11 +141,15 @@ namespace Juliet VerifyVectorState(vec, 2); Assert(vec[0] == (TestItem{ 1, 1.0f })); Assert(vec[1] == (TestItem{ 3, 3.0f })); + + vec.Destroy(); } // Test 5: Add 2, Remove 2 -> Expect nullptr { - VectorArena vec; + VectorArena vec = {}; + vec.Create(); + vec.PushBack(1); vec.PushBack(2); VerifyVectorState(vec, 2); @@ -141,11 +157,15 @@ namespace Juliet vec.RemoveAtFast(0); vec.RemoveAtFast(0); VerifyVectorState(vec, 0); + + vec.Destroy(); } // Test 6: Volume Test (100 items) { - VectorArena vec; + VectorArena vec = {}; + vec.Create(); + VerifyVectorState(vec, 0); // Push 100 items @@ -170,6 +190,8 @@ namespace Juliet vec.Clear(); VerifyVectorState(vec, 0); + + vec.Destroy(); } } } // namespace Juliet