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 Reserved;
|
||||
|
||||
Arena* FreeLast;
|
||||
};
|
||||
static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
|
||||
|
||||
|
||||
@@ -20,13 +20,19 @@ namespace Juliet
|
||||
}
|
||||
|
||||
// Single linked list
|
||||
void SingleLinkedListPush(auto*& stackTop, auto* node)
|
||||
void SingleLinkedListPushNext(auto*& stackTop, auto* node)
|
||||
{
|
||||
node->Next = stackTop;
|
||||
stackTop = node;
|
||||
}
|
||||
|
||||
void SingleLinkedListPop(auto*& stackTop)
|
||||
void SingleLinkedListPushPrevious(auto*& stackTop, auto* node)
|
||||
{
|
||||
node->Previous = stackTop;
|
||||
stackTop = node;
|
||||
}
|
||||
|
||||
void SingleLinkedListPopNext(auto*& stackTop)
|
||||
{
|
||||
stackTop = stackTop->Next;
|
||||
}
|
||||
|
||||
@@ -22,13 +22,15 @@ namespace Juliet
|
||||
{
|
||||
Assert(!g_CurrentDisplayDevice);
|
||||
|
||||
Arena* arena = ArenaAllocate();
|
||||
|
||||
DisplayDevice* candidateDevice = nullptr;
|
||||
DisplayDeviceFactory* candidateFactory = nullptr;
|
||||
for (DisplayDeviceFactory* factory : Internal::Display::Factories)
|
||||
{
|
||||
if (factory)
|
||||
{
|
||||
candidateDevice = factory->CreateDevice();
|
||||
candidateDevice = factory->CreateDevice(arena);
|
||||
if (candidateDevice)
|
||||
{
|
||||
candidateFactory = factory;
|
||||
@@ -40,8 +42,9 @@ namespace Juliet
|
||||
// TODO : handle error instead of crashing
|
||||
Assert(candidateDevice);
|
||||
|
||||
g_CurrentDisplayDevice = candidateDevice;
|
||||
g_CurrentDisplayDevice->Name = candidateFactory->Name;
|
||||
g_CurrentDisplayDevice = candidateDevice;
|
||||
g_CurrentDisplayDevice->Arena = arena;
|
||||
g_CurrentDisplayDevice->Name = candidateFactory->Name;
|
||||
|
||||
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
|
||||
// no op for now
|
||||
g_CurrentDisplayDevice->Free(g_CurrentDisplayDevice);
|
||||
|
||||
ArenaRelease(g_CurrentDisplayDevice->Arena);
|
||||
g_CurrentDisplayDevice = nullptr;
|
||||
}
|
||||
|
||||
@@ -73,21 +78,16 @@ namespace Juliet
|
||||
{
|
||||
Assert(g_CurrentDisplayDevice->CreatePlatformWindow);
|
||||
|
||||
MemoryArena* arena = GetEngineArena();
|
||||
auto window = ArenaPushType<Window>(arena, ConstString("Window"));
|
||||
if (!window)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
auto* arena = g_CurrentDisplayDevice->Arena;
|
||||
Assert(arena);
|
||||
|
||||
auto* window = ArenaPushStruct<Window>(arena);
|
||||
Assert(window);
|
||||
|
||||
window->Width = width;
|
||||
window->Height = height;
|
||||
|
||||
auto titleLen = StringLength(title);
|
||||
auto buffer = ArenaPushArray<char>(arena, titleLen, ConstString("Window Title Array"));
|
||||
MemCopy(buffer, title, titleLen);
|
||||
|
||||
window->Title.Data = buffer;
|
||||
window->Title.Size = titleLen;
|
||||
window->Title = StringCopy(arena, WrapString(title));
|
||||
|
||||
g_CurrentDisplayDevice->MainWindow = window;
|
||||
if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, window))
|
||||
@@ -109,7 +109,7 @@ namespace Juliet
|
||||
|
||||
HideWindow(window);
|
||||
|
||||
// We don't free from arena, these are persistent until shutdown.
|
||||
// TODO: Pop from arena.
|
||||
window->Title.Data = nullptr;
|
||||
window->Title.Size = 0;
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Juliet
|
||||
// Acts as a singleton after Initialize has been called and is freed in Shutdown.
|
||||
struct DisplayDevice
|
||||
{
|
||||
Arena* Arena;
|
||||
|
||||
const char* Name = "Unknown";
|
||||
|
||||
// Initialize all subsystems needed for the device to works
|
||||
@@ -33,7 +35,7 @@ namespace Juliet
|
||||
struct DisplayDeviceFactory
|
||||
{
|
||||
const char* Name = "Unknown";
|
||||
DisplayDevice* (*CreateDevice)(void);
|
||||
DisplayDevice* (*CreateDevice)(Arena* arena);
|
||||
};
|
||||
|
||||
// TODO : Support more platforms
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace Juliet::Win32
|
||||
void Shutdown(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)
|
||||
{
|
||||
|
||||
@@ -63,8 +63,8 @@ namespace Juliet::Win32
|
||||
|
||||
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
|
||||
const int w = window->Width, h = window->Height;
|
||||
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, "JULIET TODO PASS TITLE", style, x, y, w, h, nullptr,
|
||||
nullptr, instance, nullptr);
|
||||
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, window->Title.Data, style, x, y, w, h, nullptr, nullptr,
|
||||
instance, nullptr);
|
||||
|
||||
PumpEvents(self);
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Juliet
|
||||
size_t position = ArenaPos(ActiveLog->Arena);
|
||||
LogScope* scope = ArenaPushStruct<LogScope>(ActiveLog->Arena);
|
||||
scope->Position = position;
|
||||
SingleLinkedListPush(ActiveLog->TopScope, scope);
|
||||
SingleLinkedListPushNext(ActiveLog->TopScope, scope);
|
||||
}
|
||||
|
||||
void LogScopeEnd()
|
||||
@@ -126,7 +126,7 @@ namespace Juliet
|
||||
LogScope* scope = ActiveLog->TopScope;
|
||||
Assert(scope != nullptr);
|
||||
|
||||
SingleLinkedListPop(ActiveLog->TopScope);
|
||||
SingleLinkedListPopNext(ActiveLog->TopScope);
|
||||
|
||||
ArenaPopTo(ActiveLog->Arena, scope->Position);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
namespace Juliet
|
||||
{
|
||||
|
||||
// TODO Get page size from os kernel call (dwPageSize)
|
||||
namespace
|
||||
{
|
||||
@@ -67,7 +66,25 @@ namespace Juliet
|
||||
if (current->Reserved < positionPostPush /* flags : chaining allowed */)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -83,13 +100,7 @@ namespace Juliet
|
||||
}
|
||||
|
||||
newBlock->BasePosition = current->BasePosition + current->Reserved;
|
||||
// Push on the linkedlist
|
||||
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))
|
||||
SingleLinkedListPushPrevious(arena->Current, newBlock);
|
||||
|
||||
current = newBlock;
|
||||
positionPrePush = AlignPow2(current->Position, align);
|
||||
@@ -108,11 +119,11 @@ namespace Juliet
|
||||
size_t commitPostAligned = positionPostPush + current->CommitSize - 1;
|
||||
commitPostAligned -= commitPostAligned % current->CommitSize;
|
||||
size_t commitPostClamped = ClampTop(commitPostAligned, current->Reserved);
|
||||
// size_t commitSize = commitPostClamped - current->Committed;
|
||||
// Byte* commitPtr = reinterpret_cast<Byte*>(current) + current->Committed;
|
||||
size_t commitSize = commitPostClamped - current->Committed;
|
||||
Byte* commitPtr = reinterpret_cast<Byte*>(current) + current->Committed;
|
||||
|
||||
// TODO os_commit / commit_large
|
||||
// os_commit(commitPtr, commitSize);
|
||||
// TODO commit_large
|
||||
Memory::OS_Commit(commitPtr, commitSize);
|
||||
|
||||
current->Committed = commitPostClamped;
|
||||
}
|
||||
@@ -143,13 +154,20 @@ namespace Juliet
|
||||
size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position);
|
||||
Arena* current = arena->Current;
|
||||
|
||||
// TODO : Free list
|
||||
for (Arena* previous = nullptr; current->BasePosition >= clampedPosition; current = previous)
|
||||
{
|
||||
previous = current->Previous;
|
||||
Memory::OS_Release(current, current->Reserved);
|
||||
previous = current->Previous;
|
||||
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;
|
||||
size_t newPosition = clampedPosition - current->BasePosition;
|
||||
Assert(newPosition <= current->Position);
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace Juliet::UnitTest
|
||||
printf("Running Paged Memory Arena Tests...\n");
|
||||
|
||||
// New Arena!
|
||||
Arena* testArena = ArenaAllocate();
|
||||
ArenaParams param{ .ReserveSize = Megabytes(64llu), .CommitSize = Kilobytes(64llu) };
|
||||
Arena* testArena = ArenaAllocate(param);
|
||||
|
||||
size_t pos = ArenaPos(testArena);
|
||||
Assert(pos == k_ArenaHeaderSize);
|
||||
@@ -55,6 +56,65 @@ namespace Juliet::UnitTest
|
||||
pos = ArenaPos(testArena);
|
||||
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);
|
||||
|
||||
// Setup Pool and Arena for Pop Tests
|
||||
|
||||
Reference in New Issue
Block a user