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

Author Topic: Is using setTexture every tick better than having lots of copies of textures?  (Read 6499 times)

0 Members and 1 Guest are viewing this topic.

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
I had an idea where I created a class which stored textures in a vector, then all the other objects could have a pointer to the necessary texture in the vector, so that they could draw without needing their own copy. That idea didn't work because I got just a white square, which according to the tutorials means it can't find the texture. There are two ways I could get around this, I could have every object have a copy of its own texture (as stated above), or I could do sf::Sprite::setTexture every time I need to draw, which brings me to the question, which would be faster? The reason I am hesitant to have lots of copies of the same texture is because the game I am developing is a 2D tile based game (similar to terraria or starbound), and I don't think having lots of copies of the same dirt texture would be a good idea
« Last Edit: April 28, 2015, 08:46:50 am by Orfby »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Or use a better suited container that doesn't move around the data.

Or you could use a vector and first add all textures and then call setTexture, that way the references won't get invalidated.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
1. What do you mean 'better suited container'? And how do I stop the data moving in that container

2. That's what it does already, it loads all the textures into the vector then sets the texture afterwards, but I still get a white square, but if the object has a copy it works perfectly

(Sorry for any delays, I am out so I get very little time to respond)

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
The reason I am hesitant to have lots of copies of the same texture is because the game I am developing is a 2D tile based game (similar to terraria or starbound), and I don't think having lots of copies of the same dirt texture would be a good idea
Yes, copying heavy resources like textures unnecessarily is definitely a bad idea.

1. What do you mean 'better suited container'? And how do I stop the data moving in that container
A different STL container, like a list or deque. I suggest reading a bit about the STL to see the trade-offs of each container type.

2. That's what it does already, it loads all the textures into the vector then sets the texture afterwards, but I still get a white square, but if the object has a copy it works perfectly
If you get a white square, the pointer is invalidated somehow. Make sure there is no reallocation or copying/moving involved. If you strictly follow eXpl0it3r's advice, this won't happen.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
2. That's what it does already, it loads all the textures into the vector then sets the texture afterwards, but I still get a white square, but if the object has a copy it works perfectly
If you really add ALL of the textures and only AFTER that call ALL setTexture and then do NOT move the container around and you still get a white square, you must be doing something wrong somewhere. But without code we can't tell.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
I made some simplified source code which also contains the same issue:
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>

class TextureArray
{
    private:
        std::vector<sf::Texture>textures;

    public:
        void addTexture(sf::Texture newTexture) {textures.push_back(newTexture);}
        sf::Texture& getTexture(int elementNumber) {return textures.at(elementNumber);}
};

class ExampleObjectWithSprite
{
    private:
        sf::Sprite sprite;

    public:
        void setTexture(sf::Texture newTexture) {sprite.setTexture(newTexture, true);}
        void draw(sf::RenderWindow& targetWindow) {targetWindow.draw(sprite);}
};


int main()
{
    TextureArray textures;
    sf::Texture txt;
    txt.loadFromFile("Texture.png");
    textures.addTexture(txt);

    ExampleObjectWithSprite object;
    object.setTexture(textures.getTexture(0));

    sf::RenderWindow window(sf::VideoMode(800, 600), "TEST");
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
                break;
            }
        }
       
        window.clear();
        object.draw(window);
        window.display();
    }
}

 
(The program requires a .png file called 'Texture', the picture in it doesn't matter because the texture isn't displayed)

Also, using a deque rather than a vector for TextureArray doesn't produce any different results (as suggested by Nexus)
« Last Edit: April 28, 2015, 06:17:50 pm by Orfby »

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Your getTexture function returns a copy. Return a reference (or pointer) instead.

Edit: at least it did before you edited the post ;-)
« Last Edit: April 28, 2015, 08:48:38 pm by Jesper Juhl »

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Your sprite has a null size. Change your code to use references:

void setTexture(sf::Texture const& newTexture) {sprite.setTexture(newTexture, true);}

and it should work better (the `true` parameter is not required but might become handy later.)

Note that as soon as your vector is resized all references to previous texture are invalided. Open door to bugs! (This was probably pointed out earlier in the discussion but I didn't read it. :-P)
SFML / OS X developer

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
That's fine, the only time any elements are added is at the start of the program (in my actual program). I will also add the true condition, though it doesn't appear to change anything

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Quote
the only time any elements are added is at the start of the program (in my actual program)

If you want my advice, this strategy is doomed: don't restraint yourself with such strong requirement. Also, there's plenty of good resource manager (e.g. Thor has some) that will do what is expected of them more efficiently than yours currently do (because, yes, even if it's only at boot time, you'll make quite a lot of copies for nothing...).
SFML / OS X developer

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
If only loading at boot is such a huge constraint, how can I change the class so it is more like a resource manager, and won't invalidate any pointers after a resize (would this be achieved by using something like a deque, or does it call for much bigger changes)?
« Last Edit: April 28, 2015, 07:31:00 pm by Orfby »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
how can I change the class so it is more like a resource manager, and won't invalidate any pointers after a resize (would this be achieved by using something like a deque, or does it call for much bigger changes)
Yes, that's why eXpl0it3r and I keep recommending another container. But if you remove resources from the middle, std::deque won't help you further. The main advantage it has over std::vector is that it doesn't relocate its elements while growing.

If you're looking for a resource manager, you could use the one from Thor as suggested by Hiura. It's relatively easy to use (see tutorial and API documentation).
namespace res = thor::Resources;

// Load resources
thor::ResourceHolder<sf::Texture, std::string> textures;
textures.acquire("monster", res::fromFile<sf::Texture>("monster.png"));

// Use resources
sf::Sprite sprite;
sprite.setTexture(textures["monster"]);

An advantage is that thor::ResourceHolder is generic: you can use it with textures, fonts, sound buffers, or even your own resource classes. It handles errors with exceptions, so you don't have to check every loading call. And it provides a few more sophisticated features in case you need them ;)
« Last Edit: April 28, 2015, 07:29:20 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
After playing around with the code that I added, I found that the bug is completely solved if you just use a normal sprite, rather than ExampleObjectWithSprite. Obviously this doesn't solve my issue, but it might be helpful in finding the root cause

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Have you tried now with a different container? If so, can you post the smallest possible code that still triggers your problem?
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Orfby

  • Newbie
  • *
  • Posts: 31
    • View Profile
Source code using the map container (also doesn't work):
#include <iostream>
#include <map>
#include <SFML/Graphics.hpp>

class TextureArray
{
    private:
        std::map<std::string, sf::Texture>textures;

    public:
        void addTexture(std::string textureName, sf::Texture newTexture)
        {
            textures.insert(std::pair<std::string, sf::Texture>(textureName, newTexture));
        }
        sf::Texture& getTexture(std::string textureName) {return textures.at(textureName);}
};

class ExampleObjectWithSprite
{
    private:
        sf::Sprite sprite;

    public:
        void setTexture(sf::Texture const& newTexture) {sprite.setTexture(newTexture, true);}
        void draw(sf::RenderWindow& targetWindow) {targetWindow.draw(sprite);}
};

int main()
{
    TextureArray textures;
    sf::Texture temp;
    temp.loadFromFile("Texture.png");
    textures.addTexture("texture", temp);

    ExampleObjectWithSprite object;
    object.setTexture(textures.getTexture("texture"));

    sf::RenderWindow window(sf::VideoMode(800, 600), "TEST");
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
                break;
            }
        }

        window.clear();
        object.draw(window);
        window.display();
    }
}
 
I can create source code from another container (like deque) but I'm doubting it's effectiveness
« Last Edit: April 28, 2015, 09:04:11 pm by Orfby »