Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
3 changes: 2 additions & 1 deletion Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -22,6 +22,7 @@ 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"
Expand Down
2 changes: 1 addition & 1 deletion Engine/include/sngl/Core/Engine.h
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Engine/include/sngl/Graphics/Device.h
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Engine/include/sngl/Graphics/Instance.h
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Engine/include/sngl/Graphics/Types.h
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Engine/src/Core/Engine.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
14 changes: 14 additions & 0 deletions Engine/src/Core/LinearArenaAllocator.cpp
Original file line number Diff line number Diff line change
@@ -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 <Platform/OS.h>

Expand Down
14 changes: 14 additions & 0 deletions Engine/src/Core/LinearArenaAllocator.h
Original file line number Diff line number Diff line change
@@ -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__

Expand Down
51 changes: 51 additions & 0 deletions Engine/src/Core/Math.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#ifndef __SNGL_CORE_MATH_H_INCLUDED__
#define __SNGL_CORE_MATH_H_INCLUDED__

#include <bit>
#include <limits>

namespace sngl::core::math
{
template <typename T>
requires std::is_integral_v<T>
constexpr bool isPOT(T x)
{
return (x > 0) && ((x & (x - 1)) == 0);
}

template <std::unsigned_integral T>
constexpr size_t floor_log2(T x)
{
if (x == 0) return 0;
return static_cast<size_t>((std::numeric_limits<T>::digits - 1) - std::countl_zero(x));
}

template <std::unsigned_integral T>
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__
141 changes: 27 additions & 114 deletions Engine/src/Core/POTSlabAllocator.cpp
Original file line number Diff line number Diff line change
@@ -1,138 +1,51 @@
#include "POTSlabAllocator.h"

#include <cstdint>
#include <bit>
#include <algorithm>
#include <stdexcept>

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;
}
FixedSizeBlock::FixedSizeBlock()
: m_freeList(nullptr),
m_blockSize(0),
m_pageSize(0)
{ }

void POTSlabAllocator::FixedSizePool::supplyNewPage(void* rawMemory, size_t pageSize)
void FixedSizeBlock::init(size_t blockSize)
{
size_t blocksInPage = pageSize / m_blockSize;
uint8_t* pageStart = static_cast<uint8_t*>(rawMemory);

for (size_t i = 0; i < blocksInPage; i++)
{
uint8_t* currentBlockAddress = pageStart + (m_blockSize * i);
Node* node = reinterpret_cast<Node*>(currentBlockAddress);
node->next = m_freeList;
m_freeList = node;
}
m_pageSize = sngl::platform::GetPageSize();
assert(math::isPOT(blockSize)); // blockSize should be a power of 2
m_blockSize = blockSize;
}

void* POTSlabAllocator::FixedSizePool::pop()
void* FixedSizeBlock::pop()
{
if (!m_freeList)
return nullptr;
if (m_freeList == nullptr)
return nullptr; // supply a page to partition

Node* freeBlock = m_freeList;
m_freeList = freeBlock->next;
Node* currentNode = m_freeList;
m_freeList = m_freeList->next;

return freeBlock;
return m_freeList;
}

void POTSlabAllocator::FixedSizePool::push(void* ptr)
void FixedSizeBlock::push(void* ptr)
{
if (!ptr)
return;

Node* node = reinterpret_cast<Node*>(ptr);
node->next = m_freeList;
m_freeList = node;
Node* newNode = reinterpret_cast<Node*>(ptr);
newNode->next = m_freeList;
m_freeList = newNode;
}

POTSlabAllocator::POTSlabAllocator(size_t reservationSize)
: m_commitedSize(0)
void FixedSizeBlock::supplyNewMemory(void* ptr, size_t memSize)
{
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);
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<uint8_t* const>(ptr);

size_t currentPoolSize = MIN_POOL_SIZE;
for (size_t i = 0; i < POOL_COUNT; i++)
for (size_t i = 0; i < nodeCount; 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_t>(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);
Node* node = reinterpret_cast<Node*>(startPtr + (i * m_blockSize));
node->next = m_freeList;
m_freeList = node;
}

assert(rawMemory);
AllocHeader* allocHeader = reinterpret_cast<AllocHeader*>(rawMemory);
allocHeader->allocationSize = potSize;

return reinterpret_cast<uint8_t*>(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<size_t>(potSize) - 5;
if (poolIndex > POOL_COUNT - 1)
return -1;

return poolIndex;
}

void POTSlabAllocator::free(void* ptr)
{
const AllocHeader* header = reinterpret_cast<const AllocHeader*>(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<uint8_t*>(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;
}
Loading