From 85f867b9f7c5e25233cc6dc0278f0df59ada2b97 Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Sun, 15 Mar 2026 21:02:37 +0100 Subject: [PATCH 1/8] calculate pool count during compilation --- Engine/CMakeLists.txt | 2 +- Engine/src/Core/Engine.cpp | 3 + Engine/src/Core/Math.h | 18 ++++ Engine/src/Core/POTSlabAllocator.cpp | 138 ------------------------- Engine/src/Core/POTSlabAllocator.h | 148 ++++++++++++++++++++++++--- 5 files changed, 158 insertions(+), 151 deletions(-) create mode 100644 Engine/src/Core/Math.h delete mode 100644 Engine/src/Core/POTSlabAllocator.cpp diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt index 339edbf..5c31358 100644 --- a/Engine/CMakeLists.txt +++ b/Engine/CMakeLists.txt @@ -22,10 +22,10 @@ set(SNGL_PLATFORM_SOURCES set(SNGL_CORE_SOURCES "include/sngl/Core/Engine.h" "src/Core/Engine.cpp" + "src/Core/Math.h" "src/Core/LinearArenaAllocator.h" "src/Core/LinearArenaAllocator.cpp" "src/Core/POTSlabAllocator.h" - "src/Core/POTSlabAllocator.cpp" ) set(SNGL_GRAPHICS_SOURCES diff --git a/Engine/src/Core/Engine.cpp b/Engine/src/Core/Engine.cpp index 4cc63f4..3c0574e 100644 --- a/Engine/src/Core/Engine.cpp +++ b/Engine/src/Core/Engine.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -37,6 +38,8 @@ void Engine::init() m_window->init("Singularity Engine"); m_renderer->init(); m_isRunning = true; + + core::POTSlabAllocator<512 * 1024> slab; } void Engine::run() diff --git a/Engine/src/Core/Math.h b/Engine/src/Core/Math.h new file mode 100644 index 0000000..7b02455 --- /dev/null +++ b/Engine/src/Core/Math.h @@ -0,0 +1,18 @@ +#ifndef __SNGL_CORE_MATH_H_INCLUDED__ +#define __SNGL_CORE_MATH_H_INCLUDED__ + +#include + +namespace sngl::core::math +{ + template + requires std::is_integral_v + constexpr uint32_t log2(T x) + { + uint32_t log = 1; + while (x >>= 1) log++; + return log; + } +} + +#endif __SNGL_CORE_MATH_H_INCLUDED__ \ No newline at end of file diff --git a/Engine/src/Core/POTSlabAllocator.cpp b/Engine/src/Core/POTSlabAllocator.cpp deleted file mode 100644 index 07e817d..0000000 --- a/Engine/src/Core/POTSlabAllocator.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "POTSlabAllocator.h" - -#include -#include -#include -#include - -using namespace sngl::core; - -void POTSlabAllocator::FixedSizePool::init(size_t blockSize) -{ - assert(blockSize >= sizeof(Node) && "blockSize must be large enough to hold a pointer"); - m_blockSize = blockSize; -} - -void POTSlabAllocator::FixedSizePool::supplyNewPage(void* rawMemory, size_t pageSize) -{ - size_t blocksInPage = pageSize / m_blockSize; - uint8_t* pageStart = static_cast(rawMemory); - - for (size_t i = 0; i < blocksInPage; i++) - { - uint8_t* currentBlockAddress = pageStart + (m_blockSize * i); - Node* node = reinterpret_cast(currentBlockAddress); - node->next = m_freeList; - m_freeList = node; - } -} - -void* POTSlabAllocator::FixedSizePool::pop() -{ - if (!m_freeList) - return nullptr; - - Node* freeBlock = m_freeList; - m_freeList = freeBlock->next; - - return freeBlock; -} - -void POTSlabAllocator::FixedSizePool::push(void* ptr) -{ - if (!ptr) - return; - - Node* node = reinterpret_cast(ptr); - node->next = m_freeList; - m_freeList = node; -} - -POTSlabAllocator::POTSlabAllocator(size_t reservationSize) - : m_commitedSize(0) -{ - assert(sngl::platform::IsInitialized()); - m_pageSize = sngl::platform::GetPageSize(); - - m_reservedSize = (reservationSize + m_pageSize - 1) & ~(m_pageSize - 1); - m_reservedPtr = sngl::platform::memory::Reserve(reservationSize); - - size_t currentPoolSize = MIN_POOL_SIZE; - for (size_t i = 0; i < POOL_COUNT; i++) - { - m_pools[i].init(currentPoolSize); - currentPoolSize *= 2; - } -} - -POTSlabAllocator::~POTSlabAllocator() -{ - sngl::platform::memory::Release(m_reservedPtr); -} - -void* POTSlabAllocator::allocate(size_t size) -{ - const size_t potSize = std::max(MIN_POOL_SIZE, std::bit_ceil(size + sizeof(AllocHeader))); - const int poolIndex = getPoolIndexForSize(potSize); - - void* rawMemory = nullptr; - if (poolIndex >= 0) - { - rawMemory = m_pools[poolIndex].pop(); - if (!rawMemory) - { - m_pools[poolIndex].supplyNewPage(requestPage(), m_pageSize); - rawMemory = m_pools[poolIndex].pop(); - } - } - else - { - // fallback - rawMemory = sngl::platform::memory::Reserve(potSize); - if (!rawMemory) - throw std::runtime_error("Failed to allocate memory"); - - sngl::platform::memory::Commit(rawMemory, potSize); - } - - assert(rawMemory); - AllocHeader* allocHeader = reinterpret_cast(rawMemory); - allocHeader->allocationSize = potSize; - - return reinterpret_cast(rawMemory) + sizeof(AllocHeader); -} - -int POTSlabAllocator::getPoolIndexForSize(size_t potSize) const -{ - // log2(x) - 4 is array index - // POT gives us O(1) complexity making this allocator super fast - // since std::bit_width gives us log2(x) + 1, we need to subtract additional 1 - // https://en.cppreference.com/w/cpp/numeric/bit_width.html - const int poolIndex = std::bit_width(potSize) - 5; - if (poolIndex > POOL_COUNT - 1) - return -1; - - return poolIndex; -} - -void POTSlabAllocator::free(void* ptr) -{ - const AllocHeader* header = reinterpret_cast(ptr); - const int poolIndex = getPoolIndexForSize(header->allocationSize); - - if (poolIndex >= 0) - m_pools[poolIndex].push(ptr); - else - sngl::platform::memory::Release(ptr); -} - -void* POTSlabAllocator::requestPage() -{ - uint8_t* currentPtr = static_cast(m_reservedPtr) + m_commitedSize; - uint8_t* newPtr = currentPtr + m_pageSize; - if (!sngl::platform::memory::Commit(newPtr, m_pageSize)) - throw std::runtime_error("POTSlabAllocator: Failed to commit memory."); - - m_commitedSize += m_pageSize; - return newPtr; -} \ No newline at end of file diff --git a/Engine/src/Core/POTSlabAllocator.h b/Engine/src/Core/POTSlabAllocator.h index fd140ee..e6a70ff 100644 --- a/Engine/src/Core/POTSlabAllocator.h +++ b/Engine/src/Core/POTSlabAllocator.h @@ -3,9 +3,15 @@ #include #include +#include +#include +#include + +#include namespace sngl::core { + template class POTSlabAllocator { private: @@ -17,11 +23,46 @@ namespace sngl::core size_t m_blockSize; public: - void init(size_t blockSize); - void supplyNewPage(void* rawMemory, size_t pageSize); + void init(size_t blockSize) + { + assert(blockSize >= sizeof(Node) && "blockSize must be large enough to hold a pointer"); + m_blockSize = blockSize; + } + + void supplyNewPage(void* rawMemory, size_t pageSize) + { + size_t blocksInPage = pageSize / m_blockSize; + uint8_t* pageStart = static_cast(rawMemory); + + for (size_t i = 0; i < blocksInPage; i++) + { + uint8_t* currentBlockAddress = pageStart + (m_blockSize * i); + Node* node = reinterpret_cast(currentBlockAddress); + node->next = m_freeList; + m_freeList = node; + } + } + + void* pop() + { + if (!m_freeList) + return nullptr; + + Node* freeBlock = m_freeList; + m_freeList = freeBlock->next; + + return freeBlock; + } + + void push(void* ptr) + { + if (!ptr) + return; - void* pop(); - void push(void* ptr); + Node* node = reinterpret_cast(ptr); + node->next = m_freeList; + m_freeList = node; + } }; struct AllocHeader @@ -30,9 +71,12 @@ namespace sngl::core }; private: - static constexpr size_t POOL_COUNT = 16; - static constexpr size_t MIN_POOL_SIZE = 16; + static constexpr size_t MIN_POOL_SIZE = 16ull; + static constexpr int POOL_COUNT = math::log2(reservationSize / MIN_POOL_SIZE); + static_assert(reservationSize >= MIN_POOL_SIZE, "Reservation size must be greater or equal than 16"); + static_assert(MIN_POOL_SIZE * (1 << POOL_COUNT) >= reservationSize); + FixedSizePool m_pools[POOL_COUNT]; void* m_reservedPtr; @@ -41,15 +85,95 @@ namespace sngl::core size_t m_pageSize; public: - POTSlabAllocator(size_t reservationSize); - ~POTSlabAllocator(); + POTSlabAllocator() + : m_commitedSize(0) + { + assert(sngl::platform::IsInitialized() && "Allocators can only be used after platform initialization"); + m_pageSize = sngl::platform::GetPageSize(); + + m_reservedSize = (reservationSize + m_pageSize - 1) & ~(m_pageSize - 1); + m_reservedPtr = sngl::platform::memory::Reserve(reservationSize); + + size_t currentPoolSize = MIN_POOL_SIZE; + for (size_t i = 0; i < POOL_COUNT; i++) + { + m_pools[i].init(currentPoolSize); + currentPoolSize *= 2; + } + } - void* allocate(size_t size); - void free(void* ptr); + ~POTSlabAllocator() + { + sngl::platform::memory::Release(m_reservedPtr); + } + + void* allocate(size_t size) + { + const size_t potSize = std::max(MIN_POOL_SIZE, std::bit_ceil(size + sizeof(AllocHeader))); + const int poolIndex = getPoolIndexForSize(potSize); + + void* rawMemory = nullptr; + if (poolIndex >= 0) + { + rawMemory = m_pools[poolIndex].pop(); + if (!rawMemory) + { + m_pools[poolIndex].supplyNewPage(requestPage(), m_pageSize); + rawMemory = m_pools[poolIndex].pop(); + } + } + else + { + // fallback + rawMemory = sngl::platform::memory::Reserve(potSize); + if (!rawMemory) + throw std::runtime_error("Failed to allocate memory"); + + sngl::platform::memory::Commit(rawMemory, potSize); + } + + assert(rawMemory); + AllocHeader* allocHeader = reinterpret_cast(rawMemory); + allocHeader->allocationSize = potSize; + + return reinterpret_cast(rawMemory) + sizeof(AllocHeader); + } + + void free(void* ptr) + { + const AllocHeader* header = reinterpret_cast(ptr); + const int poolIndex = getPoolIndexForSize(header->allocationSize); + + if (poolIndex >= 0) + m_pools[poolIndex].push(ptr); + else + sngl::platform::memory::Release(ptr); + } private: - void* requestPage(); - int getPoolIndexForSize(size_t potSize) const; + void* requestPage() + { + uint8_t* currentPtr = static_cast(m_reservedPtr) + m_commitedSize; + uint8_t* newPtr = currentPtr + m_pageSize; + if (!sngl::platform::memory::Commit(newPtr, m_pageSize)) + throw std::runtime_error("POTSlabAllocator: Failed to commit memory."); + + m_commitedSize += m_pageSize; + return newPtr; + } + + int getPoolIndexForSize(size_t potSize) const + { + // log2(x) - 4 is array index + // POT gives us O(1) complexity making this allocator super fast + // since std::bit_width gives us log2(x) + 1, we need to subtract additional 1 + // https://en.cppreference.com/w/cpp/numeric/bit_width.html + const int poolIndex = std::bit_width(potSize) - 5; + if (poolIndex > POOL_COUNT - 1) + return -1; + + return poolIndex; + } }; } From 31bd7b6ed688255c55495e439ef26de4d3d1ff3f Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Sun, 15 Mar 2026 23:01:11 +0100 Subject: [PATCH 2/8] add readme and modify license texts --- CMakeLists.txt | 2 +- Engine/CMakeLists.txt | 2 +- Engine/include/sngl/Core/Engine.h | 2 +- Engine/include/sngl/Graphics/Device.h | 2 +- Engine/include/sngl/Graphics/Instance.h | 2 +- Engine/include/sngl/Graphics/Types.h | 2 +- Engine/src/Core/Engine.cpp | 2 +- Engine/src/Core/LinearArenaAllocator.cpp | 14 +++++ Engine/src/Core/LinearArenaAllocator.h | 14 +++++ Engine/src/Core/POTSlabAllocator.cpp | 14 +++++ Engine/src/Core/POTSlabAllocator.h | 14 +++++ Engine/src/Graphics/Instance.cpp | 14 +++++ Engine/src/Graphics/Vulkan/VulkanDevice.cpp | 2 +- Engine/src/Graphics/Vulkan/VulkanDevice.h | 2 +- Engine/src/Graphics/Vulkan/VulkanInstance.cpp | 2 +- Engine/src/Graphics/Vulkan/VulkanInstance.h | 2 +- Engine/src/Platform/OS.h | 2 +- Engine/src/Platform/OS_win32.cpp | 2 +- Engine/src/Platform/SDLWindow.cpp | 2 +- README.md | 55 +++++++++++++++++++ Sandbox/CMakeLists.txt | 14 +++++ Sandbox/main.cpp | 2 +- 22 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index e521dae..d16f9eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2026 Tesseract Interactive +# Copyright 2026 Singularity Engine # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt index 339edbf..f0f4641 100644 --- a/Engine/CMakeLists.txt +++ b/Engine/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2026 Tesseract Interactive +# Copyright 2026 Singularity Engine # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/Engine/include/sngl/Core/Engine.h b/Engine/include/sngl/Core/Engine.h index bc3d40d..634e4a4 100644 --- a/Engine/include/sngl/Core/Engine.h +++ b/Engine/include/sngl/Core/Engine.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/include/sngl/Graphics/Device.h b/Engine/include/sngl/Graphics/Device.h index f2f4959..3230618 100644 --- a/Engine/include/sngl/Graphics/Device.h +++ b/Engine/include/sngl/Graphics/Device.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/include/sngl/Graphics/Instance.h b/Engine/include/sngl/Graphics/Instance.h index 1e9f988..3d237c6 100644 --- a/Engine/include/sngl/Graphics/Instance.h +++ b/Engine/include/sngl/Graphics/Instance.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/include/sngl/Graphics/Types.h b/Engine/include/sngl/Graphics/Types.h index d426d5c..af3dc2e 100644 --- a/Engine/include/sngl/Graphics/Types.h +++ b/Engine/include/sngl/Graphics/Types.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Core/Engine.cpp b/Engine/src/Core/Engine.cpp index 4cc63f4..022274a 100644 --- a/Engine/src/Core/Engine.cpp +++ b/Engine/src/Core/Engine.cpp @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Core/LinearArenaAllocator.cpp b/Engine/src/Core/LinearArenaAllocator.cpp index 7e5041a..a5660bb 100644 --- a/Engine/src/Core/LinearArenaAllocator.cpp +++ b/Engine/src/Core/LinearArenaAllocator.cpp @@ -1,3 +1,17 @@ +// Copyright 2026 Singularity Engine +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "LinearArenaAllocator.h" #include diff --git a/Engine/src/Core/LinearArenaAllocator.h b/Engine/src/Core/LinearArenaAllocator.h index 670ec01..d14e039 100644 --- a/Engine/src/Core/LinearArenaAllocator.h +++ b/Engine/src/Core/LinearArenaAllocator.h @@ -1,3 +1,17 @@ +// Copyright 2026 Singularity Engine +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef __SNGL_CORE_LINEARARENAALLOCATOR_H_INCLUDED__ #define __SNGL_CORE_LINEARARENAALLOCATOR_H_INCLUDED__ diff --git a/Engine/src/Core/POTSlabAllocator.cpp b/Engine/src/Core/POTSlabAllocator.cpp index 07e817d..5505928 100644 --- a/Engine/src/Core/POTSlabAllocator.cpp +++ b/Engine/src/Core/POTSlabAllocator.cpp @@ -1,3 +1,17 @@ +// Copyright 2026 Singularity Engine +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "POTSlabAllocator.h" #include diff --git a/Engine/src/Core/POTSlabAllocator.h b/Engine/src/Core/POTSlabAllocator.h index fd140ee..10bae33 100644 --- a/Engine/src/Core/POTSlabAllocator.h +++ b/Engine/src/Core/POTSlabAllocator.h @@ -1,3 +1,17 @@ +// Copyright 2026 Singularity Engine +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef __SNGL_CORE_POTSLABALLOCATOR_H_INCLUDED__ #define __SNGL_CORE_POTSLABALLOCATOR_H_INCLUDED__ diff --git a/Engine/src/Graphics/Instance.cpp b/Engine/src/Graphics/Instance.cpp index 9ff7567..420918d 100644 --- a/Engine/src/Graphics/Instance.cpp +++ b/Engine/src/Graphics/Instance.cpp @@ -1,3 +1,17 @@ +// Copyright 2026 Singularity Engine +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include "Vulkan/VulkanInstance.h" diff --git a/Engine/src/Graphics/Vulkan/VulkanDevice.cpp b/Engine/src/Graphics/Vulkan/VulkanDevice.cpp index 3a7c3c6..b36de8a 100644 --- a/Engine/src/Graphics/Vulkan/VulkanDevice.cpp +++ b/Engine/src/Graphics/Vulkan/VulkanDevice.cpp @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Graphics/Vulkan/VulkanDevice.h b/Engine/src/Graphics/Vulkan/VulkanDevice.h index b9101d8..ef3fcd7 100644 --- a/Engine/src/Graphics/Vulkan/VulkanDevice.h +++ b/Engine/src/Graphics/Vulkan/VulkanDevice.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Graphics/Vulkan/VulkanInstance.cpp b/Engine/src/Graphics/Vulkan/VulkanInstance.cpp index d6fac2b..e15f6c0 100644 --- a/Engine/src/Graphics/Vulkan/VulkanInstance.cpp +++ b/Engine/src/Graphics/Vulkan/VulkanInstance.cpp @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Graphics/Vulkan/VulkanInstance.h b/Engine/src/Graphics/Vulkan/VulkanInstance.h index 4b2cc36..4667bc6 100644 --- a/Engine/src/Graphics/Vulkan/VulkanInstance.h +++ b/Engine/src/Graphics/Vulkan/VulkanInstance.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Platform/OS.h b/Engine/src/Platform/OS.h index d317e6c..2ee89c1 100644 --- a/Engine/src/Platform/OS.h +++ b/Engine/src/Platform/OS.h @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Platform/OS_win32.cpp b/Engine/src/Platform/OS_win32.cpp index 6f0116c..47138a1 100644 --- a/Engine/src/Platform/OS_win32.cpp +++ b/Engine/src/Platform/OS_win32.cpp @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Engine/src/Platform/SDLWindow.cpp b/Engine/src/Platform/SDLWindow.cpp index 2baf0c3..06c8bcb 100644 --- a/Engine/src/Platform/SDLWindow.cpp +++ b/Engine/src/Platform/SDLWindow.cpp @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d076240 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# Singularity Engine +## A lightweight, code driven 3D game engine built for performance + +Singularity Engine is a minimalist game engine designed for people who prefer writing code +over clicking over menus. It prioritizes low-overhead, platform agnosticism, fast compilation and straightforward API. +The engine embraces the verbosity of the newest graphics APIs (Vulkan, D3D12) to deliver performance tuned display system. + +> [!IMPORTANT] +> This project is in very early development state (Alpha). It's not functional yet. +> The API may go through fundamental changes and core features are still being implemented. + +# 🚀 Key Features +- Editor-less Workflow: Build your entire game in your IDE of choice. You don't need to worry about installing ton of heavy SDKs. +- High performance: Minimal abstraction for maximum frame rates. +- Platform agnostic: Support for Windows, Linux and MacOS + +> [!WARNING] +> Abstractions for Linux and MacOS aren't implemented yet, but they will be soon. + +# 🛠 Gettning Started +## Prerequisities +List of what you need to compile and run the engine +- A compiler capable of compiling C++20 +- CMake +- VulkanSDK (only if taking part in engine development around Vulkan API) + +> [!NOTE] +> Please note, that the list of prerequisities may change in the future due to the engine's early state of development. + +## Quick start +``` +# Clone the repository +git clone https://github.com/SingularityEngineDevelopers/SingularityEngine.git + +# Build the engine +cd SingularityEngine +cmake --preset vulkan-release . +cmake --build --preset build-vulkan-release . +``` + +# 🗺 Current Status & Roadmap +- [] Custom memory allocators (in progress) +- [] 3D Rendering +- [] Editor interface (long term goal) + +# 🤝 Contributions +I'm aiming to make this engine one of the fastest indie game engines available. If you'd like to help +in development or bug fixes, feel free to open a pull request! +1. Fork the project +2. Create a branch +3. Commit +4. Open a Pull Request + +# ⚖️ License +This project is licensed under Apache License, version 2.0. See the [License.md](/LICENSE.md) file for details. diff --git a/Sandbox/CMakeLists.txt b/Sandbox/CMakeLists.txt index c584b02..42ef297 100644 --- a/Sandbox/CMakeLists.txt +++ b/Sandbox/CMakeLists.txt @@ -1,3 +1,17 @@ +# Copyright 2026 Singularity Engine +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + add_executable(Singularity_SandboxApp "main.cpp") target_link_libraries( Singularity_SandboxApp diff --git a/Sandbox/main.cpp b/Sandbox/main.cpp index 5236a51..cdb2ba9 100644 --- a/Sandbox/main.cpp +++ b/Sandbox/main.cpp @@ -1,4 +1,4 @@ -// Copyright 2026 Tesseract Interactive +// Copyright 2026 Singularity Engine // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 5b63b3b0ce13bca4a984dbdcdb6c4deb851ff80c Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Sun, 15 Mar 2026 23:07:36 +0100 Subject: [PATCH 3/8] add credits --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d076240..17bf955 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,9 @@ in development or bug fixes, feel free to open a pull request! 4. Open a Pull Request # ⚖️ License -This project is licensed under Apache License, version 2.0. See the [License.md](/LICENSE.md) file for details. +This project is licensed under Apache License, version 2.0. See the [LICENSE.md](/LICENSE.md) file for details. + +# 📜 Credits +## Open-Source projects +- [SDL3](https://github.com/libsdl-org/SDL): Used for window management and input handling +- [volk](https://github.com/zeux/volk): meta loader for Vulkan to ease development \ No newline at end of file From 5ddfd1bbcdfe5910d7ef3c47bdc27c7f338b02cb Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Sun, 15 Mar 2026 23:08:15 +0100 Subject: [PATCH 4/8] fix checkboxes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 17bf955..e8215f2 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ cmake --build --preset build-vulkan-release . ``` # 🗺 Current Status & Roadmap -- [] Custom memory allocators (in progress) -- [] 3D Rendering -- [] Editor interface (long term goal) +- [ ] Custom memory allocators (in progress) +- [ ] 3D Rendering +- [ ] Editor interface (long term goal) # 🤝 Contributions I'm aiming to make this engine one of the fastest indie game engines available. If you'd like to help From 2eca3557a2cbef286694febed220410b99701694 Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Sun, 15 Mar 2026 23:09:58 +0100 Subject: [PATCH 5/8] final notice --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e8215f2..c7b6a15 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ cmake --build --preset build-vulkan-release . - [ ] 3D Rendering - [ ] Editor interface (long term goal) +> [!IMPORTANT] +> Current roadmap contains only several entries now. More of them are going to be added +> in the future as more ideas would come. + # 🤝 Contributions I'm aiming to make this engine one of the fastest indie game engines available. If you'd like to help in development or bug fixes, feel free to open a pull request! From abc8dbffdf0f8054e4ae9bfc5d26607d65be0441 Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Mon, 16 Mar 2026 03:04:04 +0100 Subject: [PATCH 6/8] restore old changes I accidentally overwritten by readme merging --- Engine/src/Core/POTSlabAllocator.cpp | 152 --------------------------- 1 file changed, 152 deletions(-) delete mode 100644 Engine/src/Core/POTSlabAllocator.cpp diff --git a/Engine/src/Core/POTSlabAllocator.cpp b/Engine/src/Core/POTSlabAllocator.cpp deleted file mode 100644 index 5505928..0000000 --- a/Engine/src/Core/POTSlabAllocator.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2026 Singularity Engine -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "POTSlabAllocator.h" - -#include -#include -#include -#include - -using namespace sngl::core; - -void POTSlabAllocator::FixedSizePool::init(size_t blockSize) -{ - assert(blockSize >= sizeof(Node) && "blockSize must be large enough to hold a pointer"); - m_blockSize = blockSize; -} - -void POTSlabAllocator::FixedSizePool::supplyNewPage(void* rawMemory, size_t pageSize) -{ - size_t blocksInPage = pageSize / m_blockSize; - uint8_t* pageStart = static_cast(rawMemory); - - for (size_t i = 0; i < blocksInPage; i++) - { - uint8_t* currentBlockAddress = pageStart + (m_blockSize * i); - Node* node = reinterpret_cast(currentBlockAddress); - node->next = m_freeList; - m_freeList = node; - } -} - -void* POTSlabAllocator::FixedSizePool::pop() -{ - if (!m_freeList) - return nullptr; - - Node* freeBlock = m_freeList; - m_freeList = freeBlock->next; - - return freeBlock; -} - -void POTSlabAllocator::FixedSizePool::push(void* ptr) -{ - if (!ptr) - return; - - Node* node = reinterpret_cast(ptr); - node->next = m_freeList; - m_freeList = node; -} - -POTSlabAllocator::POTSlabAllocator(size_t reservationSize) - : m_commitedSize(0) -{ - assert(sngl::platform::IsInitialized()); - m_pageSize = sngl::platform::GetPageSize(); - - m_reservedSize = (reservationSize + m_pageSize - 1) & ~(m_pageSize - 1); - m_reservedPtr = sngl::platform::memory::Reserve(reservationSize); - - size_t currentPoolSize = MIN_POOL_SIZE; - for (size_t i = 0; i < POOL_COUNT; i++) - { - m_pools[i].init(currentPoolSize); - currentPoolSize *= 2; - } -} - -POTSlabAllocator::~POTSlabAllocator() -{ - sngl::platform::memory::Release(m_reservedPtr); -} - -void* POTSlabAllocator::allocate(size_t size) -{ - const size_t potSize = std::max(MIN_POOL_SIZE, std::bit_ceil(size + sizeof(AllocHeader))); - const int poolIndex = getPoolIndexForSize(potSize); - - void* rawMemory = nullptr; - if (poolIndex >= 0) - { - rawMemory = m_pools[poolIndex].pop(); - if (!rawMemory) - { - m_pools[poolIndex].supplyNewPage(requestPage(), m_pageSize); - rawMemory = m_pools[poolIndex].pop(); - } - } - else - { - // fallback - rawMemory = sngl::platform::memory::Reserve(potSize); - if (!rawMemory) - throw std::runtime_error("Failed to allocate memory"); - - sngl::platform::memory::Commit(rawMemory, potSize); - } - - assert(rawMemory); - AllocHeader* allocHeader = reinterpret_cast(rawMemory); - allocHeader->allocationSize = potSize; - - return reinterpret_cast(rawMemory) + sizeof(AllocHeader); -} - -int POTSlabAllocator::getPoolIndexForSize(size_t potSize) const -{ - // log2(x) - 4 is array index - // POT gives us O(1) complexity making this allocator super fast - // since std::bit_width gives us log2(x) + 1, we need to subtract additional 1 - // https://en.cppreference.com/w/cpp/numeric/bit_width.html - const int poolIndex = std::bit_width(potSize) - 5; - if (poolIndex > POOL_COUNT - 1) - return -1; - - return poolIndex; -} - -void POTSlabAllocator::free(void* ptr) -{ - const AllocHeader* header = reinterpret_cast(ptr); - const int poolIndex = getPoolIndexForSize(header->allocationSize); - - if (poolIndex >= 0) - m_pools[poolIndex].push(ptr); - else - sngl::platform::memory::Release(ptr); -} - -void* POTSlabAllocator::requestPage() -{ - uint8_t* currentPtr = static_cast(m_reservedPtr) + m_commitedSize; - uint8_t* newPtr = currentPtr + m_pageSize; - if (!sngl::platform::memory::Commit(newPtr, m_pageSize)) - throw std::runtime_error("POTSlabAllocator: Failed to commit memory."); - - m_commitedSize += m_pageSize; - return newPtr; -} \ No newline at end of file From 66757cf81719bae002808d1a5e4a49950becf835 Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Tue, 17 Mar 2026 01:55:47 +0100 Subject: [PATCH 7/8] rewriten POTSlabAllocator from the ground up, still requires edge-cases implementations --- Engine/CMakeLists.txt | 1 + Engine/src/Core/Engine.cpp | 3 - Engine/src/Core/Math.h | 41 ++++- Engine/src/Core/POTSlabAllocator.cpp | 51 +++++++ Engine/src/Core/POTSlabAllocator.h | 217 ++++++++++++--------------- 5 files changed, 183 insertions(+), 130 deletions(-) create mode 100644 Engine/src/Core/POTSlabAllocator.cpp diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt index 17a765f..150cc8b 100644 --- a/Engine/CMakeLists.txt +++ b/Engine/CMakeLists.txt @@ -26,6 +26,7 @@ set(SNGL_CORE_SOURCES "src/Core/LinearArenaAllocator.h" "src/Core/LinearArenaAllocator.cpp" "src/Core/POTSlabAllocator.h" + "src/Core/POTSlabAllocator.cpp" ) set(SNGL_GRAPHICS_SOURCES diff --git a/Engine/src/Core/Engine.cpp b/Engine/src/Core/Engine.cpp index 37c1707..022274a 100644 --- a/Engine/src/Core/Engine.cpp +++ b/Engine/src/Core/Engine.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include @@ -38,8 +37,6 @@ void Engine::init() m_window->init("Singularity Engine"); m_renderer->init(); m_isRunning = true; - - core::POTSlabAllocator<512 * 1024> slab; } void Engine::run() diff --git a/Engine/src/Core/Math.h b/Engine/src/Core/Math.h index 7b02455..313ef7b 100644 --- a/Engine/src/Core/Math.h +++ b/Engine/src/Core/Math.h @@ -2,17 +2,50 @@ #define __SNGL_CORE_MATH_H_INCLUDED__ #include +#include namespace sngl::core::math { template requires std::is_integral_v - constexpr uint32_t log2(T x) + constexpr bool isPOT(T x) { - uint32_t log = 1; - while (x >>= 1) log++; - return log; + return (x > 0) && ((x & (x - 1)) == 0); + } + + template + constexpr size_t floor_log2(T x) + { + if (x == 0) return 0; + return static_cast((std::numeric_limits::digits - 1) - std::countl_zero(x)); + } + + template + constexpr size_t ceil_log2(T x) + { + if (x <= 1) return 0; + + if (std::is_constant_evaluated()) + { + size_t log = 0; + T val = x - 1; + while (val > 0) + { + val >>= 1; + log++; + } + + return log; + } + else + return std::bit_width(x - 1); } } +static_assert(sngl::core::math::isPOT(2ull)); +static_assert(sngl::core::math::floor_log2(2ull) == 1); +static_assert(sngl::core::math::floor_log2(3ull) == 1); +static_assert(sngl::core::math::ceil_log2(2ull) == 1); +static_assert(sngl::core::math::ceil_log2(3ull) == 2); + #endif __SNGL_CORE_MATH_H_INCLUDED__ \ No newline at end of file diff --git a/Engine/src/Core/POTSlabAllocator.cpp b/Engine/src/Core/POTSlabAllocator.cpp new file mode 100644 index 0000000..26ce38a --- /dev/null +++ b/Engine/src/Core/POTSlabAllocator.cpp @@ -0,0 +1,51 @@ +#include "POTSlabAllocator.h" + +using namespace sngl::core; + +FixedSizeBlock::FixedSizeBlock() + : m_freeList(nullptr), + m_blockSize(0), + m_pageSize(0) +{ } + +void FixedSizeBlock::init(size_t blockSize) +{ + m_pageSize = sngl::platform::GetPageSize(); + assert(math::isPOT(blockSize)); // blockSize should be a power of 2 + m_blockSize = blockSize; +} + +void* FixedSizeBlock::pop() +{ + if (m_freeList == nullptr) + return nullptr; // supply a page to partition + + Node* currentNode = m_freeList; + m_freeList = m_freeList->next; + + return m_freeList; +} + +void FixedSizeBlock::push(void* ptr) +{ + if (!ptr) + return; + + Node* newNode = reinterpret_cast(ptr); + newNode->next = m_freeList; + m_freeList = newNode; +} + +void FixedSizeBlock::supplyNewMemory(void* ptr, size_t memSize) +{ + assert(memSize % m_pageSize == 0); // Block size should be a multiple of system page size + const size_t nodeCount = memSize / m_blockSize; + uint8_t* const startPtr = static_cast(ptr); + + for (size_t i = 0; i < nodeCount; i++) + { + Node* node = reinterpret_cast(startPtr + (i * m_blockSize)); + node->next = m_freeList; + m_freeList = node; + } +} \ No newline at end of file diff --git a/Engine/src/Core/POTSlabAllocator.h b/Engine/src/Core/POTSlabAllocator.h index c38584f..b0a5683 100644 --- a/Engine/src/Core/POTSlabAllocator.h +++ b/Engine/src/Core/POTSlabAllocator.h @@ -25,168 +25,139 @@ namespace sngl::core { - template - class POTSlabAllocator + class FixedSizeBlock { private: - class FixedSizePool - { - private: - struct Node { Node* next; }; - Node* m_freeList = nullptr; - size_t m_blockSize; + struct Node { Node* next; }; + Node* m_freeList; + size_t m_blockSize; + size_t m_pageSize; - public: - void init(size_t blockSize) - { - assert(blockSize >= sizeof(Node) && "blockSize must be large enough to hold a pointer"); - m_blockSize = blockSize; - } + public: + FixedSizeBlock(); - void supplyNewPage(void* rawMemory, size_t pageSize) - { - size_t blocksInPage = pageSize / m_blockSize; - uint8_t* pageStart = static_cast(rawMemory); + void init(size_t blockSize); - for (size_t i = 0; i < blocksInPage; i++) - { - uint8_t* currentBlockAddress = pageStart + (m_blockSize * i); - Node* node = reinterpret_cast(currentBlockAddress); - node->next = m_freeList; - m_freeList = node; - } - } + void* pop(); + void push(void* ptr); + void supplyNewMemory(void* ptr, size_t memSize); - void* pop() - { - if (!m_freeList) - return nullptr; + inline size_t getSize() const { return m_blockSize; } + }; - Node* freeBlock = m_freeList; - m_freeList = freeBlock->next; + template + class POTSlabAllocator + { + private: + static constexpr size_t getBlockSize(size_t arrIx) + { + return (1ull << (arrIx + 4)); + } - return freeBlock; - } + static constexpr int getBlockIxFromSize(size_t blockSize) + { + int result = math::ceil_log2(blockSize) - 4; + if (result > MAX_BLOCK_COUNT) + return -1; + + return result; + } - void push(void* ptr) - { - if (!ptr) - return; + static_assert(math::isPOT(reservationSize), "Reservation size must be a power of 2"); + static constexpr size_t MIN_CELL_SIZE = 16ull; + static constexpr size_t MAX_BLOCK_COUNT = 16ull; + static constexpr size_t MAX_EFFECTIVE_ALLOCATION = getBlockSize(MAX_BLOCK_COUNT); - Node* node = reinterpret_cast(ptr); - node->next = m_freeList; - m_freeList = node; - } - }; + FixedSizeBlock m_blocks[MAX_BLOCK_COUNT]; + + bool m_initialized = false; + void* m_reservedMemory = nullptr; + size_t m_currentOffset = 0; + size_t m_pageSize = 0; struct AllocHeader { + static constexpr uint64_t MAGIC_VALUE = std::bit_cast("POTSLAB"); + uint64_t magicValue; size_t allocationSize; }; - private: - static constexpr size_t MIN_POOL_SIZE = 16ull; - static constexpr int POOL_COUNT = math::log2(reservationSize / MIN_POOL_SIZE); - - static_assert(reservationSize >= MIN_POOL_SIZE, "Reservation size must be greater or equal than 16"); - static_assert(MIN_POOL_SIZE * (1 << POOL_COUNT) >= reservationSize); - - FixedSizePool m_pools[POOL_COUNT]; - - void* m_reservedPtr; - size_t m_reservedSize; - size_t m_commitedSize; - size_t m_pageSize; - public: - POTSlabAllocator() - : m_commitedSize(0) + void init() { - assert(sngl::platform::IsInitialized() && "Allocators can only be used after platform initialization"); + assert(!m_initialized); // You can't initialize an allocator twice m_pageSize = sngl::platform::GetPageSize(); + m_reservedMemory = sngl::platform::memory::Reserve(reservationSize); + if (!m_reservedMemory) + throw std::runtime_error("POTSlabAllocator: Failed to reserve memory for allocations"); - m_reservedSize = (reservationSize + m_pageSize - 1) & ~(m_pageSize - 1); - m_reservedPtr = sngl::platform::memory::Reserve(reservationSize); - - size_t currentPoolSize = MIN_POOL_SIZE; - for (size_t i = 0; i < POOL_COUNT; i++) - { - m_pools[i].init(currentPoolSize); - currentPoolSize *= 2; - } - } + for (size_t i = 0; i < MAX_BLOCK_COUNT; i++) + m_blocks[i].init(getBlockSize(i)); - ~POTSlabAllocator() - { - sngl::platform::memory::Release(m_reservedPtr); + m_initialized = true; } void* allocate(size_t size) - { - const size_t potSize = std::max(MIN_POOL_SIZE, std::bit_ceil(size + sizeof(AllocHeader))); - const int poolIndex = getPoolIndexForSize(potSize); + { + assert(m_initialized); // Using an uninitialized allocator + const size_t potSize = std::max(MIN_CELL_SIZE, std::bit_ceil(size + sizeof(AllocHeader))); + const int blockIx = getBlockIxFromSize(potSize); + void* memPtr = nullptr; - void* rawMemory = nullptr; - if (poolIndex >= 0) + if (blockIx >= 0) { - rawMemory = m_pools[poolIndex].pop(); - if (!rawMemory) + auto& block = m_blocks[blockIx]; + memPtr = block.pop(); + + if (!memPtr) { - m_pools[poolIndex].supplyNewPage(requestPage(), m_pageSize); - rawMemory = m_pools[poolIndex].pop(); + uint8_t* currentMem = static_cast(m_reservedMemory) + m_currentOffset; + + // TODO: handle blocks that are bigger than page size + // some math may be required for optimization + if (block.getSize() > m_pageSize) + throw std::runtime_error("Not Implemented"); + + if (!sngl::platform::memory::Commit(currentMem, m_pageSize)) + throw std::runtime_error("POTSlabAllocator: Failed to commit new memory"); + + block.supplyNewMemory(currentMem, m_pageSize); + memPtr = block.pop(); + m_currentOffset += m_pageSize; } } else { - // fallback - rawMemory = sngl::platform::memory::Reserve(potSize); - if (!rawMemory) - throw std::runtime_error("Failed to allocate memory"); - - sngl::platform::memory::Commit(rawMemory, potSize); + // FALLBACK + // if we can't allocate in the pool + // we just allocate memory almost like malloc does + memPtr = sngl::platform::memory::Reserve(potSize); + if (!memPtr) + throw std::runtime_error("POTSlabAllocator: Failed to reserve fallback memory"); + + if (!sngl::platform::memory::Commit(memPtr, potSize)) + throw std::runtime_error("POTSlabAllocator: Failed to commit fallback memory"); } - assert(rawMemory); - AllocHeader* allocHeader = reinterpret_cast(rawMemory); - allocHeader->allocationSize = potSize; + AllocHeader* header = static_cast(memPtr); + header->magicValue = AllocHeader::MAGIC_VALUE; + header->allocationSize = potSize; - return reinterpret_cast(rawMemory) + sizeof(AllocHeader); + return reinterpret_cast(memPtr) + sizeof(AllocHeader); } void free(void* ptr) { - const AllocHeader* header = reinterpret_cast(ptr); - const int poolIndex = getPoolIndexForSize(header->allocationSize); - - if (poolIndex >= 0) - m_pools[poolIndex].push(ptr); + AllocHeader* allocHeader = reinterpret_cast( + static_cast(ptr) - sizeof(AllocHeader) + ); + + assert(allocHeader->magicValue == AllocHeader::MAGIC_VALUE); // Wrong memory pointer used + const int blockIx = getBlockIxFromSize(allocHeader->allocationSize); + if (blockIx < 0) + sngl::platform::memory::Release(allocHeader); else - sngl::platform::memory::Release(ptr); - } - - private: - void* requestPage() - { - uint8_t* currentPtr = static_cast(m_reservedPtr) + m_commitedSize; - uint8_t* newPtr = currentPtr + m_pageSize; - if (!sngl::platform::memory::Commit(newPtr, m_pageSize)) - throw std::runtime_error("POTSlabAllocator: Failed to commit memory."); - - m_commitedSize += m_pageSize; - return newPtr; - } - - int getPoolIndexForSize(size_t potSize) const - { - // log2(x) - 4 is array index - // POT gives us O(1) complexity making this allocator super fast - // since std::bit_width gives us log2(x) + 1, we need to subtract additional 1 - // https://en.cppreference.com/w/cpp/numeric/bit_width.html - const int poolIndex = std::bit_width(potSize) - 5; - if (poolIndex > POOL_COUNT - 1) - return -1; - - return poolIndex; + m_blocks->push(allocHeader); } }; } From 67df3c5e7bd22c015c4d16e7fa57c4434c9729a0 Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Tue, 17 Mar 2026 22:10:11 +0100 Subject: [PATCH 8/8] implement missing things and fix memory leak --- Engine/src/Core/POTSlabAllocator.h | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Engine/src/Core/POTSlabAllocator.h b/Engine/src/Core/POTSlabAllocator.h index b0a5683..a25e778 100644 --- a/Engine/src/Core/POTSlabAllocator.h +++ b/Engine/src/Core/POTSlabAllocator.h @@ -56,7 +56,7 @@ namespace sngl::core static constexpr int getBlockIxFromSize(size_t blockSize) { - int result = math::ceil_log2(blockSize) - 4; + int result = math::ceil_log2(blockSize) - 5; if (result > MAX_BLOCK_COUNT) return -1; @@ -66,7 +66,7 @@ namespace sngl::core static_assert(math::isPOT(reservationSize), "Reservation size must be a power of 2"); static constexpr size_t MIN_CELL_SIZE = 16ull; static constexpr size_t MAX_BLOCK_COUNT = 16ull; - static constexpr size_t MAX_EFFECTIVE_ALLOCATION = getBlockSize(MAX_BLOCK_COUNT); + static constexpr size_t MAX_EFFECTIVE_ALLOCATION = getBlockSize(MAX_BLOCK_COUNT - 1); FixedSizeBlock m_blocks[MAX_BLOCK_COUNT]; @@ -83,6 +83,11 @@ namespace sngl::core }; public: + ~POTSlabAllocator() + { + sngl::platform::memory::Release(m_reservedMemory); + } + void init() { assert(!m_initialized); // You can't initialize an allocator twice @@ -113,17 +118,17 @@ namespace sngl::core { uint8_t* currentMem = static_cast(m_reservedMemory) + m_currentOffset; - // TODO: handle blocks that are bigger than page size - // some math may be required for optimization - if (block.getSize() > m_pageSize) - throw std::runtime_error("Not Implemented"); + const size_t blockSize = block.getSize(); + size_t commitSize = m_pageSize; + if (blockSize > m_pageSize) + commitSize = 4 * blockSize; - if (!sngl::platform::memory::Commit(currentMem, m_pageSize)) + if (!sngl::platform::memory::Commit(currentMem, commitSize)) throw std::runtime_error("POTSlabAllocator: Failed to commit new memory"); - block.supplyNewMemory(currentMem, m_pageSize); + block.supplyNewMemory(currentMem, commitSize); memPtr = block.pop(); - m_currentOffset += m_pageSize; + m_currentOffset += commitSize; } } else @@ -159,6 +164,8 @@ namespace sngl::core else m_blocks->push(allocHeader); } + + static constexpr size_t getMaxEffectiveAllocSize() { return MAX_EFFECTIVE_ALLOCATION; } }; }