/*---------------------------------------------------------------------------* Project: Horizon File: MemoryManager.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 47228 $ *---------------------------------------------------------------------------*/ #include "MemoryManager.h" #define DEBUG_PRINT 0 namespace demo { namespace memory_manager { namespace { const int ALIGNMENT_SYSTEM_BUFFER = 4; // Specify the maximum among the rules const int ALIGNMENT_VERTEX = 4; // Take into account 4-byte alignment rules for GLfloat type const int ALIGNMENT_TEXTURE = 128; const int ALIGNMENT_RENDER_BUFFER = 64; // 24-bit format depth buffer (D24) must be aligned to 96 bytes const int ALIGNMENT_DISPLAY_BUFFER = 16; const int ALIGNMENT_3D_COMMAND_BUFFER = 16; inline uptr MathRoundup(uptr x, int base) { return ((x) + ((base)-1)) & ~((base)-1); } } namespace detail { MemoryManager::MemoryManager() : m_Initialized(false), m_pStartAddrFcram(NULL), m_CurrentAddrVramA(NULL), m_CurrentAddrVramB(NULL), m_HeapOnFcram(), m_AllocatedBlockSize(0), m_DebugPrint(false) { } MemoryManager::~MemoryManager() { Finalize(); } void MemoryManager::Initialize(const uptr fcramAddress, const size_t memorySize) { if (m_Initialized) { return; } m_pStartAddrFcram = fcramAddress; NN_TASSERT_(m_pStartAddrFcram != NULL); m_AllocatedBlockSize = memorySize; m_HeapOnFcram.Initialize(m_pStartAddrFcram, memorySize ); m_CurrentAddrVramA = nn::gx::GetVramStartAddr(nn::gx::MEM_VRAMA); m_CurrentAddrVramB = nn::gx::GetVramStartAddr(nn::gx::MEM_VRAMB); #if DEBUG_PRINT NN_TLOG_("[demo::MemoryManager] Start address in Main Memory (FCRAM): 0x%08X\n", m_pStartAddrFcram); NN_TLOG_("[demo::MemoryManager] Start address in VRAM-A: 0x%08X\n", m_CurrentAddrVramA); NN_TLOG_("[demo::MemoryManager] Start address in VRAM-B: 0x%08X\n", m_CurrentAddrVramB); #endif m_Initialized = true; } void MemoryManager::Finalize(void) { m_HeapOnFcram.Finalize(); m_Initialized = false; } void MemoryManager::PrintFreeMemorySize(void) { NN_TLOG_("[demo::MemoryManager] Free memory size in Main Memory (FCRAM) is 0x%06X(max 0x%06X)\n", m_HeapOnFcram.GetTotalFreeSize(), m_AllocatedBlockSize); NN_TLOG_("[demo::MemoryManager] Free memory size in VRAM-A is 0x%06X(max 0x%06X)\n", nn::gx::GetVramEndAddr(nn::gx::MEM_VRAMA) - m_CurrentAddrVramA, nn::gx::GetVramSize(nn::gx::MEM_VRAMA)); NN_TLOG_("[demo::MemoryManager] Free memory size in VRAM-B is 0x%06X(max 0x%06X)\n", nn::gx::GetVramEndAddr(nn::gx::MEM_VRAMB) - m_CurrentAddrVramB, nn::gx::GetVramSize(nn::gx::MEM_VRAMB)); } void* MemoryManager::Allocate(GLenum area, GLenum aim, GLuint id, GLsizei size) { if (!m_Initialized) { NN_TPANIC_("Not initialized.\n"); } if (size == 0) return 0; // When allocating to VRAM, the VRAM can be treated as a stack due to its simplicity // In addition, do not respond to fragmentation // In order to handle fragmentation, information to be stored in allocated memory named area, aim, and id when allocation succeeds must be saved before subsequent deallocation. // int addrAlign = 8; void* resultAddr = NULL; // Take into account the alignment restrictions on each data set's position switch (aim) { case NN_GX_MEM_SYSTEM: addrAlign = ALIGNMENT_SYSTEM_BUFFER; break; case NN_GX_MEM_TEXTURE: addrAlign = ALIGNMENT_TEXTURE; break; case NN_GX_MEM_VERTEXBUFFER: addrAlign = ALIGNMENT_VERTEX; break; case NN_GX_MEM_RENDERBUFFER: addrAlign = ALIGNMENT_RENDER_BUFFER; break; case NN_GX_MEM_DISPLAYBUFFER: addrAlign = ALIGNMENT_DISPLAY_BUFFER; break; case NN_GX_MEM_COMMANDBUFFER: addrAlign = ALIGNMENT_3D_COMMAND_BUFFER; break; default: NN_TPANIC_("Invalid parameter. (0x%X)\n", aim); break; } switch (area) { case NN_GX_MEM_FCRAM: // When using FCRAM, no need to manage this because it is allocated from the expanded heap that is supported by the SDK if ( (resultAddr = m_HeapOnFcram.Allocate(size, addrAlign)) == NULL) { NN_TPANIC_("Lack of resources in Main Memory (FCRAM).\n"); } break; case NN_GX_MEM_VRAMA: { if (MathRoundup(m_CurrentAddrVramA, addrAlign) + size > nn::gx::GetVramEndAddr(nn::gx::MEM_VRAMA)) { NN_TPANIC_("Lack of resources in VRAM-A.\n"); } m_CurrentAddrVramA = MathRoundup(m_CurrentAddrVramA, addrAlign); resultAddr = reinterpret_cast(m_CurrentAddrVramA); m_CurrentAddrVramA += size; } break; case NN_GX_MEM_VRAMB: { if (MathRoundup(m_CurrentAddrVramB, addrAlign) + size > nn::gx::GetVramEndAddr(nn::gx::MEM_VRAMB)) { NN_TPANIC_("Lack of resources in VRAM-B.\n"); } m_CurrentAddrVramB = MathRoundup(m_CurrentAddrVramB, addrAlign); resultAddr = reinterpret_cast(m_CurrentAddrVramB); m_CurrentAddrVramB += size; } break; default: // This is not likely in normal use; PANIC NN_TPANIC_("Invalid parameter. (0x%X)\n", area); break; } #if DEBUG_PRINT if (aim == NN_GX_MEM_SYSTEM) { NN_TLOG_("[demo::MemoryManager] NN_GX_MEM_SYSTEM is allocated at 0x%08X - 0x%08X on %X\n", resultAddr, reinterpret_cast(resultAddr) + size - 1, area); } else if (aim == NN_GX_MEM_TEXTURE) { NN_TLOG_("[demo::MemoryManager] NN_GX_MEM_TEXTURE(id:%d) is allocated at 0x%08X - 0x%08X on %X\n", resultAddr, id reinterpret_cast(resultAddr) + size - 1, area); } else if (aim == NN_GX_MEM_VERTEXBUFFER) { NN_TLOG_("[demo::MemoryManager] NN_GX_MEM_VERTEXBUFFER(id:%d) is allocated at 0x%08X - 0x%08X on %X\n", resultAddr, id reinterpret_cast(resultAddr) + size - 1, area); } else if (aim == NN_GX_MEM_DISPLAYBUFFER) { NN_TLOG_("[demo::MemoryManager] NN_GX_MEM_DISPLAYBUFFER(id:%d) is allocated at 0x%08X - 0x%08X on %X\n", resultAddr, id reinterpret_cast(resultAddr) + size - 1, area); } else if (aim == NN_GX_MEM_RENDERBUFFER) { NN_TLOG_("[demo::MemoryManager] NN_GX_MEM_RENDERBUFFER(id:%d) is allocated at 0x%08X - 0x%08X on %X\n", resultAddr, id reinterpret_cast(resultAddr) + size - 1, area); } else if (aim == NN_GX_MEM_COMMANDBUFFER) { NN_TLOG_("[demo::MemoryManager] NN_GX_MEM_COMMANDBUFFER(id:%d) is allocated at 0x%08X - 0x%08X on %X\n", resultAddr, id reinterpret_cast(resultAddr) + size - 1, area); } #else NN_UNUSED_VAR(id); #endif // #if DEBUG_PRINT if ( m_DebugPrint ) { NN_LOG(" Allocate 0x%x (Physical) (size %d) ", resultAddr, size); if (aim == NN_GX_MEM_SYSTEM) { NN_LOG("NN_GX_MEM_SYSTEM"); } else if (aim == NN_GX_MEM_TEXTURE) { NN_LOG("NN_GX_MEM_TEXTURE"); } else if (aim == NN_GX_MEM_VERTEXBUFFER) { NN_LOG("NN_GX_MEM_VERTEXBUFFER"); } else if (aim == NN_GX_MEM_DISPLAYBUFFER) { NN_LOG("NN_GX_MEM_DISPLAYBUFFER"); } else if (aim == NN_GX_MEM_RENDERBUFFER) { NN_LOG("NN_GX_MEM_RENDERBUFFER"); } else if (aim == NN_GX_MEM_COMMANDBUFFER) { NN_LOG("NN_GX_MEM_COMMANDBUFFER"); } if ( area == NN_GX_MEM_FCRAM ) { NN_LOG(" FCRAM"); } else if ( area == NN_GX_MEM_VRAMA ) { NN_LOG(" VRAM-A"); } else if ( area == NN_GX_MEM_VRAMB ) { NN_LOG(" VRAM-B"); } NN_LOG("\n"); } return resultAddr; } /* Memory deallocator for DMPGL */ void MemoryManager::Deallocate(GLenum area, GLenum aim, GLuint id, void* addr) { NN_UNUSED_VAR(aim); NN_UNUSED_VAR(id); if (! m_Initialized) { NN_TPANIC_("Not initialized.\n"); } #if DEBUG_PRINT NN_TLOG_("[demo::MemoryManager] (id %d) was deallocated at 0x%08X on %X\n", id, addr, area); #endif switch (area) { case NN_GX_MEM_FCRAM: m_HeapOnFcram.Free(addr); break; case NN_GX_MEM_VRAMA: case NN_GX_MEM_VRAMB: // Because the buffer in VRAM is simple, do not deallocate break; default: NN_TPANIC_("Invalid parameter.\n"); break; } } } namespace { demo::memory_manager::detail::MemoryManager s_MemoryManager; } void InitializeMemoryManager(const uptr fcramAddress, const size_t memorySize) { s_MemoryManager.Initialize(fcramAddress, memorySize); } void FinalizeMemoryManager(void) { s_MemoryManager.Finalize(); } void PrintMemoryManagerInfo(void) { s_MemoryManager.PrintFreeMemorySize(); } void* GetAllocator(GLenum area, GLenum aim, GLuint id, GLsizei size) { return s_MemoryManager.Allocate(area, aim, id, size); } void GetDeallocator(GLenum area, GLenum aim, GLuint id, void* addr) { s_MemoryManager.Deallocate(area, aim, id, addr); } void* Alloc(size_t size) { return s_MemoryManager.Allocate(NN_GX_MEM_FCRAM, NN_GX_MEM_SYSTEM, 0, size); } void* Alloc(GLenum aim, const size_t size) { return s_MemoryManager.Allocate(NN_GX_MEM_FCRAM, aim, 0, size); } void* Alloc(GLenum area, GLenum aim, const size_t size) { return s_MemoryManager.Allocate(area, aim, 0, size); } void Free(void* ptr) { s_MemoryManager.Deallocate(NN_GX_MEM_FCRAM, NN_GX_MEM_SYSTEM, 0, ptr); } } }