2
\$\begingroup\$

I'm having an issue with my tilemap renderer, with small maps (like 40*40 tiles) it works fine but with maps that have 400*400 tiles the framerate becomes really slow when I try to move the camera.

Here are my rendering methods

/// Renders the map
void Map::Map::render(sf::RenderWindow& window)
{
    for (auto c : m_tiles)
    {
        for (Tile& tile : c)
        {
            m_renderer.render(tile);
        }
    }
}

And for the TileRenderer

void Renderer::TileRenderer::render(const Map::Tile& tile) const
{
    try
    {
        if (m_previousType != tile.getType())
            m_sprite.setTextureRect(getTypeTextureRect(tile.getType()));

        m_sprite.setPosition(tile.getAbsolutePosition());

        m_renderWindow.draw(m_sprite);
        m_previousType = tile.getType();
    }
    catch (ETileTypeUndefined e)
    {
        Logger::getInstance().log(e.what());
    }
}

Any idea to solve that poor framerate ? It doesn't seems to be a memory issue. Visual Studio debugger tells me that external code spend a lot of time in the renderer's render function (calls to sf::RenderWindow::draw(sf::Drawable) I guess).

\$\endgroup\$
11
  • \$\begingroup\$ How large are your tiles? And are all 400x400 on screen at the same time? \$\endgroup\$
    – William
    May 16, 2015 at 11:14
  • \$\begingroup\$ My tiles are 32*32px and no they aren't but they could since I'm making a map editor which allows to unzoom the map. \$\endgroup\$
    – Lisible
    May 16, 2015 at 11:17
  • \$\begingroup\$ Then you have to get creative. Even only 400x400 that's 160000 draw calls per frame. Do you have access to fragment-/pixel-shaders? \$\endgroup\$
    – William
    May 16, 2015 at 11:29
  • \$\begingroup\$ Yes, I have access to them. \$\endgroup\$
    – Lisible
    May 16, 2015 at 11:32
  • 1
    \$\begingroup\$ Then you might be interested in this: blog.tojicode.com/2012/07/sprite-tile-maps-on-gpu.html \$\endgroup\$
    – William
    May 16, 2015 at 11:39

2 Answers 2

2
\$\begingroup\$

I think what you're looking for is to draw all of your tiles each frame to a sf::RenderTexture and then render that single object to the screen.

The render texture is stored in an offscreen texture which is great for precomputing a complex static texture (like a level's background from multiple tiles) etc.

Example:

// Create a new render-window
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
// Create a new render-texture
sf::RenderTexture texture;
if (!texture.create(500, 500))
    return -1;
// The main loop
while (window.isOpen())
{
   // Event processing
   // ...
   // Clear the whole texture with red color
   texture.clear(sf::Color::Red);
   // Draw stuff to the texture
   texture.draw(sprite);  // sprite is a sf::Sprite
   texture.draw(shape);   // shape is a sf::Shape
   texture.draw(text);    // text is a sf::Text
   // We're done drawing to the texture
   texture.display();
   // Now we start rendering to the window, clear it first
   window.clear();
   // Draw the texture
   sf::Sprite sprite(texture.getTexture());
   window.draw(sprite);
   // End the current frame and display its contents on screen
   window.display();
}
\$\endgroup\$
1
  • \$\begingroup\$ Seems interesting too, I'll try that too when I'll have time. Thank you \$\endgroup\$
    – Lisible
    May 18, 2015 at 20:25
1
\$\begingroup\$

You are going to have to embrace level of details. The rendering itself may already be using mipmaps, so the reduction filter will not consume lots of bandwidth to draw sprites that end up being 2x2 on the screen even if its texture is originally 200x200. Your problem comes from too many draw calls !
so you are going to have to make a decision about what could be completely cut out of the render after a certain level of zoom out.
Maybe you don't need terrain details like stones or bushes who knows.
You could also identify all the static parts of your world, and fuse this all together by using DanoThorn's technique of baking them into an impostor. Most complex worlds require layers, so you can bake in layers as necessary. You will need a cache manager to keep baked impostors with a garbage collection technique based on frequency of use. When the camera spans impostors will move out of screen. You can attach a "counter of not-rendered frames" and the highest get de-allocated first when you decide it's time to free memory.
This is all very costly, so you have to decide. Do you need to zoom out so much that you will pay to implement all of this ? or will you cut the feature altogether and be content by limiting the zoom out factor so that your on-screen number of tiles never exceed 5k/10k top ?
Your game, your choice.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .