commit
6431810185
@ -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
|
||||
|
||||
|
50
libctru/source/allocator/linear.cpp
Normal file
50
libctru/source/allocator/linear.cpp
Normal file
@ -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);
|
||||
}
|
125
libctru/source/allocator/mem_pool.cpp
Normal file
125
libctru/source/allocator/mem_pool.cpp
Normal file
@ -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);
|
||||
}
|
||||
*/
|
88
libctru/source/allocator/mem_pool.h
Normal file
88
libctru/source/allocator/mem_pool.h
Normal file
@ -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);
|
||||
};
|
@ -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 8-byte alignment
|
||||
size = (size + 7) &~ 7;
|
||||
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue
Block a user