diff --git a/libctru/include/3ds/allocator/vram.h b/libctru/include/3ds/allocator/vram.h index 3658699..a1e593a 100644 --- a/libctru/include/3ds/allocator/vram.h +++ b/libctru/include/3ds/allocator/vram.h @@ -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. diff --git a/libctru/source/allocator/linear.cpp b/libctru/source/allocator/linear.cpp index 8d8182a..74dd88d 100644 --- a/libctru/source/allocator/linear.cpp +++ b/libctru/source/allocator/linear.cpp @@ -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< #include +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; diff --git a/libctru/source/allocator/vram.cpp b/libctru/source/allocator/vram.cpp index 14838fa..79b2554 100644 --- a/libctru/source/allocator/vram.cpp +++ b/libctru/source/allocator/vram.cpp @@ -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<= 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(); }