Reallocate-Arena #1

Merged
Patedam merged 7 commits from Reallocate-Arena into main 2026-02-14 11:24:30 -05:00
10 changed files with 164 additions and 119 deletions
Showing only changes of commit 0876ed8205 - Show all commits

View File

@@ -162,11 +162,12 @@ namespace Juliet
return (x + alignment - 1) & ~(alignment - 1); return (x + alignment - 1) & ~(alignment - 1);
} }
inline void Swap(auto* Restrict a, auto* Restrict b) template <typename T>
inline void Swap(T* Restrict a, T* Restrict b)
{ {
auto temp = *a; T temp = std::move(*a);
*a = *b; *a = std::move(*b);
*b = temp; *b = std::move(temp);
} }
// Move to another file dedicated to those // Move to another file dedicated to those

View File

@@ -3,75 +3,80 @@
#include <Core/Common/NonNullPtr.h> #include <Core/Common/NonNullPtr.h>
#include <Core/Memory/MemoryArena.h> #include <Core/Memory/MemoryArena.h>
#include <vector> #include <vector>
#include <WinSock2.h>
namespace Juliet namespace Juliet
{ {
template <typename Type> template <typename Type>
struct VectorArena struct VectorArena
{ {
public: void Create()
VectorArena()
: First(nullptr)
, Last(nullptr)
, Count(0)
, Stride(sizeof(Type))
{ {
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&) Type* entry = ArenaPushStruct<Type>(Arena);
: Arena(nullptr) *entry = value;
, First(nullptr)
, Last(nullptr) if (Count == 0)
, Count(0) {
, Stride(sizeof(Type)) DataFirst = entry;
{ }
Assert(false, "Copying VectorArena is not allowed"); 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) void PushBack(Type&& value)
{ {
Assert(Arena);
Type* entry = ArenaPushStruct<Type>(Arena); Type* entry = ArenaPushStruct<Type>(Arena);
*entry = std::move(value); *entry = std::move(value);
if (Count == 0) if (Count == 0)
{ {
First = entry; DataFirst = entry;
} }
Last = entry; DataLast = entry;
++Count; ++Count;
} }
void RemoveAtFast(index_t index) void RemoveAtFast(index_t index)
{ {
Assert(Arena);
Assert(index < Count); Assert(index < Count);
Assert(Count > 0); Assert(Count > 0);
Type* elementAdr = First + index; Type* elementAdr = DataFirst + index;
// Swap Last and element // Swap DataLast and element
Swap(Last, elementAdr); if (DataLast != elementAdr)
--Last; {
Swap(DataLast, elementAdr);
}
--DataLast;
--Count; --Count;
if (Count == 0) if (Count == 0)
{ {
First = Last = nullptr; DataFirst = DataLast = nullptr;
} }
ArenaPop(Arena, Stride); ArenaPop(Arena, Stride);
@@ -79,34 +84,36 @@ namespace Juliet
void Clear() void Clear()
{ {
Assert(Arena);
ArenaClear(Arena); ArenaClear(Arena);
First = Last = nullptr; DataFirst = DataLast = nullptr;
Count = 0; Count = 0;
} }
// C++ Accessors for loop supports and Index based access // C++ Accessors for loop supports and Index based access
Type& operator[](size_t index) { return First[index]; } Type& operator[](size_t index) { return DataFirst[index]; }
const Type& operator[](size_t index) const { return First[index]; } const Type& operator[](size_t index) const { return DataFirst[index]; }
Type* begin() { return First; } Type* begin() { return DataFirst; }
Type* end() { return First + Count; } Type* end() { return DataFirst + Count; }
const Type* begin() const { return First; } const Type* begin() const { return DataFirst; }
const Type* end() const { return First + Count; } const Type* end() const { return DataFirst + Count; }
Type* First() { return DataFirst; }
Type* Last() { return DataLast; }
size_t Size() const { return Count; } size_t Size() const { return Count; }
private:
Arena* Arena; Arena* Arena;
Type* First; Type* DataFirst;
Type* Last; Type* DataLast;
size_t Count; size_t Count;
size_t Stride; size_t Stride;
}; };
static_assert(std::is_standard_layout_v<VectorArena<int>>,
// TODO : Create my own Vector class based on https://github.com/niklas-ourmachinery/bitsquid-foundation/blob/master/collection_types.h "VectorArena must have a standard layout to remain POD-like.");
template <typename T> static_assert(std::is_trivially_copyable_v<VectorArena<int>>,
class Vector : public std::vector<T> "VectorArena must be trivially copyable (no custom destructors/assignment).");
{
};
} // namespace Juliet } // namespace Juliet

View File

@@ -5,6 +5,12 @@
namespace Juliet namespace Juliet
{ {
// TODO Use VectorArena
template <typename T>
class Vector : public std::vector<T>
{
};
class NetworkPacket class NetworkPacket
{ {
public: public:

View File

@@ -10,7 +10,9 @@ namespace Juliet
namespace namespace
{ {
DisplayDevice* g_CurrentDisplayDevice = nullptr; DisplayDevice* g_CurrentDisplayDevice = nullptr;
}
void DestroyPlatformWindow(index_t windowIndex);
} // namespace
namespace Internal::Display namespace Internal::Display
{ {
@@ -60,9 +62,9 @@ namespace Juliet
} }
// Destroy all Windows that are still alive // 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); g_CurrentDisplayDevice->Shutdown(g_CurrentDisplayDevice);
@@ -78,44 +80,59 @@ namespace Juliet
{ {
Assert(g_CurrentDisplayDevice->CreatePlatformWindow); Assert(g_CurrentDisplayDevice->CreatePlatformWindow);
auto* arena = g_CurrentDisplayDevice->Arena; Window window = {};
Assert(arena); window.Arena = ArenaAllocate();
window.Width = width;
window.Height = height;
auto* window = ArenaPushStruct<Window>(arena); window.Title = StringCopy(window.Arena, WrapString(title));
Assert(window);
window->Width = width; g_CurrentDisplayDevice->Windows.PushBack(window);
window->Height = height;
window->Title = StringCopy(arena, WrapString(title)); auto* pWindow = g_CurrentDisplayDevice->Windows.Last();
if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, pWindow))
g_CurrentDisplayDevice->MainWindow = window;
if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, window))
{ {
// Note: We don't "free" from arena easily, but since this is catastrophic ArenaRelease(window.Arena);
// and persistent, we just leak the small amount of arena space or handle it if we had a marker.
return nullptr; return nullptr;
} }
// TODO : make SHOW optional on creation with a flag // 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<Window>& 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> window) void DestroyPlatformWindow(NonNullPtr<Window> window)
{ {
Assert(g_CurrentDisplayDevice->MainWindow == window.Get()); // Find and destroy
VectorArena<Window>& windows = g_CurrentDisplayDevice->Windows;
HideWindow(window); for (index_t idx = windows.Size() - 1; idx != 0; --idx)
{
// TODO: Pop from arena. Window& windowRef = windows[idx];
window->Title.Data = nullptr; if (windowRef.ID == window->ID)
window->Title.Size = 0; {
DestroyPlatformWindow(idx);
g_CurrentDisplayDevice->DestroyPlatformWindow(g_CurrentDisplayDevice, window); break;
}
g_CurrentDisplayDevice->MainWindow = nullptr; }
} }
void ShowWindow(NonNullPtr<Window> window) void ShowWindow(NonNullPtr<Window> window)

View File

@@ -29,9 +29,7 @@ namespace Juliet
// Events // Events
void (*PumpEvents)(NonNullPtr<DisplayDevice> self); void (*PumpEvents)(NonNullPtr<DisplayDevice> self);
// TODO : Use vector VectorArena<Window> Windows;
VectorArena<Window*> Windows;
Window* MainWindow = nullptr;
}; };
struct DisplayDeviceFactory struct DisplayDeviceFactory

View File

@@ -1,9 +1,7 @@
#include <Core/HAL/Display/DisplayDevice.h> #include <Core/HAL/Display/DisplayDevice.h>
#include <Core/HAL/Display/Win32/Win32DisplayEvent.h> #include <Core/HAL/Display/Win32/Win32DisplayEvent.h>
#include <Core/HAL/Display/Win32/Win32Window.h> #include <Core/HAL/Display/Win32/Win32Window.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h> #include <Core/Memory/EngineArena.h>
#include <Core/Memory/Utils.h>
namespace Juliet::Win32 namespace Juliet::Win32
{ {
@@ -14,7 +12,10 @@ namespace Juliet::Win32
return true; return true;
} }
void Shutdown(NonNullPtr<DisplayDevice> /*self*/) {} void Shutdown(NonNullPtr<DisplayDevice> /*self*/) {}
void Free(NonNullPtr<DisplayDevice> /*self*/) {} void Free(NonNullPtr<DisplayDevice> device)
{
device->Windows.Destroy();
}
DisplayDevice* CreateDevice(Arena* arena) DisplayDevice* CreateDevice(Arena* arena)
{ {
@@ -36,6 +37,8 @@ namespace Juliet::Win32
device->PumpEvents = PumpEvents; device->PumpEvents = PumpEvents;
device->Windows.Create();
return device; return device;
} }
} // namespace } // namespace

View File

@@ -17,7 +17,6 @@
#include <imgui.h> // Need For IMGUI_IMPL_API #include <imgui.h> // Need For IMGUI_IMPL_API
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace Juliet::Win32 namespace Juliet::Win32
{ {
namespace namespace
@@ -29,17 +28,12 @@ namespace Juliet::Win32
{ {
if (auto* displayDevice = GetDisplayDevice()) if (auto* displayDevice = GetDisplayDevice())
{ {
auto* window = displayDevice->MainWindow; for (auto& window : displayDevice->Windows)
// TODO : use a vector
// for (Window* window : displayDevice->MainWindow)
{ {
if (window) auto state = static_cast<Window32State*>(window.State);
if (state && state->Handle == handle)
{ {
auto state = static_cast<Window32State*>(window->State); return state;
if (state && state->Handle == handle)
{
return state;
}
} }
} }
} }
@@ -158,7 +152,6 @@ namespace Juliet::Win32
LRESULT returnCode = -1; LRESULT returnCode = -1;
// Wait until the window state is created before doing anything // Wait until the window state is created before doing anything
auto* windowState = GetWindowStateFromHandle(handle); auto* windowState = GetWindowStateFromHandle(handle);
if (!windowState) if (!windowState)

View File

@@ -14,7 +14,7 @@ namespace Juliet::Win32
bool SetupWindowState(NonNullPtr<DisplayDevice> /*self*/, NonNullPtr<Window> window, HWND handle) bool SetupWindowState(NonNullPtr<DisplayDevice> /*self*/, NonNullPtr<Window> window, HWND handle)
{ {
auto state = ArenaPushType<Window32State>(GetEngineArena(), ConstString("Window32State")); auto* state = ArenaPushStruct<Window32State>(window->Arena);
window->State = state; window->State = state;
state->Handle = handle; state->Handle = handle;
@@ -23,18 +23,15 @@ namespace Juliet::Win32
state->IsMouseTracked = false; state->IsMouseTracked = false;
// TODO Use SetProp to associate data to the window handle. Could be used to fetch it from the WinProc
return true; return true;
} }
void CleanUpWindowState(NonNullPtr<DisplayDevice> /*self*/, NonNullPtr<Window> window) void CleanUpWindowState(NonNullPtr<DisplayDevice> /*self*/, NonNullPtr<Window> window)
{ {
if (auto* state = static_cast<Window32State*>(window->State)) auto* state = static_cast<Window32State*>(window->State);
{ Assert(state);
ReleaseDC(state->Handle, state->HDC); ReleaseDC(state->Handle, state->HDC);
DestroyWindow(state->Handle); DestroyWindow(state->Handle);
}
window->State = nullptr; window->State = nullptr;
} }
} // namespace } // namespace

View File

@@ -15,9 +15,10 @@ namespace Juliet
{ {
WindowID ID; WindowID ID;
WindowState* State; WindowState* State;
Arena* Arena;
int32 Width; int32 Width;
int32 Height; int32 Height;
String Title; String Title;
}; };
} // namespace Juliet } // namespace Juliet

View File

@@ -31,7 +31,8 @@ namespace Juliet
{ {
// Test 1: Integer VectorArena // Test 1: Integer VectorArena
{ {
VectorArena<int> vec; VectorArena<int> vec = {};
vec.Create();
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
vec.PushBack(10); vec.PushBack(10);
@@ -58,11 +59,14 @@ namespace Juliet
sum += val; sum += val;
} }
Assert(sum == 60); Assert(sum == 60);
vec.Destroy();
} }
// Test 2: RemoveAtFast // Test 2: RemoveAtFast
{ {
VectorArena<int> vec; VectorArena<int> vec = {};
vec.Create();
vec.PushBack(10); vec.PushBack(10);
vec.PushBack(20); vec.PushBack(20);
vec.PushBack(30); vec.PushBack(30);
@@ -84,11 +88,15 @@ namespace Juliet
// Remove remaining element // Remove remaining element
vec.RemoveAtFast(0); vec.RemoveAtFast(0);
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
vec.Destroy();
} }
// Test 3: Clear and Reuse // Test 3: Clear and Reuse
{ {
VectorArena<int> vec; VectorArena<int> vec = {};
vec.Create();
vec.PushBack(100); vec.PushBack(100);
vec.PushBack(200); vec.PushBack(200);
VerifyVectorState(vec, 2); VerifyVectorState(vec, 2);
@@ -100,6 +108,8 @@ namespace Juliet
VerifyVectorState(vec, 1); VerifyVectorState(vec, 1);
Assert(vec[0] == 300); Assert(vec[0] == 300);
Assert(*(vec.end() - 1) == 300); Assert(*(vec.end() - 1) == 300);
vec.Destroy();
} }
// Test 4: Struct VectorArena // Test 4: Struct VectorArena
@@ -116,7 +126,9 @@ namespace Juliet
} }
}; };
VectorArena<TestItem> vec; VectorArena<TestItem> vec = {};
vec.Create();
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
vec.PushBack({ 1, 1.0f }); vec.PushBack({ 1, 1.0f });
@@ -129,11 +141,15 @@ namespace Juliet
VerifyVectorState(vec, 2); VerifyVectorState(vec, 2);
Assert(vec[0] == (TestItem{ 1, 1.0f })); Assert(vec[0] == (TestItem{ 1, 1.0f }));
Assert(vec[1] == (TestItem{ 3, 3.0f })); Assert(vec[1] == (TestItem{ 3, 3.0f }));
vec.Destroy();
} }
// Test 5: Add 2, Remove 2 -> Expect nullptr // Test 5: Add 2, Remove 2 -> Expect nullptr
{ {
VectorArena<int> vec; VectorArena<int> vec = {};
vec.Create();
vec.PushBack(1); vec.PushBack(1);
vec.PushBack(2); vec.PushBack(2);
VerifyVectorState(vec, 2); VerifyVectorState(vec, 2);
@@ -141,11 +157,15 @@ namespace Juliet
vec.RemoveAtFast(0); vec.RemoveAtFast(0);
vec.RemoveAtFast(0); vec.RemoveAtFast(0);
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
vec.Destroy();
} }
// Test 6: Volume Test (100 items) // Test 6: Volume Test (100 items)
{ {
VectorArena<int> vec; VectorArena<int> vec = {};
vec.Create();
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
// Push 100 items // Push 100 items
@@ -170,6 +190,8 @@ namespace Juliet
vec.Clear(); vec.Clear();
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
vec.Destroy();
} }
} }
} // namespace Juliet } // namespace Juliet