VRAM allocator enhancements, see details:
- Added proper handling for VRAM banks (A and B, 3 MiB each) - Allocations no longer cross VRAM bank boundaries - Added vramAllocAt, vramMemAlignAt
This commit is contained in:
parent
48967dc417
commit
6360f4bdb1
@ -4,6 +4,13 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
typedef enum vramAllocPos
|
||||
{
|
||||
VRAM_ALLOC_A = BIT(0),
|
||||
VRAM_ALLOC_B = BIT(1),
|
||||
VRAM_ALLOC_ANY = VRAM_ALLOC_A | VRAM_ALLOC_B,
|
||||
} vramAllocPos;
|
||||
|
||||
/**
|
||||
* @brief Allocates a 0x80-byte aligned buffer.
|
||||
* @param size Size of the buffer to allocate.
|
||||
@ -11,6 +18,14 @@
|
||||
*/
|
||||
void* vramAlloc(size_t size);
|
||||
|
||||
/**
|
||||
* @brief Allocates a 0x80-byte aligned buffer in the given VRAM bank.
|
||||
* @param size Size of the buffer to allocate.
|
||||
* @param pos VRAM bank to use (see \ref vramAllocPos).
|
||||
* @return The allocated buffer.
|
||||
*/
|
||||
void* vramAllocAt(size_t size, vramAllocPos pos);
|
||||
|
||||
/**
|
||||
* @brief Allocates a buffer aligned to the given size.
|
||||
* @param size Size of the buffer to allocate.
|
||||
@ -19,6 +34,15 @@ void* vramAlloc(size_t size);
|
||||
*/
|
||||
void* vramMemAlign(size_t size, size_t alignment);
|
||||
|
||||
/**
|
||||
* @brief Allocates a buffer aligned to the given size in the given VRAM bank.
|
||||
* @param size Size of the buffer to allocate.
|
||||
* @param alignment Alignment to use.
|
||||
* @param pos VRAM bank to use (see \ref vramAllocPos).
|
||||
* @return The allocated buffer.
|
||||
*/
|
||||
void* vramMemAlignAt(size_t size, size_t alignment, vramAllocPos pos);
|
||||
|
||||
/**
|
||||
* @brief Reallocates a buffer.
|
||||
* Note: Not implemented yet.
|
||||
|
@ -27,18 +27,9 @@ static bool linearInit()
|
||||
|
||||
void* linearMemAlign(size_t size, size_t alignment)
|
||||
{
|
||||
// Enforce minimum alignment
|
||||
if (alignment < 16)
|
||||
alignment = 16;
|
||||
|
||||
// Convert alignment to shift amount
|
||||
int shift;
|
||||
for (shift = 4; shift < 32; shift ++)
|
||||
{
|
||||
if ((1U<<shift) == alignment)
|
||||
break;
|
||||
}
|
||||
if (shift == 32) // Invalid alignment
|
||||
// Convert alignment to shift
|
||||
int shift = alignmentToShift(alignment);
|
||||
if (shift < 0)
|
||||
return nullptr;
|
||||
|
||||
// Initialize the pool if it is not ready
|
||||
|
@ -2,6 +2,15 @@
|
||||
#include <3ds/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline int alignmentToShift(size_t alignment)
|
||||
{
|
||||
if (alignment < 16)
|
||||
alignment = 16;
|
||||
else if (alignment & (alignment - 1))
|
||||
return -1; // Not a power of two
|
||||
return __builtin_ffs(alignment)-1;
|
||||
}
|
||||
|
||||
struct MemChunk
|
||||
{
|
||||
u8* addr;
|
||||
|
@ -9,60 +9,107 @@ extern "C"
|
||||
#include "mem_pool.h"
|
||||
#include "addrmap.h"
|
||||
|
||||
static MemPool sVramPool;
|
||||
static MemPool sVramPoolA, sVramPoolB;
|
||||
|
||||
static bool vramInit()
|
||||
{
|
||||
auto blk = MemBlock::Create((u8*)OS_VRAM_VADDR, OS_VRAM_SIZE);
|
||||
if (blk)
|
||||
{
|
||||
sVramPool.AddBlock(blk);
|
||||
rbtree_init(&sAddrMap, addrMapNodeComparator);
|
||||
if (sVramPoolA.Ready() || sVramPoolB.Ready())
|
||||
return true;
|
||||
|
||||
auto blkA = MemBlock::Create((u8*)OS_VRAM_VADDR, OS_VRAM_SIZE/2);
|
||||
if (!blkA)
|
||||
return false;
|
||||
|
||||
auto blkB = MemBlock::Create((u8*)OS_VRAM_VADDR + OS_VRAM_SIZE/2, OS_VRAM_SIZE/2);
|
||||
if (!blkB)
|
||||
{
|
||||
free(blkA);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
sVramPoolA.AddBlock(blkA);
|
||||
sVramPoolB.AddBlock(blkB);
|
||||
rbtree_init(&sAddrMap, addrMapNodeComparator);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MemPool* vramPoolForAddr(void* addr)
|
||||
{
|
||||
uintptr_t addr_ = (uintptr_t)addr;
|
||||
if (addr_ < OS_VRAM_VADDR)
|
||||
return nullptr;
|
||||
if (addr_ < OS_VRAM_VADDR + OS_VRAM_SIZE/2)
|
||||
return &sVramPoolA;
|
||||
if (addr_ < OS_VRAM_VADDR + OS_VRAM_SIZE)
|
||||
return &sVramPoolB;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* vramAlloc(size_t size)
|
||||
{
|
||||
return vramMemAlignAt(size, 0x80, VRAM_ALLOC_ANY);
|
||||
}
|
||||
|
||||
void* vramAllocAt(size_t size, vramAllocPos pos)
|
||||
{
|
||||
return vramMemAlignAt(size, 0x80, pos);
|
||||
}
|
||||
|
||||
void* vramMemAlign(size_t size, size_t alignment)
|
||||
{
|
||||
// Enforce minimum alignment
|
||||
if (alignment < 16)
|
||||
alignment = 16;
|
||||
return vramMemAlignAt(size, alignment, VRAM_ALLOC_ANY);
|
||||
}
|
||||
|
||||
// Convert alignment to shift amount
|
||||
int shift;
|
||||
for (shift = 4; shift < 32; shift ++)
|
||||
{
|
||||
if ((1U<<shift) == alignment)
|
||||
break;
|
||||
}
|
||||
if (shift == 32) // Invalid alignment
|
||||
void* vramMemAlignAt(size_t size, size_t alignment, vramAllocPos pos)
|
||||
{
|
||||
// Convert alignment to shift
|
||||
int shift = alignmentToShift(alignment);
|
||||
if (shift < 0)
|
||||
return nullptr;
|
||||
|
||||
// Initialize the pool if it is not ready
|
||||
if (!sVramPool.Ready() && !vramInit())
|
||||
// Initialize the allocator if it is not ready
|
||||
if (!vramInit())
|
||||
return nullptr;
|
||||
|
||||
// Allocate the chunk
|
||||
MemChunk chunk;
|
||||
if (!sVramPool.Allocate(chunk, size, shift))
|
||||
bool didAlloc = false;
|
||||
switch (pos & VRAM_ALLOC_ANY)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case VRAM_ALLOC_A:
|
||||
didAlloc = sVramPoolA.Allocate(chunk, size, shift);
|
||||
break;
|
||||
case VRAM_ALLOC_B:
|
||||
didAlloc = sVramPoolB.Allocate(chunk, size, shift);
|
||||
break;
|
||||
case VRAM_ALLOC_ANY:
|
||||
{
|
||||
// Crude attempt at "load balancing" VRAM A and B
|
||||
bool prefer_a = sVramPoolA.GetFreeSpace() >= sVramPoolB.GetFreeSpace();
|
||||
MemPool& firstPool = prefer_a ? sVramPoolA : sVramPoolB;
|
||||
MemPool& secondPool = prefer_a ? sVramPoolB : sVramPoolA;
|
||||
|
||||
didAlloc = firstPool.Allocate(chunk, size, shift);
|
||||
if (!didAlloc) didAlloc = secondPool.Allocate(chunk, size, shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didAlloc)
|
||||
return nullptr;
|
||||
|
||||
auto node = newNode(chunk);
|
||||
if (!node)
|
||||
{
|
||||
sVramPool.Deallocate(chunk);
|
||||
vramPoolForAddr(chunk.addr)->Deallocate(chunk);
|
||||
return nullptr;
|
||||
}
|
||||
if (rbtree_insert(&sAddrMap, &node->node));
|
||||
return chunk.addr;
|
||||
}
|
||||
|
||||
void* vramAlloc(size_t size)
|
||||
{
|
||||
return vramMemAlign(size, 0x80);
|
||||
}
|
||||
|
||||
void* vramRealloc(void* mem, size_t size)
|
||||
{
|
||||
// TODO
|
||||
@ -81,7 +128,7 @@ void vramFree(void* mem)
|
||||
if (!node) return;
|
||||
|
||||
// Free the chunk
|
||||
sVramPool.Deallocate(node->chunk);
|
||||
vramPoolForAddr(mem)->Deallocate(node->chunk);
|
||||
|
||||
// Free the node
|
||||
delNode(node);
|
||||
@ -89,5 +136,5 @@ void vramFree(void* mem)
|
||||
|
||||
u32 vramSpaceFree()
|
||||
{
|
||||
return sVramPool.GetFreeSpace();
|
||||
return sVramPoolA.GetFreeSpace() + sVramPoolB.GetFreeSpace();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user