So I have a strange issue and I am not even sure how to explain it properly. :)

I have code that creates a childwindow. I then want a canvas on the childwindow so I can draw lots of sprite, SFtexts etc.
Now when I reach part of the code that has a childwindow with a canvas (which is virtually all my childwindows), the screen blinks for a split second. It is hard to describe but it looks like the gamewindow is cleared or refreshed or that I quickly pressed alt-tab really quickly (which I did not). As my game relies on lots of childwindows opening and closing frequently then this makes the game all but unplayable.
Note it only seems to happen when in fullscreen mode.

Now when I comment out the line that creates a canvas, the blinking/refreshing does not happen.

First see the code where I call the childwindow and canvas in one of my menu methods. If I comment out the //initSubMenuCanvas method in this function then the blinking does not happen. (obviously I have to comment out all of the lines that are drawing to that canvas as well).

void drawEngine::drawStartTurnSummarySubMenu(Nation *passed_Nation)
{
        sf::Vector2i pos(gameWindow.getSize().x * 0.1, gameWindow.getSize().y * 0);
        sf::Vector2i size((gameWindow.getSize().x - (gameWindow.getSize().x * 0.25)), getLowerScreenPos().y);

        setupChildWindow(pos, size, "Start Turn Summary", sf::Color::Red, battleChildWin);
       
        tgui::Container::Ptr container = gui.get<tgui::Container>(battleChildWin);
        initSubMenuCanvas(container);
.....
.....
//lots of draw sprites or sf texts onto the canvas throughout the rest of this method.

 


So you can see below the functions where I actually create the childwindow and canvas. In the initSubMenuCanvas I have tried removing everything except the setupCanvas call but it makes not difference. I had wanted to provide a full working example but the code is quite long and complex now.


bool drawEngine::setupChildWindow(sf::Vector2i pos, sf::Vector2i size, string title_text, sf::Color titleText_Color, string passed_childName)
{
        if (gui.get(passed_childName) == NULL)
        {
                tgui::ChildWindow::Ptr childWin = theme->load("ChildWindow");
                childWin->setPosition(pos.x, pos.y);
                childWin->setSize(size.x, size.y);
                childWin->getRenderer()->setTitleColor(titleText_Color);
                childWin->setTitle(title_text);
                gui.add(childWin, passed_childName);
                cout << "creating win " << passed_childName << "\n";
                return true;
        }

        return false;
}

void drawEngine::initSubMenuCanvas(tgui::Container::Ptr container)
{
        sf::Sprite subMenuBackGroundSprite(subMenuBackGround);
        float childSizeX = container->getSize().x;
        float childSizeY = container->getSize().y;
        sf::Vector2f scaledValues;

        sf::Vector2i pos;
        sf::Vector2i size(childSizeX, childSizeY);;

        if (setupCanvas(pos, size, "subMenuCanvas", container))
        {
                scaledValues = calcScale(childSizeX, childSizeY, subMenuBackGround.getSize());
                subMenuBackGroundSprite.setScale(scaledValues.x, scaledValues.y);

                tgui::Canvas::Ptr canvas = container->get<tgui::Canvas>("subMenuCanvas");
                canvas->draw(subMenuBackGroundSprite);
        }
}

bool drawEngine::setupCanvas(sf::Vector2i pos, sf::Vector2i size, string passed_canvasName, tgui::Container::Ptr container)
{
        if ((container != NULL && container->get(passed_canvasName) == NULL) || (container == NULL && gui.get(passed_canvasName) == NULL))
        {
                auto canvas = std::make_shared<tgui::Canvas>();
                canvas->setPosition(pos.x, pos.y);
                canvas->setSize(size.x, size.y);

                if (container == NULL)
                {
                        gui.add(canvas, passed_canvasName);
                }
                else
                {
                        container->add(canvas, passed_canvasName);
                }

                return true;
        }

        return false;
}

 

*

texus

  • *****
  • 1163
    • View Profile
    • Texus's Blog
Are you calling canvas->clear() and canvas->display() around your canvas->draw() calls?

If you are then all I can think of is a problem with the graphics driver.
Canvas is just a wrapper around sf::RenderTexture and an sf::Sprite to draw it. I have seen weird things happen with render textures before.
If you have the latest driver then you will have to verify whether this is an issue with sfml or tgui:
To test you might want to replace the Canvas with a sf::RenderTexture. It could be passed to a picture to render it with tgui with pic->setTexture(rt.getTexture()). The render texture does not has to be stored since picture copies the texture.
If it still occurs then you could even try without using tgui at all and create a render texture which you draw to the screen yourself on the position of the child window (it has a getChildWidgetsOffset() to find the distance between its position and the area where the widgets in it are drawn).

Are you calling canvas->clear() and canvas->display() around your canvas->draw() calls?

I call a canvas->clear() at the beginning.
I then call the below (drawSFTextOnCanvas) multiple times to draw various SFTexts to the canvas.
Then at the end of the method, I call canvas->display.

void drawEngine::drawSFTextOnCanvas(vector<sf::Text> vectorSFText, tgui::Canvas::Ptr canvas)
{
        for (int i = 0; i < vectorSFText.size(); i++)
        {
                canvas->draw(vectorSFText[i]);
        }
}
 

Now what is strange is if I comment out all drawSFTextOnCanvas, canvas->clear and canvas->display then the issue still happens when all I am doing is creating the canvas. So basically if I just call the below, the blinking happens. It only happens with canvas. Other widgets getting added to the container are fine. I've tried buttons, listboxes and panels.

//return true if creates new canvas
bool drawEngine::setupCanvas(sf::Vector2i pos, sf::Vector2i size, string passed_canvasName, tgui::Container::Ptr container)
{
        if ((container != NULL && container->get(passed_canvasName) == NULL) || (container == NULL && gui.get(passed_canvasName) == NULL))
        {
                auto canvas = std::make_shared<tgui::Canvas>();
                canvas->setPosition(pos.x, pos.y);
                canvas->setSize(size.x, size.y);

                if (container == NULL)
                {
                        gui.add(canvas, passed_canvasName);
                }
                else
                {
                        container->add(canvas, passed_canvasName);
                }

                return true;
        }

        return false;
}
 

*

texus

  • *****
  • 1163
    • View Profile
    • Texus's Blog
Canvas is the only widget that uses sf::RenderTexture so I still think it is related to that.

There are a few things you could look at:
- What happens if instead of canvases you just create a render texture (call create, clear and display on it)? Does it cause blinking as well?
- What if you create the canvases upfront and in setupCanvas you just retrieve the canvas? (you can't call setSize in setupCanvas either because it will cause the render texture to be recreated)
- Try removing the m_renderTexture.clear() and m_renderTexture.display() call inside tgui::Canvas::setSize (src/TGUI/Widgets/Canvas.cpp lines 98 and 99)

There are a few things you could look at:
- What happens if instead of canvases you just create a render texture (call create, clear and display on it)? Does it cause blinking as well?

Ok so I comment out the setupCanvas function and instead I add these lines. I didn't even bother doing a clear or display. The blinking does happen. It is not as pronounced or as bad as when I am doing the setupCanvas but it does happen. (Say a blink of 0.1 seconds as opposed to say 0.4 seconds)


// create a 500x500 render-texture
        sf::RenderTexture renderTexture;
        if (!renderTexture.create(500, 500))
        {
                // error...
        }

 

I haven't tried the other 2 points yet but does the above mean it is an SFML issue?

*

texus

  • *****
  • 1163
    • View Profile
    • Texus's Blog
The reason why the blink would be longer with Canvas is because both the creation and the setSize call will call renderTexture.create so I would expect the blink with Canvas to be twice as long as the code you have now. Maybe the clear/display in setSize make it even longer as well.

I doubt that it has anything to do with TGUI and since it blinks with just sf::RenderTexture it does indeed look like its directly related to SFML.

But if you go to the SFML forum I already know what they are going to tell you: this is a graphics driver issue. Hundreds of people use sf::RenderTexture without having such blinking so this is not going to be a mistake in the SFML code. It would rather be the combination of specific opengl calls from sfml in combination with your graphics driver. So I suggest you try update or downgrade your graphics driver before trying anything else and see if that makes a difference.

Ok thanks. Driver is already up-to-date. It might be easier for me to avoid using canvas then.

Just to clarify that only canvas uses RenderTexture? So if I convert my SFText to labels then I would not need a canvas as I can draw them directly onto the childwindow container?

Also can childwindows have background image? I don't see that method when I check the methods from the getRenderer() method. Most of the sprites that I am drawing onto the canvas are part of the background image.

childWin->getRenderer()->
 

*

texus

  • *****
  • 1163
    • View Profile
    • Texus's Blog
Quote
Just to clarify that only canvas uses RenderTexture? So if I convert my SFText to labels then I would not need a canvas as I can draw them directly onto the childwindow container?
Yes, RenderTexture is not used anywhere else.
If they are labels then they can indeed be drawn directly to the child window.

Quote
Also can childwindows have background image?
The possibility to assign a background image to child window was removed a long time ago since you could do the same by creating a Picture of a Canvas and adding it to the child window.

Ok thanks. Can I make a create a picture from an existing loaded sf::Texture? I tried the below but I get error

Error   304   error C2664: 'tgui::Picture::Picture(const tgui::Picture &)' : cannot convert argument 1 from 'sf::Texture *' to 'const std::string &'   c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory   932   1   rome_vs2013


bool drawEngine::setupPicture(sf::Vector2i pos, sf::Vector2i size, string passed_pictureName, sf::Texture *passed_texture, tgui::Container::Ptr container)
{
        if ((container != NULL && container->get(passed_pictureName) == NULL) || (container == NULL && gui.get(passed_pictureName) == NULL))
        {
                auto picture = std::make_shared<tgui::Picture>(passed_texture);
                picture->setPosition(pos.x, pos.y);
                picture->setSize(size.x, size.y);
                if (container == NULL)
                {
                        gui.add(picture, passed_pictureName);
                }
                else
                {
                        container->add(picture, passed_pictureName);
                }

                return true;
        }

        return false;
}

 

arrggh nevermind.

I needed to do this instead, i.e. use *passed_texture instead of passed_texture. Sorry.

auto picture = std::make_shared<tgui::Picture>(*passed_texture);
 

Hi Texus,

Ok so I have replaced nearly all of what I was doing/drawing onto a canvas via labels or pictures. However one thing I cannot seem to replace easily is some sf::RectangleShape onto a child window.

Is there any way to do that in a childwindow without a canvas? I'm assuming no and that I will have to draw a picture with the border outline but I was hoping to avoid that if possible.

*

texus

  • *****
  • 1163
    • View Profile
    • Texus's Blog
It isn't possible directly, Cavas was created exactly for these cases where you needed to do this.

One option would be to create a custom widget though. Create a new class that inherits from tgui::ClickableWidget and override the draw function with something like this:
void CustomWidget::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    states.transform.translate(getPosition());
    sf::RectangleShape background(getSize());
    target.draw(background);
}

Ok thanks, I'll give the custom widget a go and see how I get on.

It isn't possible directly, Cavas was created exactly for these cases where you needed to do this.

I guess I am struggling now to understand why there is a canvas in order to do sfml sprites, texts etc directly. Is there any reason why we cannot simply draw these items to gui or containers directly, i.e. why the need for an extra layer?

Obviously I know the canvas issue is specific to my graphics card but I would just find it much easier to draw directly to containers rather than needing to have another layer in there.

*

texus

  • *****
  • 1163
    • View Profile
    • Texus's Blog
The problems start because you draw tgui with a single gui.draw() call: this means that you can only draw sfml stuff behind everything or before everything. So the only way to have sfml inside a child window and thus between tgui draw calls is to render it upfront to an off-screen texture.

It is that off-screen texture (which is created through sf::RenderTexture) where things go wrong in some graphics card drivers. So it doesn't matter whether I let you draw to a Canvas or whether I create the RenderTexture directly in the child windows, the same problem remains.

As why I chose to use a Canvas: it allows sfml rendering anywhere inbetween tgui rendering, not only at the back of a panel or child window.