Почему push_back выдает ошибку сегментации? - PullRequest
0 голосов
/ 07 мая 2020

О проекте:

Я работаю над трассировщиком лучей Opengl, который может загружать файлы obj и отслеживать их. Мое приложение загружает файл obj с помощью assimp, а затем отправляет все грани треугольника (вершины и индексы) во фрагментный шейдер, используя объекты хранилища шейдеров. Структура basi c собирается визуализировать результаты в четырехугольник из фрагментного шейдера.

Когда я загружаю более крупные объекты (более 100 треугольников), компьютеру потребовалось много времени, чтобы сделать пересечениях, поэтому я начал создавать BVH-дерево, чтобы ускорить процесс. Мой BVH рекурсивно разделяет пространство на два выровненных по оси ограничивающих прямоугольников на основе средней медианы граней треугольников, содержащихся в AABB.

Мне удалось построить древовидную структуру BVH (на CPU), и теперь Я хочу преобразовать его в простой массив, а затем отправить его во фрагментный шейдер (в буфер хранения шейдера).

Вот метод, отвечающий за преобразование узла BVH root в массив:

BvhNode bvhNode; //global variable

BvhNode* putNodeIntoArray() {

    int size=bvhNode.getNumberOfNodes();
    BvhNode nodesArray[size];

    int current_index = 0;
    vector<BvhNode> tempNodes;

    tempNodes.push_back(bvhNode);
    BvhNode current_node;

    while (!tempNodes.empty()) {
        current_node = tempNodes.at(0);

        tempNodes.erase(tempNodes.begin());
        nodesArray[current_index] = current_node;

        if(!current_node.isLeaf)
        {
            tempNodes.push_back(current_node.children.at(0)); // Segmentation error occurs here!
            tempNodes.push_back(current_node.children.at(1));
        }

        current_index++;
    }

    return nodesArray;
}

О проблеме:

Не знаю почему, но это дает мне ошибку сегментации, когда я хочу push_back первого потомка в tempNodes вектор (точное место можно увидеть из комментария выше). Мне кажется, что current_node.children.at(0) не существует, но на самом деле он существует согласно отладчику. Я попытался написать оператор ссылки (&): tempNodes.push_back(&current_node.children.at(0));, но в этом случае он дает мне странные координаты объектов. Я попытался определить переменные в функции как глобальные, пытаясь избежать проблем с областью видимости, а также определить переменную current_node как указатель. К сожалению, ни один из них не дал мне лучших результатов.

Вот мой класс BvhNode, если он помогает:

class BvhNode {

public:
    BBox bBox;
    int depthOfNode;
    vector<BvhNode> children;
    int order;
    bool isLeaf;
    bool createdEmpty = false;
    vector<glm::vec3> primitiveCoordinates;

    BvhNode() {}
    BvhNode(BvhNode *pNode) {}

    BvhNode(vector<glm::vec3> &primitiveCoordinates) {
        this->primitiveCoordinates = primitiveCoordinates; }

    void buildTree(vector<glm::vec3>& indicesPerFaces, int depth) {... }

Обновление 1:

Я обновил метод согласно комментариям. Поэтому я изменил тип возвращаемого значения на вектор, вставленный в BvhNode *. Алгоритм работает нормально, пока не дойдет до процесса помещения листовых узлов в std :: vector. Поэтому, когда он начинает помещать последний уровень графика в вектор, он дает мне эту ошибку:

Program received signal SIGSEGV, Segmentation fault.
0x00007fbda4d69c01 in __GI___libc_free (mem=0x555be3a9dba0) at malloc.c:3123
3123    malloc.c: No such file or directory.

Мне удалось разместить семь узлов (что означает все уровни глубины дерева, кроме уровня листьев ) в вектор. Я также пытался запустить valgring, но на самом деле valgrind не выдает никаких ошибок, в отличие от CLion.

Это мой модифицированный метод. Я прокомментировал место ошибки сегментации и исправления.

    BvhNode bvhNode;

    vector<BvhNode> putNodeIntoArray() {

        int size=bvhNode.getNumberOfNodes();

    // FIX:  I modified the array into an std::vector
        vector<BvhNode> nodesArray(size);  

        int current_index = 0;
        vector<BvhNode> tempNodes;

        tempNodes.push_back(bvhNode);   
        BvhNode current_node;

        while (!tempNodes.empty()) {
            current_node = tempNodes.front();// Segmentation error!!
            tempNodes.erase(tempNodes.begin());

            nodesArray.at(current_index)=current_node;
            nodesArray.at(current_index).children.clear();


// FIX: I also modified this not to iterate through leaves' children, because they don't exist.
            if(!current_node.children.empty())  
            {
                tempNodes.push_back(current_node.children.at(0));
                tempNodes.push_back(current_node.children.at(1));
            }
            current_index++;
        }
        return nodesArray;
    }

Ответы [ 2 ]

1 голос
/ 08 мая 2020

Ваши векторы хранят BvhNode везде по значению. Это означает, что каждый раз, когда вы push_back узел, вызывается его конструктор копирования, который, в свою очередь, копирует элемент вектора children внутри узла, который копирует свои собственные элементы et c. Это в основном приводит к тому, что полные поддеревья копируются / освобождаются каждый раз, когда вы вставляете или стираете узел.

Это, в свою очередь, может привести к фрагментации памяти, которая в конечном итоге может привести к сбою перераспределения вектора и вызвать segfault.

Без полного кода я могу порекомендовать следующие 2 вещи:

  1. Хранить дочерние элементы как (умные) указатели, а не по значению

  2. Создайте собственный распределитель для векторов, чтобы включить более детальную отладку, и проверьте наличие сбоев выделения

0 голосов
/ 09 мая 2020

На самом деле проблема была в создании Bvh Tree. Поскольку я хотел обратиться к дочерним элементам в виде 2 * i + 1 и 2 * i + 2, я использовал метод, позволяющий заполнить двоичное дерево (каждый уровень имеет максимальное количество узлов). В этом методе мне пришлось добавить sh пустые узлы к дереву.

Я создавал пустой узел следующим образом:

BvhNode emptyNode;

вместо этого:

BvhNode *emptyNode = new BvhNode();

Итак, когда метод завершился, время жизни emptyNode закончилось. И это вызвало ошибку сегментации.

Более того, была также проблема, что я сохранил BvhNode по значению во время создания плоского двоичного дерева, как указал Балаж Ковачис. Этот параметр значительно замедлил работу приложения и стал еще одним потенциальным источником ошибки сегментации.

...