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

Author Topic: [SFML 2.0 RC] Tiles render slow?  (Read 12882 times)

0 Members and 1 Guest are viewing this topic.

Moonkis

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
[SFML 2.0 RC] Tiles render slow?
« on: May 15, 2012, 04:52:39 pm »
Hello all!
This time I started prototyping on a tile engine.
I'v noticed that it's very slow ( rendering 55 x 55 tiles ) drags my computer down to around 15 FPS ( In debug I know... ) but it still seems awfully slow even for debug?

I'm not sure If I'm doing something wrong or if it's just my computer acting up, here is the code I'm currently using:

#include <SFML/Graphics.hpp>
#include <iostream>

/* Dimensions of the Map */
const int MAP_WIDTH = 55;
const int MAP_HEIGTH = 55;

const int TILE_WIDTH = 32;
const int TILE_HEIGTH = 32;

int main()
{

  sf::RenderWindow window(sf::VideoMode( 800, 600, 32), "Tile Engine");

  /* To make sure they are disabled
     EDIT: Doing this seems to gain 3 - 5 FPS
  */

  window.setFramerateLimit(0);
  window.setVerticalSyncEnabled(false);

  sf::Event evnt;

  sf::Texture texture;
  texture.loadFromFile("tile.png");

  sf::Sprite sprite(texture);

  while(window.isOpen())
  {
    while(window.pollEvent(evnt))
    {
      if(evnt.type == sf::Event::Closed)
        window.close();
    }

    window.clear(sf::Color::Black);

    /* Render the Tiles */
    for(int y = 0; y < MAP_HEIGTH; y++)
    {
      for(int x = 0;  x < MAP_WIDTH; x++)
      {
        sprite.setPosition(x * TILE_WIDTH, y * TILE_HEIGTH);
        window.draw(sprite);
      }
    }
   
    window.display();
  }


  return EXIT_SUCCESS;
}
« Last Edit: May 15, 2012, 04:56:54 pm by Laurent »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #1 on: May 15, 2012, 04:57:38 pm »
How many FPS do you get if:
- you never change the position of the sprite
- you run it in Release mode
?
Laurent Gomila - SFML developer

Moonkis

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #2 on: May 15, 2012, 05:08:54 pm »
How many FPS do you get if:
- you never change the position of the sprite
- you run it in Release mode
?
Debug with commented sprite.setPosition: Same ( 15 )
Release with commented sprite.setPosition: 445~
Release: 420~

Oh, WOW that debug mode REALLY drags the "game" down!

Moonkis

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #3 on: May 15, 2012, 10:26:52 pm »
I'v been trying to implement "Frustum Culling" into the program though with limited success.
I'v understand the concept of this basic culling as doing a collision check on every object in the "map" and then if the tile collides with the the "camera" rectangle it's inside the screen and should be drawn.

I'v been trying to get something working with SFML's sf::View but I'm not sure how it's supposed to be done.
This is my code so far:
#include <SFML/Graphics.hpp>
#include <iostream>

/* Dimensions of the Map */
const int MAP_WIDTH = 255;
const int MAP_HEIGTH = 255;

const int TILE_WIDTH = 32;
const int TILE_HEIGTH = 32;

int main()
{

  sf::RenderWindow window(sf::VideoMode( 800, 600, 32), "Tile Engine");

  /* To make sure they are disabled
  EDIT: Doing this seems to gain 3 - 5 FPS
  */

  window.setFramerateLimit(0);
  window.setVerticalSyncEnabled(false);

  sf::Event evnt;

  sf::Texture texture;
  texture.loadFromFile("tile.png");

  sf::Sprite sprite(texture);


  while(window.isOpen())
  {
    while(window.pollEvent(evnt))
    {
      if(evnt.type == sf::Event::Closed)
        window.close();
    }

    sf::View veiw = window.getView();


    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
      veiw.move(1.0f, 0);
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
      veiw.move(-1.0f, 0);
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
      veiw.move(0, -1.0f);
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
      veiw.move(0, 1.0f);
    }

    /* Set the new veiew */
    window.setView(veiw);


    window.clear(sf::Color::Black);

    /* Culling code */
    for(int y = 0; y < MAP_HEIGTH; y++)
    {
      for(int x = 0; x < MAP_WIDTH; x++)
      {
        /* Assign the sprite a position */
        sprite.setPosition( x * TILE_WIDTH, y * TILE_HEIGTH);

        /* Check if the tile is inside the Viewport */
        if(veiw.getViewport().intersects(sprite.getLocalBounds())) // This is not right, don't know really how it's supposed to be
        {
          /* If it's true then draw the sprite */
          window.draw(sprite);        
        }

      }
    }

    window.display();

  }
  return EXIT_SUCCESS;
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #4 on: May 15, 2012, 10:34:28 pm »
First mistake: you must use getGlobalBounds(). getLocalBounds() doesn't take the transformations (position, rotation, scale, ...) in account and thus will be the same for all your sprites.

Second mistake: the view's viewport is not what you want. Use its center and size instead.

But if your world is a matrix of tiles, you can do without any collision check: just compute the index of the first and last tile that fits in the view in both X and Y, and iterate only between these indices.
Laurent Gomila - SFML developer

Moonkis

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #5 on: May 15, 2012, 11:08:46 pm »
First mistake: you must use getGlobalBounds(). getLocalBounds() doesn't take the transformations (position, rotation, scale, ...) in account and thus will be the same for all your sprites.

Second mistake: the view's viewport is not what you want. Use its center and size instead.
I did update it to this:
    window.setView(veiw);

    sf::FloatRect rect(veiw.getCenter(), veiw.getSize());

    window.clear(sf::Color::Black);

    /* Culling code */
    for(int y = 0; y < MAP_HEIGTH; y++)
    {
      for(int x = 0; x < MAP_WIDTH; x++)
      {
        /* Assign the sprite a position */
        sprite.setPosition( x * TILE_WIDTH, y * TILE_HEIGTH);

        /* Check if the tile is inside the Viewport */
        if(rect.intersects(sprite.getGlobalBounds()))
        {
          /* If it's true then draw the sprite */
          window.draw(sprite);    
        }

      }
    }
 
But it renders the tiles from the center and down to the right ( It only draws tiles on 1/4 of the actual window ). How do I get it to align correctly and draw the entire screen full of the tiles.


But if your world is a matrix of tiles, you can do without any collision check: just compute the index of the first and last tile that fits in the view in both X and Y, and iterate only between these indices.
Something like:
Code: [Select]
int startX = (view.getCenter().x - (NUMBER_OF_TILES_TO_BUFFER * TILE_WIDTH) )/TILE_WIDTH;
int endX = (view.getCenter().x + (NUMBER_OF_TILES_TO_BUFFER * TILE_WIDTH) )/TILE_WIDTH;

int startY = (view.getCenter().y - (NUMBER_OF_TILES_TO_BUFFER * TILE_HEIGTH) )/TILE_HEIGHT;
int endY = (view.getCenter().y + (NUMBER_OF_TILES_TO_BUFFER * TILE_HEIGTH) )/TILE_HEIGHT;

if(startY < 0)
 startY = 0;
if(startY > MAP_HEIGHT)
 startY = MAP_HEIGHT;

/* Same with the X values so that we don't access values outside our array. */


for(int y = startY; y < endY; y++)
{
  for(int x = startX; x < endX; x++)
  {
     sprite.setPosition(x * TILE_WIDTH, y * TILE_HEIGTH);
     window.draw(sprite);
  }
}

I'm sorry if I'm asking these obvious questions but truth is I'm still struggling with these basic stuff when it comes down to graphics. I'm actually stunned on how easy people understand this ( When googling around most people had NO problems understanding what needed to be done in order to achieve the desired result.). Which makes me feel slightly bad since I'm 21 and I still don't get ( having a hard time visualize inside my head how it works ) basics like this...
« Last Edit: May 15, 2012, 11:12:33 pm by Moonkis »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #6 on: May 16, 2012, 07:35:11 am »
Quote
But it renders the tiles from the center and down to the right ( It only draws tiles on 1/4 of the actual window ). How do I get it to align correctly and draw the entire screen full of the tiles.
Read the documentation of sf::FloatRect ;)
You just copied and pasted the view's center and size in it, without checking what arguments it expects.

Your second code looks good, but the formula is probably wrong (the view's size doesn't appear in it).
Laurent Gomila - SFML developer

Moonkis

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #7 on: May 16, 2012, 07:49:30 am »
Quote
But it renders the tiles from the center and down to the right ( It only draws tiles on 1/4 of the actual window ). How do I get it to align correctly and draw the entire screen full of the tiles.
Read the documentation of sf::FloatRect ;)
You just copied and pasted the view's center and size in it, without checking what arguments it expects.

Your second code looks good, but the formula is probably wrong (the view's size doesn't appear in it).
I'll do it when I get home later today! Thanks for the quick replys Laurent.

The Indexed Culling actually worked like a charm!
Here is the code I did last night ( I get around 750 FPS on a 10000 by 10000 map ):
  /* Culling code */
    int startX = (veiw.getCenter().x - ( 20 * TILE_WIDTH))/TILE_WIDTH;
    int endX = (veiw.getCenter().x + ( 20 * TILE_WIDTH))/TILE_WIDTH;
    int startY = (veiw.getCenter().y - ( 20 * TILE_HEIGTH))/TILE_HEIGTH;
    int endY = (veiw.getCenter().y + ( 20 * TILE_HEIGTH))/TILE_HEIGTH;

    if(startX > MAP_WIDTH)
      startX = MAP_WIDTH;
    if(startX < 0)
      startX = 0;

    if(startY > MAP_HEIGTH)
      startY = MAP_HEIGTH;
    if(startY < 0)
      startY = 0;

    if(endY > MAP_HEIGTH)
      endY = MAP_HEIGTH;
    if(endY < 0)
      endY = 0;

    if(endX > MAP_WIDTH)
      endX = MAP_WIDTH;
    if(endX < 0)
      endX = 0;


    for(int y = startY; y < endY; y++)
    {
      for(int x = startX; x < endX; x++)
      {
        sprite.setPosition(x * TILE_WIDTH, y * TILE_HEIGTH);
        window.draw(sprite);
      }
    }
 

EDIT:
I think I realized what you ment by not using my view's size. It's that the code doesn't dynamically adjust itself to another resolution so if I were to support a larger resolution their is a risk that the tiles I'm drawing wont cover the screen area. Is that what you meant?


EDIT 2:
I think I managed to actually get the culling using the view right this time:
It's drawing the entire screen full of tiles and the maximum tiles it's drawing is about 520 per frame which is sounds about right.

Here is the code I'm currently using
    sf::FloatRect screenRect(sf::Vector2f(veiw.getCenter().x - (veiw.getSize().x)/2, veiw.getCenter().y - (veiw.getSize().y)/2) , veiw.getSize());
    std::cout << "View X = " << veiw.getCenter().x - veiw.getSize().x << "  " << "View Y = " << veiw.getCenter().y - veiw.getSize().y << std::endl;

    window.clear(sf::Color::Black);
    int tilesDrawn = 0;
    for(int y = 0; y < MAP_HEIGTH; y++)
    {
      for(int x = 0; x < MAP_WIDTH; x++)
      {
        sprite.setPosition(x * TILE_WIDTH, y * TILE_HEIGTH);
        sf::FloatRect collider(sprite.getGlobalBounds().left, sprite.getGlobalBounds().top, TILE_WIDTH, TILE_HEIGTH);

        if(screenRect.intersects(collider))
        {
          window.draw(sprite);
          tilesDrawn++;
        }
      }
    }
    std::cout << "Tiles Drawn: " << tilesDrawn << std::endl;
 


I'm eagerly waiting for your reply Laurent :D I really hope I got it right this time.
« Last Edit: May 16, 2012, 08:35:14 am by Moonkis »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #8 on: May 16, 2012, 10:41:42 am »
Quote
I think I realized what you ment by not using my view's size. It's that the code doesn't dynamically adjust itself to another resolution so if I were to support a larger resolution their is a risk that the tiles I'm drawing wont cover the screen area. Is that what you meant?
I just meant that, to find which tiles are inside the view, you obviously need to know its size ;)
In fact yuo have it (20 * TILE_WIDTH), but using view.getSize() instead would be more explicit and maintainable.

Quote
I think I managed to actually get the culling using the view right this time:
Yep, looks good. But it's definitely not as easy and efficient as the other solution.
Laurent Gomila - SFML developer

Moonkis

  • Jr. Member
  • **
  • Posts: 58
    • View Profile
Re: [SFML 2.0 RC] Tiles render slow?
« Reply #9 on: May 16, 2012, 11:29:24 am »
Quote
I think I realized what you ment by not using my view's size. It's that the code doesn't dynamically adjust itself to another resolution so if I were to support a larger resolution their is a risk that the tiles I'm drawing wont cover the screen area. Is that what you meant?
I just meant that, to find which tiles are inside the view, you obviously need to know its size ;)
In fact yuo have it (20 * TILE_WIDTH), but using view.getSize() instead would be more explicit and maintainable.
Yeah, realized that but at the time it was getting really late so I figured I'd just hardcode how many tiles to draw for each side of the middle. But now that I know it works ( and it works WELL ) I'll complete it with View.getSize() calculations to adjust how many tiles will be drawn.


Quote
I think I managed to actually get the culling using the view right this time:
Yep, looks good. But it's definitely not as easy and efficient as the other solution.
Haha sweet!
Yeah no kidding, getting 750 FPS with Indexed Culling ( Which also is map-size independent )  and about 41 FPS with the Collision-based culling of an 255x255 map ( 65025 Collision checks... ). But with a quad-tree it supposed to be a lot faster.


I also must thank you! You gave quick replies and ( even though it was a bit frustrating ) you didn't give me the direct solution right way just said and hinted what I'd have to use so I could think and code it myself, which hopefully earns me some knowledge that makes me ( slowly ) a bit better in the art of graphical programming. Though I'm getting a feeling that it's not the easiest thing to wrap your head around ( Especially the math in the beginning since you haven't really used it in this way before ).