nlib
heap/gameheap/gameheap_appcode.cpp

The heap systems that generally tend to be used in game programs are instantiated using nn::nlib::heap::CentralHeap.

Memory can be allocated and freed from different address spaces for each HeapHandle. It is also possible to create each heap from memory regions that have been allocated in advance using structures like arrays.

It allocates and frees memory by instantiating a HeapHandle as follows.

  1. Use HeapHandle::createHeap(mem, size) to create a heap in memory and return an object handle of type HeapHandle.
  2. Memory can be allocated from the heap using handle.alloc. This is thread-safe.
  3. Memory can be returned to the heap using handle.free. This is thread-safe.
  4. The entire heap can be deleted using HeapHandle::destroyHeap(handle).
  5. Use GetThreadDefaultHeapHandle to get the per-thread heap handle. You can set this using SetThreadDefaultHeapHandle.

Separating memory and using multiple heaps has the following advantages.

Classes that allocate memory from different heaps for each object can also be created by instantiating objects from the Factory method pattern (derived from IDisposer).

Also refer to the heap/gameheap/gameheap.h and heap/gameheap/gameheap.cpp samples.

#include "gameheap.h"
using nlib_ns::heap::CentralHeap;
// heap_handle[kGraphcisObjectHeap] for the graphics objects.
// heap_handle[kSoundObjectHeap] for the sound objects.
const int kGraphcisObjectHeap = 1;
const int kSoundObjectHeap = 2;
// This sample has three 2M Bytes heaps.
// One for the graphics objects, another for the sound objects, and the rest for the rest objects.
// You also can nest heaps.
static char mymem[3][1024 * 1024 * 2];
static HeapHandle heap_handle[3];
void init_my_heap() {
nlib_printf("We split memory into 3 parts.\n");
nlib_printf("Graphics objects and Sound objects are allocated from different regions\n");
nlib_printf("\n");
for (int i = 0; i < 3; ++i) {
heap_handle[i] = HeapHandle::createHeap(&mymem[i][0], 1024 * 1024 * 2);
NLIB_ASSERT(heap_handle[i].isValid());
nlib_printf("Heap %d: [%p, %p]\n", i,
(void*)&mymem[i][0], (void*)(&mymem[i][0] + 1024 * 1024 * 2));
}
nlib_printf("GraphicsObject <---- Heap%d\n", kGraphcisObjectHeap);
nlib_printf("SoundObject <---- Heap%d\n", kSoundObjectHeap);
nlib_printf("others <---- Heap0\n"); // GetThreadDefaultHeapHandle();
nlib_printf("\n");
}
void terminate_my_heap() {
for (int i = 0; i < 3; ++i) {
HeapHandle::destroyHeap(heap_handle[i]);
}
}
class GraphicsObject : public IDisposer {
public:
static GraphicsObject* Create() {
nlib_printf(" GraphicsObject::Create begin, allocate GraphicsObject itself\n");
HeapHandle h = heap_handle[kGraphcisObjectHeap];
void* mem = h.alloc(sizeof(GraphicsObject), NLIB_ALIGNOF(GraphicsObject));
GraphicsObject* obj = new(mem)GraphicsObject();
nlib_printf(" GraphicsObject::Create end\n");
return obj;
}
bool Init(const char* resource_path) {
nlib_printf(" GraphicsObject::Init begin, allocate its data members\n");
NLIB_ASSERT(resource_path);
size_t n = strlen(resource_path);
HeapHandle h = GetHeapHandle();
void* path = h.alloc(n + 1);
if (!path) return false;
memcpy(path, resource_path, n + 1);
path_ = reinterpret_cast<char*>(path);
// DUMMY:
// Sets up vertices_ and triangles_. It may read some files.
// The regions for vertices_ and triangles_ are allocated
// from heap_handle[kGraphcisObjectHeap].
vertices_ = reinterpret_cast<float*>(h.alloc(1024 * 3 * sizeof(float)));
triangles_ = reinterpret_cast<uint16_t*>(h.alloc(512 * 3 * sizeof(uint16_t)));
nlib_printf(" GraphicsObject::Init end\n");
return vertices_ && triangles_;
}
private:
GraphicsObject() NLIB_NOEXCEPT : IDisposer(heap_handle[kGraphcisObjectHeap]) {
path_ = NULL;
vertices_ = NULL;
triangles_ = NULL;
}
virtual ~GraphicsObject() NLIB_NOEXCEPT NLIB_OVERRIDE {
nlib_printf(" GraphicsObject::~GraphicsObject() begin, free its data members\n");
HeapHandle h = GetHeapHandle();
h.free(const_cast<char*>(path_));
h.free(vertices_);
h.free(triangles_);
nlib_printf(" GraphicsObject::~GraphicsObject() end\n");
}
private:
const char* path_;
float* vertices_;
uint16_t* triangles_;
};
class SoundObject : public IDisposer {
public:
static SoundObject* Create() {
nlib_printf(" SoundObject::Crate() begin, allocate SoundObject itself\n");
HeapHandle h = heap_handle[kSoundObjectHeap];
void* mem = h.alloc(sizeof(SoundObject), NLIB_ALIGNOF(SoundObject));
SoundObject* obj = new(mem)SoundObject();
nlib_printf(" SoundObject::Crate() end\n");
return obj;
}
bool Init(const char* resource_path) {
nlib_printf(" SoundObject::Init() begin, allocate its data members\n");
NLIB_ASSERT(resource_path);
size_t n = strlen(resource_path);
HeapHandle h = GetHeapHandle();
void* path = h.alloc(n + 1);
if (!path) return false;
memcpy(path, resource_path, n + 1);
path_ = reinterpret_cast<char*>(path);
// DUMMY:
// Sets up wavedata_count_ and wavedata_. It may read some files.
// The regions for wavedata_ are allocated from heap_handle[kSoundObjectHeap].
wavedata_count_ = 2;
wavedata_ = (void**)h.alloc(n * sizeof(*wavedata_));
if (!wavedata_) return false;
for (size_t i = 0; i < wavedata_count_; ++i) {
wavedata_[i] = NULL;
}
for (size_t i = 0; i < wavedata_count_; ++i) {
wavedata_[i] = h.alloc(8192);
if (!wavedata_[i]) return false;
}
nlib_printf(" SoundObject::Init() end\n");
return true;
}
private:
SoundObject() NLIB_NOEXCEPT : IDisposer(heap_handle[kSoundObjectHeap]) {
wavedata_count_ = 0;
wavedata_ = NULL;
}
virtual ~SoundObject() NLIB_NOEXCEPT NLIB_OVERRIDE {
nlib_printf(" SoundObject::~SoundObject() begin, free its data members\n");
HeapHandle h = GetHeapHandle();
h.free(const_cast<char*>(path_));
for (size_t i = 0; i < wavedata_count_; ++i) {
h.free(wavedata_[i]);
}
h.free(wavedata_);
nlib_printf(" SoundObject::~SoundObject() end\n");
}
private:
const char* path_;
size_t wavedata_count_;
void** wavedata_;
};
class MiscObject : public IDisposer {
public:
static MiscObject* Create(HeapHandle h) {
nlib_printf(" MiscObject::Create begin, allocate MiscObject itself\n");
void* mem = h.alloc(sizeof(MiscObject), NLIB_ALIGNOF(MiscObject));
if (!mem) return NULL;
MiscObject* obj = new(mem)MiscObject(h);
nlib_printf(" MiscObject::Create end\n");
return obj;
}
bool Init() {
nlib_printf(" MiscObject::Init begin, allocate its data members\n");
data_ = GetHeapHandle().alloc(1024);
if (!data_) return false;
nlib_printf(" MiscObject::Init end\n");
return true;
}
private:
explicit MiscObject(HeapHandle h) NLIB_NOEXCEPT : IDisposer(h) {
data_ = NULL;
}
virtual ~MiscObject() NLIB_NOEXCEPT NLIB_OVERRIDE {
nlib_printf(" MiscObject::~MiscObject() begin, free its data members\n");
GetHeapHandle().free(data_);
nlib_printf(" MiscObject::~MiscObject() end\n");
}
private:
void* data_;
};
class CharacterObject : public IDisposer {
public:
static CharacterObject* Create() {
nlib_printf(" CharacterObject::Create begin, allocate CharacterObject itself\n");
HeapHandle h = GetThreadDefaultHeapHandle();
void* mem = h.alloc(sizeof(CharacterObject), NLIB_ALIGNOF(CharacterObject));
CharacterObject* obj = new(mem)CharacterObject(h);
nlib_printf(" CharacterObject::Create end\n");
return obj;
}
bool Init(const char* graphics_res_path, const char* sound_res_path) {
nlib_printf(" CharacterObject::Init begin\n");
graphics_object_ = GraphicsObject::Create();
if (!graphics_object_ || !graphics_object_->Init(graphics_res_path)) return false;
sound_object_ = SoundObject::Create();
if (!sound_object_ || !sound_object_->Init(sound_res_path)) return false;
misc_object_ = MiscObject::Create(GetHeapHandle());
if (!misc_object_ || !misc_object_->Init()) return false;
nlib_printf(" CharacterObject::Init end\n");
return true;
}
private:
explicit CharacterObject(HeapHandle h) NLIB_NOEXCEPT : IDisposer(h) {
graphics_object_ = NULL;
sound_object_ = NULL;
misc_object_ = NULL;
}
virtual ~CharacterObject() NLIB_NOEXCEPT NLIB_OVERRIDE {
nlib_printf(" CharacterObject::~CharacterObject() begin, free its data members\n");
if (graphics_object_) graphics_object_->Destroy();
if (sound_object_) sound_object_->Destroy();
if (misc_object_) misc_object_->Destroy();
nlib_printf(" CharacterObject::~CharacterObject() end\n");
}
private:
// ChacacterObject itself from Heap0
GraphicsObject* graphics_object_; // from Heap1
SoundObject* sound_object_; // from Heap2
MiscObject* misc_object_; // from Heap0
};
bool TestGraphicsObject() {
SetThreadDefaultHeapHandle(heap_handle[0]);
CharacterObject* cobj = CharacterObject::Create();
if (!cobj || !cobj->Init("graphics_resource.bin", "sound_resource.bin")) {
nlib_printf("cobj cannot be initialized\n");
return false;
}
cobj->Destroy();
if (!heap_handle[0].isClean()) {
// it is due to someone, or a chief programmer.
nlib_printf("!!!!! Leak at Heap0 !!!!!\n");
heap_handle[0].freeAll(); // force to free the leaked memory.
return false;
}
if (!heap_handle[1].isClean()) {
// it is due to graphics programmers.
nlib_printf("!!!!! Leak at Heap1 !!!!!\n");
heap_handle[1].freeAll(); // force to free the leaked memory.
return false;
}
if (!heap_handle[2].isClean()) {
// it is due to sound programmers.
nlib_printf("!!!!! Leak at Heap2 !!!!!\n");
heap_handle[2].freeAll(); // force to free the leaked memory.
return false;
}
return true;
}
static bool SampleMain(int, char**) {
init_my_heap();
bool result = TestGraphicsObject();
terminate_my_heap();
return result;
}
NLIB_MAINFUNC