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

Author Topic: Crash when drawing to sfml window multiple times using vertex arrays  (Read 2857 times)

0 Members and 1 Guest are viewing this topic.

Dincio

  • Newbie
  • *
  • Posts: 13
    • View Profile
So I made this gamish thing that uses a quickly-and-poorly made custom GUI library to manage the player interface. This thing currently consists in the drawing of a tile map, which is implemented using a sf::VertexArray of quads (basically a copy of the tutorial one...) and a TextBox object, also implemented using a vertex array of quads (since the tutorial made it look so efficent I figured I'd just spam it everywhere in my design :P). The problem is that the two don't seem to get along nicely... when I try to draw them at the same time, the game crashes. This is what the debugger says about the crash (I'm using CodeBlocks with a recent version of mingw):


#0 0x53eb9f83   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#1 0x53fea39c   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#2 0x5409c9d4   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#3 0x5409c642   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#4 0x5409c3e2   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#5 0x53ebea1a   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#6 0x53ebeadf   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#7 0x54208ad3   ig9icd32!RegisterProcTableCallback() (C:\WINDOWS\SysWOW64\ig9icd32.dll:??)
#8 0x66b9c466   sf::RenderTarget::draw(sf::Vertex const*, unsigned int, sf::PrimitiveType, sf::RenderStates const&) () (C:\Code\C\sfml\Wars\sfml-graphics-2.dll:??)
#9 0x66baa258   sf::VertexArray::draw(sf::RenderTarget&, sf::RenderStates) const() (C:\Code\C\sfml\Wars\sfml-graphics-2.dll:??)
#10 0x66b9b87c   sf::RenderTarget::draw(sf::Drawable const&, sf::RenderStates const&) () (C:\Code\C\sfml\Wars\sfml-graphics-2.dll:??)
#11 0x403a75   _fu21___ZSt4cout() (C:\Code\C\sfml\Wars\src\interface\TextBox.cpp:101)

And here's the part of the code where the game breaks:
//draw
void TextBox::parentDraw(sf::RenderTarget& target, sf::RenderStates states) const {
    //if no font is set, abort
    assert(mTextInfo.font);
    //update vertices if necessary
    if(mNeedToUpdateVertices){
        std::cout << "text: " << mTextInfo.text << "\n\n";
        mNeedToUpdateVertices = false;
        //calculate new vertex array size needed
        //(updated constantly when special (not printable) characters
        // are handled and used at the end to resize the vertex array)
        size_t verticesNum = 0;
        for(const auto& ele : mTextInfo.text) verticesNum++;
        mVertices.resize(verticesNum * 4);
        //adjust vertex array
        const auto& newText = mTextInfo.text;
        unsigned int lineOffset = getBounds().left;
        unsigned int verticalOffset = getBounds().top + mTextInfo.font->getLineSpacing(mTextInfo.charSize);
        for(size_t j = 0, vj = 0; j < newText.size(); j++, vj++){
            //handle special characters
            if(newText[j] == '\n'){
                verticesNum--;
                vj--;
                lineOffset = getBounds().left;
                verticalOffset += mTextInfo.font->getLineSpacing(mTextInfo.charSize);
            }
            //handle letters (non-handled characters are rendered as letters)
            else {
                //handle space
                if(newText[j] == ' '){
                    //generate a new line
                    auto futLineOffset = lineOffset;
                    auto k = j + 1;
                    for(; newText[k] != ' ' && newText[k] != '\n' && k < newText.size(); k++){
                        futLineOffset += mTextInfo.font->getGlyph(newText[k], mTextInfo.charSize, false).advance;
                    }
                    if(futLineOffset > getBounds().width){
                        verticesNum--;
                        j++;
                        lineOffset = getBounds().left;
                        verticalOffset += mTextInfo.font->getLineSpacing(mTextInfo.charSize);
                    }
                }
                //change vertex quad associated with letter
                auto glyph = mTextInfo.font->getGlyph(newText[j], mTextInfo.charSize, false);
                auto bRect = glyph.bounds;
                float kerning = 0;
                //handle kerning
                if(j < newText.size() - 1)
                    kerning = mTextInfo.font->getKerning(newText[j], newText[j+1], mTextInfo.charSize);
                //set glyph bounds within text box
                bRect.left = lineOffset + kerning;
                lineOffset += glyph.advance + kerning;
                bRect.top += verticalOffset;
                //get texture rect
                const auto& tRect = glyph.textureRect;
                //convert glyph into vertex quad
                mVertices[vj*4]      = sf::Vertex({bRect.left, bRect.top},                               {tRect.left, tRect.top});
                mVertices[vj*4 + 1]  = sf::Vertex({bRect.left + bRect.width, bRect.top},                 {tRect.left + tRect.width, tRect.top});
                mVertices[vj*4 + 2]  = sf::Vertex({bRect.left + bRect.width, bRect.top + bRect.height},  {tRect.left + tRect.width, tRect.top + tRect.height});
                mVertices[vj*4 + 3]  = sf::Vertex({bRect.left, bRect.top + bRect.height},                {tRect.left, tRect.top + tRect.height});
            }
            //handle horizontal overflow
            if(lineOffset >= getBounds().width){
                lineOffset = getBounds().left;
                verticalOffset += mTextInfo.font->getLineSpacing(mTextInfo.charSize);
            }
        }
        //resize vertex array if necessary (special characters were encountered)
        if(mVertices.getVertexCount() != verticesNum * 4)
            mVertices.resize(verticesNum * 4);
    }
    //set transform
    states.transform *= getTransform();
    //set texture
    states.texture = &(mTextInfo.font->getTexture(mTextInfo.charSize));
    //draw vertex array to render target
    target.draw(mVertices, states);
}
 

Again, the problem only comes up when I draw a TextBox object (or really anything else - even using an object from a less-poorly made GUI library, such as TGUI, causes a segmentation fault) in conjunction with the tilemap; which is implemented as such (I'll only put the draw method here, but I'm willing to post more code on request):

void World::draw(sf::RenderTarget& target, sf::RenderStates states) const {
    if(mNeedToUpdateVertices){
        //update vertices if needed
        for(int x = 0; x < mWidth; x++){
            for(int y = 0; y < mHeight; y++){
                if(mNeedToUpdateAllVertices || mPositionsToUpdate[y * mWidth + x]){
                    //determine which tile to show
                    auto tile = getTile({x, y});
                    sf::IntRect groundTR;
                    if(tile.up == UpTile::Air){
                        groundTR = sf::IntRect(gTerrainTileModels[tile.terrain].info.textureRect);
                    }
                    else{
                        groundTR = sf::IntRect(gUpTileModels[tile.up].info.textureRect);
                    }
                    //add up/terrain vertices
                    updateQuadAtPosition(sf::Vector2i(x, y), groundTR, false);
                    //add area vertices
                    updateQuadAtPosition(sf::Vector2i(x, y), gUpTileModels[tile.area].info.textureRect, true);
                }
            }
        }
        //render entities
        for(const auto& entity : mEntities){
            const auto& pos = entity->getPosition();
            if(mNeedToUpdateAllVertices || mPositionsToUpdate[pos.y * mWidth + pos.x])
                updateQuadAtPosition(pos, entity->getTextureRect(), false);
        }
    }
    //apply transform
    states.transform = getTransform();
    //set texture
    states.texture = mTileSheetTexture;
    //reset positions to update
    mNeedToUpdateVertices = false;
    mNeedToUpdateAllVertices = false;
    for(auto q = mPositionsToUpdate.begin(); q != mPositionsToUpdate.end(); ++q)
        *q = false;
    //render vertices
    target.draw(mVertices, states);
}

void World::updateQuadAtPosition(const sf::Vector2i& pos, const sf::IntRect& textureRect, bool isArea) const {
    auto index = (pos.y * mWidth + pos.x) * 8;
    if(isArea) index += 4;
    sf::FloatRect rr = {pos.x * World::TILE_WIDTH,
                        pos.y * World::TILE_HEIGHT,
                        World::TILE_WIDTH,
                        World::TILE_HEIGHT};
    const auto& tr = textureRect;
    //add up/terrain vertices
    mVertices[index]     = sf::Vertex({rr.left, rr.top},                        {tr.left, tr.top});
    mVertices[index + 1] = sf::Vertex({rr.left + rr.width, rr.top},             {tr.left + tr.width, tr.top});
    mVertices[index + 2] = sf::Vertex({rr.left + rr.width, rr.top + rr.height}, {tr.left + tr.width, tr.top + tr.height});
    mVertices[index + 3] = sf::Vertex({rr.left, rr.top + rr.height},            {tr.left, tr.top + tr.height});
}
 

And here's where I draw the two:
void MatchHandler::handleDrawing()
{
    //draw player view of world
    auto standardView = mWindow->getDefaultView();
    auto playerView = mPlayer->calcView();
    playerView.setViewport(sf::FloatRect(0.f, 0.f, 0.5, 1.f));
    mWindow->setView(playerView);
    mWindow->draw(*mWorld);
    //draw player interface
    mWindow->setView(standardView);
    mWindow->draw(*(mPlayerInterface->getChild("message_box")));
}
 

I know this is a long question, but I really tried everything before posting. Also, I repeat, the problem is not specific to this instance of it, and happens even when I use other GUI libraries or just anything that is implemented using sf::VertexArray.

Thaks in advance for any response  ;D.

Dincio

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: Crash when drawing to sfml window multiple times using vertex arrays
« Reply #1 on: January 22, 2017, 10:37:11 am »
I found out that the problem was in the way I drew the area tiles. In a more general way: whenever I use a "double layer" vertex array (in other words, a vertex array that consists of two layers of quads, one rendered on top of the other) things start to break and lag. Still though, I can't figure why this happens or if this is even supposed to be a sfml bug.

P.S. I know nothing of OpenGL and of how vertex arrays work under the hood (apart from seeing the sfml github repository).

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Crash when drawing to sfml window multiple times using vertex arrays
« Reply #2 on: January 22, 2017, 08:04:04 pm »
Since it crashes within your GPU driver, there's little SFML can do.

What's your GPU? What's your GPU driver version? Try grabbing the latest version from your vendor's site.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Dincio

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: Crash when drawing to sfml window multiple times using vertex arrays
« Reply #3 on: January 30, 2017, 10:20:35 pm »
Sorry for the long wait, but I couldn't work on the project for some time and kind of forgot about it... ayways, I have the fallowing video devices:

 -) Intel(R) HD Graphics 530
 -) NVIDIA GeForce GTX 950M

Also, as I kept working on the project, I unfortunately encountered some other bugs related to vertex arrays. For instance, when I draw a lot of them (like three layers, one on top of the other), black lines come out of no where (look at attachment for details).

I also found out that the bug is connected to using the viewport, since the lines only show themselves when I set the viewport of the currect view to be half the size of the screen. Strangely, though, they also show up when the viewport is of the same size as that of the window but only if I render two layers of vertex arrays (I basically draw two of them, one on top of the other)... I have to admit that I am rather confused  ;D

The same bug also seems to be connected to the texture rects of the vertex arrays, as the lines are really just a piece of the texture which is near them in the tile sheet I'm using. Hope this helps diagnosing a solution...

thanks for the reply.
« Last Edit: January 30, 2017, 10:43:33 pm by Dincio »

Dincio

  • Newbie
  • *
  • Posts: 13
    • View Profile
Re: Crash when drawing to sfml window multiple times using vertex arrays
« Reply #4 on: January 30, 2017, 11:46:14 pm »
Ok I've been playing around some more with the code and the bug jst seems to be conncted to vertex arrays and zooming in general. Every time I zoom a vertex array by modifying the window dimensions (even if I only use 1 with the default view and viewport), weird lines appear all over... this happens only at particular levels of zooming.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Crash when drawing to sfml window multiple times using vertex arrays
« Reply #5 on: February 01, 2017, 01:23:02 pm »
-) Intel(R) HD Graphics 530
 -) NVIDIA GeForce GTX 950M
So which GPU are you using then?

I also found out that the bug is connected to using the viewport, since the lines only show themselves when I set the viewport of the currect view to be half the size of the screen. Strangely, though, they also show up when the viewport is of the same size as that of the window but only if I render two layers of vertex arrays (I basically draw two of them, one on top of the other)... I have to admit that I am rather confused  ;D

The same bug also seems to be connected to the texture rects of the vertex arrays, as the lines are really just a piece of the texture which is near them in the tile sheet I'm using. Hope this helps diagnosing a solution...
It's called texture bleeding, where the next pixel of a texture is being used, due to rasterization of non-pixel perfect coordinates. First workaround is to always render on integer positions and don't use zooming, etc.
The other workaround is to render to a render texture before applying transformations of the view etc.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/