How to use caching to increase render performance?
- by Christian Ivicevic
First of all I am going to cover the basic design of my 2d tile-based engine written with SDL in C++, then I will point out what I am up to and where I need some hints.
Concept of my engine
My engine uses the concept of GameScreens which are stored on a stack in the main game class. The main methods of a screen are usually LoadContent, Render, Update and InitMultithreading. (I use the last one because I am using v8 as a JavaScript bridge to the engine.
The main game loop then renders the top screen on the stack (if there is one; otherwise, it exits the game) - actually it calls the render methods, but stores all items to be rendered in a list. After gathering all this information the methods like SDL_BlitSurface are called by my GameUIRenderer which draws the enqueued content and then draws some overlay. The code looks like this:
while(Game is running) {
Handle input
if(Screens on stack == 0) exit
Update timer etc.
Clear the screen
Peek the screen on the stack and collect information on what to render
Actually render the enqueue screen stuff and some overlay etc.
Flip the screen
}
The GameUIRenderer uses as hinted a std::vector<std::shared_ptr<ImageToRender>> to hold all necessary information described by this class:
class ImageToRender {
private:
SDL_Surface* image;
int x, y, w, h, xOffset, yOffset;
};
This bunch of attributes is usually needed if I have a texture atlas with all tiles in one SDL_Surface and then the engine should crop one specific area and draw this to the screen.
The GameUIRenderer::Render() method then just iterates over all elements and renders them something like this:
std::for_each(
this->m_vImageVector.begin(),
this->m_vImageVector.end(),
[this](std::shared_ptr<ImageToRender> pCurrentImage) {
SDL_Rect rc = { pCurrentImage->x, pCurrentImage->y, 0, 0 };
// For the sake of simplicity ignore offsets...
SDL_Rect srcRect = { 0, 0, pCurrentImage->w, pCurrentImage->h };
SDL_BlitSurface(pCurrentImage->pImage, &srcRect, g_pFramework->GetScreen(), &rc);
}
);
this->m_vImageVector.clear();
Current ideas which need to be reviewed
The specified approach works really good and IMHO it is really has a good structure, however the performance could be definitely increased. I would like to know what do you suggest, how to implement efficient caching of surfaces etc so that there is no need to redraw the same scene over and over again?
The map itself would be almost static, only when the player moves, we would need to move the map. Furthermore animated entities would either require updates of the whole map or updates of only the specific areas the entities are currently moving in. My first approaches were to include a flag IsTainted which should be used by the GameUIRenderer to decide whether to redraw everything or use cached version (or to not render anything so that we do not have to Clear the screen and let the last frame persist). However this seems to be quite messy if I have to manually handle in my Render method of the screen class if something has changed or not.