SFML community forums

Help => Graphics => Topic started by: Riser on September 18, 2020, 10:39:01 pm

Title: glFlush() error. [SOLVED]
Post by: Riser on September 18, 2020, 10:39:01 pm
This error comes when I'm working with textures sometimes and I don't know exactly what causes it, even weirder is that when i pops up there are no apparent issues, and everything seems to work just fine, but I want to know what causes it, and how I can avoid it:
(https://i.stack.imgur.com/hpWxJ.png)
Since I don't know exactly what causes the error I didn't post any code, but I'll do so if needed.

Title: Re: glFlush() error.
Post by: eXpl0it3r on September 19, 2020, 05:40:49 pm
Are you doing any custom OpenGL calls or use multiple threads?
Title: Re: glFlush() error.
Post by: Riser on September 19, 2020, 09:34:02 pm
Nope, I am merely trying to display a sprite.
I am using an Asset Manager like you recommended to me to when I first came here.
A texture that calls the Asset Manager is a member inside a Sprite class that I use to make working with sprites easier:

Sprite.h:

class Sprite {
public:
        AssetManager manager;
        sf::Texture m_Texture;
        sf::Sprite m_Sprite;

        sf::Vector2f sprite_scale;
        sf::Vector2u original_size;
        sf::Vector2f texture_size;

        Sprite(std::string path,sf::IntRect rect,sf::Vector2f size);
       
};
 

Sprite.cpp:

Sprite::Sprite(std::string path, sf::IntRect rect, sf::Vector2f size) {
        m_Texture = sf::Texture(AssetManager::LoadTexture(path));
        m_Sprite.setTextureRect(rect);
        m_Sprite.setTexture(m_Texture);

        original_size = m_Texture.getSize();

        texture_size.x = static_cast<float>(original_size.x);
        texture_size.y = static_cast<float>(original_size.y);

        sprite_scale.x = size.x / texture_size.x;
        sprite_scale.y = size.y / texture_size.y;

        m_Sprite.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
        m_Sprite.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));

}
 


An object of said Sprite class is itself a member inside an Entity class where I use assignment on said object to pass it arguments:

Entity.h:

class Entity {
public:
        Sprite entity_sprite = Sprite("res/wildcard.png", { 0,0,36,63 }, { 36,63 });
        int health;
        float speed;
        bool collision = false;

        bool entity_collision(sf::Sprite entity2_sprite);
       

};
 

This isn't exactly what I'm trying to do, I'm trying to create a Player class that inherits from this class and pass the arguments to that, but I'm having a completely separate problem with that and for now I want to see what this bug is all about.
Like I said, this code works, and the previous project this error popped up it in also worked, but I'm a bit worried about it.
Title: Re: glFlush() error.
Post by: eXpl0it3r on September 20, 2020, 08:36:22 am
Are you using a build from the master branch?
Title: Re: glFlush() error.
Post by: fallahn on September 20, 2020, 12:19:16 pm
The error is quite possibly caused by the fact that this line

m_Texture = sf::Texture(AssetManager::LoadTexture(path));


is copying the texture at least once. You should be storing references to your textures in the sprite, not copies.

To start with ensure that your asset manager returns a reference to the held textures, else you'll be getting a copy here (you haven't shared the code for your asset manager). It should look something like
sf::Texture& AssetManager::loadTexture(const std::string& path)

Then use a reference (or a pointer) to sf::Texture in your sprite class and use an initialiser list (https://en.cppreference.com/w/cpp/language/constructor) to  set it up correctly on construction.

As a side note: placing your asset manager as a sprite member is somewhat redundant, you'll be creating a new asset manager with every sprite. The idea of asset managers is that a single instance exists which can share its held resources with multiple sprites (or other types eg sounds etc). I'd suggest refactoring your sprite class to something like:

class Sprite {
public:
        sf::Texture& m_Texture;
        sf::Sprite m_Sprite;

        sf::Vector2f sprite_scale;
        sf::Vector2u original_size;
        sf::Vector2f texture_size;

        Sprite(sf::Texture&,sf::IntRect rect,sf::Vector2f size);    
};

Sprite::Sprite(sf::Texture& texture, sf::IntRect rect, sf::Vector2f size)
    : m_texture(texture),
    m_sprite(texture)
 {
        m_Sprite.setTextureRect(rect);

        original_size = m_Texture.getSize();

        texture_size.x = static_cast<float>(original_size.x);
        texture_size.y = static_cast<float>(original_size.y);

        sprite_scale.x = size.x / texture_size.x;
        sprite_scale.y = size.y / texture_size.y;

        m_Sprite.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
        m_Sprite.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));

}

 

Note that the texture is passed purely by reference, removing any copy operations (which should hopefully fix your OpenGL error). The sprite and texture members are both initialised with an initialiser list (which you should be using in all of your constructors, btw).To use this sprite class make sure to add a single instance of your asset manager to your game class (or wherever your sprites are created) then pass texture references from it like so:

Sprite spr1(m_assetManager.loadTexture(path), rect, size);
Sprite spr2(m_assetManager.loadTexture(otherPath), otherRect, otherSize);
 

This way your asset manager *owns* a single instance of each texture, references to which are passed out and shared with as many sprites which need them.






Although when all is said an done you can remove the sf::Texture member entirely: the sf::Sprite member will hold a reference for you and all your texture queries can be done through sf::Sprite::getTexture()
Title: Re: glFlush() error.
Post by: eXpl0it3r on September 20, 2020, 01:17:50 pm
This is really just a bug that was already fixed on master: https://github.com/SFML/SFML/pull/1609
Title: Re: glFlush() error.
Post by: Riser on September 20, 2020, 11:42:08 pm
fallahn, I did as you said, but now it stopped working entirely, the thing is, the reason I wrote this line like this in the first place:
m_Texture = sf::Texture(AssetManager::LoadTexture(path));
Is because I couldn't pass arguments to objects normally for some reason, and had to use assignment.
So in the end I had to write it like this:
Sprite spr1=Sprite(m_assetManager::loadTexture(path), rect, size);
(Also I'm using a scope resolution operator because the Asset Manager is set up in a way that allows me to access the class directly without having to declare objects, there's supposed to be a single instance of it after all.)
Same thing for the Sprite object declared in the entity class, I have to use assignment there too, otherwise I get multiple errors such as "expected type specifier" and "call of an object of a class type without appropriate operator() or conversion function to pointer-to-function type", any idea why that's happening?

eXpl0it3r, I'll figure out how to update to the master build after I solve this issue, I've been using my own template that I setup following a Youtube tutorial (By Sonar Systems).
Title: Re: glFlush() error.
Post by: eXpl0it3r on September 21, 2020, 07:46:33 am
I hope, by "solve my issue" you don't mean the glFlush errors, because thise can only be fixed by updating SFML. :D
Title: Re: glFlush() error.
Post by: fallahn on September 21, 2020, 11:45:07 am
I can't say for sure without knowing how your asset manager works. It seems really odd to me that you have to construct a texture from the return value like this

sf::Texture(AssetManager::LoadTexture(path));

when really you want to be able to do something like

sf::Texture& myTex = AssetManager::loadTexture(path);

The SFML Game Development (https://www.packtpub.com/product/sfml-game-development/9781849696845) book has a good chapter on resource management, the source of which (https://github.com/SFML/SFML-Game-Development-Book) is also available.
Title: Re: glFlush() error.
Post by: Riser on September 21, 2020, 12:12:08 pm
Alright here it is, it's the asset manager from the "SFML Essentials" book, but I added a method for fonts after seeing one in another asset manager tutorial on Youtube (Also by Sonar Systems):

AssetManager.h:

#pragma once
#include "Window/Window.h"
#include <map>


class AssetManager {
public:
        AssetManager();
       
        static sf::Texture& LoadTexture(std::string const& path);
        static sf::SoundBuffer& LoadSoundBuffer(std::string const& path);
        static sf::Font& LoadFont(std::string const& path);

private:
        std::map<std::string, sf::Texture> m_Textures;
        std::map<std::string, sf::SoundBuffer> m_SoundBuffers;
        std::map<std::string, sf::Font> m_Fonts;

        static AssetManager* sInstance;
};
 

AssetManager.cpp:

#include "AssetManager.h"
#include <assert.h>

AssetManager* AssetManager::sInstance = nullptr;

AssetManager::AssetManager() {
        assert(sInstance == nullptr);
        sInstance = this;
}

sf::Texture& AssetManager::LoadTexture(std::string const& path) {

        auto& texMap = sInstance->m_Textures;
        auto pairFound = texMap.find(path);

        if (pairFound != texMap.end()) {
                return pairFound->second;
        }
        else {
                auto& texture = texMap[path];
                texture.loadFromFile(path);
                return texture;
        }

}

sf::SoundBuffer& AssetManager::LoadSoundBuffer(std::string const& path) {

        auto& sBufferMap = sInstance->m_SoundBuffers;
        auto pairFound = sBufferMap.find(path);

        if (pairFound != sBufferMap.end()) {
                return pairFound->second;
        }
        else {
                auto& sBuffer = sBufferMap[path];
                sBuffer.loadFromFile(path);
                return sBuffer;
        }

}

sf::Font& AssetManager::LoadFont(std::string const& path) {

        auto& FontMap = sInstance->m_Fonts;
        auto pairFound = FontMap.find(path);

        if (pairFound != FontMap.end()) {
                return pairFound->second;
        }
        else {
                auto& font = FontMap[path];
                font.loadFromFile(path);
                return font;
        }

}
 

Right now I wanna learn how to do this correctly, as soon as I get it to work I'll update SFML, whether the glFlush() error was still there or not.
Title: Re: glFlush() error.
Post by: fallahn on September 21, 2020, 02:00:19 pm
OK, seems reasonable to me (it works but my personal preference would be to stay away from singletons, although that's a whole different topic ;) ). I created this CME with your modified Sprite class, which works for me. Can you test it?


#include "AssetManager.h"

#include <SFML/Graphics.hpp>

class Sprite
{
public:
    Sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size)
        : m_texture(tx), m_sprite(tx)
    {
        m_sprite.setTextureRect(rect);

        original_size = m_texture.getSize();

        texture_size.x = static_cast<float>(original_size.x);
        texture_size.y = static_cast<float>(original_size.y);

        sprite_scale.x = size.x / texture_size.x;
        sprite_scale.y = size.y / texture_size.y;

        m_sprite.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
        m_sprite.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));
    }

    sf::Texture& m_texture;
    sf::Sprite m_sprite;

    sf::Vector2f sprite_scale;
    sf::Vector2u original_size;
    sf::Vector2f texture_size;
};

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "My window");

    AssetManager ass;

    auto& tx = AssetManager::LoadTexture("assets/images/flower.png");
    Sprite spr1(tx, sf::IntRect(0,0,64,192), sf::Vector2f(64.f, 192.f));
    spr1.m_sprite.setPosition(64.f, 128.f);

    Sprite spr2(AssetManager::LoadTexture("assets/images/flower.png"), sf::IntRect(0,0,64,192), sf::Vector2f(32.f, 96.f));
    spr2.m_sprite.setPosition(192.f, 128.f);

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

        window.clear(sf::Color::Black);
        window.draw(spr1.m_sprite);
        window.draw(spr2.m_sprite);
        window.display();
    }

    return 0;
}

 
Title: Re: glFlush() error.
Post by: Riser on September 22, 2020, 12:28:33 am
Ok, the code as is DOES work with zero issues, but when I try to use the asset manager from the Entity class, several issues start arising:
-I can't include a Sprite object because I get an error saying "no default constructor exists for class "Sprite"".
-I can't call the Sprite constructor inside the Entity constructor like so:
        Entity() {
                entity_texture = AssetManager::LoadTexture("res/wildcard.png");
                entity_sprite = Sprite(entity_texture, sf::IntRect(0, 0, 36, 63), { 36,63 }).m_sprite;
        }
 
Because the app immediately crashes.
-And I didn't bother trying global variables (Declaring the sf::Texture and the Sprite globally outside the Entity class) even though I have a suspicion it might work, because I've learned global variables are not a good idea.

Now what?
Title: Re: glFlush() error.
Post by: fallahn on September 22, 2020, 10:24:33 am
The Sprite class is not default constructable because the Texture reference must always be initialised to a value. The most flexible fix is to add a default constructor to the Sprite class.

Either turn the texture reference into a pointer and initialise it to nullptr, and update the existing constructor accordingly:
Sprite()
  : m_texture(nullptr)
{

}

Sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size)
    : m_texture(&tx), m_sprite(tx)
{
    m_sprite.setTextureRect(rect);

    original_size = m_texture->getSize();

    texture_size.x = static_cast<float>(original_size.x);
    texture_size.y = static_cast<float>(original_size.y);

    sprite_scale.x = size.x / texture_size.x;
    sprite_scale.y = size.y / texture_size.y;

    m_sprite.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
    m_sprite.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));
}
 

Or remove the texture member completely. As I mentioned in a previous post, sf::Sprite can be used to handle the texture reference.

Sprite()
{

}

Sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size)
    :  m_sprite(tx)
{
    m_sprite.setTextureRect(rect);

    original_size = m_sprite.getTexture()->getSize();

    texture_size.x = static_cast<float>(original_size.x);
    texture_size.y = static_cast<float>(original_size.y);

    sprite_scale.x = size.x / texture_size.x;
    sprite_scale.y = size.y / texture_size.y;

    m_sprite.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
    m_sprite.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));
}
 

HOWEVER from your entity constructor it looks like you're using the Sprite class to initialise and resize an sf::Sprite, which is then stored as a member of the Entity class. In which case why not use a simple function to set up your sprites?

sf::Sprite initSprite(sf::Texture& t, sf::IntRect rect, sf::Vector2f size)
{
    sf::Sprite sprite(t);
    sprite.setTextureRect(rect);

    sf::Vector2f texture_size(t.getSize());

    sprite.setScale(size.x / texture_size.x, size.y / texture_size.y );
    sprite.setOrigin(texture_size / 2.f);

    return sprite;
}

Entity()
{
    entity_sprite = initSprite(AssetManager::LoadTexture("res/wildcard.png"), sf::IntRect(0, 0, 36, 63), { 36,63 });
}

 

You're quite right to try avoiding globals, particularly with SFML types as SFML maintains its own global state which may get messed up. Using an asset manager implemented as a singleton probably constitutes global access, however :)
Title: Re: glFlush() error.
Post by: eXpl0it3r on September 22, 2020, 12:38:01 pm
Didn't really follow the thread further, but if you want a "sprite" that also supports not having a texture, then it's probably better to just go with a sf::RectangleShape
Title: Re: glFlush() error.
Post by: Riser on September 22, 2020, 01:52:52 pm
Yeah I've been working with sf::RectangleShape when I started for with SFML, but now I'm using sf::Sprite because almost all of the entities I'm using will have textures, plus you need Sprites when working with things like pixel-perfect collision and stuff.

Anyway, I refactored the code a bit following your advice, fallahn:

This is the Sprite class, it has a default constructor to get rid of that "no default constructor exists for class "Sprite"" error, and a method to set the Sprite, I also got rid of the Texture member as you said:

class Sprite{
public:
    sf::Sprite m_sprite;

    sf::Vector2f sprite_scale;
    sf::Vector2u original_size;
    sf::Vector2f texture_size;

    Sprite(){}
    sf::Sprite set_sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size);

};


sf::Sprite Sprite::set_sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size) {

    sf::Sprite spr(tx);

    spr.setTextureRect(rect);

    original_size =tx.getSize();

    texture_size.x = static_cast<float>(original_size.x);
    texture_size.y = static_cast<float>(original_size.y);

    sprite_scale.x = size.x / texture_size.x;
    sprite_scale.y = size.y / texture_size.y;

    spr.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
    spr.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));

    return spr;
}
 

And this is the Entity class:

class Entity {
public:
        Sprite spr;
        sf::Texture entity_texture;
        sf::Sprite entity_sprite;
        int health;
        float speed;
        bool collision = false;

        Entity() {
                entity_texture = AssetManager::LoadTexture("res/wildcard.png");
               
                entity_sprite = spr.set_sprite(entity_texture, { 0,0,36,63 }, { 36,63 });
        }
       
};
 

Same result, app still crashes immediately, what could possibly be causing this problem?
Title: Re: glFlush() error.
Post by: fallahn on September 22, 2020, 03:26:58 pm
I couldn't say what the crash is without more info like an error or seeing the call stack. What I can say though, is that you're creating a copy of the texture again, this time in your Entity class.
Title: Re: glFlush() error.
Post by: Riser on September 23, 2020, 01:00:36 pm
Oh my God there was a second page this whole time!

The output window didn't display anything, but when running the debugger I got an exception pointing to this line in a file named "xtree":
(Scroll sideways to see the exception)
(https://i.imgur.com/MztZRVs.png)

I think I already established that I'm very inexperienced, so this is way beyond me.
Title: Re: glFlush() error.
Post by: fallahn on September 23, 2020, 02:46:43 pm
In the pane on the bottom right there is a tab labelled 'call stack'. This is often useful when debugging as it tells you the order of the functions called leading up to the crash.

In this case however it looks like the info you've shared is saying the std::map inside your asset manager is currently null. It crashes when you try to read a texture from it. This is probably because you haven't instanciated the AssetManager at least once, so there's no singleton instance created yet.

This can be fixed by adding
AssetManager assetManager;
 

or so, right at the beginning of your program, perhaps even in main(). The book from which you got this code *ought* to explain why this works, although perhaps it doesn't... again in my opinion singletons are not the best solution to this and I much prefer the asset management outlined in SFML Game Development. (https://www.packtpub.com/product/sfml-game-development/9781849696845) At the very least there should be an assert that sInstance != nullptr in LoadTexture()/LoadFont() etc, this would indicate right away the reason for the crash.
Title: Re: glFlush() error.
Post by: Riser on September 25, 2020, 12:47:50 am
Alright it worked!
Thank you, I'm planning on reading all the other SFML books at some point and learn better practices, but for now I'm satisfied with the results.
The derived player class also works fine and I removed the texture member and instead called the asset manager directly as an argument:
entity_sprite = spr.set_sprite(AssetManager::LoadTexture("res/wildcard.png"), { 0,0,36,63 }, { 36,63 });

I'm gonna go and update the SFML library when I feel like it, but for now, thank you both, your help was invaluable, seriously, thank you so much!