LPCGame icon indicating copy to clipboard operation
LPCGame copied to clipboard

Slow Draw()

Open btstevens89 opened this issue 12 years ago • 16 comments

On a 23x40 grid, we are getting between 50 to 22 fps, best case vs. worst case.

Neither of these numbers are acceptable since all we're drawing right now is the background.

I want to speed this up

I had two idea for how to do this.

  1. Take the mBox of the current map, render all of the tiles that are in view as one Texture, and draw that large texture every iteration. The camera will contain information as to whether or not it has moved. If it has moved, we re-create the large texture.
  2. Save the image of the map to the hard drive and just have the mBox move around this one image as need. This would further speed up the program because we wouldn't have to build up another texture if the camera moved.

Both ideas require me writing something that can combine textures, which I'm not sure how difficult that will be since I haven't researched it. It can't be too hard to figure out.

The 20x43 map lookups are killing us and either way we choose here we will reduce the load they cause significantly.

I guess I'll start with the first idea and then if we want/need we can evolve it into idea two.

Thoughts?

btstevens89 avatar Nov 12 '12 23:11 btstevens89

Actually I could just do idea 1, but create the full map as the large texture and move it around. duh.

btstevens89 avatar Nov 12 '12 23:11 btstevens89

Yep yep, idea 1 is best. After creating the map for the first time its layout will never change as it's just a background so batching its draw into one big texture and just moving it around is best. Just need to find a way for combining SDL_Textures into a big texture, might be able to get some information at the SDL forums or Google of course heh.

Twinklebear avatar Nov 12 '12 23:11 Twinklebear

:cry: SDL's documentation is terrible. I don't see a thing about editing or building new textures. Google returned nothing either. Maybe I can use libpng to build the full image and then use that.

btstevens89 avatar Nov 13 '12 01:11 btstevens89

Ya the documentation is a bit lacking. It may be a bit of a pain to do it with SDL, it looks like you'd have to load the tileset images as SDL_Surfaces then make a new SDL_Surface that's the size of the entire map image and then blit the tiles onto the map, then convert the map SDL_Surface to an SDL_Texture to be used for rendering. So kind of a pain.

So you'd load the tileset images into SDL_Surfaces via

SDL_Surface *s = IMG_Load("tileset.png");

then make a map sized surface via: SDL_CreateRGBSurface using the code from the example (lousy endianness)

and then blit the tiles onto the map surface with SDL_BlitSurface using the clip and destination rects.

Then once the map texture is made, convert it with SDL_CreateTextureFromSurface. Use the function from window.h SurfaceToTexture for this part.

I wonder how long this would take/memory usage of the map image. It would only be done once but then we keep the map in memory the whole time.

Or maybe a streaming texture would be doable? And set it up that way without using SDL_Surfaces? Hmm. Not as familiar with SDL streaming textures.

It looks like you could do a streaming texture way for this too. Create with SDL_CreateTexture with the SDL_TextureAccess option for streaming, then get the pixels of the tiles and lock the map texture SDL_LockTexture for access and do updates with SDL_UpdateTexture then when you're done, unlock it SDL_UnlockTexture. Interesting indeed. Although how would you get the tile's pixels instead of the whole texture?

Perhaps there's a better way to batch the draw calls? It's an area I haven't spent much time looking into so I can't be of much help unfortunately. Let me know what you find though.

Also, if you find OpenGL specific stuff about batching draw calls you may want to bookmark it and/or send me a link, since I plan to migrate to straight OpenGL for rendering and SDL for window management, input and audio eventually. Then we can use fancy shaders and other cool stuff.

Twinklebear avatar Nov 13 '12 01:11 Twinklebear

Bilt looks great, I'm going to try to implement that now.

btstevens89 avatar Nov 13 '12 03:11 btstevens89

So this is where I am at with this. I've verified the surface is being blit sucessfully with SDL_SaveBMP(map, "out.bmp");. A .bmp of the full map is created.

Something must be going wrong during Window::SurfaceToTexture(map); because the texture that is drawn is always empty. Do you see anything unusual?

//Create the empty map's surface
SDL_Surface* map = SDL_CreateRGBSurface(0, lastCamera->SceneBox().W(), lastCamera->SceneBox().H(), 32, rmask, gmask, bmask, amask);
if (map < 0)
    std::cout << SDL_GetError() << std::endl;

//Create a std::map to store prviously opened tile surfaces
std::map<std::string,SDL_Surface*> mymap;

//Blit each tile onto the map's surface
for (int i = 0; i < mTiles.size(); i++){

    //Get the surface for the current tile
    std::string file = mTileSet.get()->File(mTiles[i].Name()).c_str();
    std::map<std::string,SDL_Surface*>::const_iterator found = mymap.find(file);
    SDL_Surface* surf;
    if (found == mymap.end()){
        mymap[file] = IMG_Load(file.c_str());
        surf = mymap[file];
        SDL_UnlockSurface(surf);
    }
    else
        surf = found->second;

    //Display error if surface isn't found
    if (surf < 0)
        std::cout << SDL_GetError() << std::endl;

    //Determine boxes for the Blit
    SDL_Rect DestR;
    Rectf box = mTiles[i].Box();
    DestR.x = box.X();
    DestR.y = box.Y();
    DestR.h = box.H();
    DestR.w = box.W();
    SDL_Rect ClipR;
    Rectf ClipB = mTileSet->Clip(mTiles[i].Name());
    ClipR.x = ClipB.X();
    ClipR.y = ClipB.Y();
    ClipR.h = ClipB.H();
    ClipR.w = ClipB.W();

    //Preform Blit
    int suc = SDL_BlitSurface(surf, &ClipR, map, &DestR);

    if (suc != 0)
        std::cout << SDL_GetError() << std::endl;
}

//Save an image of the full map
SDL_SaveBMP(map, "out.bmp");

//Convert the map surface to a texture (also free's the map after completion)
SDL_Texture* tex = Window::SurfaceToTexture(map);

//Display the map
Rectf pos(xOffset, yOffset, lastCamera->Box().W(), lastCamera->Box().H());
Window::DrawTexture(tex, pos);

//Free the surfaces
for(std::map<std::string,SDL_Surface*>::iterator it = mymap.begin(); it != mymap.end(); it++)
    SDL_FreeSurface(it->second);

btstevens89 avatar Nov 18 '12 22:11 btstevens89

Hmm, I'm not sure. If the out.bmp looks as it should then I'm not sure quite what's going on in SurfaceToTexture. It's being called after Window::Init and there's no entry in the debug log "Failed to convert surface"? Those are the two issues that I think would be possible. Not sure here. Maybe test the SurfaceToTexture on some regular surfaces loaded from existing images to make sure it's ok?

Maybe if this doesn't work out it's time to start looking more seriously into dropping into straight OpenGL for rendering? I'm not sure how familiar you are with OpenGL, I'm not very experienced but I've got a little project setup where I've been playing with OpenGL and SDL2 and have some links to lessons you may find useful.

I planned to do it anyways in the future, but perhaps now is that time in the future? haha

Twinklebear avatar Nov 18 '12 23:11 Twinklebear

SDL_Surface* map = SDL_CreateRGBSurface(0, lastCamera->SceneBox().W(), lastCamera->SceneBox().H(), 32, 0,0,0,0); Makes the function work. I'm going to finish implementing the idea, clean up the code and then push it.

btstevens89 avatar Nov 19 '12 00:11 btstevens89

Interesting! Let me know how much of a boost this gives and changes in memory usage. Pretty neat stuff.

Twinklebear avatar Nov 19 '12 00:11 Twinklebear

The map that was previously getting 22fps is now getting 59.94fps. That's a full 23x40 map which takes up the entire screen. Pretty awesome speed up.

btstevens89 avatar Nov 19 '12 05:11 btstevens89

Ah that's really nice! The map texture probably isn't that big in memory either right? How big where the bmp files?

Oh also, how have you been measuring the framerate? Just throwing a calculation into the game loop, or did you manage to get Fraps to pick it up?

Twinklebear avatar Nov 19 '12 05:11 Twinklebear

I think the BMP images are about 3KB per tile. Not great but I believe any SDL_Texture/SDL_Surface will be of equivalent size since there is no compression.

A 43x20 map will be 2.7MB, while a 100x100 map will be close to 30MB.

Maybe the program should try to maximize efficiency somehow and only render 5MB portions of the map? It can always re-create the map when the camera exceeds the rendered boundaries. We can even have it on it's own thread so it doesn't slow down the game.

I've been measuring framerate with a function thrown into the game loop. Nothing fancy.

btstevens89 avatar Nov 19 '12 12:11 btstevens89

I guess that isn't too bad for now, if you can trim it down to do only the camera area + a bit extra, the bit extra would give you time to prepare the next map area when the player starts to leave. I guess how much extra room you'd need to render to give yourself time would depend on how long it takes to prepare the next section. But it may be kind of tricky to do this.

It may be best to just leave it as is until we move to OpenGL where I'm sure we can do something better. Since it works there's not too much need to get it completely perfect since it'll be replaced down the line anyways, and 30MB isn't so much.

Twinklebear avatar Nov 19 '12 18:11 Twinklebear

Is the 60fps hardcoded somewhere?

btstevens89 avatar Nov 21 '12 17:11 btstevens89

Sort of, Vsync is currently enabled, so whatever your monitor's refresh rate is the cap. See window.cpp line 44

mRenderer.reset(SDL_CreateRenderer(mWindow.get(), -1, SDL_RENDERER_ACCELERATED | 
SDL_RENDERER_PRESENTVSYNC));

Take out the VSYNC bit to disable it. I was using it to cap the framerate from going to high and using up too much cpu. If we're able to go higher, we could bump the cap up to something like 120-160 or something.

Twinklebear avatar Nov 21 '12 17:11 Twinklebear

I just want to look at it for debugging and ensuring we always stay above 60fps.

btstevens89 avatar Nov 21 '12 17:11 btstevens89