#---------------------------------------------------------------------------------
TARGET := ctru
BUILD := build
-SOURCES := source source/services source/gpu
+SOURCES := source source/services source/gpu source/allocator
DATA := data
INCLUDES := include
--- /dev/null
+#include <3ds.h>
+#include "mem_pool.h"
+
+extern u32 __linear_heap, __linear_heap_size;
+
+static MemPool sLinearPool;
+
+static bool linearInit()
+{
+ auto blk = MemBlock::Create((u8*)__linear_heap, __linear_heap_size);
+ if (blk)
+ {
+ sLinearPool.AddBlock(blk);
+ return true;
+ }
+ return false;
+}
+
+void* linearAlloc(size_t size)
+{
+ // Initialize the pool if it is not ready
+ if (!sLinearPool.Ready() && !linearInit())
+ return nullptr;
+
+ // Reserve memory for MemChunk structure
+ size += 16;
+
+ // Allocate the chunk
+ MemChunk chunk;
+ if (!sLinearPool.Allocate(chunk, size, 4)) // 16-byte alignment
+ return nullptr;
+
+ // Copy the MemChunk structure and return memory
+ auto addr = chunk.addr;
+ *(MemChunk*)addr = chunk;
+ return addr + 16;
+}
+
+void* linearRealloc(void* mem, size_t size)
+{
+ // TODO
+ return NULL;
+}
+
+void linearFree(void* mem)
+{
+ // Find MemChunk structure and free the chunk
+ auto pChunk = (MemChunk*)((u8*)mem - 16);
+ sLinearPool.Deallocate(*pChunk);
+}
--- /dev/null
+#include "mem_pool.h"
+
+/*
+// This method is currently unused
+void MemPool::CoalesceLeft(MemBlock* b)
+{
+ auto curPtr = b->base;
+ for (auto p = b->prev; p; p = p->prev)
+ {
+ if ((p->base + p->size) != curPtr) break;
+ curPtr = p->base;
+ p->size += b->size;
+ DelBlock(b);
+ b = p;
+ }
+}
+*/
+
+void MemPool::CoalesceRight(MemBlock* b)
+{
+ auto curPtr = b->base + b->size;
+ auto next = b->next;
+ for (auto n = next; n; n = next)
+ {
+ next = n->next;
+ if (n->base != curPtr) break;
+ b->size += n->size;
+ curPtr += n->size;
+ DelBlock(n);
+ }
+}
+
+bool MemPool::Allocate(MemChunk& chunk, u32 size, int align)
+{
+ int alignM = (1 << align) - 1;
+ size = (size + alignM) &~ alignM; // Round the size
+ // Find the first suitable block
+ for (auto b = first; b; b = b->next)
+ {
+ auto addr = b->base;
+ u32 begWaste = (u32)addr & alignM;
+ addr += begWaste;
+ u32 bSize = b->size - begWaste;
+ if (bSize < size) continue;
+
+ // Found space!
+ chunk.addr = addr;
+ chunk.size = size;
+
+ // Resize the block
+ if (!begWaste)
+ {
+ b->base += size;
+ b->size -= size;
+ if (!b->size)
+ DelBlock(b);
+ } else
+ {
+ auto nAddr = addr + size;
+ auto nSize = bSize - size;
+ b->size = begWaste;
+ if (nSize)
+ {
+ // We need to add the tail chunk that wasn't used to the list
+ auto n = MemBlock::Create(nAddr, nSize);
+ if (n) InsertAfter(b, n);
+ else chunk.size += nSize; // we have no choice but to waste the space.
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void MemPool::Deallocate(const MemChunk& chunk)
+{
+ u8* cAddr = chunk.addr;
+ auto cSize = chunk.size;
+ bool done = false;
+
+ // Try to merge the chunk somewhere into the list
+ for (auto b = first; !done && b; b = b->next)
+ {
+ auto addr = b->base;
+ if (addr > cAddr)
+ {
+ if ((cAddr + cSize) == addr)
+ {
+ // Merge the chunk to the left of the block
+ b->base = cAddr;
+ b->size += cSize;
+ } else
+ {
+ // We need to insert a new block
+ auto c = MemBlock::Create(cAddr, cSize);
+ if (c) InsertBefore(b, c);
+ }
+ done = true;
+ } else if ((b->base + b->size) == cAddr)
+ {
+ // Coalesce to the right
+ b->size += cSize;
+ CoalesceRight(b);
+ done = true;
+ }
+ }
+
+ if (!done)
+ {
+ // Either the list is empty or the chunk address is past the end
+ // address of the last block -- let's add a new block at the end
+ auto b = MemBlock::Create(cAddr, cSize);
+ if (b) AddBlock(b);
+ }
+}
+
+/*
+void MemPool::Dump(const char* title)
+{
+ printf("<%s> VRAM Pool Dump\n", title);
+ for (auto b = first; b; b = b->next)
+ printf(" - %p (%u bytes)\n", b->base, b->size);
+}
+*/
--- /dev/null
+#pragma once
+#include <3ds.h>
+
+struct MemChunk
+{
+ u8* addr;
+ u32 size;
+};
+
+struct MemBlock
+{
+ MemBlock *prev, *next;
+ u8* base;
+ u32 size;
+
+ static MemBlock* Create(u8* base, u32 size)
+ {
+ auto b = (MemBlock*)malloc(sizeof(MemBlock));
+ if (!b) return nullptr;
+ b->prev = nullptr;
+ b->next = nullptr;
+ b->base = base;
+ b->size = size;
+ return b;
+ }
+};
+
+struct MemPool
+{
+ MemBlock *first, *last;
+
+ bool Ready() { return first != nullptr; }
+
+ void AddBlock(MemBlock* blk)
+ {
+ blk->prev = last;
+ if (last) last->next = blk;
+ if (!first) first = blk;
+ last = blk;
+ }
+
+ void DelBlock(MemBlock* b)
+ {
+ auto prev = b->prev, &pNext = prev ? prev->next : first;
+ auto next = b->next, &nPrev = next ? next->prev : last;
+ pNext = next;
+ nPrev = prev;
+ free(b);
+ }
+
+ void InsertBefore(MemBlock* b, MemBlock* p)
+ {
+ auto prev = b->prev, &pNext = prev ? prev->next : first;
+ b->prev = p;
+ p->next = b;
+ p->prev = prev;
+ pNext = p;
+ }
+
+ void InsertAfter(MemBlock* b, MemBlock* n)
+ {
+ auto next = b->next, &nPrev = next ? next->prev : last;
+ b->next = n;
+ n->prev = b;
+ n->next = next;
+ nPrev = n;
+ }
+
+ //void CoalesceLeft(MemBlock* b);
+ void CoalesceRight(MemBlock* b);
+
+ bool Allocate(MemChunk& chunk, u32 size, int align);
+ void Deallocate(const MemChunk& chunk);
+
+ void Destroy()
+ {
+ MemBlock* next = nullptr;
+ for (auto b = first; b; b = next)
+ {
+ next = b->next;
+ free(b);
+ }
+ first = nullptr;
+ last = nullptr;
+ }
+
+ //void Dump(const char* title);
+};
+++ /dev/null
-#include <3ds.h>
-
-extern u32 __linear_heap, __linear_heap_size;
-
-// TODO: this allocator sucks! It is not thread-safe and you cannot 'free' this memory.
-void* linearAlloc(size_t size)
-{
- static size_t currentOffset = 0;
- size_t free = __linear_heap_size - currentOffset;
-
- // Enforce 16-byte alignment
- size = (size + 15) &~ 15;
-
- void* mem = NULL;
- if (free >= size)
- {
- mem = (void*)(__linear_heap + currentOffset);
- currentOffset += size;
- }
-
- return mem;
-}
-
-void* linearRealloc(void* mem, size_t size)
-{
- return NULL; // TODO
-}
-
-void linearFree(void* mem)
-{
- // TODO
-}