Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Improving performance on Netbook  (Read 7302 times)

0 Members and 1 Guest are viewing this topic.

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« on: January 22, 2010, 10:56:51 am »
Hi folks,
My game was mainly written on OSX with XCode, I've now moved to linux on a netbook for on the go hacking and find performance is just dreadful. My game is turn based strategy, with ascii based with sf::string tiles. I generate a cache of tiles of ascii chars (32-127) and keep them in a std::map. Here's my cache code.

Code: [Select]

    //create lookup table of tiles
    //first make a char array of our tile GfxId's (ascii table 32-127)
std::string gfxStr;
for (int i=32;i<128;i++){
char chr=i;
gfxStr.append(1,i);
}
//std::cout << "gfxStr = " << gfxStr << std::endl;

    //then iterate it creating SFML drawables
    for (int i=0; i < gfxStr.size();i++){
        const char gfxChr=gfxStr[i];
        std::string str(1,gfxChr);
sf::String tile;
        tile.SetText(str);
        tile.SetFont(m_displayFont);
        tile.SetSize(TILEHEIGHT-TEXTPADDING);
   tile.SetStyle(sf::String::Regular);
//add the drawable to the  tile cache
   tileLookupMap[gfxChr]=tile;
    }


My screen is draw by sending an array of ScreenTile's objects, a member of which is a char depicting the tile to be shown. The screen is 80x26 tiles and every one must be drawn every frame. However, I only call a redraw on events or when animating a single projectile. When animating it calls the redraw (which draws every tile) as fast as possible until the animation is finished. Currently my single projectile tile crawls over the screen at about 1fps. Not good.

Here's the draw code. My latest effort looks up the tile object from the container map and modifies it's position and attribs (bold etc) before drawing it.
Code: [Select]


void GfxEngine::render(std::vector<std::vector<ScreenTile> > screenArray, gameState_t gameState)
{
m_mainWindow.Clear(sf::Color(0, 0, 0));
//iterate and draw map
for (int n=0; n < SCREENTILEHEIGHT; n++){
for (int m=0; m < SCREENTILEWIDTH; m++){
sf::String *tile=&tileLookupMap[screenArray[n][m].gfxId];
tile->SetColor(colorLookup[screenArray[n][m].color]);
tile->SetPosition(m*TILEWIDTH,n*TILEHEIGHT);
//set attributes
if (screenArray[n][m].attribBold) tile->SetStyle(sf::String::Bold);
if (screenArray[n][m].attribReverse){
//if reverse we draw a coloured square underneath the tile then the ascii on top.
tile->SetColor(colorLookup[0]);
sf::Image background(TILEWIDTH, TILEHEIGHT, colorLookup[screenArray[n][m].color]);
sf::Sprite sprite(background);
sprite.SetPosition(m*TILEWIDTH,n*TILEHEIGHT);
m_mainWindow.Draw(sprite);
}
m_mainWindow.Draw(tileLookupMap[screenArray[n][m].gfxId]);
}
}


I've posted gprof scores at the link below that shows large chunks of time stuck in the render routine and sf::string. Top also shows my program using 60% CPU and Xorg the remaining 40% while animating. Other OpenGL apps appear to work fine and use little CPU. Is my approach sane and should it actually work? The netbook is a N280 1.66Mhz (about the speed of a 900Mhz P3) with a GMA950 chipset (945G). Any help is greatly appreciated.

http://code.bulix.org/1ntsw4-73971

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Improving performance on Netbook
« Reply #1 on: January 22, 2010, 11:14:07 am »
This kind of code should be much faster in SFML 2. If you want you can already try it from the SVN repository.

You should also cache every single tile of your screen, so that your rendering loop is reduced to calling Draw on each tile (changing a drawable attributes all the time has a small cost).

If it's still too slow, you can try using sprites (in this case you'll have to generate the glyphs yourself with an image editor), they may be faster than 1-character texts.
In SFML 2 it may even be possible to use a sf::Font and sf::Sprite together; that should be a better strategy than using sf::Text in your case.

But try SFML 2 first ;)

EDIT: oh my god, I didn't see that you were creating a sf::Image on the fly everytime that you want to draw a colored square. this is insane. Rather use a sf::Shape, and better: create it once and store it!
Laurent Gomila - SFML developer

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #2 on: January 22, 2010, 12:22:49 pm »
Thanks for the fast reply Laurent. Is SFML v2.0 usable on OSX? I though the Mac port was now unsupported. Regarding the sf::Image there would only ever be one of those at any one time. It's basically used to show a "reverse" character when a tile is targetting or selected. When it's the AI's turn (which I use for benchmarking as it's trigger happy) it's never used.

I'll look into expanding my cache. This does mean creating  96 tiles, 16 colours each with both bold and reverse. 96x16x2 = 3072 tiles.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Improving performance on Netbook
« Reply #3 on: January 22, 2010, 12:32:10 pm »
Quote
Is SFML v2.0 usable on OSX? I though the Mac port was now unsupported

Ah, you're right. I forgot that you were using it, sorry.

Quote
Regarding the sf::Image there would only ever be one of those at any one time. It's basically used to show a "reverse" character when a tile is targetting or selected. When it's the AI's turn (which I use for benchmarking as it's trigger happy) it's never used.

Ok, that sounds better than what it looks like in your code ;)
But even if it's almost never used, it is still a good practice to handle it properly.

Quote
I'll look into expanding my cache. This does mean creating 96 tiles, 16 colours each with both bold and reverse. 96x16x2 = 3072 tiles.

Well, I didn't mean to precompute every possible combination that you will have to draw. I rather say that you should have one sf::Sprite instance for every tile drawn (so that what you see is what you have in memory). You don't have to store 16 times a sprite, you can still change its color during runtime. But don't do every time you draw it, do it when the color actually changes.
Laurent Gomila - SFML developer

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Improving performance on Netbook
« Reply #4 on: January 22, 2010, 12:35:31 pm »
And... you should pass your vectors by const reference rather than by value. Each time you call render(), you do an entire copy of your tile array. This can improve the performances a lot, too.

Code: [Select]
void GfxEngine::render(const std::vector<std::vector<ScreenTile> >& screenArray, gameState_t gameState)
Laurent Gomila - SFML developer

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #5 on: January 23, 2010, 11:26:09 am »
Good tips. After having a good look at my pipeline it appears that my n00b programmer days are showing through. This was all written a year ago when I first started C++ and I'm copying arrays of data all over the place before it even gets to the renderer. I've already got a 50% speedup by changing value passes to reference and there lot's of room for improvement to go. Thanks.

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #6 on: January 25, 2010, 05:22:49 am »
Just a quickie. Is it possible for sf::string objects to have a solid background color? That way I can do away with sf::Images altogether and save myself a chunk of drawing.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Improving performance on Netbook
« Reply #7 on: January 25, 2010, 08:27:20 am »
No. But like I said you don't need a sf::Image + sf::Sprite, just use a sf::Shape (create it with the sf::Shape::Rectangle function).
Laurent Gomila - SFML developer

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #8 on: January 26, 2010, 12:40:52 am »
Ok thanks. As an option I've got the idea now of creating a single background image of my terrain tiles (sf::string) and drawing that then blitting my creatures and objects over the top. I would then recreate this background image when terrain changes or a creature moves (FOV update). What's the best way to compile a single sf::image (or sf::sprite) from multiple sf::strings?

I suppose in this instance I might be better off rendering my own tiles in a graphics package, arrange them in memory and creating an sf::image from that chunk of memory.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Improving performance on Netbook
« Reply #9 on: January 26, 2010, 08:19:58 am »
In SFML 1.x you can't, but in SFML 2 there's a RenderImage class (which is basically a RenderWindow that draws to a sf::Image rather than a window).
Laurent Gomila - SFML developer

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #10 on: January 26, 2010, 11:32:57 am »
Ok. So my plan is to draw some ASCII images and use them instead of sf::string. If drawing 1560 (60x26) sprites every frame can't be done quickly enough I plan to "compose" a single image from the 1560 tiles when required. Due to FOV this will be when the player moves a tile (think of XCom) so I'll be rendering 1560 sprites AND doing a Copy of those sprite's sf::Image's into a bigger single sf::Image.

Is that feasible? There's some big warnings about copying images on the wiki.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Improving performance on Netbook
« Reply #11 on: January 26, 2010, 11:35:15 am »
You should first test drawing all your sprites, it may be fast enough so that you don't have to find a complicated solution ;)
Laurent Gomila - SFML developer

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #12 on: January 27, 2010, 03:50:00 am »
I've now converted over to drawing each tile as a sprite and nothing else. I'm using a single sprite image that's preloaded and cached. My current loop looks like this;

Code: [Select]
void GfxEngine::createMap(std::vector<std::vector<Terrain> >& mapArray)
{
for (int n=0; n < MAPHEIGHT; n++){
for (int m=0; m < MAPWIDTH; m++){
tileSprite.SetPosition(m*TILEWIDTH,n*TILEHEIGHT);
tileSprite.SetColor(colorLookup[mapArray[n][m].screenTile.color]);
m_mainWindow.Draw(tileSprite);
}
}
}


This draws 1560 sprites (or changes the position and colour that many times) and I'm getting about 1-2fps. Due to they nature of the map and my FOV code even if I optimise it further and draw only changes, that's still going to be over 50% of the tiles, and near the end of a level where every tile has been uncovered it could approach 100% of the tiles being drawn. I've also narrowed it down to SFML rendering by not drawing the map and leaving all other game code turned on. It then flies along. Turn on the map and boom, slowdown.

I find it hard to believe that SFML rendering is this slow. Other OpenGL and SDL games (Crawl, Naev, glblur) run at full speed. Am I doing something that unusual?

OniLinkPlus

  • Hero Member
  • *****
  • Posts: 500
    • View Profile
Improving performance on Netbook
« Reply #13 on: January 27, 2010, 04:50:51 am »
Is createMap only being called once or is it called every frame?
I use the latest build of SFML2

mongrol

  • Newbie
  • *
  • Posts: 29
    • View Profile
Improving performance on Netbook
« Reply #14 on: January 27, 2010, 07:54:51 am »
It's called every frame. As explained earlier due to FOV its not worthing caching a giant single map image as the amount of changes will be large needing a new image needing copied.