Added a basic MemoryArena.

Added one scratch, one engine and one game arena.
Converted the game alloc to arena + the display stuff.
WIP

Made using Antigravity+gemini
This commit is contained in:
2026-01-17 21:09:23 -05:00
parent 98783b7e8f
commit f95ba51c13
28 changed files with 462 additions and 59 deletions

View File

@@ -0,0 +1,10 @@
---
trigger: always_on
---
No exceptions
Use [[nodiscard]]
auto is allowed but when its a pointer add the * and when reference adds the &
Member variable are CamelCase
Types are CamelCase
Functions are CamelCase.

View File

@@ -1,17 +1,15 @@
---
description: Build the Juliet project using FastBuild
---
// turbo-all
This workflow sets up the Juliet build environment and runs `fbuild`.
1. To build a specific configuration (e.g., msvc-Debug):
// turbo
`cmd /c "misc\shell.bat & fbuild msvc-Debug"`
2. To build the default (msvc):
// turbo
`cmd /c "misc\shell.bat & fbuild msvc"`
3. To see all available targets:
// turbo
`cmd /c "misc\shell.bat & fbuild -targets"`
`cmd /c "misc\shell.bat & fbuild -showtargets"`

View File

@@ -0,0 +1,6 @@
---
description: Launch the Juliet application
---
1. Run the launch script
misc\launch.bat autoclose

View File

@@ -1,9 +1,9 @@
---
description: Recompile shaders for the Juliet project
---
// turbo-all
This workflow recompiles all shaders using the `recompile_shaders.bat` script.
1. Recompile all shaders:
// turbo
`cmd /c "misc\shell.bat & misc\recompile_shaders.bat"`

49
AgentData/MemoryArena.md Normal file
View File

@@ -0,0 +1,49 @@
# Memory System Status & Migration Plan
## Completed Work
### Core Systems
- **MemoryArena**: Implemented linear allocator with alignment, markers, and reset support.
- **Global Arenas**:
- `ScratchArena` (64MB): Transient per-frame memory.
- `EngineArena` (256MB): Persistent engine memory (internal).
- `GameArena` (512MB): Persistent game memory (exported to Game DLL).
- **Helpers**: Added `ArenaPushType<T>` and `ArenaPushArray<T>` with automatic zero-initialization.
### Migrated Subsystems
- **Display**: `Win32Window` and `Win32DisplayDevice` now use `EngineArena`.
- **Game Entities**: `Entity.h` uses `GameArena` for entity allocation; manual `free` calls removed from `game.cpp`.
## Remaining Work
The following subsystems still use legacy `malloc`/`calloc`/`realloc`/`free` and need to be migrated.
### Hot Reload System
- **Files**: `Core/HotReload/HotReload.cpp`, `Core/HotReload/Win32/Win32HotReload.cpp`
- **Allocations**: `DLLFullPath`, `LockFullPath`, `tempDllPath`.
- **Strategy**:
- Use `EngineArena` for persistent paths (`DLLFullPath`, `LockFullPath`).
- Use `ScratchArena` for temporary paths (`tempDllPath`).
### IO System
- **Files**: `Core/HAL/IO/IOStream.cpp`, `Core/HAL/IO/Win32/Win32IOStream.cpp`
- **Allocations**: `IOStream` instance, data buffers, `Win32IOStreamDataPayload`.
- **Challenge**: `Realloc` is used for growing buffers.
- **Strategy**:
- `IOStream` struct -> `ScratchArena` (if transient) or `EngineArena`.
- Buffers: Evaluate if `ArenaPush` with large enough capacity is sufficient, or implement a growable buffer on top of arena (or use `std::vector` with custom allocator if absolutely needed, but prefer simple fixed max size if possible).
### Graphics / Debug
- **Files**: `Graphics/DebugDisplayRenderer.cpp`
- **Allocations**: `DepthTestedVertices`, `OverlayVertices`.
- **Strategy**: Use `EngineArena` or a dedicated `RenderArena` if these are persistent. If per-frame, move to `ScratchArena`.
### Shader Compiler
- **Files**: `JulietShaderCompiler/ShaderCompiler.cpp`
- **Allocations**: Argument arrays, file buffers.
- **Strategy**: Use `ScratchArena` for all compilation tasks as they are transient.
### Filesystem
- **Files**: `Core/HAL/Filesystem/Filesystem.cpp`
- **Allocations**: `CachedBasePath`.
- **Strategy**: Migrate to `EngineArena` (persistent).

View File

@@ -3,6 +3,7 @@
#include <concepts>
#include <Core/Common/CoreUtils.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
#include <Engine/Class.h>
#include <Entity/EntityManager.h>
#include <type_traits>
@@ -17,6 +18,8 @@
constexpr Juliet::Class entityKind##entity(#entity, sizeof(#entity) / sizeof(char)); \
const Juliet::Class* entity::Kind = &entityKind##entity;
namespace Game
{
using DerivedType = void*;
@@ -46,8 +49,9 @@ namespace Game
requires EntityConcept<EntityType>
EntityType* MakeEntity(EntityManager& manager, float x, float y)
{
EntityType* result = static_cast<EntityType*>(Juliet::Calloc(1, sizeof(EntityType)));
Entity* base = result->Base = static_cast<Entity*>(Juliet::Calloc(1, sizeof(Entity)));
auto* arena = Juliet::GetGameArena();
EntityType* result = Juliet::ArenaPushType<EntityType>(arena);
Entity* base = result->Base = Juliet::ArenaPushType<Entity>(arena);
base->X = x;
base->Y = y;
base->Derived = result;

View File

@@ -38,7 +38,7 @@
// Extra Compiler Options
.CompilerOptions + ' "-IJuliet/include"'
+ ' "-IGame"'
// + ' -DGAME_EXPORT' // I'm just always exporting anyway but just in case
.CompilerOptions + ' -DJULIET_EXPORT'
#if __WINDOWS__
.CompilerOptions + ' -DJULIET_WIN32'

View File

@@ -4,11 +4,11 @@
#undef max
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/MemoryArena.h>
#include <Entity/Entity.h>
#include <Entity/EntityManager.h>
#include <Graphics/Graphics.h>
// Test code
namespace Game
@@ -31,8 +31,21 @@ namespace Game
using namespace Juliet;
extern "C" __declspec(dllexport) void __cdecl GameInit()
extern "C" JULIET_API void GameInit(GameInitParams* /*params*/)
{
// Example allocation in GameArena
struct GameState
{
float TotalTime;
int Score;
};
auto* gameState = ArenaPushType<GameState>(GetGameArena());
gameState->TotalTime = 0.0f;
gameState->Score = 0;
printf("Game Arena Allocated: %p\n", gameState);
using namespace Game;
// Entity Use case
@@ -41,8 +54,8 @@ extern "C" __declspec(dllexport) void __cdecl GameInit()
Door* door = MakeEntity<Door>(manager, 10.0f, 2.0f);
door->IsOpened = true;
Entity* ent = door->Base;
[[maybe_unused]] Door* stillDoor = DownCast<Door>(ent);
Entity* ent = door->Base;
[[maybe_unused]] Door* stillDoor = DownCast<Door>(ent);
Assert(door == stillDoor);
Rock* rock = MakeEntity<Rock>(manager, 1.f, 2.f);
@@ -51,20 +64,14 @@ extern "C" __declspec(dllexport) void __cdecl GameInit()
printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed");
printf("Rock has %d health points\n", rock->Health);
// Have to manually free for now because im not using arenas or anything
free(door->Base);
free(door);
free(rock->Base);
free(rock);
}
extern "C" __declspec(dllexport) void __cdecl GameShutdown()
extern "C" JULIET_API void __cdecl GameShutdown()
{
printf("Shutting down game...\n");
}
extern "C" __declspec(dllexport) void __cdecl GameUpdate([[maybe_unused]] float deltaTime)
extern "C" JULIET_API void __cdecl GameUpdate([[maybe_unused]] float deltaTime)
{
//printf("Updating game...\n");
// printf("Updating game...\n");
}

View File

@@ -57,6 +57,7 @@
<CustomBuild Include="include\Core\Math\Shape.h" />
<CustomBuild Include="include\Core\Math\Vector.h" />
<CustomBuild Include="include\Core\Memory\Allocator.h" />
<CustomBuild Include="include\Core\Memory\MemoryArena.h" />
<CustomBuild Include="include\Core\Memory\Utils.h" />
<CustomBuild Include="include\Core\Networking\IPAddress.h" />
<CustomBuild Include="include\Core\Networking\NetworkPacket.h" />
@@ -118,6 +119,9 @@
<CustomBuild Include="src\Core\Math\Math_Private.h" />
<CustomBuild Include="src\Core\Math\MathRound.cpp" />
<CustomBuild Include="src\Core\Memory\Allocator.cpp" />
<CustomBuild Include="src\Core\Memory\EngineArena.h" />
<CustomBuild Include="src\Core\Memory\MemoryArena.cpp" />
<CustomBuild Include="src\Core\Memory\MemoryArenaTests.cpp" />
<CustomBuild Include="src\Core\Networking\NetworkPacket.cpp" />
<CustomBuild Include="src\Core\Networking\Socket.cpp" />
<CustomBuild Include="src\Core\Networking\SocketPlatformImpl.h" />

View File

@@ -91,6 +91,9 @@
<CustomBuild Include="include\Core\Memory\Allocator.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Memory\MemoryArena.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Memory\Utils.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
@@ -273,6 +276,15 @@
<CustomBuild Include="src\Core\Memory\Allocator.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\EngineArena.h">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\MemoryArena.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\MemoryArenaTests.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\NetworkPacket.cpp">
<Filter>src\Core\Networking</Filter>
</CustomBuild>

View File

@@ -13,6 +13,14 @@ namespace Juliet
All = 0xFb
};
struct MemoryArena;
struct GameInitParams
{
MemoryArena* GameArena;
MemoryArena* ScratchArena;
};
void JulietInit(JulietInit_Flags flags);
void JulietShutdown();
} // namespace Juliet

View File

@@ -0,0 +1,61 @@
#pragma once
#include <Juliet.h>
#include <Core/Common/CoreTypes.h>
#include <Core/Common/CoreUtils.h>
#include <Core/Memory/Utils.h>
namespace Juliet
{
struct MemoryArena
{
uint8* Data;
size_t Size;
size_t Offset;
};
JULIET_API void MemoryArenaCreate(MemoryArena* arena, void* backingMemory, size_t size);
JULIET_API void* ArenaPush(MemoryArena* arena, size_t size, size_t alignment = 16);
JULIET_API void ArenaReset(MemoryArena* arena);
JULIET_API size_t ArenaGetMarker(MemoryArena* arena);
JULIET_API void ArenaResetToMarker(MemoryArena* arena, size_t marker);
// --- Global Arenas & Management ---
// Returns a global arena that resets every frame.
JULIET_API MemoryArena* GetScratchArena();
// Persistent game arena.
JULIET_API MemoryArena* GetGameArena();
// Internal engine function to reset the scratch arena.
JULIET_API void ScratchArenaReset();
// Internal engine function to initialize memory arenas.
void MemoryArenasInit();
// Internal engine function to shutdown memory arenas.
void MemoryArenasShutdown();
template <typename T>
inline T* ArenaPushType(MemoryArena* arena)
{
T* result = static_cast<T*>(ArenaPush(arena, sizeof(T), alignof(T)));
if (result)
{
MemSet(result, 0, sizeof(T));
}
return result;
}
template <typename T>
inline T* ArenaPushArray(MemoryArena* arena, size_t count)
{
T* result = static_cast<T*>(ArenaPush(arena, sizeof(T) * count, alignof(T)));
if (result)
{
MemSet(result, 0, sizeof(T) * count);
}
return result;
}
} // namespace Juliet

View File

@@ -2,6 +2,8 @@
#include <Core/HAL/Display/Display_Private.h>
#include <Core/HAL/Display/DisplayDevice.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Memory/EngineArena.h>
#include <format>
namespace Juliet
@@ -72,7 +74,8 @@ namespace Juliet
{
Assert(g_CurrentDisplayDevice->CreatePlatformWindow);
auto window = static_cast<Window*>(Calloc(1, sizeof(Window)));
MemoryArena* arena = GetEngineArena();
auto window = ArenaPushType<Window>(arena);
if (!window)
{
return nullptr;
@@ -80,9 +83,8 @@ namespace Juliet
window->Width = width;
window->Height = height;
// TODO String creator that copy
auto titleLen = StringLength(title);
auto buffer = static_cast<char*>(Calloc(titleLen, sizeof(char)));
auto buffer = ArenaPushArray<char>(arena, titleLen);
MemCopy(buffer, title, titleLen);
window->Title.Data = buffer;
@@ -91,7 +93,8 @@ namespace Juliet
g_CurrentDisplayDevice->MainWindow = window;
if (!g_CurrentDisplayDevice->CreatePlatformWindow(g_CurrentDisplayDevice, window))
{
// TODO : Destroy
// 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.
return nullptr;
}
@@ -107,13 +110,12 @@ namespace Juliet
HideWindow(window);
// TODO : Free string function
SafeFree(window->Title.Data);
// We don't free from arena, these are persistent until shutdown.
window->Title.Data = nullptr;
window->Title.Size = 0;
g_CurrentDisplayDevice->DestroyPlatformWindow(g_CurrentDisplayDevice, window);
Free(window.Get());
g_CurrentDisplayDevice->MainWindow = nullptr;
}

View File

@@ -2,6 +2,8 @@
#include <Core/HAL/Display/Win32/Win32DisplayEvent.h>
#include <Core/HAL/Display/Win32/Win32Window.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h>
#include <Core/Memory/Utils.h>
namespace Juliet::Win32
{
@@ -12,14 +14,12 @@ namespace Juliet::Win32
return true;
}
void Shutdown(NonNullPtr<DisplayDevice> /*self*/) {}
void Free(NonNullPtr<DisplayDevice> self)
{
Juliet::Free(self.Get());
}
void Free(NonNullPtr<DisplayDevice> /*self*/) {}
DisplayDevice* CreateDevice()
{
auto device = static_cast<DisplayDevice*>(Calloc(1, sizeof(DisplayDevice)));
auto device = ArenaPushType<DisplayDevice>(GetEngineArena());
if (!device)
{
return nullptr;

View File

@@ -2,6 +2,8 @@
#include <Core/HAL/Display/Win32/Win32Window.h>
#include <Core/HAL/Display/Window.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h>
#include <Core/Memory/Utils.h>
namespace Juliet::Win32
{
@@ -12,7 +14,8 @@ namespace Juliet::Win32
bool SetupWindowState(NonNullPtr<DisplayDevice> /*self*/, NonNullPtr<Window> window, HWND handle)
{
auto state = static_cast<Window32State*>(Calloc(1, sizeof(Window32State)));
auto state = ArenaPushType<Window32State>(GetEngineArena());
window->State = state;
state->Handle = handle;
state->Window = window;
@@ -31,8 +34,6 @@ namespace Juliet::Win32
{
ReleaseDC(state->Handle, state->HDC);
DestroyWindow(state->Handle);
SafeFree(state);
}
window->State = nullptr;
}

View File

@@ -3,6 +3,8 @@
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Thread/Thread.h>
#define MAX_TRIES 100
@@ -21,11 +23,11 @@ namespace Juliet
// First allocate all the full path.
// TODO: Add path composition into filesystem + string format + string builder
const size_t dllFullPathLength = basePathLength + StringLength(dllName) + 1; // Need +1 because snprintf needs 0 terminated strings
code.DLLFullPath.Data = static_cast<char*>(Calloc(dllFullPathLength, sizeof(char)));
code.DLLFullPath.Data = ArenaPushArray<char>(GetEngineArena(), dllFullPathLength);
int writtenSize = snprintf(CStr(code.DLLFullPath), dllFullPathLength, "%s%s", CStr(basePath), CStr(dllName));
if (writtenSize < static_cast<int>(dllFullPathLength) - 1)
{
SafeFree(code.DLLFullPath.Data);
// Arena memory persists, no free needed
Log(LogLevel::Error, LogCategory::Core, "Cannot create DLL Full Path");
return;
}
@@ -33,12 +35,12 @@ namespace Juliet
// Lock filename path
const size_t lockPathLength = basePathLength + StringLength(lockFilename) + 1; // Need +1 because snprintf needs 0 terminated strings
code.LockFullPath.Data = static_cast<char*>(Calloc(lockPathLength, sizeof(char)));
code.LockFullPath.Data = ArenaPushArray<char>(GetEngineArena(), lockPathLength);
writtenSize = snprintf(CStr(code.LockFullPath), lockPathLength, "%s%s", CStr(basePath), CStr(lockFilename));
if (writtenSize < static_cast<int>(lockPathLength) - 1)
{
code.LockFullPath.Size = 0;
SafeFree(code.LockFullPath.Data);
// Arena memory persists, no free needed
Log(LogLevel::Error, LogCategory::Core, "Cannot create lock file full path");
return;
}
@@ -52,9 +54,9 @@ namespace Juliet
UnloadCode(code);
code.DLLFullPath.Size = 0;
SafeFree(code.DLLFullPath.Data);
// Arena memory persists until engine shutdown
code.LockFullPath.Size = 0;
SafeFree(code.LockFullPath.Data);
// Arena memory persists until engine shutdown
}
void ReloadCode(HotReloadCode& code)

View File

@@ -5,6 +5,7 @@
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
namespace Juliet
{
@@ -53,7 +54,10 @@ namespace Juliet
const size_t tempDllMaxBufferSize =
basePathLength + StringLength(code.TransientDLLName) + /* _ */ 1 + kTempDLLBufferSizeForID + 1 /* \0 */;
auto tempDllPath = static_cast<char*>(Calloc(tempDllMaxBufferSize, sizeof(char)));
// Allocate from Scratch Arena (transient)
auto tempDllPath = ArenaPushArray<char>(GetScratchArena(), tempDllMaxBufferSize);
for (uint32 attempt = 0; attempt < kMaxAttempts; ++attempt)
{
// int to char
@@ -61,7 +65,7 @@ namespace Juliet
int idLength = snprintf(idToStr, sizeof(idToStr), "%u", code.UniqueID);
if (idLength < 0)
{
SafeFree(tempDllPath);
// Scratch memory, no free needed
Log(LogLevel::Error, LogCategory::Core, "Cannot create temp full path");
return;
}
@@ -70,14 +74,14 @@ namespace Juliet
CStr(code.TransientDLLName));
if (writtenSize < 0)
{
SafeFree(tempDllPath);
// Scratch memory, no free needed
Log(LogLevel::Error, LogCategory::Core, "Cannot create temp full path");
return;
}
if (static_cast<size_t>(writtenSize) + 1 < basePathLength + static_cast<size_t>(idLength) + code.TransientDLLName.Size)
{
SafeFree(tempDllPath);
// Scratch memory, no free needed
Log(LogLevel::Error, LogCategory::Core, "Cannot create temp full path");
return;
}
@@ -109,7 +113,7 @@ namespace Juliet
}
}
SafeFree(tempDllPath);
// Scratch memory, no free needed
}
if (!code.IsValid)

View File

@@ -3,6 +3,7 @@
#include <Core/HAL/Display/Display_Private.h>
#include <Core/HAL/Filesystem/Filesystem_Private.h>
#include <Core/JulietInit.h>
#include <Core/Memory/MemoryArena.h>
namespace Juliet
{
@@ -38,9 +39,22 @@ namespace Juliet
}
} // namespace
#if JULIET_DEBUG
namespace UnitTest
{
extern void TestMemoryArena();
}
#endif
void JulietInit(JulietInit_Flags flags)
{
// Mandatory systems
MemoryArenasInit();
#if JULIET_DEBUG
UnitTest::TestMemoryArena();
#endif
InitFilesystem();
// Optional systems
@@ -61,6 +75,7 @@ namespace Juliet
}
ShutdownFilesystem();
MemoryArenasShutdown();
}
} // namespace Juliet

View File

@@ -0,0 +1,10 @@
#pragma once
#include <Core/Memory/MemoryArena.h>
namespace Juliet
{
// Persistent engine-only arena.
// Not exported to the Game DLL.
MemoryArena* GetEngineArena();
} // namespace Juliet

View File

@@ -0,0 +1,116 @@
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Memory/Utils.h>
namespace Juliet
{
void MemoryArenaCreate(MemoryArena* arena, void* backingMemory, size_t size)
{
Assert(arena);
Assert(backingMemory);
arena->Data = static_cast<uint8*>(backingMemory);
arena->Size = size;
arena->Offset = 0;
}
void* ArenaPush(MemoryArena* arena, size_t size, size_t alignment)
{
Assert(arena);
// Alignment must be power of 2
Assert((alignment & (alignment - 1)) == 0);
size_t currentPtr = reinterpret_cast<size_t>(arena->Data + arena->Offset);
size_t offset = (currentPtr + (alignment - 1)) & ~(alignment - 1);
size_t newOffset = offset - reinterpret_cast<size_t>(arena->Data) + size;
if (newOffset > arena->Size)
{
Assert(false, "Memory Arena overflow");
return nullptr;
}
void* result = arena->Data + (offset - reinterpret_cast<size_t>(arena->Data));
arena->Offset = newOffset;
return result;
}
void ArenaReset(MemoryArena* arena)
{
Assert(arena);
arena->Offset = 0;
}
size_t ArenaGetMarker(MemoryArena* arena)
{
Assert(arena);
return arena->Offset;
}
void ArenaResetToMarker(MemoryArena* arena, size_t marker)
{
Assert(arena);
Assert(marker <= arena->Offset);
arena->Offset = marker;
}
// --- Global Arenas & Management ---
namespace
{
MemoryArena g_ScratchArena;
MemoryArena g_EngineArena;
MemoryArena g_GameArena;
void* g_ScratchBacking = nullptr;
void* g_EngineBacking = nullptr;
void* g_GameBacking = nullptr;
constexpr size_t kScratchSize = 64 * 1024 * 1024; // 64MB
constexpr size_t kEngineSize = 256 * 1024 * 1024; // 256MB
constexpr size_t kGameSize = 512 * 1024 * 1024; // 512MB
} // namespace
MemoryArena* GetScratchArena()
{
return &g_ScratchArena;
}
MemoryArena* GetEngineArena()
{
return &g_EngineArena;
}
MemoryArena* GetGameArena()
{
return &g_GameArena;
}
void ScratchArenaReset()
{
ArenaReset(&g_ScratchArena);
}
void MemoryArenasInit()
{
// TODO: Use the VirtualAlloc API for this on windows
g_ScratchBacking = Malloc(kScratchSize);
MemSet(g_ScratchBacking, 0, kScratchSize);
g_EngineBacking = Malloc(kEngineSize);
MemSet(g_EngineBacking, 0, kEngineSize);
g_GameBacking = Malloc(kGameSize);
MemSet(g_GameBacking, 0, kGameSize);
MemoryArenaCreate(&g_ScratchArena, g_ScratchBacking, kScratchSize);
MemoryArenaCreate(&g_EngineArena, g_EngineBacking, kEngineSize);
MemoryArenaCreate(&g_GameArena, g_GameBacking, kGameSize);
}
void MemoryArenasShutdown()
{
SafeFree(g_ScratchBacking);
SafeFree(g_EngineBacking);
SafeFree(g_GameBacking);
}
} // namespace Juliet

View File

@@ -0,0 +1,67 @@
#include <cstdio>
#include <Core/Common/CoreUtils.h>
#include <Core/Memory/MemoryArena.h>
#if JULIET_DEBUG
namespace Juliet::UnitTest
{
void TestMemoryArena()
{
// 1. Core Arena Functionality
uint8 buffer[1024];
MemoryArena arena;
MemoryArenaCreate(&arena, buffer, 1024);
Assert(arena.Offset == 0);
Assert(arena.Size == 1024);
void* p1 = ArenaPush(&arena, 100);
Assert(p1 != nullptr);
Assert(arena.Offset >= 100);
size_t marker = ArenaGetMarker(&arena);
void* p2 = ArenaPush(&arena, 200);
Assert(p2 != nullptr);
Assert(arena.Offset >= marker + 200);
ArenaResetToMarker(&arena, marker);
Assert(arena.Offset == marker);
ArenaReset(&arena);
Assert(arena.Offset == 0);
// 2. Alignment Test
void* p3 = ArenaPush(&arena, 1, 1);
[[maybe_unused]] size_t addr = reinterpret_cast<size_t>(p3);
void* p4 = ArenaPush(&arena, 1, 16);
size_t addr2 = reinterpret_cast<size_t>(p4);
Assert((addr2 % 16) == 0);
// 3. Template Helpers
struct TestData
{
int a;
float b;
};
TestData* data = ArenaPushType<TestData>(&arena);
Assert(data != nullptr);
data->a = 10;
data->b = 20.0f;
TestData* dataArray = ArenaPushArray<TestData>(&arena, 10);
Assert(dataArray != nullptr);
// 4. Scratch Arena
MemoryArena* scratch = GetScratchArena();
Assert(scratch != nullptr);
void* sp = ArenaPush(scratch, 100);
Assert(sp != nullptr);
ScratchArenaReset();
Assert(scratch->Offset == 0);
printf("All MemoryArena tests passed.\n");
}
} // namespace Juliet::UnitTest
#endif

View File

View File

@@ -4,6 +4,7 @@
.ProjectName = 'JulietApp'
.ProjectPath = 'JulietApp'
.JulietIncludePath = ' "-IJuliet/include"'
+ ' "-IJuliet/src"'
.ProjectDefPath = '$_WORKING_DIR_$/$ProjectName$/$ProjectName$.def'
// Library

View File

@@ -376,7 +376,7 @@
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
@@ -386,7 +386,7 @@
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
@@ -396,7 +396,7 @@
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
@@ -406,7 +406,7 @@
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
@@ -416,7 +416,7 @@
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
@@ -426,7 +426,7 @@
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>

View File

@@ -17,6 +17,8 @@
#include <Core/Common/String.h>
#include <Core/Memory/Utils.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Memory/EngineArena.h>
#include <cstdlib>
// TODO : Replace with message box from framework + call main and not winmain + subsystem
@@ -33,7 +35,7 @@ using namespace Juliet;
namespace
{
using GameInit_t = void (*)(void);
using GameInit_t = void (*)(GameInitParams*);
using GameShutdown_t = void (*)(void);
using GameUpdate_t = void (*)(float deltaTime);
struct GameFunctionTable
@@ -158,7 +160,10 @@ void JulietApplication::Init()
InitHotReloadCode(GameCode, ConstString("Game.dll"), ConstString("Game_Temp.dll"), ConstString("lock.tmp"));
if ((Running = GameCode.IsValid))
{
Game.Init();
GameInitParams params;
params.GameArena = GetGameArena();
params.ScratchArena = GetScratchArena();
Game.Init(&params);
}
// Initialize DebugDisplay
@@ -383,6 +388,9 @@ void JulietApplication::Update()
// Submit Commands
SubmitCommandLists(cmdList);
// Reset Scratch Arena at the end of the frame
ScratchArenaReset();
}
bool JulietApplication::IsRunning()

View File

@@ -2,8 +2,21 @@
setlocal
:: --- 1. Argument Parsing Logic ---
set "ARG1=%~1"
set "ARG2=%~2"
set "ARG1="
set "ARG2="
set "AUTOCLOSE=0"
for %%x in (%*) do (
if /I "%%~x"=="autoclose" (
set "AUTOCLOSE=1"
) else (
if not defined ARG1 (
set "ARG1=%%~x"
) else if not defined ARG2 (
set "ARG2=%%~x"
)
)
)
:: Set Defaults
set "COMPILER_TYPE=clang"
@@ -44,6 +57,12 @@ if exist "%APP_DIR%\%APP_EXE%" (
pushd "%APP_DIR%"
start "" "%APP_EXE%"
popd
if "%AUTOCLOSE%"=="1" (
echo [AUTOCLOSE] Waiting 5 seconds...
timeout /t 5 /nobreak >nul
taskkill /IM "%APP_EXE%" /F >nul 2>&1
)
) else (
echo [ERROR] Executable not found at: %APP_DIR%\%APP_EXE%
echo Please build first: fbuild JulietApp-%PLATFORM%-%CONFIG%

View File

@@ -73,5 +73,4 @@ for %%F in ("%SOURCE_DIR%\*.hlsl") do (
echo.
echo Operation terminee.
pause
endlocal