h1

threading the memory management and future dev plans

July 2, 2009

So, I finally got the memory management system threaded and defeated the only race condition, which could be found on the close down because of the order it waited for shit in. I fixed it with a shut down event that would poke when both mutexes were released in a timeout waiting loop so there was no wrong order to wait for them in. Anyway, I’m not here to show you that, but instead my threading library. It’s very bare bones, and only has a Win32 implementation for now, but could easily be implemented in another threading library, and I will do so with pthreads sometime in the future.

But I want to show it. I will show it now. SHOWING!

It makes use of factories and singletons, but those should be obvious in terms of their functionality. Factory inherits Factory<Singleton > as well. I might change this if it becomes more efficient to create multiple factory instances for different creation modes (i.e.) each has a different set of creation parameters.

threading.h
#ifndef THREADING_H
#define THREADING_H

#include
#include
#include
#include
#include
#include

#include “dlmalloc_object.h”
#include “factory.h”

namespace SpaceSim
{
class WaitingObject: public DLMallocAlloced
{
protected:
static std::list *wobjs;
public:
WaitingObject();
~WaitingObject();

static bool WaitForAll(std::list *objs = NULL);

virtual bool Wait() = 0;
};

class Mutex: public WaitingObject
{
public:
static Mutex *Create(bool is_global = false, unsigned long spincount = 0x80000400);

virtual bool Lock() = 0;
virtual void Unlock() = 0;

virtual unsigned long SpinCount() const = 0;
};

class GlobalMutex: public Mutex
{
protected:
HANDLE mutex;
public:
GlobalMutex();
~GlobalMutex();

virtual bool Lock();
virtual void Unlock();

virtual bool Wait();

virtual unsigned long SpinCount() const;
};

class LocalMutex: public Mutex
{
protected:
unsigned long spincount;
CRITICAL_SECTION mutex;

public:
LocalMutex(unsigned long spincount);
~LocalMutex();

virtual bool Lock();
virtual void Unlock();

virtual bool Wait();

virtual unsigned long SpinCount() const;
};

class Event: public WaitingObject
{
protected:
HANDLE event;

public:
static Event *Create();

Event();
~Event();

bool Wait();
void Poke();
void Reset();
};

typedef int (*ThreadMainProc)(void *args);
typedef unsigned long ThreadID;

class Thread: public WaitingObject
{
protected:
void *args;
HANDLE thread;
ThreadMainProc proc;
bool is_running;

public:
static Thread *Create(ThreadMainProc main_proc, void *args);
static ThreadID GetID();

Thread(ThreadMainProc main_proc, void *args);
~Thread();

bool Run();
bool Break();
bool Wait();

bool IsRunning() const;
};
};

#endif

threading.cpp
#include “threading.h”
#include “logger.h”

namespace SpaceSim
{
std::list *WaitingObject::wobjs = NULL;

WaitingObject::WaitingObject()
{
if (!wobjs)
wobjs = new std::list;

wobjs->push_back(this);
}

WaitingObject::~WaitingObject()
{
wobjs->remove(this);

if (wobjs->empty())
{
delete wobjs;
wobjs = NULL;
}
}

bool WaitingObject::WaitForAll(std::list *objs)
{
bool success = true;
if ((!objs) || objs->empty())
objs = wobjs;

std::list::iterator it;
for (it = objs->begin(); it != objs->end(); ++it)
success = success && (*it)->Wait();

return success;
}

class MutexFactoryParameters: public FactoryParameters
{
public:
bool is_global;
unsigned long spincount;

MutexFactoryParameters(bool is_global = false, unsigned long spincount = 0x80000400):
is_global(is_global), spincount(spincount) {}
};

template<> class Factory: public Singleton >
{
public:
virtual Mutex *Create(const FactoryParameters &params)
{
const MutexFactoryParameters &mparams = param_cast(params);

if (mparams.is_global)
return new GlobalMutex();
else
return new LocalMutex(mparams.spincount);
}
};

Mutex *Mutex::Create(bool is_global, unsigned long spincount)
{
return Factory::GetSingleton().Create(MutexFactoryParameters(is_global, spincount));
}

GlobalMutex::GlobalMutex()
{
static unsigned long mtctr = 0;
char name[256], cvbuf[16];

ltoa(mtctr++, cvbuf, 16);
strcpy(name, “SPACESIM_MUTEX_0x”);
strcat(name, cvbuf);

assert(mutex = CreateMutex(NULL, FALSE, name));
}

GlobalMutex::~GlobalMutex()
{
CloseHandle(mutex);
}

bool GlobalMutex::Lock()
{
return WaitForSingleObject(mutex, INFINITE) == WAIT_OBJECT_0;
}

void GlobalMutex::Unlock()
{
ReleaseMutex(mutex);
}

bool GlobalMutex::Wait()
{
bool ls = Lock();
Unlock();
return ls;
}

unsigned long GlobalMutex::SpinCount() const {return 0;}

LocalMutex::LocalMutex(unsigned long spincount): spincount(spincount)
{
assert(InitializeCriticalSectionAndSpinCount(&mutex, spincount));
}

LocalMutex::~LocalMutex()
{
DeleteCriticalSection(&mutex);
}

bool LocalMutex::Lock()
{
EnterCriticalSection(&mutex);

return true;
}

void LocalMutex::Unlock()
{
LeaveCriticalSection(&mutex);
}

bool LocalMutex::Wait()
{
bool ls = Lock();
Unlock();
return ls;
}

unsigned long LocalMutex::SpinCount() const {return spincount;}

Event *Event::Create()
{
return Factory::GetSingleton().Create(FactoryParameters());
}

Event::Event()
{
static unsigned long evctr = 0;
char name[256], cvbuf[16];

ltoa(evctr++, cvbuf, 16);
strcpy(name, “SPACESIM_EVENT_0x”);
strcat(name, cvbuf);

assert(event = CreateEvent(NULL, TRUE, FALSE, name));
}

Event::~Event()
{
CloseHandle(event);
}

bool Event::Wait()
{
return WaitForSingleObject(event, INFINITE) == WAIT_OBJECT_0;
}

void Event::Poke()
{
SetEvent(event);
}

void Event::Reset()
{
ResetEvent(event);
}

class ThreadFactoryParameters: public FactoryParameters
{
public:
void *args;
ThreadMainProc proc;

ThreadFactoryParameters(ThreadMainProc proc, void *args): proc(proc), args(args) {}
};

template<> class Factory: public Singleton >
{
public:
virtual Thread *Create(const FactoryParameters &params)
{
const ThreadFactoryParameters &mparams = param_cast(params);

return new Thread(mparams.proc, mparams.args);
}
};

Thread *Thread::Create(ThreadMainProc main_proc, void *args)
{
return Factory::GetSingleton().Create(ThreadFactoryParameters(main_proc, args));
}

struct proc_attribs
{
ThreadMainProc proc;
void *args;
Event *mevent;
};

static DWORD WINAPI __ThreadMainProc(LPVOID param)
{
proc_attribs &pa = *((proc_attribs *)param);

ThreadMainProc proc = pa.proc;
void *args = pa.args;

pa.mevent->Poke();

return proc(args);
}

ThreadID Thread::GetID() {return GetCurrentThreadId();}

Thread::Thread(ThreadMainProc main_proc, void *args): proc(main_proc), args(args) {}

Thread::~Thread()
{
if (is_running) Break();
}

bool Thread::Run()
{
if (is_running) return false;

DWORD temp;

proc_attribs pa;
pa.args = args;
pa.proc = proc;
pa.mevent = Event::Create();

thread = CreateThread(NULL, 0,
__ThreadMainProc, (void *)&pa, 0, &temp);

is_running = (bool)thread;

pa.mevent->Wait();
return is_running;
}

bool Thread::Break()
{
if (!is_running) return false;

bool success = CloseHandle(thread);
is_running = !success;
return success;
}

bool Thread::Wait()
{
if (!is_running) return false;

bool success = (WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0);
return success && Break();
}

bool Thread::IsRunning() const {return is_running;}
};

Now, for stuff more interesting to the consumer who buys the final game (yes… the game will be sold, and I don’t care if people see snippets of the code). The Actor and StaticObject classes will extend the SceneObject class, and all that good stuff will be Lua bound. Actors will be partly virtual and will have to have trigger functions such as being told where to go by the controller filled in. This would be in certain implementations, especially ships and the like. Anyway, that about says it and all that good stuff. I’m making tracks, and it is AWESOME.

h1

reference counter based memory management + bitching about life

June 8, 2009

The bitching about life part comes first.  I believe the DCFC song “No Sunlight” sums up my rant in that regard nicely, so I won’t waste the space here.  Okay I’m done with that.

Now, on to the not depressing stuff.  I was thinking of ways I could potentially speed up memory management with threads.  I was basically thinking along these lines:  I should queue my object deletions so that I can delete them on the side while the next iteration is in progress.  So I came up with an interesting system.  I haven’t implemented the threading yet, but I have seperated object deletion from the main loop entirely and it’s just tacked on the end for now.

Basically I implemented a reference counter object management system.  Each ManagedObject has a grab() and drop() method, used to count references.  When the number of references reaches zero, the drop() method calls the queue_deletion() method in the ObjectManager singleton.  The ObjectManager also keeps track of ManagedObject’s that are still kicking, so that they can be cleaned up at the very end if they weren’t properly dropped.  This should keep memory leaks from being as bad, though that doesn’t mean I won’t bust my ass trying to find all of them.  So the current code for updating is as follows:

void Engine::RunMainLoop(double timestep)
{
while (running)
{
if (!Update(timestep)) running = false;
if (!Render()) running = false;
ObjectManager::Singleton().CleanUp();
}
}

The ObjectManager deletes all things queued for deletion in CleanUp(). What I could do is simply start a cleanup thread that cleans up every second or so. For the threaded version it will lock the mutex for the queue to copy it over to a private queue and clear the public one so that it can be used again while the cleanup iterator goes through and deletes everything added previously. If you want the full classes, I shall have t3h c0dez in an article on custom memory management for C++ classes shortly.

EDIT: Okay, article’s up. It’s called “C++ custom memory management primer”. Link under My Articles in the sidebar.

h1

OGRE integration

June 6, 2009

Well, I’m beginning to integrate OGRE, and lemme tell you… it’s growing on me. I also find that it can actually be quite slim when you leave out the utility components and do most things yourself. I just want it’s fantastic material system. I’m beginning to figure out just how unbelievably moddable this can make the game as well. In addition to loading an Actor file for every ship, that actor file will reference an OGRE material and mesh file, which is also fully moddable. I’ve also mastered the material abstraction system. For example, you can create a base class material called ship_material that implements normal mapping and paralax mapping over a main texture with all textures referenced by alias, then extend it with set_texture_alias on each ship, so none of that fancy shit has to be replicated, just say which textures to use and reference the .mesh in the Actor file. bam! I love modularity.

h1

Continuum Engine

June 3, 2009

That’s the name I have for it now.  It’s a badass name, no?

Anyway, I’ve got 3demon engine working as a rendering engine for now, but I still hate the Irrlicht style shadows it uses.  It IS a branch of Irrlicht.  They’re just so… broken.  They don’t even have a consistent appearance across subsystems.  OpenGL has a different volume method from D3D9 in it.  I want something like OGRE, but I absolutely hate the way it’s organized.

Does anyone have an idea as to what I should do?  I don’t want to use my own rendering engine because I’d like the focus of this project to be on the game itself this time, not every little technical thing to support it.  I will give OGRE a shot, and if that’s how it has to be, so be it.  But do you absolutely HAVE to use the ReferenceApp layer?  That, I put to you, is TERRIBLE API design.

h1

homeworld 2 like engine and other happenstance

May 27, 2009

So if you’ve never played Homeworld 2, then too bad for you because you missed out.  Big time.  That being said, when attempting a mod of it, I was immediately inspired by how unbelievably modular and streamlined it was.  It’s a space combat simulation with incredible levels of detail and realism, and it was modded in to a naval combat sim with the same level of detail, and graphics to make it look like it’s own game.  This is with no actual access to the engine source code.  So I’m like:  DAYUM SON.

I am attempting to create a modular engine of the likes.  You could also compare this engine to the Cortex Command engine, but CC is a totally different beast, because of it’s focus on pixel graphics and physics.

I’ve had one false start already, because I hadn’t thought the iterface through entirely.  Abstracting a camera as an actor makes sense, but only if you can unify it under the controller class, which would be a horrible mess.  Also, in order to get the root entity crap to work, and still have a new entity add itself to the singleton engine, I had to derive an otherwise worthless class to do this, in order to keep GetSingleton() from locking up the first time it’s called.

In other words, a disaster of bad code.  I’ve taken a few lessons in engine design away from it though, and I will start a new one, hopefully with less fail.  I’m drawing up a new inheritance tree right now.

And in terms of life, it’s being something of a bitch and something of a thing which is awesome.  I let slip about how I’m a rollercoaster of genetic disorders that will crash and burn before my time, and Travis just said, “well, we’ll make it a time ’till then, brother.”  Which I found extremely heart warming but also extremely sad.  Sigh.  It bugs me that I still can’t tell if I’ll be leaving anything behind, and it’s a loss for someone no matter what the case.

You should all check out Inhuman Comic, because it’s more baller than your mom’s genetalia.  OOOOO BURN.

That’s about it for now.

h1

first post in a long while + some erosion progress

March 15, 2009

Well apparently school takes up time.  So it’s been a while.  But here goes:

I got all A’s on my midterms, so fuck YES!  I feel happy about that.  Good times.  I have been spending an increasing amount of time with other people as well.  I know, crazy right?  It’s me.  Apparently some people don’t hate my guts, and I seem to have met some in person.  They are decidedly cool.  Hence that last post about Travis’ birthday.  So, yeah.

What else?  Oh, making progress on the Qix clone, Erosion.  Here you go:

It has a game state machine with GAME OVER and gameplay states and whatnot.  It has much better graphics now, I implemented bloom with my old shader framework and improved the color scheme.  You can die, and I’ve even got gamepad working with rumble on collisions and all.  You will also notice that I replaced the simple bouncing bar with a bar with a thruster in a damped system that actually semi-tries to follow you.  Enjoy this clip:

h1

tesselation + travis’ b-day

February 23, 2009

Both are filled with good times.  My good man Travis turned 17 on Thursday.  Yesterday we had had a b-day party at Ian’s place, and it was a fucking good time is what it was.  I can’t handle big parties, I hate ’em, but it was really just a few close friends just hanging out.  Which is my favorite.  I can’t handle too many people, or else I do an Asperger’s freakout.  I played some guitar, explained some autonoma theory, talked about stuff, then got blitzed and turned in to a sentemental cartoon of a wino.  I’d say my +20 liver of steel diety level holy liquor containment was definitely called upon.  I rarely drink so much.  But anyway.  I also recorded some hilarious footage, but I was told posting it would be in bad taste, so no drunken antics for you.

But here are two pictures:

Travis recites a random Lord of the Rings passage to Cat in the spirit of awesome.

Travis recites a random Lord of the Rings passage to Cat in the spirit of awesome.

SHR3DZ0RZ

You know what else totally shreds?  Tesselation algorithms!  Check this shit out:

  1. Walk around loop and record all x and y values in respective lists.  O(n) [iterate through nodes and copy values to lists]
  2. Remove duplicate x and y values, treating the lists independently.  O(n) [iterate through lists, remove and continue only]
  3. Divide the space in to tiles using the x and y values from the nodes as seperate lists.  O(1) [create tile array]
  4. Scan the tiles left to right, top to bottom.  Anywhere possible: Grow a rectangle right and down for each iteration until as much space as possible is full.  Repeat until all scanned.  O(amortize(n))

Conclusion: this is a good-ass algorithm!  BAM!