More memory arena unit test
Starting to converting the display system to the new memory arena.
This commit is contained in:
@@ -29,6 +29,8 @@ namespace Juliet
|
|||||||
|
|
||||||
uint64 Committed;
|
uint64 Committed;
|
||||||
uint64 Reserved;
|
uint64 Reserved;
|
||||||
|
|
||||||
|
Arena* FreeLast;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
|
static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,19 @@ namespace Juliet
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Single linked list
|
// Single linked list
|
||||||
void SingleLinkedListPush(auto*& stackTop, auto* node)
|
void SingleLinkedListPushNext(auto*& stackTop, auto* node)
|
||||||
{
|
{
|
||||||
node->Next = stackTop;
|
node->Next = stackTop;
|
||||||
stackTop = node;
|
stackTop = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleLinkedListPop(auto*& stackTop)
|
void SingleLinkedListPushPrevious(auto*& stackTop, auto* node)
|
||||||
|
{
|
||||||
|
node->Previous = stackTop;
|
||||||
|
stackTop = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleLinkedListPopNext(auto*& stackTop)
|
||||||
{
|
{
|
||||||
stackTop = stackTop->Next;
|
stackTop = stackTop->Next;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,15 @@ namespace Juliet
|
|||||||
{
|
{
|
||||||
Assert(!g_CurrentDisplayDevice);
|
Assert(!g_CurrentDisplayDevice);
|
||||||
|
|
||||||
|
Arena* arena = ArenaAllocate();
|
||||||
|
|
||||||
DisplayDevice* candidateDevice = nullptr;
|
DisplayDevice* candidateDevice = nullptr;
|
||||||
DisplayDeviceFactory* candidateFactory = nullptr;
|
DisplayDeviceFactory* candidateFactory = nullptr;
|
||||||
for (DisplayDeviceFactory* factory : Internal::Display::Factories)
|
for (DisplayDeviceFactory* factory : Internal::Display::Factories)
|
||||||
{
|
{
|
||||||
if (factory)
|
if (factory)
|
||||||
{
|
{
|
||||||
candidateDevice = factory->CreateDevice();
|
candidateDevice = factory->CreateDevice(arena);
|
||||||
if (candidateDevice)
|
if (candidateDevice)
|
||||||
{
|
{
|
||||||
candidateFactory = factory;
|
candidateFactory = factory;
|
||||||
@@ -41,6 +43,7 @@ namespace Juliet
|
|||||||
Assert(candidateDevice);
|
Assert(candidateDevice);
|
||||||
|
|
||||||
g_CurrentDisplayDevice = candidateDevice;
|
g_CurrentDisplayDevice = candidateDevice;
|
||||||
|
g_CurrentDisplayDevice->Arena = arena;
|
||||||
g_CurrentDisplayDevice->Name = candidateFactory->Name;
|
g_CurrentDisplayDevice->Name = candidateFactory->Name;
|
||||||
|
|
||||||
if (!g_CurrentDisplayDevice->Initialize(g_CurrentDisplayDevice))
|
if (!g_CurrentDisplayDevice->Initialize(g_CurrentDisplayDevice))
|
||||||
@@ -66,6 +69,8 @@ namespace Juliet
|
|||||||
// Free anything that was freed by the shutdown and then free the display
|
// Free anything that was freed by the shutdown and then free the display
|
||||||
// no op for now
|
// no op for now
|
||||||
g_CurrentDisplayDevice->Free(g_CurrentDisplayDevice);
|
g_CurrentDisplayDevice->Free(g_CurrentDisplayDevice);
|
||||||
|
|
||||||
|
ArenaRelease(g_CurrentDisplayDevice->Arena);
|
||||||
g_CurrentDisplayDevice = nullptr;
|
g_CurrentDisplayDevice = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,21 +78,16 @@ namespace Juliet
|
|||||||
{
|
{
|
||||||
Assert(g_CurrentDisplayDevice->CreatePlatformWindow);
|
Assert(g_CurrentDisplayDevice->CreatePlatformWindow);
|
||||||
|
|
||||||
MemoryArena* arena = GetEngineArena();
|
auto* arena = g_CurrentDisplayDevice->Arena;
|
||||||
auto window = ArenaPushType<Window>(arena, ConstString("Window"));
|
Assert(arena);
|
||||||
if (!window)
|
|
||||||
{
|
auto* window = ArenaPushStruct<Window>(arena);
|
||||||
return nullptr;
|
Assert(window);
|
||||||
}
|
|
||||||
window->Width = width;
|
window->Width = width;
|
||||||
window->Height = height;
|
window->Height = height;
|
||||||
|
|
||||||
auto titleLen = StringLength(title);
|
window->Title = StringCopy(arena, WrapString(title));
|
||||||
auto buffer = ArenaPushArray<char>(arena, titleLen, ConstString("Window Title Array"));
|
|
||||||
MemCopy(buffer, title, titleLen);
|
|
||||||
|
|
||||||
window->Title.Data = buffer;
|
|
||||||
window->Title.Size = titleLen;
|
|
||||||
|
|
||||||
g_CurrentDisplayDevice->MainWindow = window;
|
g_CurrentDisplayDevice->MainWindow = window;
|
||||||
if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, window))
|
if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, window))
|
||||||
@@ -109,7 +109,7 @@ namespace Juliet
|
|||||||
|
|
||||||
HideWindow(window);
|
HideWindow(window);
|
||||||
|
|
||||||
// We don't free from arena, these are persistent until shutdown.
|
// TODO: Pop from arena.
|
||||||
window->Title.Data = nullptr;
|
window->Title.Data = nullptr;
|
||||||
window->Title.Size = 0;
|
window->Title.Size = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace Juliet
|
|||||||
// Acts as a singleton after Initialize has been called and is freed in Shutdown.
|
// Acts as a singleton after Initialize has been called and is freed in Shutdown.
|
||||||
struct DisplayDevice
|
struct DisplayDevice
|
||||||
{
|
{
|
||||||
|
Arena* Arena;
|
||||||
|
|
||||||
const char* Name = "Unknown";
|
const char* Name = "Unknown";
|
||||||
|
|
||||||
// Initialize all subsystems needed for the device to works
|
// Initialize all subsystems needed for the device to works
|
||||||
@@ -33,7 +35,7 @@ namespace Juliet
|
|||||||
struct DisplayDeviceFactory
|
struct DisplayDeviceFactory
|
||||||
{
|
{
|
||||||
const char* Name = "Unknown";
|
const char* Name = "Unknown";
|
||||||
DisplayDevice* (*CreateDevice)(void);
|
DisplayDevice* (*CreateDevice)(Arena* arena);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO : Support more platforms
|
// TODO : Support more platforms
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ namespace Juliet::Win32
|
|||||||
void Shutdown(NonNullPtr<DisplayDevice> /*self*/) {}
|
void Shutdown(NonNullPtr<DisplayDevice> /*self*/) {}
|
||||||
void Free(NonNullPtr<DisplayDevice> /*self*/) {}
|
void Free(NonNullPtr<DisplayDevice> /*self*/) {}
|
||||||
|
|
||||||
DisplayDevice* CreateDevice()
|
DisplayDevice* CreateDevice(Arena* arena)
|
||||||
{
|
{
|
||||||
auto device = ArenaPushType<DisplayDevice>(GetEngineArena(), ConstString("DisplayDevice"));
|
auto device = ArenaPushStruct<DisplayDevice>(arena);
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ namespace Juliet::Win32
|
|||||||
|
|
||||||
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
|
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
|
||||||
const int w = window->Width, h = window->Height;
|
const int w = window->Width, h = window->Height;
|
||||||
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, "JULIET TODO PASS TITLE", style, x, y, w, h, nullptr,
|
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, window->Title.Data, style, x, y, w, h, nullptr, nullptr,
|
||||||
nullptr, instance, nullptr);
|
instance, nullptr);
|
||||||
|
|
||||||
PumpEvents(self);
|
PumpEvents(self);
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ namespace Juliet
|
|||||||
size_t position = ArenaPos(ActiveLog->Arena);
|
size_t position = ArenaPos(ActiveLog->Arena);
|
||||||
LogScope* scope = ArenaPushStruct<LogScope>(ActiveLog->Arena);
|
LogScope* scope = ArenaPushStruct<LogScope>(ActiveLog->Arena);
|
||||||
scope->Position = position;
|
scope->Position = position;
|
||||||
SingleLinkedListPush(ActiveLog->TopScope, scope);
|
SingleLinkedListPushNext(ActiveLog->TopScope, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogScopeEnd()
|
void LogScopeEnd()
|
||||||
@@ -126,7 +126,7 @@ namespace Juliet
|
|||||||
LogScope* scope = ActiveLog->TopScope;
|
LogScope* scope = ActiveLog->TopScope;
|
||||||
Assert(scope != nullptr);
|
Assert(scope != nullptr);
|
||||||
|
|
||||||
SingleLinkedListPop(ActiveLog->TopScope);
|
SingleLinkedListPopNext(ActiveLog->TopScope);
|
||||||
|
|
||||||
ArenaPopTo(ActiveLog->Arena, scope->Position);
|
ArenaPopTo(ActiveLog->Arena, scope->Position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
namespace Juliet
|
namespace Juliet
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO Get page size from os kernel call (dwPageSize)
|
// TODO Get page size from os kernel call (dwPageSize)
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -67,7 +66,25 @@ namespace Juliet
|
|||||||
if (current->Reserved < positionPostPush /* flags : chaining allowed */)
|
if (current->Reserved < positionPostPush /* flags : chaining allowed */)
|
||||||
{
|
{
|
||||||
Arena* newBlock = nullptr;
|
Arena* newBlock = nullptr;
|
||||||
// TODO : use the free list
|
{
|
||||||
|
Arena* prev_block;
|
||||||
|
for (newBlock = arena->FreeLast, prev_block = nullptr; newBlock != nullptr;
|
||||||
|
prev_block = newBlock, newBlock = newBlock->Previous)
|
||||||
|
{
|
||||||
|
if (newBlock->Reserved >= AlignPow2(newBlock->Position, align) + size)
|
||||||
|
{
|
||||||
|
if (prev_block)
|
||||||
|
{
|
||||||
|
prev_block->Previous = newBlock->Previous;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arena->FreeLast = newBlock->Previous;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (newBlock == nullptr)
|
if (newBlock == nullptr)
|
||||||
{
|
{
|
||||||
@@ -83,13 +100,7 @@ namespace Juliet
|
|||||||
}
|
}
|
||||||
|
|
||||||
newBlock->BasePosition = current->BasePosition + current->Reserved;
|
newBlock->BasePosition = current->BasePosition + current->Reserved;
|
||||||
// Push on the linkedlist
|
SingleLinkedListPushPrevious(arena->Current, newBlock);
|
||||||
newBlock->Previous = arena->Current;
|
|
||||||
arena->Current = newBlock;
|
|
||||||
|
|
||||||
// TODO: Think about ryan fleur way to push:
|
|
||||||
// SLLStackPush_N(arena->current, new_block, prev);
|
|
||||||
// #define SLLStackPush_N(f,n,next) ((n)->next=(f), (f)=(n))
|
|
||||||
|
|
||||||
current = newBlock;
|
current = newBlock;
|
||||||
positionPrePush = AlignPow2(current->Position, align);
|
positionPrePush = AlignPow2(current->Position, align);
|
||||||
@@ -108,11 +119,11 @@ namespace Juliet
|
|||||||
size_t commitPostAligned = positionPostPush + current->CommitSize - 1;
|
size_t commitPostAligned = positionPostPush + current->CommitSize - 1;
|
||||||
commitPostAligned -= commitPostAligned % current->CommitSize;
|
commitPostAligned -= commitPostAligned % current->CommitSize;
|
||||||
size_t commitPostClamped = ClampTop(commitPostAligned, current->Reserved);
|
size_t commitPostClamped = ClampTop(commitPostAligned, current->Reserved);
|
||||||
// size_t commitSize = commitPostClamped - current->Committed;
|
size_t commitSize = commitPostClamped - current->Committed;
|
||||||
// Byte* commitPtr = reinterpret_cast<Byte*>(current) + current->Committed;
|
Byte* commitPtr = reinterpret_cast<Byte*>(current) + current->Committed;
|
||||||
|
|
||||||
// TODO os_commit / commit_large
|
// TODO commit_large
|
||||||
// os_commit(commitPtr, commitSize);
|
Memory::OS_Commit(commitPtr, commitSize);
|
||||||
|
|
||||||
current->Committed = commitPostClamped;
|
current->Committed = commitPostClamped;
|
||||||
}
|
}
|
||||||
@@ -143,13 +154,20 @@ namespace Juliet
|
|||||||
size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position);
|
size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position);
|
||||||
Arena* current = arena->Current;
|
Arena* current = arena->Current;
|
||||||
|
|
||||||
// TODO : Free list
|
|
||||||
for (Arena* previous = nullptr; current->BasePosition >= clampedPosition; current = previous)
|
for (Arena* previous = nullptr; current->BasePosition >= clampedPosition; current = previous)
|
||||||
{
|
{
|
||||||
previous = current->Previous;
|
previous = current->Previous;
|
||||||
Memory::OS_Release(current, current->Reserved);
|
current->Position = k_ArenaHeaderSize;
|
||||||
|
SingleLinkedListPushPrevious(arena->FreeLast, 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);
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ namespace Juliet::UnitTest
|
|||||||
printf("Running Paged Memory Arena Tests...\n");
|
printf("Running Paged Memory Arena Tests...\n");
|
||||||
|
|
||||||
// New Arena!
|
// New Arena!
|
||||||
Arena* testArena = ArenaAllocate();
|
ArenaParams param{ .ReserveSize = Megabytes(64llu), .CommitSize = Kilobytes(64llu) };
|
||||||
|
Arena* testArena = ArenaAllocate(param);
|
||||||
|
|
||||||
size_t pos = ArenaPos(testArena);
|
size_t pos = ArenaPos(testArena);
|
||||||
Assert(pos == k_ArenaHeaderSize);
|
Assert(pos == k_ArenaHeaderSize);
|
||||||
@@ -55,6 +56,65 @@ namespace Juliet::UnitTest
|
|||||||
pos = ArenaPos(testArena);
|
pos = ArenaPos(testArena);
|
||||||
Assert(pos == k_ArenaHeaderSize + sizeof(TestStruct) * 2);
|
Assert(pos == k_ArenaHeaderSize + sizeof(TestStruct) * 2);
|
||||||
|
|
||||||
|
ArenaClear(testArena);
|
||||||
|
|
||||||
|
// TEST COMMIT
|
||||||
|
// Push more than the default commit size
|
||||||
|
uint8* myArray = ArenaPushArray<uint8>(testArena, 100'000);
|
||||||
|
*myArray = 5;
|
||||||
|
Assert(*myArray == 5);
|
||||||
|
bool isCommitSizeSame = testArena->Committed == (2 * param.CommitSize);
|
||||||
|
Assert(isCommitSizeSame);
|
||||||
|
ArenaClear(testArena);
|
||||||
|
|
||||||
|
// Push again above default commit size
|
||||||
|
myArray = ArenaPushArray<uint8>(testArena, 100'000);
|
||||||
|
// Shoud be zero again
|
||||||
|
Assert(*myArray == 0);
|
||||||
|
// Should not have commited more
|
||||||
|
isCommitSizeSame = testArena->Current->Committed == (2 * param.CommitSize);
|
||||||
|
Assert(isCommitSizeSame);
|
||||||
|
ArenaClear(testArena);
|
||||||
|
|
||||||
|
// TEST RESERVE
|
||||||
|
size_t posPreBigArray = ArenaPos(testArena);
|
||||||
|
size_t reservedSizePreBigArray = testArena->Current->Reserved;
|
||||||
|
|
||||||
|
size_t arraySize = Megabytes(100llu);
|
||||||
|
myArray = ArenaPushArray<uint8>(testArena, arraySize);
|
||||||
|
|
||||||
|
// Align and support the header size because we need to allocate a new block + header
|
||||||
|
size_t reserve_size = AlignPow2(arraySize + k_ArenaHeaderSize, AlignOf(uint8));
|
||||||
|
Assert(reserve_size == testArena->Current->Position);
|
||||||
|
Assert(reservedSizePreBigArray == testArena->Current->BasePosition);
|
||||||
|
|
||||||
|
// TODO Get page size from os kernel call (dwPageSize)
|
||||||
|
constexpr uint64 k_PageSize = Kilobytes(4);
|
||||||
|
size_t reserve_sizeAlignToPage = AlignPow2(reserve_size, k_PageSize);
|
||||||
|
|
||||||
|
bool isReserveSizeSame = testArena->Current->Reserved == reserve_sizeAlignToPage;
|
||||||
|
Assert(isReserveSizeSame);
|
||||||
|
Assert(testArena->Current->Reserved == testArena->Current->Committed);
|
||||||
|
|
||||||
|
size_t basePos = ArenaPos(testArena);
|
||||||
|
Assert(basePos == reserve_size + testArena->Current->BasePosition);
|
||||||
|
|
||||||
|
myStruct = ArenaPushStruct<TestStruct>(testArena);
|
||||||
|
|
||||||
|
// Should not delete the current block because we only delete the struct
|
||||||
|
ArenaPopTo(testArena, basePos);
|
||||||
|
Assert(testArena->Current->Previous != nullptr);
|
||||||
|
|
||||||
|
// Should still not delete the current block. We only remove the array
|
||||||
|
ArenaPop(testArena, arraySize);
|
||||||
|
Assert(testArena->Current->Previous != nullptr);
|
||||||
|
// We should only have the header remaining
|
||||||
|
Assert(testArena->Current->Position == k_ArenaHeaderSize);
|
||||||
|
|
||||||
|
// This one should delete the block
|
||||||
|
ArenaPopTo(testArena, posPreBigArray);
|
||||||
|
Assert(testArena->Current->Previous == nullptr);
|
||||||
|
|
||||||
ArenaRelease(testArena);
|
ArenaRelease(testArena);
|
||||||
|
|
||||||
// Setup Pool and Arena for Pop Tests
|
// Setup Pool and Arena for Pop Tests
|
||||||
|
|||||||
Reference in New Issue
Block a user