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

Author Topic: How to improve rendering with big vertex size?  (Read 6846 times)

0 Members and 1 Guest are viewing this topic.

Silderan

  • Newbie
  • *
  • Posts: 17
    • View Profile
    • Email
How to improve rendering with big vertex size?
« on: January 29, 2015, 02:51:46 pm »
Hello.

I've tested vertex array as explained in this article and works fine. But, when the array size is so big, FPS drops.

I'm not going to use such size of array, but I'm curious of how to do it correctly.

That's the code:
#include <SFML/Graphics.hpp>
#include <time.h>
#include <cstdio>

const int tilesX = 1000;
const int tilesY = 1000;

class TileMap : public sf::Drawable, public sf::Transformable
{
public:

    bool load(const std::string& tileset, sf::Vector2u tileSize, const int* tiles, unsigned int width, unsigned int height)
    {
        // load the tileset texture
        if (!m_tileset.loadFromFile(tileset))
            return false;

        // resize the vertex array to fit the level size
        m_vertices = new sf::Vertex [width * height * 4];

        // populate the vertex array, with one quad per tile
        for (unsigned int i = 0; i < width; ++i)
            for (unsigned int j = 0; j < height; ++j)
            {
                // get the current tile number
                int tileNumber = tiles[i + j * width];

                // find its position in the tileset texture
                int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);
                int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);

                // get a pointer to the current tile's quad
                sf::Vertex* quad = &m_vertices[(i + j * width) * 4];

                // define its 4 corners
                quad[0].position = sf::Vector2f(i * tileSize.x, j * tileSize.y);
                quad[1].position = sf::Vector2f((i + 1) * tileSize.x, j * tileSize.y);
                quad[2].position = sf::Vector2f((i + 1) * tileSize.x, (j + 1) * tileSize.y);
                quad[3].position = sf::Vector2f(i * tileSize.x, (j + 1) * tileSize.y);

                // define its 4 texture coordinates
                quad[0].texCoords = sf::Vector2f(tu * tileSize.x, tv * tileSize.y);
                quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tv * tileSize.y);
                quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, (tv + 1) * tileSize.y);
                quad[3].texCoords = sf::Vector2f(tu * tileSize.x, (tv + 1) * tileSize.y);
            }

        return true;
    }

private:

    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
    {
        // apply the transform
        states.transform *= getTransform();

        // apply the tileset texture
        states.texture = &m_tileset;

        // draw the vertex array
        target.draw(m_vertices, tilesX * tilesY * 4, sf::Quads, states);
    }

    sf::Vertex *m_vertices;
    sf::Texture m_tileset;
};

int main()
{
    // create the window
    sf::RenderWindow window(sf::VideoMode(800, 600), "Tilemap");

    // define the level with an array of tile indices
    int *level = new int[tilesX*tilesY];

    for( int i = tilesX*tilesY-1; i>=0; i--)
    {
        level[i] = rand()%12;
    }
    // create the tilemap from the level definition
    TileMap map;
    if (!map.load("tileset.png", sf::Vector2u(32, 32), level, tilesX, tilesY))
        return -1;

    sf::Text text;
    sf::Font font;
    font.loadFromFile("consola.ttf");
    text.setFont(font); // font is a sf::Font
    text.setString("FPS: wait...");
    text.setCharacterSize(24); // in pixels, not points!
    text.setColor(sf::Color::Red);
    text.setStyle(sf::Text::Bold | sf::Text::Underlined);

    sf::View vistaPj = window.getView();
    sf::Vector2f centerPj = vistaPj.getCenter();
    int frames = 0;
    int frameTime = time(0);
    // run the main loop
    while (window.isOpen())
    {
        // handle events
        sf::Event event;
        while (window.pollEvent(event))
        {
            if(event.type == sf::Event::Closed)
                window.close();
       }

        // draw the map
        window.clear();
        window.draw(map);
        if( frameTime != time(0))
        {
            static char tmp[256];
            snprintf(tmp, 256, "FPS: %d", frames);
            text.setString(tmp);
            frames = 0;
            frameTime = time(0);
        }
        window.draw(text);
        window.display();
        frames++;
    }

    return 0;
}
 
For it to work, you'll need a Tileset, for example: https://vxresource.files.wordpress.com/2011/05/tileeyl5.png (don't forget to rename it "tileset.png" and place it at correct folder) And "console.tff" font to see FPS count.
As you can see, I've changed VertexArray to a simple Vertex[]. I did it just for testing another draw() function that gives to me more control about the chunk of vertex to draw.

Low FPS are quite normal as draw() must iterate through all vertex, searching for the vertex ones to be drawn.

I think that the solution is to call one draw() per tile line, with the vertex pointer and size up to view possition.
What do you think?

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to improve rendering with big vertex size?
« Reply #1 on: January 29, 2015, 09:28:17 pm »
Firstly, I couldn't run this code as it is; it refused to recognise snprintf. No big deal, however, as:
text.setString("FPS: " + std::to_string(frames));
is simpler anyway ;)

After adjusting that, I was getting 10 FPS.

I questioned the timing you were doing so I rewrote it with an sf::Clock to check:
sf::Clock clock;
// start window loop, do the event loop, and clear and draw (the map)
                if (clock.getElapsedTime().asSeconds() >= 1.f)
                {
                        clock.restart();
                        text.setString("FPS: " + std::to_string(frames));
                        frames = 0;
                }
I was then getting around 30 FPS.
Might just have been bad luck on my part when testing your code, however.

I don't think it's too bad for a million tiles...
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Silderan

  • Newbie
  • *
  • Posts: 17
    • View Profile
    • Email
Re: How to improve rendering with big vertex size?
« Reply #2 on: January 30, 2015, 12:07:56 am »
Yes, you're right... it's simpler :) Maybe you had bad luck. I got 39 FPS in this computer with mine and your code version.

Not sure if 1M tiles is big, huge or insane XD
But, for sure, 30 FPS is not good because there is nothing more than rendering: No IA, no player interaction, no music, no collision, no effects...

Thanks for your attention, Hapax! :)

Silderán.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
AW: How to improve rendering with big vertex size?
« Reply #3 on: January 30, 2015, 03:33:34 pm »
I hope you all ran it in release mode, otherwise any timing is useless.
Additionally you should profile it CPU (and GPU) wise to figure out where the bottlenecks are.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Silderan

  • Newbie
  • *
  • Posts: 17
    • View Profile
    • Email
Re: How to improve rendering with big vertex size?
« Reply #4 on: January 30, 2015, 09:07:11 pm »
It's release mode. But on debug gives me 36 FPS :)

Do you think that 39 FPS is too low with a 1M map?
It's 39 milions of square comparations per second plus rendering tiles + text.
For that reason, I thought that must be something to improve this. But don't know if SFML has something or I must code it by my own.

Another way I thought is to create a vertex array just for screen view and update their values accordingly to camera position.

Silderán.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: How to improve rendering with big vertex size?
« Reply #5 on: January 30, 2015, 09:38:26 pm »
Well the only answer I can give is what I already said.

Measurements in debug mode are useless. Debug mode is meant to be slow.
And again if you want to find out where the problem is, use a profiler, any other guessing or "I think because" are useless. Often the bottlenecks are somewhere totally different than first expected. ;)

As additional point: Don't try to over engineer things. Ask yourself will I need 1mil tiles in the next 3 months of my game development. If the answer is "no" then move on. You're just wasting time trying to "optimize" things you'll never need.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Silderan

  • Newbie
  • *
  • Posts: 17
    • View Profile
    • Email
Re: How to improve rendering with big vertex size?
« Reply #6 on: January 30, 2015, 10:36:29 pm »
As I said, it's just for learning how to do that because someone talks about 1 milion tile map as if it was quite ussual. Furthermore, I tested a graphic game engine, based on SDL, with spacific APIs for tile maps and FPS doesn't change regardless of map size.

Silderán.
« Last Edit: January 31, 2015, 07:56:52 pm by Silderan »

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: How to improve rendering with big vertex size?
« Reply #7 on: January 31, 2015, 12:16:15 am »
https://github.com/SFML/SFML/wiki/Source%3A-ShaderTileMap
https://github.com/SFML/SFML/wiki/Source%3A-TileMap
https://github.com/SFML/SFML/wiki/Source%3A-TileMap-Render
<disclaimer>Two (or more like one and a half - see comments about original idea of shader) of these are mine BTW.</disclaimer>
The shader way is just pure magic BTW, I hope it works still. ;D
« Last Edit: January 31, 2015, 12:19:52 am by FRex »
Back to C++ gamedev with SFML in May 2023

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: How to improve rendering with big vertex size?
« Reply #8 on: January 31, 2015, 01:42:28 pm »
I'm guessing here, but are you culling non-visible tiles before you draw (SFML won't do it for you like SDL does)?
If not, then that's the most obvious optimization that comes to mind.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to improve rendering with big vertex size?
« Reply #9 on: January 31, 2015, 04:02:40 pm »
I agree with Jesper.
If you are thinking that displaying a million tiles is usual, I would question which resolution you are trying to display them in. 800x600 is less than half a million pixels. Therefore, displaying a million tiles is very unusual but having a million tiles and culling them so that you only display - for example - 100-500 (the ones actually in view) would be usual.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Silderan

  • Newbie
  • *
  • Posts: 17
    • View Profile
    • Email
Re: How to improve rendering with big vertex size?
« Reply #10 on: January 31, 2015, 08:29:47 pm »
For sure, it's my fault as is hard to me to write in english. :P

- I don't know how SDL works. I tested a game engine based on SDL.
- I'm not trying to display 1Milion tiles!!! Maybe some day with 100" screen with ultra-hyper-high-definition xDD
- It's not a render problem. It's how to handle big tile maps. After reading some posts arround here I see that I must reduce the vertex array size used for draw() (culling). I start from tutorial and tile map and vertex array has the same size (well, vertex is 4x because of Quad) I didn't thought about culling. Furthermore, as vertex array is one dimension and view is two dimension (at least) there is no direct way for culling... or I don't know!!!

Right now, I think the best way to do that is:
- Allocate a vertex array just for the view needs.
- Every time the view moves, recalculate the vertes array view positions and texture source squares.

Reading your answers I can realize that you take for granted some things that I don't. That means that I must read much more.

Thank you all for your infinite patience xD

Silderán.

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: How to improve rendering with big vertex size?
« Reply #11 on: January 31, 2015, 10:54:23 pm »
You need something like an octree or bsp tree to break your map up into sections. Simply load the sections that the player is in and the ones that the player can see and cull the rest.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to improve rendering with big vertex size?
« Reply #12 on: February 01, 2015, 12:22:14 am »
It seems that we (or maybe just I) may have misunderstood what you were saying.

This:
- Allocate a vertex array just for the view needs.
- Every time the view moves, recalculate the vertes array view positions and texture source squares.
is the answer.
Basically, you'll need enough tiles to fill the screen plus one extra column and one extra row to allow for partially visible tiles on either side.

Try using the tilemap vertex array in the way that all it does it draw tiles that are visible - or partly visible - on the screen.
The map definition itself can be independent and used for collisions but probably won't need much information so could be much larger than the vertex array tilemap. It also doesn't need to all be drawn :)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*