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

Author Topic: Every time I optimise my map rendering code, it gets slower.  (Read 1364 times)

0 Members and 1 Guest are viewing this topic.

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Every time I optimise my map rendering code, it gets slower.
« on: August 01, 2023, 03:02:13 am »
I'm making a game similar to terraria in SFML, but I'm completely fine with absolutely every graphic in the game being flat colored rectangles. my map is on a grid, and will be roughly 2000x900 tiles.

my first attempt at rendering this map was just making a lot of rectangleShapes, and window.draw()ing each one. that one did not perform well. my second attempt was to make the map a texture, and use img.setpixel() to change the texture. load that onto a sprite, and draw that. Contrary to expectation, that performed worse. So I optimized it by manipulating a pixel array directly, and loading the image from that. it performed worse again (with the added benefit of the map looking glitchy, though that can be fixed.)

I'm making this in x86, and I've been running in debug mode.
here's the relevant code:
//map drawing
            {
                sf::Vector2i middleOfScreen(window.getSize().x / 2, window.getSize().y / 2);
                sf::Uint8* pixels = new sf::Uint8[mapWidth * mapHeight * 4];
                // Convert pixel coordinates to world coordinates
                sf::Vector2f worldCoords = window.mapPixelToCoords(middleOfScreen);

                float screenwidth = sf::VideoMode::getDesktopMode().width;
                float screenheight = sf::VideoMode::getDesktopMode().width;
                screenwidth = sqrt((screenwidth / 2) * (screenwidth / 2) + (screenheight / 2) * (screenheight / 2));
                for (auto it = map.begin(); it < map.end(); ++it) {
                    int item = *it;
                    int position = std::distance(map.begin(), it);



                    sf::RectangleShape rect(sf::Vector2f(blockZoom, blockZoom));
                    rect.setPosition(position % mapWidth * blockZoom, position / mapWidth * blockZoom);
                    rect.setFillColor(blockColors[item]);
                    //std::cout << item;
                    int x(position % mapWidth), y(floor(position/mapWidth));


                    float distance = sqrt((rect.getPosition().x - worldCoords.x) * (rect.getPosition().x - worldCoords.x) + (rect.getPosition().y - worldCoords.y) * (rect.getPosition().y - worldCoords.y));
                    bool visible = (distance < screenwidth);
                    if (visible && item != 0)
                    {
                        sf::Color pixCol(blockColors[item]);
                       
                        pixels[4 * (y * mapHeight + x)] = pixCol.r; // R?
                        pixels[4 * (y * mapHeight + x) + 1] = pixCol.g; // G?
                        pixels[4 * (y * mapHeight + x) + 2] = pixCol.b; // B?
                        pixels[4 * (y * mapHeight + x) + 3] = pixCol.a; // A?
                    }

                    mapimg.create(mapWidth, mapHeight, pixels);
                    maptex.update(mapimg);
                    sf::Sprite sprite;
                    sprite.setScale(blockZoom, blockZoom);
                    sprite.setPosition(0, 0);
                    sprite.setTexture(maptex);
                    window.draw(sprite);
                }
            }
forgive me if the code looks terrible, this is my first SFML project ever.

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #1 on: August 01, 2023, 05:35:40 am »
I think the thing you've skipped over might be the solution for you:
using a vertex array (or buffer) to display multiple rectangles with a single draw call.

That's what Selba Ward's Pixel Display so maybe seeing that may help you. You may also find some things interesting in similar Selba Ward drawables such as Tile Map and Console Screen.

It's worth noting that with the number of tiles you are using, it's not that different from one per pixel in full HD (1920x1080), presuming you're drawing all to the screen at once! I wouldn't call this "flat colored rectangles", I'd call them pixels! ;D
In this case, you may actually want to use a vertex array with point primitive; that changes a single pixel.
Of course, if you're in 4k or something, this won't do.

Indeed, if you have a large map but only drawing a few at once, this is a very different story. You can draw only the visible tiles so the number is significantly easier. You can change the visible ones to match where in the map you are looking! See Selba Ward's Tile Map for one example of how to do this (you could just use that tile map! ;) ) Tile Map uses textures for each tile (but draws them all as a vertex array) so you can create any tilemap you like!

                float screenheight = sf::VideoMode::getDesktopMode().width;
I don't think this is your intention.

                screenwidth = sqrt((screenwidth / 2) * (screenwidth / 2) + (screenheight / 2) * (screenheight / 2));
I'm also interested in knowing what the intended effect of this is (it's changing the width to be the same as the length of the diagonal of a quarter of the screen).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #2 on: August 01, 2023, 03:26:05 pm »
thanks for the reply, I'm going to check that out if an idea I have doesn't work, (changing the way the for loop works to hopefully optimize it)
you're right, I didn't mean to set screenHeight to screenWidth, but that screenWidth = (long code) was to reduce the amount of variables declared, I use that screenWidth variable later to check distance with a cube to the center of the screen for rendering. an impermanent solution that would render a circle of cubes around the screen instead of exactly only the cubes visible. the diagonal of a quarter of the screen is the exact smallest length that renders a circle exactly large enough to cover the screen.

EDIT:
for loop optimization did not work, but I did fix the glitchy map (somewhat). I'm going to try out this pixel display thing. I just download this and place it in my project folder, or does this have an installation page?
EDIT AGAIN:
figured out installation
« Last Edit: August 01, 2023, 04:07:08 pm by CHKN »

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #3 on: August 01, 2023, 05:00:33 pm »
well now I'm getting an error with my includes. I'm including Common.hpp, and PixelDisplay.hpp, both autocompleteted in the VS editor, both present in the same folder. Common.hpp works fine, but I'm getting a "file does not exist" error with PixelDisplay.hpp . both have the correct caps and spelling, given they were autocompleted.
EDIT:
the error wasn't with my includes in main.cpp, but in PixelDisplay.cpp. changed that, it's fixed now
« Last Edit: August 01, 2023, 05:09:47 pm by CHKN »

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #4 on: August 01, 2023, 05:07:08 pm »
Depending on how you are displaying your map, Pixel Display may or may not be your solution. It will only draw multiple rectangles of which you can change the colour.
If you need a map of tiles that needs to move more smoothly than one tile at a time, consider trying Tile Map instead; it does all that automatically.
Just give it your array of tile indices and it'll draw the texture for each one. If you definitely 'want' solid colours, you could set up a simple texture with those colours in it and map it to a tile (as most of the tiles in the simple example are). However, if you later decide to use actual textures for the tiles, it'll already be working and you just change the texture mapping.
You could, of course, still use Pixel Display (or, indeed, Console Screen's background coloured tiles as I did originally for a platformer I tinkered with) but you will need to manually adjust offset tiles and the like whereas Tile Map does all this automatically. Try it (Tile Map) and see!

As for inclusion/linking, have you also included PixelDisplay.cpp and is that in the same folder too?
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #5 on: August 01, 2023, 05:11:50 pm »
PixelDisplay.cpp was included, and it was actually the error. I've fixed it now.
I'll definitely look into texturing and tile maps later, but for my purposes I think this might work for now.
I'm iffy on texturing because I don't know anyone good at texturing, including myself.

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #6 on: August 01, 2023, 05:20:01 pm »
the title, again.
using the pixel display is even slower than all of the previous methods. I'm going to try the tilemap, and if that doesn't work, I guess I'll try a vertexArray, and if that doesn't work, I'm going to go back to individual rectangleShapes.

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #7 on: August 01, 2023, 05:23:13 pm »
I'm curious. What was the error with PixelDisplay.cpp? Is it something I need to fix?

You can use solid-colour textures with Tile Map so they are just plain rectangles. Have a look at that simple example I linked above and you can see what I mean.
I think you'll likely prefer to use Tile Map if your map will scroll. Pixel Display doesn't do that; it simply lets you colour rectangles as it's designed to be like the pixels of a display (for recreating some basic retro displays, for example). Pixel Display can also be quite slow for large grids as it's not optimised for that.

In fact, the good thing about Tile Map is that it optimises what it needs to show and only draws that!

If you try Tile Map and don't like it, let me know how I can improve it!



Also, this is what I mean by using a texture to display solid rectangles.

You can use smaller rectangles in the texture; they are this size for consistency as I wanted a tree!
« Last Edit: August 01, 2023, 05:26:14 pm by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #8 on: August 01, 2023, 05:26:42 pm »
the error was just that I put the header files in a seperate folder, and PixelDisplay.cpp didn't know which folder they were in, so the #include "PixelDisplay.hpp" line didn't point to the right file.
tilemap sounds like exactly what I need, a large, optimized scrolling map. I'm going to take a break for an hour or two, but when I come back and implement this, I'll let you know how it works.

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #9 on: August 01, 2023, 07:13:21 pm »
okay, I have a question: the tilemap comes with an hpp file like normal, but also an inl file, instead of a cpp file. is the inl file like a cpp file? if it's different, how do I use it?
sorry if this is a really beginner question, this is my first project in c++ as well as SFML.

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #10 on: August 01, 2023, 07:59:39 pm »
The .inl is similar to a .cpp file in that it contains the actual code definitions but it differs in that it will be included in the header at compile instead of being compiled separately.
Basically, it's as if the header had all of that code in it. Just keep them next to each other (the header includes the "inl" file) and it should be fine.

The "inl" here is short for "inline" as it's just added "inline" to the header.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #11 on: August 01, 2023, 08:07:13 pm »
ah I see, so just keep the hpp and inl files besides eachother, got it. thanks!

EDIT:
further question, should my map be a seperate array/list/vector of ints, that I then pass to the tilemap for display, or should I store my map directly in the tilemap?
« Last Edit: August 01, 2023, 08:10:27 pm by CHKN »

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #12 on: August 01, 2023, 08:32:22 pm »
Separate.

You can keep an vector of tile values yourself so you can manipulate them as you see fit.
You link it to Tile Map so that when it's ready to draw, it reads the current values (that it needs).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

CHKN

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #13 on: August 01, 2023, 08:37:27 pm »
about linking actually, using setLevel(), the parameters are: (std::vector<T>& level, int, int)
I know what an std::vector<T> is, but what is the & level at the end? just passing in my map vector, it's size, and it's width gives me an error due to the mismatched arguments.
thanks for your patience and help so far, btw!

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Every time I optimise my map rendering code, it gets slower.
« Reply #14 on: August 01, 2023, 09:29:00 pm »
"level" is the name of the vector when it is used inside Tile Map (so you know that it is the level data!).
The "&" means it is a reference to your vector so it doesn't have to copy the entire thing every time and can just use the original one.

Your level data (the vector) should be the same as the type you made Tile Map with.
e.g. in the simple example, it uses:
std::vector<unsigned char> levelData(10000);
and
sw::TileMap<unsigned char> tileMap;

If you're using a vector, you don't pass its size as well, just its width. Vectors contain their size information. The size parameter is for arrays where it would be impossible to know its size from the array itself.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*