From 2c589cd2ef9a2b663e7c792e0451af1f1d544223 Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 16 Sep 2014 22:24:26 +0200 Subject: [PATCH 1/2] linearAlloc: use 16-byte alignment --- libctru/source/linear.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libctru/source/linear.c b/libctru/source/linear.c index ba78f40..66eb43c 100644 --- a/libctru/source/linear.c +++ b/libctru/source/linear.c @@ -5,20 +5,20 @@ 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; + static size_t currentOffset = 0; + size_t free = __linear_heap_size - currentOffset; - // Enforce 8-byte alignment - size = (size + 7) &~ 7; + // Enforce 16-byte alignment + size = (size + 15) &~ 15; void* mem = NULL; - if (free >= size) - { + if (free >= size) + { mem = (void*)(__linear_heap + currentOffset); - currentOffset += size; - } + currentOffset += size; + } - return mem; + return mem; } void* linearRealloc(void* mem, size_t size) From 27be99a79306526f9da16ebe6bf31fd7f3c5e1ec Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 17 Sep 2014 00:22:30 +0200 Subject: [PATCH 2/2] Add proper linear heap allocator --- libctru/Makefile | 2 +- libctru/source/allocator/linear.cpp | 50 +++++++++++ libctru/source/allocator/mem_pool.cpp | 125 ++++++++++++++++++++++++++ libctru/source/allocator/mem_pool.h | 88 ++++++++++++++++++ libctru/source/linear.c | 32 ------- 5 files changed, 264 insertions(+), 33 deletions(-) create mode 100644 libctru/source/allocator/linear.cpp create mode 100644 libctru/source/allocator/mem_pool.cpp create mode 100644 libctru/source/allocator/mem_pool.h delete mode 100644 libctru/source/linear.c diff --git a/libctru/Makefile b/libctru/Makefile index 83aa6d0..c42536b 100644 --- a/libctru/Makefile +++ b/libctru/Makefile @@ -17,7 +17,7 @@ include $(DEVKITARM)/base_rules #--------------------------------------------------------------------------------- TARGET := ctru BUILD := build -SOURCES := source source/services source/gpu +SOURCES := source source/services source/gpu source/allocator DATA := data INCLUDES := include diff --git a/libctru/source/allocator/linear.cpp b/libctru/source/allocator/linear.cpp new file mode 100644 index 0000000..a7581e8 --- /dev/null +++ b/libctru/source/allocator/linear.cpp @@ -0,0 +1,50 @@ +#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); +} diff --git a/libctru/source/allocator/mem_pool.cpp b/libctru/source/allocator/mem_pool.cpp new file mode 100644 index 0000000..93eeb4f --- /dev/null +++ b/libctru/source/allocator/mem_pool.cpp @@ -0,0 +1,125 @@ +#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); +} +*/ diff --git a/libctru/source/allocator/mem_pool.h b/libctru/source/allocator/mem_pool.h new file mode 100644 index 0000000..7237f49 --- /dev/null +++ b/libctru/source/allocator/mem_pool.h @@ -0,0 +1,88 @@ +#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); +}; diff --git a/libctru/source/linear.c b/libctru/source/linear.c deleted file mode 100644 index 66eb43c..0000000 --- a/libctru/source/linear.c +++ /dev/null @@ -1,32 +0,0 @@ -#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 -}