Adding scratch arena for main thread (and any thread in the future)

Added some function based on raddbg code for string conversion and more.
This commit is contained in:
2026-08-16 18:06:46 -04:00
parent 3e7216f57d
commit d1b7c5dbbe
32 changed files with 477 additions and 102 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ void InitEntityManager(Juliet::NonNullPtr<World> world)
newManager->Entities.Create(world->WorldArena JULIET_DEBUG_PARAM("Entities"));
newManager->Arena = Juliet::ArenaAllocate({} JULIET_DEBUG_PARAM("Entity Arena"));
newManager->Arena = Juliet::ArenaAllocate({ .Name = "Entity Arena" });
}
void ShutdownEntityManager()
+2 -2
View File
@@ -43,7 +43,7 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_
gGameState = params->GameState;
if (!gGameState)
{
Arena* gameStateArena = ArenaAllocate({ .ReserveSize = Megabytes(1) } JULIET_DEBUG_PARAM("Game Total Arena"));
Arena* gameStateArena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "Game Total Arena" });
auto* gameState = ArenaPushStruct<GameState>(gameStateArena);
gGameState = params->GameState = gameState;
gameState->TotalArena = gameStateArena;
@@ -57,7 +57,7 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_
ReserveCamera(4);
// Bootstrap world
auto* worldArena = ArenaAllocate({ .ReserveSize = Megabytes(1) } JULIET_DEBUG_PARAM("World Arena"));
auto* worldArena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "World Arena" });
World* world = ArenaPushStruct<World>(worldArena JULIET_DEBUG_PARAM("World"));
gameState->World = world;
gameState->World->WorldArena = worldArena;
+26
View File
@@ -202,4 +202,30 @@ namespace Juliet
return "UnknownType";
#endif
}
inline uint16 safe_cast_uint16(uint32 value)
{
Assert(value <= uint16Max);
uint16 result = (uint16)value;
return result;
}
const uint32 bitmask1 = 0b0000'0001;
const uint32 bitmask2 = 0b0000'0011;
const uint32 bitmask3 = 0b0000'0111;
const uint32 bitmask4 = 0b0000'1111;
const uint32 bitmask5 = 0b0001'1111;
const uint32 bitmask6 = 0b0011'1111;
const uint32 bitmask7 = 0b0111'1111;
const uint32 bitmask8 = 0b1111'1111;
const uint32 bitmask9 = 0x0000'01ff;
const uint32 bitmask10 = 0x0000'03ff;
const uint32 bitmask11 = 0x0000'07ff;
const uint32 bitmask12 = 0x0000'0fff;
const uint32 bitmask13 = 0x0000'1fff;
const uint32 bitmask14 = 0x0000'3fff;
const uint32 bitmask15 = 0x0000'7fff;
const uint32 bitmask16 = 0x0000'ffff;
// ...
const uint32 bitmask32 = 0xffff'ffff;
} // namespace Juliet
+30 -14
View File
@@ -19,7 +19,7 @@ namespace Juliet
struct Arena;
#define ConstString(str) { const_cast<char*>((str)), sizeof(str) - 1 }
#define CStr(str) ((str).Data)
#define CStr(str) ((str).Str)
#define InplaceString(name, size) \
char name##_[size]; \
MemSet(name##_, 0, sizeof(uint32)); \
@@ -40,17 +40,30 @@ namespace Juliet
// Represents a UTF-8 String.
// Not null terminated.
struct String
struct String8
{
char* Data;
char* Str;
size_t Size;
};
using String = String8;
struct String16
{
uint16* Str;
size_t Size;
};
struct StringBuffer : String
{
size_t Capacity;
};
struct UnicodeDecode
{
uint32 Increment;
uint32 Codepoint;
};
constexpr uint32 kInvalidUTF8 = 0xFFFD;
inline size_t StringLength(String str)
@@ -75,13 +88,13 @@ namespace Juliet
inline bool IsValid(String str)
{
return str.Size > 0 && str.Data != nullptr && *str.Data;
return str.Size > 0 && str.Str != nullptr && *str.Str;
}
inline String WrapString(const char* str)
{
String result = {};
result.Data = const_cast<char*>(str);
result.Str = const_cast<char*>(str);
result.Size = str ? strlen(str) : 0;
return result;
}
@@ -91,9 +104,9 @@ namespace Juliet
String result = str;
while (result.Size)
{
if (*result.Data != c)
if (*result.Str != c)
{
++result.Data;
++result.Str;
--result.Size;
}
else
@@ -150,7 +163,8 @@ namespace Juliet
extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst, bool nullTerminate);
extern JULIET_API bool ConvertString(String from, String to, String src, StringBuffer& dst, bool nullTerminate);
JULIET_API String StringCopy(NonNullPtr<Arena> arena, String str);
JULIET_API String StringCopy(NonNullPtr<Arena> arena, String str);
JULIET_API String16 str16_from_8(NonNullPtr<Arena> arena, String8 str);
template <typename... Args>
String Format(NonNullPtr<Arena> arena, const char* formatStr, Args&&... args)
@@ -158,6 +172,8 @@ namespace Juliet
std::string result = std::vformat(formatStr, std::make_format_args(args...));
return StringCopy(arena, WrapString(result.c_str()));
}
#define juliet_snprintf snprintf
} // namespace Juliet
#ifdef UNIT_TEST
@@ -169,12 +185,12 @@ namespace Juliet::UnitTest
String s2 = ConstString("abcdefabcdef");
String s3 = ConstString("11111111111111111111");
Assert(FindChar(s1, 'x').Data == nullptr);
Assert(FindChar(s2, 'y').Data == nullptr);
Assert(FindChar(s2, 'a').Data - s2.Data == 0);
Assert(FindChar(s2, 'd').Data - s2.Data == 3);
Assert(FindChar(s2, 'f').Data - s2.Data == 5);
Assert(FindChar(s3, '1').Data - s3.Data == 0);
Assert(FindChar(s1, 'x').Str == nullptr);
Assert(FindChar(s2, 'y').Str == nullptr);
Assert(FindChar(s2, 'a').Str - s2.Str == 0);
Assert(FindChar(s2, 'd').Str - s2.Str == 3);
Assert(FindChar(s2, 'f').Str - s2.Str == 5);
Assert(FindChar(s3, '1').Str - s3.Str == 0);
}
} // namespace Juliet::UnitTest
#endif
+9 -7
View File
@@ -44,7 +44,7 @@ namespace Juliet
JULIET_DEBUG_ONLY(Arena* GlobalNext;)
JULIET_DEBUG_ONLY(Arena* GlobalPrev;)
JULIET_DEBUG_ONLY(ArenaDebugInfo* FirstDebugInfo;)
JULIET_DEBUG_ONLY(const char* Name;)
const char* Name;
};
static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
@@ -59,11 +59,13 @@ namespace Juliet
uint64 ReserveSize = g_Arena_Default_Reserve_Size;
uint64 CommitSize = g_Arena_Default_Commit_Size;
const char* Name;
// When false, will assert if a new block is reserved.
JULIET_DEBUG_ONLY(bool CanReserveMore : 1 = true;)
};
[[nodiscard]] JULIET_API Arena* ArenaAllocate(const ArenaParams& params JULIET_DEBUG_PARAM(const char* name),
[[nodiscard]] JULIET_API Arena* ArenaAllocate(const ArenaParams& params,
const std::source_location& loc = std::source_location::current());
JULIET_API void ArenaRelease(NonNullPtr<Arena> arena);
@@ -88,7 +90,7 @@ namespace Juliet
{
return Format(GetDebugInfoArena(), std::forward<FirstDebugArg>(firstDebugArg),
std::forward<DebugArgs>(debugArgs)...)
.Data;
.Str;
}()));
}
@@ -102,23 +104,23 @@ namespace Juliet
{
if constexpr (sizeof...(DebugArgs) > 0)
{
return Format(GetDebugInfoArena(), std::forward<DebugArgs>(debugArgs)...).Data;
return Format(GetDebugInfoArena(), std::forward<DebugArgs>(debugArgs)...).Str;
}
return GetTypeName<Type>();
}())));
}
template <typename Type JULIET_DEBUG_ONLY(, typename... DebugArgs)>
template <typename Type, bool shouldZero = true JULIET_DEBUG_ONLY(, typename... DebugArgs)>
[[nodiscard]] Type* ArenaPushArray(NonNullPtr<Arena> arena, size_t count JULIET_DEBUG_PARAM(DebugArgs&&... debugArgs))
{
return static_cast<Type*>(
ArenaPush(arena, sizeof(Type) * count, Max(8ull, AlignOf(Type)),
true JULIET_DEBUG_PARAM(
shouldZero JULIET_DEBUG_PARAM(
[&]() -> const char*
{
if constexpr (sizeof...(DebugArgs) > 0)
{
return Format(GetDebugInfoArena(), std::forward<DebugArgs>(debugArgs)...).Data;
return Format(GetDebugInfoArena(), std::forward<DebugArgs>(debugArgs)...).Str;
}
return GetTypeName<Type>();
}())));
+2
View File
@@ -28,3 +28,5 @@
#include <string>
#include <type_traits>
#include <vector>
#include <Core/Common/CoreTypes.h>
+5 -1
View File
@@ -1,8 +1,12 @@
#pragma once
#include <Core/Common/String.h>
namespace Juliet
{
using Thread = std::thread;
uint32 thread_id();
void set_thread_name(String name);
// TODO : Proper wait
inline void wait_ms(int milliseconds)
@@ -0,0 +1,24 @@
#pragma once
#include <Core/Common/CoreTypes.h>
#include <Core/Memory/MemoryArena.h>
namespace Juliet
{
struct thread_context
{
Arena* ScratchArenas[2];
char ThreadName[64];
uint8 ThreadNameSize;
};
thread_context* thread_context_alloc();
void thread_context_release(NonNullPtr<thread_context> ctx);
void thread_context_select(NonNullPtr<thread_context> ctx);
thread_context* thread_context_current();
Arena* thread_context_get_scratch(Arena** conflicts, size_t count);
TempArena scratch_begin(Arena** conflicts, size_t count);
void scratch_end(TempArena scratch);
} // namespace Juliet
+136 -10
View File
@@ -60,6 +60,7 @@ namespace Juliet
}
} // namespace
// TODO: remove this as we convert to simple unicode decode / encode at the bottom
uint32 StepUTF8(String& inStr)
{
// From rfc3629, the UTF-8 spec:
@@ -92,7 +93,7 @@ namespace Juliet
}
if ((octet & 0x80) == 0x0) // One byte code point: 0xxxxxxx
{
inStr.Data += 1;
inStr.Str += 1;
inStr.Size -= 1;
return octet;
}
@@ -104,7 +105,7 @@ namespace Juliet
const uint32 result = ((octet & 0x1F) << 6) | (secondByte & 0x3F);
if (result >= 0x80) // If the result is smaller than 0x80 its an overlong!
{
inStr.Data += 2;
inStr.Str += 2;
inStr.Size -= 1;
return result;
}
@@ -131,7 +132,7 @@ namespace Juliet
{
if ((result < 0xD800) || (result > 0xDFFF)) // If out of range its an UTF-16 surrogate
{
inStr.Data += 3;
inStr.Str += 3;
inStr.Size -= 1;
return result;
}
@@ -164,7 +165,7 @@ namespace Juliet
const uint32 result = ((octet & 0x07) << 18) | secondOctet | thirdOctet | fourthOctet;
if (result >= 0x10000) // If smaller its an overlong
{
inStr.Data += 4;
inStr.Str += 4;
inStr.Size -= 1;
return result;
}
@@ -180,7 +181,7 @@ namespace Juliet
}
LogError(LogCategory::Core, "StepUTF8: Non supported codepoint. IsOverlong: %s. IsInvalid %s. IsUTF16Surrogate %s",
isOverlong ? "true" : "false", isInvalid ? "true" : "false", isUTF16Surrogate ? "true" : "false");
inStr.Data += 1;
inStr.Str += 1;
return kInvalidUTF8;
}
@@ -254,8 +255,8 @@ namespace Juliet
{
Assert(IsValid(src));
const char* srcStr = src.Data;
char* dstStr = dst.Data;
const char* srcStr = src.Str;
char* dstStr = dst.Str;
size_t remainingCapacity = dst.Capacity;
uint32 character = 0;
@@ -489,13 +490,138 @@ namespace Juliet
return ConvertString(sourceFormat, destFormat, src, dst, nullTerminate);
}
namespace
{
uint8 utf8_class[32] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 5,
};
UnicodeDecode utf8_decode(uint8* str, size_t max)
{
// From rfc3629, the UTF-8 spec:
// https://www.ietf.org/rfc/rfc3629.txt
//
// Char. number range | UTF-8 octet sequence
// (hexadecimal) | (binary)
// --------------------+---------------------------------------------
// 0000 0000-0000 007F | 0xxxxxxx
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UnicodeDecode result = { 1, uint32Max };
uint8 byte = str[0];
uint8 byte_class = utf8_class[byte >> 3];
switch (byte_class)
{
case 1:
{
result.Codepoint = byte;
break;
}
case 2:
{
if (1 < max)
{
uint8 next_byte = str[1];
if (utf8_class[next_byte >> 3] == 0)
{
result.Codepoint = (byte & bitmask5) << 6;
result.Codepoint |= (next_byte & bitmask6);
result.Increment = 2;
}
}
break;
}
case 3:
{
if (2 < max)
{
uint8 next_byte[2] = { str[1], str[2] };
if (utf8_class[next_byte[0] >> 3] == 0 && utf8_class[next_byte[1] >> 3] == 0)
{
result.Codepoint = (byte & bitmask4) << 12;
result.Codepoint |= ((next_byte[0] & bitmask6) << 6);
result.Codepoint |= (next_byte[1] & bitmask6);
result.Increment = 3;
}
}
break;
}
case 4:
{
if (3 < max)
{
uint8 next_byte[3] = { str[1], str[2], str[3] };
if (utf8_class[next_byte[0] >> 3] == 0 && utf8_class[next_byte[1] >> 3] == 0 &&
utf8_class[next_byte[2] >> 3] == 0)
{
result.Codepoint = (byte & bitmask3) << 18;
result.Codepoint |= ((next_byte[0] & bitmask6) << 12);
result.Codepoint |= ((next_byte[1] & bitmask6) << 6);
result.Codepoint |= (next_byte[2] & bitmask6);
result.Increment = 4;
}
}
break;
}
}
return result;
}
uint32 utf16_encode(uint16* str, uint32 codepoint)
{
Assert(str);
uint32 increment = 1;
if (codepoint == uint32Max)
{
str[0] = '?';
}
else if (codepoint < 0x10000)
{
str[0] = (uint16)codepoint;
}
else
{
uint32 var = codepoint - 0x10000;
str[0] = safe_cast_uint16(0xD800 + (var >> 10));
str[1] = safe_cast_uint16(0xDC00 + (var & bitmask10));
increment = 2;
}
return increment;
}
} // namespace
String StringCopy(NonNullPtr<Arena> arena, String str)
{
String result;
result.Size = str.Size;
result.Data = static_cast<char*>(ArenaPush(arena, str.Size + 1, alignof(char), true JULIET_DEBUG_PARAM("String")));
MemCopy(result.Data, str.Data, str.Size);
result.Data[result.Size] = 0;
result.Str = static_cast<char*>(ArenaPush(arena, str.Size + 1, alignof(char), true JULIET_DEBUG_PARAM("String")));
MemCopy(result.Str, str.Str, str.Size);
result.Str[result.Size] = 0;
return result;
}
String16 str16_from_8(NonNullPtr<Arena> arena, String8 in)
{
String16 result = {};
if (in.Size > 0)
{
size_t neededCapacity = in.Size * 2;
uint16* str = ArenaPushArray<uint16, false>(arena, neededCapacity + 1);
uint8* inStr = (uint8*)in.Str;
uint8* inStrEnd = inStr + in.Size;
UnicodeDecode consumed;
uint64 size = 0;
for (; inStr < inStrEnd; inStr += consumed.Increment)
{
consumed = utf8_decode(inStr, inStrEnd - inStr);
size += utf16_encode(str + size, consumed.Codepoint);
}
str[size] = 0;
ArenaPop(arena, (neededCapacity - size) * 2);
result = { str, size };
}
return result;
}
+2 -2
View File
@@ -23,7 +23,7 @@ namespace Juliet
{
Assert(!g_CurrentDisplayDevice);
Arena* arena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Display System"));
Arena* arena = ArenaAllocate({ .Name = "Display System" });
DisplayDevice* candidateDevice = nullptr;
DisplayDeviceFactory* candidateFactory = nullptr;
@@ -80,7 +80,7 @@ namespace Juliet
Assert(g_CurrentDisplayDevice->CreatePlatformWindow);
Window window = {};
window.Arena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Window"));
window.Arena = ArenaAllocate({ .Name = "Window" });
window.Width = width;
window.Height = height;
@@ -59,7 +59,7 @@ namespace Juliet::Win32
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
const int w = window->Width, h = window->Height;
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, window->Title.Data, style, x, y, w, h, nullptr, nullptr,
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, window->Title.Str, style, x, y, w, h, nullptr, nullptr,
instance, nullptr);
PumpEvents(self);
@@ -22,7 +22,7 @@ namespace Juliet
DWORD attributes = GetFileAttributesA(path);
return (attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY);
}
}
} // namespace
String GetBasePath()
{
@@ -71,10 +71,7 @@ namespace Juliet
// Probe candidate paths for compiled shader directory
// 1. Shipping layout: Assets/Shaders/ next to the exe
// 2. Dev layout: ../../Assets/compiled/ (exe is in bin/x64Clang-<Config>/)
constexpr const char* kCandidates[] = {
"Assets/Shaders/",
"../../Assets/compiled/"
};
constexpr const char* kCandidates[] = { "Assets/Shaders/", "../../Assets/compiled/" };
for (const char* candidate : kCandidates)
{
@@ -103,12 +100,12 @@ namespace Juliet
if (IsValid(CachedBasePath))
{
CachedBasePath.Size = 0;
SafeFree(CachedBasePath.Data);
SafeFree(CachedBasePath.Str);
}
if (IsValid(CachedAssetBasePath))
{
CachedAssetBasePath.Size = 0;
SafeFree(CachedAssetBasePath.Data);
SafeFree(CachedAssetBasePath.Str);
}
}
} // namespace Juliet
@@ -58,7 +58,7 @@ namespace Juliet::Platform
bool IsAbsolutePath(String path)
{
if (!path.Data || path.Size == 0)
if (!path.Str || path.Size == 0)
{
return false;
}
@@ -69,7 +69,7 @@ namespace Juliet::Platform
// A disk designator with a backslash, for example "C:\" or "d:\".
// A single backslash, for example, "\directory" or "\file.txt". This is also referred to as an absolute path.
// We will only handle the first two. Single backslash is weird.
const char* pathStr = path.Data;
const char* pathStr = path.Str;
size_t pathLen = path.Size;
if (pathLen > 1)
+12 -12
View File
@@ -24,8 +24,8 @@ namespace Juliet::Internal
int64 FileSize(NonNullPtr<IOStreamDataPayload> payload)
{
auto win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
LARGE_INTEGER size;
Win32IOStreamDataPayload* win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
LARGE_INTEGER size;
if (!GetFileSizeEx(win32Payload->Handle, &size))
{
@@ -38,7 +38,7 @@ namespace Juliet::Internal
int64 FileSeek(NonNullPtr<IOStreamDataPayload> payload, int64 offset, IOStreamSeekPivot pivot)
{
auto win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
Win32IOStreamDataPayload* win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
if ((pivot == IOStreamSeekPivot::Current) && (win32Payload->SizeLeft > 0))
{
offset -= static_cast<int64>(win32Payload->SizeLeft);
@@ -69,10 +69,10 @@ namespace Juliet::Internal
size_t FileRead(NonNullPtr<IOStreamDataPayload> payload, void* outBuffer, size_t size, NonNullPtr<IOStreamStatus> status)
{
auto win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
size_t totalNeed = size;
size_t totalRead = 0;
size_t sizeToReadAhead = 0;
Win32IOStreamDataPayload* win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
size_t totalNeed = size;
size_t totalRead = 0;
size_t sizeToReadAhead = 0;
if (win32Payload->SizeLeft > 0)
{
uint8* data = static_cast<uint8*>(win32Payload->Data) + win32Payload->Size - win32Payload->SizeLeft;
@@ -133,8 +133,8 @@ namespace Juliet::Internal
size_t FileWrite(NonNullPtr<IOStreamDataPayload> payload, ByteBuffer inBuffer, NonNullPtr<IOStreamStatus> status)
{
auto win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
DWORD bytes;
Win32IOStreamDataPayload* win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
DWORD bytes;
if (win32Payload->SizeLeft)
{
@@ -204,12 +204,12 @@ namespace Juliet::Internal
"Mode should have at most 2 characters, one being either r,w or a and the other can only be +");
if (modeLength == 1)
{
Assert((mode.Data[0] == 'r' || mode.Data[0] == 'w' || mode.Data[0] == 'a') &&
Assert((mode.Str[0] == 'r' || mode.Str[0] == 'w' || mode.Str[0] == 'a') &&
"Invalid Mode. First char is not r,w or a");
}
else
{
Assert((mode.Data[1] == '+' || mode.Data[1] == 'b') && "Invalid Mode. Second char is not +");
Assert((mode.Str[1] == '+' || mode.Str[1] == 'b') && "Invalid Mode. Second char is not +");
}
#endif
@@ -238,7 +238,7 @@ namespace Juliet::Internal
}
constexpr bool autoClose = true;
auto payload = static_cast<Win32IOStreamDataPayload*>(Calloc(1, sizeof(Win32IOStreamDataPayload)));
Win32IOStreamDataPayload* payload = static_cast<Win32IOStreamDataPayload*>(Calloc(1, sizeof(Win32IOStreamDataPayload)));
if (!payload)
{
if (autoClose)
+13 -1
View File
@@ -1,5 +1,6 @@
#include <Core/HAL/OS/OS.h>
#include <Core/HAL/OS/OS_Private.h>
#include <Core/Thread/Thread.h>
namespace Juliet
{
@@ -61,7 +62,18 @@ namespace Juliet
int Bootstrap(EntryPointFunc entryPointFunc, int argc, wchar_t** argv)
{
return Internal::OS_Main(entryPointFunc, argc, argv);
int result = Internal::OS_Main(argc, argv);
if (result == 0)
{
set_thread_name(WrapString("main_thread"));
result = entryPointFunc(argc, argv);
}
Internal::OS_Main_Exit();
return result;
}
} // namespace Juliet
+3 -2
View File
@@ -22,6 +22,7 @@ namespace Juliet
namespace Internal
{
int OS_Main(EntryPointFunc entryPointFunc, int argc, wchar_t** argv);
}
int OS_Main(int argc, wchar_t** argv);
void OS_Main_Exit();
} // namespace Internal
} // namespace Juliet
+25 -5
View File
@@ -1,17 +1,21 @@
#include <Core/Common/CoreTypes.h>
#include <Core/Common/CoreUtils.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/HAL/OS/OS.h>
#include <Core/HAL/OS/OS_Private.h>
#include <Core/HAL/Win32.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Thread/ThreadContext.h>
namespace Juliet
{
Win32_SetThreadDescription_FuncType* Win32_SetThreadDescription_Func = nullptr;
namespace
{
global RIO_EXTENSION_FUNCTION_TABLE w32_rio_functions = {};
}
RIO_EXTENSION_FUNCTION_TABLE w32_rio_functions = {};
} // namespace
namespace Memory::Internal
{
@@ -97,7 +101,9 @@ namespace Juliet
namespace Internal
{
int OS_Main(EntryPointFunc entryPointFunc, int argc, wchar_t** argv)
thread_local thread_context* mainThread = nullptr;
int OS_Main(int argc, wchar_t** argv)
{
SetUnhandledExceptionFilter(&ExceptionFilter);
@@ -109,6 +115,11 @@ namespace Juliet
return EXIT_FAILURE;
}
// Get some kernel32 functions
auto dynLib = LoadDynamicLibrary("kernel32.dll");
Win32_SetThreadDescription_Func =
(Win32_SetThreadDescription_FuncType*)LoadFunction(dynLib, "SetThreadDescription");
// Create a dummy socket to access RIO functions. Those will be used to do fine memory management
{
WSADATA WinSockData;
@@ -121,8 +132,17 @@ namespace Juliet
closesocket(Sock);
}
int result = entryPointFunc(argc, argv);
return result;
mainThread = thread_context_alloc();
thread_context_select(mainThread);
return 0;
}
void OS_Main_Exit()
{
Assert(mainThread);
thread_context_release(mainThread);
mainThread = nullptr;
}
} // namespace Internal
} // namespace Juliet
+2
View File
@@ -75,3 +75,5 @@
#undef min
#undef max
using Win32_SetThreadDescription_FuncType = HRESULT(HANDLE hThread, PCWSTR lpThreadDescription);
+3 -3
View File
@@ -11,7 +11,7 @@ namespace Juliet
{
void InitHotReloadCode(HotReloadCode& code, String dllName, String transientDllName, String lockFilename)
{
code.Arena = ArenaAllocate({ .ReserveSize = Megabytes(1) } JULIET_DEBUG_ONLY(, "Hot Reload"));
code.Arena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "Hot Reload" });
// Get the app base path and build the dll path from there.
String basePath = GetBasePath();
@@ -25,7 +25,7 @@ namespace Juliet
const size_t dllFullPathLength =
basePathLength + StringLength(dllName) + 1; // Need +1 because snprintf needs 0 terminated strings
code.DLLFullPath.Data =
code.DLLFullPath.Str =
static_cast<char*>(ArenaPush(code.Arena, dllFullPathLength, alignof(char), true JULIET_DEBUG_PARAM("DLL Path")));
int writtenSize = snprintf(CStr(code.DLLFullPath), dllFullPathLength, "%s%s", CStr(basePath), CStr(dllName));
if (writtenSize < static_cast<int>(dllFullPathLength) - 1)
@@ -39,7 +39,7 @@ namespace Juliet
// Lock filename path
const size_t lockPathLength =
basePathLength + StringLength(lockFilename) + 1; // Need +1 because snprintf needs 0 terminated strings
code.LockFullPath.Data =
code.LockFullPath.Str =
static_cast<char*>(ArenaPush(code.Arena, lockPathLength, alignof(char), true JULIET_DEBUG_PARAM("Lock File Path")));
writtenSize = snprintf(CStr(code.LockFullPath), lockPathLength, "%s%s", CStr(basePath), CStr(lockFilename));
if (writtenSize < static_cast<int>(lockPathLength) - 1)
@@ -34,11 +34,11 @@ namespace Juliet
// TODO : Create and use a TransientAllocator
// Create temp dll name
char* lockFilename = code.LockFullPath.Data;
char* lockFilename = code.LockFullPath.Str;
WIN32_FILE_ATTRIBUTE_DATA Ignored;
if (!GetFileAttributesExA(lockFilename, GetFileExInfoStandard, &Ignored))
{
const char* dllName = code.DLLFullPath.Data;
const char* dllName = code.DLLFullPath.Str;
FILETIME lastWriteTime = GetLastWriteTime(dllName);
ULARGE_INTEGER result{ .LowPart = lastWriteTime.dwLowDateTime, .HighPart = lastWriteTime.dwHighDateTime };
@@ -145,7 +145,7 @@ namespace Juliet
codeLastWriteTime.dwHighDateTime = largeInt.HighPart;
codeLastWriteTime.dwLowDateTime = largeInt.LowPart;
FILETIME lastWriteTime = GetLastWriteTime(code.DLLFullPath.Data);
FILETIME lastWriteTime = GetLastWriteTime(code.DLLFullPath.Str);
int compare = CompareFileTime(&lastWriteTime, &codeLastWriteTime);
return compare != 0;
}
+1 -1
View File
@@ -75,7 +75,7 @@ namespace Juliet::ImGuiService
Assert(!g_Initialized);
// Initialize ImGui Arena using Engine Pool
g_ImGuiArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "ImGui"));
g_ImGuiArena = ArenaAllocate({ .Name = "Juliet" });
// Setup Allocator
ImGui::SetAllocatorFunctions(ImGuiAllocWrapper, ImGuiFreeWrapper, nullptr);
+1 -1
View File
@@ -59,7 +59,7 @@ namespace Juliet
Logs* LogAllocate()
{
Arena* arena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Log Manager"));
Arena* arena = ArenaAllocate({ .Name = "Log Manager" });
Logs* logs = ArenaPushStruct<Logs>(arena);
logs->Arena = arena;
return logs;
+16 -9
View File
@@ -18,17 +18,15 @@ namespace Juliet
// https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c
Arena* ArenaAllocate(const ArenaParams& params JULIET_DEBUG_ONLY(, const char* name), const std::source_location& loc)
Arena* ArenaAllocate(const ArenaParams& params, const std::source_location& loc)
{
Log(LogLevel::Message, LogCategory::Core, "Allocating from %s : %ul", loc.file_name(), loc.line());
Byte* baseMem = nullptr;
uint64 reserve_size = AlignPow2(params.ReserveSize, k_PageSize);
uint64 commit_size = AlignPow2(params.CommitSize, k_PageSize);
// TODO: handle large pages
baseMem = Memory::OS_Reserve(reserve_size);
Byte* baseMem = Memory::OS_Reserve(reserve_size);
Memory::OS_Commit(baseMem, commit_size);
Arena* arena = reinterpret_cast<Arena*>(baseMem);
@@ -47,7 +45,16 @@ namespace Juliet
arena->CanReserveMore = params.CanReserveMore;
arena->FirstDebugInfo = nullptr;
DebugArenaSetDebugName(arena, name);
if (params.Name == nullptr)
{
arena->Name = "NoName";
}
else
{
arena->Name = params.Name;
}
DebugArenaSetDebugName(arena, arena->Name);
DebugRegisterArena(arena);
#endif
@@ -112,8 +119,7 @@ namespace Juliet
commitSize = AlignPow2(size + k_ArenaHeaderSize, align);
}
newBlock =
ArenaAllocate({ .ReserveSize = reserveSize, .CommitSize = commitSize } JULIET_DEBUG_ONLY(, arena->Name));
newBlock = ArenaAllocate({ .ReserveSize = reserveSize, .CommitSize = commitSize, .Name = arena->Name });
}
newBlock->BasePosition = current->BasePosition + current->Reserved;
@@ -151,8 +157,9 @@ namespace Juliet
{
result = reinterpret_cast<Byte*>(current) + positionPrePush;
current->Position = positionPostPush;
JULIET_DEBUG_ONLY(if (!IsDebugInfoArena(arena)) { DebugArenaAddDebugInfo(current, size, positionPrePush, tag); })
JULIET_DEBUG_ONLY(
if (!IsDebugInfoArena(arena)) { DebugArenaAddDebugInfo(current, size, positionPrePush, tag); })
if (sizeToZero != 0)
{
+5 -5
View File
@@ -24,8 +24,8 @@ namespace Juliet
if (!g_DebugInfoArena)
{
// Create a dedicated arena for debug info
g_DebugInfoArena = ArenaAllocate(
{ .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64) } JULIET_DEBUG_ONLY(, "Debug Info Arena"));
g_DebugInfoArena =
ArenaAllocate({ .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64), .Name = "Debug Info Arena" });
}
return ArenaPushStruct<ArenaDebugInfo>(g_DebugInfoArena JULIET_DEBUG_PARAM("ArenaDebugInfo"));
@@ -35,8 +35,8 @@ namespace Juliet
{
if (!g_DebugInfoArena)
{
g_DebugInfoArena = ArenaAllocate(
{ .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64) } JULIET_DEBUG_ONLY(, "Debug Info Arena"));
g_DebugInfoArena =
ArenaAllocate({ .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64), .Name = "Debug Info Arena" });
}
return g_DebugInfoArena;
}
@@ -150,7 +150,7 @@ namespace Juliet
if (tag)
{
String copiedTag = StringCopy(g_DebugInfoArena, WrapString(tag));
info->Tag = copiedTag.Data;
info->Tag = copiedTag.Str;
}
else
{
+2 -2
View File
@@ -19,8 +19,8 @@ namespace Juliet::UnitTest
printf("Running Paged Memory Arena Tests...\n");
// New Arena!
ArenaParams param{ .ReserveSize = Megabytes(64llu), .CommitSize = Kilobytes(64llu) };
Arena* testArena = ArenaAllocate(param JULIET_DEBUG_ONLY(, "Test Arena"));
ArenaParams param{ .ReserveSize = Megabytes(64llu), .CommitSize = Kilobytes(64llu), .Name = "Test Arena" };
Arena* testArena = ArenaAllocate(param);
size_t pos = ArenaPos(testArena);
Assert(pos == k_ArenaHeaderSize);
+76
View File
@@ -0,0 +1,76 @@
#include <Core/Thread/ThreadContext.h>
#include <Core/Thread/Thread.h>
namespace Juliet
{
thread_local thread_context* local_thread_context;
thread_context* thread_context_alloc()
{
thread_local char name[2][1024];
juliet_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", thread_id());
juliet_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", thread_id());
Arena* arena_0 = ArenaAllocate({ .Name = name[0] });
Arena* arena_1 = ArenaAllocate({ .Name = name[1] });
thread_context* ctx = ArenaPushStruct<thread_context>(arena_0);
ctx->ScratchArenas[0] = arena_0;
ctx->ScratchArenas[1] = arena_1;
return ctx;
}
void thread_context_release(NonNullPtr<thread_context> ctx)
{
ArenaRelease(ctx->ScratchArenas[1]);
ArenaRelease(ctx->ScratchArenas[0]);
}
void thread_context_select(NonNullPtr<thread_context> ctx)
{
local_thread_context = ctx.Get();
}
thread_context* thread_context_current()
{
Assert(local_thread_context);
return local_thread_context;
}
Arena* thread_context_get_scratch(Arena** conflicts, size_t count)
{
thread_context* ctx = thread_context_current();
Arena* result = nullptr;
Arena** arena_ptr = ctx->ScratchArenas;
for (size_t i = 0; i < ArraySize(ctx->ScratchArenas); i += 1, arena_ptr += 1)
{
Arena** conflict_ptr = conflicts;
bool has_conflict = false;
for (size_t j = 0; j < count; j += 1, conflict_ptr += 1)
{
if (*arena_ptr == *conflict_ptr)
{
has_conflict = true;
break;
}
}
if (!has_conflict)
{
result = *arena_ptr;
break;
}
}
return result;
}
TempArena scratch_begin(Arena** conflicts, size_t count)
{
return ArenaTempBegin(thread_context_get_scratch(conflicts, count));
}
void scratch_end(TempArena scratch)
{
ArenaTempEnd(scratch);
}
} // namespace Juliet
+63
View File
@@ -0,0 +1,63 @@
#include <Core/Thread/Thread.h>
#include <Core/HAL/Win32.h>
#include <Core/Thread/ThreadContext.h>
#include <processthreadsapi.h>
namespace Juliet
{
uint32 thread_id()
{
uint32 id = GetCurrentThreadId();
return id;
}
extern Win32_SetThreadDescription_FuncType* Win32_SetThreadDescription_Func;
void set_thread_name(String name)
{
TempArena scratch = scratch_begin(0, 0);
// New way:
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription
// Old way:
// https://learn.microsoft.com/fr-fr/previous-versions/visualstudio/visual-studio-2015/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2015&redirectedfrom=MSDN
if (Win32_SetThreadDescription_Func != nullptr)
{
String16 name16 = str16_from_8(scratch.Arena, name);
Win32_SetThreadDescription_Func(GetCurrentThread(), (WCHAR*)name16.Str);
}
String8 name_copy = StringCopy(scratch.Arena, name);
#pragma pack(push, 8)
struct THREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
};
#pragma pack(pop)
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = CStr(name_copy);
info.dwThreadID = thread_id();
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable : 6320 6322)
__try
{
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
#pragma warning(pop)
scratch_end(scratch);
}
} // namespace Juliet
+2 -2
View File
@@ -46,7 +46,7 @@ namespace Juliet::Debug
{
if (s_PagedArenaStates.Arena == nullptr)
{
Arena* pagedArenaStates = ArenaAllocate({} JULIET_DEBUG_ONLY(, "DebugState States"));
Arena* pagedArenaStates = ArenaAllocate({ .Name = "DebugState States" });
s_PagedArenaStates.Create(pagedArenaStates);
}
@@ -105,7 +105,7 @@ namespace Juliet::Debug
static VectorArena<const Arena*> blocks;
if (blocks.Arena == nullptr)
{
Arena* pagedArenaStates = ArenaAllocate({} JULIET_DEBUG_ONLY(, "DebugState Blocks"));
Arena* pagedArenaStates = ArenaAllocate({ .Name = "DebugState Blocks" });
blocks.Create(pagedArenaStates);
}
blocks.Clear();
+2 -4
View File
@@ -151,10 +151,8 @@ namespace Juliet
void InitializeEngine(JulietInit_Flags flags)
{
EngineInstance.PlatformArena =
ArenaAllocate({ .ReserveSize = Megabytes(128) } JULIET_DEBUG_PARAM("Platform Arena"));
EngineInstance.AssetArena =
ArenaAllocate({ .ReserveSize = Megabytes(256) } JULIET_DEBUG_PARAM("Asset Arena"));
EngineInstance.PlatformArena = ArenaAllocate({ .ReserveSize = Megabytes(128), .Name = "Platform Arena" });
EngineInstance.AssetArena = ArenaAllocate({ .ReserveSize = Megabytes(256), .Name = "Asset Arena" });
InitializeLogManager();
@@ -4230,7 +4230,6 @@ namespace Juliet
driver->D3D12SerializeVersionedRootSignatureFct = nullptr;
Assert(ArenaPos(driver->DriverArena) == sizeof(D3D12Driver)); // Verify we didnt forget to release something
ArenaRelease(driver->DriverArena);
}
@@ -4314,7 +4313,7 @@ namespace Juliet
GraphicsDevice* D3D12_CreateGraphicsDevice(bool enableDebug)
{
Arena* driverArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "D3D12 Driver Arena"));
Arena* driverArena = ArenaAllocate({ .Name = "D3D12 Driver Arena" });
D3D12Driver* driver = ArenaPushStruct<D3D12Driver>(driverArena JULIET_DEBUG_PARAM("D3D12Driver struct"));
driver->DriverArena = driverArena;
+1 -1
View File
@@ -328,7 +328,7 @@ namespace Juliet
// TODO: Add path builder in the lib
String base = GetBasePath();
char inplaceBuffer[256];
snprintf(inplaceBuffer, sizeof(inplaceBuffer), "%s%s", base.Data, filename.Data);
snprintf(inplaceBuffer, sizeof(inplaceBuffer), "%s%s", base.Str, filename.Str);
String absolutePath = WrapString(inplaceBuffer);
shaderByteCode = LoadFile(absolutePath);
}
@@ -28,8 +28,8 @@ namespace Juliet::UnitTest
void VectorUnitTest()
{
ArenaParams params{};
Arena* testArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorUnitTestArena"));
ArenaParams params{ .Name = "VectorUnitTestArena" };
Arena* testArena = ArenaAllocate(params);
// Test 1: Integer VectorArena (Basic Operations)
{