Compare commits

..

3 Commits

29 changed files with 443 additions and 342 deletions

View File

@@ -156,7 +156,6 @@
<CustomBuild Include="src\Core\Math\Math_Private.h" /> <CustomBuild Include="src\Core\Math\Math_Private.h" />
<CustomBuild Include="src\Core\Math\MathRound.cpp" /> <CustomBuild Include="src\Core\Math\MathRound.cpp" />
<CustomBuild Include="src\Core\Memory\Allocator.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\MemoryArena.cpp" />
<CustomBuild Include="src\Core\Memory\MemoryArenaTests.cpp" /> <CustomBuild Include="src\Core\Memory\MemoryArenaTests.cpp" />
<CustomBuild Include="src\Core\Memory\ScratchArena.cpp" /> <CustomBuild Include="src\Core\Memory\ScratchArena.cpp" />

View File

@@ -315,9 +315,6 @@
<CustomBuild Include="src\Core\Memory\Allocator.cpp"> <CustomBuild Include="src\Core\Memory\Allocator.cpp">
<Filter>src\Core\Memory</Filter> <Filter>src\Core\Memory</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="src\Core\Memory\EngineArena.h">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\MemoryArena.cpp"> <CustomBuild Include="src\Core\Memory\MemoryArena.cpp">
<Filter>src\Core\Memory</Filter> <Filter>src\Core\Memory</Filter>
</CustomBuild> </CustomBuild>

View File

@@ -25,6 +25,8 @@ struct ByteBuffer
size_t Size; size_t Size;
}; };
using ptrdiff_t = decltype(static_cast<int*>(nullptr) - static_cast<int*>(nullptr));
using FunctionPtr = auto (*)(void) -> void; using FunctionPtr = auto (*)(void) -> void;
// Limits // Limits

View File

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

View File

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

View File

@@ -9,6 +9,8 @@ namespace Juliet
struct HotReloadCode struct HotReloadCode
{ {
Arena* Arena;
String DLLFullPath; String DLLFullPath;
String LockFullPath; String LockFullPath;
String TransientDLLName; String TransientDLLName;
@@ -26,8 +28,7 @@ namespace Juliet
bool IsValid : 1; bool IsValid : 1;
}; };
extern JULIET_API void InitHotReloadCode(HotReloadCode& code, String dllName, extern JULIET_API void InitHotReloadCode(HotReloadCode& code, String dllName, String transientDllName, String lockFilename);
String transientDllName, String lockFilename);
extern JULIET_API void ShutdownHotReloadCode(HotReloadCode& code); extern JULIET_API void ShutdownHotReloadCode(HotReloadCode& code);
extern JULIET_API void LoadCode(HotReloadCode& code); extern JULIET_API void LoadCode(HotReloadCode& code);

View File

@@ -13,7 +13,8 @@ namespace Juliet
constexpr global uint64 g_Arena_Default_Commit_Size = Kilobytes(64); constexpr global uint64 g_Arena_Default_Commit_Size = Kilobytes(64);
constexpr global uint64 k_ArenaHeaderSize = 128; constexpr global uint64 k_ArenaHeaderSize = 128;
// Refactor struct ArenaFreeNode;
struct Arena struct Arena
{ {
Arena* Previous; Arena* Previous;
@@ -29,7 +30,14 @@ namespace Juliet
uint64 Committed; uint64 Committed;
uint64 Reserved; uint64 Reserved;
Arena* FreeLast; Arena* FreeBlockLast;
ArenaFreeNode* FreeNodes;
bool AllowRealloc : 1;
JULIET_DEBUG_ONLY(uint16 LostNodeCount);
JULIET_DEBUG_ONLY(bool CanReserveMore : 1;)
}; };
static_assert(sizeof(Arena) <= k_ArenaHeaderSize); static_assert(sizeof(Arena) <= k_ArenaHeaderSize);
@@ -37,6 +45,13 @@ namespace Juliet
{ {
uint64 ReserveSize = g_Arena_Default_Reserve_Size; uint64 ReserveSize = g_Arena_Default_Reserve_Size;
uint64 CommitSize = g_Arena_Default_Commit_Size; uint64 CommitSize = g_Arena_Default_Commit_Size;
// True: All push will be 32 bytes minimum
bool AllowRealloc = false;
// When false, will assert if a new block is reserved.
// Useful for Vectors as they are guaranteed to be linear and i wont need to implement memcopy to increase capacity
JULIET_DEBUG_ONLY(bool CanReserveMore : 1 = true;)
}; };
[[nodiscard]] Arena* ArenaAllocate(const ArenaParams& params = {}, [[nodiscard]] Arena* ArenaAllocate(const ArenaParams& params = {},
@@ -45,6 +60,8 @@ namespace Juliet
// Raw Push, can be used but templated helpers exists below // Raw Push, can be used but templated helpers exists below
[[nodiscard]] void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, bool shouldBeZeroed); [[nodiscard]] void* ArenaPush(NonNullPtr<Arena> arena, size_t size, size_t align, bool shouldBeZeroed);
[[nodiscard]] void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize,
size_t align, bool shouldBeZeroed);
void ArenaPopTo(NonNullPtr<Arena> arena, size_t position); void ArenaPopTo(NonNullPtr<Arena> arena, size_t position);
void ArenaPop(NonNullPtr<Arena> arena, size_t amount); void ArenaPop(NonNullPtr<Arena> arena, size_t amount);
void ArenaClear(NonNullPtr<Arena> arena); void ArenaClear(NonNullPtr<Arena> arena);

View File

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

View File

@@ -21,8 +21,10 @@
#ifdef DEBUG #ifdef DEBUG
#define JULIET_DEBUG 1 #define JULIET_DEBUG 1
#define JULIET_DEBUG_ONLY(expr) expr
#else #else
#define JULIET_DEBUG 0 #define JULIET_DEBUG 0
#define JULIET_DEBUG_ONLY(expr)
#endif #endif
// Manual override to disable ImGui // Manual override to disable ImGui

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,8 +2,6 @@
#include <Core/HotReload/HotReload.h> #include <Core/HotReload/HotReload.h>
#include <Core/Logging/LogManager.h> #include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h> #include <Core/Logging/LogTypes.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h>
#include <Core/Memory/MemoryArena.h> #include <Core/Memory/MemoryArena.h>
#include <Core/Thread/Thread.h> #include <Core/Thread/Thread.h>
@@ -13,6 +11,8 @@ namespace Juliet
{ {
void InitHotReloadCode(HotReloadCode& code, String dllName, String transientDllName, String lockFilename) void InitHotReloadCode(HotReloadCode& code, String dllName, String transientDllName, String lockFilename)
{ {
code.Arena = ArenaAllocate();
// Get the app base path and build the dll path from there. // Get the app base path and build the dll path from there.
String basePath = GetBasePath(); String basePath = GetBasePath();
size_t basePathLength = StringLength(basePath); size_t basePathLength = StringLength(basePath);
@@ -24,7 +24,8 @@ namespace Juliet
// TODO: Add path composition into filesystem + string format + string builder // TODO: Add path composition into filesystem + string format + string builder
const size_t dllFullPathLength = const size_t dllFullPathLength =
basePathLength + StringLength(dllName) + 1; // Need +1 because snprintf needs 0 terminated strings basePathLength + StringLength(dllName) + 1; // Need +1 because snprintf needs 0 terminated strings
code.DLLFullPath.Data = ArenaPushArray<char>(GetEngineArena(), dllFullPathLength, ConstString("DLLFullPath"));
code.DLLFullPath.Data = ArenaPushArray<char>(code.Arena, dllFullPathLength);
int writtenSize = snprintf(CStr(code.DLLFullPath), dllFullPathLength, "%s%s", CStr(basePath), CStr(dllName)); int writtenSize = snprintf(CStr(code.DLLFullPath), dllFullPathLength, "%s%s", CStr(basePath), CStr(dllName));
if (writtenSize < static_cast<int>(dllFullPathLength) - 1) if (writtenSize < static_cast<int>(dllFullPathLength) - 1)
{ {
@@ -37,7 +38,7 @@ namespace Juliet
// Lock filename path // Lock filename path
const size_t lockPathLength = const size_t lockPathLength =
basePathLength + StringLength(lockFilename) + 1; // Need +1 because snprintf needs 0 terminated strings basePathLength + StringLength(lockFilename) + 1; // Need +1 because snprintf needs 0 terminated strings
code.LockFullPath.Data = ArenaPushArray<char>(GetEngineArena(), lockPathLength, ConstString("LockFullPath")); code.LockFullPath.Data = ArenaPushArray<char>(code.Arena, lockPathLength);
writtenSize = snprintf(CStr(code.LockFullPath), lockPathLength, "%s%s", CStr(basePath), CStr(lockFilename)); writtenSize = snprintf(CStr(code.LockFullPath), lockPathLength, "%s%s", CStr(basePath), CStr(lockFilename));
if (writtenSize < static_cast<int>(lockPathLength) - 1) if (writtenSize < static_cast<int>(lockPathLength) - 1)
{ {
@@ -59,6 +60,8 @@ namespace Juliet
// Arena memory persists until engine shutdown // Arena memory persists until engine shutdown
code.LockFullPath.Size = 0; code.LockFullPath.Size = 0;
// Arena memory persists until engine shutdown // Arena memory persists until engine shutdown
ArenaRelease(code.Arena);
} }
void ReloadCode(HotReloadCode& code) void ReloadCode(HotReloadCode& code)

View File

@@ -9,7 +9,6 @@
#include <backends/imgui_impl_win32.h> #include <backends/imgui_impl_win32.h>
#include <imgui.h> #include <imgui.h>
#include <Core/Memory/EngineArena.h>
#include <cstdio> #include <cstdio>
// Forward declare implementation functions from backends // Forward declare implementation functions from backends
@@ -24,34 +23,16 @@ namespace Juliet::ImGuiService
// Dedicated Paged Arena for ImGui // Dedicated Paged Arena for ImGui
// Sharing the same underlying Engine Pool for blocks, but separate Arena chain. // Sharing the same underlying Engine Pool for blocks, but separate Arena chain.
MemoryArena g_ImGuiArena; Arena* g_ImGuiArena = {};
void* ImGuiAllocWrapper(size_t size, void* /*user_data*/) void* ImGuiAllocWrapper(size_t size, void* /*user_data*/)
{ {
// Store size in header to allow Pop return ArenaPush(g_ImGuiArena, size, 8, false);
// Align total size to 16 to avoid padding issues with ArenaPop LIFO check
size_t actualSize = size + 16;
actualSize = (actualSize + 15) & ~static_cast<size_t>(15);
// We do save the size when we push so we can pop exactly the size.
if (void* ptr = ArenaPush(&g_ImGuiArena, actualSize, 16, ConstString("ImGui")))
{
// Write size at start
*static_cast<size_t*>(ptr) = actualSize;
return static_cast<uint8*>(ptr) + 16;
}
return nullptr;
} }
void ImGuiFreeWrapper(void* ptr, void* /*user_data*/) void ImGuiFreeWrapper(void* /*ptr*/, void* /*user_data*/)
{ {
Assert(ptr); // TODO : Free list of imgui elements.
uint8* originalPtr = static_cast<uint8*>(ptr) - 16;
size_t actualSize = *reinterpret_cast<size_t*>(originalPtr);
// Attempt LIFO Pop
ArenaPop(&g_ImGuiArena, originalPtr, actualSize);
} }
} // namespace } // namespace
@@ -60,7 +41,7 @@ namespace Juliet::ImGuiService
Assert(!g_Initialized); Assert(!g_Initialized);
// Initialize ImGui Arena using Engine Pool // Initialize ImGui Arena using Engine Pool
MemoryArenaCreate(&g_ImGuiArena, GetEngineArena()->BackingPool); g_ImGuiArena = ArenaAllocate();
// Setup Allocator // Setup Allocator
ImGui::SetAllocatorFunctions(ImGuiAllocWrapper, ImGuiFreeWrapper, nullptr); ImGui::SetAllocatorFunctions(ImGuiAllocWrapper, ImGuiFreeWrapper, nullptr);

View File

@@ -1,10 +0,0 @@
#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

@@ -17,6 +17,20 @@ namespace Juliet
constexpr uint64 k_PageSize = Kilobytes(4); constexpr uint64 k_PageSize = Kilobytes(4);
} // namespace } // namespace
struct ArenaFreeNode
{
ArenaFreeNode* Next;
ArenaFreeNode* Previous;
index_t Position;
size_t Size;
};
namespace
{
static_assert(sizeof(ArenaFreeNode) == 32, "ArenaFreeNode should be 32 bytes and not more");
constexpr size_t k_ArenaFreeNodeSize = sizeof(ArenaFreeNode);
} // namespace
// https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c // https://github.com/EpicGamesExt/raddebugger/blob/master/src/base/base_arena.c
Arena* ArenaAllocate(const ArenaParams& params, const std::source_location& loc) Arena* ArenaAllocate(const ArenaParams& params, const std::source_location& loc)
@@ -44,6 +58,11 @@ namespace Juliet
arena->BasePosition = 0; arena->BasePosition = 0;
arena->Position = k_ArenaHeaderSize; arena->Position = k_ArenaHeaderSize;
arena->FreeNodes = nullptr;
arena->AllowRealloc = params.AllowRealloc;
arena->CanReserveMore = params.CanReserveMore;
return arena; return arena;
} }
@@ -62,13 +81,56 @@ namespace Juliet
size_t positionPrePush = AlignPow2(current->Position, align); size_t positionPrePush = AlignPow2(current->Position, align);
size_t positionPostPush = positionPrePush + size; size_t positionPostPush = positionPrePush + size;
if (arena->AllowRealloc)
{
size = Max(size, k_ArenaFreeNodeSize);
for (ArenaFreeNode* freeNode = current->FreeNodes; freeNode != nullptr; freeNode = freeNode->Next)
{
if (size <= freeNode->Size)
{
index_t position = freeNode->Position;
size_t remainingSize = freeNode->Size - size;
if (remainingSize < k_ArenaFreeNodeSize)
{
ArenaFreeNode* previous = freeNode->Previous;
ArenaFreeNode* next = freeNode->Next;
if (previous)
{
previous->Next = next;
}
if (next)
{
next->Previous = previous;
}
JULIET_DEBUG_ONLY(remainingSize > 0 ? ++current->LostNodeCount : current->LostNodeCount);
}
else
{
freeNode->Position += size;
}
auto* result = reinterpret_cast<Byte*>(current) + position;
if (shouldBeZeroed)
{
MemoryZero(result, size);
}
return result;
}
}
}
// If allowed and needed, add a new block and chain it to the arena. // If allowed and needed, add a new block and chain it to the arena.
if (current->Reserved < positionPostPush /* flags : chaining allowed */) if (current->Reserved < positionPostPush /* flags : chaining allowed */)
{ {
Assert(arena->CanReserveMore);
Arena* newBlock = nullptr; Arena* newBlock = nullptr;
{ {
Arena* prev_block; Arena* prev_block;
for (newBlock = arena->FreeLast, prev_block = nullptr; newBlock != nullptr; for (newBlock = arena->FreeBlockLast, prev_block = nullptr; newBlock != nullptr;
prev_block = newBlock, newBlock = newBlock->Previous) prev_block = newBlock, newBlock = newBlock->Previous)
{ {
if (newBlock->Reserved >= AlignPow2(newBlock->Position, align) + size) if (newBlock->Reserved >= AlignPow2(newBlock->Position, align) + size)
@@ -79,7 +141,7 @@ namespace Juliet
} }
else else
{ {
arena->FreeLast = newBlock->Previous; arena->FreeBlockLast = newBlock->Previous;
} }
break; break;
} }
@@ -149,6 +211,42 @@ namespace Juliet
return result; return result;
} }
void* ArenaReallocate(NonNullPtr<Arena> arena, void* oldPtr, size_t oldSize, size_t newSize, size_t align, bool shouldBeZeroed)
{
// Find the correct block
Arena* block = nullptr;
for (block = arena->Current; block != nullptr; block = block->Previous)
{
if ((block < oldPtr) && (oldPtr <= (block + block->Reserved)))
{
break;
}
}
Assert(block != nullptr);
MemoryZero(oldPtr, oldSize);
ArenaFreeNode* freeNode = static_cast<ArenaFreeNode*>(oldPtr);
ptrdiff_t posPtr = static_cast<Byte*>(oldPtr) - reinterpret_cast<Byte*>(block);
index_t position = static_cast<index_t>(posPtr) - block->BasePosition;
freeNode->Position = position;
freeNode->Size = oldSize;
if (block->FreeNodes != nullptr)
{
block->FreeNodes = freeNode;
}
else
{
freeNode->Previous = block->FreeNodes;
if (block->FreeNodes != nullptr)
{
block->FreeNodes->Next = freeNode;
}
block->FreeNodes = freeNode;
}
return ArenaPush(arena, newSize, align, shouldBeZeroed);
}
void ArenaPopTo(NonNullPtr<Arena> arena, size_t position) void ArenaPopTo(NonNullPtr<Arena> arena, size_t position)
{ {
size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position); size_t clampedPosition = ClampBottom(k_ArenaHeaderSize, position);
@@ -158,7 +256,7 @@ namespace Juliet
{ {
previous = current->Previous; previous = current->Previous;
current->Position = k_ArenaHeaderSize; current->Position = k_ArenaHeaderSize;
SingleLinkedListPushPrevious(arena->FreeLast, current); SingleLinkedListPushPrevious(arena->FreeBlockLast, current);
} }
// No freelist : // No freelist :
@@ -552,11 +650,9 @@ namespace Juliet
namespace namespace
{ {
MemoryPool g_ScratchMemory; MemoryPool g_ScratchMemory;
MemoryPool g_EngineMemory;
MemoryPool g_GameMemory; MemoryPool g_GameMemory;
MemoryArena g_ScratchArena; MemoryArena g_ScratchArena;
MemoryArena g_EngineArena;
MemoryArena g_GameArena; MemoryArena g_GameArena;
// Backing Buffers // Backing Buffers
@@ -565,7 +661,6 @@ namespace Juliet
void* g_GameBuffer = nullptr; void* g_GameBuffer = nullptr;
constexpr size_t kScratchSize = Megabytes(64); constexpr size_t kScratchSize = Megabytes(64);
constexpr size_t kEngineSize = Megabytes(256);
constexpr size_t kGameSize = Megabytes(512); constexpr size_t kGameSize = Megabytes(512);
void InitPool(MemoryPool* pool, void* buffer, size_t size) void InitPool(MemoryPool* pool, void* buffer, size_t size)
@@ -589,10 +684,7 @@ namespace Juliet
{ {
return &g_ScratchArena; return &g_ScratchArena;
} }
MemoryArena* GetEngineArena()
{
return &g_EngineArena;
}
MemoryArena* GetGameArena() MemoryArena* GetGameArena()
{ {
return &g_GameArena; return &g_GameArena;
@@ -606,15 +698,12 @@ namespace Juliet
void MemoryArenasInit() void MemoryArenasInit()
{ {
g_ScratchBuffer = Malloc(kScratchSize); g_ScratchBuffer = Malloc(kScratchSize);
g_EngineBuffer = Malloc(kEngineSize);
g_GameBuffer = Malloc(kGameSize); g_GameBuffer = Malloc(kGameSize);
InitPool(&g_ScratchMemory, g_ScratchBuffer, kScratchSize); InitPool(&g_ScratchMemory, g_ScratchBuffer, kScratchSize);
InitPool(&g_EngineMemory, g_EngineBuffer, kEngineSize);
InitPool(&g_GameMemory, g_GameBuffer, kGameSize); InitPool(&g_GameMemory, g_GameBuffer, kGameSize);
MemoryArenaCreate(&g_ScratchArena, &g_ScratchMemory); MemoryArenaCreate(&g_ScratchArena, &g_ScratchMemory);
MemoryArenaCreate(&g_EngineArena, &g_EngineMemory);
MemoryArenaCreate(&g_GameArena, &g_GameMemory); MemoryArenaCreate(&g_GameArena, &g_GameMemory);
#if JULIET_DEBUG #if JULIET_DEBUG

View File

@@ -139,40 +139,22 @@ namespace Juliet::UnitTest
pool.FreeList = blk; pool.FreeList = blk;
} }
MemoryArena arena; {
MemoryArenaCreate(&arena, &pool); // Test reallocate
Arena* arena = ArenaAllocate({ .AllowRealloc = true });
char* charArray = ArenaPushArray<char>(arena, 128);
char* secondCharArray = ArenaPushArray<char>(arena, 128);
char* thirdCharArray = ArenaPushArray<char>(arena, 128);
// 5. Arena Pop secondCharArray = static_cast<char*>(ArenaReallocate(arena, secondCharArray, 128, 256, alignof(char), true));
// Align sizes to 16 to avoid padding issues during Pop
void* pop1 = ArenaPush(&arena, 5008, 16, ConstString("Pop1"));
void* pop2 = ArenaPush(&arena, 208, 16, ConstString("Pop2")); char* fourthCharArray = ArenaPushArray<char>(arena, 128);
// Pop Middle (Should Fail)
bool res1 = ArenaPop(&arena, pop1, 5008);
Assert(res1 == false);
// Pop Top (Should Success) Assert(charArray);
bool res2 = ArenaPop(&arena, pop2, 208); // 200->208 Assert(secondCharArray);
Assert(res2 == true); Assert(thirdCharArray);
Assert(fourthCharArray);
// Verify Used space is back to pop1 end }
Assert(arena.CurrentBlock->Used == ArenaGetMarker(&arena).Offset); // This usage of GetMarker is valid if marker was implicit?
// Actually we didn't take a marker.
// We can verify by allocating pop3. It should overwrite pop2 location.
void* pop3 = ArenaPush(&arena, 16, 16, ConstString("Pop3"));
Assert(pop3 == pop2);
// Cleanup popped items from stack logic for reset...
// Pop pop3
ArenaPop(&arena, pop3, 16);
// Pop pop1
ArenaPop(&arena, pop1, 5008);
Assert(arena.CurrentBlock->Used == 0); // Should be effectively 0
printf("[Success] Arena Pop\n");
// Cleanup
SafeFree(testBacking);
printf("All Paged MemoryArena tests passed.\n"); printf("All Paged MemoryArena tests passed.\n");
} }

View File

@@ -1,5 +1,4 @@
#include <Core/Common/String.h> #include <Core/Common/String.h>
#include <Core/Memory/EngineArena.h>
#include <Engine/Debug/MemoryDebugger.h> #include <Engine/Debug/MemoryDebugger.h>
#include <imgui.h> #include <imgui.h>
@@ -420,7 +419,6 @@ namespace Juliet::Debug
if (ImGui::Begin("Memory Debugger")) if (ImGui::Begin("Memory Debugger"))
{ {
DrawMemoryArena(ConstString("Game Arena"), *GetGameArena(), s_ConfirmedHovered, frameHovered); DrawMemoryArena(ConstString("Game Arena"), *GetGameArena(), s_ConfirmedHovered, frameHovered);
DrawMemoryArena(ConstString("Engine Arena"), *GetEngineArena(), s_ConfirmedHovered, frameHovered);
} }
ImGui::End(); ImGui::End();

View File

@@ -454,8 +454,11 @@ namespace Juliet::D3D12
return false; return false;
} }
ReturnSamplerHeapToPool(driver, commandList->Sampler_Heap); if (commandList->Sampler_Heap) [[likely]]
commandList->Sampler_Heap = nullptr; {
ReturnSamplerHeapToPool(driver, commandList->Sampler_Heap);
commandList->Sampler_Heap = nullptr;
}
commandList->CRB_SRV_UAV_Heap = nullptr; commandList->CRB_SRV_UAV_Heap = nullptr;
// Clean up resource tracking // Clean up resource tracking

View File

@@ -4,8 +4,8 @@
#include <Graphics/D3D12/D3D12Common.h> #include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/D3D12GraphicsDevice.h> #include <Graphics/D3D12/D3D12GraphicsDevice.h>
#include <Graphics/D3D12/D3D12Includes.h> #include <Graphics/D3D12/D3D12Includes.h>
#include <Graphics/D3D12/D3D12Utils.h>
// TODO: Convert the whole file to memory arenas
namespace Juliet::D3D12::Internal namespace Juliet::D3D12::Internal
{ {
namespace namespace
@@ -25,8 +25,8 @@ namespace Juliet::D3D12::Internal
bool ExtendStagingDescriptorPool(NonNullPtr<D3D12Driver> driver, D3D12StagingDescriptorPool& pool) bool ExtendStagingDescriptorPool(NonNullPtr<D3D12Driver> driver, D3D12StagingDescriptorPool& pool)
{ {
D3D12DescriptorHeap* heap = D3D12DescriptorHeap* heap = Internal::CreateDescriptorHeap(driver, pool.Arena, pool.Heaps[0]->HeapType,
Internal::CreateDescriptorHeap(driver, pool.Heaps[0]->HeapType, kStagingHeapDescriptorExpectedCount, true); kStagingHeapDescriptorExpectedCount, true);
if (!heap) if (!heap)
{ {
return false; return false;
@@ -49,13 +49,17 @@ namespace Juliet::D3D12::Internal
D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type) D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type)
{ {
D3D12DescriptorHeap* heap = CreateDescriptorHeap(driver, type, kStagingHeapDescriptorExpectedCount, true); Arena* arena = ArenaAllocate();
D3D12DescriptorHeap* heap = CreateDescriptorHeap(driver, arena, type, kStagingHeapDescriptorExpectedCount, true);
if (!heap) if (!heap)
{ {
ArenaRelease(arena);
return nullptr; return nullptr;
} }
auto pool = static_cast<D3D12StagingDescriptorPool*>(Calloc(1, sizeof(D3D12StagingDescriptorPool))); auto pool = static_cast<D3D12StagingDescriptorPool*>(Calloc(1, sizeof(D3D12StagingDescriptorPool)));
pool->Arena = arena;
// First create the heaps // First create the heaps
pool->HeapCount = 1; pool->HeapCount = 1;
@@ -113,6 +117,8 @@ namespace Juliet::D3D12::Internal
Free(pool->Heaps); Free(pool->Heaps);
Free(pool->FreeDescriptors); Free(pool->FreeDescriptors);
ArenaRelease(pool->Arena);
Free(pool.Get()); Free(pool.Get());
} }
} // namespace Juliet::D3D12::Internal } // namespace Juliet::D3D12::Internal

View File

@@ -22,8 +22,9 @@ namespace Juliet::D3D12
struct D3D12StagingDescriptorPool struct D3D12StagingDescriptorPool
{ {
Arena* Arena;
Internal::D3D12DescriptorHeap** Heaps; Internal::D3D12DescriptorHeap** Heaps;
uint32 HeapCount; uint32 HeapCount;
// Descriptor handles are owned by resources, so these can be thought of as descriptions of a free index within a heap. // Descriptor handles are owned by resources, so these can be thought of as descriptions of a free index within a heap.
uint32 FreeDescriptorCapacity; uint32 FreeDescriptorCapacity;
@@ -35,9 +36,9 @@ namespace Juliet::D3D12
struct D3D12StagingDescriptor struct D3D12StagingDescriptor
{ {
D3D12StagingDescriptorPool* Pool; D3D12StagingDescriptorPool* Pool;
Internal::D3D12DescriptorHeap* Heap; Internal::D3D12DescriptorHeap* Heap;
D3D12_CPU_DESCRIPTOR_HANDLE CpuHandle; D3D12_CPU_DESCRIPTOR_HANDLE CpuHandle;
uint32 CpuHandleIndex; uint32 CpuHandleIndex;
}; };
namespace Internal namespace Internal

View File

@@ -1,26 +1,52 @@
#include <Core/Logging/LogManager.h> #include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h> #include <Core/Logging/LogTypes.h>
#include <Core/Memory/Allocator.h> #include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h>
#include <Graphics/D3D12/D3D12DescriptorHeap.h> #include <Graphics/D3D12/D3D12DescriptorHeap.h>
#include <Graphics/D3D12/D3D12GraphicsDevice.h> #include <Graphics/D3D12/D3D12GraphicsDevice.h>
#include <Graphics/D3D12/D3D12Utils.h> #include <Graphics/D3D12/D3D12Utils.h>
namespace Juliet::D3D12::Internal namespace Juliet::D3D12::Internal
{ {
D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging) void CreateDescriptorHeapPool(NonNullPtr<D3D12Driver> driver, D3D12DescriptorHeapPool& heapPool,
D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count)
{ {
auto heap = ArenaPushType<D3D12DescriptorHeap>(GetEngineArena(), ConstString("D3D12DescriptorHeap")); // Heap pool is just single linked list of free elements
if (!heap) constexpr size_t kInitialCapacity = 4;
heapPool.Arena = ArenaAllocate();
heapPool.FirstFreeDescriptorHeap = nullptr;
// Pre allocate 4
for (uint32 i = 0; i < kInitialCapacity; ++i)
{ {
return nullptr; D3D12DescriptorHeap* descriptorHeap = CreateDescriptorHeap(driver, heapPool.Arena, type, count, false);
descriptorHeap->Next = heapPool.FirstFreeDescriptorHeap;
heapPool.FirstFreeDescriptorHeap = descriptorHeap;
} }
}
void DestroyDescriptorHeapPool(D3D12DescriptorHeapPool& heapPool)
{
D3D12DescriptorHeap* current = heapPool.FirstFreeDescriptorHeap;
while (current != nullptr)
{
D3D12DescriptorHeap* next = current->Next;
DestroyDescriptorHeap(current);
current = next;
}
ArenaRelease(heapPool.Arena);
}
D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, NonNullPtr<Arena> arena,
D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging)
{
D3D12DescriptorHeap* heap = ArenaPushStruct<D3D12DescriptorHeap>(arena.Get());
Assert(heap);
heap->CurrentDescriptorIndex = 0; heap->CurrentDescriptorIndex = 0;
heap->FreeIndicesCapacity = 16;
heap->FreeIndicesCount = 0; heap->FreeIndices.Create();
heap->FreeIndices = ArenaPushArray<uint32>(GetEngineArena(), heap->FreeIndicesCapacity, heap->FreeIndices.Resize(16);
ConstString("D3D12DescriptorHeap/FreeIndices")); heap->CurrentFreeIndex = 0;
D3D12_DESCRIPTOR_HEAP_DESC heapDesc; D3D12_DESCRIPTOR_HEAP_DESC heapDesc;
heapDesc.NumDescriptors = count; heapDesc.NumDescriptors = count;
@@ -54,23 +80,21 @@ namespace Juliet::D3D12::Internal
void DestroyDescriptorHeap(NonNullPtr<D3D12DescriptorHeap> heap) void DestroyDescriptorHeap(NonNullPtr<D3D12DescriptorHeap> heap)
{ {
heap->FreeIndices.Destroy();
if (heap->Handle) if (heap->Handle)
{ {
heap->Handle->Release(); heap->Handle->Release();
} }
// TODO: should not free because its on an arena, but need to use arena properly
// SafeFree(heap->FreeIndices);
// Free(heap.Get());
} }
bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor) bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor)
{ {
uint32 index = UINT32_MAX; uint32 index = UINT32_MAX;
if (heap->FreeIndicesCount > 0) if (heap->CurrentFreeIndex > 0)
{ {
heap->FreeIndicesCount -= 1; heap->CurrentFreeIndex -= 1;
index = heap->FreeIndices[heap->FreeIndicesCount]; index = heap->FreeIndices[heap->CurrentFreeIndex];
} }
else if (heap->CurrentDescriptorIndex < heap->MaxDescriptors) else if (heap->CurrentDescriptorIndex < heap->MaxDescriptors)
{ {
@@ -102,55 +126,42 @@ namespace Juliet::D3D12::Internal
D3D12DescriptorHeap* heap = descriptor.Heap; D3D12DescriptorHeap* heap = descriptor.Heap;
if (heap->FreeIndicesCount >= heap->FreeIndicesCapacity) if (heap->CurrentFreeIndex >= heap->FreeIndices.Count)
{ {
size_t oldCapacity = heap->FreeIndicesCapacity; heap->FreeIndices.PushBack(descriptor.Index);
heap->FreeIndicesCapacity *= 2;
heap->FreeIndices = ArenaRealloc<uint32>(GetEngineArena(), heap->FreeIndices, oldCapacity,
heap->FreeIndicesCapacity, ConstString("FreeIndices"));
}
heap->FreeIndices[heap->FreeIndicesCount] = descriptor.Index;
heap->FreeIndicesCount++;
}
D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver, DescriptorHeapCreator creator)
{
D3D12DescriptorHeap* result;
D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool;
if (pool->Count > 0)
{
result = pool->Heaps[pool->Count - 1];
pool->Count -= 1;
} }
else else
{ {
result = creator(d3d12Driver, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, GPUDriver::kSampler_HeapDescriptorCount, false); heap->FreeIndices[heap->CurrentFreeIndex] = descriptor.Index;
heap->CurrentFreeIndex++;
}
}
D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver)
{
D3D12DescriptorHeapPool& pool = d3d12Driver->SamplerHeapPool;
D3D12DescriptorHeap* result = pool.FirstFreeDescriptorHeap;
if (result)
{
pool.FirstFreeDescriptorHeap = pool.FirstFreeDescriptorHeap->Next;
}
else
{
result = CreateDescriptorHeap(d3d12Driver, pool.Arena, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
GPUDriver::kSampler_HeapDescriptorCount, false);
} }
return result; return result;
} }
void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, D3D12DescriptorHeap* heap) void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, NonNullPtr<D3D12DescriptorHeap> heap)
{ {
if (heap == nullptr) D3D12DescriptorHeapPool& pool = d3d12Driver->SamplerHeapPool;
{
return;
}
D3D12DescriptorHeapPool* pool = &d3d12Driver->SamplerHeapPool;
heap->CurrentDescriptorIndex = 0; heap->CurrentDescriptorIndex = 0;
if (pool->Count >= pool->Capacity) heap->Next = pool.FirstFreeDescriptorHeap;
{ pool.FirstFreeDescriptorHeap = heap;
size_t oldCapacity = pool->Capacity;
pool->Capacity *= 2;
pool->Heaps = ArenaRealloc(GetEngineArena(), pool->Heaps, oldCapacity, pool->Capacity, ConstString("Heaps"));
}
pool->Heaps[pool->Count] = heap;
pool->Count += 1;
} }
} // namespace Juliet::D3D12::Internal } // namespace Juliet::D3D12::Internal

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <Core/Common/NonNullPtr.h> #include <Core/Common/NonNullPtr.h>
#include <Core/Networking/NetworkPacket.h>
#include <Graphics/D3D12/D3D12Includes.h> #include <Graphics/D3D12/D3D12Includes.h>
// Forward declare // Forward declare
@@ -14,6 +15,7 @@ namespace Juliet::D3D12::Internal
// https://learn.microsoft.com/en-us/windows/win32/direct3d12/descriptor-heaps // https://learn.microsoft.com/en-us/windows/win32/direct3d12/descriptor-heaps
struct D3D12DescriptorHeap struct D3D12DescriptorHeap
{ {
D3D12DescriptorHeap* Next;
ID3D12DescriptorHeap* Handle; ID3D12DescriptorHeap* Handle;
D3D12_DESCRIPTOR_HEAP_TYPE HeapType; D3D12_DESCRIPTOR_HEAP_TYPE HeapType;
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHeapCPUStart; D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHeapCPUStart;
@@ -22,11 +24,10 @@ namespace Juliet::D3D12::Internal
uint32 DescriptorSize; uint32 DescriptorSize;
uint32 CurrentDescriptorIndex; // only used by GPU heaps uint32 CurrentDescriptorIndex; // only used by GPU heaps
uint32* FreeIndices; VectorArena<uint32> FreeIndices;
uint32 FreeIndicesCapacity; index_t CurrentFreeIndex;
uint32 FreeIndicesCount;
bool Staging : 1; bool Staging : 1;
}; };
struct D3D12Descriptor struct D3D12Descriptor
@@ -39,20 +40,24 @@ namespace Juliet::D3D12::Internal
struct D3D12DescriptorHeapPool struct D3D12DescriptorHeapPool
{ {
D3D12DescriptorHeap** Heaps; Arena* Arena;
size_t Capacity; D3D12DescriptorHeap* FirstFreeDescriptorHeap;
size_t Count;
}; };
using DescriptorHeapCreator = D3D12DescriptorHeap* (*)(NonNullPtr<D3D12Driver>, D3D12_DESCRIPTOR_HEAP_TYPE, uint32, bool); using DescriptorHeapCreator = D3D12DescriptorHeap* (*)(NonNullPtr<D3D12Driver>, D3D12DescriptorHeapPool& heapPool,
D3D12_DESCRIPTOR_HEAP_TYPE, uint32, bool);
extern D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, extern void CreateDescriptorHeapPool(NonNullPtr<D3D12Driver> driver, D3D12DescriptorHeapPool& heapPool,
D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging); D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count);
extern void DestroyDescriptorHeapPool(D3D12DescriptorHeapPool& pool);
extern D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, NonNullPtr<Arena> arena,
D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging);
extern void DestroyDescriptorHeap(NonNullPtr<D3D12DescriptorHeap> heap); extern void DestroyDescriptorHeap(NonNullPtr<D3D12DescriptorHeap> heap);
extern D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver, DescriptorHeapCreator creator = CreateDescriptorHeap); extern D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr<D3D12Driver> d3d12Driver);
extern void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, D3D12DescriptorHeap* heap); extern void ReturnSamplerHeapToPool(NonNullPtr<D3D12Driver> d3d12Driver, NonNullPtr<D3D12DescriptorHeap> heap);
extern bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor); extern bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor);
extern void ReleaseDescriptor(const D3D12Descriptor& descriptor); extern void ReleaseDescriptor(const D3D12Descriptor& descriptor);
} // namespace Juliet::D3D12 } // namespace Juliet::D3D12::Internal

View File

@@ -3,7 +3,6 @@
#include <Core/Logging/LogManager.h> #include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h> #include <Core/Logging/LogTypes.h>
#include <Core/Memory/Allocator.h> #include <Core/Memory/Allocator.h>
#include <Core/Memory/EngineArena.h>
#include <Graphics/D3D12/D3D12Buffer.h> #include <Graphics/D3D12/D3D12Buffer.h>
#include <Graphics/D3D12/D3D12CommandList.h> #include <Graphics/D3D12/D3D12CommandList.h>
#include <Graphics/D3D12/D3D12DescriptorHeap.h> #include <Graphics/D3D12/D3D12DescriptorHeap.h>
@@ -471,22 +470,7 @@ namespace Juliet::D3D12
} }
} }
auto DestroyDescriptorHeapPool = [](Internal::D3D12DescriptorHeapPool& heapPool) Internal::DestroyDescriptorHeapPool(driver->SamplerHeapPool);
{
if (heapPool.Heaps)
{
for (uint32 i = 0; i < heapPool.Count; ++i)
{
if (heapPool.Heaps[i])
{
Internal::DestroyDescriptorHeap(heapPool.Heaps[i]);
heapPool.Heaps[i] = nullptr;
}
}
// SafeFree(heapPool.Heaps); // Allocated with Arena, do not free.
}
};
DestroyDescriptorHeapPool(driver->SamplerHeapPool);
// Release command buffers // Release command buffers
for (uint32 i = 0; i < driver->AvailableCommandListCount; i += 1) for (uint32 i = 0; i < driver->AvailableCommandListCount; i += 1)
@@ -571,6 +555,7 @@ namespace Juliet::D3D12
driver->D3D12SerializeVersionedRootSignatureFct = nullptr; driver->D3D12SerializeVersionedRootSignatureFct = nullptr;
ArenaRelease(driver->DriverArena);
Free(driver.Get()); Free(driver.Get());
} }
@@ -745,6 +730,9 @@ namespace Juliet::D3D12
{ {
auto driver = static_cast<D3D12Driver*>(Calloc(1, sizeof(D3D12Driver))); auto driver = static_cast<D3D12Driver*>(Calloc(1, sizeof(D3D12Driver)));
// TODO : Convert everything to arena
driver->DriverArena = ArenaAllocate();
#if JULIET_DEBUG #if JULIET_DEBUG
#ifdef IDXGIINFOQUEUE_SUPPORTED #ifdef IDXGIINFOQUEUE_SUPPORTED
if (enableDebug) if (enableDebug)
@@ -1023,32 +1011,8 @@ namespace Juliet::D3D12
} }
} }
// Other Descriptor pools CreateDescriptorHeapPool(driver, driver->SamplerHeapPool, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
auto CreateDescriptorPool = [&](Internal::D3D12DescriptorHeapPool& heapPool, GPUDriver::kSampler_HeapDescriptorCount);
D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count) -> bool
{
heapPool.Capacity = 4;
heapPool.Count = 4;
heapPool.Heaps = ArenaPushArray<Internal::D3D12DescriptorHeap*>(GetEngineArena(), heapPool.Capacity,
ConstString("DescriptorHeap"));
for (uint32 i = 0; i < heapPool.Capacity; ++i)
{
heapPool.Heaps[i] = Internal::CreateDescriptorHeap(driver, type, count, false);
if (heapPool.Heaps[i] == nullptr)
{
return false;
}
}
return true;
};
if (!CreateDescriptorPool(driver->SamplerHeapPool, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, GPUDriver::kSampler_HeapDescriptorCount))
{
DestroyDriver_Internal(driver);
return nullptr;
}
// Deferred dispose vectors // Deferred dispose vectors
driver->GraphicsPipelinesToDisposeCapacity = 4; driver->GraphicsPipelinesToDisposeCapacity = 4;
@@ -1125,8 +1089,9 @@ namespace Juliet::D3D12
driver->GraphicsDevice = device; driver->GraphicsDevice = device;
// Create Global Bindless Heap that stays alive for the driver whole lifetime // Create Global Bindless Heap that stays alive for the driver whole lifetime
driver->BindlessDescriptorHeap = Internal::CreateDescriptorHeap(driver, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, driver->BindlessDescriptorHeap =
GPUDriver::kCBV_SRV_UAV_HeapDescriptorCount, false); Internal::CreateDescriptorHeap(driver, driver->DriverArena, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
GPUDriver::kCBV_SRV_UAV_HeapDescriptorCount, false);
return device; return device;
} }

View File

@@ -30,6 +30,8 @@ namespace Juliet
struct GPUDriver struct GPUDriver
{ {
Arena* DriverArena = {};
static constexpr uint8 kMaxFramesInFlight = 3; static constexpr uint8 kMaxFramesInFlight = 3;
static constexpr uint8 kMaxColorTargetInfo = 4; static constexpr uint8 kMaxColorTargetInfo = 4;
static constexpr uint8 kMaxUniformBuffersPerStage = 4; static constexpr uint8 kMaxUniformBuffersPerStage = 4;
@@ -74,9 +76,6 @@ namespace Juliet
void (*DrawIndexedPrimitives)(NonNullPtr<CommandList> commandList, uint32 numIndices, uint32 numInstances, void (*DrawIndexedPrimitives)(NonNullPtr<CommandList> commandList, uint32 numIndices, uint32 numInstances,
uint32 firstIndex, uint32 vertexOffset, uint32 firstInstance); uint32 firstIndex, uint32 vertexOffset, uint32 firstInstance);
void (*SetPushConstants)(NonNullPtr<CommandList> commandList, ShaderStage stage, uint32 rootParameterIndex, void (*SetPushConstants)(NonNullPtr<CommandList> commandList, ShaderStage stage, uint32 rootParameterIndex,
uint32 numConstants, const void* constants); uint32 numConstants, const void* constants);
@@ -108,10 +107,10 @@ namespace Juliet
void* (*MapGraphicsTransferBuffer)(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsTransferBuffer> buffer); void* (*MapGraphicsTransferBuffer)(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsTransferBuffer> buffer);
void (*UnmapGraphicsTransferBuffer)(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsTransferBuffer> buffer); void (*UnmapGraphicsTransferBuffer)(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsTransferBuffer> buffer);
void (*CopyBuffer)(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> dst, void (*CopyBuffer)(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> dst,
NonNullPtr<GraphicsTransferBuffer> src, size_t size, size_t dstOffset, size_t srcOffset); NonNullPtr<GraphicsTransferBuffer> src, size_t size, size_t dstOffset, size_t srcOffset);
void (*CopyBufferToTexture)(NonNullPtr<CommandList> commandList, NonNullPtr<Texture> dst, void (*CopyBufferToTexture)(NonNullPtr<CommandList> commandList, NonNullPtr<Texture> dst,
NonNullPtr<GraphicsTransferBuffer> src); // Assume full copy for now NonNullPtr<GraphicsTransferBuffer> src); // Assume full copy for now
void (*TransitionBufferToReadable)(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> buffer); void (*TransitionBufferToReadable)(NonNullPtr<CommandList> commandList, NonNullPtr<GraphicsBuffer> buffer);
uint32 (*GetDescriptorIndex)(NonNullPtr<GraphicsDevice> device, NonNullPtr<GraphicsBuffer> buffer); uint32 (*GetDescriptorIndex)(NonNullPtr<GraphicsDevice> device, NonNullPtr<GraphicsBuffer> buffer);

View File

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