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

Author Topic: Segfault on destructors of SFML objects (SOLVED)  (Read 4997 times)

0 Members and 1 Guest are viewing this topic.

Ozcar

  • Newbie
  • *
  • Posts: 3
    • View Profile
Segfault on destructors of SFML objects (SOLVED)
« on: October 31, 2013, 06:12:14 pm »
Background

Hello! This is my first post on SFML forums, having been introduced to SFML about a week ago. I am making a 2d worms-like game in which I need a random map generator for two islands. For that I am using SFML::Image and SFML::Shape to dish out the map and add some rocklike structures (actually more like gold patches for the player to mine for money in bedrock). Unfortunately this approach SEGFAULTs at every instance my program tries to destruct an SFML object, ultimately at program exit if not sooner.

The code

Here is my version of the code, the exact place which raises the segfault is the calling of generate for terrainMap in the constructor of Terrain. The program faults between the end of generate and the return to the constructor, but more of that later.

Terrain.cc:
#include <SFML/Graphics.hpp> // SFML image

#include <iostream>     // std::cout
#include <vector>       // vector
#include <cstdlib>      // rand
#include <ctime>        // ctime
#include <cmath>        // cos, sin
#include <algorithm>    // max

#include "terrain.hh"

Terrain::Terrain()
{
        width = 0;
        height = 0;
}

Terrain::Terrain(size_t width, size_t height, double roughness, size_t waterLvl) {
        width = width;
        height = height;
        this->generate(width, height, roughness, waterLvl);
}

Terrain::~Terrain() {
    delete &terrainMap;
}

void Terrain::generate(size_t width, size_t height, double roughness, size_t waterLvl) {
    terrainMap.create(width, height, sf::Color::White);
        double displacementFactor = 1.0 * height / 2;

    // Here editing the amount of initial cells determines the amount of mountains
    // Use [Number you want] + 1
        std::vector<int> heightMap(3, 0);
    // Create waterlevel
        for (size_t x = 0; x < width; x++) {
                for (size_t y = 0; y < waterLvl; y++) {
                        terrainMap.setPixel(x, height - y, sf::Color::Blue);
                }
        }
        // Create sand
        for (int i = 0; i < 6; i++) {
                heightMap = split(heightMap, displacementFactor);
                displacementFactor *= roughness;
        }
        // Draw sand
        double segmentWidth = (width / (2 * heightMap.size()));
        for (size_t x = 0; x < heightMap.size() - 1; x++) {
                double slope = (heightMap[x + 1] - heightMap[x]) / segmentWidth;
                for (size_t xMap = 0; xMap < segmentWidth; xMap++) {
                        for (size_t y = 0; y < heightMap[x] + slope * xMap; y++) {
                                terrainMap.setPixel(width / 4 + x * segmentWidth + xMap, height - y, sf::Color::Green);
                        }
                }
        }
       
        // Clear and initialize the heightMap (again)
        heightMap.clear();
    heightMap.resize(3);
       
        // Create stone
    displacementFactor = 0.7 * height / 2;
   
    for (int i = 0; i < 6; i++) {
        heightMap = split(heightMap, displacementFactor);
        displacementFactor *= roughness;
    }
    // Draw stone
    segmentWidth = (width / (2 * heightMap.size()));
    for (size_t x = 0; x < heightMap.size() - 1; x++) {
        double slope = (heightMap[x + 1] - heightMap[x]) / segmentWidth;
        for (size_t xMap = 0; xMap < segmentWidth; xMap++) {
            for (size_t y = 0; y < heightMap[x] + slope * xMap; y++) {
                terrainMap.setPixel(width / 4 + x * segmentWidth + xMap, height - y, sf::Color::Black);
            }
        }
    }

    // Create gold patches
    size_t xGold, yGold;
    sf::ConvexShape shape;
    shape.setPointCount(6);
    size_t max_patches = size_t(width / 500);
    for(size_t i = 0; i < max_patches; i++){
        xGold = rand() % width; yGold = rand() % height;
        if (getMaterial(xGold, yGold) == Terrain::Material::STONE || getMaterial(xGold, yGold) == Terrain::Material::SAND){             //if random point on map is STONE, make an elliptical gold patch around it
            for (size_t i = 0; i < 6; i++) {
                size_t radius = rand() % 60 + 1;
                shape.setPoint(i, sf::Vector2f( xGold + radius * cos(30*i), yGold + radius * sin(30 * i)));
            }
            for (size_t i = 0; i < shape.getPointCount() - 1; i++) {
                terrainMap.setPixel(shape.getPoint(i).x, shape.getPoint(i).y, sf::Color::Yellow);
            }
        }
    }   <<<<<<<<< GDB OUTPUT PASTED FROM HERE ONWARDS
}

   
// Generate the mountain ridge with midpoint dislocation algorithm
std::vector<int> Terrain::split(std::vector<int> heightMap, double displacementFactor) {
        std::vector<int> newHeightMap(2 * heightMap.size() - 1);
        for (size_t i = 0; i < heightMap.size() - 1; i++) {
                // Calculate midpoint heights
                double midpoint = (heightMap[i] + heightMap[i + 1]) / 2;
        double fRand = (double)rand() / (double)RAND_MAX;
                double displacement = fRand * displacementFactor;
                // Force island shape
                if (i == 0) {
                        if (displacement < 0) {
                displacement *= -1;
                        }
                }
                newHeightMap[2 * i] = heightMap[i];
                newHeightMap[2 * i + 1] = midpoint + displacement;
        }
        return newHeightMap;
}

Terrain::Material Terrain::getMaterial(const size_t x, const size_t y) {
        sf::Color pixel = terrainMap.getPixel(x, y);
    if (pixel == sf::Color::Blue) {
        return WATER;
    }
    if (pixel == sf::Color::Green) {
        return SAND;
    }
    if (pixel == sf::Color::Black) {
        return STONE;
    }
    if (pixel == sf::Color::Yellow) {
        return GOLD;
    }
    return AIR;
}


Terrain.hh:
#ifndef ART_13_TERRAIN
#define ART_13_TERRAIN

#include <SFML/Graphics.hpp> // SFML image
#include <vector> // vector
#include <cstdlib> //rand

   /**
        * This is the class for generating and modifying the terrain image
        * Terrain is constructed from an image representing the different materials.
        * The physics library is used to erode the class after initial generating.
        */


class Terrain {
public:
        enum Material {
                STONE,
                SAND,
                AIR,
                WATER,
        GOLD
    };
        // Constructor
        Terrain();
        // Constructor with parameters
        Terrain(size_t width, size_t height, double roughness, size_t waterLvl);
        // CpyConstructor - DELETED
        Terrain(const Terrain& terrain) = delete;
        // Destructor
        ~Terrain();
        // Generate rough terrain
        void generate(size_t width, size_t height, double roughness, size_t waterLvl);
        // Recursive function for splitting the heightmap
        std::vector<int> split(std::vector<int> heightMap, double roughness);
        // Erode sand until stationary
        void erode();
    // Get the map
    sf::Image getMap() {
        return terrainMap;
    }
        // Get map width
        size_t getWidth() {
                return width;
        }
        // Get map height
        size_t getHeight() {
                return height;
        }
        // Returns the material of given pixel
        Material getMaterial(const size_t x, const size_t y);
   
    sf::Image emptyImage();
private:
        sf::Image terrainMap;
        size_t width;
        size_t height;
};

#endif

 

I compile this with SFML 2.1 and GCC (compile++ is my alias for compiling c++ with std=c++11 and all the warnings enabled)

compile++ main main.cc terrain.cc -I/home/Developement/SMFL2.1 -lsfml-graphics -lsfml-window -lsfml-system
 

GDB

As I said earlier the code seems to segfault just as it returns from the generate-method. This is the GDB output from my code from line 95 (end of generate, marked in the code for your enjoyment) onwards using gdb's step command:

Breakpoint 1, Terrain::generate (this=0x7fffffffe290, width=3200, height=800, roughness=0.5, waterLvl=100) at terrain.cc:95
95          }
(gdb) step
sf::ConvexShape::~ConvexShape (this=0x7fffffffdfc0, __in_chrg=<optimized out>) at /usr/local/include/SFML/Graphics/ConvexShape.hpp:42
42      class SFML_GRAPHICS_API ConvexShape : public Shape
(gdb)
std::__debug::vector<sf::Vector2<float>, std::allocator<sf::Vector2<float> > >::~vector (this=0x7fffffffe150, __in_chrg=<optimized out>)
    at /usr/include/c++/4.7/debug/vector:140
140           ~vector() _GLIBCXX_NOEXCEPT { }
(gdb)
__gnu_debug::_Safe_sequence<std::__debug::vector<sf::Vector2<float>, std::allocator<sf::Vector2<float> > > >::~_Safe_sequence (this=0x7fffffffe168,
    __in_chrg=<optimized out>) at /usr/include/c++/4.7/debug/safe_sequence.h:112
112         class _Safe_sequence : public _Safe_sequence_base
(gdb)
__gnu_debug::_Safe_sequence_base::~_Safe_sequence_base (this=0x7fffffffe168, __in_chrg=<optimized out>) at /usr/include/c++/4.7/debug/safe_base.h:199
199         { this->_M_detach_all(); }
(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7527f60 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
 

This seems to me like the destructor for convexShape failed, but how? I am by no means an expert C++ coder and just now working my way out from pure standard libraries but I can't get my head around to which could be the source of this. If this is an error in my understanding of SFML/C++ altogether it would help to get it solved now rather than 2000 more lines into the code. Thank you ever so much for your time!
« Last Edit: November 01, 2013, 02:43:42 pm by Ozcar »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10819
    • View Profile
    • development blog
    • Email
Re: Segfault on destructors of SFML objects
« Reply #1 on: October 31, 2013, 06:30:15 pm »
The code is too long for us to actually go through and understand every detail, but I can already say that SFML is using RAII, thus you calling delete on sf::Image is wrong and can/should lead to a crash, don't do that!
In general you should always use RAII yourself, thus removing the need for calling delete in mostly all cases. You should however know, that IF you ever would call delete it's also you that should have called new. If libraries only provide factories but no destruction mechanism you shouldn't touch those libraries. ;)

If removing the manual delete doesn't help, then you might want to start creating a minimal and complete example that reproduces the error. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Ozcar

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Segfault on destructors of SFML objects
« Reply #2 on: October 31, 2013, 06:45:07 pm »
Sure thing! Letting SFML handle the RAII led me nowhere (which was the reason I implemented it myself in the first place, trying to circumvent the segfault), but here is a small main function which I use for testing. 

#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>

#include <iostream>
#include <string>
#include <vector>
#include <cmath>

#include "terrain.hh"

void eventHandler(sf::RenderWindow& mainWindow, sf::Sprite& sprite);

int main()
{
    std::srand((unsigned)time(0));
    sf::RenderWindow mainWindow;
    mainWindow.create(sf::VideoMode(800, 800), "Maptest", sf::Style::Resize | sf::Style::Close);
    mainWindow.setFramerateLimit(60);
   
    size_t width = 3200;
    size_t height = 800;
    Terrain newTerrain(width, height, 0.5, 100);
    sf::Texture texture;
    texture.loadFromImage(newTerrain.getMap());
    sf::Sprite sprite;
    sprite.setTexture(texture, true);
   
    while (mainWindow.isOpen()) {
        eventHandler(mainWindow, sprite);
        mainWindow.clear(sf::Color::Black);
        mainWindow.draw(sprite);
        mainWindow.display();
    }

    return 0;
}

void eventHandler(sf::RenderWindow& mainWindow, sf::Sprite& sprite) {
    // Create an event handler that monitors program events
    sf::Event event;
    while (mainWindow.pollEvent(event)) {
        // Create a switch to match these events, eg. user wants to close the window but
        // we want to save the gamestate before quitting or need to run thru some destructors first
        switch (event.type) {
            case sf::Event::Closed:

                mainWindow.close();
                break;
       
            case sf::Event::KeyPressed:
                if (event.key.code == sf::Keyboard::Left) {
                    sprite.move(-10, 0);
                }
                if (event.key.code == sf::Keyboard::Right) {
                    sprite.move(10, 0);
                }
                if (event.key.code == sf::Keyboard::Up) {
                    sprite.move(0, -5);
                }
                if (event.key.code == sf::Keyboard::Down) {
                    sprite.move(0, 5);
                }
               
            default:
                break;
        }
   
    }
}

It is worth noting that the code runs if you comment out the gold patch generation, only crashing on exit in that case. Including it segfaults before the program gets to event handling and you may need to force quit with xkill or the like.

The Hatchet

  • Full Member
  • ***
  • Posts: 135
    • View Profile
    • Email
Re: Segfault on destructors of SFML objects
« Reply #3 on: October 31, 2013, 08:51:27 pm »
Well so far from my testing i've found multiple issues. 

1.  When creating the waterlevel if x becomes greater than 1016 it crashes (probably due to the image size of 3200 pixels wide.  the video card may not be able to support this).  I lowered the level size to 256x128 and got further.

2. When creating the sand and going into the Split() function, once the heightmaps size got to 17 the newHeightMap would fail to generate from it. 

This is as far as I've gotten but it seems you're code is riddled with issues.  Maybe you should take a step back and get one aspect of the level generation working at a time.  First make sure all the water gets made and is displayed correclty, then the sand, then the stone, etc.

Also I don't think you are using the vector constructor correctly in your Terrain::split() method.  Correct vector constructors are as follows:
std::vector<int> first;                                // empty vector of ints
  std::vector<int> second (4,100);                       // four ints with value 100
  std::vector<int> third (second.begin(),second.end());  // iterating through second
  std::vector<int> fourth (third);                       // a copy of third

There does not seem to be a std::vector<int> another(int #);  If you want to create an empty vector of a certain size you should create an empty vector then call a resize on it or call vector<int> another(x, 0); where x is how many values you want set to 0
« Last Edit: October 31, 2013, 09:31:44 pm by The Hatchet »

Ozcar

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Segfault on destructors of SFML objects
« Reply #4 on: November 01, 2013, 02:43:19 pm »
Thank you for your replies. I did find the reason the program kept segfaulting. I compiled my programs using a wrong flag (namely -D_GLIBCXX_DEBUG) which somehow broke up the compiling process. Removing that helped and the code works now as intended. Thank you so much for your time!