Jump to content
  • Advertisement
Sign in to follow this  
Danicco

General Engine Optimizations

This topic is 1904 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm coding my first Engine and I'm unexperienced in both game programming and C++, so I'd like to ask about general optimizations and methods.

 

First, I'm going to develop games with a group of friends, and I decided to work in the engine, so my goal is to keep things as simple as possible for them and whoever else codes the game itself.

For example, I don't want to tell the game programmer "to create an object, you need to first load the object from the resource manager, call the object manager, load it to the graphic card, position it's orientation, then add it to the sceneGraph".

Instead, I'd like to keep it as simple as:

 

//Game Programming
GameStaticObject* playerHouse = new GameStaticObject();
playerHouse->Load("House1"); //done!

 

I'm going to be the only one coding the engine, so I'd like to hide how it's working and keep things simple, so when programming the game whoever does it doesn't need to know how the engine is doing it, but instead focus on creating the game.

 

So far my main layout looks like this:

 

                                  ___________________
                                 |    GameEngine     |
                                 |  .GraphicsModule  |
                                 |  .OSModule        |
                                 |  .SoundModule     |
                                 |  .NetworkModule   |
                                 |  .ResourceModule  |
                                 |  .InputModule     |
                                 |                   |
                                 |  .GameTime        |
                                 |  .GameConfig      |
                                 |  .GameScene       |
                                 |  .GameState       |
                                 |  .GameErrors      |
                                 |                   |
                                 |  .SetKey()        |
                                 |  .SetButton()     |
                                 |  .SetMouseMove()  |
                                 |  .Update()        |
                                 |  .Render()        |
                                 |___________________|
 
 __________________      ________________      ________________      _______________________
|  GraphicsModule  |    |    OSModule    |    |  OtherModules  |    |      GameObjects      |
|  .Init()         |    |  .Init()       |    |  .Init()       |    |  //Not really a class,|
|  .Uninit();      |    |  .Uninit()     |    |  .Uninit()     |    |  //I'm just grouping  |
|                  |    |                |    |________________|    |  //them all here      | 
|  #ifdef OGL/DX   |    |  #ifdef iOS/Win|                          |  - GameTime           |
|  .gfxAPI         |    |  .osAPI        |                          |  - GameConfig         |
|__________________|    |________________|                          |  - GameScene          |
                                                                    |  - GameState          |
                                                                    |  - GameErrors         |
                                                                    |_______________________|
 
                                   *And the Game Class*
                                  ______________________
                                 |         Game         |
                                 |  .GameEngine         |
                                 |  .Init()             |
                                 |  .Uninit()           |
                                 |______________________|

 

The GameEngine class have methods such as SetKey and Update so the Game class call them and don't have to worry about using the inputModule/graphicModule and figuring how it works, the class just focus on populating the data to the scene and updating the objects, and the engine deals with updating the states and rendering.

 

The Game Time and Main Game Loop

 

I've read about time in various sources and I think I got the concept of how I should implement it, but I'm not really sure if it's correctly implemented because I that got me really confused.

#include <chrono>
class GameTime
{  
    public:
        GameTime();
        void ResetTimers();
        bool UpdateGame();
        float GetInterpolation();
        __int64 GetRealTime(); 
    private:
        __int64 _currentRealTime;
        __int64 _currentGameTime;
        __int64 _nextGameUpdateTime;
  
        int _updatesPerSecond, _maxFrameSkip, _timeResolution;
        int _currentFrameSkipCounter;
        float _interpolation;
};
 
GameTime::GameTime()
{
    //Defining default values
    _updatesPerSecond = 25;
    _timeResolution = 1000 / _updatesPerSecond;
    _maxFrameSkip = 5;
 
    _nextGameUpdateTime = GetRealTime();
    _currentRealTime = _currentGameTime = 0;
    _currentFrameSkipCounter = 0;
}
//This is called once per logical update
void GameTime::ResetTimers()
{
   _currentRealTime = GetRealTime();
   _currentFrameSkipCounter = 0;
}
//This returns if the game logic should be updated or not, it's called right after ResetTimers()
bool GameTime::UpdateGame()
{
    if( (_currentRealTime - _nextGameUpdateTime > _timeResolution) && (_currentFrameSkipCounter < _maxFrameSkip) )
    {
        _currentGameTime += _timeResolution;
        _nextGameUpdate += _timeResolution;
        _currentFrameSkipCounter++;
        return true;   
    }
    else
    {
        _interpolation = (_currentRealTime + _timeResolution - _nextGameUpdate) / (float)_timeResolution;
        return false;    
    }
}
__int64 GameTime::GetRealTime()
{
    return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
 
//And my main game loop is this:
void GameEngine::Update()
{
    _gameTime.Reset();
    while(_gameTime.UpdateGame()) //Update the game until _nextGameUpdateTime > _currentRealTime
    {
       //Get the inputState from the last request to this current one
       InputState inputState = inputModule.GetInputState(_gameTime.GetCurrentGameTime());
       gameState.Update(inputState);
    }
 
    //Get the "left over time" and send to render
    Render(_gameTime.GetInterpolation());
}
void GameEngine::Render(float interpolation)
{
    //render here
}

 

Did I got the concept right?

After reading this post (http://lspiroengine.com/?p=378) I think I got the concept, but I'm not sure if it's correctly implemented.

 

The Input State

 

class InputModule
{
    public:
        void SetKey(int keyCode, bool keyStatus);
        void SetButton(int buttonCode, bool buttonStatus);
        void SetMouseMove(int positionX, int positionY);
        void SetWheel(int wheelMove);
        //Those functions above will fill the _inputQueue vector with timestamps
 
        InputState* GetInputState(__int64 currentRequest);
    private:
        InputState _inputState;
        vector<Input*> _inputQueue;
 
        __int64 previousRequest;
};
 
void InputModule::SetKey(int keyCode, bool keyStatus)
{
    Input* newInput = new Input();
    newInput->inputType = InputType::Keyboard;
    newInput->inputCode = keyCode;
    newInput->inputStatus = keyStatus;
    newInput->inputTime = Engine::_time->GetRealTime();
 
    _inputQueue.push_back(newInput);
}
//Same for all other inputs
InputState* GetInputState(__int64 currentRequest)
{
    ClearState();
    if(_inputQueue.size() > 0)
    {
        while(_inputQueue[0]->inputTime <= currentRequest)
        {
            switch(_inputQueue[0]->inputType)
            {
                case InputType::Keyboard:
                {
                    _inputState.keyboard[_inputQueue[0]->inputCode].keyStatus = _inputQueue[0]->inputStatus;
                    break;
                }
                case InputType::Mouse:
                {
                    _inputState.mouse[_inputQueue[0]->inputCode].buttonStatus = _inputQueue[0]->inputStatus;
                    break;
                }
                case InputType::MouseMove:
                {
                    _inputState.mouse.positionX = _inputQueue[0]->positionX;
                    _inputState.mouse.positionY = _inputQueue[0]->positionY;
                    break;
                }
            }
 
            //Input processed, removing it from the queue
            delete _inputQueue[0]; //memory
            _inputQueue.erase(_inputQueue.begin()); //queue
            if(_inputQueue.size() == 0) { break; }; //breaking if it's empty
        } 
    }
 
    return &_inputState;
}

 

Keyboard is an array of 256 elements with a single bool variable for the key status, same for mouse, but mouse is only 16 elements.

So I return an InputState with the bools true/false depending on whether the key were pressed until the requestedTime. I'm not sure if I need to keep track of the lastRequestedTime, so the variable is still there until I figure it out.

 

ClearState() iterates over the 256 keyboard array and 16 mouse array and sets all the bool values to false. I think there should be a better way to clear (maybe deleting the state and creating a new one is faster?) but I still can't test it.

 

The GameScene

 

class SceneObject
{
    public:
        GameObject* objectPointer; 
        //The game object also has a pointer to it's own SceneObject
        //So if it gets a child, it's easy to find and add
        SceneObject* previousObject;
        SceneObject* nextObject;
        SceneObject* parentObject;
};
class GameScene
{
     public:
         //functions to add and remove sceneObjects, same as linked lists
         void Add(SceneObject* sceneObject);
         void Remove(SceneObject* sceneObject);
     private:
         SceneObject* sceneObjectList;
         SceneObject* currentObject;
}
 
void GameScene::Add(SceneObject* sceneObject)
{
    currentObject = sceneObjectList;
    while(currentObject->nextObject != 0)
    { currentObject = currentObject->nextObject; }
 
    currentObject->nextObject = sceneObject;
    sceneObject->previousObject = currentObject;
}
void GameScene::Remove(SceneObject* sceneObject)
{
    if(sceneObject->nextObject == 0 && sceneObject->previousObject != 0)
    {
        sceneObject->previousObject->nextObject = 0;
    }
    else if(sceneObject->nextObject != 0 && sceneObject->previousObject == 0)
    {
        sceneObject->nextObject->previousObject = 0;
    }
    else if(sceneObject->nextObject != 0 && sceneObject->previousObject != 0)
    {
        sceneObject->previousObject->nextObject = sceneObject->nextObject;
        sceneObject->nextObject->previousObject = sceneObject->previousObject;
    }
 
    delete sceneObject;
}

 

To add an object as child of another, I do this:

 

GameObject* myParentObject = new GameObject();
GameObject* myChildObject = new GameObject();
 
myChildObject->sceneObject->parentObject = myParentObject->sceneObject;

 

And for transformations I do this:

 

SceneObject* myCurrentSceneObjectTransformation = myCurrentObject->sceneObject;
while(myCurrentSceneObjectTransformation->parentObject != 0)
{
    //get the transformation mat4 from the object->gameObject and multiply to my currentObj
    myCurrentSceneObjectTransformation = myCurrentSceneObjectTransformation->parentObject;
}

 

I think this is a bad way to do it... anything recursive like this during the game loop looks like a bad idea to me, but I'm not sure and I couldn't think of other alternatives.

 

The Game Model

 

class BaseModel
{
    public:
        void Load(char* assetName);
        
        static ResourceModule* resourceModule;
        static GraphicModule* graphicModule;
};
class GameModel : public BaseModel
{
};
 
void GameEngine::Initialize()
{
    //Setting the addresses that the model will use
    BaseModel::_resourceModule = &_resourceModule;
    BaseModel::_graphicModule = &_graphicModule;
}
 
void BaseModel::Load(char* assetName)
{
    ResourceObject* resObject;
    if(resourceModule->IsLoaded(assetName)) //if the data is already in the vector<Objects> in the resourceModule
    {
        resObject = resourceModule->GetAssetData(assetName);
    }
    else
    {
        ifstream fileReader = resourceModule->GetPointer(assetName);
        resObject = new ResourceObject();
        //fill resObject from the stream
 
        resourceModule->AddResourceObject(resObject);
    }
 
    //fill this obj according to resObject
    vertexBufferObject.vertexes = new float[x];
    //etc
 
    graphicModule->LoadVBO(&vertexBufferObject);
    graphicModule->LoadMaterial(&material);
    //model ready for rendering
 
    //Thinking if I should add an instance to the sceneGraph already
}

 

This way I want to get to this:

 

GameModel* newModel = new GameModel();
newModel->Load("assetName"); //object ready to use/display ingame

 

This is what I got so far... do you have any suggestions or optimizations, or even alternatives for what I'm doing?

I'm unexperienced with C++ so I think there should have plenty of bad practices there too, and any advice is really appreciated.

Share this post


Link to post
Share on other sites
Advertisement

that is actually not too bad, a good start. First off be aware of memory and use smart-pointers when you want to express ownership. I think you are on the right track with thinking "this is what I want my interface to look like how do I get it". You should really be using the standard library, instead of taking a char* you should take a const std::string& and instead of storing your vertex buffer in a C style array you can use a std::vector.

Share this post


Link to post
Share on other sites

Are you using some kind of memory pooling? It is an easy to implement struct that increases the performance by a lot:

http://en.wikipedia.org/wiki/Memory_pool

 

Also for the boolean arrays, you can use bit masks to solve tests faster. You may also use bitsets if you want to just reduce size:

www.cplusplus.com/reference/bitset/bitset/

 

Finally, you should be careful with vectors, sometimes it is faster to use an Map or a list instead (specially if you are going to remove a lot of elements from it).

Share this post


Link to post
Share on other sites

If you spend time, before you even have a working game, trying to optimise .ClearState() method, then your priorities are really screwed up.

 

As far as I understand it, ClearState is called at most once per logic tick. It iterates an incredibly tiny array (only a few 100 elements) of bools. Therefore you almost certainly don't care.

 

Even if ClearState *DID* become a problem, you can deal with it later. Make your game work first.

 

It seems that you might be in danger of becoming an architecture astronaut, too. Remember to do the simplest thing that could possibly work.

Share this post


Link to post
Share on other sites

Are you using some kind of memory pooling? It is an easy to implement struct that increases the performance by a lot:

http://en.wikipedia.org/wiki/Memory_pool

 

Also for the boolean arrays, you can use bit masks to solve tests faster. You may also use bitsets if you want to just reduce size:

www.cplusplus.com/reference/bitset/bitset/

 

Finally, you should be careful with vectors, sometimes it is faster to use an Map or a list instead (specially if you are going to remove a lot of elements from it).

 

As for vectors, if element order is irrelevant you can swap the element you wish to remove with the last element in the vector and then remove the last element to make element removal faster.

Share this post


Link to post
Share on other sites

If possible use object pools to short-circuit overhead of creating/destroying similar objects constantly.  Variable sized sub objects may require seperate allocation, but fixed sized (even dissimilar) objects may still be numerous  (UNION and PACKED   ?? do these even work anymore for 'smart compilers???)

 

Data size minimalizing - picking proper data size to shrink data structures  (cache misses waste huge amounts of time for games which often have large randomly/irrefularly  accessed data sets)

Edited by wodinoneeye

Share this post


Link to post
Share on other sites

Are you using some kind of memory pooling? It is an easy to implement struct that increases the performance by a lot:

http://en.wikipedia.org/wiki/Memory_pool

 

Also for the boolean arrays, you can use bit masks to solve tests faster. You may also use bitsets if you want to just reduce size:

www.cplusplus.com/reference/bitset/bitset/

 

Finally, you should be careful with vectors, sometimes it is faster to use an Map or a list instead (specially if you are going to remove a lot of elements from it).

 

I'll add these two for the "enhancing" part, I'm still trying to get what's currently not working before optimizing what already is, thanks!

 

If you spend time, before you even have a working game, trying to optimise .ClearState() method, then your priorities are really screwed up.

 

As far as I understand it, ClearState is called at most once per logic tick. It iterates an incredibly tiny array (only a few 100 elements) of bools. Therefore you almost certainly don't care.

 

Even if ClearState *DID* become a problem, you can deal with it later. Make your game work first.

 

It seems that you might be in danger of becoming an architecture astronaut, too. Remember to do the simplest thing that could possibly work.

 

Oh I know I do that quite often, but I'm being careful with this one.

I have a very crude working project already but it's all hardcoded, nothing automated and classes are a mess.

 

For this one I'm trying to get everything well organized and hopefully better than my very basic prototype.

 

If possible use object pools to short-circuit overhead of creating/destroying similar objects constantly

 

By object pools do you mean this?

 

class ResourceModule
{
    public:
        //various Load functions
    private:
        vector<Mesh*> _loadedMeshes;
        vector<Texture2D*> _loadedTextures;
};

 

When my model object requests it's meshes/textures, the resource checks if they're loaded already and return a pointer, else I have to request the stream to load them in the vector and then return the pointer.

 

I'm not sure how this is usually done though, so I'm trying to come up with the resource format, something like this:

 

//Single File in Binary
//[byte size] #identifier
 
[int] MeshesHeader_start
[int] MeshesHeader_end
 
[int] TexturesHeader_start
[int] TexturesHeader_end
 
//MeshesHeader_start
[int] MeshID
[int] MeshFilePointer
//MeshesHeader_end
 
//MeshData Start - "MeshFilePointer" points to here
[int] vertexCount
[vertexCount * float] vertexData
[int] normalCount
[normalCount * float] normalData
[int] uvCount
[uvCount * float] uvData
[int] indexCount
[indexCount * int] indexData
//MeshData End
 
//TexturesHeader_start
[int] TextureID
[int] TextureFilePointer
//TexturesHeader_end
 
//TextureData Start - "TextureFilePointer" points to here
[int] textureWidth
[int] textureHeight
[textureWidth * textureHeight * 4 * char] textureData in char R, char G, char B, char Alpha format
//TextureData End

 

And I have another file that has a "Model" object which has a meshID and textureID, and I just ask the ResourceModule to check for these IDs and load in the vector if they're not there, or return the pointers if they are.

 

I'm unsure of how to deal with objects of multiple models, such as a sword that has two different meshes such as "Blade" and "Hilt", since my "Mesh" class has no positioning at all. Maybe a higher model that contains two+ models or something.

 

And should I keep a vector<Model*> too? It would only contain pointers or ints to the meshID and textureID, but I'm not sure if I should read from the file everytime a model is request or if I should store in memory to just loop through the vector.

They should be requested either during a loading phase if the game has levels, or during gameplay if it's open world, but I don't know if it's better to loop through a vector or read from a file.

 

Anyway, I got lots of these doubts which is probably due to my inexperience and lack of knowledge, so I really appreciate these advices, thank you very much!

Share this post


Link to post
Share on other sites

 


By object pools do you mean this?

 


 

He likely means the object pool pattern, an extremely useful software design pattern for games because you can offload the cost of allocating most things to loading screens/while you're loading files/etc, as allocating a new object is a relatively expensive operation.

You can find a nice game specific writeup about the pattern here.

Share this post


Link to post
Share on other sites

Since you're using C++11 anyway (as I can see from #include <chrono>), why not use fixed width integer types from <cstdint> instead of non-portable __int64?

See: http://en.cppreference.com/w/cpp/header/cstdint

 

You've also mentioned that you'd like to avoid sequential coupling:

For example, I don't want to tell the game programmer "to create an object, you need to first load the object from the resource manager, call the object manager, load it to the graphic card, position it's orientation, then add it to the sceneGraph".

 

However, you're using raw pointers with manual memory management, which introduces sequential coupling on its own (need to allocate before use, need to check against nullptr during use, need to delete after use). I think you should get rid of all of the resource-owning raw pointers: http://klmr.me/slides/modern-cpp/

Instead, by default prefer std::unique_ptr (to express unique ownership; note that it has no run-time overhead compared to raw pointers), and then (only if you absolutely have to share) std::shared_ptr.

 

See:

http://www.informit.com/articles/article.aspx?p=1944072

http://herbsutter.com/elements-of-modern-c-style/

http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/

Edited by Matt-D

Share this post


Link to post
Share on other sites

If you spend time, before you even have a working game, trying to optimise .ClearState() method, then your priorities are really screwed up.

 

As far as I understand it, ClearState is called at most once per logic tick. It iterates an incredibly tiny array (only a few 100 elements) of bools. Therefore you almost certainly don't care.

 

Even if ClearState *DID* become a problem, you can deal with it later. Make your game work first.

 

It seems that you might be in danger of becoming an architecture astronaut, too. Remember to do the simplest thing that could possibly work.

 

 

Unfortunately like most conflicting advice paradoxes   doing 'the simplest; thing' doesnt always result in success when a bad but easy solution becoming so imbedded that it cant practically be removed  (within resource budgets) when it is found to be a unworkable solution..

 

Design with performance consideration should be made but in proper moderation 

 

'work' doesnt equate to 'work to the specs and real final requirements of the project'  especially when prototyping just to 'get the damned thing working'  is often done.  (Ive seen enough 'prototypes' become THE product resulting in company killing delays)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!